diff --git a/.env.example b/.env.example index e726d42112..5f637ee116 100644 --- a/.env.example +++ b/.env.example @@ -587,7 +587,7 @@ GOOGLE_KEY=user_provided #============# OPENAI_API_KEY=user_provided -# OPENAI_MODELS=gpt-5.6,gpt-5.6-terra,gpt-5.6-luna,gpt-5.5,gpt-5.5-pro,chat-latest,gpt-5.4,gpt-5.4-pro,gpt-5.4-mini,gpt-5.4-nano,gpt-5.3-codex,gpt-5.2,gpt-5,gpt-5-codex,gpt-5-mini,gpt-5-nano,o3-pro,o3,o4-mini,gpt-4.1,gpt-4.1-mini,gpt-4.1-nano,o3-mini,o1-pro,o1,gpt-4o,gpt-4o-mini +# OPENAI_MODELS=gpt-6-astra,gpt-5.6,gpt-5.6-terra,gpt-5.6-luna,gpt-5.5,gpt-5.5-pro,chat-latest,gpt-5.4,gpt-5.4-pro,gpt-5.4-mini,gpt-5.4-nano,gpt-5.3-codex,gpt-5.2,gpt-5,gpt-5-codex,gpt-5-mini,gpt-5-nano,o3-pro,o3,o4-mini,gpt-4.1,gpt-4.1-mini,gpt-4.1-nano,o3-mini,o1-pro,o1,gpt-4o,gpt-4o-mini DEBUG_OPENAI=false diff --git a/api/package.json b/api/package.json index d015228905..71959412f2 100644 --- a/api/package.json +++ b/api/package.json @@ -46,7 +46,7 @@ "@azure/storage-blob": "^12.30.0", "@google/genai": "^2.8.0", "@keyv/redis": "5.1.6", - "@librechat/agents": "^3.7.19", + "@librechat/agents": "^3.7.20", "@librechat/api": "*", "@librechat/data-schemas": "*", "@microsoft/microsoft-graph-client": "^3.0.7", diff --git a/package-lock.json b/package-lock.json index f411f9d288..12bb93691e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,7 +63,7 @@ "@azure/storage-blob": "^12.30.0", "@google/genai": "^2.8.0", "@keyv/redis": "5.1.6", - "@librechat/agents": "^3.7.19", + "@librechat/agents": "^3.7.20", "@librechat/api": "*", "@librechat/data-schemas": "*", "@microsoft/microsoft-graph-client": "^3.0.7", @@ -10627,9 +10627,9 @@ } }, "node_modules/@librechat/agents": { - "version": "3.7.19", - "resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.7.19.tgz", - "integrity": "sha512-/dHHKBvLoXjX1hFkDJBGP5CjkMp1vS+e3OyLqffa4c7JCenFhOHThBDUdZmCDvoEylRhima0h3fYl/JqCR8+hQ==", + "version": "3.7.20", + "resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.7.20.tgz", + "integrity": "sha512-YnfBpC5MK7QDgP+6VpmOYBTA2mXbaA+fDggfi7n/bsB56FSqenCzizSpC+Nn83lhwJz7GNntJpYNX53nRhFGgw==", "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "^0.115.0", @@ -42681,7 +42681,7 @@ "@azure/storage-blob": "^12.30.0", "@google/genai": "^2.8.0", "@keyv/redis": "5.1.6", - "@librechat/agents": "^3.7.19", + "@librechat/agents": "^3.7.20", "@librechat/data-schemas": "*", "@modelcontextprotocol/sdk": "^1.30.0", "@opentelemetry/api": "^1.9.0", diff --git a/packages/api/package.json b/packages/api/package.json index d8193a0e3c..66f436c601 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -113,7 +113,7 @@ "@azure/storage-blob": "^12.30.0", "@google/genai": "^2.8.0", "@keyv/redis": "5.1.6", - "@librechat/agents": "^3.7.19", + "@librechat/agents": "^3.7.20", "@librechat/data-schemas": "*", "@modelcontextprotocol/sdk": "^1.30.0", "@opentelemetry/api": "^1.9.0", diff --git a/packages/api/src/endpoints/openai/config.backward-compat.spec.ts b/packages/api/src/endpoints/openai/config.backward-compat.spec.ts index acc3496b2b..f89482f534 100644 --- a/packages/api/src/endpoints/openai/config.backward-compat.spec.ts +++ b/packages/api/src/endpoints/openai/config.backward-compat.spec.ts @@ -33,6 +33,7 @@ describe('getOpenAIConfig - Backward Compatibility', () => { streaming: true, model: 'gpt-5-nano', useResponsesApi: true, + firstPartyEndpoint: true, user: 'some-user', apiKey: 'sk-proj-somekey', reasoning: { diff --git a/packages/api/src/endpoints/openai/llm.spec.ts b/packages/api/src/endpoints/openai/llm.spec.ts index 23db811e1d..faa5edc25c 100644 --- a/packages/api/src/endpoints/openai/llm.spec.ts +++ b/packages/api/src/endpoints/openai/llm.spec.ts @@ -709,6 +709,122 @@ describe('getOpenAILLMConfig', () => { }); }); + describe('GPT-6 Astra Responses API routing', () => { + const astraConfig = (overrides: Record = {}) => + getOpenAILLMConfig({ + apiKey: 'test-api-key', + streaming: true, + endpoint: EModelEndpoint.openAI, + modelOptions: { model: 'gpt-6-astra' }, + ...overrides, + }); + + it('routes every Astra turn to the Responses API, not only reasoning ones', () => { + expect(astraConfig().llmConfig).toHaveProperty('useResponsesApi', true); + }); + + /** + * The reason routing is decided here rather than at invocation time: the + * max-tokens field is shaped from `useResponsesApi`, so switching APIs later + * would send `max_completion_tokens` to an endpoint expecting + * `max_output_tokens`. + */ + it('shapes max tokens for the API it actually uses', () => { + const result = astraConfig({ + modelOptions: { model: 'gpt-6-astra', max_tokens: 4096 }, + }); + const kwargs = (result.llmConfig.modelKwargs ?? {}) as Record; + expect(kwargs).not.toHaveProperty('max_completion_tokens'); + expect(kwargs.max_output_tokens ?? result.llmConfig.maxTokens).toBeDefined(); + }); + + it('keeps routing when a drop rule only clears reasoning_effort', () => { + /** + * Unlike the GPT-5.6 default, Astra's routing is not reasoning-driven, so + * a rule clearing an unsupported stored effort must not disable it. + */ + expect(astraConfig({ dropParams: ['reasoning_effort'] }).llmConfig).toHaveProperty( + 'useResponsesApi', + true, + ); + }); + + it('respects an explicit opt-out', () => { + expect(astraConfig({ dropParams: ['useResponsesApi'] }).llmConfig).not.toHaveProperty( + 'useResponsesApi', + true, + ); + }); + + it('leaves a custom gateway on its configured path', () => { + expect(astraConfig({ baseURL: 'https://gateway.internal/v1' }).llmConfig).not.toHaveProperty( + 'useResponsesApi', + true, + ); + }); + + it('does not change routing for other models', () => { + const result = getOpenAILLMConfig({ + apiKey: 'test-api-key', + streaming: true, + endpoint: EModelEndpoint.openAI, + modelOptions: { model: 'gpt-5.5' }, + }); + expect(result.llmConfig).not.toHaveProperty('useResponsesApi', true); + }); + }); + + describe('First-party endpoint declaration', () => { + /** + * The agents SDK gates its model-specific request constraints on this flag + * and defaults them off, rather than inferring the endpoint from a base + * URL. Only this layer can tell a faithful first-party route from a + * gateway, so the decision is made here and declared downstream. + */ + const configFor = (overrides: Record = {}) => + getOpenAILLMConfig({ + apiKey: 'test-api-key', + streaming: true, + endpoint: EModelEndpoint.openAI, + modelOptions: { model: 'gpt-6-astra' }, + ...overrides, + }); + + it('declares the first-party endpoint for canonical OpenAI', () => { + expect(configFor().llmConfig).toHaveProperty('firstPartyEndpoint', true); + }); + + it('declares it for an explicit api.openai.com base URL', () => { + expect(configFor({ baseURL: 'https://api.openai.com/v1' }).llmConfig).toHaveProperty( + 'firstPartyEndpoint', + true, + ); + }); + + /** + * Astra is not documented as available on Azure OpenAI, and Azure's + * first-party hosts do not satisfy the OpenAI-host check, so declaring it + * there would claim a surface this cannot verify. + */ + it('does not declare it for Azure OpenAI', () => { + expect(configFor({ endpoint: EModelEndpoint.azureOpenAI }).llmConfig).not.toHaveProperty( + 'firstPartyEndpoint', + ); + }); + + it('does not declare it for a custom gateway base URL', () => { + expect(configFor({ baseURL: 'https://gateway.internal/v1' }).llmConfig).not.toHaveProperty( + 'firstPartyEndpoint', + ); + }); + + it('does not declare it for a non-OpenAI endpoint', () => { + expect(configFor({ endpoint: EModelEndpoint.custom }).llmConfig).not.toHaveProperty( + 'firstPartyEndpoint', + ); + }); + }); + describe('GPT-5.6 Responses API Requirement', () => { it.each(['gpt-5.6-terra', 'gpt-5.6-luna', 'gpt-5.6-sol', 'gpt-5.6'])( 'should default to Responses API for %s when reasoning_effort is set', diff --git a/packages/api/src/endpoints/openai/llm.ts b/packages/api/src/endpoints/openai/llm.ts index 282aca2a17..04f02e1810 100644 --- a/packages/api/src/endpoints/openai/llm.ts +++ b/packages/api/src/endpoints/openai/llm.ts @@ -130,6 +130,24 @@ function isOpenAIEndpoint(endpoint?: EModelEndpoint | string | null): boolean { */ const responsesApiRequiredPattern = /\bgpt-5\.6\b/; +/** + * Models that take the Responses API for every turn, not only reasoning ones. + * OpenAI's guidance for GPT-6 Astra is to use Responses, and tool calls require + * it outright. + * + * Decided here rather than in the agents SDK at invocation time: the max-tokens + * field below is shaped from `useResponsesApi`, so a later switch would send + * `max_completion_tokens` to an endpoint expecting `max_output_tokens`. Config + * time is also the only place that knows the model before Azure replaces it + * with a deployment name. + * @see https://developers.openai.com/api/docs/guides/latest-model + */ +const responsesApiPreferredPattern = /^gpt-6-astra(?:-|$)/i; + +function prefersResponsesApi(model?: string): boolean { + return typeof model === 'string' && responsesApiPreferredPattern.test(model); +} + function requiresResponsesApiForReasoning({ model, reasoningEffort, @@ -848,6 +866,14 @@ export function getOpenAILLMConfig({ const responsesApiOptedOut = dropParams != null && (dropParams.includes('reasoning_effort') || dropParams.includes('useResponsesApi')); + /** + * The GPT-5.6 default above is reasoning-driven, so dropping `reasoning_effort` + * removes its reason to route. Astra's is not: it takes Responses for every + * turn, and a drop rule clearing an unsupported stored effort must not also + * disable its routing. Only an explicit `useResponsesApi` drop does that. + */ + const responsesApiExplicitlyOptedOut = + dropParams != null && dropParams.includes('useResponsesApi'); if ( !useOpenRouter && endpoint === EModelEndpoint.openAI && @@ -860,6 +886,38 @@ export function getOpenAILLMConfig({ llmConfig.useResponsesApi = true; } + /** + * Route GPT-6 Astra to the Responses API for every turn. Unlike the GPT-5.6 + * rule above this does not depend on reasoning params: Astra serves tool calls + * only from Responses, and OpenAI recommends it generally. + */ + if ( + !useOpenRouter && + endpoint === EModelEndpoint.openAI && + isCanonicalOpenAIBaseURL(baseURL) && + llmConfig.useResponsesApi == null && + !responsesApiExplicitlyOptedOut && + prefersResponsesApi(llmConfig.model) + ) { + llmConfig.useResponsesApi = true; + } + + /** + * Declare the first-party surface for the agents SDK's model-specific request + * constraints. Computed here, from the same checks the Responses default + * above uses, so the decision lives in one place: OpenRouter and custom + * gateways route through endpoints whose contract is not OpenAI's, and only + * this layer can tell them apart. + * + * Scoped to the canonical OpenAI endpoint. Astra is not documented as + * available on Azure OpenAI, and Azure's first-party hosts do not satisfy the + * OpenAI-host check, so declaring it there would claim a surface this cannot + * verify. + */ + if (!useOpenRouter && endpoint === EModelEndpoint.openAI && isCanonicalOpenAIBaseURL(baseURL)) { + llmConfig.firstPartyEndpoint = true; + } + if (!useOpenRouter) { hasModelKwargs = applyReasoningConfig({ diff --git a/packages/api/src/types/openai.ts b/packages/api/src/types/openai.ts index e0c7923ed7..8e05b0711b 100644 --- a/packages/api/src/types/openai.ts +++ b/packages/api/src/types/openai.ts @@ -36,6 +36,15 @@ export type OAIClientOptions = Omit & { includeReasoningContent?: boolean; promptCache?: boolean; promptCacheTtl?: '5m' | '1h'; + /** + * Declares that this client talks to the first-party OpenAI surface, which is + * what gates the agents SDK's model-specific request constraints (GPT-6 + * Astra: Responses-only tool calls, rejected sampling parameters, + * unsupported reasoning efforts). The SDK defaults them off and takes this as + * a declaration rather than inferring it from a base URL, because only this + * layer knows whether a URL is a faithful first-party route or a gateway. + */ + firstPartyEndpoint?: boolean; _lc_stream_delay?: number; verbosity?: string | null; }; diff --git a/packages/api/src/utils/tokens.spec.ts b/packages/api/src/utils/tokens.spec.ts index aac009bce3..8d9070e149 100644 --- a/packages/api/src/utils/tokens.spec.ts +++ b/packages/api/src/utils/tokens.spec.ts @@ -91,6 +91,24 @@ describe('Gemini 3.8 Flash', () => { }); }); +describe('GPT-6 Astra', () => { + it('resolves 1.05M context and 128K output', () => { + expect(getModelMaxTokens('gpt-6-astra', EModelEndpoint.openAI)).toBe(1050000); + expect(getModelMaxOutputTokens('gpt-6-astra', EModelEndpoint.openAI)).toBe(128000); + }); + + it('matches its own key for snapshots and provider prefixes', () => { + for (const model of [ + 'gpt-6-astra-2026-04-30', + 'openai/gpt-6-astra', + 'gpt-6-astra-2026-04-30/openai', + ]) { + expect(getModelMaxTokens(model, EModelEndpoint.openAI)).toBe(1050000); + expect(getModelMaxOutputTokens(model, EModelEndpoint.openAI)).toBe(128000); + } + }); +}); + describe('Qwen3.5 and later generations', () => { it('resolves 262K for the vLLM model id that regressed to the qwen3 window', () => { expect(getModelMaxTokens('Qwen/Qwen3.5-397B-A17B-FP8', EModelEndpoint.openAI)).toBe(262144); diff --git a/packages/api/src/utils/tokens.ts b/packages/api/src/utils/tokens.ts index 951f44a020..38048affb6 100644 --- a/packages/api/src/utils/tokens.ts +++ b/packages/api/src/utils/tokens.ts @@ -64,6 +64,7 @@ const openAIModels = { 'gpt-5.6': 1050000, 'gpt-5.6-terra': 1050000, 'gpt-5.6-luna': 1050000, + 'gpt-6-astra': 1050000, // >272K input prices at the long-context tier (2x input/cache, 1.5x output) 'chat-latest': 400000, 'gpt-5-mini': 400000, 'gpt-5-nano': 400000, @@ -508,6 +509,7 @@ export const modelMaxOutputs = { 'gpt-5.6': 128000, 'gpt-5.6-terra': 128000, 'gpt-5.6-luna': 128000, + 'gpt-6-astra': 128000, 'chat-latest': 128000, 'gpt-5-mini': 128000, 'gpt-5-nano': 128000, diff --git a/packages/data-provider/specs/astra-catalog.spec.ts b/packages/data-provider/specs/astra-catalog.spec.ts new file mode 100644 index 0000000000..3b0d6580ce --- /dev/null +++ b/packages/data-provider/specs/astra-catalog.spec.ts @@ -0,0 +1,45 @@ +import { EModelEndpoint } from '../src/schemas'; +import { defaultModels, initialModelsConfig } from '../src/config'; + +/** + * GPT-6 Astra serves tool calls only from the Responses API. The Assistants + * endpoints do not route through `getOpenAILLMConfig`, so listing it there + * would offer a configuration the provider rejects. + */ +describe('GPT-6 Astra catalog placement', () => { + it('is offered on the endpoints that route through the OpenAI config', () => { + expect(defaultModels[EModelEndpoint.openAI]).toContain('gpt-6-astra'); + expect(defaultModels[EModelEndpoint.agents]).toContain('gpt-6-astra'); + }); + + it('is kept out of both Assistants catalogs', () => { + expect(defaultModels[EModelEndpoint.assistants]).not.toContain('gpt-6-astra'); + expect(defaultModels[EModelEndpoint.azureAssistants]).not.toContain('gpt-6-astra'); + }); + + it('is kept out of the initial Azure and Assistants catalogs', () => { + /** + * Azure shares the OpenAI list, but Astra gets neither Responses routing nor + * its request constraints there — and being first in the list would let it + * become the default selection. + */ + expect(initialModelsConfig[EModelEndpoint.azureOpenAI]).not.toContain('gpt-6-astra'); + expect(initialModelsConfig[EModelEndpoint.assistants]).not.toContain('gpt-6-astra'); + }); + + it('keeps Astra in the initial catalogs that can run it', () => { + expect(initialModelsConfig[EModelEndpoint.openAI]).toContain('gpt-6-astra'); + expect(initialModelsConfig[EModelEndpoint.agents]).toContain('gpt-6-astra'); + }); + + it('does not disturb the rest of the shared OpenAI catalog', () => { + for (const endpoint of [ + EModelEndpoint.openAI, + EModelEndpoint.agents, + EModelEndpoint.assistants, + EModelEndpoint.azureAssistants, + ]) { + expect(defaultModels[endpoint]).toContain('gpt-5.6'); + } + }); +}); diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts index 7cbea42d19..852293df94 100644 --- a/packages/data-provider/src/config.ts +++ b/packages/data-provider/src/config.ts @@ -2695,6 +2695,15 @@ export const alternateName = { [KnownEndpoints.helicone]: 'Helicone', }; +/** + * Models the Assistants endpoints cannot run. GPT-6 Astra serves tool calls only + * from the Responses API, and the Assistants surface does not route through + * `getOpenAILLMConfig`, so listing it there would offer a configuration the + * provider rejects. Kept out of `sharedOpenAIModels`, which both Assistants + * catalogs consume. + */ +const responsesOnlyOpenAIModels = ['gpt-6-astra']; + const sharedOpenAIModels = [ 'gpt-5.6', 'gpt-5.6-terra', @@ -2797,7 +2806,8 @@ export const bedrockModels = [ export const defaultModels = { [EModelEndpoint.azureAssistants]: sharedOpenAIModels, [EModelEndpoint.assistants]: [...sharedOpenAIModels, 'chatgpt-4o-latest'], - [EModelEndpoint.agents]: sharedOpenAIModels, // TODO: Add agent models (agentsModels) + // TODO: Add agent models (agentsModels) + [EModelEndpoint.agents]: [...responsesOnlyOpenAIModels, ...sharedOpenAIModels], [EModelEndpoint.google]: [ // Gemini 3.8 Models 'gemini-3.8-flash', @@ -2822,6 +2832,7 @@ export const defaultModels = { ], [EModelEndpoint.anthropic]: sharedAnthropicModels, [EModelEndpoint.openAI]: [ + ...responsesOnlyOpenAIModels, ...sharedOpenAIModels, 'chatgpt-4o-latest', 'gpt-4-vision-preview', @@ -2837,12 +2848,22 @@ const fitlerAssistantModels = (str: string) => { const openAIModels = defaultModels[EModelEndpoint.openAI]; +/** + * The OpenAI catalog without the models only the first-party OpenAI endpoint + * can run. Azure OpenAI shares this list, but Astra is neither routed to the + * Responses API nor given its request constraints there, and listing it first + * would let it become the default selection. + */ +const nonResponsesOnlyOpenAIModels = openAIModels.filter( + (model) => !responsesOnlyOpenAIModels.includes(model), +); + export const initialModelsConfig: TModelsConfig = { initial: [], [EModelEndpoint.openAI]: openAIModels, [EModelEndpoint.assistants]: openAIModels.filter(fitlerAssistantModels), [EModelEndpoint.agents]: openAIModels, // TODO: Add agent models (agentsModels) - [EModelEndpoint.azureOpenAI]: openAIModels, + [EModelEndpoint.azureOpenAI]: nonResponsesOnlyOpenAIModels, [EModelEndpoint.google]: defaultModels[EModelEndpoint.google], [EModelEndpoint.anthropic]: defaultModels[EModelEndpoint.anthropic], [EModelEndpoint.bedrock]: defaultModels[EModelEndpoint.bedrock], diff --git a/packages/data-schemas/src/methods/tx.spec.ts b/packages/data-schemas/src/methods/tx.spec.ts index 676484c963..0630ba57ab 100644 --- a/packages/data-schemas/src/methods/tx.spec.ts +++ b/packages/data-schemas/src/methods/tx.spec.ts @@ -470,6 +470,59 @@ describe('getMultiplier', () => { } }); + it('should use the documented gpt-6-astra pricing', () => { + expect(tokenValues['gpt-6-astra']).toEqual({ prompt: 10, completion: 50 }); + expect(cacheTokenValues['gpt-6-astra']).toEqual({ write: 12.5, read: 1 }); + expect(premiumTokenValues['gpt-6-astra']).toEqual({ + threshold: 272000, + prompt: 20, + completion: 75, + }); + expect(premiumCacheTokenValues['gpt-6-astra']).toEqual({ + threshold: 272000, + write: 25, + read: 2, + }); + }); + + it('should bill gpt-6-astra cache writes at the documented 1.25x input surcharge', () => { + expect(cacheTokenValues['gpt-6-astra'].write).toBeCloseTo( + tokenValues['gpt-6-astra'].prompt * 1.25, + ); + }); + + it('should apply the documented gpt-6-astra long-context multipliers', () => { + const standard = tokenValues['gpt-6-astra']; + const premium = premiumTokenValues['gpt-6-astra']; + const standardCache = cacheTokenValues['gpt-6-astra']; + const premiumCache = premiumCacheTokenValues['gpt-6-astra']; + /** >272K input: 2x input and cache rates, 1.5x output, for the full request. */ + expect(premium.prompt).toBeCloseTo(standard.prompt * 2); + expect(premium.completion).toBeCloseTo(standard.completion * 1.5); + expect(premiumCache.write).toBeCloseTo(standardCache.write * 2); + expect(premiumCache.read).toBeCloseTo(standardCache.read * 2); + }); + + it('should resolve gpt-6-astra to its own key rather than a gpt-6 prefix', () => { + for (const model of [ + 'gpt-6-astra', + 'gpt-6-astra-2026-04-30', + 'openai/gpt-6-astra', + 'gpt-6-astra/openai', + ]) { + expect(getValueKey(model)).toBe('gpt-6-astra'); + expect(getMultiplier({ model, tokenType: 'prompt' })).toBe(10); + expect(getMultiplier({ model, tokenType: 'completion' })).toBe(50); + } + }); + + it('should charge gpt-6-astra premium rates only past the 272K threshold', () => { + const model = 'gpt-6-astra'; + expect(getMultiplier({ model, tokenType: 'prompt', inputTokenCount: 272000 })).toBe(10); + expect(getMultiplier({ model, tokenType: 'prompt', inputTokenCount: 272001 })).toBe(20); + expect(getMultiplier({ model, tokenType: 'completion', inputTokenCount: 272001 })).toBe(75); + }); + it('should use the documented gpt-5.6 pricing', () => { const expectedPricing = { 'gpt-5.6': { diff --git a/packages/data-schemas/src/methods/tx.ts b/packages/data-schemas/src/methods/tx.ts index d2e3548807..acc9e2d001 100644 --- a/packages/data-schemas/src/methods/tx.ts +++ b/packages/data-schemas/src/methods/tx.ts @@ -150,6 +150,7 @@ export const tokenValues: Record 'gpt-5.6': { prompt: 4, completion: 20 }, 'gpt-5.6-terra': { prompt: 2, completion: 12 }, 'gpt-5.6-luna': { prompt: 0.2, completion: 1.2 }, + 'gpt-6-astra': { prompt: 10, completion: 50 }, 'chat-latest': { prompt: 5, completion: 30 }, 'gpt-5-chat-latest': { prompt: 1.25, completion: 10 }, 'gpt-5.1-chat-latest': { prompt: 1.25, completion: 10 }, @@ -389,6 +390,7 @@ export const cacheTokenValues: Record = 'gpt-5.6': { write: 5, read: 0.4 }, 'gpt-5.6-terra': { write: 2.5, read: 0.2 }, 'gpt-5.6-luna': { write: 0.25, read: 0.02 }, + 'gpt-6-astra': { write: 12.5, read: 1 }, 'chat-latest': { write: 5, read: 0.5 }, 'gpt-5-chat-latest': { write: 1.25, read: 0.125 }, 'gpt-5.1-chat-latest': { write: 1.25, read: 0.125 }, @@ -445,6 +447,7 @@ export const premiumTokenValues: Record< 'gpt-5.6': { threshold: 272000, prompt: 8, completion: 30 }, 'gpt-5.6-terra': { threshold: 272000, prompt: 4, completion: 18 }, 'gpt-5.6-luna': { threshold: 272000, prompt: 0.4, completion: 1.8 }, + 'gpt-6-astra': { threshold: 272000, prompt: 20, completion: 75 }, 'grok-4.5': { threshold: 200000, prompt: 4, completion: 12 }, 'grok-4-5': { threshold: 200000, prompt: 4, completion: 12 }, 'grok-4.6': { threshold: 200000, prompt: 4, completion: 12 }, @@ -466,6 +469,7 @@ export const premiumCacheTokenValues: Record< 'gpt-5.6': { threshold: 272000, write: 10, read: 0.8 }, 'gpt-5.6-terra': { threshold: 272000, write: 5, read: 0.4 }, 'gpt-5.6-luna': { threshold: 272000, write: 0.5, read: 0.04 }, + 'gpt-6-astra': { threshold: 272000, write: 25, read: 2 }, }; export function createTxMethods(