Repository navigation
feat: refresh AI/ML API integration and add usage attribution #1132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
33521a4
5f3936d
2908dd1
4ebbce9
09c504e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -179,7 +179,17 @@ export const openRouterApiModelKeys = [ | |
| 'openRouter_deepseek_v4_flash', | ||
| ] | ||
| export const aimlApiModelKeys = [ | ||
| 'aiml_claude_sonnet_4_6_20260218', | ||
| 'aiml_openai_gpt_6_1_sol', | ||
| 'aiml_openai_gpt_6_astra', | ||
| 'aiml_openai_gpt_6_luna', | ||
| 'aiml_claude_fable_5_1', | ||
| 'aiml_claude_opus_5_5', | ||
| 'aiml_claude_sonnet_5_5', | ||
| 'aiml_claude_haiku_5_5', | ||
| 'aiml_google_gemini_4_argon', | ||
| 'aiml_google_gemini_3_8_flash', | ||
| 'aiml_moonshot_kimi_k3', | ||
| 'aiml_claude_sonnet_4_6', | ||
| 'aiml_openai_gpt_5_2', | ||
| 'aiml_google_gemini_3_flash_preview', | ||
| 'aiml_google_gemini_3_1_pro_preview', | ||
|
|
@@ -643,69 +653,109 @@ export const Models = { | |
| value: 'openai/gpt-4.1-mini', | ||
| desc: 'OpenRouter (GPT-4.1 Mini)', | ||
| }, | ||
| aiml_openai_gpt_6_1_sol: { | ||
| value: 'openai/gpt-6.1-sol', | ||
| desc: 'AI/ML (GPT-6.1 Sol)', | ||
| }, | ||
| aiml_openai_gpt_6_astra: { | ||
| value: 'openai/gpt-6-astra', | ||
| desc: 'AI/ML (GPT-6 Astra)', | ||
| }, | ||
| aiml_openai_gpt_6_luna: { | ||
| value: 'openai/gpt-6-luna', | ||
| desc: 'AI/ML (GPT-6 Luna)', | ||
| }, | ||
| aiml_openai_gpt_5_5: { | ||
| value: 'openai/gpt-5-5', | ||
| desc: 'AIML (GPT-5.5)', | ||
| desc: 'AI/ML (GPT-5.5)', | ||
| }, | ||
| aiml_openai_gpt_5_4: { | ||
| value: 'openai/gpt-5-4', | ||
| desc: 'AIML (GPT-5.4)', | ||
| desc: 'AI/ML (GPT-5.4)', | ||
| }, | ||
| aiml_openai_gpt_5_1: { | ||
| value: 'openai/gpt-5-1', | ||
| desc: 'AIML (GPT-5.1)', | ||
| desc: 'AI/ML (GPT-5.1)', | ||
| }, | ||
| aiml_openai_gpt_5: { | ||
| value: 'openai/gpt-5', | ||
| desc: 'AIML (GPT-5)', | ||
| desc: 'AI/ML (GPT-5)', | ||
| }, | ||
| aiml_claude_fable_5_1: { | ||
| value: 'anthropic/claude-fable-5.1', | ||
| desc: 'AI/ML (Claude Fable 5.1)', | ||
| }, | ||
| aiml_claude_opus_5_5: { | ||
| value: 'anthropic/claude-opus-5.5', | ||
| desc: 'AI/ML (Claude Opus 5.5)', | ||
| }, | ||
| aiml_claude_sonnet_5_5: { | ||
| value: 'anthropic/claude-sonnet-5.5', | ||
| desc: 'AI/ML (Claude Sonnet 5.5)', | ||
| }, | ||
| aiml_claude_haiku_5_5: { | ||
| value: 'anthropic/claude-haiku-5.5', | ||
| desc: 'AI/ML (Claude Haiku 5.5)', | ||
| }, | ||
| aiml_claude_opus_4_8: { | ||
| value: 'anthropic/claude-opus-4-8', | ||
| desc: 'AIML (Claude Opus 4.8)', | ||
| desc: 'AI/ML (Claude Opus 4.8)', | ||
| }, | ||
| aiml_claude_haiku_4_5: { | ||
| value: 'anthropic/claude-haiku-4.5', | ||
| desc: 'AIML (Claude Haiku 4.5)', | ||
| desc: 'AI/ML (Claude Haiku 4.5)', | ||
| }, | ||
| aiml_claude_sonnet_4_6_20260218: { | ||
| value: 'anthropic/claude-sonnet-4-6-20260218', | ||
| desc: 'AIML (Claude Sonnet 4.6)', | ||
| aiml_claude_sonnet_4_6: { | ||
| value: 'anthropic/claude-sonnet-4.6', | ||
| desc: 'AI/ML (Claude Sonnet 4.6)', | ||
| }, | ||
| aiml_openai_gpt_5_2: { | ||
| value: 'openai/gpt-5-2', | ||
| desc: 'AIML (GPT-5.2)', | ||
| desc: 'AI/ML (GPT-5.2)', | ||
| }, | ||
| aiml_google_gemini_4_argon: { | ||
| value: 'google/gemini-4-argon', | ||
| desc: 'AI/ML (Gemini 4 Argon)', | ||
| }, | ||
| aiml_google_gemini_3_8_flash: { | ||
| value: 'google/gemini-3.8-flash', | ||
| desc: 'AI/ML (Gemini 3.8 Flash)', | ||
| }, | ||
| aiml_google_gemini_3_5_flash: { | ||
| value: 'google/gemini-3-5-flash', | ||
| desc: 'AIML (Gemini 3.5 Flash)', | ||
| desc: 'AI/ML (Gemini 3.5 Flash)', | ||
| }, | ||
| aiml_google_gemini_3_flash_preview: { | ||
| value: 'google/gemini-3-flash-preview', | ||
| desc: 'AIML (Gemini 3 Flash)', | ||
| desc: 'AI/ML (Gemini 3 Flash)', | ||
| }, | ||
| aiml_google_gemini_3_1_pro_preview: { | ||
| value: 'google/gemini-3-1-pro-preview', | ||
| desc: 'AIML (Gemini 3.1 Pro)', | ||
| value: 'google/gemini-3.1-pro-preview', | ||
| desc: 'AI/ML (Gemini 3.1 Pro)', | ||
| }, | ||
| aiml_google_gemini_2_5_pro: { | ||
| value: 'google/gemini-2.5-pro', | ||
| desc: 'AIML (Gemini 2.5 Pro)', | ||
| desc: 'AI/ML (Gemini 2.5 Pro)', | ||
| }, | ||
| aiml_google_gemini_2_5_flash: { | ||
| value: 'google/gemini-2.5-flash', | ||
| desc: 'AIML (Gemini 2.5 Flash)', | ||
| desc: 'AI/ML (Gemini 2.5 Flash)', | ||
| }, | ||
| aiml_moonshot_kimi_k3: { | ||
| value: 'moonshot/kimi-k3', | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: This preset uses an unlisted AI/ML model ID, so Kimi K3 requests can fail model validation. Use a supported Kimi K3 ID, and update the label if the selected variant is K3.1 Pro. Prompt for AI agents |
||
| desc: 'AI/ML (Kimi K3)', | ||
| }, | ||
| aiml_moonshot_kimi_k2_5: { | ||
| value: 'moonshot/kimi-k2-5', | ||
| desc: 'AIML (Kimi K2.5)', | ||
| value: 'moonshotai/kimi-k2.5', | ||
| desc: 'AI/ML (Kimi K2.5)', | ||
| }, | ||
| aiml_deepseek_v4_pro: { | ||
| value: 'deepseek/deepseek-v4-pro', | ||
| desc: 'AIML (DeepSeek V4 Pro)', | ||
| desc: 'AI/ML (DeepSeek V4 Pro)', | ||
| }, | ||
| aiml_deepseek_v4_flash: { | ||
| value: 'deepseek/deepseek-v4-flash', | ||
| desc: 'AIML (DeepSeek V4 Flash)', | ||
| desc: 'AI/ML (DeepSeek V4 Flash)', | ||
| }, | ||
| googleGemini3_1Pro: { | ||
| value: 'gemini-3.1-pro-preview', | ||
|
|
@@ -796,6 +846,7 @@ export const defaultApiModeIds = [ | |
| 'mistralMediumLatest', | ||
| 'openRouter_auto', | ||
| 'openRouter_free', | ||
| 'aiml_openai_gpt_6_1_sol', | ||
| 'nvidiaNim_nemotron_3_super', | ||
| ] | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,6 +43,11 @@ const OPENROUTER_ATTRIBUTION_HEADERS = { | |
| 'X-OpenRouter-Title': 'ChatGPTBox', | ||
| 'X-OpenRouter-Categories': 'general-chat,writing-assistant', | ||
| } | ||
| const AIML_API_HOSTNAME = 'api.aimlapi.com' | ||
| const AIML_ATTRIBUTION_HEADERS = { | ||
| 'X-AIMLAPI-Source': 'agent/chatgptbox', | ||
| 'X-AIMLAPI-Partner-ID': 'part_suAsqzeIpC2a5yS8K1leCNtf', | ||
| } | ||
|
|
||
| function hasOpenAICompatibleRuntimeConfig(config) { | ||
| if (!config || typeof config !== 'object') return false | ||
|
|
@@ -115,6 +120,22 @@ function getOpenRouterAttributionHeaders(requestUrl) { | |
| return OPENROUTER_ATTRIBUTION_HEADERS | ||
| } | ||
|
|
||
| function getAimlAttributionHeaders(requestUrl) { | ||
| try { | ||
| if (new URL(requestUrl).hostname !== AIML_API_HOSTNAME) return {} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1. Redirected requests tag other hosts getAimlAttributionHeaders checks only the initial request URL, while fetchSSE follows redirects without rechecking the destination or removing the attribution headers. If an AI/ML endpoint redirects to another hostname, the redirected request can carry both headers to a host that the exact-hostname check would otherwise exclude. Agent Prompt
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Reject redirects for requests carrying AI/ML attribution, or remove these headers before following a cross-origin redirect. Prompt for AI agents |
||
| } catch { | ||
| return {} | ||
| } | ||
| return AIML_ATTRIBUTION_HEADERS | ||
| } | ||
|
|
||
| function getAppAttributionHeaders(requestUrl) { | ||
| return { | ||
| ...getOpenRouterAttributionHeaders(requestUrl), | ||
| ...getAimlAttributionHeaders(requestUrl), | ||
| } | ||
| } | ||
|
|
||
| function resolveOllamaKeepAliveBaseUrl(request) { | ||
| const requestUrl = normalizeBaseUrl(request?.requestUrl) | ||
| if (requestUrl) { | ||
|
|
@@ -329,7 +350,7 @@ export async function generateAnswersWithOpenAICompatibleApi(port, question, ses | |
| apiKey: request.apiKey, | ||
| config: runtimeConfig, | ||
| provider: providerRequestShapingId, | ||
| extraHeaders: getOpenRouterAttributionHeaders(request.requestUrl), | ||
| extraHeaders: getAppAttributionHeaders(request.requestUrl), | ||
| allowLegacyResponseField: request.provider.allowLegacyResponseField, | ||
| }) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When these new model descriptions are rendered,
modelNameToDescpasses them through i18next, butsrc/_locales/en/main.jsoncontains none of the new or renamedAI/ML (...)keys, so they bypass the English source of truth and cannot participate in the normal localization fallback/translation workflow. Add all labels introduced in this block to the English locale; the rootAGENTS.mdexplicitly requires new source strings there before other locale work.AGENTS.md reference: AGENTS.md:L211-L213
Useful? React with 👍 / 👎.