Skip to content

Commit 1ba90d3

Browse files
committed
✨ feat: support open router auto model list
1 parent 1583011 commit 1ba90d3

File tree

5 files changed

+70
-8
lines changed

5 files changed

+70
-8
lines changed

src/app/api/chat/models/[provider]/route.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { NextResponse } from 'next/server';
22

33
import { getPreferredRegion } from '@/app/api/config';
44
import { createErrorResponse } from '@/app/api/errorResponse';
5-
import { ChatCompletionErrorPayload } from '@/libs/agent-runtime';
5+
import { ChatCompletionErrorPayload, ModelProvider } from '@/libs/agent-runtime';
66
import { ChatErrorType } from '@/types/fetch';
77

88
import AgentRuntime from '../../agentRuntime';
@@ -16,7 +16,12 @@ export const GET = checkAuth(async (req, { params, jwtPayload }) => {
1616
const { provider } = params;
1717

1818
try {
19-
const agentRuntime = await AgentRuntime.initializeWithUserPayload(provider, jwtPayload);
19+
const openRouterKey = jwtPayload.apiKey || 'openrouter-dont-need-api-key-for-model-list';
20+
21+
const agentRuntime = await AgentRuntime.initializeWithUserPayload(provider, {
22+
...jwtPayload,
23+
apiKey: provider === ModelProvider.OpenRouter ? openRouterKey : jwtPayload.apiKey,
24+
});
2025

2126
const list = await agentRuntime.models();
2227

src/libs/agent-runtime/openrouter/index.ts

+23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
import { LOBE_DEFAULT_MODEL_LIST } from '@/config/modelProviders';
2+
13
import { AgentRuntimeErrorType } from '../error';
24
import { ModelProvider } from '../types';
35
import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory';
6+
import { OpenRouterModelCard } from './type';
47

58
export const LobeOpenRouterAI = LobeOpenAICompatibleFactory({
69
baseURL: 'https://openrouter.ai/api/v1',
@@ -13,9 +16,29 @@ export const LobeOpenRouterAI = LobeOpenAICompatibleFactory({
1316
debug: {
1417
chatCompletion: () => process.env.DEBUG_OPENROUTER_CHAT_COMPLETION === '1',
1518
},
19+
1620
errorType: {
1721
bizError: AgentRuntimeErrorType.OpenRouterBizError,
1822
invalidAPIKey: AgentRuntimeErrorType.InvalidOpenRouterAPIKey,
1923
},
24+
models: {
25+
transformModel: (m) => {
26+
const model = m as unknown as OpenRouterModelCard;
27+
28+
return {
29+
description: model.description,
30+
displayName: model.name,
31+
enabled: LOBE_DEFAULT_MODEL_LIST.find((m) => model.id.endsWith(m.id))?.enabled || false,
32+
functionCall: model.description.includes('function calling'),
33+
id: model.id,
34+
maxTokens:
35+
typeof model.top_provider.max_completion_tokens === 'number'
36+
? model.top_provider.max_completion_tokens
37+
: undefined,
38+
tokens: model.context_length,
39+
vision: model.description.includes('vision'),
40+
};
41+
},
42+
},
2043
provider: ModelProvider.OpenRouter,
2144
});
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
interface ModelPricing {
2+
completion: string;
3+
image: string;
4+
prompt: string;
5+
request: string;
6+
}
7+
8+
interface ModelArchitecture {
9+
instruct_type: string | null;
10+
modality: string;
11+
tokenizer: string;
12+
}
13+
14+
interface ModelTopProvider {
15+
is_moderated: boolean;
16+
max_completion_tokens: number | null;
17+
}
18+
19+
export interface OpenRouterModelCard {
20+
architecture: ModelArchitecture;
21+
context_length: number;
22+
description: string;
23+
id: string;
24+
name: string;
25+
per_request_limits: any | null;
26+
pricing: ModelPricing;
27+
top_provider: ModelTopProvider;
28+
}

src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ interface OpenAICompatibleFactoryOptions {
2525
bizError: ILobeAgentRuntimeErrorType;
2626
invalidAPIKey: ILobeAgentRuntimeErrorType;
2727
};
28+
models?: {
29+
transformModel?: (model: OpenAI.Model) => ChatModelCard;
30+
};
2831
provider: string;
2932
}
3033

@@ -35,6 +38,7 @@ export const LobeOpenAICompatibleFactory = ({
3538
debug,
3639
constructorOptions,
3740
chatCompletion,
41+
models,
3842
}: OpenAICompatibleFactoryOptions) =>
3943
class LobeOpenAICompatibleAI implements LobeRuntimeAI {
4044
client: OpenAI;
@@ -107,15 +111,17 @@ export const LobeOpenAICompatibleFactory = ({
107111
async models() {
108112
const list = await this.client.models.list();
109113

110-
const modelIds = list.data.map((i) => i.id);
114+
return list.data
115+
.map((item) => {
116+
if (models?.transformModel) {
117+
return models.transformModel(item);
118+
}
111119

112-
return modelIds
113-
.map((id) => {
114-
const knownModel = LOBE_DEFAULT_MODEL_LIST.find((model) => model.id === id);
120+
const knownModel = LOBE_DEFAULT_MODEL_LIST.find((model) => model.id === item.id);
115121

116122
if (knownModel) return knownModel;
117123

118-
return { id: id };
124+
return { id: item.id };
119125
})
120126
.filter(Boolean) as ChatModelCard[];
121127
}

src/services/models.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ class ModelsService {
99
headers: { 'Content-Type': 'application/json' },
1010
provider,
1111
});
12-
1312
try {
1413
const res = await fetch(API_ENDPOINTS.chatModels(provider), { headers });
14+
if (!res.ok) return;
1515

1616
return res.json();
1717
} catch {

0 commit comments

Comments
 (0)