titan-agent 5.3.2 → 5.4.1
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/dist/agent/agent.js +11 -1
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/agentLoop.js +36 -1
- package/dist/agent/agentLoop.js.map +1 -1
- package/dist/agent/session.js +106 -5
- package/dist/agent/session.js.map +1 -1
- package/dist/agent/subAgent.js +62 -1
- package/dist/agent/subAgent.js.map +1 -1
- package/dist/config/config.js +30 -8
- package/dist/config/config.js.map +1 -1
- package/dist/config/schema.js +25 -2
- package/dist/config/schema.js.map +1 -1
- package/dist/gateway/server.js +32 -1
- package/dist/gateway/server.js.map +1 -1
- package/dist/memory/graph.js +49 -15
- package/dist/memory/graph.js.map +1 -1
- package/dist/memory/index.js +192 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/memory.js +1 -0
- package/dist/memory/memory.js.map +1 -1
- package/dist/mesh/transport.js +60 -8
- package/dist/mesh/transport.js.map +1 -1
- package/dist/providers/anthropic.js +3 -2
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/base.js.map +1 -1
- package/dist/providers/google.js +94 -20
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/modelCapabilities.js +59 -0
- package/dist/providers/modelCapabilities.js.map +1 -0
- package/dist/providers/ollama.js +3 -2
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai.js +4 -3
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openai_compat.js +3 -2
- package/dist/providers/openai_compat.js.map +1 -1
- package/dist/providers/router.js +63 -21
- package/dist/providers/router.js.map +1 -1
- package/dist/safety/fabricationGuard.js +140 -0
- package/dist/safety/fabricationGuard.js.map +1 -0
- package/dist/skills/builtin/gepa.js +23 -1
- package/dist/skills/builtin/gepa.js.map +1 -1
- package/dist/skills/builtin/model_trainer.js +31 -4
- package/dist/skills/builtin/model_trainer.js.map +1 -1
- package/dist/skills/builtin/self_improve.js +50 -2
- package/dist/skills/builtin/self_improve.js.map +1 -1
- package/dist/utils/constants.js +2 -2
- package/dist/utils/constants.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/providers/openai.ts"],"sourcesContent":["/**\n * TITAN — OpenAI Provider (GPT-4, o-series)\n */\nimport {\n LLMProvider,\n type ChatOptions,\n type ChatResponse,\n type ChatStreamChunk,\n type ToolCall,\n} from './base.js';\nimport { loadConfig } from '../config/config.js';\nimport logger from '../utils/logger.js';\nimport { fetchWithRetry } from '../utils/helpers.js';\nimport { resolveApiKey } from './authResolver.js';\nimport { v4 as uuid } from 'uuid';\n\nconst COMPONENT = 'OpenAI';\n\nexport class OpenAIProvider extends LLMProvider {\n readonly name = 'openai';\n readonly displayName = 'OpenAI (GPT)';\n\n private get apiKey(): string {\n const config = loadConfig();\n const p = config.providers.openai;\n return resolveApiKey('openai', p.authProfiles || [], p.apiKey || '', 'OPENAI_API_KEY', p.rotationStrategy, p.credentialCooldownMs);\n }\n\n private get baseUrl(): string {\n const config = loadConfig();\n return config.providers.openai.baseUrl || 'https://api.openai.com';\n }\n\n async chat(options: ChatOptions): Promise<ChatResponse> {\n const model = options.model || 'gpt-4o';\n const apiKey = this.apiKey;\n if (!apiKey) throw new Error('OpenAI API key not configured');\n\n logger.debug(COMPONENT, `Chat request: model=${model}, messages=${options.messages.length}`);\n\n const cleanModel = model.replace('openai/', '');\n const isReasoningModel = /^(o1|o3|o4)/.test(cleanModel);\n\n const body: Record<string, unknown> = {\n model: cleanModel,\n messages: options.messages.map((m) => {\n if (m.role === 'tool') {\n return { role: 'tool', content: m.content, tool_call_id: m.toolCallId };\n }\n if (m.role === 'assistant' && m.toolCalls) {\n return {\n role: 'assistant',\n content: m.content || null,\n tool_calls: m.toolCalls.map((tc) => ({\n id: tc.id,\n type: 'function',\n function: { name: tc.function.name, arguments: tc.function.arguments },\n })),\n };\n }\n // o-series reasoning models use 'developer' role instead of 'system'\n if (m.role === 'system' && isReasoningModel) {\n return { role: 'developer', content: m.content };\n }\n return { role: m.role, content: m.content };\n }),\n };\n\n // o-series models require max_completion_tokens, not max_tokens\n if (isReasoningModel) {\n body.max_completion_tokens = options.maxTokens || 8192;\n } else {\n body.max_tokens = options.maxTokens || 8192;\n }\n\n if (options.tools && options.tools.length > 0) {\n body.tools = options.tools;\n // Force at least one tool call on first round when task requires it.\n // Use \"auto\" for o-series (they manage tool use internally via reasoning).\n if (options.forceToolUse && !isReasoningModel) {\n body.tool_choice = 'required';\n }\n }\n\n // o-series models reject the temperature parameter\n if (options.temperature !== undefined && !isReasoningModel) {\n body.temperature = options.temperature;\n }\n\n // Reasoning effort for o-series models\n if (options.thinking && isReasoningModel) {\n const effortMap: Record<string, string> = { low: 'low', medium: 'medium', high: 'high' };\n body.reasoning_effort = effortMap[options.thinkingLevel || 'medium'] || 'medium';\n }\n\n const response = await fetchWithRetry(`${this.baseUrl}/v1/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n // Hunt Finding #37: attach status + Retry-After so the router can respect backoff\n const { createProviderError } = await import('./errorTaxonomy.js');\n throw createProviderError('OpenAI API', response, errorText, { provider: 'openai', model });\n }\n\n const data = await response.json() as Record<string, unknown>;\n const choices = data.choices as Array<Record<string, unknown>> | undefined;\n\n if (!choices || choices.length === 0) {\n return {\n id: (data.id as string) || uuid(),\n content: '',\n usage: undefined,\n finishReason: 'stop',\n model,\n };\n }\n\n const choice = choices[0];\n const message = choice.message as Record<string, unknown>;\n\n const toolCalls: ToolCall[] = [];\n if (message.tool_calls) {\n for (const tc of message.tool_calls as Array<Record<string, unknown>>) {\n const fn = tc.function as Record<string, string>;\n toolCalls.push({\n id: tc.id as string,\n type: 'function',\n function: { name: fn.name, arguments: fn.arguments },\n });\n }\n }\n\n const usage = data.usage as { prompt_tokens: number; completion_tokens: number; total_tokens: number } | undefined;\n\n return {\n id: (data.id as string) || uuid(),\n content: (message.content as string) || '',\n toolCalls: toolCalls.length > 0 ? toolCalls : undefined,\n usage: usage\n ? {\n promptTokens: usage.prompt_tokens,\n completionTokens: usage.completion_tokens,\n totalTokens: usage.total_tokens,\n }\n : undefined,\n finishReason: toolCalls.length > 0 ? 'tool_calls' : (choice.finish_reason as 'stop' | 'length') || 'stop',\n model,\n };\n }\n\n async *chatStream(options: ChatOptions): AsyncGenerator<ChatStreamChunk> {\n const model = options.model || 'gpt-4o';\n const apiKey = this.apiKey;\n if (!apiKey) { yield { type: 'error', error: 'OpenAI API key not configured' }; return; }\n\n const cleanModel = model.replace('openai/', '');\n const isReasoningModel = /^(o1|o3|o4)/.test(cleanModel);\n\n const body: Record<string, unknown> = {\n model: cleanModel,\n stream: true,\n messages: options.messages.map((m) => {\n if (m.role === 'tool') return { role: 'tool', content: m.content, tool_call_id: m.toolCallId };\n if (m.role === 'assistant' && m.toolCalls) {\n return {\n role: 'assistant', content: m.content || null,\n tool_calls: m.toolCalls.map((tc) => ({ id: tc.id, type: 'function', function: { name: tc.function.name, arguments: tc.function.arguments } })),\n };\n }\n if (m.role === 'system' && isReasoningModel) return { role: 'developer', content: m.content };\n return { role: m.role, content: m.content };\n }),\n };\n\n if (isReasoningModel) { body.max_completion_tokens = options.maxTokens || 8192; }\n else { body.max_tokens = options.maxTokens || 8192; }\n if (options.tools && options.tools.length > 0) body.tools = options.tools;\n if (options.temperature !== undefined && !isReasoningModel) body.temperature = options.temperature;\n\n // Reasoning effort for o-series models\n if (options.thinking && isReasoningModel) {\n const effortMap: Record<string, string> = { low: 'low', medium: 'medium', high: 'high' };\n body.reasoning_effort = effortMap[options.thinkingLevel || 'medium'] || 'medium';\n }\n\n try {\n const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },\n body: JSON.stringify(body),\n });\n\n if (!response.ok || !response.body) {\n const errorText = await response.text();\n yield { type: 'error', error: `OpenAI API error (${response.status}): ${errorText}` };\n return;\n }\n\n const toolCalls = new Map<number, { id: string; name: string; args: string }>();\n yield* this.parseOpenAISSE(response.body, toolCalls);\n } catch (error) {\n yield { type: 'error', error: (error as Error).message };\n }\n }\n\n /** Parse OpenAI-format SSE stream and yield ChatStreamChunks */\n private async *parseOpenAISSE(\n body: ReadableStream<Uint8Array>,\n toolCalls: Map<number, { id: string; name: string; args: string }>,\n ): AsyncGenerator<ChatStreamChunk> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const json = line.slice(6).trim();\n if (json === '[DONE]') { break; }\n if (!json) continue;\n\n try {\n const chunk = JSON.parse(json);\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) continue;\n\n if (delta.content) {\n yield { type: 'text', content: delta.content };\n }\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCalls.has(idx)) {\n toolCalls.set(idx, { id: tc.id || '', name: '', args: '' });\n }\n const entry = toolCalls.get(idx)!;\n if (tc.id) entry.id = tc.id;\n if (tc.function?.name) entry.name = tc.function.name;\n if (tc.function?.arguments) entry.args += tc.function.arguments;\n }\n }\n } catch { /* skip malformed lines */ }\n }\n }\n\n // Emit accumulated tool calls\n for (const [, tc] of toolCalls) {\n if (tc.id && tc.name) {\n yield { type: 'tool_call', toolCall: { id: tc.id, type: 'function', function: { name: tc.name, arguments: tc.args || '{}' } } };\n }\n }\n yield { type: 'done' };\n }\n\n async listModels(): Promise<string[]> {\n return ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1', 'o1-mini', 'o3-mini'];\n }\n\n async healthCheck(): Promise<boolean> {\n try {\n if (!this.apiKey) return false;\n const response = await fetch(`${this.baseUrl}/v1/models`, {\n headers: { Authorization: `Bearer ${this.apiKey}` },\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/providers/openai.ts"],"sourcesContent":["/**\n * TITAN — OpenAI Provider (GPT-4, o-series)\n */\nimport {\n LLMProvider,\n type ChatOptions,\n type ChatResponse,\n type ChatStreamChunk,\n type ToolCall,\n} from './base.js';\nimport { loadConfig } from '../config/config.js';\nimport logger from '../utils/logger.js';\nimport { fetchWithRetry } from '../utils/helpers.js';\nimport { resolveApiKey } from './authResolver.js';\nimport { v4 as uuid } from 'uuid';\nimport { clampMaxTokens } from './modelCapabilities.js';\n\nconst COMPONENT = 'OpenAI';\n\nexport class OpenAIProvider extends LLMProvider {\n readonly name = 'openai';\n readonly displayName = 'OpenAI (GPT)';\n\n private get apiKey(): string {\n const config = loadConfig();\n const p = config.providers.openai;\n return resolveApiKey('openai', p.authProfiles || [], p.apiKey || '', 'OPENAI_API_KEY', p.rotationStrategy, p.credentialCooldownMs);\n }\n\n private get baseUrl(): string {\n const config = loadConfig();\n return config.providers.openai.baseUrl || 'https://api.openai.com';\n }\n\n async chat(options: ChatOptions): Promise<ChatResponse> {\n const model = options.model || 'gpt-4o';\n const apiKey = this.apiKey;\n if (!apiKey) throw new Error('OpenAI API key not configured');\n\n logger.debug(COMPONENT, `Chat request: model=${model}, messages=${options.messages.length}`);\n\n const cleanModel = model.replace('openai/', '');\n const isReasoningModel = /^(o1|o3|o4)/.test(cleanModel);\n\n const body: Record<string, unknown> = {\n model: cleanModel,\n messages: options.messages.map((m) => {\n if (m.role === 'tool') {\n return { role: 'tool', content: m.content, tool_call_id: m.toolCallId };\n }\n if (m.role === 'assistant' && m.toolCalls) {\n return {\n role: 'assistant',\n content: m.content || null,\n tool_calls: m.toolCalls.map((tc) => ({\n id: tc.id,\n type: 'function',\n function: { name: tc.function.name, arguments: tc.function.arguments },\n })),\n };\n }\n // o-series reasoning models use 'developer' role instead of 'system'\n if (m.role === 'system' && isReasoningModel) {\n return { role: 'developer', content: m.content };\n }\n return { role: m.role, content: m.content };\n }),\n };\n\n // o-series models require max_completion_tokens, not max_tokens\n if (isReasoningModel) {\n body.max_completion_tokens = clampMaxTokens(model, options.maxTokens);\n } else {\n body.max_tokens = clampMaxTokens(model, options.maxTokens);\n }\n\n if (options.tools && options.tools.length > 0) {\n body.tools = options.tools;\n // Force at least one tool call on first round when task requires it.\n // Use \"auto\" for o-series (they manage tool use internally via reasoning).\n if (options.forceToolUse && !isReasoningModel) {\n body.tool_choice = 'required';\n }\n }\n\n // o-series models reject the temperature parameter\n if (options.temperature !== undefined && !isReasoningModel) {\n body.temperature = options.temperature;\n }\n\n // Reasoning effort for o-series models\n if (options.thinking && isReasoningModel) {\n const effortMap: Record<string, string> = { low: 'low', medium: 'medium', high: 'high' };\n body.reasoning_effort = effortMap[options.thinkingLevel || 'medium'] || 'medium';\n }\n\n const response = await fetchWithRetry(`${this.baseUrl}/v1/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n // Hunt Finding #37: attach status + Retry-After so the router can respect backoff\n const { createProviderError } = await import('./errorTaxonomy.js');\n throw createProviderError('OpenAI API', response, errorText, { provider: 'openai', model });\n }\n\n const data = await response.json() as Record<string, unknown>;\n const choices = data.choices as Array<Record<string, unknown>> | undefined;\n\n if (!choices || choices.length === 0) {\n return {\n id: (data.id as string) || uuid(),\n content: '',\n usage: undefined,\n finishReason: 'stop',\n model,\n };\n }\n\n const choice = choices[0];\n const message = choice.message as Record<string, unknown>;\n\n const toolCalls: ToolCall[] = [];\n if (message.tool_calls) {\n for (const tc of message.tool_calls as Array<Record<string, unknown>>) {\n const fn = tc.function as Record<string, string>;\n toolCalls.push({\n id: tc.id as string,\n type: 'function',\n function: { name: fn.name, arguments: fn.arguments },\n });\n }\n }\n\n const usage = data.usage as { prompt_tokens: number; completion_tokens: number; total_tokens: number } | undefined;\n\n return {\n id: (data.id as string) || uuid(),\n content: (message.content as string) || '',\n toolCalls: toolCalls.length > 0 ? toolCalls : undefined,\n usage: usage\n ? {\n promptTokens: usage.prompt_tokens,\n completionTokens: usage.completion_tokens,\n totalTokens: usage.total_tokens,\n }\n : undefined,\n finishReason: toolCalls.length > 0 ? 'tool_calls' : (choice.finish_reason as 'stop' | 'length') || 'stop',\n model,\n };\n }\n\n async *chatStream(options: ChatOptions): AsyncGenerator<ChatStreamChunk> {\n const model = options.model || 'gpt-4o';\n const apiKey = this.apiKey;\n if (!apiKey) { yield { type: 'error', error: 'OpenAI API key not configured' }; return; }\n\n const cleanModel = model.replace('openai/', '');\n const isReasoningModel = /^(o1|o3|o4)/.test(cleanModel);\n\n const body: Record<string, unknown> = {\n model: cleanModel,\n stream: true,\n messages: options.messages.map((m) => {\n if (m.role === 'tool') return { role: 'tool', content: m.content, tool_call_id: m.toolCallId };\n if (m.role === 'assistant' && m.toolCalls) {\n return {\n role: 'assistant', content: m.content || null,\n tool_calls: m.toolCalls.map((tc) => ({ id: tc.id, type: 'function', function: { name: tc.function.name, arguments: tc.function.arguments } })),\n };\n }\n if (m.role === 'system' && isReasoningModel) return { role: 'developer', content: m.content };\n return { role: m.role, content: m.content };\n }),\n };\n\n if (isReasoningModel) { body.max_completion_tokens = options.maxTokens || 8192; }\n else { body.max_tokens = clampMaxTokens(model, options.maxTokens); }\n if (options.tools && options.tools.length > 0) body.tools = options.tools;\n if (options.temperature !== undefined && !isReasoningModel) body.temperature = options.temperature;\n\n // Reasoning effort for o-series models\n if (options.thinking && isReasoningModel) {\n const effortMap: Record<string, string> = { low: 'low', medium: 'medium', high: 'high' };\n body.reasoning_effort = effortMap[options.thinkingLevel || 'medium'] || 'medium';\n }\n\n try {\n const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },\n body: JSON.stringify(body),\n });\n\n if (!response.ok || !response.body) {\n const errorText = await response.text();\n yield { type: 'error', error: `OpenAI API error (${response.status}): ${errorText}` };\n return;\n }\n\n const toolCalls = new Map<number, { id: string; name: string; args: string }>();\n yield* this.parseOpenAISSE(response.body, toolCalls);\n } catch (error) {\n yield { type: 'error', error: (error as Error).message };\n }\n }\n\n /** Parse OpenAI-format SSE stream and yield ChatStreamChunks */\n private async *parseOpenAISSE(\n body: ReadableStream<Uint8Array>,\n toolCalls: Map<number, { id: string; name: string; args: string }>,\n ): AsyncGenerator<ChatStreamChunk> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const json = line.slice(6).trim();\n if (json === '[DONE]') { break; }\n if (!json) continue;\n\n try {\n const chunk = JSON.parse(json);\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) continue;\n\n if (delta.content) {\n yield { type: 'text', content: delta.content };\n }\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCalls.has(idx)) {\n toolCalls.set(idx, { id: tc.id || '', name: '', args: '' });\n }\n const entry = toolCalls.get(idx)!;\n if (tc.id) entry.id = tc.id;\n if (tc.function?.name) entry.name = tc.function.name;\n if (tc.function?.arguments) entry.args += tc.function.arguments;\n }\n }\n } catch { /* skip malformed lines */ }\n }\n }\n\n // Emit accumulated tool calls\n for (const [, tc] of toolCalls) {\n if (tc.id && tc.name) {\n yield { type: 'tool_call', toolCall: { id: tc.id, type: 'function', function: { name: tc.name, arguments: tc.args || '{}' } } };\n }\n }\n yield { type: 'done' };\n }\n\n async listModels(): Promise<string[]> {\n return ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1', 'o1-mini', 'o3-mini'];\n }\n\n async healthCheck(): Promise<boolean> {\n try {\n if (!this.apiKey) return false;\n const response = await fetch(`${this.baseUrl}/v1/models`, {\n headers: { Authorization: `Bearer ${this.apiKey}` },\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -7,6 +7,7 @@ import logger from "../utils/logger.js";
|
|
|
7
7
|
import { fetchWithRetry } from "../utils/helpers.js";
|
|
8
8
|
import { resolveApiKey } from "./authResolver.js";
|
|
9
9
|
import { v4 as uuid } from "uuid";
|
|
10
|
+
import { clampMaxTokens } from "./modelCapabilities.js";
|
|
10
11
|
class OpenAICompatProvider extends LLMProvider {
|
|
11
12
|
name;
|
|
12
13
|
displayName;
|
|
@@ -60,7 +61,7 @@ class OpenAICompatProvider extends LLMProvider {
|
|
|
60
61
|
}
|
|
61
62
|
return { role: m.role, content: m.content || " " };
|
|
62
63
|
}),
|
|
63
|
-
max_tokens: options.maxTokens
|
|
64
|
+
max_tokens: clampMaxTokens(model, options.maxTokens)
|
|
64
65
|
};
|
|
65
66
|
if (options.tools && options.tools.length > 0) {
|
|
66
67
|
body.tools = options.tools;
|
|
@@ -144,7 +145,7 @@ class OpenAICompatProvider extends LLMProvider {
|
|
|
144
145
|
}
|
|
145
146
|
return { role: m.role, content: m.content || " " };
|
|
146
147
|
}),
|
|
147
|
-
max_tokens: options.maxTokens
|
|
148
|
+
max_tokens: clampMaxTokens(model, options.maxTokens)
|
|
148
149
|
};
|
|
149
150
|
if (options.tools && options.tools.length > 0) body.tools = options.tools;
|
|
150
151
|
if (options.temperature !== void 0) body.temperature = options.temperature;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/providers/openai_compat.ts"],"sourcesContent":["/**\n * TITAN — Generic OpenAI-Compatible Provider\n * A single provider class that works with any OpenAI-compatible API endpoint.\n * Used by: Groq, Mistral, OpenRouter, Fireworks, xAI, Together, DeepSeek,\n * Cerebras, Cohere, Perplexity, and any custom provider.\n */\nimport {\n LLMProvider,\n type ChatOptions,\n type ChatResponse,\n type ChatStreamChunk,\n type ToolCall,\n} from './base.js';\nimport { loadConfig } from '../config/config.js';\nimport type { ProviderConfig } from '../config/schema.js';\nimport logger from '../utils/logger.js';\nimport { fetchWithRetry } from '../utils/helpers.js';\nimport { resolveApiKey } from './authResolver.js';\nimport { v4 as uuid } from 'uuid';\n\n/** Configuration for an OpenAI-compatible provider */\nexport interface OpenAICompatConfig {\n /** Internal provider name (e.g. 'groq') */\n name: string;\n /** Display name shown to users (e.g. 'Groq (Fast Inference)') */\n displayName: string;\n /** Default API base URL */\n defaultBaseUrl: string;\n /** Environment variable name for the API key */\n envKey: string;\n /** Config key name in titan.json providers section */\n configKey: string;\n /** Default model ID */\n defaultModel: string;\n /** Static model list (returned when health check fails) */\n knownModels: string[];\n /** Extra headers to send with every request */\n extraHeaders?: Record<string, string>;\n /** Whether to fetch models from /v1/models endpoint */\n supportsModelList?: boolean;\n /** Keep org/ prefix in model name (e.g. NIM API expects 'nvidia/model-name') */\n keepModelPrefix?: boolean;\n}\n\nexport class OpenAICompatProvider extends LLMProvider {\n readonly name: string;\n readonly displayName: string;\n private readonly config: OpenAICompatConfig;\n\n constructor(config: OpenAICompatConfig) {\n super();\n this.name = config.name;\n this.displayName = config.displayName;\n this.config = config;\n }\n\n private get apiKey(): string {\n const cfg = loadConfig();\n const providerCfg = (cfg.providers as Record<string, ProviderConfig>)[this.config.configKey];\n return resolveApiKey(this.config.name, providerCfg?.authProfiles || [], providerCfg?.apiKey || '', this.config.envKey, providerCfg?.rotationStrategy, providerCfg?.credentialCooldownMs);\n }\n\n private get baseUrl(): string {\n const cfg = loadConfig();\n const providerCfg = (cfg.providers as Record<string, ProviderConfig>)[this.config.configKey];\n return providerCfg?.baseUrl || this.config.defaultBaseUrl;\n }\n\n /** Sanitize messages for strict APIs (e.g., NIM) that reject empty content strings */\n private sanitizeMessages(messages: ChatOptions['messages']): ChatOptions['messages'] {\n return messages.map(m => ({\n ...m,\n content: m.content || (m.role === 'assistant' && m.toolCalls ? '' : ' '),\n }));\n }\n\n async chat(options: ChatOptions): Promise<ChatResponse> {\n const rawModel = options.model || this.config.defaultModel;\n // NIM API requires org/model format — keep prefix as-is or add it\n const model = this.config.keepModelPrefix\n ? (rawModel.includes('/') ? rawModel : `${this.name}/${rawModel}`)\n : rawModel.replace(`${this.name}/`, '');\n const apiKey = this.apiKey;\n if (!apiKey) throw new Error(`${this.displayName} API key not configured (set ${this.config.envKey} or providers.${this.config.configKey}.apiKey)`);\n\n logger.debug(this.name, `Chat request: model=${model}, messages=${options.messages.length}`);\n\n const sanitized = this.sanitizeMessages(options.messages);\n const body: Record<string, unknown> = {\n model,\n messages: sanitized.map((m) => {\n if (m.role === 'tool') {\n return { role: 'tool', content: m.content || ' ', tool_call_id: m.toolCallId };\n }\n if (m.role === 'assistant' && m.toolCalls) {\n return {\n role: 'assistant',\n content: m.content || null,\n tool_calls: m.toolCalls.map((tc) => ({\n id: tc.id,\n type: 'function',\n function: { name: tc.function.name, arguments: tc.function.arguments },\n })),\n };\n }\n return { role: m.role, content: m.content || ' ' };\n }),\n max_tokens: options.maxTokens || 8192,\n };\n\n if (options.tools && options.tools.length > 0) {\n body.tools = options.tools;\n }\n\n if (options.temperature !== undefined) {\n body.temperature = options.temperature;\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n ...(this.config.extraHeaders || {}),\n };\n\n const response = await fetchWithRetry(`${this.baseUrl}/chat/completions`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n // Hunt Finding #37: attach status + Retry-After so the router can respect backoff\n const { createProviderError } = await import('./errorTaxonomy.js');\n throw createProviderError(`${this.displayName} API`, response, errorText, { provider: this.name, model });\n }\n\n const data = await response.json() as Record<string, unknown>;\n const choices = data.choices as Array<Record<string, unknown>> | undefined;\n\n if (!choices || choices.length === 0) {\n return {\n id: (data.id as string) || uuid(),\n content: '',\n usage: undefined,\n finishReason: 'stop',\n model: model.includes('/') ? model : `${this.name}/${model}`,\n };\n }\n\n const choice = choices[0];\n const message = choice.message as Record<string, unknown>;\n\n const toolCalls: ToolCall[] = [];\n if (message.tool_calls) {\n for (const tc of message.tool_calls as Array<Record<string, unknown>>) {\n const fn = tc.function as Record<string, string>;\n toolCalls.push({\n id: (tc.id as string) || uuid(),\n type: 'function',\n function: { name: fn.name, arguments: fn.arguments },\n });\n }\n }\n\n const usage = data.usage as { prompt_tokens: number; completion_tokens: number; total_tokens: number } | undefined;\n\n return {\n id: (data.id as string) || uuid(),\n content: (message.content as string) || '',\n toolCalls: toolCalls.length > 0 ? toolCalls : undefined,\n usage: usage\n ? {\n promptTokens: usage.prompt_tokens,\n completionTokens: usage.completion_tokens,\n totalTokens: usage.total_tokens,\n }\n : undefined,\n finishReason: toolCalls.length > 0 ? 'tool_calls' : (choice.finish_reason as 'stop' | 'length') || 'stop',\n model: model.includes('/') ? model : `${this.name}/${model}`,\n };\n }\n\n async *chatStream(options: ChatOptions): AsyncGenerator<ChatStreamChunk> {\n const rawModel = options.model || this.config.defaultModel;\n const model = this.config.keepModelPrefix\n ? (rawModel.includes('/') ? rawModel : `${this.name}/${rawModel}`)\n : rawModel.replace(`${this.name}/`, '');\n const apiKey = this.apiKey;\n if (!apiKey) { yield { type: 'error', error: `${this.displayName} API key not configured` }; return; }\n\n const sanitized = this.sanitizeMessages(options.messages);\n const body: Record<string, unknown> = {\n model,\n stream: true,\n messages: sanitized.map((m) => {\n if (m.role === 'tool') return { role: 'tool', content: m.content || ' ', tool_call_id: m.toolCallId };\n if (m.role === 'assistant' && m.toolCalls) {\n return {\n role: 'assistant', content: m.content || null,\n tool_calls: m.toolCalls.map((tc) => ({ id: tc.id, type: 'function', function: { name: tc.function.name, arguments: tc.function.arguments } })),\n };\n }\n return { role: m.role, content: m.content || ' ' };\n }),\n max_tokens: options.maxTokens || 8192,\n };\n if (options.tools && options.tools.length > 0) body.tools = options.tools;\n if (options.temperature !== undefined) body.temperature = options.temperature;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n ...(this.config.extraHeaders || {}),\n };\n\n try {\n const response = await fetch(`${this.baseUrl}/chat/completions`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok || !response.body) {\n const errorText = await response.text();\n yield { type: 'error', error: `${this.displayName} API error (${response.status}): ${errorText}` };\n return;\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n const toolCalls = new Map<number, { id: string; name: string; args: string }>();\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const json = line.slice(6).trim();\n if (json === '[DONE]' || !json) continue;\n try {\n const chunk = JSON.parse(json);\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) continue;\n if (delta.content) yield { type: 'text', content: delta.content };\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCalls.has(idx)) toolCalls.set(idx, { id: tc.id || '', name: '', args: '' });\n const entry = toolCalls.get(idx)!;\n if (tc.id) entry.id = tc.id;\n if (tc.function?.name) entry.name = tc.function.name;\n if (tc.function?.arguments) entry.args += tc.function.arguments;\n }\n }\n } catch { /* skip malformed SSE lines */ }\n }\n }\n\n for (const [, tc] of toolCalls) {\n if (tc.id && tc.name) {\n yield { type: 'tool_call', toolCall: { id: tc.id, type: 'function', function: { name: tc.name, arguments: tc.args || '{}' } } };\n }\n }\n yield { type: 'done' };\n } catch (error) {\n yield { type: 'error', error: (error as Error).message };\n }\n }\n\n async listModels(): Promise<string[]> {\n if (!this.config.supportsModelList || !this.apiKey) {\n return this.config.knownModels;\n }\n try {\n const response = await fetch(`${this.baseUrl}/models`, {\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n ...(this.config.extraHeaders || {}),\n },\n signal: AbortSignal.timeout(5000),\n });\n if (!response.ok) return this.config.knownModels;\n const data = await response.json() as { data?: Array<{ id: string }> };\n return (data.data || []).map((m) => m.id);\n } catch {\n return this.config.knownModels;\n }\n }\n\n async healthCheck(): Promise<boolean> {\n try {\n if (!this.apiKey) return false;\n const response = await fetch(`${this.baseUrl}/models`, {\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n ...(this.config.extraHeaders || {}),\n },\n signal: AbortSignal.timeout(5000),\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n}\n\n// ── Provider Presets ──────────────────────────────────────────────\n\nexport const PROVIDER_PRESETS: OpenAICompatConfig[] = [\n {\n name: 'groq',\n displayName: 'Groq (Fast Inference)',\n defaultBaseUrl: 'https://api.groq.com/openai/v1',\n envKey: 'GROQ_API_KEY',\n configKey: 'groq',\n defaultModel: 'llama-3.3-70b-versatile',\n knownModels: ['llama-3.3-70b-versatile', 'llama-3.1-8b-instant', 'mixtral-8x7b-32768', 'gemma2-9b-it', 'deepseek-r1-distill-llama-70b'],\n supportsModelList: true,\n },\n {\n name: 'mistral',\n displayName: 'Mistral AI',\n defaultBaseUrl: 'https://api.mistral.ai/v1',\n envKey: 'MISTRAL_API_KEY',\n configKey: 'mistral',\n defaultModel: 'mistral-small-latest',\n knownModels: ['mistral-large-latest', 'mistral-medium-latest', 'mistral-small-latest', 'codestral-latest', 'mistral-nemo'],\n supportsModelList: true,\n },\n {\n name: 'fireworks',\n displayName: 'Fireworks AI',\n defaultBaseUrl: 'https://api.fireworks.ai/inference/v1',\n envKey: 'FIREWORKS_API_KEY',\n configKey: 'fireworks',\n defaultModel: 'accounts/fireworks/models/llama-v3p3-70b-instruct',\n knownModels: ['accounts/fireworks/models/llama-v3p3-70b-instruct', 'accounts/fireworks/models/mixtral-8x7b-instruct', 'accounts/fireworks/models/qwen3-8b'],\n supportsModelList: true,\n },\n {\n name: 'xai',\n displayName: 'xAI (Grok)',\n defaultBaseUrl: 'https://api.x.ai/v1',\n envKey: 'XAI_API_KEY',\n configKey: 'xai',\n defaultModel: 'grok-3-fast',\n knownModels: ['grok-3', 'grok-3-fast', 'grok-3-mini', 'grok-3-mini-fast'],\n supportsModelList: true,\n },\n {\n name: 'together',\n displayName: 'Together AI',\n defaultBaseUrl: 'https://api.together.xyz/v1',\n envKey: 'TOGETHER_API_KEY',\n configKey: 'together',\n defaultModel: 'meta-llama/Llama-3.3-70B-Instruct-Turbo',\n knownModels: ['meta-llama/Llama-3.3-70B-Instruct-Turbo', 'deepseek-ai/DeepSeek-R1', 'Qwen/Qwen2.5-72B-Instruct-Turbo', 'mistralai/Mixtral-8x7B-Instruct-v0.1'],\n supportsModelList: true,\n },\n {\n name: 'deepseek',\n displayName: 'DeepSeek',\n defaultBaseUrl: 'https://api.deepseek.com/v1',\n envKey: 'DEEPSEEK_API_KEY',\n configKey: 'deepseek',\n defaultModel: 'deepseek-chat',\n knownModels: ['deepseek-chat', 'deepseek-reasoner'],\n supportsModelList: false,\n },\n {\n name: 'cerebras',\n displayName: 'Cerebras (Ultra-Fast)',\n defaultBaseUrl: 'https://api.cerebras.ai/v1',\n envKey: 'CEREBRAS_API_KEY',\n configKey: 'cerebras',\n defaultModel: 'llama-3.3-70b',\n knownModels: ['llama-3.3-70b', 'llama-3.1-8b', 'qwen-3-32b'],\n supportsModelList: true,\n },\n {\n name: 'cohere',\n displayName: 'Cohere',\n defaultBaseUrl: 'https://api.cohere.com/compatibility/v1',\n envKey: 'COHERE_API_KEY',\n configKey: 'cohere',\n defaultModel: 'command-r-plus',\n knownModels: ['command-r-plus', 'command-r', 'command-r7b'],\n supportsModelList: false,\n },\n {\n name: 'perplexity',\n displayName: 'Perplexity (Search-Augmented)',\n defaultBaseUrl: 'https://api.perplexity.ai',\n envKey: 'PERPLEXITY_API_KEY',\n configKey: 'perplexity',\n defaultModel: 'sonar',\n knownModels: ['sonar', 'sonar-pro', 'sonar-reasoning'],\n supportsModelList: false,\n },\n {\n name: 'venice',\n displayName: 'Venice AI (Privacy-First)',\n defaultBaseUrl: 'https://api.venice.ai/api/v1',\n envKey: 'VENICE_API_KEY',\n configKey: 'venice',\n defaultModel: 'llama-3.3-70b',\n knownModels: ['llama-3.3-70b', 'deepseek-r1-671b', 'qwen-2.5-vl-72b'],\n supportsModelList: true,\n },\n {\n name: 'bedrock',\n displayName: 'AWS Bedrock (via Proxy)',\n defaultBaseUrl: 'http://localhost:4000/v1',\n envKey: 'AWS_BEDROCK_API_KEY',\n configKey: 'bedrock',\n defaultModel: 'anthropic.claude-3-5-sonnet-20241022-v2:0',\n knownModels: ['anthropic.claude-3-5-sonnet-20241022-v2:0', 'amazon.titan-text-premier-v1:0', 'meta.llama3-70b-instruct-v1:0'],\n supportsModelList: false,\n },\n {\n name: 'litellm',\n displayName: 'LiteLLM (Universal Proxy)',\n defaultBaseUrl: 'http://localhost:4000/v1',\n envKey: 'LITELLM_API_KEY',\n configKey: 'litellm',\n defaultModel: 'gpt-4o',\n knownModels: ['gpt-4o', 'claude-sonnet-4-20250514', 'gemini-2.5-flash'],\n supportsModelList: true,\n },\n // NOTE: Azure OpenAI uses custom endpoints (https://{resource}.openai.azure.com/openai/deployments/{model})\n // and requires api-version query param + api-key header instead of Bearer token.\n // Users must configure baseUrl to their Azure deployment endpoint.\n {\n name: 'azure',\n displayName: 'Azure OpenAI (Enterprise)',\n defaultBaseUrl: '',\n envKey: 'AZURE_OPENAI_API_KEY',\n configKey: 'azure',\n defaultModel: 'gpt-4o',\n knownModels: ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1-preview'],\n supportsModelList: false,\n },\n {\n name: 'deepinfra',\n displayName: 'DeepInfra (Fast Inference)',\n defaultBaseUrl: 'https://api.deepinfra.com/v1/openai',\n envKey: 'DEEPINFRA_API_KEY',\n configKey: 'deepinfra',\n defaultModel: 'meta-llama/Llama-3.3-70B-Instruct',\n knownModels: ['meta-llama/Llama-3.3-70B-Instruct', 'mistralai/Mixtral-8x22B-Instruct-v0.1', 'Qwen/Qwen2.5-72B-Instruct', 'deepseek-ai/DeepSeek-R1'],\n supportsModelList: true,\n },\n {\n name: 'sambanova',\n displayName: 'SambaNova (Fast Inference)',\n defaultBaseUrl: 'https://api.sambanova.ai/v1',\n envKey: 'SAMBANOVA_API_KEY',\n configKey: 'sambanova',\n defaultModel: 'Meta-Llama-3.3-70B-Instruct',\n knownModels: ['Meta-Llama-3.3-70B-Instruct', 'DeepSeek-R1-Distill-Llama-70B', 'Qwen2.5-72B-Instruct'],\n supportsModelList: true,\n },\n {\n name: 'kimi',\n displayName: 'Kimi (Moonshot)',\n defaultBaseUrl: 'https://api.moonshot.cn/v1',\n envKey: 'MOONSHOT_API_KEY',\n configKey: 'kimi',\n defaultModel: 'kimi-k2.5',\n knownModels: ['kimi-k2.5', 'kimi-k2', 'moonshot-v1-8k', 'moonshot-v1-32k', 'moonshot-v1-128k'],\n supportsModelList: true,\n },\n {\n name: 'huggingface',\n displayName: 'Hugging Face Inference',\n defaultBaseUrl: 'https://api-inference.huggingface.co/v1',\n envKey: 'HF_API_KEY',\n configKey: 'huggingface',\n defaultModel: 'meta-llama/Llama-3.3-70B-Instruct',\n knownModels: ['meta-llama/Llama-3.3-70B-Instruct', 'mistralai/Mixtral-8x7B-Instruct-v0.1', 'Qwen/Qwen2.5-72B-Instruct', 'microsoft/Phi-3-medium-4k-instruct'],\n supportsModelList: true,\n },\n {\n name: 'ai21',\n displayName: 'AI21 Labs (Jamba)',\n defaultBaseUrl: 'https://api.ai21.com/studio/v1',\n envKey: 'AI21_API_KEY',\n configKey: 'ai21',\n defaultModel: 'jamba-1.5-large',\n knownModels: ['jamba-1.5-large', 'jamba-1.5-mini', 'jamba-instruct'],\n supportsModelList: false,\n },\n {\n name: 'cohere-v2',\n displayName: 'Cohere v2 (OpenAI-compat)',\n defaultBaseUrl: 'https://api.cohere.com/v2',\n envKey: 'COHERE_API_KEY',\n configKey: 'cohere-v2',\n defaultModel: 'command-a-03-2025',\n knownModels: ['command-a-03-2025', 'command-r-plus-08-2024', 'command-r-08-2024', 'command-r7b-12-2024'],\n supportsModelList: false,\n },\n {\n name: 'reka',\n displayName: 'Reka AI',\n defaultBaseUrl: 'https://api.reka.ai/v1',\n envKey: 'REKA_API_KEY',\n configKey: 'reka',\n defaultModel: 'reka-core',\n knownModels: ['reka-core', 'reka-flash', 'reka-edge'],\n supportsModelList: false,\n },\n {\n name: 'zhipu',\n displayName: 'Zhipu GLM',\n defaultBaseUrl: 'https://open.bigmodel.cn/api/paas/v4',\n envKey: 'ZHIPU_API_KEY',\n configKey: 'zhipu',\n defaultModel: 'glm-4-plus',\n knownModels: ['glm-4-plus', 'glm-4', 'glm-4-flash', 'glm-4-long'],\n supportsModelList: false,\n },\n {\n name: 'yi',\n displayName: '01.AI (Yi)',\n defaultBaseUrl: 'https://api.01.ai/v1',\n envKey: 'YI_API_KEY',\n configKey: 'yi',\n defaultModel: 'yi-large',\n knownModels: ['yi-large', 'yi-medium', 'yi-spark', 'yi-large-turbo'],\n supportsModelList: true,\n },\n {\n name: 'inflection',\n displayName: 'Inflection (Pi)',\n defaultBaseUrl: 'https://api.inflection.ai/v1',\n envKey: 'INFLECTION_API_KEY',\n configKey: 'inflection',\n defaultModel: 'inflection-3-pi',\n knownModels: ['inflection-3-pi', 'inflection-3-productivity'],\n supportsModelList: false,\n },\n {\n name: 'novita',\n displayName: 'Novita AI',\n defaultBaseUrl: 'https://api.novita.ai/v3/openai',\n envKey: 'NOVITA_API_KEY',\n configKey: 'novita',\n defaultModel: 'meta-llama/llama-3.3-70b-instruct',\n knownModels: ['meta-llama/llama-3.3-70b-instruct', 'deepseek/deepseek-r1', 'qwen/qwen-2.5-72b-instruct', 'mistralai/mistral-large-2411'],\n supportsModelList: true,\n },\n {\n name: 'replicate',\n displayName: 'Replicate',\n defaultBaseUrl: 'https://api.replicate.com/v1',\n envKey: 'REPLICATE_API_KEY',\n configKey: 'replicate',\n defaultModel: 'meta/meta-llama-3-70b-instruct',\n knownModels: ['meta/meta-llama-3-70b-instruct', 'mistralai/mixtral-8x7b-instruct-v0.1', 'meta/meta-llama-3.1-405b-instruct'],\n supportsModelList: false,\n },\n {\n name: 'lepton',\n displayName: 'Lepton AI',\n defaultBaseUrl: 'https://llama3-3-70b.lepton.run/api/v1',\n envKey: 'LEPTON_API_KEY',\n configKey: 'lepton',\n defaultModel: 'llama-3.3-70b',\n knownModels: ['llama-3.3-70b', 'mixtral-8x7b', 'qwen2.5-72b'],\n supportsModelList: false,\n },\n {\n name: 'anyscale',\n displayName: 'Anyscale Endpoints',\n defaultBaseUrl: 'https://api.endpoints.anyscale.com/v1',\n envKey: 'ANYSCALE_API_KEY',\n configKey: 'anyscale',\n defaultModel: 'meta-llama/Meta-Llama-3-70B-Instruct',\n knownModels: ['meta-llama/Meta-Llama-3-70B-Instruct', 'mistralai/Mixtral-8x7B-Instruct-v0.1', 'codellama/CodeLlama-70b-Instruct-hf'],\n supportsModelList: true,\n },\n {\n name: 'octo',\n displayName: 'OctoAI',\n defaultBaseUrl: 'https://text.octoai.run/v1',\n envKey: 'OCTOAI_API_KEY',\n configKey: 'octo',\n defaultModel: 'meta-llama-3.1-70b-instruct',\n knownModels: ['meta-llama-3.1-70b-instruct', 'mixtral-8x7b-instruct', 'qwen2.5-72b-instruct'],\n supportsModelList: true,\n },\n {\n name: 'nous',\n displayName: 'Nous Research (Hermes)',\n defaultBaseUrl: 'https://inference-api.nousresearch.com/v1',\n envKey: 'NOUS_API_KEY',\n configKey: 'nous',\n defaultModel: 'hermes-3-llama-3.1-70b',\n knownModels: ['hermes-3-llama-3.1-70b', 'hermes-3-llama-3.1-8b', 'hermes-2-pro-mistral-7b'],\n supportsModelList: false,\n },\n {\n name: 'openrouter',\n displayName: 'OpenRouter (Universal Gateway)',\n defaultBaseUrl: 'https://openrouter.ai/api/v1',\n envKey: 'OPENROUTER_API_KEY',\n configKey: 'openrouter',\n defaultModel: 'anthropic/claude-3.5-sonnet',\n knownModels: [\n 'anthropic/claude-3.5-sonnet',\n 'anthropic/claude-3.5-haiku',\n 'anthropic/claude-3-opus',\n 'openai/gpt-4o',\n 'openai/gpt-4o-mini',\n 'meta-llama/llama-3.3-70b-instruct',\n 'google/gemini-2.5-flash-preview',\n 'deepseek/deepseek-chat',\n 'deepseek/deepseek-r1',\n 'x-ai/grok-3-beta',\n 'nvidia/llama-3.1-nemotron-70b-instruct',\n ],\n supportsModelList: true,\n extraHeaders: {\n 'HTTP-Referer': 'https://titan.local',\n 'X-Title': 'TITAN',\n },\n },\n {\n name: 'nvidia',\n displayName: 'NVIDIA NIM',\n defaultBaseUrl: 'https://integrate.api.nvidia.com/v1',\n envKey: 'NVIDIA_API_KEY',\n configKey: 'nvidia',\n defaultModel: 'nvidia/llama-3.3-nemotron-super-49b-v1',\n knownModels: [\n 'nvidia/llama-3.3-nemotron-super-49b-v1',\n 'nvidia/llama-3.3-nemotron-super-49b-v1.5',\n 'nvidia/llama-3.1-nemotron-ultra-253b-v1',\n 'nvidia/llama-3.1-nemotron-70b-instruct',\n 'nvidia/nemotron-3-nano-30b-a3b',\n 'nvidia/nemotron-3-super-120b-a12b',\n ],\n supportsModelList: true,\n keepModelPrefix: true,\n },\n {\n name: 'minimax',\n displayName: 'MiniMax',\n defaultBaseUrl: 'https://api.minimax.chat/v1',\n envKey: 'MINIMAX_API_KEY',\n configKey: 'minimax',\n defaultModel: 'minimax-m2.7',\n knownModels: [\n 'minimax-m2.7',\n 'minimax-m2.7-highspeed',\n 'minimax-m2.5',\n 'minimax-01',\n 'minimax-text-01',\n ],\n supportsModelList: false,\n },\n];\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/providers/openai_compat.ts"],"sourcesContent":["/**\n * TITAN — Generic OpenAI-Compatible Provider\n * A single provider class that works with any OpenAI-compatible API endpoint.\n * Used by: Groq, Mistral, OpenRouter, Fireworks, xAI, Together, DeepSeek,\n * Cerebras, Cohere, Perplexity, and any custom provider.\n */\nimport {\n LLMProvider,\n type ChatOptions,\n type ChatResponse,\n type ChatStreamChunk,\n type ToolCall,\n} from './base.js';\nimport { loadConfig } from '../config/config.js';\nimport type { ProviderConfig } from '../config/schema.js';\nimport logger from '../utils/logger.js';\nimport { fetchWithRetry } from '../utils/helpers.js';\nimport { resolveApiKey } from './authResolver.js';\nimport { v4 as uuid } from 'uuid';\nimport { clampMaxTokens } from './modelCapabilities.js';\n\n/** Configuration for an OpenAI-compatible provider */\nexport interface OpenAICompatConfig {\n /** Internal provider name (e.g. 'groq') */\n name: string;\n /** Display name shown to users (e.g. 'Groq (Fast Inference)') */\n displayName: string;\n /** Default API base URL */\n defaultBaseUrl: string;\n /** Environment variable name for the API key */\n envKey: string;\n /** Config key name in titan.json providers section */\n configKey: string;\n /** Default model ID */\n defaultModel: string;\n /** Static model list (returned when health check fails) */\n knownModels: string[];\n /** Extra headers to send with every request */\n extraHeaders?: Record<string, string>;\n /** Whether to fetch models from /v1/models endpoint */\n supportsModelList?: boolean;\n /** Keep org/ prefix in model name (e.g. NIM API expects 'nvidia/model-name') */\n keepModelPrefix?: boolean;\n}\n\nexport class OpenAICompatProvider extends LLMProvider {\n readonly name: string;\n readonly displayName: string;\n private readonly config: OpenAICompatConfig;\n\n constructor(config: OpenAICompatConfig) {\n super();\n this.name = config.name;\n this.displayName = config.displayName;\n this.config = config;\n }\n\n private get apiKey(): string {\n const cfg = loadConfig();\n const providerCfg = (cfg.providers as Record<string, unknown>)[this.config.configKey] as ProviderConfig | undefined;\n return resolveApiKey(this.config.name, providerCfg?.authProfiles || [], providerCfg?.apiKey || '', this.config.envKey, providerCfg?.rotationStrategy, providerCfg?.credentialCooldownMs);\n }\n\n private get baseUrl(): string {\n const cfg = loadConfig();\n const providerCfg = (cfg.providers as Record<string, unknown>)[this.config.configKey] as ProviderConfig | undefined;\n return providerCfg?.baseUrl || this.config.defaultBaseUrl;\n }\n\n /** Sanitize messages for strict APIs (e.g., NIM) that reject empty content strings */\n private sanitizeMessages(messages: ChatOptions['messages']): ChatOptions['messages'] {\n return messages.map(m => ({\n ...m,\n content: m.content || (m.role === 'assistant' && m.toolCalls ? '' : ' '),\n }));\n }\n\n async chat(options: ChatOptions): Promise<ChatResponse> {\n const rawModel = options.model || this.config.defaultModel;\n // NIM API requires org/model format — keep prefix as-is or add it\n const model = this.config.keepModelPrefix\n ? (rawModel.includes('/') ? rawModel : `${this.name}/${rawModel}`)\n : rawModel.replace(`${this.name}/`, '');\n const apiKey = this.apiKey;\n if (!apiKey) throw new Error(`${this.displayName} API key not configured (set ${this.config.envKey} or providers.${this.config.configKey}.apiKey)`);\n\n logger.debug(this.name, `Chat request: model=${model}, messages=${options.messages.length}`);\n\n const sanitized = this.sanitizeMessages(options.messages);\n const body: Record<string, unknown> = {\n model,\n messages: sanitized.map((m) => {\n if (m.role === 'tool') {\n return { role: 'tool', content: m.content || ' ', tool_call_id: m.toolCallId };\n }\n if (m.role === 'assistant' && m.toolCalls) {\n return {\n role: 'assistant',\n content: m.content || null,\n tool_calls: m.toolCalls.map((tc) => ({\n id: tc.id,\n type: 'function',\n function: { name: tc.function.name, arguments: tc.function.arguments },\n })),\n };\n }\n return { role: m.role, content: m.content || ' ' };\n }),\n max_tokens: clampMaxTokens(model, options.maxTokens),\n };\n\n if (options.tools && options.tools.length > 0) {\n body.tools = options.tools;\n }\n\n if (options.temperature !== undefined) {\n body.temperature = options.temperature;\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n ...(this.config.extraHeaders || {}),\n };\n\n const response = await fetchWithRetry(`${this.baseUrl}/chat/completions`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n // Hunt Finding #37: attach status + Retry-After so the router can respect backoff\n const { createProviderError } = await import('./errorTaxonomy.js');\n throw createProviderError(`${this.displayName} API`, response, errorText, { provider: this.name, model });\n }\n\n const data = await response.json() as Record<string, unknown>;\n const choices = data.choices as Array<Record<string, unknown>> | undefined;\n\n if (!choices || choices.length === 0) {\n return {\n id: (data.id as string) || uuid(),\n content: '',\n usage: undefined,\n finishReason: 'stop',\n model: model.includes('/') ? model : `${this.name}/${model}`,\n };\n }\n\n const choice = choices[0];\n const message = choice.message as Record<string, unknown>;\n\n const toolCalls: ToolCall[] = [];\n if (message.tool_calls) {\n for (const tc of message.tool_calls as Array<Record<string, unknown>>) {\n const fn = tc.function as Record<string, string>;\n toolCalls.push({\n id: (tc.id as string) || uuid(),\n type: 'function',\n function: { name: fn.name, arguments: fn.arguments },\n });\n }\n }\n\n const usage = data.usage as { prompt_tokens: number; completion_tokens: number; total_tokens: number } | undefined;\n\n return {\n id: (data.id as string) || uuid(),\n content: (message.content as string) || '',\n toolCalls: toolCalls.length > 0 ? toolCalls : undefined,\n usage: usage\n ? {\n promptTokens: usage.prompt_tokens,\n completionTokens: usage.completion_tokens,\n totalTokens: usage.total_tokens,\n }\n : undefined,\n finishReason: toolCalls.length > 0 ? 'tool_calls' : (choice.finish_reason as 'stop' | 'length') || 'stop',\n model: model.includes('/') ? model : `${this.name}/${model}`,\n };\n }\n\n async *chatStream(options: ChatOptions): AsyncGenerator<ChatStreamChunk> {\n const rawModel = options.model || this.config.defaultModel;\n const model = this.config.keepModelPrefix\n ? (rawModel.includes('/') ? rawModel : `${this.name}/${rawModel}`)\n : rawModel.replace(`${this.name}/`, '');\n const apiKey = this.apiKey;\n if (!apiKey) { yield { type: 'error', error: `${this.displayName} API key not configured` }; return; }\n\n const sanitized = this.sanitizeMessages(options.messages);\n const body: Record<string, unknown> = {\n model,\n stream: true,\n messages: sanitized.map((m) => {\n if (m.role === 'tool') return { role: 'tool', content: m.content || ' ', tool_call_id: m.toolCallId };\n if (m.role === 'assistant' && m.toolCalls) {\n return {\n role: 'assistant', content: m.content || null,\n tool_calls: m.toolCalls.map((tc) => ({ id: tc.id, type: 'function', function: { name: tc.function.name, arguments: tc.function.arguments } })),\n };\n }\n return { role: m.role, content: m.content || ' ' };\n }),\n max_tokens: clampMaxTokens(model, options.maxTokens),\n };\n if (options.tools && options.tools.length > 0) body.tools = options.tools;\n if (options.temperature !== undefined) body.temperature = options.temperature;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n ...(this.config.extraHeaders || {}),\n };\n\n try {\n const response = await fetch(`${this.baseUrl}/chat/completions`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok || !response.body) {\n const errorText = await response.text();\n yield { type: 'error', error: `${this.displayName} API error (${response.status}): ${errorText}` };\n return;\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n const toolCalls = new Map<number, { id: string; name: string; args: string }>();\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const json = line.slice(6).trim();\n if (json === '[DONE]' || !json) continue;\n try {\n const chunk = JSON.parse(json);\n const delta = chunk.choices?.[0]?.delta;\n if (!delta) continue;\n if (delta.content) yield { type: 'text', content: delta.content };\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n if (!toolCalls.has(idx)) toolCalls.set(idx, { id: tc.id || '', name: '', args: '' });\n const entry = toolCalls.get(idx)!;\n if (tc.id) entry.id = tc.id;\n if (tc.function?.name) entry.name = tc.function.name;\n if (tc.function?.arguments) entry.args += tc.function.arguments;\n }\n }\n } catch { /* skip malformed SSE lines */ }\n }\n }\n\n for (const [, tc] of toolCalls) {\n if (tc.id && tc.name) {\n yield { type: 'tool_call', toolCall: { id: tc.id, type: 'function', function: { name: tc.name, arguments: tc.args || '{}' } } };\n }\n }\n yield { type: 'done' };\n } catch (error) {\n yield { type: 'error', error: (error as Error).message };\n }\n }\n\n async listModels(): Promise<string[]> {\n if (!this.config.supportsModelList || !this.apiKey) {\n return this.config.knownModels;\n }\n try {\n const response = await fetch(`${this.baseUrl}/models`, {\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n ...(this.config.extraHeaders || {}),\n },\n signal: AbortSignal.timeout(5000),\n });\n if (!response.ok) return this.config.knownModels;\n const data = await response.json() as { data?: Array<{ id: string }> };\n return (data.data || []).map((m) => m.id);\n } catch {\n return this.config.knownModels;\n }\n }\n\n async healthCheck(): Promise<boolean> {\n try {\n if (!this.apiKey) return false;\n const response = await fetch(`${this.baseUrl}/models`, {\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n ...(this.config.extraHeaders || {}),\n },\n signal: AbortSignal.timeout(5000),\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n}\n\n// ── Provider Presets ──────────────────────────────────────────────\n\nexport const PROVIDER_PRESETS: OpenAICompatConfig[] = [\n {\n name: 'groq',\n displayName: 'Groq (Fast Inference)',\n defaultBaseUrl: 'https://api.groq.com/openai/v1',\n envKey: 'GROQ_API_KEY',\n configKey: 'groq',\n defaultModel: 'llama-3.3-70b-versatile',\n knownModels: ['llama-3.3-70b-versatile', 'llama-3.1-8b-instant', 'mixtral-8x7b-32768', 'gemma2-9b-it', 'deepseek-r1-distill-llama-70b'],\n supportsModelList: true,\n },\n {\n name: 'mistral',\n displayName: 'Mistral AI',\n defaultBaseUrl: 'https://api.mistral.ai/v1',\n envKey: 'MISTRAL_API_KEY',\n configKey: 'mistral',\n defaultModel: 'mistral-small-latest',\n knownModels: ['mistral-large-latest', 'mistral-medium-latest', 'mistral-small-latest', 'codestral-latest', 'mistral-nemo'],\n supportsModelList: true,\n },\n {\n name: 'fireworks',\n displayName: 'Fireworks AI',\n defaultBaseUrl: 'https://api.fireworks.ai/inference/v1',\n envKey: 'FIREWORKS_API_KEY',\n configKey: 'fireworks',\n defaultModel: 'accounts/fireworks/models/llama-v3p3-70b-instruct',\n knownModels: ['accounts/fireworks/models/llama-v3p3-70b-instruct', 'accounts/fireworks/models/mixtral-8x7b-instruct', 'accounts/fireworks/models/qwen3-8b'],\n supportsModelList: true,\n },\n {\n name: 'xai',\n displayName: 'xAI (Grok)',\n defaultBaseUrl: 'https://api.x.ai/v1',\n envKey: 'XAI_API_KEY',\n configKey: 'xai',\n defaultModel: 'grok-3-fast',\n knownModels: ['grok-3', 'grok-3-fast', 'grok-3-mini', 'grok-3-mini-fast'],\n supportsModelList: true,\n },\n {\n name: 'together',\n displayName: 'Together AI',\n defaultBaseUrl: 'https://api.together.xyz/v1',\n envKey: 'TOGETHER_API_KEY',\n configKey: 'together',\n defaultModel: 'meta-llama/Llama-3.3-70B-Instruct-Turbo',\n knownModels: ['meta-llama/Llama-3.3-70B-Instruct-Turbo', 'deepseek-ai/DeepSeek-R1', 'Qwen/Qwen2.5-72B-Instruct-Turbo', 'mistralai/Mixtral-8x7B-Instruct-v0.1'],\n supportsModelList: true,\n },\n {\n name: 'deepseek',\n displayName: 'DeepSeek',\n defaultBaseUrl: 'https://api.deepseek.com/v1',\n envKey: 'DEEPSEEK_API_KEY',\n configKey: 'deepseek',\n defaultModel: 'deepseek-chat',\n knownModels: ['deepseek-chat', 'deepseek-reasoner'],\n supportsModelList: false,\n },\n {\n name: 'cerebras',\n displayName: 'Cerebras (Ultra-Fast)',\n defaultBaseUrl: 'https://api.cerebras.ai/v1',\n envKey: 'CEREBRAS_API_KEY',\n configKey: 'cerebras',\n defaultModel: 'llama-3.3-70b',\n knownModels: ['llama-3.3-70b', 'llama-3.1-8b', 'qwen-3-32b'],\n supportsModelList: true,\n },\n {\n name: 'cohere',\n displayName: 'Cohere',\n defaultBaseUrl: 'https://api.cohere.com/compatibility/v1',\n envKey: 'COHERE_API_KEY',\n configKey: 'cohere',\n defaultModel: 'command-r-plus',\n knownModels: ['command-r-plus', 'command-r', 'command-r7b'],\n supportsModelList: false,\n },\n {\n name: 'perplexity',\n displayName: 'Perplexity (Search-Augmented)',\n defaultBaseUrl: 'https://api.perplexity.ai',\n envKey: 'PERPLEXITY_API_KEY',\n configKey: 'perplexity',\n defaultModel: 'sonar',\n knownModels: ['sonar', 'sonar-pro', 'sonar-reasoning'],\n supportsModelList: false,\n },\n {\n name: 'venice',\n displayName: 'Venice AI (Privacy-First)',\n defaultBaseUrl: 'https://api.venice.ai/api/v1',\n envKey: 'VENICE_API_KEY',\n configKey: 'venice',\n defaultModel: 'llama-3.3-70b',\n knownModels: ['llama-3.3-70b', 'deepseek-r1-671b', 'qwen-2.5-vl-72b'],\n supportsModelList: true,\n },\n {\n name: 'bedrock',\n displayName: 'AWS Bedrock (via Proxy)',\n defaultBaseUrl: 'http://localhost:4000/v1',\n envKey: 'AWS_BEDROCK_API_KEY',\n configKey: 'bedrock',\n defaultModel: 'anthropic.claude-3-5-sonnet-20241022-v2:0',\n knownModels: ['anthropic.claude-3-5-sonnet-20241022-v2:0', 'amazon.titan-text-premier-v1:0', 'meta.llama3-70b-instruct-v1:0'],\n supportsModelList: false,\n },\n {\n name: 'litellm',\n displayName: 'LiteLLM (Universal Proxy)',\n defaultBaseUrl: 'http://localhost:4000/v1',\n envKey: 'LITELLM_API_KEY',\n configKey: 'litellm',\n defaultModel: 'gpt-4o',\n knownModels: ['gpt-4o', 'claude-sonnet-4-20250514', 'gemini-2.5-flash'],\n supportsModelList: true,\n },\n // NOTE: Azure OpenAI uses custom endpoints (https://{resource}.openai.azure.com/openai/deployments/{model})\n // and requires api-version query param + api-key header instead of Bearer token.\n // Users must configure baseUrl to their Azure deployment endpoint.\n {\n name: 'azure',\n displayName: 'Azure OpenAI (Enterprise)',\n defaultBaseUrl: '',\n envKey: 'AZURE_OPENAI_API_KEY',\n configKey: 'azure',\n defaultModel: 'gpt-4o',\n knownModels: ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1-preview'],\n supportsModelList: false,\n },\n {\n name: 'deepinfra',\n displayName: 'DeepInfra (Fast Inference)',\n defaultBaseUrl: 'https://api.deepinfra.com/v1/openai',\n envKey: 'DEEPINFRA_API_KEY',\n configKey: 'deepinfra',\n defaultModel: 'meta-llama/Llama-3.3-70B-Instruct',\n knownModels: ['meta-llama/Llama-3.3-70B-Instruct', 'mistralai/Mixtral-8x22B-Instruct-v0.1', 'Qwen/Qwen2.5-72B-Instruct', 'deepseek-ai/DeepSeek-R1'],\n supportsModelList: true,\n },\n {\n name: 'sambanova',\n displayName: 'SambaNova (Fast Inference)',\n defaultBaseUrl: 'https://api.sambanova.ai/v1',\n envKey: 'SAMBANOVA_API_KEY',\n configKey: 'sambanova',\n defaultModel: 'Meta-Llama-3.3-70B-Instruct',\n knownModels: ['Meta-Llama-3.3-70B-Instruct', 'DeepSeek-R1-Distill-Llama-70B', 'Qwen2.5-72B-Instruct'],\n supportsModelList: true,\n },\n {\n name: 'kimi',\n displayName: 'Kimi (Moonshot)',\n defaultBaseUrl: 'https://api.moonshot.cn/v1',\n envKey: 'MOONSHOT_API_KEY',\n configKey: 'kimi',\n defaultModel: 'kimi-k2.5',\n knownModels: ['kimi-k2.5', 'kimi-k2', 'moonshot-v1-8k', 'moonshot-v1-32k', 'moonshot-v1-128k'],\n supportsModelList: true,\n },\n {\n name: 'huggingface',\n displayName: 'Hugging Face Inference',\n defaultBaseUrl: 'https://api-inference.huggingface.co/v1',\n envKey: 'HF_API_KEY',\n configKey: 'huggingface',\n defaultModel: 'meta-llama/Llama-3.3-70B-Instruct',\n knownModels: ['meta-llama/Llama-3.3-70B-Instruct', 'mistralai/Mixtral-8x7B-Instruct-v0.1', 'Qwen/Qwen2.5-72B-Instruct', 'microsoft/Phi-3-medium-4k-instruct'],\n supportsModelList: true,\n },\n {\n name: 'ai21',\n displayName: 'AI21 Labs (Jamba)',\n defaultBaseUrl: 'https://api.ai21.com/studio/v1',\n envKey: 'AI21_API_KEY',\n configKey: 'ai21',\n defaultModel: 'jamba-1.5-large',\n knownModels: ['jamba-1.5-large', 'jamba-1.5-mini', 'jamba-instruct'],\n supportsModelList: false,\n },\n {\n name: 'cohere-v2',\n displayName: 'Cohere v2 (OpenAI-compat)',\n defaultBaseUrl: 'https://api.cohere.com/v2',\n envKey: 'COHERE_API_KEY',\n configKey: 'cohere-v2',\n defaultModel: 'command-a-03-2025',\n knownModels: ['command-a-03-2025', 'command-r-plus-08-2024', 'command-r-08-2024', 'command-r7b-12-2024'],\n supportsModelList: false,\n },\n {\n name: 'reka',\n displayName: 'Reka AI',\n defaultBaseUrl: 'https://api.reka.ai/v1',\n envKey: 'REKA_API_KEY',\n configKey: 'reka',\n defaultModel: 'reka-core',\n knownModels: ['reka-core', 'reka-flash', 'reka-edge'],\n supportsModelList: false,\n },\n {\n name: 'zhipu',\n displayName: 'Zhipu GLM',\n defaultBaseUrl: 'https://open.bigmodel.cn/api/paas/v4',\n envKey: 'ZHIPU_API_KEY',\n configKey: 'zhipu',\n defaultModel: 'glm-4-plus',\n knownModels: ['glm-4-plus', 'glm-4', 'glm-4-flash', 'glm-4-long'],\n supportsModelList: false,\n },\n {\n name: 'yi',\n displayName: '01.AI (Yi)',\n defaultBaseUrl: 'https://api.01.ai/v1',\n envKey: 'YI_API_KEY',\n configKey: 'yi',\n defaultModel: 'yi-large',\n knownModels: ['yi-large', 'yi-medium', 'yi-spark', 'yi-large-turbo'],\n supportsModelList: true,\n },\n {\n name: 'inflection',\n displayName: 'Inflection (Pi)',\n defaultBaseUrl: 'https://api.inflection.ai/v1',\n envKey: 'INFLECTION_API_KEY',\n configKey: 'inflection',\n defaultModel: 'inflection-3-pi',\n knownModels: ['inflection-3-pi', 'inflection-3-productivity'],\n supportsModelList: false,\n },\n {\n name: 'novita',\n displayName: 'Novita AI',\n defaultBaseUrl: 'https://api.novita.ai/v3/openai',\n envKey: 'NOVITA_API_KEY',\n configKey: 'novita',\n defaultModel: 'meta-llama/llama-3.3-70b-instruct',\n knownModels: ['meta-llama/llama-3.3-70b-instruct', 'deepseek/deepseek-r1', 'qwen/qwen-2.5-72b-instruct', 'mistralai/mistral-large-2411'],\n supportsModelList: true,\n },\n {\n name: 'replicate',\n displayName: 'Replicate',\n defaultBaseUrl: 'https://api.replicate.com/v1',\n envKey: 'REPLICATE_API_KEY',\n configKey: 'replicate',\n defaultModel: 'meta/meta-llama-3-70b-instruct',\n knownModels: ['meta/meta-llama-3-70b-instruct', 'mistralai/mixtral-8x7b-instruct-v0.1', 'meta/meta-llama-3.1-405b-instruct'],\n supportsModelList: false,\n },\n {\n name: 'lepton',\n displayName: 'Lepton AI',\n defaultBaseUrl: 'https://llama3-3-70b.lepton.run/api/v1',\n envKey: 'LEPTON_API_KEY',\n configKey: 'lepton',\n defaultModel: 'llama-3.3-70b',\n knownModels: ['llama-3.3-70b', 'mixtral-8x7b', 'qwen2.5-72b'],\n supportsModelList: false,\n },\n {\n name: 'anyscale',\n displayName: 'Anyscale Endpoints',\n defaultBaseUrl: 'https://api.endpoints.anyscale.com/v1',\n envKey: 'ANYSCALE_API_KEY',\n configKey: 'anyscale',\n defaultModel: 'meta-llama/Meta-Llama-3-70B-Instruct',\n knownModels: ['meta-llama/Meta-Llama-3-70B-Instruct', 'mistralai/Mixtral-8x7B-Instruct-v0.1', 'codellama/CodeLlama-70b-Instruct-hf'],\n supportsModelList: true,\n },\n {\n name: 'octo',\n displayName: 'OctoAI',\n defaultBaseUrl: 'https://text.octoai.run/v1',\n envKey: 'OCTOAI_API_KEY',\n configKey: 'octo',\n defaultModel: 'meta-llama-3.1-70b-instruct',\n knownModels: ['meta-llama-3.1-70b-instruct', 'mixtral-8x7b-instruct', 'qwen2.5-72b-instruct'],\n supportsModelList: true,\n },\n {\n name: 'nous',\n displayName: 'Nous Research (Hermes)',\n defaultBaseUrl: 'https://inference-api.nousresearch.com/v1',\n envKey: 'NOUS_API_KEY',\n configKey: 'nous',\n defaultModel: 'hermes-3-llama-3.1-70b',\n knownModels: ['hermes-3-llama-3.1-70b', 'hermes-3-llama-3.1-8b', 'hermes-2-pro-mistral-7b'],\n supportsModelList: false,\n },\n {\n name: 'openrouter',\n displayName: 'OpenRouter (Universal Gateway)',\n defaultBaseUrl: 'https://openrouter.ai/api/v1',\n envKey: 'OPENROUTER_API_KEY',\n configKey: 'openrouter',\n defaultModel: 'anthropic/claude-3.5-sonnet',\n knownModels: [\n 'anthropic/claude-3.5-sonnet',\n 'anthropic/claude-3.5-haiku',\n 'anthropic/claude-3-opus',\n 'openai/gpt-4o',\n 'openai/gpt-4o-mini',\n 'meta-llama/llama-3.3-70b-instruct',\n 'google/gemini-2.5-flash-preview',\n 'deepseek/deepseek-chat',\n 'deepseek/deepseek-r1',\n 'x-ai/grok-3-beta',\n 'nvidia/llama-3.1-nemotron-70b-instruct',\n ],\n supportsModelList: true,\n extraHeaders: {\n 'HTTP-Referer': 'https://titan.local',\n 'X-Title': 'TITAN',\n },\n },\n {\n name: 'nvidia',\n displayName: 'NVIDIA NIM',\n defaultBaseUrl: 'https://integrate.api.nvidia.com/v1',\n envKey: 'NVIDIA_API_KEY',\n configKey: 'nvidia',\n defaultModel: 'nvidia/llama-3.3-nemotron-super-49b-v1',\n knownModels: [\n 'nvidia/llama-3.3-nemotron-super-49b-v1',\n 'nvidia/llama-3.3-nemotron-super-49b-v1.5',\n 'nvidia/llama-3.1-nemotron-ultra-253b-v1',\n 'nvidia/llama-3.1-nemotron-70b-instruct',\n 'nvidia/nemotron-3-nano-30b-a3b',\n 'nvidia/nemotron-3-super-120b-a12b',\n ],\n supportsModelList: true,\n keepModelPrefix: true,\n },\n {\n name: 'minimax',\n displayName: 'MiniMax',\n defaultBaseUrl: 'https://api.minimax.chat/v1',\n envKey: 'MINIMAX_API_KEY',\n configKey: 'minimax',\n defaultModel: 'minimax-m2.7',\n knownModels: [\n 'minimax-m2.7',\n 'minimax-m2.7-highspeed',\n 'minimax-m2.5',\n 'minimax-01',\n 'minimax-text-01',\n ],\n supportsModelList: false,\n },\n];\n"],"mappings":"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","names":[]}
|
package/dist/providers/router.js
CHANGED
|
@@ -409,33 +409,55 @@ async function tryFallbackChainStream(options, primaryModelId, originalError) {
|
|
|
409
409
|
if (attempts >= maxRetries) break;
|
|
410
410
|
if (fallbackModelId === primaryModelId) continue;
|
|
411
411
|
attempts++;
|
|
412
|
+
let fbProviderName;
|
|
413
|
+
let gen;
|
|
412
414
|
try {
|
|
413
415
|
const { provider: fbProvider, model: fbModel } = resolveModel(fallbackModelId);
|
|
414
|
-
|
|
416
|
+
fbProviderName = fbProvider.name;
|
|
415
417
|
if (!canRequest(fbProviderName, true)) {
|
|
416
418
|
const cb = getCircuitBreaker(fbProviderName);
|
|
417
419
|
logger.warn(COMPONENT, `Skipping stream fallback ${fallbackModelId} \u2014 circuit breaker OPEN (${cb.failureCount} failures)`);
|
|
418
420
|
continue;
|
|
419
421
|
}
|
|
420
422
|
logger.warn(COMPONENT, `Stream model ${primaryModelId} failed (${originalError.message}), falling back to ${fallbackModelId}`);
|
|
421
|
-
|
|
422
|
-
recordSuccess(fbProviderName);
|
|
423
|
-
lastFallbackEvent = {
|
|
424
|
-
primary: primaryModelId,
|
|
425
|
-
active: fallbackModelId,
|
|
426
|
-
reason: originalError.message,
|
|
427
|
-
timestamp: Date.now()
|
|
428
|
-
};
|
|
429
|
-
return gen;
|
|
423
|
+
gen = fbProvider.chatStream({ ...options, model: fbModel });
|
|
430
424
|
} catch (chainErr) {
|
|
431
425
|
try {
|
|
432
426
|
const { provider: fbProvider } = resolveModel(fallbackModelId);
|
|
433
427
|
recordFailure(fbProvider.name);
|
|
434
428
|
} catch {
|
|
435
429
|
}
|
|
436
|
-
logger.warn(COMPONENT, `Fallback stream model ${fallbackModelId}
|
|
430
|
+
logger.warn(COMPONENT, `Fallback stream model ${fallbackModelId} setup failed: ${chainErr.message}`);
|
|
437
431
|
continue;
|
|
438
432
|
}
|
|
433
|
+
lastFallbackEvent = {
|
|
434
|
+
primary: primaryModelId,
|
|
435
|
+
active: fallbackModelId,
|
|
436
|
+
reason: originalError.message,
|
|
437
|
+
timestamp: Date.now()
|
|
438
|
+
};
|
|
439
|
+
async function* monitored(inner, providerName) {
|
|
440
|
+
let recorded = false;
|
|
441
|
+
try {
|
|
442
|
+
for await (const chunk of inner) {
|
|
443
|
+
if (chunk.type === "error") {
|
|
444
|
+
if (!recorded) {
|
|
445
|
+
recordFailure(providerName);
|
|
446
|
+
recorded = true;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
yield chunk;
|
|
450
|
+
}
|
|
451
|
+
if (!recorded) recordSuccess(providerName);
|
|
452
|
+
} catch (innerErr) {
|
|
453
|
+
if (!recorded) {
|
|
454
|
+
recordFailure(providerName);
|
|
455
|
+
recorded = true;
|
|
456
|
+
}
|
|
457
|
+
throw innerErr;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return monitored(gen, fbProviderName);
|
|
439
461
|
}
|
|
440
462
|
return null;
|
|
441
463
|
}
|
|
@@ -686,11 +708,15 @@ async function* chatStream(options) {
|
|
|
686
708
|
}
|
|
687
709
|
let lastError = null;
|
|
688
710
|
const maxRetries = RETRY_CONFIG.maxRetries;
|
|
711
|
+
let fallbackChainAttempted = false;
|
|
712
|
+
let priorityFailoverAttempted = false;
|
|
689
713
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
690
714
|
try {
|
|
715
|
+
let recordedSuccess = false;
|
|
691
716
|
for await (const chunk of provider.chatStream({ ...options, model })) {
|
|
692
|
-
if (
|
|
717
|
+
if (!recordedSuccess && chunk.type !== "error" && attempt === 0) {
|
|
693
718
|
recordSuccess(providerName);
|
|
719
|
+
recordedSuccess = true;
|
|
694
720
|
}
|
|
695
721
|
lastFallbackEvent = null;
|
|
696
722
|
yield chunk;
|
|
@@ -710,11 +736,13 @@ async function* chatStream(options) {
|
|
|
710
736
|
const retryDelayMs = Math.max(classified.cooldownMs, calculateBackoffDelay(attempt));
|
|
711
737
|
logger.warn(COMPONENT, `${errorMsg} [${classified.reason}] \u2014 streaming retry in ${Math.round(retryDelayMs)}ms`);
|
|
712
738
|
yield {
|
|
713
|
-
type: "
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
739
|
+
type: "retry",
|
|
740
|
+
attempt: attempt + 1,
|
|
741
|
+
maxRetries,
|
|
742
|
+
reason: classified.reason,
|
|
743
|
+
provider: providerName,
|
|
744
|
+
model,
|
|
745
|
+
delayMs: Math.round(retryDelayMs)
|
|
718
746
|
};
|
|
719
747
|
await sleep(retryDelayMs);
|
|
720
748
|
continue;
|
|
@@ -724,7 +752,8 @@ async function* chatStream(options) {
|
|
|
724
752
|
} else {
|
|
725
753
|
logger.error(COMPONENT, `${errorMsg} \u2014 streaming max retries exceeded [${classified.reason}]`);
|
|
726
754
|
}
|
|
727
|
-
if (classified.retryable || classified.shouldFallback) {
|
|
755
|
+
if (!fallbackChainAttempted && (classified.retryable || classified.shouldFallback)) {
|
|
756
|
+
fallbackChainAttempted = true;
|
|
728
757
|
const chainStream = await tryFallbackChainStream(options, modelId, error);
|
|
729
758
|
if (chainStream) {
|
|
730
759
|
yield {
|
|
@@ -752,7 +781,8 @@ async function* chatStream(options) {
|
|
|
752
781
|
}
|
|
753
782
|
}
|
|
754
783
|
}
|
|
755
|
-
if (
|
|
784
|
+
if (!priorityFailoverAttempted) {
|
|
785
|
+
priorityFailoverAttempted = true;
|
|
756
786
|
const failoverOrder = getFailoverOrder(providerName);
|
|
757
787
|
let failedOver = false;
|
|
758
788
|
for (const fallbackName of failoverOrder) {
|
|
@@ -777,8 +807,20 @@ async function* chatStream(options) {
|
|
|
777
807
|
originalModel: model,
|
|
778
808
|
error: errorMsg
|
|
779
809
|
};
|
|
780
|
-
|
|
781
|
-
|
|
810
|
+
let recorded = false;
|
|
811
|
+
try {
|
|
812
|
+
for await (const chunk of fallback.chatStream({ ...options, model: preferred })) {
|
|
813
|
+
if (chunk.type === "error" && !recorded) {
|
|
814
|
+
recordFailure(fallbackName);
|
|
815
|
+
recorded = true;
|
|
816
|
+
}
|
|
817
|
+
yield chunk;
|
|
818
|
+
}
|
|
819
|
+
if (!recorded) recordSuccess(fallbackName);
|
|
820
|
+
} catch (innerErr) {
|
|
821
|
+
if (!recorded) recordFailure(fallbackName);
|
|
822
|
+
throw innerErr;
|
|
823
|
+
}
|
|
782
824
|
failedOver = true;
|
|
783
825
|
break;
|
|
784
826
|
} catch (fallbackErr) {
|