Compare commits

...

17 Commits

Author SHA1 Message Date
Enrico Ros 9fc0b39730 AIX: Transmit token stop errors, if provided 2026-04-24 17:08:40 -07:00
Enrico Ros 194bfe23a1 AIX: OpenAI: mark the need for roundtrip of hosted tool pairs 2026-04-24 17:08:40 -07:00
Enrico Ros 35110480ef Beam: Fix ghost columns. Fixes #1073 2026-04-24 16:04:29 -07:00
Enrico Ros 959595e33a Merge: smaller copy update 2026-04-24 16:04:29 -07:00
Enrico Ros a960424dfb Merge: copy update. Fixes #1083 2026-04-24 15:56:13 -07:00
Enrico Ros 0df6c7d08b Merge: copy. Fixes #1083 2026-04-24 15:48:56 -07:00
Enrico Ros 65c841e7a7 Roll AIX 2026-04-24 15:23:30 -07:00
Enrico Ros b21b8cc982 AIX: Anthropic: show refusal details, if present, as inline text 2026-04-24 15:20:10 -07:00
Enrico Ros aa2c4f06b7 AI Inspector: compress intermediate large string fields 2026-04-24 15:19:35 -07:00
Enrico Ros b8d7b4ec10 AIX: OpenAI: fix svs on !ma for for NS 2026-04-24 15:19:35 -07:00
Enrico Ros c48520255a AIX: OpenAI: fix tool reparsing for NS 2026-04-24 15:19:34 -07:00
Enrico Ros 0790da989d Don't truncate the Beam Title on Edit. Fix #1085 part 1. 2026-04-24 15:19:34 -07:00
Enrico Ros 506d24d2fd AIX: OpenAI Response: fix reparse of tools 2026-04-24 15:19:34 -07:00
Enrico Ros 1348dbf493 AIX: update _upstreams 2026-04-24 15:19:33 -07:00
Enrico Ros ce677f3cd9 LLMs: OpenAI: GPT 5.5 2026-04-24 15:19:33 -07:00
Enrico Ros 39203d78e3 LLMs: OpenAI: hide lots of older models, so by default the lastest are shown 2026-04-24 15:19:33 -07:00
Enrico Ros 2ef7daf369 LLMs: Gemini: hide 3.0 Pro (silently remapped to 3.1 by Gemini). Fixes #1082 2026-04-24 15:19:33 -07:00
24 changed files with 273 additions and 86 deletions
@@ -33,7 +33,10 @@ const _styles = {
} as const,
'& nav > ol > li:first-of-type': {
overflow: 'hidden',
maxWidth: { xs: '110px', md: '140px' },
// allow the chat title to use available space, shrinking gracefully when the bar is narrow
// NOTE: already performed by virtue of the breadcrumb having agi-ellipsize on the crumbs
// flexShrink: 1,
// minWidth: '60px',
} as const,
} as const,
+1 -1
View File
@@ -23,7 +23,7 @@ export const Release = {
// this is here to trigger revalidation of data, e.g. models refresh
Monotonics: {
Aix: 68,
Aix: 69,
NewsVersion: 204,
},
+4 -1
View File
@@ -905,9 +905,12 @@ export class ContentReassembler {
/**
* Stores raw termination data from the wire - classification deferred to finalizeReassembly()
*/
private onCGEnd({ terminationReason, tokenStopReason }: Extract<AixWire_Particles.ChatGenerateOp, { cg: 'end' }>): void {
private onCGEnd({ terminationReason, tokenStopReason, tokenStopError }: Extract<AixWire_Particles.ChatGenerateOp, { cg: 'end' }>): void {
this.S.terminationReason = terminationReason;
this.S.dialectStopReason = tokenStopReason;
// Vendor-composed stop error, surfaced as a complementary error fragment alongside the generic classification message
if (tokenStopError)
this._appendErrorFragment(tokenStopError);
}
/**
@@ -7,6 +7,7 @@ import { Box, Card, Chip, Divider, Sheet, Typography } from '@mui/joy';
import { RenderCodeMemo } from '~/modules/blocks/code/RenderCode';
import { ExpanderControlledBox } from '~/common/components/ExpanderControlledBox';
import { objectDeepCloneWithStringLimit } from '~/common/util/objectUtils';
import TimelapseIcon from '@mui/icons-material/Timelapse';
import type { AixClientDebugger } from './memstore-aix-client-debugger';
@@ -184,12 +185,10 @@ export function AixDebuggerFrame(props: {
{/* List of particles */}
{frame.particles.map((particle, idx) => {
// truncated preview of particle content
// preview of particle content: preserve structure, trim long string fields
let jsonPreview = '';
try {
const content = particle.content;
jsonPreview = JSON.stringify(content).substring(0, 1024);
if (jsonPreview.length >= 1024) jsonPreview += '...';
jsonPreview = JSON.stringify(objectDeepCloneWithStringLimit(particle.content, 'aix-debugger-particle', 64));
} catch (e) {
jsonPreview = 'Error parsing content';
}
+1 -1
View File
@@ -689,7 +689,7 @@ export namespace AixWire_Particles {
export type ChatControlOp =
// | { cg: 'start' } // not really used for now
| { cg: 'end', terminationReason: CGEndReason /* we know why we're sending 'end' */, tokenStopReason?: GCTokenStopReason /* we may or not have gotten a logical token stop reason from the dispatch */ }
| { cg: 'end', terminationReason: CGEndReason /* we know why we're sending 'end' */, tokenStopReason?: GCTokenStopReason /* we may or not have gotten a logical token stop reason from the dispatch */, tokenStopError?: string /* optional vendor-composed human-readable detail paired with tokenStopReason */ }
| { cg: 'issue', issueId: CGIssueId, issueText: string }
| { cg: 'aix-info', ait: 'flow-cont' /* important: establishes a checkpoint */, text: string }
| { cg: 'aix-retry-reset', rScope: 'srv-dispatch' | 'srv-op' | 'cli-ll', rClearStrategy: 'none' | 'since-checkpoint' | 'all', reason: string, attempt: number, maxAttempts: number, delayMs: number, causeHttp?: number, causeConn?: string }
@@ -56,6 +56,7 @@ export class ChatGenerateTransmitter implements IParticleTransmitter {
// Token stop reason
private tokenStopReason: AixWire_Particles.GCTokenStopReason | undefined = undefined;
private tokenStopError: string | undefined = undefined;
// Metrics
private accMetrics: AixWire_Particles.CGSelectMetrics | undefined = undefined;
@@ -105,6 +106,7 @@ export class ChatGenerateTransmitter implements IParticleTransmitter {
cg: 'end',
terminationReason: this.terminationReason,
tokenStopReason: this.tokenStopReason, // See NOTE above - || (dispatchOrDialectIssue ? 'cg-issue' : 'ok'),
...(this.tokenStopError && { tokenStopError: this.tokenStopError }),
});
// Keep this in a terminated state, so that every subsequent call will yield errors (not implemented)
// this.terminationReason = null;
@@ -201,12 +203,13 @@ export class ChatGenerateTransmitter implements IParticleTransmitter {
this.setDialectEnded('issue-dialect');
}
setTokenStopReason(reason: AixWire_Particles.GCTokenStopReason) {
setTokenStopReason(reason: AixWire_Particles.GCTokenStopReason, errorText?: string) {
if (SERVER_DEBUG_WIRE)
console.log('|token-stop|', reason);
console.log('|token-stop|', reason, errorText ?? '');
if (this.tokenStopReason && this.tokenStopReason !== reason)
console.warn(`[Aix.${this.prettyDialect}] setTokenStopReason('${reason}'): already has token stop reason '${this.tokenStopReason}' (overriding)`);
this.tokenStopReason = reason;
if (errorText) this.tokenStopError = errorText;
}
@@ -15,8 +15,8 @@ export interface IParticleTransmitter {
/** End the current part and flush it, which also calls `setDialectEnded('issue-dialect')` */
setDialectTerminatingIssue(dialectText: string, symbol: string | null, serverLog: ParticleServerLogLevel): void;
/** Communicates the finish reason to the client - Data only, this does not do Control, like the above */
setTokenStopReason(reason: AixWire_Particles.GCTokenStopReason): void;
/** Communicates the finish reason to the client - Data only. Optional `errorText` is a vendor-composed string rendered as a complementary error fragment alongside the generic classification message. */
setTokenStopReason(reason: AixWire_Particles.GCTokenStopReason, errorText?: string): void;
// Parts data //
@@ -404,7 +404,7 @@ export function createAnthropicMessageParser(): ChatGenerateParseFunction {
// -> Token Stop Reason
const tokenStopReason = _fromAnthropicStopReason(delta.stop_reason, 'message_delta');
if (tokenStopReason !== null)
pt.setTokenStopReason(tokenStopReason);
pt.setTokenStopReason(tokenStopReason, _formatAnthropicStopError(delta.stop_details));
// NOTE: we have more fields we're not parsing yet - https://platform.claude.com/docs/en/api/typescript/messages#message_delta_usage
if (usage?.output_tokens && messageStartTime) {
@@ -511,6 +511,7 @@ export function createAnthropicMessageParserNS(): ChatGenerateParseFunction {
content,
container,
stop_reason,
stop_details,
usage,
} = AnthropicWire_API_Message_Create.Response_schema.parse(JSON.parse(fullData));
@@ -653,7 +654,7 @@ export function createAnthropicMessageParserNS(): ChatGenerateParseFunction {
// -> Token Stop Reason (pause_turn already thrown above)
const tokenStopReason = _fromAnthropicStopReason(stop_reason, 'parser_NS');
if (tokenStopReason !== null)
pt.setTokenStopReason(tokenStopReason);
pt.setTokenStopReason(tokenStopReason, _formatAnthropicStopError(stop_details));
};
}
@@ -681,6 +682,19 @@ function _emitContainerState(pt: IParticleTransmitter, container: { id: string;
});
}
/** Compose a human-readable error string from Anthropic's stop_details. Returns undefined when nothing useful to surface. */
function _formatAnthropicStopError(stopDetails: { type: string; category?: string | null; explanation?: string | null } | null | undefined): string | undefined {
if (!stopDetails) return undefined;
if (stopDetails.type !== 'refusal') {
aixResilientUnknownValue('Anthropic', 'stopDetailsType', stopDetails.type);
return undefined;
}
const parts: string[] = [];
if (stopDetails.category) parts.push(`[${stopDetails.category}]`);
if (stopDetails.explanation) parts.push(stopDetails.explanation);
return parts.length ? `Refusal: ${parts.join(' ')}` : undefined;
}
// --- Shared server tool result handlers (used by both S and NS parsers) ---
@@ -18,6 +18,21 @@ const OPENAI_RESPONSES_SAME_PART_SPACER = '\n\n';
const INLINE_IMAGE_SKIP_RESIZE_MAX_B64_BYTES = 250_000; // skip resize for small images (e.g. code interpreter charts)
/**
* Wishlist marker: hosted tool calls (web_search_call, image_generation_call, code_interpreter_call, ...)
* are rendered via ephemeral OperationState/inline-asset paths and are NOT round-tripped as structured
* fragments. This breaks stateless multi-turn with reasoning models. See PRD.FUTURE-atol.md "Wishlist:
* Hosted tool invocations as first-class fragments".
*/
// const _hostedToolWishlistSeen = new Set<string>();
function _hostedToolWishlistHint(family: 'web_search' | 'image_generation' | 'code_interpreter' | 'custom_tool'): void {
// if (_hostedToolWishlistSeen.has(family)) return;
// _hostedToolWishlistSeen.add(family);
// NOTE: disable the log because it's logging all the time evenrwyehre; just implement this
// console.log(`[DEV] AIX: ATOL wishlist - hosted '${family}' call observed; not round-tripped as a structured fragment yet (see kb/product/PRD.FUTURE-atol.md)`);
}
/**
* Safely sanitizes a URL for display in placeholders by removing query parameters and paths
* to prevent leaking sensitive information while keeping the domain recognizable.
@@ -46,6 +61,11 @@ type TEventType = OpenAIWire_API_Responses.StreamingEvent['type'];
// cached config for the image_generation hosted tool, captured at response.created
type TImageGenToolCfg = Extract<OpenAIWire_Responses_Tools.Tool, { type: 'image_generation' }>;
/** Extract the image_generation tool config from the echoed tools array (API does not echo `model` per-item). Shared by streaming and non-streaming paths. */
function _findImageGenToolCfg(tools: TResponse['tools']): TImageGenToolCfg | undefined {
return tools?.find((t): t is TImageGenToolCfg => t.type === 'image_generation');
}
/**
* We need this just to ensure events are not out of order, as out streaming is progressive
@@ -248,8 +268,7 @@ class ResponseParserStateMachine {
// Hosted tool config capture
captureHostedToolConfigs(tools: TResponse['tools']) {
if (!tools?.length) return;
this.#imageGenToolCfg = tools.find((t): t is TImageGenToolCfg => t.type === 'image_generation');
this.#imageGenToolCfg = _findImageGenToolCfg(tools);
}
get imageGenToolCfg() {
@@ -448,6 +467,7 @@ export function createOpenAIResponsesEventParser(): ChatGenerateParseFunction {
break;
case 'image_generation_call':
_hostedToolWishlistHint('image_generation');
// -> IGC: process completed image generation using 'ii' particle for inline images
const { id: igId, result: igResult, revised_prompt: igRevisedPrompt } = doneItem;
const igDoneText = !igRevisedPrompt?.length ? 'Image generated'
@@ -765,6 +785,9 @@ export function createOpenAIResponseParserNS(): ChatGenerateParseFunction {
if (response.model)
pt.setModelName(response.model);
// -> Hosted tool config capture (needed for enriching done-item particles with tool params the API does not echo per-item, e.g. image_generation.model)
const imageGenToolCfg = _findImageGenToolCfg(response.tools);
// -> Upstream Handle (for remote control: resume, cancel, delete)
// NOTE: we don't do it for full responses, because they're supposed to be 'complete' - i.e. no 'background' execution
@@ -875,11 +898,16 @@ export function createOpenAIResponseParserNS(): ChatGenerateParseFunction {
pt.appendReasoningText(item.text);
}
// [DEV] surface cases that diverge from our continuity round-trip expectations
if (!reasoningId && !reasoningEC)
console.warn('[DEV] AIX: OpenAI-Response-NS: reasoning item has neither id nor encrypted_content - no continuity handle captured for this turn', { oItem });
else if (!reasoningEC)
console.log('[DEV] AIX: OpenAI-Response-NS: reasoning item has id but no encrypted_content - stateless round-trip requires include:[\'reasoning.encrypted_content\'] on the request');
// Capture the continuity handle (encrypted_content + id) for stateless multi-turn round-tripping.
// Attached to the ma fragment produced by the summary above; if no summary was emitted, this may
// attach to an unrelated preceding fragment - tolerable as the worst case is a misfiled blob.
// FIXME: make sure we are attaching to an 'ma' (i.e. reasoning text or somehting was emitted)
if (reasoningEC || reasoningId)
if (reasoningEC || reasoningId) {
// Defensive: ensure an ma fragment exists as the attach target for the svs particle below (parity with the streaming path).
pt.appendReasoningText('');
pt.sendSetVendorState({
p: 'svs',
vendor: 'openai',
@@ -890,10 +918,7 @@ export function createOpenAIResponseParserNS(): ChatGenerateParseFunction {
},
},
});
else if (!reasoningId && !reasoningEC)
console.warn('[DEV] AIX: OpenAI-Response-NS: reasoning item has neither id nor encrypted_content - no continuity handle captured for this turn', { oItem });
else if (!reasoningEC)
console.log('[DEV] AIX: OpenAI-Response-NS: reasoning item has id but no encrypted_content - stateless round-trip requires include:[\'reasoning.encrypted_content\'] on the request');
}
break;
// Message contains the main 'assistant' response
@@ -957,6 +982,7 @@ export function createOpenAIResponseParserNS(): ChatGenerateParseFunction {
break;
case 'image_generation_call':
_hostedToolWishlistHint('image_generation');
// -> IGC: process completed image generation using 'ii' particle for inline images
const { result: igResult, revised_prompt: igRevisedPrompt } = oItem;
// Create inline image with base64 data
@@ -965,7 +991,7 @@ export function createOpenAIResponseParserNS(): ChatGenerateParseFunction {
_imageGenerationMimeType(oItem), // infer from output_format echoed in the item
igResult,
igRevisedPrompt || 'Generated image',
AIX_OAI_DEFAULT_IMAGE_GEN_MODEL, // generator: non-streaming path has no captured tool config, use current default
imageGenToolCfg?.model || AIX_OAI_DEFAULT_IMAGE_GEN_MODEL, // generator: read from echoed tools (API does not echo model per-item), fallback to current default
igRevisedPrompt || '', // prompt used
);
else
@@ -1150,6 +1176,7 @@ function _imageGenerationMimeType(item: { output_format?: string }): string {
* - citations: High-quality links (2-3) via annotations in message content
*/
function _forwardDoneWebSearchCallItem(pt: IParticleTransmitter, webSearchCall: Extract<OpenAIWire_API_Responses.Response['output'][number], { type: 'web_search_call' }>, opId: string): void {
_hostedToolWishlistHint('web_search');
const { action, status } = webSearchCall;
const doneOpts = { opId, state: 'done' } as const;
@@ -1203,6 +1230,7 @@ function _forwardDoneWebSearchCallItem(pt: IParticleTransmitter, webSearchCall:
* - addCodeExecutionResponse for each output result
*/
function _forwardDoneCodeInterpreterCallItem(pt: IParticleTransmitter, codeInterpreterCall: Extract<OpenAIWire_API_Responses.Response['output'][number], { type: 'code_interpreter_call' }>): void {
_hostedToolWishlistHint('code_interpreter');
const { id, code, outputs, status /*,container_id*/ } = codeInterpreterCall;
// <- Emit code (like Gemini's executableCode)
@@ -1,7 +1,7 @@
<!--
Upstream snapshot - DO NOT EDIT - run _upstream/sync.sh to refresh
Source: https://platform.claude.com/docs/en/api/messages/create.md
Synced: 2026-04-23
Synced: 2026-04-24
Consumed by: anthropic.wiretypes.ts, anthropic.parser.ts, anthropic.messageCreate.ts, anthropic.transform-fileInline.ts
-->
@@ -2429,7 +2429,7 @@ Learn more about the Messages API in our [user guide](https://docs.claude.com/en
Configuration options for the model's output, such as the output format.
- `effort: optional "low" or "medium" or "high" or 2 more`
- `effort: optional "low" or "medium" or "high" or "max"`
All possible effort levels.
@@ -2439,8 +2439,6 @@ Learn more about the Messages API in our [user guide](https://docs.claude.com/en
- `"high"`
- `"xhigh"`
- `"max"`
- `format: optional JSONOutputFormat`
@@ -3822,15 +3820,15 @@ Learn more about the Messages API in our [user guide](https://docs.claude.com/en
Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277).
Recommended for advanced use cases only. You usually only need to use `temperature`.
Recommended for advanced use cases only.
- `top_p: optional number`
Use nucleus sampling.
In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. You should either alter `temperature` or `top_p`, but not both.
In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`.
Recommended for advanced use cases only. You usually only need to use `temperature`.
Recommended for advanced use cases only.
### Returns
@@ -1,7 +1,7 @@
<!--
Upstream snapshot - DO NOT EDIT - run _upstream/sync.sh to refresh
Source: https://ai.google.dev/gemini-api/docs/deep-research.md.txt
Synced: 2026-04-23
Synced: 2026-04-24
Consumed by: gemini.interactions.wiretypes.ts, gemini.interactions.parser.ts, gemini.interactionsCreate.ts, gemini.interactionsPoller.ts
Companion: ./gemini.interactions.guide.md (the Interactions API guide)
-->
@@ -1,7 +1,7 @@
<!--
Upstream snapshot - DO NOT EDIT - run _upstream/sync.sh to refresh
Source: https://ai.google.dev/gemini-api/docs/interactions.md.txt
Synced: 2026-04-23
Synced: 2026-04-24
Consumed by: gemini.interactions.wiretypes.ts, gemini.interactions.parser.ts, gemini.interactionsCreate.ts, gemini.interactionsPoller.ts
Companion: ./gemini.interactions.spec.md (the Interactions API reference spec), ./gemini.deep-research.guide.md (the Deep Research agent guide)
-->
@@ -1,7 +1,7 @@
<!--
Upstream snapshot - DO NOT EDIT - run _upstream/sync.sh to refresh
Source: https://ai.google.dev/api/interactions-api.md.txt
Synced: 2026-04-23
Synced: 2026-04-24
Consumed by: gemini.interactions.wiretypes.ts, gemini.interactions.parser.ts, gemini.interactionsCreate.ts, gemini.interactionsPoller.ts
Companion: ./gemini.interactions.guide.md (the Interactions API guide)
-->
@@ -1,7 +1,7 @@
<!--
Upstream snapshot - DO NOT EDIT - run _upstream/sync.sh to refresh
Source: https://developers.openai.com/api/reference/resources/responses/methods/create/index.md
Synced: 2026-04-23
Synced: 2026-04-24
Consumed by: openai.wiretypes.ts, openai.responses.parser.ts, openai.responsesCreate.ts
-->
@@ -13,6 +13,10 @@ const hotFixAntShipNoEmptyTextBlocks = true; // Replace empty text blocks with a
*
* ## Updates
*
* ### 2026-04-24 - API Sync: stop_details for structured refusals
* - Response: added `stop_details` ({ type: 'refusal', category: 'cyber'|'bio'|null, explanation: string|null })
* - event_MessageDelta.delta: added `stop_details` (arrives alongside stop_reason in streaming)
*
* ### 2026-03-21 - API Sync: GA tool versions, thinking display, caller updates, cache_control
* - Tools: Added web_search_20260209 (GA), web_fetch_20260209/20260309 (GA), code_execution_20260120 (GA REPL)
* - Request: Added top-level `cache_control` for automatic caching (Feb 2026)
@@ -825,6 +829,16 @@ export namespace AnthropicWire_API_Message_Create {
'model_context_window_exceeded',
]);
/**
* Structured stop details, paired with stop_reason. Currently only populated when stop_reason === 'refusal'.
* Both `type` and `category` are loosely typed for forward-compat - parser warns on unknown `type`.
*/
const StopDetails_schema = z.object({
type: z.enum(['refusal']).or(z.string()),
category: z.enum(['cyber', 'bio']).or(z.string()).nullish(),
explanation: z.string().nullish(),
});
/// Request
export type Request = z.infer<typeof Request_schema>;
@@ -1030,6 +1044,12 @@ export namespace AnthropicWire_API_Message_Create {
// Which custom stop sequence was generated, if any.
stop_sequence: z.string().nullable(),
/**
* Structured stop details. Present when stop_reason === 'refusal' (carries category + explanation).
* In streaming, stop_details is null at message_start and appears on message_delta alongside stop_reason.
*/
stop_details: StopDetails_schema.nullish(),
/**
* Billing and rate-limit usage.
* Token counts represent the underlying cost to Anthropic's systems.
@@ -1088,6 +1108,10 @@ export namespace AnthropicWire_API_Message_Create {
delta: z.object({
stop_reason: StopReason_schema.nullable(),
stop_sequence: z.string().nullable(),
/**
* Structured stop details - present alongside stop_reason === 'refusal' (category + explanation).
*/
stop_details: StopDetails_schema.nullish(),
/**
* Container state updates - present when Skills/code_execution tools are used.
* Provides container id/expiry that may differ from message_start if the container was created mid-stream.
@@ -1183,9 +1183,11 @@ export namespace OpenAIWire_Responses_Items {
// [OpenAI 2026-03-xx] DEPRECATED query might not always be present in done event
query: z.string().optional(),
// the output websites, if any [{"type":"url","url":"https://www.enricoros.com/"}, {"type":"url","url": "https://linkedin.com/in/enricoros/"}, ...]
// [OpenAI 2026-04-23, GPT-5.5] new source types: { type: 'api', name: 'oai-calculator' } for hosted-tool invocations (no url)
sources: z.array(z.object({
type: z.literal('url').optional(), // source type
url: z.string(),
type: z.enum(['url', 'api']).or(z.string()).optional(), // 'url' (default) | 'api' (GPT-5.5 hosted tools) | future types
url: z.string().nullish(), // optional: 'api' sources have no url, only name
name: z.string().nullish(), // for 'api' sources (e.g., 'oai-calculator')
// [OpenAI 2026-03-xx] not present anymore
// title: z.string().optional(),
// snippet: z.string().optional(),
@@ -1446,6 +1448,7 @@ export namespace OpenAIWire_Responses_Tools {
const WebSearchTool_schema = z.object({
type: z.enum(['web_search', 'web_search_preview', 'web_search_preview_2025_03_11']),
search_context_size: z.enum(['low', 'medium', 'high']).optional(),
// [OpenAI 2026-04-23, GPT-5.5] API echoes user_location as `null` (not undefined) when unset - so .nullish()
user_location: z.object({
type: z.literal('approximate'),
// API echoes these as `null` when unset, not omitted - so .nullish()
@@ -1453,7 +1456,7 @@ export namespace OpenAIWire_Responses_Tools {
country: z.string().nullish(),
region: z.string().nullish(),
timezone: z.string().nullish(),
}).optional(),
}).nullish(),
external_web_access: z.boolean().optional(),
});
+25 -6
View File
@@ -1,7 +1,9 @@
import * as React from 'react';
import { useShallow } from 'zustand/react/shallow';
import { Alert, Box, CircularProgress } from '@mui/joy';
import { Alert, Box, Button, CircularProgress } from '@mui/joy';
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import TelegramIcon from '@mui/icons-material/Telegram';
import { ConfirmationModal } from '~/common/components/modals/ConfirmationModal';
import { ShortcutKey, useGlobalShortcuts } from '~/common/components/shortcuts/useGlobalShortcuts';
@@ -204,13 +206,30 @@ export function BeamView(props: {
isMobile={props.isMobile}
rayIds={rayIds}
showRayAdd={cardAdd}
showRaysOps={(isScattering || raysReady < 2) ? undefined : raysReady}
hadImportedRays={hadImportedRays}
onIncreaseRayCount={handleRayIncreaseCount}
onRaysOperation={handleRaysOperation}
// linkedLlmId={currentGatherLlmId}
/>
{/* Rays Action Bar (2+ ready beams) - sibling of the grid (NOT a grid child); an in-grid spanning element with gridColumn:'1/-1' pins all auto-fit tracks open and leaves dead whitespace when raysCount < tracksCount. Fixes #1073. */}
{(!isScattering && raysReady >= 2) && (
<Box sx={{ display: 'flex', justifyContent: 'center', gap: 2, mx: 'var(--Pad)' }}>
<Button size='sm' variant='outlined' color='neutral' onClick={() => handleRaysOperation('copy')} endDecorator={<ContentCopyIcon sx={{ fontSize: 'md' }} />} sx={{
backgroundColor: 'background.surface',
'&:hover': { backgroundColor: 'background.popup' },
}}>
Copy {raysReady}
</Button>
<Button size='sm' variant='outlined' color='success' onClick={() => handleRaysOperation('use')} endDecorator={<TelegramIcon sx={{ fontSize: 'xl' }} />} sx={{
justifyContent: 'space-between',
backgroundColor: 'background.surface',
'&:hover': { backgroundColor: 'background.popup' },
}}>
Use {raysReady === 2 ? 'both' : 'all ' + raysReady} messages
</Button>
</Box>
)}
{/* Gapper between Rays and Merge, without compromising the auto margin of the Ray Grid */}
<Box />
@@ -246,9 +265,9 @@ export function BeamView(props: {
onPositive={handleStartMergeConfirmation}
// lowStakes
noTitleBar
confirmationText='Some responses are still being generated. Do you want to stop and proceed with merging the available responses now?'
positiveActionText='Proceed with Merge'
negativeActionText='Wait for All Responses'
confirmationText={'Some replies are still generating. Merge what\'s ready?'}
positiveActionText='Merge now'
negativeActionText='Wait for all'
negativeActionStartDecorator={
<CircularProgress color='neutral' sx={{ '--CircularProgress-size': '24px', '--CircularProgress-trackThickness': '1px' }} />
}
+2 -1
View File
@@ -149,7 +149,8 @@ export function BeamFusionGrid(props: {
</Box> : (
<Typography level='body-sm' sx={{ opacity: 0.8 }}>
{/*You need two or more replies for a {currentFactory?.shortLabel?.toLocaleLowerCase() ?? ''} merge.*/}
Waiting for multiple responses.
{/*Waiting for multiple responses.*/}
Merge needs 2+ replies. Beam some first.
</Typography>
)}
</BeamCard>
@@ -49,7 +49,7 @@ export async function executeGatherInstruction(_i: GatherInstruction, inputs: Ex
if (!inputs.chatMessages.length)
throw new Error('No conversation history available');
if (!inputs.rayMessages.length)
throw new Error('No responses available');
throw new Error('Needs two Beams at least');
for (let rayMessage of inputs.rayMessages)
if (rayMessage.role !== 'assistant')
throw new Error('Invalid response role');
@@ -58,7 +58,7 @@ export function gatherStartFusion(
if (chatMessages.length < 1)
return onError('No conversation history available');
if (rayMessages.length <= 1)
return onError('No responses available');
return onError('Needs two Beams at least');
if (!initialFusion.llmId)
return onError('No Merge model selected');
@@ -122,7 +122,7 @@ The final output should reflect a deep understanding of the user's preferences a
addLabel: 'Add Breakdown',
cardTitle: 'Evaluation Table',
Icon: TableViewRoundedIcon as typeof SvgIcon,
description: 'Analyzes and compares AI responses, offering a structured framework to support your response choice.',
description: 'Analyzes and compares replies, with a structured framework to support your choice.',
createInstructions: () => [
{
type: 'gather',
-23
View File
@@ -3,8 +3,6 @@ import * as React from 'react';
import type { SxProps } from '@mui/joy/styles/types';
import { Box, Button } from '@mui/joy';
import AddCircleOutlineRoundedIcon from '@mui/icons-material/AddCircleOutlineRounded';
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import TelegramIcon from '@mui/icons-material/Telegram';
import type { BeamStoreApi } from '../store-beam.hooks';
import { BeamCard } from '../BeamCard';
@@ -32,10 +30,8 @@ export function BeamRayGrid(props: {
hadImportedRays: boolean,
isMobile: boolean,
onIncreaseRayCount: () => void,
onRaysOperation: (operation: 'copy' | 'use') => void,
rayIds: string[],
showRayAdd: boolean,
showRaysOps: undefined | number,
}) {
const raysCount = props.rayIds.length;
@@ -71,25 +67,6 @@ export function BeamRayGrid(props: {
</BeamCard>
)}
{/* Multi-Use and Copy Buttons */}
{!!props.showRaysOps && (
<Box sx={{ gridColumn: '1 / -1', display: 'flex', justifyContent: 'center', gap: 2, mt: 2 }}>
<Button size='sm' variant='outlined' color='neutral' onClick={() => props.onRaysOperation('copy')} endDecorator={<ContentCopyIcon sx={{ fontSize: 'md' }} />} sx={{
backgroundColor: 'background.surface',
'&:hover': { backgroundColor: 'background.popup' },
}}>
Copy {props.showRaysOps}
</Button>
<Button size='sm' variant='outlined' color='success' onClick={() => props.onRaysOperation('use')} endDecorator={<TelegramIcon sx={{ fontSize: 'xl' }} />} sx={{
justifyContent: 'space-between',
backgroundColor: 'background.surface',
'&:hover': { backgroundColor: 'background.popup' },
}}>
Use {props.showRaysOps == 2 ? 'both' : 'all ' + props.showRaysOps} messages
</Button>
</Box>
)}
{/*/!* Takes a full row *!/*/}
{/*<Divider sx={{*/}
{/* gridColumn: '1 / -1',*/}
@@ -72,7 +72,7 @@ const geminiExpFree: ModelDescriptionSchema['chatPrice'] = {
};
// Pricing based on https://ai.google.dev/pricing (Apr 22, 2026)
// Pricing based on https://ai.google.dev/pricing (Apr 24, 2026)
const gemini31FlashLitePricing: ModelDescriptionSchema['chatPrice'] = {
input: 0.25, // text/image/video; audio is $0.50 but we don't differentiate yet
@@ -262,8 +262,10 @@ const _knownGeminiModels: ({
/// Generation 3.0
// 3.0 Pro (Preview) - Released November 18, 2025; DEPRECATED: shutdown March 9, 2026 (still served by API as of Apr 17, 2026)
// 3.0 Pro (Preview) - Released November 18, 2025; SHUT DOWN March 9, 2026 - now silently routed to gemini-3.1-pro-preview
// Kept hidden (still returned by API) to avoid confusing users with a silently-redirected model.
{
hidden: true, // March 9, 2026: API silently routes 'gemini-3-pro-preview' to 'gemini-3.1-pro-preview' - hide to prevent user confusion
id: 'models/gemini-3-pro-preview',
labelOverride: 'Gemini 3 Pro Preview',
isPreview: true,
@@ -335,6 +337,7 @@ const _knownGeminiModels: ({
// 2.5 Pro (Stable) - Released June 17, 2025; DEPRECATED: shutdown June 17, 2026
{
hidden: true, // outperformed by 3.1 Pro (1493) and even 3 Flash (1474) - deprecated in 2 months
id: 'models/gemini-2.5-pro',
labelOverride: 'Gemini 2.5 Pro',
deprecated: '2026-06-17',
@@ -412,6 +415,7 @@ const _knownGeminiModels: ({
// 2.5 Flash
{
hidden: true, // outperformed by 3 Flash Preview (1474 vs 1411) - deprecated in 2 months
id: 'models/gemini-2.5-flash',
labelOverride: 'Gemini 2.5 Flash',
deprecated: '2026-06-17',
@@ -467,6 +471,7 @@ const _knownGeminiModels: ({
// 2.5 Flash-Based: Gemini Robotics-ER 1.5 Preview - Released September 25, 2025; DEPRECATED: shutdown April 30, 2026
{
hidden: true, // superseded by Robotics-ER 1.6 - shutdown April 30, 2026
id: 'models/gemini-robotics-er-1.5-preview',
labelOverride: 'Gemini Robotics-ER 1.5 Preview',
isPreview: true,
@@ -573,6 +578,7 @@ const _knownGeminiModels: ({
// 2.0 Flash - DEPRECATED: shutdown June 1, 2026 (announced Feb 18, 2026)
{
hidden: true, // outclassed by all Flash models in 2.5/3.x series - shutdown in ~5 weeks
id: 'models/gemini-2.0-flash-001',
deprecated: '2026-06-01',
chatPrice: gemini20FlashPricing,
@@ -580,6 +586,7 @@ const _knownGeminiModels: ({
benchmark: { cbaElo: 1360 }, // gemini-2.0-flash-001
},
{
hidden: true, // outclassed by all Flash models in 2.5/3.x series - shutdown in ~5 weeks
id: 'models/gemini-2.0-flash',
symLink: 'models/gemini-2.0-flash-001',
deprecated: '2026-06-01',
@@ -591,6 +598,7 @@ const _knownGeminiModels: ({
// 2.0 Flash Lite - DEPRECATED: shutdown June 1, 2026 (announced Feb 18, 2026)
{
hidden: true, // outclassed by 2.5/3.1 Flash-Lite - shutdown in ~5 weeks
id: 'models/gemini-2.0-flash-lite',
chatPrice: gemini20FlashLitePricing,
symLink: 'models/gemini-2.0-flash-lite-001',
@@ -599,6 +607,7 @@ const _knownGeminiModels: ({
benchmark: { cbaElo: 1310 },
},
{
hidden: true, // outclassed by 2.5/3.1 Flash-Lite - shutdown in ~5 weeks
id: 'models/gemini-2.0-flash-lite-001',
chatPrice: gemini20FlashLitePricing,
deprecated: '2026-06-01',
@@ -12,6 +12,23 @@ import { fromManualMapping, KnownModel, llmDevCheckModels_DEV, ManualMappings }
// OpenAI Model Variants
export const hardcodedOpenAIVariants: ModelVariantMap = {
// GPT-5.5 with reasoning disabled (non-thinking) - supports temperature control
'gpt-5.5-2026-04-23': {
idVariant: '::thinking-none',
label: 'GPT-5.5 (No-thinking)',
hidden: true, // hidden by default as redundant, user can unhide in settings
description: 'Supports temperature control for creative applications. GPT-5.5 with reasoning disabled (reasoning_effort=none).',
interfaces: [LLM_IF_OAI_Responses, LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_OAI_PromptCaching], // NO LLM_IF_OAI_Reasoning, NO LLM_IF_HOTFIX_NoTemperature
parameterSpecs: [
{ paramId: 'llmVndOaiEffort', enumValues: ['none', 'low', 'medium', 'high', 'xhigh'], initialValue: 'none', hidden: true }, // factory 'none', not changeable
{ paramId: 'llmVndOaiWebSearchContext' },
{ paramId: 'llmVndOaiVerbosity' },
{ paramId: 'llmVndOaiImageGeneration' },
{ paramId: 'llmVndOaiCodeInterpreter' },
{ paramId: 'llmForceNoStream' },
],
},
// GPT-5.4 with reasoning disabled (non-thinking) - supports temperature control
'gpt-5.4-2026-03-05': {
idVariant: '::thinking-none',
@@ -88,6 +105,58 @@ const PS_DEEP_RESEARCH = [{ paramId: 'llmVndOaiWebSearchContext' as const, initi
// https://platform.openai.com/docs/pricing
export const _knownOpenAIChatModels: ManualMappings = [
/// GPT-5.5 series - Released April 23, 2026
// GPT-5.5
{
idPrefix: 'gpt-5.5-2026-04-23',
label: 'GPT-5.5 (2026-04-23)',
description: 'New baseline for complex production workflows. Stronger task execution, more precise tool use, more efficient reasoning with fewer tokens. 1M token context.',
contextWindow: 1050000,
maxCompletionTokens: 128000,
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_CACHE_REASON, LLM_IF_HOTFIX_NoTemperature],
parameterSpecs: [
{ paramId: 'llmVndOaiEffort', enumValues: ['none', 'low', 'medium', 'high', 'xhigh'], initialValue: 'medium' }, // medium is the new default for 5.5
{ paramId: 'llmVndOaiWebSearchContext' },
{ paramId: 'llmVndOaiVerbosity' },
{ paramId: 'llmVndOaiImageGeneration' },
{ paramId: 'llmVndOaiCodeInterpreter' },
{ paramId: 'llmForceNoStream' },
],
chatPrice: { input: 5, cache: { cType: 'oai-ac', read: 0.5 }, output: 30 },
// benchmark: TBD - no CBA ELO yet
},
{
idPrefix: 'gpt-5.5',
label: 'GPT-5.5',
symLink: 'gpt-5.5-2026-04-23',
},
// GPT-5.5 Pro
{
idPrefix: 'gpt-5.5-pro-2026-04-23',
label: 'GPT-5.5 Pro (2026-04-23)',
description: 'Most capable model for complex tasks. Uses more compute for smarter, more precise responses on the hardest problems.',
contextWindow: 1050000,
maxCompletionTokens: 272000,
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_MIN, LLM_IF_OAI_Reasoning, LLM_IF_HOTFIX_NoTemperature],
parameterSpecs: [
{ paramId: 'llmVndOaiEffort', enumValues: ['medium', 'high', 'xhigh'] }, // Pro: no low/none
{ paramId: 'llmVndOaiWebSearchContext' },
{ paramId: 'llmVndOaiVerbosity' },
{ paramId: 'llmVndOaiImageGeneration' },
{ paramId: 'llmForceNoStream' },
],
chatPrice: { input: 30, output: 180 },
// benchmark: TBD
},
{
idPrefix: 'gpt-5.5-pro',
label: 'GPT-5.5 Pro',
symLink: 'gpt-5.5-pro-2026-04-23',
},
/// GPT-5.4 series - Released March 5, 2026
// GPT-5.4
@@ -250,6 +319,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
// GPT-5.2
{
hidden: true, // superseded by GPT-5.4/5.5
idPrefix: 'gpt-5.2-2025-12-11',
label: 'GPT-5.2 (2025-12-11)',
description: 'Most capable model for professional work and long-running agents. Improvements in general intelligence, long-context, agentic tool-calling, and vision.',
@@ -268,6 +338,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
benchmark: { cbaElo: 1441 }, // gpt-5.2-high
},
{
hidden: true, // superseded by GPT-5.4/5.5
idPrefix: 'gpt-5.2',
label: 'GPT-5.2',
symLink: 'gpt-5.2-2025-12-11',
@@ -275,6 +346,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
// GPT-5.2 Codex
{
hidden: true, // superseded by GPT-5.3 Codex
idPrefix: 'gpt-5.2-codex',
label: 'GPT-5.2 Codex',
description: 'GPT-5.2 optimized for long-horizon, agentic coding tasks in Codex or similar environments. Supports low, medium, high, and xhigh reasoning effort settings.',
@@ -293,6 +365,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
// GPT-5.2 Chat Latest
{
hidden: true, // superseded by GPT-5.3 Instant
idPrefix: 'gpt-5.2-chat-latest',
label: 'GPT-5.2 Instant',
description: 'GPT-5.2 model powering ChatGPT. Fast, capable for everyday work with clear improvements in info-seeking, how-tos, technical writing.',
@@ -311,6 +384,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
// GPT-5.2 Pro
{
hidden: true, // superseded by GPT-5.4/5.5 Pro
idPrefix: 'gpt-5.2-pro-2025-12-11',
label: 'GPT-5.2 Pro (2025-12-11)',
description: 'Smartest and most trustworthy option for difficult questions. Uses more compute for harder thinking on complex domains like programming.',
@@ -328,6 +402,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
// benchmark: TBD
},
{
hidden: true, // superseded by GPT-5.4/5.5 Pro
idPrefix: 'gpt-5.2-pro',
label: 'GPT-5.2 Pro',
symLink: 'gpt-5.2-pro-2025-12-11',
@@ -338,6 +413,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
// GPT-5.1
{
hidden: true, // superseded by GPT-5.4/5.5
idPrefix: 'gpt-5.1-2025-11-13',
label: 'GPT-5.1 (2025-11-13)',
description: 'The best model for coding and agentic tasks with configurable reasoning effort.',
@@ -355,6 +431,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
benchmark: { cbaElo: 1455 }, // gpt-5.1-high
},
{
hidden: true, // superseded by GPT-5.4/5.5
idPrefix: 'gpt-5.1',
label: 'GPT-5.1',
symLink: 'gpt-5.1-2025-11-13',
@@ -362,6 +439,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
// GPT-5.1 Chat Latest
{
hidden: true, // superseded by GPT-5.3 Instant
idPrefix: 'gpt-5.1-chat-latest',
label: 'GPT-5.1 Instant',
description: 'GPT-5.1 Instant with adaptive reasoning. More conversational with improved instruction following.',
@@ -381,6 +459,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
// GPT-5.1 Codex Max
{
hidden: true, // superseded by GPT-5.3 Codex
idPrefix: 'gpt-5.1-codex-max',
label: 'GPT-5.1 Codex Max',
description: 'Our most intelligent coding model optimized for long-horizon, agentic coding tasks.',
@@ -398,6 +477,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
},
// GPT-5.1 Codex
{
hidden: true, // superseded by GPT-5.3 Codex
idPrefix: 'gpt-5.1-codex',
label: 'GPT-5.1 Codex',
description: 'A version of GPT-5.1 optimized for agentic coding tasks in Codex or similar environments.',
@@ -415,6 +495,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
},
// GPT-5.1 Codex Mini
{
hidden: true, // superseded by GPT-5.3 Codex
idPrefix: 'gpt-5.1-codex-mini',
label: 'GPT-5.1 Codex Mini',
description: 'Smaller, faster version of GPT-5.1 Codex for efficient coding tasks.',
@@ -436,6 +517,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
// GPT-5
{
hidden: true, // superseded by GPT-5.4/5.5
idPrefix: 'gpt-5-2025-08-07',
label: 'GPT-5 (2025-08-07)',
description: 'The best model for coding and agentic tasks across domains.',
@@ -453,6 +535,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
benchmark: { cbaElo: 1433 }, // gpt-5-high
},
{
hidden: true, // superseded by GPT-5.4/5.5
idPrefix: 'gpt-5',
label: 'GPT-5',
symLink: 'gpt-5-2025-08-07',
@@ -460,6 +543,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
// GPT-5 Pro
{
hidden: true, // superseded by GPT-5.4/5.5 Pro
idPrefix: 'gpt-5-pro-2025-10-06',
label: 'GPT-5 Pro (2025-10-06)',
description: 'Version of GPT-5 that uses more compute to produce smarter and more precise responses. Designed for tough problems.',
@@ -471,6 +555,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
// benchmark: has not been measured yet
},
{
hidden: true, // superseded by GPT-5.4/5.5 Pro
idPrefix: 'gpt-5-pro',
label: 'GPT-5 Pro',
symLink: 'gpt-5-pro-2025-10-06',
@@ -511,6 +596,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
// GPT-5 Search API
{
hidden: true, // poor quality - use llmVndOaiWebSearchContext on regular models instead
idPrefix: 'gpt-5-search-api-2025-10-14',
label: 'GPT-5 Search API (2025-10-14)',
description: 'Updated web search model in Chat Completions API. 60% cheaper with domain filtering support.',
@@ -522,6 +608,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
// benchmark: TBD
},
{
hidden: true, // poor quality - use llmVndOaiWebSearchContext on regular models instead
idPrefix: 'gpt-5-search-api',
label: 'GPT-5 Search API',
symLink: 'gpt-5-search-api-2025-10-14',
@@ -529,6 +616,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
// GPT-5 mini
{
hidden: true, // superseded by GPT-5.4 Mini
idPrefix: 'gpt-5-mini-2025-08-07',
label: 'GPT-5 Mini (2025-08-07)',
description: 'A faster, more cost-efficient version of GPT-5 for well-defined tasks.',
@@ -540,6 +628,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
benchmark: { cbaElo: 1390 }, // gpt-5-mini-high
},
{
hidden: true, // superseded by GPT-5.4 Mini
idPrefix: 'gpt-5-mini',
label: 'GPT-5 Mini',
symLink: 'gpt-5-mini-2025-08-07',
@@ -547,6 +636,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
// GPT-5 nano
{
hidden: true, // superseded by GPT-5.4 Nano
idPrefix: 'gpt-5-nano-2025-08-07',
label: 'GPT-5 Nano (2025-08-07)',
description: 'Fastest, most cost-efficient version of GPT-5 for summarization and classification tasks.',
@@ -558,6 +648,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
benchmark: { cbaElo: 1337 }, // gpt-5-nano-high
},
{
hidden: true, // superseded by GPT-5.4 Nano
idPrefix: 'gpt-5-nano',
label: 'GPT-5 Nano',
symLink: 'gpt-5-nano-2025-08-07',
@@ -608,8 +699,9 @@ export const _knownOpenAIChatModels: ManualMappings = [
// o4-mini-deep-research - (v1/responses API)
{
idPrefix: 'o4-mini-deep-research-2025-06-26',
label: 'o4 Mini Deep Research (2025-06-26)',
description: 'Faster, more affordable deep research model for complex, multi-step research tasks.',
label: 'o4 Mini Deep Research [Deprecated]',
isLegacy: true,
description: 'Faster, more affordable deep research model for complex, multi-step research tasks. [Shutdown: 2026-07-23 - migrate to GPT-5.5 with web search.]',
contextWindow: 200000,
maxCompletionTokens: 100000,
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_CACHE_REASON, LLM_IF_HOTFIX_NoTemperature],
@@ -625,8 +717,9 @@ export const _knownOpenAIChatModels: ManualMappings = [
/// o4-mini
{
idPrefix: 'o4-mini-2025-04-16',
label: 'o4 Mini (2025-04-16)',
description: 'Latest o4-mini model. Optimized for fast, effective reasoning with exceptionally efficient performance in coding and visual tasks.',
label: 'o4 Mini [Deprecated]',
isLegacy: true,
description: 'Latest o4-mini model. Optimized for fast, effective reasoning with exceptionally efficient performance in coding and visual tasks. [Shutdown: 2026-10-23 - migrate to GPT-5.4 Mini.]',
contextWindow: 200000,
maxCompletionTokens: 100000,
interfaces: IFS_CHAT_CACHE_REASON,
@@ -643,8 +736,9 @@ export const _knownOpenAIChatModels: ManualMappings = [
// o3-deep-research - (v1/responses API)
{
idPrefix: 'o3-deep-research-2025-06-26',
label: 'o3 Deep Research (2025-06-26)',
description: 'Our most powerful deep research model for complex, multi-step research tasks.',
label: 'o3 Deep Research [Deprecated]',
isLegacy: true,
description: 'Our most powerful deep research model for complex, multi-step research tasks. [Shutdown: 2026-07-23 - migrate to GPT-5.5 Pro with web search.]',
contextWindow: 200000,
maxCompletionTokens: 100000,
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_CACHE_REASON, LLM_IF_HOTFIX_NoTemperature],
@@ -696,8 +790,9 @@ export const _knownOpenAIChatModels: ManualMappings = [
// o3-mini
{
idPrefix: 'o3-mini-2025-01-31',
label: 'o3 Mini (2025-01-31)',
description: 'Latest o3-mini model snapshot. High intelligence at the same cost and latency targets of o1-mini. Excels at science, math, and coding tasks.',
label: 'o3 Mini [Deprecated]',
isLegacy: true,
description: 'Latest o3-mini model snapshot. High intelligence at the same cost and latency targets of o1-mini. Excels at science, math, and coding tasks. [Shutdown: 2026-10-23 - migrate to GPT-5.4 Mini.]',
contextWindow: 200000,
maxCompletionTokens: 100000,
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_PromptCaching, LLM_IF_OAI_Reasoning, LLM_IF_HOTFIX_StripImages],
@@ -733,8 +828,9 @@ export const _knownOpenAIChatModels: ManualMappings = [
// o1
{
idPrefix: 'o1-2024-12-17',
label: 'o1 (2024-12-17)',
description: 'Previous full o-series reasoning model.',
label: 'o1 [Deprecated]',
isLegacy: true,
description: 'Previous full o-series reasoning model. [Shutdown: 2026-10-23 - migrate to GPT-5.5 or o3.]',
contextWindow: 200000,
maxCompletionTokens: 100000,
interfaces: IFS_CHAT_CACHE_REASON,
@@ -788,8 +884,9 @@ export const _knownOpenAIChatModels: ManualMappings = [
// GPT-4.1 nano
{
idPrefix: 'gpt-4.1-nano-2025-04-14',
label: 'GPT-4.1 Nano (2025-04-14)',
description: 'Fastest, most cost-effective GPT 4.1 model. Delivers exceptional performance with low latency, ideal for tasks like classification or autocompletion.',
label: 'GPT-4.1 Nano [Deprecated]',
isLegacy: true,
description: 'Fastest, most cost-effective GPT 4.1 model. Delivers exceptional performance with low latency, ideal for tasks like classification or autocompletion. [Shutdown: 2026-10-23 - migrate to GPT-5.4 Nano.]',
contextWindow: 1047576,
maxCompletionTokens: 32768,
interfaces: IFS_CHAT_CACHE,
@@ -819,6 +916,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
// gpt-audio
{
hidden: true, // superseded by GPT Audio 1.5
idPrefix: 'gpt-audio-2025-08-28',
label: 'GPT Audio (2025-08-28)',
description: 'First generally available audio model. Accepts audio inputs and outputs, and can be used in the Chat Completions REST API.',
@@ -829,6 +927,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
// benchmark: TBD
},
{
hidden: true, // superseded by GPT Audio 1.5
idPrefix: 'gpt-audio',
label: 'GPT Audio',
symLink: 'gpt-audio-2025-08-28',
@@ -1220,6 +1319,12 @@ export function openAIInjectVariants(acc: ModelDescriptionSchema[], model: Model
const _manualOrderingIdPrefixes = [
// GPT-5.5
'gpt-5.5-20',
'gpt-5.5-pro-20',
'gpt-5.5-pro',
'gpt-5.5-chat-latest',
'gpt-5.5',
// GPT-5.4
'gpt-5.4-20',
'gpt-5.4-pro-20',
@@ -1419,6 +1524,7 @@ export function llmOrtOaiLookup(orModelName: string): OrtVendorLookupResult | un
// typemap to known models
const ortOaiRefMap: Record<string, string | null> = {
// renames
'gpt-5.5-chat': 'gpt-5.5-2026-04-23', // no chat-latest yet, map to snapshot
'gpt-5.4-chat': 'gpt-5.4-2026-03-05', // no chat-latest yet, map to snapshot
'gpt-5.3-chat': 'gpt-5.3-chat-latest',
'gpt-5.2-chat': 'gpt-5.2-chat-latest',