Add GPT-6 Sol and Luna models
- commit
- 9a5c5656e272d0092af07391bfc801e5987c0529
- author
- patriceckhart · · 2026-09-24 11:22 UTC
- parent
- 6f6753c
- signature
- unsigned
- refs
- tag: v0.3.91
10 files · +155 -22
M README.md +3 -1
| @@ -749,10 +749,12 @@ Use `/login` and pick **api key** to paste an AI Studio key. zot probes `/v1beta | |||
| 749 | 749 | ||
| 750 | 750 | > **Free-tier rate limits.** AI Studio's free tier has tight per-minute and per-day caps that vary by model: `gemini-2.5-pro` is the strictest (a few requests per minute, ~50 per day), Flash and Flash-Lite are far more generous. If a Pro turn 429s with `"You exceeded your current quota"` while Flash on the same key still works, you've hit the Pro free-tier RPD. Either switch to Flash for agent loops, or [enable billing](https://aistudio.google.com/app/apikey) on your AI Studio project to flip the same key from free to pay-as-you-go pricing (`$1.25/M` input, `$10/M` output for Pro). | |
| 751 | 751 | ||
| 752 | - | Reasoning levels (`--reasoning off|minimum|low|medium|high|xhigh|max`, also configurable through `/reasoning` or in `/settings` as **reasoning level**) map differently per generation. `max` is a distinct opt-in tier above `xhigh`. GPT-5.6 and adaptive-thinking Claude models receive native `max`; unsupported providers clamp it to their highest accepted effort. Budget-based providers retain their provider/model caps. Gemini 3.x uses the `thinkingLevel` enum (`MINIMAL`/`LOW`/`MEDIUM`/`HIGH`), with Gemini-3-Pro pinned to `LOW` minimum and `HIGH` for any medium-or-higher request. `off` sends no reasoning config except on always-on thinking models. Gemini 2.0 models have no thinking config. | |
| 752 | + | Reasoning levels (`--reasoning off|minimum|low|medium|high|xhigh|max`, also configurable through `/reasoning` or in `/settings` as **reasoning level**) map differently per generation. `max` is a distinct opt-in tier above `xhigh`. GPT-5.6, GPT-6, and adaptive-thinking Claude models receive native `max`; unsupported providers clamp it to their highest accepted effort. Budget-based providers retain their provider/model caps. Gemini 3.x uses the `thinkingLevel` enum (`MINIMAL`/`LOW`/`MEDIUM`/`HIGH`), with Gemini-3-Pro pinned to `LOW` minimum and `HIGH` for any medium-or-higher request. `off` sends no reasoning config except on always-on thinking models. Gemini 2.0 models have no thinking config. | |
| 753 | 753 | ||
| 754 | 754 | Claude Opus 5.5 is available through Anthropic (`--provider anthropic --model claude-opus-5-5`) and GitHub Copilot (`--provider github-copilot --model claude-opus-5.5`, subject to account availability). Both entries support a 1,000,000-token context window and up to 128,000 output tokens. Adaptive thinking is always on, with `low`, `medium`, `high`, `xhigh`, and `max` effort levels. The default, `off`, and `minimum` requests use `low` instead. The reasoning selector omits unsupported levels. Catalog prices per million tokens are $4 input, $20 output, $0.20 cache reads, and $5 cache writes. Copilot billing remains governed by your plan. | |
| 755 | 755 | ||
| 756 | + | GPT-6 Sol and GPT-6 Luna are available through OpenAI API keys, OpenAI Codex subscriptions, and GitHub Copilot. Select `gpt-6-sol` or `gpt-6-luna` with `--provider openai`, `openai-responses`, `openai-codex`, or `github-copilot`. Both use the Responses API, support up to 128,000 output tokens, and expose `low` through `max` reasoning. OpenAI and Codex entries use a 272,000-token context window, while Copilot entries use 1,000,000 tokens. Access through subscription providers depends on account availability and organization policy. | |
| 757 | + | ||
| 756 | 758 | You can add additional Gemini model IDs to `models.json` under the `google` provider. | |
| 757 | 759 | ||
| 758 | 760 | ### Gemini Enterprise Agent Platform (formerly Google Vertex AI) | |
| @@ -749,10 +749,12 @@ Use `/login` and pick **api key** to paste an AI Studio key. zot probes `/v1beta | |||||
| 749 | 749 | ||||
| 750 | > **Free-tier rate limits.** AI Studio's free tier has tight per-minute and per-day caps that vary by model: `gemini-2.5-pro` is the strictest (a few requests per minute, ~50 per day), Flash and Flash-Lite are far more generous. If a Pro turn 429s with `"You exceeded your current quota"` while Flash on the same key still works, you've hit the Pro free-tier RPD. Either switch to Flash for agent loops, or [enable billing](https://aistudio.google.com/app/apikey) on your AI Studio project to flip the same key from free to pay-as-you-go pricing (`$1.25/M` input, `$10/M` output for Pro). | 750 | > **Free-tier rate limits.** AI Studio's free tier has tight per-minute and per-day caps that vary by model: `gemini-2.5-pro` is the strictest (a few requests per minute, ~50 per day), Flash and Flash-Lite are far more generous. If a Pro turn 429s with `"You exceeded your current quota"` while Flash on the same key still works, you've hit the Pro free-tier RPD. Either switch to Flash for agent loops, or [enable billing](https://aistudio.google.com/app/apikey) on your AI Studio project to flip the same key from free to pay-as-you-go pricing (`$1.25/M` input, `$10/M` output for Pro). | ||
| 751 | 751 | ||||
| 752 | - | Reasoning levels (`--reasoning off|minimum|low|medium|high|xhigh|max`, also configurable through `/reasoning` or in `/settings` as **reasoning level**) map differently per generation. `max` is a distinct opt-in tier above `xhigh`. GPT-5.6 and adaptive-thinking Claude models receive native `max`; unsupported providers clamp it to their highest accepted effort. Budget-based providers retain their provider/model caps. Gemini 3.x uses the `thinkingLevel` enum (`MINIMAL`/`LOW`/`MEDIUM`/`HIGH`), with Gemini-3-Pro pinned to `LOW` minimum and `HIGH` for any medium-or-higher request. `off` sends no reasoning config except on always-on thinking models. Gemini 2.0 models have no thinking config. | 752 | + | Reasoning levels (`--reasoning off|minimum|low|medium|high|xhigh|max`, also configurable through `/reasoning` or in `/settings` as **reasoning level**) map differently per generation. `max` is a distinct opt-in tier above `xhigh`. GPT-5.6, GPT-6, and adaptive-thinking Claude models receive native `max`; unsupported providers clamp it to their highest accepted effort. Budget-based providers retain their provider/model caps. Gemini 3.x uses the `thinkingLevel` enum (`MINIMAL`/`LOW`/`MEDIUM`/`HIGH`), with Gemini-3-Pro pinned to `LOW` minimum and `HIGH` for any medium-or-higher request. `off` sends no reasoning config except on always-on thinking models. Gemini 2.0 models have no thinking config. |
| 753 | 753 | ||||
| 754 | Claude Opus 5.5 is available through Anthropic (`--provider anthropic --model claude-opus-5-5`) and GitHub Copilot (`--provider github-copilot --model claude-opus-5.5`, subject to account availability). Both entries support a 1,000,000-token context window and up to 128,000 output tokens. Adaptive thinking is always on, with `low`, `medium`, `high`, `xhigh`, and `max` effort levels. The default, `off`, and `minimum` requests use `low` instead. The reasoning selector omits unsupported levels. Catalog prices per million tokens are $4 input, $20 output, $0.20 cache reads, and $5 cache writes. Copilot billing remains governed by your plan. | 754 | Claude Opus 5.5 is available through Anthropic (`--provider anthropic --model claude-opus-5-5`) and GitHub Copilot (`--provider github-copilot --model claude-opus-5.5`, subject to account availability). Both entries support a 1,000,000-token context window and up to 128,000 output tokens. Adaptive thinking is always on, with `low`, `medium`, `high`, `xhigh`, and `max` effort levels. The default, `off`, and `minimum` requests use `low` instead. The reasoning selector omits unsupported levels. Catalog prices per million tokens are $4 input, $20 output, $0.20 cache reads, and $5 cache writes. Copilot billing remains governed by your plan. | ||
| 755 | 755 | ||||
| 756 | + | GPT-6 Sol and GPT-6 Luna are available through OpenAI API keys, OpenAI Codex subscriptions, and GitHub Copilot. Select `gpt-6-sol` or `gpt-6-luna` with `--provider openai`, `openai-responses`, `openai-codex`, or `github-copilot`. Both use the Responses API, support up to 128,000 output tokens, and expose `low` through `max` reasoning. OpenAI and Codex entries use a 272,000-token context window, while Copilot entries use 1,000,000 tokens. Access through subscription providers depends on account availability and organization policy. | |||
| 757 | + | ||||
| 756 | You can add additional Gemini model IDs to `models.json` under the `google` provider. | 758 | You can add additional Gemini model IDs to `models.json` under the `google` provider. | ||
| 757 | 759 | ||||
| 758 | ### Gemini Enterprise Agent Platform (formerly Google Vertex AI) | 760 | ### Gemini Enterprise Agent Platform (formerly Google Vertex AI) | ||
M docs/providers.md +16 -16
| @@ -114,10 +114,10 @@ use Chat Completions without `reasoning_effort`; their reasoning is managed by t | |||
| 114 | 114 | endpoint. All three routes send user/agent initiator metadata and image-request | |
| 115 | 115 | headers where appropriate. | |
| 116 | 116 | ||
| 117 | - | As of September 8, 2026, the catalog includes 27 public chat models: Claude | |
| 118 | - | Fable 5/5.1, Haiku 4.5, Opus 4.7/4.8/4.8 Fast/5, Sonnet 5, Gemini | |
| 117 | + | As of September 22, 2026, the catalog includes 30 public chat models: Claude | |
| 118 | + | Fable 5/5.1, Haiku 4.5, Opus 4.7/4.8/4.8 Fast/5/5.5, Sonnet 5, Gemini | |
| 119 | 119 | 3.5/3.6/3.7/3.8 Flash, GPT-5 mini, GPT-5.3-Codex, GPT-5.4/5.4 mini/5.5, | |
| 120 | - | GPT-5.6 Luna/Sol/Terra, GPT-6 Astra, Grok 4.5/4.6, Kimi K2.7 Code/K3, | |
| 120 | + | GPT-5.6 Luna/Sol/Terra, GPT-6 Astra/Luna/Sol, Grok 4.5/4.6, Kimi K2.7 Code/K3, | |
| 121 | 121 | and MAI-Code-1-Flash/1.1-Flash. MAI-Code-1-Flash uses the public picker ID | |
| 122 | 122 | `mai-code-1-flash-picker` and is scheduled to retire on September 10, 2026. | |
| 123 | 123 | ||
| @@ -139,24 +139,24 @@ September 1, 2026, except for individual annual Copilot Pro and Pro+ subscribers | |||
| 139 | 139 | Eligible subscribers can add it through `$ZOT_HOME/models.json`. | |
| 140 | 140 | See [GitHub's model retirement history and plan exceptions](https://docs.github.com/en/copilot/reference/ai-models/supported-models#model-retirement-history). | |
| 141 | 141 | ||
| 142 | - | ## GPT-6 Astra | |
| 142 | + | ## GPT-6 models | |
| 143 | 143 | ||
| 144 | - | Select `gpt-6-astra` with provider `openai`, `openai-responses`, | |
| 145 | - | `openai-codex`, or `github-copilot`, for example: | |
| 144 | + | Select `gpt-6-astra`, `gpt-6-sol`, or `gpt-6-luna` with provider `openai`, | |
| 145 | + | `openai-responses`, `openai-codex`, or `github-copilot`, for example: | |
| 146 | 146 | ||
| 147 | 147 | ```bash | |
| 148 | - | zot --provider github-copilot --model gpt-6-astra | |
| 148 | + | zot --provider github-copilot --model gpt-6-sol | |
| 149 | 149 | ``` | |
| 150 | 150 | ||
| 151 | - | Astra uses the Responses API and supports `low`, `medium`, `high`, `xhigh`, | |
| 152 | - | and `max` reasoning. OpenAI API-key and Codex subscription entries use the | |
| 153 | - | 272,000-token short-context tier by default; the Copilot entry uses a | |
| 154 | - | 1,050,000-token context window. All entries include the 128,000-token output | |
| 155 | - | limit and standard token prices, including the higher rates for prompts above | |
| 156 | - | 272,000 tokens. Subscription access depends on your | |
| 157 | - | plan, rollout, and organization policy; catalog availability does not grant access. | |
| 158 | - | See the [OpenAI model specifications](https://developers.openai.com/api/docs/models/gpt-6-astra) | |
| 159 | - | and [GitHub Copilot availability](https://github.blog/changelog/2026-09-04-gpt-6-astra-is-generally-available-in-github-copilot/). | |
| 151 | + | All three use the Responses API, accept image input, support `low`, `medium`, | |
| 152 | + | `high`, `xhigh`, and `max` reasoning, and allow up to 128,000 output tokens. | |
| 153 | + | OpenAI API-key and Codex subscription entries use a 272,000-token context | |
| 154 | + | window. Copilot uses 1,050,000 tokens for Astra and 1,000,000 tokens for Sol | |
| 155 | + | and Luna. Standard per-million-token prices are $10 input and $50 output for | |
| 156 | + | Astra, $2 input and $10 output for Sol, and $0.10 input and $0.50 output for | |
| 157 | + | Luna. Prompts above 272,000 tokens use each model's long-context price tier. | |
| 158 | + | Subscription access depends on your plan, rollout, and organization policy. | |
| 159 | + | Catalog availability does not grant access. | |
| 160 | 160 | ||
| 161 | 161 | ## API-key providers | |
| 162 | 162 | ||
| @@ -114,10 +114,10 @@ use Chat Completions without `reasoning_effort`; their reasoning is managed by t | |||||
| 114 | endpoint. All three routes send user/agent initiator metadata and image-request | 114 | endpoint. All three routes send user/agent initiator metadata and image-request | ||
| 115 | headers where appropriate. | 115 | headers where appropriate. | ||
| 116 | 116 | ||||
| 117 | - | As of September 8, 2026, the catalog includes 27 public chat models: Claude | 117 | + | As of September 22, 2026, the catalog includes 30 public chat models: Claude |
| 118 | - | Fable 5/5.1, Haiku 4.5, Opus 4.7/4.8/4.8 Fast/5, Sonnet 5, Gemini | 118 | + | Fable 5/5.1, Haiku 4.5, Opus 4.7/4.8/4.8 Fast/5/5.5, Sonnet 5, Gemini |
| 119 | 3.5/3.6/3.7/3.8 Flash, GPT-5 mini, GPT-5.3-Codex, GPT-5.4/5.4 mini/5.5, | 119 | 3.5/3.6/3.7/3.8 Flash, GPT-5 mini, GPT-5.3-Codex, GPT-5.4/5.4 mini/5.5, | ||
| 120 | - | GPT-5.6 Luna/Sol/Terra, GPT-6 Astra, Grok 4.5/4.6, Kimi K2.7 Code/K3, | 120 | + | GPT-5.6 Luna/Sol/Terra, GPT-6 Astra/Luna/Sol, Grok 4.5/4.6, Kimi K2.7 Code/K3, |
| 121 | and MAI-Code-1-Flash/1.1-Flash. MAI-Code-1-Flash uses the public picker ID | 121 | and MAI-Code-1-Flash/1.1-Flash. MAI-Code-1-Flash uses the public picker ID | ||
| 122 | `mai-code-1-flash-picker` and is scheduled to retire on September 10, 2026. | 122 | `mai-code-1-flash-picker` and is scheduled to retire on September 10, 2026. | ||
| 123 | 123 | ||||
| @@ -139,24 +139,24 @@ September 1, 2026, except for individual annual Copilot Pro and Pro+ subscribers | |||||
| 139 | Eligible subscribers can add it through `$ZOT_HOME/models.json`. | 139 | Eligible subscribers can add it through `$ZOT_HOME/models.json`. | ||
| 140 | See [GitHub's model retirement history and plan exceptions](https://docs.github.com/en/copilot/reference/ai-models/supported-models#model-retirement-history). | 140 | See [GitHub's model retirement history and plan exceptions](https://docs.github.com/en/copilot/reference/ai-models/supported-models#model-retirement-history). | ||
| 141 | 141 | ||||
| 142 | - | ## GPT-6 Astra | 142 | + | ## GPT-6 models |
| 143 | 143 | ||||
| 144 | - | Select `gpt-6-astra` with provider `openai`, `openai-responses`, | 144 | + | Select `gpt-6-astra`, `gpt-6-sol`, or `gpt-6-luna` with provider `openai`, |
| 145 | - | `openai-codex`, or `github-copilot`, for example: | 145 | + | `openai-responses`, `openai-codex`, or `github-copilot`, for example: |
| 146 | 146 | ||||
| 147 | ```bash | 147 | ```bash | ||
| 148 | - | zot --provider github-copilot --model gpt-6-astra | 148 | + | zot --provider github-copilot --model gpt-6-sol |
| 149 | ``` | 149 | ``` | ||
| 150 | 150 | ||||
| 151 | - | Astra uses the Responses API and supports `low`, `medium`, `high`, `xhigh`, | 151 | + | All three use the Responses API, accept image input, support `low`, `medium`, |
| 152 | - | and `max` reasoning. OpenAI API-key and Codex subscription entries use the | 152 | + | `high`, `xhigh`, and `max` reasoning, and allow up to 128,000 output tokens. |
| 153 | - | 272,000-token short-context tier by default; the Copilot entry uses a | 153 | + | OpenAI API-key and Codex subscription entries use a 272,000-token context |
| 154 | - | 1,050,000-token context window. All entries include the 128,000-token output | 154 | + | window. Copilot uses 1,050,000 tokens for Astra and 1,000,000 tokens for Sol |
| 155 | - | limit and standard token prices, including the higher rates for prompts above | 155 | + | and Luna. Standard per-million-token prices are $10 input and $50 output for |
| 156 | - | 272,000 tokens. Subscription access depends on your | 156 | + | Astra, $2 input and $10 output for Sol, and $0.10 input and $0.50 output for |
| 157 | - | plan, rollout, and organization policy; catalog availability does not grant access. | 157 | + | Luna. Prompts above 272,000 tokens use each model's long-context price tier. |
| 158 | - | See the [OpenAI model specifications](https://developers.openai.com/api/docs/models/gpt-6-astra) | 158 | + | Subscription access depends on your plan, rollout, and organization policy. |
| 159 | - | and [GitHub Copilot availability](https://github.blog/changelog/2026-09-04-gpt-6-astra-is-generally-available-in-github-copilot/). | 159 | + | Catalog availability does not grant access. |
| 160 | 160 | ||||
| 161 | ## API-key providers | 161 | ## API-key providers | ||
| 162 | 162 | ||||
M packages/provider/catalog_builtin.go +3 -1
| @@ -146,7 +146,7 @@ var builtinCatalog = []Model{ | |||
| 146 | 146 | {Provider: "fireworks", ID: "accounts/fireworks/routers/glm-5p1-fast", DisplayName: "GLM 5.1 Fast", ContextWindow: 202800, MaxOutput: 131072, Reasoning: true, PriceInput: 2.8, PriceOutput: 8.8, PriceCacheRead: 0.52, BaseURL: "https://api.fireworks.ai/inference"}, | |
| 147 | 147 | {Provider: "fireworks", ID: "accounts/fireworks/routers/kimi-k2p6-turbo", DisplayName: "Kimi K2.6 Turbo", ContextWindow: 262000, MaxOutput: 262000, Reasoning: true, PriceInput: 2, PriceOutput: 8, PriceCacheRead: 0.3, BaseURL: "https://api.fireworks.ai/inference"}, | |
| 148 | 148 | // ----- github-copilot ----- | |
| 149 | - | // Public chat models checked against GitHub's supported-model list on 2026-09-08. | |
| 149 | + | // Public chat models checked against GitHub's supported-model list on 2026-09-22. | |
| 150 | 150 | // New model limits and token prices: https://models.dev/api.json (github-copilot). | |
| 151 | 151 | // Copilot's fast Opus model ID comes from the inference API's available-model list. | |
| 152 | 152 | {Provider: "github-copilot", ID: "claude-fable-5", DisplayName: "Claude Fable 5", ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true, PriceInput: 10, PriceOutput: 50, PriceCacheRead: 1, PriceCacheWrite: 12.5, BaseURL: "https://api.individual.githubcopilot.com"}, | |
| @@ -178,6 +178,8 @@ var builtinCatalog = []Model{ | |||
| 178 | 178 | {Provider: "github-copilot", ID: "gpt-5.6-terra", DisplayName: "GPT-5.6 Terra", API: APIResponses, ContextWindow: 400000, MaxOutput: 128000, Reasoning: true, PriceInput: 0, PriceOutput: 0, PriceCacheRead: 0, BaseURL: "https://api.individual.githubcopilot.com"}, | |
| 179 | 179 | {Provider: "github-copilot", ID: "gpt-5.6-luna", DisplayName: "GPT-5.6 Luna", API: APIResponses, ContextWindow: 400000, MaxOutput: 128000, Reasoning: true, PriceInput: 0, PriceOutput: 0, PriceCacheRead: 0, BaseURL: "https://api.individual.githubcopilot.com"}, | |
| 180 | 180 | {Provider: "github-copilot", ID: "gpt-6-astra", DisplayName: "GPT-6 Astra", API: APIResponses, ContextWindow: 1050000, MaxOutput: 128000, Reasoning: true, PriceInput: 10, PriceOutput: 50, PriceCacheRead: 1, PriceCacheWrite: 12.5, PriceTierInputTokens: 272000, PriceInputAbove: 20, PriceOutputAbove: 75, PriceCacheReadAbove: 2, PriceCacheWriteAbove: 25, BaseURL: "https://api.individual.githubcopilot.com"}, | |
| 181 | + | {Provider: "github-copilot", ID: "gpt-6-sol", DisplayName: "GPT-6 Sol", API: APIResponses, ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5, PriceTierInputTokens: 272000, PriceInputAbove: 4, PriceOutputAbove: 15, PriceCacheReadAbove: 0.4, PriceCacheWriteAbove: 5, BaseURL: "https://api.individual.githubcopilot.com"}, | |
| 182 | + | {Provider: "github-copilot", ID: "gpt-6-luna", DisplayName: "GPT-6 Luna", API: APIResponses, ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, PriceInput: 0.1, PriceOutput: 0.5, PriceCacheRead: 0.01, PriceCacheWrite: 0.125, PriceTierInputTokens: 272000, PriceInputAbove: 0.2, PriceOutputAbove: 0.75, PriceCacheReadAbove: 0.02, PriceCacheWriteAbove: 0.25, BaseURL: "https://api.individual.githubcopilot.com"}, | |
| 181 | 183 | // ----- google ----- | |
| 182 | 184 | {Provider: "google", ID: "gemini-3-flash-preview", DisplayName: "Gemini 3 Flash Preview", ContextWindow: 1048576, MaxOutput: 65536, Reasoning: true, PriceInput: 0.5, PriceOutput: 3, PriceCacheRead: 0.05, BaseURL: "https://generativelanguage.googleapis.com/v1beta"}, | |
| 183 | 185 | {Provider: "google", ID: "gemini-3-pro-preview", DisplayName: "Gemini 3 Pro Preview", ContextWindow: 1048576, MaxOutput: 65536, Reasoning: true, PriceInput: 2, PriceOutput: 12, PriceCacheRead: 0.2, BaseURL: "https://generativelanguage.googleapis.com/v1beta"}, | |
| @@ -146,7 +146,7 @@ var builtinCatalog = []Model{ | |||||
| 146 | {Provider: "fireworks", ID: "accounts/fireworks/routers/glm-5p1-fast", DisplayName: "GLM 5.1 Fast", ContextWindow: 202800, MaxOutput: 131072, Reasoning: true, PriceInput: 2.8, PriceOutput: 8.8, PriceCacheRead: 0.52, BaseURL: "https://api.fireworks.ai/inference"}, | 146 | {Provider: "fireworks", ID: "accounts/fireworks/routers/glm-5p1-fast", DisplayName: "GLM 5.1 Fast", ContextWindow: 202800, MaxOutput: 131072, Reasoning: true, PriceInput: 2.8, PriceOutput: 8.8, PriceCacheRead: 0.52, BaseURL: "https://api.fireworks.ai/inference"}, | ||
| 147 | {Provider: "fireworks", ID: "accounts/fireworks/routers/kimi-k2p6-turbo", DisplayName: "Kimi K2.6 Turbo", ContextWindow: 262000, MaxOutput: 262000, Reasoning: true, PriceInput: 2, PriceOutput: 8, PriceCacheRead: 0.3, BaseURL: "https://api.fireworks.ai/inference"}, | 147 | {Provider: "fireworks", ID: "accounts/fireworks/routers/kimi-k2p6-turbo", DisplayName: "Kimi K2.6 Turbo", ContextWindow: 262000, MaxOutput: 262000, Reasoning: true, PriceInput: 2, PriceOutput: 8, PriceCacheRead: 0.3, BaseURL: "https://api.fireworks.ai/inference"}, | ||
| 148 | // ----- github-copilot ----- | 148 | // ----- github-copilot ----- | ||
| 149 | - | // Public chat models checked against GitHub's supported-model list on 2026-09-08. | 149 | + | // Public chat models checked against GitHub's supported-model list on 2026-09-22. |
| 150 | // New model limits and token prices: https://models.dev/api.json (github-copilot). | 150 | // New model limits and token prices: https://models.dev/api.json (github-copilot). | ||
| 151 | // Copilot's fast Opus model ID comes from the inference API's available-model list. | 151 | // Copilot's fast Opus model ID comes from the inference API's available-model list. | ||
| 152 | {Provider: "github-copilot", ID: "claude-fable-5", DisplayName: "Claude Fable 5", ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true, PriceInput: 10, PriceOutput: 50, PriceCacheRead: 1, PriceCacheWrite: 12.5, BaseURL: "https://api.individual.githubcopilot.com"}, | 152 | {Provider: "github-copilot", ID: "claude-fable-5", DisplayName: "Claude Fable 5", ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, AdaptiveThinking: true, PriceInput: 10, PriceOutput: 50, PriceCacheRead: 1, PriceCacheWrite: 12.5, BaseURL: "https://api.individual.githubcopilot.com"}, | ||
| @@ -178,6 +178,8 @@ var builtinCatalog = []Model{ | |||||
| 178 | {Provider: "github-copilot", ID: "gpt-5.6-terra", DisplayName: "GPT-5.6 Terra", API: APIResponses, ContextWindow: 400000, MaxOutput: 128000, Reasoning: true, PriceInput: 0, PriceOutput: 0, PriceCacheRead: 0, BaseURL: "https://api.individual.githubcopilot.com"}, | 178 | {Provider: "github-copilot", ID: "gpt-5.6-terra", DisplayName: "GPT-5.6 Terra", API: APIResponses, ContextWindow: 400000, MaxOutput: 128000, Reasoning: true, PriceInput: 0, PriceOutput: 0, PriceCacheRead: 0, BaseURL: "https://api.individual.githubcopilot.com"}, | ||
| 179 | {Provider: "github-copilot", ID: "gpt-5.6-luna", DisplayName: "GPT-5.6 Luna", API: APIResponses, ContextWindow: 400000, MaxOutput: 128000, Reasoning: true, PriceInput: 0, PriceOutput: 0, PriceCacheRead: 0, BaseURL: "https://api.individual.githubcopilot.com"}, | 179 | {Provider: "github-copilot", ID: "gpt-5.6-luna", DisplayName: "GPT-5.6 Luna", API: APIResponses, ContextWindow: 400000, MaxOutput: 128000, Reasoning: true, PriceInput: 0, PriceOutput: 0, PriceCacheRead: 0, BaseURL: "https://api.individual.githubcopilot.com"}, | ||
| 180 | {Provider: "github-copilot", ID: "gpt-6-astra", DisplayName: "GPT-6 Astra", API: APIResponses, ContextWindow: 1050000, MaxOutput: 128000, Reasoning: true, PriceInput: 10, PriceOutput: 50, PriceCacheRead: 1, PriceCacheWrite: 12.5, PriceTierInputTokens: 272000, PriceInputAbove: 20, PriceOutputAbove: 75, PriceCacheReadAbove: 2, PriceCacheWriteAbove: 25, BaseURL: "https://api.individual.githubcopilot.com"}, | 180 | {Provider: "github-copilot", ID: "gpt-6-astra", DisplayName: "GPT-6 Astra", API: APIResponses, ContextWindow: 1050000, MaxOutput: 128000, Reasoning: true, PriceInput: 10, PriceOutput: 50, PriceCacheRead: 1, PriceCacheWrite: 12.5, PriceTierInputTokens: 272000, PriceInputAbove: 20, PriceOutputAbove: 75, PriceCacheReadAbove: 2, PriceCacheWriteAbove: 25, BaseURL: "https://api.individual.githubcopilot.com"}, | ||
| 181 | + | {Provider: "github-copilot", ID: "gpt-6-sol", DisplayName: "GPT-6 Sol", API: APIResponses, ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5, PriceTierInputTokens: 272000, PriceInputAbove: 4, PriceOutputAbove: 15, PriceCacheReadAbove: 0.4, PriceCacheWriteAbove: 5, BaseURL: "https://api.individual.githubcopilot.com"}, | |||
| 182 | + | {Provider: "github-copilot", ID: "gpt-6-luna", DisplayName: "GPT-6 Luna", API: APIResponses, ContextWindow: 1000000, MaxOutput: 128000, Reasoning: true, PriceInput: 0.1, PriceOutput: 0.5, PriceCacheRead: 0.01, PriceCacheWrite: 0.125, PriceTierInputTokens: 272000, PriceInputAbove: 0.2, PriceOutputAbove: 0.75, PriceCacheReadAbove: 0.02, PriceCacheWriteAbove: 0.25, BaseURL: "https://api.individual.githubcopilot.com"}, | |||
| 181 | // ----- google ----- | 183 | // ----- google ----- | ||
| 182 | {Provider: "google", ID: "gemini-3-flash-preview", DisplayName: "Gemini 3 Flash Preview", ContextWindow: 1048576, MaxOutput: 65536, Reasoning: true, PriceInput: 0.5, PriceOutput: 3, PriceCacheRead: 0.05, BaseURL: "https://generativelanguage.googleapis.com/v1beta"}, | 184 | {Provider: "google", ID: "gemini-3-flash-preview", DisplayName: "Gemini 3 Flash Preview", ContextWindow: 1048576, MaxOutput: 65536, Reasoning: true, PriceInput: 0.5, PriceOutput: 3, PriceCacheRead: 0.05, BaseURL: "https://generativelanguage.googleapis.com/v1beta"}, | ||
| 183 | {Provider: "google", ID: "gemini-3-pro-preview", DisplayName: "Gemini 3 Pro Preview", ContextWindow: 1048576, MaxOutput: 65536, Reasoning: true, PriceInput: 2, PriceOutput: 12, PriceCacheRead: 0.2, BaseURL: "https://generativelanguage.googleapis.com/v1beta"}, | 185 | {Provider: "google", ID: "gemini-3-pro-preview", DisplayName: "Gemini 3 Pro Preview", ContextWindow: 1048576, MaxOutput: 65536, Reasoning: true, PriceInput: 2, PriceOutput: 12, PriceCacheRead: 0.2, BaseURL: "https://generativelanguage.googleapis.com/v1beta"}, | ||
M packages/provider/copilot_catalog_test.go +1 -1
| @@ -8,7 +8,7 @@ import ( | |||
| 8 | 8 | func TestCopilotCatalogCoversPublicChatModels(t *testing.T) { | |
| 9 | 9 | groups := map[string][]string{ | |
| 10 | 10 | APIAnthropicMessages: {"claude-fable-5", "claude-fable-5.1", "claude-haiku-4.5", "claude-opus-4.7", "claude-opus-4.8", "claude-opus-4.8-fast", "claude-opus-5", "claude-opus-5.5", "claude-sonnet-5"}, | |
| 11 | - | APIResponses: {"gpt-5-mini", "gpt-5.3-codex", "gpt-5.4", "gpt-5.4-mini", "gpt-5.5", "gpt-5.6-luna", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-6-astra", "grok-4.5", "grok-4.6", "mai-code-1-flash-picker", "mai-code-1.1-flash"}, | |
| 11 | + | APIResponses: {"gpt-5-mini", "gpt-5.3-codex", "gpt-5.4", "gpt-5.4-mini", "gpt-5.5", "gpt-5.6-luna", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-6-astra", "gpt-6-luna", "gpt-6-sol", "grok-4.5", "grok-4.6", "mai-code-1-flash-picker", "mai-code-1.1-flash"}, | |
| 12 | 12 | APICompletions: {"gemini-3.5-flash", "gemini-3.6-flash", "gemini-3.7-flash", "gemini-3.8-flash", "kimi-k2.7-code", "kimi-k3"}, | |
| 13 | 13 | } | |
| 14 | 14 | for api, ids := range groups { | |
| @@ -8,7 +8,7 @@ import ( | |||||
| 8 | func TestCopilotCatalogCoversPublicChatModels(t *testing.T) { | 8 | func TestCopilotCatalogCoversPublicChatModels(t *testing.T) { | ||
| 9 | groups := map[string][]string{ | 9 | groups := map[string][]string{ | ||
| 10 | APIAnthropicMessages: {"claude-fable-5", "claude-fable-5.1", "claude-haiku-4.5", "claude-opus-4.7", "claude-opus-4.8", "claude-opus-4.8-fast", "claude-opus-5", "claude-opus-5.5", "claude-sonnet-5"}, | 10 | APIAnthropicMessages: {"claude-fable-5", "claude-fable-5.1", "claude-haiku-4.5", "claude-opus-4.7", "claude-opus-4.8", "claude-opus-4.8-fast", "claude-opus-5", "claude-opus-5.5", "claude-sonnet-5"}, | ||
| 11 | - | APIResponses: {"gpt-5-mini", "gpt-5.3-codex", "gpt-5.4", "gpt-5.4-mini", "gpt-5.5", "gpt-5.6-luna", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-6-astra", "grok-4.5", "grok-4.6", "mai-code-1-flash-picker", "mai-code-1.1-flash"}, | 11 | + | APIResponses: {"gpt-5-mini", "gpt-5.3-codex", "gpt-5.4", "gpt-5.4-mini", "gpt-5.5", "gpt-5.6-luna", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-6-astra", "gpt-6-luna", "gpt-6-sol", "grok-4.5", "grok-4.6", "mai-code-1-flash-picker", "mai-code-1.1-flash"}, |
| 12 | APICompletions: {"gemini-3.5-flash", "gemini-3.6-flash", "gemini-3.7-flash", "gemini-3.8-flash", "kimi-k2.7-code", "kimi-k3"}, | 12 | APICompletions: {"gemini-3.5-flash", "gemini-3.6-flash", "gemini-3.7-flash", "gemini-3.8-flash", "kimi-k2.7-code", "kimi-k3"}, | ||
| 13 | } | 13 | } | ||
| 14 | for api, ids := range groups { | 14 | for api, ids := range groups { | ||
M packages/provider/discover_copilot_test.go +1 -1
| @@ -97,7 +97,7 @@ func TestModelAvailabilityFiltersWithoutDeletingCatalog(t *testing.T) { | |||
| 97 | 97 | t.Error("empty availability did not hide models") | |
| 98 | 98 | } | |
| 99 | 99 | SetModelAvailability("github-copilot", nil) | |
| 100 | - | if len(ModelsForProvider("github-copilot")) != 28 { | |
| 100 | + | if len(ModelsForProvider("github-copilot")) != 30 { | |
| 101 | 101 | t.Error("clearing restriction did not restore catalog") | |
| 102 | 102 | } | |
| 103 | 103 | } | |
| @@ -97,7 +97,7 @@ func TestModelAvailabilityFiltersWithoutDeletingCatalog(t *testing.T) { | |||||
| 97 | t.Error("empty availability did not hide models") | 97 | t.Error("empty availability did not hide models") | ||
| 98 | } | 98 | } | ||
| 99 | SetModelAvailability("github-copilot", nil) | 99 | SetModelAvailability("github-copilot", nil) | ||
| 100 | - | if len(ModelsForProvider("github-copilot")) != 28 { | 100 | + | if len(ModelsForProvider("github-copilot")) != 30 { |
| 101 | t.Error("clearing restriction did not restore catalog") | 101 | t.Error("clearing restriction did not restore catalog") | ||
| 102 | } | 102 | } | ||
| 103 | } | 103 | } | ||
M packages/provider/extra_models.go +10 -0
| @@ -53,6 +53,16 @@ var supplementCatalog = []Model{ | |||
| 53 | 53 | PriceInput: 10, PriceOutput: 50, PriceCacheRead: 1, PriceCacheWrite: 12.5, | |
| 54 | 54 | PriceTierInputTokens: 272000, | |
| 55 | 55 | PriceInputAbove: 20, PriceOutputAbove: 75, PriceCacheReadAbove: 2, PriceCacheWriteAbove: 25}, | |
| 56 | + | {Provider: "openai-responses", ID: "gpt-6-sol", DisplayName: "GPT-6 Sol (Responses)", API: APIResponses, | |
| 57 | + | ContextWindow: 272000, MaxOutput: 128000, Reasoning: true, | |
| 58 | + | PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5, | |
| 59 | + | PriceTierInputTokens: 272000, | |
| 60 | + | PriceInputAbove: 4, PriceOutputAbove: 15, PriceCacheReadAbove: 0.4, PriceCacheWriteAbove: 5}, | |
| 61 | + | {Provider: "openai-responses", ID: "gpt-6-luna", DisplayName: "GPT-6 Luna (Responses)", API: APIResponses, | |
| 62 | + | ContextWindow: 272000, MaxOutput: 128000, Reasoning: true, | |
| 63 | + | PriceInput: 0.1, PriceOutput: 0.5, PriceCacheRead: 0.01, PriceCacheWrite: 0.125, | |
| 64 | + | PriceTierInputTokens: 272000, | |
| 65 | + | PriceInputAbove: 0.2, PriceOutputAbove: 0.75, PriceCacheReadAbove: 0.02, PriceCacheWriteAbove: 0.25}, | |
| 56 | 66 | {Provider: "openai-responses", ID: "gpt-5", DisplayName: "GPT-5 (Responses)", | |
| 57 | 67 | ContextWindow: 400000, MaxOutput: 128000, Reasoning: true, | |
| 58 | 68 | PriceInput: 1.25, PriceOutput: 10.00, PriceCacheRead: 0.125}, | |
| @@ -53,6 +53,16 @@ var supplementCatalog = []Model{ | |||||
| 53 | PriceInput: 10, PriceOutput: 50, PriceCacheRead: 1, PriceCacheWrite: 12.5, | 53 | PriceInput: 10, PriceOutput: 50, PriceCacheRead: 1, PriceCacheWrite: 12.5, | ||
| 54 | PriceTierInputTokens: 272000, | 54 | PriceTierInputTokens: 272000, | ||
| 55 | PriceInputAbove: 20, PriceOutputAbove: 75, PriceCacheReadAbove: 2, PriceCacheWriteAbove: 25}, | 55 | PriceInputAbove: 20, PriceOutputAbove: 75, PriceCacheReadAbove: 2, PriceCacheWriteAbove: 25}, | ||
| 56 | + | {Provider: "openai-responses", ID: "gpt-6-sol", DisplayName: "GPT-6 Sol (Responses)", API: APIResponses, | |||
| 57 | + | ContextWindow: 272000, MaxOutput: 128000, Reasoning: true, | |||
| 58 | + | PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5, | |||
| 59 | + | PriceTierInputTokens: 272000, | |||
| 60 | + | PriceInputAbove: 4, PriceOutputAbove: 15, PriceCacheReadAbove: 0.4, PriceCacheWriteAbove: 5}, | |||
| 61 | + | {Provider: "openai-responses", ID: "gpt-6-luna", DisplayName: "GPT-6 Luna (Responses)", API: APIResponses, | |||
| 62 | + | ContextWindow: 272000, MaxOutput: 128000, Reasoning: true, | |||
| 63 | + | PriceInput: 0.1, PriceOutput: 0.5, PriceCacheRead: 0.01, PriceCacheWrite: 0.125, | |||
| 64 | + | PriceTierInputTokens: 272000, | |||
| 65 | + | PriceInputAbove: 0.2, PriceOutputAbove: 0.75, PriceCacheReadAbove: 0.02, PriceCacheWriteAbove: 0.25}, | |||
| 56 | {Provider: "openai-responses", ID: "gpt-5", DisplayName: "GPT-5 (Responses)", | 66 | {Provider: "openai-responses", ID: "gpt-5", DisplayName: "GPT-5 (Responses)", | ||
| 57 | ContextWindow: 400000, MaxOutput: 128000, Reasoning: true, | 67 | ContextWindow: 400000, MaxOutput: 128000, Reasoning: true, | ||
| 58 | PriceInput: 1.25, PriceOutput: 10.00, PriceCacheRead: 0.125}, | 68 | PriceInput: 1.25, PriceOutput: 10.00, PriceCacheRead: 0.125}, | ||
M packages/provider/gpt6_astra_test.go +87 -0
| @@ -6,6 +6,93 @@ import ( | |||
| 6 | 6 | "testing" | |
| 7 | 7 | ) | |
| 8 | 8 | ||
| 9 | + | func TestGPT6SolAndLunaCatalog(t *testing.T) { | |
| 10 | + | models := []struct { | |
| 11 | + | id string | |
| 12 | + | input float64 | |
| 13 | + | output float64 | |
| 14 | + | cacheRead float64 | |
| 15 | + | cacheWrite float64 | |
| 16 | + | inputAbove float64 | |
| 17 | + | outAbove float64 | |
| 18 | + | readAbove float64 | |
| 19 | + | writeAbove float64 | |
| 20 | + | }{ | |
| 21 | + | {"gpt-6-sol", 2, 10, 0.2, 2.5, 4, 15, 0.4, 5}, | |
| 22 | + | {"gpt-6-luna", 0.1, 0.5, 0.01, 0.125, 0.2, 0.75, 0.02, 0.25}, | |
| 23 | + | } | |
| 24 | + | for _, model := range models { | |
| 25 | + | for _, name := range []string{"openai", "openai-responses", "openai-codex", "github-copilot"} { | |
| 26 | + | t.Run(name+"/"+model.id, func(t *testing.T) { | |
| 27 | + | m, err := FindModel(name, model.id) | |
| 28 | + | if err != nil { | |
| 29 | + | t.Fatal(err) | |
| 30 | + | } | |
| 31 | + | wantContext := 272000 | |
| 32 | + | if name == "github-copilot" { | |
| 33 | + | wantContext = 1000000 | |
| 34 | + | } | |
| 35 | + | if m.API != APIResponses || m.ContextWindow != wantContext || m.MaxOutput != 128000 || !m.Reasoning || m.Speculative { | |
| 36 | + | t.Fatalf("unexpected model capabilities: %+v", m) | |
| 37 | + | } | |
| 38 | + | if m.PriceInput != model.input || m.PriceOutput != model.output || m.PriceCacheRead != model.cacheRead || m.PriceCacheWrite != model.cacheWrite { | |
| 39 | + | t.Fatalf("unexpected standard prices: %+v", m) | |
| 40 | + | } | |
| 41 | + | if m.PriceTierInputTokens != 272000 || m.PriceInputAbove != model.inputAbove || m.PriceOutputAbove != model.outAbove || m.PriceCacheReadAbove != model.readAbove || m.PriceCacheWriteAbove != model.writeAbove { | |
| 42 | + | t.Fatalf("unexpected long-context prices: %+v", m) | |
| 43 | + | } | |
| 44 | + | wantLevels := []string{"", "low", "medium", "high", "xhigh", "max"} | |
| 45 | + | if got := AvailableReasoningLevels(m); !slices.Equal(got, wantLevels) { | |
| 46 | + | t.Fatalf("reasoning levels = %q, want %q", got, wantLevels) | |
| 47 | + | } | |
| 48 | + | }) | |
| 49 | + | } | |
| 50 | + | } | |
| 51 | + | } | |
| 52 | + | ||
| 53 | + | func TestGPT6SolAndLunaResponsesReasoning(t *testing.T) { | |
| 54 | + | copilot := NewGithubCopilotClient("test-token").(*copilotClient).router | |
| 55 | + | clients := map[string]*codexClient{ | |
| 56 | + | "openai": NewOpenAIResponsesNamed("test-token", "", "openai").(*renamedClient).inner.(*codexClient), | |
| 57 | + | "openai-responses": NewOpenAIResponsesNamed("test-token", "", "openai-responses").(*renamedClient).inner.(*codexClient), | |
| 58 | + | "openai-codex": NewOpenAICodex("test-token", "test-account", "").(*codexClient), | |
| 59 | + | "github-copilot": copilot.byAPI[APIResponses].(*codexClient), | |
| 60 | + | } | |
| 61 | + | for name, client := range clients { | |
| 62 | + | for _, model := range []string{"gpt-6-sol", "gpt-6-luna"} { | |
| 63 | + | t.Run(name+"/"+model, func(t *testing.T) { | |
| 64 | + | wire, err := client.buildRequest(Request{Model: model, Reasoning: "max"}) | |
| 65 | + | if err != nil { | |
| 66 | + | t.Fatal(err) | |
| 67 | + | } | |
| 68 | + | if wire.Model != model || wire.Reasoning == nil || wire.Reasoning.Effort != "max" { | |
| 69 | + | t.Fatalf("unexpected request: %+v", wire) | |
| 70 | + | } | |
| 71 | + | }) | |
| 72 | + | } | |
| 73 | + | } | |
| 74 | + | } | |
| 75 | + | ||
| 76 | + | func TestGPT6SolAndLunaCopilotDispatch(t *testing.T) { | |
| 77 | + | router := NewGithubCopilotClient("test-token").(*copilotClient).router | |
| 78 | + | completions := &routeCaptureClient{name: "github-copilot"} | |
| 79 | + | responses := &routeCaptureClient{name: "github-copilot"} | |
| 80 | + | router.fallback = completions | |
| 81 | + | router.byAPI[APIResponses] = responses | |
| 82 | + | ||
| 83 | + | for _, model := range []string{"gpt-6-sol", "gpt-6-luna"} { | |
| 84 | + | stream, err := router.Stream(context.Background(), Request{Model: model}) | |
| 85 | + | if err != nil { | |
| 86 | + | t.Fatal(err) | |
| 87 | + | } | |
| 88 | + | for range stream { | |
| 89 | + | } | |
| 90 | + | } | |
| 91 | + | if !slices.Equal(responses.models, []string{"gpt-6-sol", "gpt-6-luna"}) || len(completions.models) != 0 { | |
| 92 | + | t.Fatalf("Responses models = %v, Completions models = %v", responses.models, completions.models) | |
| 93 | + | } | |
| 94 | + | } | |
| 95 | + | ||
| 9 | 96 | func TestGPT6AstraCatalog(t *testing.T) { | |
| 10 | 97 | for _, name := range []string{"openai", "openai-responses", "openai-codex", "github-copilot"} { | |
| 11 | 98 | t.Run(name, func(t *testing.T) { | |
| @@ -6,6 +6,93 @@ import ( | |||||
| 6 | "testing" | 6 | "testing" | ||
| 7 | ) | 7 | ) | ||
| 8 | 8 | ||||
| 9 | + | func TestGPT6SolAndLunaCatalog(t *testing.T) { | |||
| 10 | + | models := []struct { | |||
| 11 | + | id string | |||
| 12 | + | input float64 | |||
| 13 | + | output float64 | |||
| 14 | + | cacheRead float64 | |||
| 15 | + | cacheWrite float64 | |||
| 16 | + | inputAbove float64 | |||
| 17 | + | outAbove float64 | |||
| 18 | + | readAbove float64 | |||
| 19 | + | writeAbove float64 | |||
| 20 | + | }{ | |||
| 21 | + | {"gpt-6-sol", 2, 10, 0.2, 2.5, 4, 15, 0.4, 5}, | |||
| 22 | + | {"gpt-6-luna", 0.1, 0.5, 0.01, 0.125, 0.2, 0.75, 0.02, 0.25}, | |||
| 23 | + | } | |||
| 24 | + | for _, model := range models { | |||
| 25 | + | for _, name := range []string{"openai", "openai-responses", "openai-codex", "github-copilot"} { | |||
| 26 | + | t.Run(name+"/"+model.id, func(t *testing.T) { | |||
| 27 | + | m, err := FindModel(name, model.id) | |||
| 28 | + | if err != nil { | |||
| 29 | + | t.Fatal(err) | |||
| 30 | + | } | |||
| 31 | + | wantContext := 272000 | |||
| 32 | + | if name == "github-copilot" { | |||
| 33 | + | wantContext = 1000000 | |||
| 34 | + | } | |||
| 35 | + | if m.API != APIResponses || m.ContextWindow != wantContext || m.MaxOutput != 128000 || !m.Reasoning || m.Speculative { | |||
| 36 | + | t.Fatalf("unexpected model capabilities: %+v", m) | |||
| 37 | + | } | |||
| 38 | + | if m.PriceInput != model.input || m.PriceOutput != model.output || m.PriceCacheRead != model.cacheRead || m.PriceCacheWrite != model.cacheWrite { | |||
| 39 | + | t.Fatalf("unexpected standard prices: %+v", m) | |||
| 40 | + | } | |||
| 41 | + | if m.PriceTierInputTokens != 272000 || m.PriceInputAbove != model.inputAbove || m.PriceOutputAbove != model.outAbove || m.PriceCacheReadAbove != model.readAbove || m.PriceCacheWriteAbove != model.writeAbove { | |||
| 42 | + | t.Fatalf("unexpected long-context prices: %+v", m) | |||
| 43 | + | } | |||
| 44 | + | wantLevels := []string{"", "low", "medium", "high", "xhigh", "max"} | |||
| 45 | + | if got := AvailableReasoningLevels(m); !slices.Equal(got, wantLevels) { | |||
| 46 | + | t.Fatalf("reasoning levels = %q, want %q", got, wantLevels) | |||
| 47 | + | } | |||
| 48 | + | }) | |||
| 49 | + | } | |||
| 50 | + | } | |||
| 51 | + | } | |||
| 52 | + | ||||
| 53 | + | func TestGPT6SolAndLunaResponsesReasoning(t *testing.T) { | |||
| 54 | + | copilot := NewGithubCopilotClient("test-token").(*copilotClient).router | |||
| 55 | + | clients := map[string]*codexClient{ | |||
| 56 | + | "openai": NewOpenAIResponsesNamed("test-token", "", "openai").(*renamedClient).inner.(*codexClient), | |||
| 57 | + | "openai-responses": NewOpenAIResponsesNamed("test-token", "", "openai-responses").(*renamedClient).inner.(*codexClient), | |||
| 58 | + | "openai-codex": NewOpenAICodex("test-token", "test-account", "").(*codexClient), | |||
| 59 | + | "github-copilot": copilot.byAPI[APIResponses].(*codexClient), | |||
| 60 | + | } | |||
| 61 | + | for name, client := range clients { | |||
| 62 | + | for _, model := range []string{"gpt-6-sol", "gpt-6-luna"} { | |||
| 63 | + | t.Run(name+"/"+model, func(t *testing.T) { | |||
| 64 | + | wire, err := client.buildRequest(Request{Model: model, Reasoning: "max"}) | |||
| 65 | + | if err != nil { | |||
| 66 | + | t.Fatal(err) | |||
| 67 | + | } | |||
| 68 | + | if wire.Model != model || wire.Reasoning == nil || wire.Reasoning.Effort != "max" { | |||
| 69 | + | t.Fatalf("unexpected request: %+v", wire) | |||
| 70 | + | } | |||
| 71 | + | }) | |||
| 72 | + | } | |||
| 73 | + | } | |||
| 74 | + | } | |||
| 75 | + | ||||
| 76 | + | func TestGPT6SolAndLunaCopilotDispatch(t *testing.T) { | |||
| 77 | + | router := NewGithubCopilotClient("test-token").(*copilotClient).router | |||
| 78 | + | completions := &routeCaptureClient{name: "github-copilot"} | |||
| 79 | + | responses := &routeCaptureClient{name: "github-copilot"} | |||
| 80 | + | router.fallback = completions | |||
| 81 | + | router.byAPI[APIResponses] = responses | |||
| 82 | + | ||||
| 83 | + | for _, model := range []string{"gpt-6-sol", "gpt-6-luna"} { | |||
| 84 | + | stream, err := router.Stream(context.Background(), Request{Model: model}) | |||
| 85 | + | if err != nil { | |||
| 86 | + | t.Fatal(err) | |||
| 87 | + | } | |||
| 88 | + | for range stream { | |||
| 89 | + | } | |||
| 90 | + | } | |||
| 91 | + | if !slices.Equal(responses.models, []string{"gpt-6-sol", "gpt-6-luna"}) || len(completions.models) != 0 { | |||
| 92 | + | t.Fatalf("Responses models = %v, Completions models = %v", responses.models, completions.models) | |||
| 93 | + | } | |||
| 94 | + | } | |||
| 95 | + | ||||
| 9 | func TestGPT6AstraCatalog(t *testing.T) { | 96 | func TestGPT6AstraCatalog(t *testing.T) { | ||
| 10 | for _, name := range []string{"openai", "openai-responses", "openai-codex", "github-copilot"} { | 97 | for _, name := range []string{"openai", "openai-responses", "openai-codex", "github-copilot"} { | ||
| 11 | t.Run(name, func(t *testing.T) { | 98 | t.Run(name, func(t *testing.T) { | ||
M packages/provider/models.go +28 -0
| @@ -415,6 +415,20 @@ var Catalog = []Model{ | |||
| 415 | 415 | PriceTierInputTokens: 272000, | |
| 416 | 416 | PriceInputAbove: 20, PriceOutputAbove: 75, PriceCacheReadAbove: 2, PriceCacheWriteAbove: 25, | |
| 417 | 417 | }, | |
| 418 | + | { | |
| 419 | + | Provider: "openai", ID: "gpt-6-sol", DisplayName: "GPT-6 Sol", API: APIResponses, | |
| 420 | + | ContextWindow: 272000, MaxOutput: 128000, Reasoning: true, | |
| 421 | + | PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5, | |
| 422 | + | PriceTierInputTokens: 272000, | |
| 423 | + | PriceInputAbove: 4, PriceOutputAbove: 15, PriceCacheReadAbove: 0.4, PriceCacheWriteAbove: 5, | |
| 424 | + | }, | |
| 425 | + | { | |
| 426 | + | Provider: "openai", ID: "gpt-6-luna", DisplayName: "GPT-6 Luna", API: APIResponses, | |
| 427 | + | ContextWindow: 272000, MaxOutput: 128000, Reasoning: true, | |
| 428 | + | PriceInput: 0.1, PriceOutput: 0.5, PriceCacheRead: 0.01, PriceCacheWrite: 0.125, | |
| 429 | + | PriceTierInputTokens: 272000, | |
| 430 | + | PriceInputAbove: 0.2, PriceOutputAbove: 0.75, PriceCacheReadAbove: 0.02, PriceCacheWriteAbove: 0.25, | |
| 431 | + | }, | |
| 418 | 432 | ||
| 419 | 433 | // ---- OpenAI Codex / ChatGPT subscription backend ---- | |
| 420 | 434 | // Same model ids as the OpenAI family, but routed through the | |
| @@ -468,6 +482,20 @@ var Catalog = []Model{ | |||
| 468 | 482 | PriceTierInputTokens: 272000, | |
| 469 | 483 | PriceInputAbove: 20, PriceOutputAbove: 75, PriceCacheReadAbove: 2, PriceCacheWriteAbove: 25, | |
| 470 | 484 | }, | |
| 485 | + | { | |
| 486 | + | Provider: "openai-codex", ID: "gpt-6-sol", DisplayName: "GPT-6 Sol", API: APIResponses, | |
| 487 | + | ContextWindow: 272000, MaxOutput: 128000, Reasoning: true, | |
| 488 | + | PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5, | |
| 489 | + | PriceTierInputTokens: 272000, | |
| 490 | + | PriceInputAbove: 4, PriceOutputAbove: 15, PriceCacheReadAbove: 0.4, PriceCacheWriteAbove: 5, | |
| 491 | + | }, | |
| 492 | + | { | |
| 493 | + | Provider: "openai-codex", ID: "gpt-6-luna", DisplayName: "GPT-6 Luna", API: APIResponses, | |
| 494 | + | ContextWindow: 272000, MaxOutput: 128000, Reasoning: true, | |
| 495 | + | PriceInput: 0.1, PriceOutput: 0.5, PriceCacheRead: 0.01, PriceCacheWrite: 0.125, | |
| 496 | + | PriceTierInputTokens: 272000, | |
| 497 | + | PriceInputAbove: 0.2, PriceOutputAbove: 0.75, PriceCacheReadAbove: 0.02, PriceCacheWriteAbove: 0.25, | |
| 498 | + | }, | |
| 471 | 499 | } | |
| 472 | 500 | ||
| 473 | 501 | // DefaultModel is used when the user does not specify one. | |
| @@ -415,6 +415,20 @@ var Catalog = []Model{ | |||||
| 415 | PriceTierInputTokens: 272000, | 415 | PriceTierInputTokens: 272000, | ||
| 416 | PriceInputAbove: 20, PriceOutputAbove: 75, PriceCacheReadAbove: 2, PriceCacheWriteAbove: 25, | 416 | PriceInputAbove: 20, PriceOutputAbove: 75, PriceCacheReadAbove: 2, PriceCacheWriteAbove: 25, | ||
| 417 | }, | 417 | }, | ||
| 418 | + | { | |||
| 419 | + | Provider: "openai", ID: "gpt-6-sol", DisplayName: "GPT-6 Sol", API: APIResponses, | |||
| 420 | + | ContextWindow: 272000, MaxOutput: 128000, Reasoning: true, | |||
| 421 | + | PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5, | |||
| 422 | + | PriceTierInputTokens: 272000, | |||
| 423 | + | PriceInputAbove: 4, PriceOutputAbove: 15, PriceCacheReadAbove: 0.4, PriceCacheWriteAbove: 5, | |||
| 424 | + | }, | |||
| 425 | + | { | |||
| 426 | + | Provider: "openai", ID: "gpt-6-luna", DisplayName: "GPT-6 Luna", API: APIResponses, | |||
| 427 | + | ContextWindow: 272000, MaxOutput: 128000, Reasoning: true, | |||
| 428 | + | PriceInput: 0.1, PriceOutput: 0.5, PriceCacheRead: 0.01, PriceCacheWrite: 0.125, | |||
| 429 | + | PriceTierInputTokens: 272000, | |||
| 430 | + | PriceInputAbove: 0.2, PriceOutputAbove: 0.75, PriceCacheReadAbove: 0.02, PriceCacheWriteAbove: 0.25, | |||
| 431 | + | }, | |||
| 418 | 432 | ||||
| 419 | // ---- OpenAI Codex / ChatGPT subscription backend ---- | 433 | // ---- OpenAI Codex / ChatGPT subscription backend ---- | ||
| 420 | // Same model ids as the OpenAI family, but routed through the | 434 | // Same model ids as the OpenAI family, but routed through the | ||
| @@ -468,6 +482,20 @@ var Catalog = []Model{ | |||||
| 468 | PriceTierInputTokens: 272000, | 482 | PriceTierInputTokens: 272000, | ||
| 469 | PriceInputAbove: 20, PriceOutputAbove: 75, PriceCacheReadAbove: 2, PriceCacheWriteAbove: 25, | 483 | PriceInputAbove: 20, PriceOutputAbove: 75, PriceCacheReadAbove: 2, PriceCacheWriteAbove: 25, | ||
| 470 | }, | 484 | }, | ||
| 485 | + | { | |||
| 486 | + | Provider: "openai-codex", ID: "gpt-6-sol", DisplayName: "GPT-6 Sol", API: APIResponses, | |||
| 487 | + | ContextWindow: 272000, MaxOutput: 128000, Reasoning: true, | |||
| 488 | + | PriceInput: 2, PriceOutput: 10, PriceCacheRead: 0.2, PriceCacheWrite: 2.5, | |||
| 489 | + | PriceTierInputTokens: 272000, | |||
| 490 | + | PriceInputAbove: 4, PriceOutputAbove: 15, PriceCacheReadAbove: 0.4, PriceCacheWriteAbove: 5, | |||
| 491 | + | }, | |||
| 492 | + | { | |||
| 493 | + | Provider: "openai-codex", ID: "gpt-6-luna", DisplayName: "GPT-6 Luna", API: APIResponses, | |||
| 494 | + | ContextWindow: 272000, MaxOutput: 128000, Reasoning: true, | |||
| 495 | + | PriceInput: 0.1, PriceOutput: 0.5, PriceCacheRead: 0.01, PriceCacheWrite: 0.125, | |||
| 496 | + | PriceTierInputTokens: 272000, | |||
| 497 | + | PriceInputAbove: 0.2, PriceOutputAbove: 0.75, PriceCacheReadAbove: 0.02, PriceCacheWriteAbove: 0.25, | |||
| 498 | + | }, | |||
| 471 | } | 499 | } | ||
| 472 | 500 | ||||
| 473 | // DefaultModel is used when the user does not specify one. | 501 | // DefaultModel is used when the user does not specify one. | ||
M packages/provider/reasoning.go +2 -2
| @@ -259,7 +259,7 @@ func OpenAICompatAnthropicEffort(level string) string { | |||
| 259 | 259 | } | |
| 260 | 260 | ||
| 261 | 261 | // OpenAICodexReasoningEffort maps zot levels onto the Responses API effort | |
| 262 | - | // enum. GPT-5.6 and GPT-6 Astra support native max; other models clamp max to xhigh. | |
| 262 | + | // enum. GPT-5.6 and GPT-6 support native max; other models clamp max to xhigh. | |
| 263 | 263 | func OpenAICodexReasoningEffort(level, model string) string { | |
| 264 | 264 | switch NormalizeReasoning(level) { | |
| 265 | 265 | case "minimum", "low": | |
| @@ -282,5 +282,5 @@ func OpenAICodexReasoningEffort(level, model string) string { | |||
| 282 | 282 | ||
| 283 | 283 | func supportsResponsesMaxEffort(model string) bool { | |
| 284 | 284 | id := strings.ToLower(model) | |
| 285 | - | return strings.HasPrefix(id, "gpt-5.6-") || id == "gpt-6-astra" | |
| 285 | + | return strings.HasPrefix(id, "gpt-5.6-") || id == "gpt-6-astra" || id == "gpt-6-sol" || id == "gpt-6-luna" | |
| 286 | 286 | } | |
| @@ -259,7 +259,7 @@ func OpenAICompatAnthropicEffort(level string) string { | |||||
| 259 | } | 259 | } | ||
| 260 | 260 | ||||
| 261 | // OpenAICodexReasoningEffort maps zot levels onto the Responses API effort | 261 | // OpenAICodexReasoningEffort maps zot levels onto the Responses API effort | ||
| 262 | - | // enum. GPT-5.6 and GPT-6 Astra support native max; other models clamp max to xhigh. | 262 | + | // enum. GPT-5.6 and GPT-6 support native max; other models clamp max to xhigh. |
| 263 | func OpenAICodexReasoningEffort(level, model string) string { | 263 | func OpenAICodexReasoningEffort(level, model string) string { | ||
| 264 | switch NormalizeReasoning(level) { | 264 | switch NormalizeReasoning(level) { | ||
| 265 | case "minimum", "low": | 265 | case "minimum", "low": | ||
| @@ -282,5 +282,5 @@ func OpenAICodexReasoningEffort(level, model string) string { | |||||
| 282 | 282 | ||||
| 283 | func supportsResponsesMaxEffort(model string) bool { | 283 | func supportsResponsesMaxEffort(model string) bool { | ||
| 284 | id := strings.ToLower(model) | 284 | id := strings.ToLower(model) | ||
| 285 | - | return strings.HasPrefix(id, "gpt-5.6-") || id == "gpt-6-astra" | 285 | + | return strings.HasPrefix(id, "gpt-5.6-") || id == "gpt-6-astra" || id == "gpt-6-sol" || id == "gpt-6-luna" |
| 286 | } | 286 | } | ||
M packages/provider/reasoning_test.go +4 -0
| @@ -16,6 +16,8 @@ func TestAvailableReasoningLevels(t *testing.T) { | |||
| 16 | 16 | {name: "responses", model: Model{Reasoning: true, API: APIResponses, ID: "gpt-5.5"}, want: []string{"", "low", "medium", "high", "xhigh"}}, | |
| 17 | 17 | {name: "responses native max", model: Model{Reasoning: true, API: APIResponses, ID: "gpt-5.6-sol"}, want: []string{"", "low", "medium", "high", "xhigh", "max"}}, | |
| 18 | 18 | {name: "astra native max", model: Model{Reasoning: true, API: APIResponses, ID: "gpt-6-astra"}, want: []string{"", "low", "medium", "high", "xhigh", "max"}}, | |
| 19 | + | {name: "sol native max", model: Model{Reasoning: true, API: APIResponses, ID: "gpt-6-sol"}, want: []string{"", "low", "medium", "high", "xhigh", "max"}}, | |
| 20 | + | {name: "luna native max", model: Model{Reasoning: true, API: APIResponses, ID: "gpt-6-luna"}, want: []string{"", "low", "medium", "high", "xhigh", "max"}}, | |
| 19 | 21 | {name: "adaptive", model: Model{Reasoning: true, AdaptiveThinking: true}, want: []string{"", "low", "medium", "high", "xhigh", "max"}}, | |
| 20 | 22 | {name: "adaptive compatible", model: Model{Reasoning: true, AdaptiveThinkingCompat: true}, want: []string{"", "high"}}, | |
| 21 | 23 | {name: "gemini pro", model: Model{Provider: "google", ID: "gemini-3-pro-preview", Reasoning: true}, want: []string{"", "low", "high"}}, | |
| @@ -136,6 +138,8 @@ func TestReasoningEffortMappings(t *testing.T) { | |||
| 136 | 138 | {"max", "gpt-5.5", "high", "max", "xhigh", 32768, "max"}, | |
| 137 | 139 | {"max", "gpt-6-astra", "high", "max", "max", 32768, "max"}, | |
| 138 | 140 | {"max", "GPT-6-ASTRA", "high", "max", "max", 32768, "max"}, | |
| 141 | + | {"max", "gpt-6-sol", "high", "max", "max", 32768, "max"}, | |
| 142 | + | {"max", "gpt-6-luna", "high", "max", "max", 32768, "max"}, | |
| 139 | 143 | {"max", "gpt-6-unknown", "high", "max", "xhigh", 32768, "max"}, | |
| 140 | 144 | } | |
| 141 | 145 | for _, tc := range cases { | |
| @@ -16,6 +16,8 @@ func TestAvailableReasoningLevels(t *testing.T) { | |||||
| 16 | {name: "responses", model: Model{Reasoning: true, API: APIResponses, ID: "gpt-5.5"}, want: []string{"", "low", "medium", "high", "xhigh"}}, | 16 | {name: "responses", model: Model{Reasoning: true, API: APIResponses, ID: "gpt-5.5"}, want: []string{"", "low", "medium", "high", "xhigh"}}, | ||
| 17 | {name: "responses native max", model: Model{Reasoning: true, API: APIResponses, ID: "gpt-5.6-sol"}, want: []string{"", "low", "medium", "high", "xhigh", "max"}}, | 17 | {name: "responses native max", model: Model{Reasoning: true, API: APIResponses, ID: "gpt-5.6-sol"}, want: []string{"", "low", "medium", "high", "xhigh", "max"}}, | ||
| 18 | {name: "astra native max", model: Model{Reasoning: true, API: APIResponses, ID: "gpt-6-astra"}, want: []string{"", "low", "medium", "high", "xhigh", "max"}}, | 18 | {name: "astra native max", model: Model{Reasoning: true, API: APIResponses, ID: "gpt-6-astra"}, want: []string{"", "low", "medium", "high", "xhigh", "max"}}, | ||
| 19 | + | {name: "sol native max", model: Model{Reasoning: true, API: APIResponses, ID: "gpt-6-sol"}, want: []string{"", "low", "medium", "high", "xhigh", "max"}}, | |||
| 20 | + | {name: "luna native max", model: Model{Reasoning: true, API: APIResponses, ID: "gpt-6-luna"}, want: []string{"", "low", "medium", "high", "xhigh", "max"}}, | |||
| 19 | {name: "adaptive", model: Model{Reasoning: true, AdaptiveThinking: true}, want: []string{"", "low", "medium", "high", "xhigh", "max"}}, | 21 | {name: "adaptive", model: Model{Reasoning: true, AdaptiveThinking: true}, want: []string{"", "low", "medium", "high", "xhigh", "max"}}, | ||
| 20 | {name: "adaptive compatible", model: Model{Reasoning: true, AdaptiveThinkingCompat: true}, want: []string{"", "high"}}, | 22 | {name: "adaptive compatible", model: Model{Reasoning: true, AdaptiveThinkingCompat: true}, want: []string{"", "high"}}, | ||
| 21 | {name: "gemini pro", model: Model{Provider: "google", ID: "gemini-3-pro-preview", Reasoning: true}, want: []string{"", "low", "high"}}, | 23 | {name: "gemini pro", model: Model{Provider: "google", ID: "gemini-3-pro-preview", Reasoning: true}, want: []string{"", "low", "high"}}, | ||
| @@ -136,6 +138,8 @@ func TestReasoningEffortMappings(t *testing.T) { | |||||
| 136 | {"max", "gpt-5.5", "high", "max", "xhigh", 32768, "max"}, | 138 | {"max", "gpt-5.5", "high", "max", "xhigh", 32768, "max"}, | ||
| 137 | {"max", "gpt-6-astra", "high", "max", "max", 32768, "max"}, | 139 | {"max", "gpt-6-astra", "high", "max", "max", 32768, "max"}, | ||
| 138 | {"max", "GPT-6-ASTRA", "high", "max", "max", 32768, "max"}, | 140 | {"max", "GPT-6-ASTRA", "high", "max", "max", 32768, "max"}, | ||
| 141 | + | {"max", "gpt-6-sol", "high", "max", "max", 32768, "max"}, | |||
| 142 | + | {"max", "gpt-6-luna", "high", "max", "max", 32768, "max"}, | |||
| 139 | {"max", "gpt-6-unknown", "high", "max", "xhigh", 32768, "max"}, | 143 | {"max", "gpt-6-unknown", "high", "max", "xhigh", 32768, "max"}, | ||
| 140 | } | 144 | } | ||
| 141 | for _, tc := range cases { | 145 | for _, tc := range cases { | ||