@ottocode/server 0.1.265 → 0.1.266
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/routes/auth/copilot.ts +699 -0
- package/src/routes/auth/oauth.ts +578 -0
- package/src/routes/auth/onboarding.ts +45 -0
- package/src/routes/auth/providers.ts +189 -0
- package/src/routes/auth/service.ts +167 -0
- package/src/routes/auth/state.ts +23 -0
- package/src/routes/auth/status.ts +203 -0
- package/src/routes/auth/wallet.ts +229 -0
- package/src/routes/auth.ts +12 -2080
- package/src/routes/config/models-service.ts +411 -0
- package/src/routes/config/models.ts +6 -426
- package/src/routes/config/providers-service.ts +237 -0
- package/src/routes/config/providers.ts +10 -242
- package/src/routes/files/handlers.ts +297 -0
- package/src/routes/files/service.ts +313 -0
- package/src/routes/files.ts +12 -608
- package/src/routes/git/commit-service.ts +207 -0
- package/src/routes/git/commit.ts +6 -220
- package/src/routes/git/remote-service.ts +116 -0
- package/src/routes/git/remote.ts +8 -115
- package/src/routes/git/staging-service.ts +111 -0
- package/src/routes/git/staging.ts +10 -205
- package/src/routes/mcp/auth.ts +338 -0
- package/src/routes/mcp/lifecycle.ts +263 -0
- package/src/routes/mcp/servers.ts +212 -0
- package/src/routes/mcp/service.ts +664 -0
- package/src/routes/mcp/state.ts +13 -0
- package/src/routes/mcp.ts +6 -1233
- package/src/routes/ottorouter/billing.ts +593 -0
- package/src/routes/ottorouter/service.ts +92 -0
- package/src/routes/ottorouter/topup.ts +301 -0
- package/src/routes/ottorouter/wallet.ts +370 -0
- package/src/routes/ottorouter.ts +6 -1319
- package/src/routes/research/service.ts +339 -0
- package/src/routes/research.ts +12 -390
- package/src/routes/sessions/crud.ts +563 -0
- package/src/routes/sessions/queue.ts +242 -0
- package/src/routes/sessions/retry.ts +121 -0
- package/src/routes/sessions/service.ts +768 -0
- package/src/routes/sessions/share.ts +434 -0
- package/src/routes/sessions.ts +8 -1977
- package/src/routes/skills/service.ts +221 -0
- package/src/routes/skills/spec.ts +309 -0
- package/src/routes/skills.ts +31 -909
- package/src/routes/terminals/service.ts +326 -0
- package/src/routes/terminals.ts +19 -295
- package/src/routes/tunnel/service.ts +217 -0
- package/src/routes/tunnel.ts +29 -219
- package/src/runtime/agent/registry-prompts.ts +147 -0
- package/src/runtime/agent/registry.ts +6 -124
- package/src/runtime/agent/runner-errors.ts +116 -0
- package/src/runtime/agent/runner-reminders.ts +45 -0
- package/src/runtime/agent/runner-setup-model.ts +75 -0
- package/src/runtime/agent/runner-setup-prompt.ts +185 -0
- package/src/runtime/agent/runner-setup-tools.ts +103 -0
- package/src/runtime/agent/runner-setup-utils.ts +21 -0
- package/src/runtime/agent/runner-setup.ts +54 -288
- package/src/runtime/agent/runner-telemetry.ts +112 -0
- package/src/runtime/agent/runner-text.ts +108 -0
- package/src/runtime/agent/runner-tool-observer.ts +86 -0
- package/src/runtime/agent/runner.ts +79 -378
- package/src/runtime/provider/custom.ts +73 -0
- package/src/runtime/provider/index.ts +2 -85
- package/src/runtime/provider/reasoning-builders.ts +280 -0
- package/src/runtime/provider/reasoning.ts +67 -264
- package/src/tools/adapter/events.ts +116 -0
- package/src/tools/adapter/execution.ts +160 -0
- package/src/tools/adapter/pending.ts +37 -0
- package/src/tools/adapter/persistence.ts +166 -0
- package/src/tools/adapter/results.ts +97 -0
- package/src/tools/adapter.ts +124 -451
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_REMOTE_MODEL_CATALOG_URL,
|
|
3
|
+
catalog,
|
|
4
|
+
discoverOllamaModels,
|
|
5
|
+
filterModelsForAuthType,
|
|
6
|
+
getAuth,
|
|
7
|
+
getProviderDefinition,
|
|
8
|
+
loadConfig,
|
|
9
|
+
logger,
|
|
10
|
+
mergeCachedModelCatalog,
|
|
11
|
+
normalizeModelCatalogPayload,
|
|
12
|
+
providerAllowsAnyModel,
|
|
13
|
+
readCachedModelCatalog,
|
|
14
|
+
readEnvKey,
|
|
15
|
+
type ModelInfo,
|
|
16
|
+
type ProviderId,
|
|
17
|
+
} from '@ottocode/sdk';
|
|
18
|
+
import type { Context } from 'hono';
|
|
19
|
+
import type { EmbeddedAppConfig } from '../../index.ts';
|
|
20
|
+
import { serializeError } from '../../runtime/errors/api-error.ts';
|
|
21
|
+
import {
|
|
22
|
+
getAuthTypeForProvider,
|
|
23
|
+
getAuthorizedProviders,
|
|
24
|
+
getDefault,
|
|
25
|
+
isProviderAuthorizedHybrid,
|
|
26
|
+
} from './utils.ts';
|
|
27
|
+
|
|
28
|
+
const COPILOT_MODELS_URL = 'https://api.githubcopilot.com/models';
|
|
29
|
+
const REMOTE_CATALOG_REFRESH_TTL_MS = 5 * 60 * 1000;
|
|
30
|
+
const PROVIDER_MODEL_REFRESH_TTL_MS = 60 * 1000;
|
|
31
|
+
const USE_BUILTIN_MODEL_CATALOG = process.env.CI === 'true';
|
|
32
|
+
|
|
33
|
+
type UiModel = {
|
|
34
|
+
id: string;
|
|
35
|
+
label: string;
|
|
36
|
+
toolCall?: boolean;
|
|
37
|
+
reasoningText?: boolean;
|
|
38
|
+
vision?: boolean;
|
|
39
|
+
attachment?: boolean;
|
|
40
|
+
free?: boolean;
|
|
41
|
+
contextWindow?: number;
|
|
42
|
+
maxOutputTokens?: number;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type UiProviderModels = {
|
|
46
|
+
label: string;
|
|
47
|
+
authType?: 'api' | 'oauth' | 'wallet';
|
|
48
|
+
allowAnyModel?: boolean;
|
|
49
|
+
dynamicModels?: boolean;
|
|
50
|
+
models: UiModel[];
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const remoteCatalogRefreshes = new Set<string>();
|
|
54
|
+
const providerModelRefreshes = new Set<string>();
|
|
55
|
+
const providerModelRefreshAt = new Map<string, number>();
|
|
56
|
+
let remoteCatalogRefreshAt = 0;
|
|
57
|
+
|
|
58
|
+
function getEmbeddedConfig(c: Context): EmbeddedAppConfig | undefined {
|
|
59
|
+
return (
|
|
60
|
+
c as unknown as {
|
|
61
|
+
get: (key: 'embeddedConfig') => EmbeddedAppConfig | undefined;
|
|
62
|
+
}
|
|
63
|
+
).get('embeddedConfig');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function toUiModel(model: ModelInfo): UiModel {
|
|
67
|
+
return {
|
|
68
|
+
id: model.id,
|
|
69
|
+
label: model.label || model.id,
|
|
70
|
+
toolCall: model.toolCall,
|
|
71
|
+
reasoningText: model.reasoningText,
|
|
72
|
+
vision: model.modalities?.input?.includes('image') ?? false,
|
|
73
|
+
attachment: model.attachment ?? false,
|
|
74
|
+
free: model.cost?.input === 0 && model.cost?.output === 0,
|
|
75
|
+
contextWindow: model.limit?.context,
|
|
76
|
+
maxOutputTokens: model.limit?.output,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function getRemoteCatalogUrl(): string {
|
|
81
|
+
return (
|
|
82
|
+
process.env.OTTO_MODEL_CATALOG_URL?.trim() ||
|
|
83
|
+
DEFAULT_REMOTE_MODEL_CATALOG_URL
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function getModelCatalogProviders(
|
|
88
|
+
cachedCatalog: Awaited<ReturnType<typeof readCachedModelCatalog>>,
|
|
89
|
+
): Record<string, { models?: ModelInfo[]; label?: string }> {
|
|
90
|
+
return cachedCatalog?.providers ?? (USE_BUILTIN_MODEL_CATALOG ? catalog : {});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function refreshRemoteCatalogInBackground(): Promise<void> {
|
|
94
|
+
const url = getRemoteCatalogUrl();
|
|
95
|
+
const now = Date.now();
|
|
96
|
+
if (now - remoteCatalogRefreshAt < REMOTE_CATALOG_REFRESH_TTL_MS) return;
|
|
97
|
+
const cachedCatalog = await readCachedModelCatalog();
|
|
98
|
+
const cachedAt = cachedCatalog ? Date.parse(cachedCatalog.updatedAt) : 0;
|
|
99
|
+
if (Number.isFinite(cachedAt)) {
|
|
100
|
+
remoteCatalogRefreshAt = Math.max(remoteCatalogRefreshAt, cachedAt);
|
|
101
|
+
if (now - remoteCatalogRefreshAt < REMOTE_CATALOG_REFRESH_TTL_MS) return;
|
|
102
|
+
}
|
|
103
|
+
if (remoteCatalogRefreshes.has(url)) return;
|
|
104
|
+
remoteCatalogRefreshes.add(url);
|
|
105
|
+
remoteCatalogRefreshAt = now;
|
|
106
|
+
try {
|
|
107
|
+
const response = await fetch(url);
|
|
108
|
+
if (!response.ok)
|
|
109
|
+
throw new Error(`${response.status} ${response.statusText}`);
|
|
110
|
+
const providers = normalizeModelCatalogPayload(await response.json());
|
|
111
|
+
if (Object.keys(providers).length > 0) {
|
|
112
|
+
await mergeCachedModelCatalog(providers);
|
|
113
|
+
logger.debug('Refreshed remote model catalog', {
|
|
114
|
+
url,
|
|
115
|
+
providers: Object.keys(providers).length,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
logger.debug('Failed to refresh remote model catalog', {
|
|
120
|
+
url,
|
|
121
|
+
error: error instanceof Error ? error.message : String(error),
|
|
122
|
+
});
|
|
123
|
+
} finally {
|
|
124
|
+
remoteCatalogRefreshes.delete(url);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function discoverProviderModels(args: {
|
|
129
|
+
provider: ProviderId;
|
|
130
|
+
providerDefinition: NonNullable<ReturnType<typeof getProviderDefinition>>;
|
|
131
|
+
projectRoot: string;
|
|
132
|
+
}): Promise<ModelInfo[] | undefined> {
|
|
133
|
+
const { provider, providerDefinition, projectRoot } = args;
|
|
134
|
+
if (
|
|
135
|
+
providerDefinition.source !== 'custom' ||
|
|
136
|
+
providerDefinition.compatibility !== 'ollama' ||
|
|
137
|
+
!providerDefinition.baseURL
|
|
138
|
+
) {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const auth = await getAuth(provider, projectRoot);
|
|
144
|
+
const apiKey =
|
|
145
|
+
auth?.type === 'api'
|
|
146
|
+
? auth.key
|
|
147
|
+
: (readEnvKey(provider) ?? providerDefinition.apiKey);
|
|
148
|
+
const discovered = await discoverOllamaModels({
|
|
149
|
+
baseURL: providerDefinition.baseURL,
|
|
150
|
+
apiKey,
|
|
151
|
+
includeDetails: true,
|
|
152
|
+
});
|
|
153
|
+
return discovered.models;
|
|
154
|
+
} catch (error) {
|
|
155
|
+
logger.warn('Failed to discover Ollama models', {
|
|
156
|
+
provider,
|
|
157
|
+
error: error instanceof Error ? error.message : String(error),
|
|
158
|
+
});
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function refreshProviderModelsInBackground(args: {
|
|
164
|
+
provider: ProviderId;
|
|
165
|
+
providerDefinition: NonNullable<ReturnType<typeof getProviderDefinition>>;
|
|
166
|
+
projectRoot: string;
|
|
167
|
+
}): Promise<void> {
|
|
168
|
+
const refreshKey = `${args.projectRoot}:${args.provider}`;
|
|
169
|
+
const now = Date.now();
|
|
170
|
+
const lastRefresh = providerModelRefreshAt.get(refreshKey) ?? 0;
|
|
171
|
+
if (now - lastRefresh < PROVIDER_MODEL_REFRESH_TTL_MS) return;
|
|
172
|
+
if (providerModelRefreshes.has(refreshKey)) return;
|
|
173
|
+
providerModelRefreshes.add(refreshKey);
|
|
174
|
+
providerModelRefreshAt.set(refreshKey, now);
|
|
175
|
+
try {
|
|
176
|
+
const { provider, providerDefinition, projectRoot } = args;
|
|
177
|
+
const discoveredModels = await discoverProviderModels({
|
|
178
|
+
provider,
|
|
179
|
+
providerDefinition,
|
|
180
|
+
projectRoot,
|
|
181
|
+
});
|
|
182
|
+
if (!discoveredModels) return;
|
|
183
|
+
await mergeCachedModelCatalog({
|
|
184
|
+
[provider]: {
|
|
185
|
+
id: provider,
|
|
186
|
+
label: providerDefinition.label,
|
|
187
|
+
models: discoveredModels,
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
} catch (error) {
|
|
191
|
+
logger.debug('Failed to refresh provider model cache', {
|
|
192
|
+
provider: args.provider,
|
|
193
|
+
error: error instanceof Error ? error.message : String(error),
|
|
194
|
+
});
|
|
195
|
+
} finally {
|
|
196
|
+
providerModelRefreshes.delete(refreshKey);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function shouldLazyLoadProviderModels(
|
|
201
|
+
providerDefinition: NonNullable<ReturnType<typeof getProviderDefinition>>,
|
|
202
|
+
): boolean {
|
|
203
|
+
return (
|
|
204
|
+
providerDefinition.source === 'custom' &&
|
|
205
|
+
providerDefinition.compatibility === 'ollama'
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function getProviderModelsForUi(args: {
|
|
210
|
+
catalogModels: ModelInfo[] | undefined;
|
|
211
|
+
provider: ProviderId;
|
|
212
|
+
authType: 'api' | 'oauth' | 'wallet' | undefined;
|
|
213
|
+
}): ModelInfo[] {
|
|
214
|
+
const catalogModels = args.catalogModels ?? [];
|
|
215
|
+
return filterModelsForAuthType(args.provider, catalogModels, args.authType);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function getUiProviderLabel(
|
|
219
|
+
providerDefinition: NonNullable<ReturnType<typeof getProviderDefinition>>,
|
|
220
|
+
): string {
|
|
221
|
+
if (providerDefinition.source !== 'custom') return providerDefinition.label;
|
|
222
|
+
return providerDefinition.label.includes('(custom)')
|
|
223
|
+
? providerDefinition.label
|
|
224
|
+
: `${providerDefinition.label} (custom)`;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function filterCopilotAvailability<T extends { id: string }>(
|
|
228
|
+
provider: ProviderId,
|
|
229
|
+
models: T[],
|
|
230
|
+
copilotAllowedModels: Set<string> | null,
|
|
231
|
+
): T[] {
|
|
232
|
+
if (provider !== 'copilot') return models;
|
|
233
|
+
if (!copilotAllowedModels || copilotAllowedModels.size === 0) return models;
|
|
234
|
+
return models.filter((m) => copilotAllowedModels.has(m.id));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async function getCopilotAuthTokens(projectRoot: string): Promise<string[]> {
|
|
238
|
+
const tokens: string[] = [];
|
|
239
|
+
const envToken = readEnvKey('copilot');
|
|
240
|
+
if (envToken) tokens.push(envToken);
|
|
241
|
+
const auth = await getAuth('copilot', projectRoot);
|
|
242
|
+
if (auth?.type === 'oauth' && auth.refresh && auth.refresh !== envToken) {
|
|
243
|
+
tokens.push(auth.refresh);
|
|
244
|
+
}
|
|
245
|
+
return tokens;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async function getAuthorizedCopilotModels(
|
|
249
|
+
projectRoot: string,
|
|
250
|
+
): Promise<Set<string> | null> {
|
|
251
|
+
const tokens = await getCopilotAuthTokens(projectRoot);
|
|
252
|
+
if (!tokens.length) return null;
|
|
253
|
+
const merged = new Set<string>();
|
|
254
|
+
let successful = false;
|
|
255
|
+
|
|
256
|
+
for (const token of tokens) {
|
|
257
|
+
try {
|
|
258
|
+
const response = await fetch(COPILOT_MODELS_URL, {
|
|
259
|
+
headers: {
|
|
260
|
+
Authorization: `Bearer ${token}`,
|
|
261
|
+
'Openai-Intent': 'conversation-edits',
|
|
262
|
+
'User-Agent': 'ottocode',
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
if (!response.ok) continue;
|
|
266
|
+
successful = true;
|
|
267
|
+
const payload = (await response.json()) as {
|
|
268
|
+
data?: Array<{ id?: string }>;
|
|
269
|
+
};
|
|
270
|
+
for (const id of (payload.data ?? []).map((item) => item.id)) {
|
|
271
|
+
if (id) merged.add(id);
|
|
272
|
+
}
|
|
273
|
+
} catch {}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return successful ? merged : null;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export async function handleGetProviderModels(c: Context) {
|
|
280
|
+
try {
|
|
281
|
+
const embeddedConfig = getEmbeddedConfig(c);
|
|
282
|
+
const provider = c.req.param('provider') as ProviderId;
|
|
283
|
+
const projectRoot = c.req.query('project') || process.cwd();
|
|
284
|
+
const cfg = await loadConfig(projectRoot);
|
|
285
|
+
const cachedCatalog = await readCachedModelCatalog();
|
|
286
|
+
const modelCatalogProviders = getModelCatalogProviders(cachedCatalog);
|
|
287
|
+
const providerCatalog = modelCatalogProviders[provider];
|
|
288
|
+
|
|
289
|
+
const authorized = await isProviderAuthorizedHybrid(
|
|
290
|
+
embeddedConfig,
|
|
291
|
+
cfg,
|
|
292
|
+
provider,
|
|
293
|
+
);
|
|
294
|
+
if (!authorized) {
|
|
295
|
+
logger.warn('Provider not authorized', { provider });
|
|
296
|
+
return c.json({ error: 'Provider not authorized' }, 403);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const providerDefinition = getProviderDefinition(cfg, provider);
|
|
300
|
+
if (!providerDefinition && !providerCatalog) {
|
|
301
|
+
logger.warn('Provider not found in catalog', { provider });
|
|
302
|
+
return c.json({ error: 'Provider not found' }, 404);
|
|
303
|
+
}
|
|
304
|
+
void refreshRemoteCatalogInBackground();
|
|
305
|
+
|
|
306
|
+
const authType = await getAuthTypeForProvider(
|
|
307
|
+
embeddedConfig,
|
|
308
|
+
provider,
|
|
309
|
+
projectRoot,
|
|
310
|
+
);
|
|
311
|
+
if (
|
|
312
|
+
providerDefinition &&
|
|
313
|
+
shouldLazyLoadProviderModels(providerDefinition)
|
|
314
|
+
) {
|
|
315
|
+
void refreshProviderModelsInBackground({
|
|
316
|
+
provider,
|
|
317
|
+
providerDefinition,
|
|
318
|
+
projectRoot,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
const filteredModels = getProviderModelsForUi({
|
|
322
|
+
catalogModels: providerCatalog?.models,
|
|
323
|
+
provider,
|
|
324
|
+
authType,
|
|
325
|
+
});
|
|
326
|
+
const copilotAllowedModels =
|
|
327
|
+
provider === 'copilot'
|
|
328
|
+
? await getAuthorizedCopilotModels(projectRoot)
|
|
329
|
+
: null;
|
|
330
|
+
const availableModels = filterCopilotAvailability(
|
|
331
|
+
provider,
|
|
332
|
+
filteredModels,
|
|
333
|
+
copilotAllowedModels,
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
return c.json({
|
|
337
|
+
models: availableModels.map(toUiModel),
|
|
338
|
+
default: getDefault(
|
|
339
|
+
embeddedConfig?.model,
|
|
340
|
+
embeddedConfig?.defaults?.model,
|
|
341
|
+
cfg.defaults.model,
|
|
342
|
+
),
|
|
343
|
+
allowAnyModel: providerDefinition
|
|
344
|
+
? providerAllowsAnyModel(cfg, provider)
|
|
345
|
+
: undefined,
|
|
346
|
+
label: providerDefinition
|
|
347
|
+
? getUiProviderLabel(providerDefinition)
|
|
348
|
+
: (providerCatalog?.label ?? provider),
|
|
349
|
+
});
|
|
350
|
+
} catch (error) {
|
|
351
|
+
logger.error('Failed to get provider models', error);
|
|
352
|
+
const errorResponse = serializeError(error);
|
|
353
|
+
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export async function handleGetAllModels(c: Context) {
|
|
358
|
+
try {
|
|
359
|
+
const embeddedConfig = getEmbeddedConfig(c);
|
|
360
|
+
const projectRoot = c.req.query('project') || process.cwd();
|
|
361
|
+
const cfg = await loadConfig(projectRoot);
|
|
362
|
+
const authorizedProviders = await getAuthorizedProviders(
|
|
363
|
+
embeddedConfig,
|
|
364
|
+
cfg,
|
|
365
|
+
);
|
|
366
|
+
const cachedCatalog = await readCachedModelCatalog();
|
|
367
|
+
const modelCatalogProviders = getModelCatalogProviders(cachedCatalog);
|
|
368
|
+
void refreshRemoteCatalogInBackground();
|
|
369
|
+
|
|
370
|
+
const modelsMap: Record<string, UiProviderModels> = {};
|
|
371
|
+
for (const provider of authorizedProviders) {
|
|
372
|
+
const providerCatalog = modelCatalogProviders[provider];
|
|
373
|
+
const providerDefinition = getProviderDefinition(cfg, provider);
|
|
374
|
+
if (!providerCatalog) continue;
|
|
375
|
+
const dynamicModels =
|
|
376
|
+
providerDefinition && shouldLazyLoadProviderModels(providerDefinition);
|
|
377
|
+
const authType = await getAuthTypeForProvider(
|
|
378
|
+
embeddedConfig,
|
|
379
|
+
provider,
|
|
380
|
+
projectRoot,
|
|
381
|
+
);
|
|
382
|
+
if (dynamicModels && providerDefinition) {
|
|
383
|
+
void refreshProviderModelsInBackground({
|
|
384
|
+
provider,
|
|
385
|
+
providerDefinition,
|
|
386
|
+
projectRoot,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
const filteredModels = getProviderModelsForUi({
|
|
390
|
+
catalogModels: providerCatalog?.models,
|
|
391
|
+
provider,
|
|
392
|
+
authType,
|
|
393
|
+
});
|
|
394
|
+
modelsMap[provider] = {
|
|
395
|
+
label: providerDefinition
|
|
396
|
+
? getUiProviderLabel(providerDefinition)
|
|
397
|
+
: (providerCatalog.label ?? provider),
|
|
398
|
+
authType,
|
|
399
|
+
allowAnyModel: providerDefinition?.allowAnyModel,
|
|
400
|
+
dynamicModels,
|
|
401
|
+
models: filteredModels.map(toUiModel),
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return c.json(modelsMap);
|
|
406
|
+
} catch (error) {
|
|
407
|
+
logger.error('Failed to get all models', error);
|
|
408
|
+
const errorResponse = serializeError(error);
|
|
409
|
+
return c.json(errorResponse, errorResponse.error.status || 500);
|
|
410
|
+
}
|
|
411
|
+
}
|