@code-yeongyu/senpi-ai 2026.10.11 → 2026.10.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/anthropic-messages.js +48 -35
- package/dist/api/anthropic-native-replay-blocks.d.ts +4 -0
- package/dist/api/anthropic-native-replay-blocks.js +28 -0
- package/dist/api/anthropic-web-search-expiry.d.ts +10 -0
- package/dist/api/anthropic-web-search-expiry.js +104 -0
- package/dist/api/openai-responses-shared.js +19 -0
- package/dist/api/openai-responses.js +5 -1
- package/dist/model.d.ts +6 -2
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/providers/data/chatgpt-subscription.json +1 -1
- package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/dist/providers/data/meta.json +1 -1
- package/dist/providers/data/opencode.json +1 -1
- package/dist/providers/data/opengateway.json +1 -1
- package/dist/providers/data/openrouter.json +1 -1
- package/dist/providers/data/radius.json +1 -1
- package/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/dist/providers/xai.js +7 -1
- package/dist/types.d.ts +22 -0
- package/dist/utils/prompt-cache-ttl.d.ts +2 -1
- package/dist/utils/prompt-cache-ttl.js +1 -0
- package/dist/utils/provider-failure-description.d.ts +3 -1
- package/dist/utils/provider-failure-description.js +8 -0
- package/dist/utils/tool-schema-compat.js +5 -3
- package/package.json +3 -3
|
@@ -27,8 +27,10 @@ import { normalizeToolCallId } from "../utils/tool-call-id.js";
|
|
|
27
27
|
import { sendWithForcedToolChoiceFallback } from "../utils/tool-choice-fallback.js";
|
|
28
28
|
import { resolveRootObjectSchema } from "../utils/tool-schema-compat.js";
|
|
29
29
|
import { getCurrentTools, getDeclaredTools, getInitialSystemMessage, hasToolRedefinitions, normalizeContext, resolveTranscript, } from "../utils/transcript.js";
|
|
30
|
+
import { isAnthropicWebSearchReplayBlock, isReplayableAnthropicProviderNativeBlock, } from "./anthropic-native-replay-blocks.js";
|
|
30
31
|
import { sanitizeAnthropicToolPairs } from "./anthropic-tool-pairs.js";
|
|
31
32
|
import { demoteToolReferenceReplay, demoteUnavailableToolReferences } from "./anthropic-tool-references.js";
|
|
33
|
+
import { demoteExpiredWebSearchReplay, expiredWebSearchReplayIds, isExpiredWebSearchContentError, } from "./anthropic-web-search-expiry.js";
|
|
32
34
|
import { resolveCloudflareBaseUrl } from "./cloudflare.js";
|
|
33
35
|
import { getJsonSchemaToolParameters, resolveJsonSchemaStrictSampling, } from "./constrained-sampling.js";
|
|
34
36
|
import { withGitHubCopilotFailureNote } from "./github-copilot-errors.js";
|
|
@@ -71,7 +73,7 @@ function getCacheControl(model, cacheRetention, env) {
|
|
|
71
73
|
// The bundled Claude Code version and the floor of the advertised `claude-cli/<version>`
|
|
72
74
|
// (see utils/claude-code-version.ts). Keep this exact declaration: a downstream installer
|
|
73
75
|
// (oh-my-openagent) rewrites it byte-for-byte in the installed dist and bundle.
|
|
74
|
-
const claudeCodeVersion = "2.1.
|
|
76
|
+
const claudeCodeVersion = "2.1.296";
|
|
75
77
|
// Claude Code 2.x tool names (canonical casing)
|
|
76
78
|
// Source: https://cchistory.mariozechner.at/data/prompts-2.1.11.md
|
|
77
79
|
// To update: https://github.com/badlogic/cchistory
|
|
@@ -243,6 +245,8 @@ function supportsMidConvoToolChanges(model) {
|
|
|
243
245
|
const unsignedThinkingTextReplayFallbacks = new Set();
|
|
244
246
|
// Endpoints that rejected a replayed tool_reference for one conversation (senpi #2568).
|
|
245
247
|
const toolReferenceReplayFallbacks = new Set();
|
|
248
|
+
// `${fallbackKey}\u0000${tool_use_id}` of web search results whose encrypted payload expired (senpi #3201).
|
|
249
|
+
const expiredWebSearchReplays = new Set();
|
|
246
250
|
registerSessionResourceCleanup((sessionId) => {
|
|
247
251
|
// One small entry per (session, base URL, model) that ever hit the fallback;
|
|
248
252
|
// drop the session's entries at teardown so long-lived hosts do not collect
|
|
@@ -250,10 +254,15 @@ registerSessionResourceCleanup((sessionId) => {
|
|
|
250
254
|
if (sessionId === undefined) {
|
|
251
255
|
unsignedThinkingTextReplayFallbacks.clear();
|
|
252
256
|
toolReferenceReplayFallbacks.clear();
|
|
257
|
+
expiredWebSearchReplays.clear();
|
|
253
258
|
return;
|
|
254
259
|
}
|
|
255
260
|
const prefix = `${sessionId}\u0000`;
|
|
256
|
-
for (const fallbacks of [
|
|
261
|
+
for (const fallbacks of [
|
|
262
|
+
unsignedThinkingTextReplayFallbacks,
|
|
263
|
+
toolReferenceReplayFallbacks,
|
|
264
|
+
expiredWebSearchReplays,
|
|
265
|
+
]) {
|
|
257
266
|
for (const key of fallbacks) {
|
|
258
267
|
if (key.startsWith(prefix))
|
|
259
268
|
fallbacks.delete(key);
|
|
@@ -355,26 +364,6 @@ let federationClient;
|
|
|
355
364
|
function isRecord(value) {
|
|
356
365
|
return typeof value === "object" && value !== null;
|
|
357
366
|
}
|
|
358
|
-
const REPLAYABLE_ANTHROPIC_PROVIDER_NATIVE_TYPES = new Set([
|
|
359
|
-
"server_tool_use",
|
|
360
|
-
"web_search_tool_result",
|
|
361
|
-
"web_fetch_tool_result",
|
|
362
|
-
"code_execution_tool_result",
|
|
363
|
-
"bash_code_execution_tool_result",
|
|
364
|
-
"text_editor_code_execution_tool_result",
|
|
365
|
-
"tool_search_tool_result",
|
|
366
|
-
"container_upload",
|
|
367
|
-
// The server-side fallback marker (`fallback`, server-side-fallback-2026-06-01
|
|
368
|
-
// beta) is deliberately absent: the Messages API rejects it as an *input* tag
|
|
369
|
-
// ("Input tag 'fallback' found using 'type' does not match any of the expected
|
|
370
|
-
// tags"), so replaying it 400s every subsequent same-model request. The stored
|
|
371
|
-
// marker is audit metadata and still drives declined-attempt pruning in
|
|
372
|
-
// convertMessages (see lastAnthropicFallbackBoundary and
|
|
373
|
-
// collectDiscardedFallbackToolCallIds); blocks after it replay verbatim.
|
|
374
|
-
]);
|
|
375
|
-
function isReplayableAnthropicProviderNativeBlock(raw) {
|
|
376
|
-
return isRecord(raw) && typeof raw.type === "string" && REPLAYABLE_ANTHROPIC_PROVIDER_NATIVE_TYPES.has(raw.type);
|
|
377
|
-
}
|
|
378
367
|
function isSameAnthropicModel(message, model) {
|
|
379
368
|
return message.provider === model.provider && message.api === model.api && message.model === model.id;
|
|
380
369
|
}
|
|
@@ -408,17 +397,6 @@ function isAnthropicServerToolUseBlock(raw) {
|
|
|
408
397
|
function isProviderNativeToolUseBlock(raw) {
|
|
409
398
|
return isRecord(raw) && (raw.type === "server_tool_use" || raw.type === "mcp_tool_use");
|
|
410
399
|
}
|
|
411
|
-
// Endpoints without `supportsWebSearch` reject replayed web-search server-tool
|
|
412
|
-
// blocks (kimi-coding 400s with `tool_call_id is not found`), wedging every
|
|
413
|
-
// subsequent request of the session. Dropping the pair loses the searched
|
|
414
|
-
// context but keeps the conversation usable.
|
|
415
|
-
function isAnthropicWebSearchReplayBlock(raw) {
|
|
416
|
-
if (!isRecord(raw))
|
|
417
|
-
return false;
|
|
418
|
-
if (raw.type === "web_search_tool_result")
|
|
419
|
-
return true;
|
|
420
|
-
return raw.type === "server_tool_use" && raw.name === "web_search";
|
|
421
|
-
}
|
|
422
400
|
// The user turn a pending server tool can survive is one that carries only
|
|
423
401
|
// tool results. Anything else the wire would emit — real text, an image —
|
|
424
402
|
// closes the assistant turn (the API then 400s the still-open use). A blank
|
|
@@ -448,7 +426,7 @@ function collectProviderNativeToolPairing(messages, model, deferredToolNames, no
|
|
|
448
426
|
if (block.type !== "providerNative" || !isRecord(block.raw))
|
|
449
427
|
continue;
|
|
450
428
|
const raw = block.raw;
|
|
451
|
-
if (
|
|
429
|
+
if (!isReplayableAnthropicProviderNativeBlock(raw))
|
|
452
430
|
continue;
|
|
453
431
|
if (isProviderNativeToolUseBlock(raw) && typeof raw.id === "string")
|
|
454
432
|
pendingUseIds.add(raw.id);
|
|
@@ -457,7 +435,7 @@ function collectProviderNativeToolPairing(messages, model, deferredToolNames, no
|
|
|
457
435
|
if (block.type !== "providerNative" || !isRecord(block.raw))
|
|
458
436
|
continue;
|
|
459
437
|
const raw = block.raw;
|
|
460
|
-
if (
|
|
438
|
+
if (!isReplayableAnthropicProviderNativeBlock(raw))
|
|
461
439
|
continue;
|
|
462
440
|
const toolUseId = raw.tool_use_id;
|
|
463
441
|
if (typeof toolUseId !== "string")
|
|
@@ -1016,6 +994,15 @@ export const stream = (model, context, options) => {
|
|
|
1016
994
|
: getAnthropicCompat(model).unsignedThinkingReplay;
|
|
1017
995
|
let demoteReferenceReplay = fallbackKey !== undefined && toolReferenceReplayFallbacks.has(fallbackKey);
|
|
1018
996
|
let requestReplaysReferences = false;
|
|
997
|
+
const expiredSearchPrefix = fallbackKey === undefined ? undefined : `${fallbackKey}\u0000`;
|
|
998
|
+
const expiredSearchIds = new Set();
|
|
999
|
+
if (expiredSearchPrefix !== undefined) {
|
|
1000
|
+
for (const key of expiredWebSearchReplays) {
|
|
1001
|
+
if (key.startsWith(expiredSearchPrefix))
|
|
1002
|
+
expiredSearchIds.add(key.slice(expiredSearchPrefix.length));
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
let lastSentParams;
|
|
1019
1006
|
const createRequest = async () => {
|
|
1020
1007
|
let params = buildParams(model, normalizedContext, isOAuth, options, unsignedThinkingReplay);
|
|
1021
1008
|
const nextParams = await options?.onPayload?.(params, model);
|
|
@@ -1029,6 +1016,8 @@ export const stream = (model, context, options) => {
|
|
|
1029
1016
|
requestReplaysReferences = withoutReferenceReplay !== params;
|
|
1030
1017
|
if (demoteReferenceReplay)
|
|
1031
1018
|
params = withoutReferenceReplay;
|
|
1019
|
+
params = demoteExpiredWebSearchReplay(params, expiredSearchIds);
|
|
1020
|
+
lastSentParams = params;
|
|
1032
1021
|
params = sanitizeAnthropicToolPairs(params);
|
|
1033
1022
|
const payloadRequestMetadata = extractPayloadRequestMetadata(params);
|
|
1034
1023
|
params = payloadRequestMetadata.params;
|
|
@@ -1053,6 +1042,28 @@ export const stream = (model, context, options) => {
|
|
|
1053
1042
|
});
|
|
1054
1043
|
return { params: sent.params, response: sent.result };
|
|
1055
1044
|
};
|
|
1045
|
+
// True when the error names web search payloads this request had not demoted yet.
|
|
1046
|
+
const markNewlyExpiredSearches = (error) => {
|
|
1047
|
+
if (!isExpiredWebSearchContentError(error) || lastSentParams === undefined)
|
|
1048
|
+
return false;
|
|
1049
|
+
const newlyExpired = expiredWebSearchReplayIds(lastSentParams, error).filter((id) => !expiredSearchIds.has(id));
|
|
1050
|
+
for (const id of newlyExpired) {
|
|
1051
|
+
expiredSearchIds.add(id);
|
|
1052
|
+
if (expiredSearchPrefix !== undefined)
|
|
1053
|
+
expiredWebSearchReplays.add(expiredSearchPrefix + id);
|
|
1054
|
+
}
|
|
1055
|
+
return newlyExpired.length > 0;
|
|
1056
|
+
};
|
|
1057
|
+
const createRequestDemotingExpiredSearches = async () => {
|
|
1058
|
+
try {
|
|
1059
|
+
return await createRequest();
|
|
1060
|
+
}
|
|
1061
|
+
catch (error) {
|
|
1062
|
+
if (markNewlyExpiredSearches(error))
|
|
1063
|
+
return createRequestDemotingExpiredSearches();
|
|
1064
|
+
throw error;
|
|
1065
|
+
}
|
|
1066
|
+
};
|
|
1056
1067
|
let requestOutcome;
|
|
1057
1068
|
try {
|
|
1058
1069
|
requestOutcome = await retryProviderRequest(async () => {
|
|
@@ -1072,6 +1083,8 @@ export const stream = (model, context, options) => {
|
|
|
1072
1083
|
toolReferenceReplayFallbacks.add(fallbackKey);
|
|
1073
1084
|
return createRequest();
|
|
1074
1085
|
}
|
|
1086
|
+
if (markNewlyExpiredSearches(error))
|
|
1087
|
+
return createRequestDemotingExpiredSearches();
|
|
1075
1088
|
if (await retryWithNewerClaudeCode(error)) {
|
|
1076
1089
|
try {
|
|
1077
1090
|
return await createRequest();
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { BetaContentBlockParam } from "@anthropic-ai/sdk/resources/beta/messages/messages.js";
|
|
2
|
+
export declare function isReplayableAnthropicProviderNativeBlock(raw: unknown): raw is BetaContentBlockParam;
|
|
3
|
+
export declare function isAnthropicWebSearchReplayBlock(raw: unknown): boolean;
|
|
4
|
+
//# sourceMappingURL=anthropic-native-replay-blocks.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const replayableTypes = new Set([
|
|
2
|
+
"server_tool_use",
|
|
3
|
+
"web_search_tool_result",
|
|
4
|
+
// GLM's Anthropic-compatible coding endpoint returns this server-result shape.
|
|
5
|
+
// Pairing still requires a preceding same-model server_tool_use.
|
|
6
|
+
"tool_result",
|
|
7
|
+
"web_fetch_tool_result",
|
|
8
|
+
"code_execution_tool_result",
|
|
9
|
+
"bash_code_execution_tool_result",
|
|
10
|
+
"text_editor_code_execution_tool_result",
|
|
11
|
+
"tool_search_tool_result",
|
|
12
|
+
"container_upload",
|
|
13
|
+
// Stored fallback markers are audit metadata, not valid Messages input tags.
|
|
14
|
+
]);
|
|
15
|
+
function isRecord(value) {
|
|
16
|
+
return value !== null && typeof value === "object";
|
|
17
|
+
}
|
|
18
|
+
export function isReplayableAnthropicProviderNativeBlock(raw) {
|
|
19
|
+
return isRecord(raw) && typeof raw.type === "string" && replayableTypes.has(raw.type);
|
|
20
|
+
}
|
|
21
|
+
export function isAnthropicWebSearchReplayBlock(raw) {
|
|
22
|
+
if (!isRecord(raw))
|
|
23
|
+
return false;
|
|
24
|
+
if (raw.type === "web_search_tool_result" || raw.type === "tool_result")
|
|
25
|
+
return true;
|
|
26
|
+
return raw.type === "server_tool_use" && raw.name === "web_search";
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=anthropic-native-replay-blocks.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MessageCreateParamsStreaming } from "@anthropic-ai/sdk/resources/beta/messages/messages.js";
|
|
2
|
+
export declare function isExpiredWebSearchContentError(error: unknown): error is Error;
|
|
3
|
+
/**
|
|
4
|
+
* The `tool_use_id`s of the web search results the rejected request replayed
|
|
5
|
+
* at or before the message the API named. Without a parsable index every
|
|
6
|
+
* replayed result counts as expired.
|
|
7
|
+
*/
|
|
8
|
+
export declare function expiredWebSearchReplayIds(params: MessageCreateParamsStreaming, error: Error): string[];
|
|
9
|
+
export declare function demoteExpiredWebSearchReplay(params: MessageCreateParamsStreaming, expired: ReadonlySet<string>): MessageCreateParamsStreaming;
|
|
10
|
+
//# sourceMappingURL=anthropic-web-search-expiry.d.ts.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The opaque `encrypted_content` of a native web search result is only valid
|
|
3
|
+
* for the account that ran the search (senpi #3201). Same-model replay keeps
|
|
4
|
+
* the payload byte-for-byte (#208), so once a request is served by another
|
|
5
|
+
* account (credential rotation, a multi-account gateway) every later request
|
|
6
|
+
* on that model fails with "messages.N.content.0: Invalid `encrypted_content` in
|
|
7
|
+
* `search_result` block" and the session only moves on through a model
|
|
8
|
+
* fallback. Stripping the field is not an option: the API requires it (#150).
|
|
9
|
+
*
|
|
10
|
+
* Recovery demotes expired search pairs to plain text: the `server_tool_use`
|
|
11
|
+
* becomes a note naming its query and the `web_search_tool_result` a list of
|
|
12
|
+
* the result titles and URLs. Only pairs whose result sits at or before the
|
|
13
|
+
* rejected message are demoted, because a search is never fresher than a
|
|
14
|
+
* later one; newer results keep replaying verbatim.
|
|
15
|
+
*/
|
|
16
|
+
const EXPIRED_SEARCH_CONTENT = /Invalid `encrypted_content` in `search_result` block/i;
|
|
17
|
+
const REJECTED_MESSAGE_INDEX = /messages\.(\d+)\.content\.\d+: Invalid `encrypted_content`/i;
|
|
18
|
+
function isRecord(value) {
|
|
19
|
+
return typeof value === "object" && value !== null;
|
|
20
|
+
}
|
|
21
|
+
export function isExpiredWebSearchContentError(error) {
|
|
22
|
+
return (error instanceof Error &&
|
|
23
|
+
"status" in error &&
|
|
24
|
+
error.status === 400 &&
|
|
25
|
+
EXPIRED_SEARCH_CONTENT.test(error.message));
|
|
26
|
+
}
|
|
27
|
+
function webSearchResultIds(message) {
|
|
28
|
+
if (message.role !== "assistant" || !Array.isArray(message.content))
|
|
29
|
+
return [];
|
|
30
|
+
const ids = [];
|
|
31
|
+
for (const block of message.content) {
|
|
32
|
+
if (isRecord(block) && block.type === "web_search_tool_result" && typeof block.tool_use_id === "string") {
|
|
33
|
+
ids.push(block.tool_use_id);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return ids;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The `tool_use_id`s of the web search results the rejected request replayed
|
|
40
|
+
* at or before the message the API named. Without a parsable index every
|
|
41
|
+
* replayed result counts as expired.
|
|
42
|
+
*/
|
|
43
|
+
export function expiredWebSearchReplayIds(params, error) {
|
|
44
|
+
const match = REJECTED_MESSAGE_INDEX.exec(error.message);
|
|
45
|
+
const lastIndex = match?.[1] === undefined ? params.messages.length - 1 : Number(match[1]);
|
|
46
|
+
return params.messages.slice(0, lastIndex + 1).flatMap(webSearchResultIds);
|
|
47
|
+
}
|
|
48
|
+
function searchQuery(block) {
|
|
49
|
+
const input = block.input;
|
|
50
|
+
return isRecord(input) && typeof input.query === "string" ? input.query : undefined;
|
|
51
|
+
}
|
|
52
|
+
function demotedResultText(block) {
|
|
53
|
+
const results = Array.isArray(block.content) ? block.content : [];
|
|
54
|
+
const lines = [];
|
|
55
|
+
for (const result of results) {
|
|
56
|
+
if (!isRecord(result) || result.type !== "web_search_result")
|
|
57
|
+
continue;
|
|
58
|
+
const title = typeof result.title === "string" ? result.title : "";
|
|
59
|
+
const url = typeof result.url === "string" ? result.url : "";
|
|
60
|
+
lines.push(`- ${title} (${url})`);
|
|
61
|
+
}
|
|
62
|
+
if (lines.length === 0)
|
|
63
|
+
return "Web search returned no results.";
|
|
64
|
+
return ["Web search results (page contents no longer available):", ...lines].join("\n");
|
|
65
|
+
}
|
|
66
|
+
function demoteBlock(block, expired) {
|
|
67
|
+
if (!isRecord(block))
|
|
68
|
+
return undefined;
|
|
69
|
+
if (block.type === "server_tool_use" && block.name === "web_search" && typeof block.id === "string") {
|
|
70
|
+
if (!expired.has(block.id))
|
|
71
|
+
return undefined;
|
|
72
|
+
const query = searchQuery(block);
|
|
73
|
+
return { type: "text", text: query === undefined ? "Searched the web." : `Searched the web for: ${query}` };
|
|
74
|
+
}
|
|
75
|
+
if (block.type === "web_search_tool_result" && typeof block.tool_use_id === "string") {
|
|
76
|
+
if (!expired.has(block.tool_use_id))
|
|
77
|
+
return undefined;
|
|
78
|
+
return { type: "text", text: demotedResultText(block) };
|
|
79
|
+
}
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
export function demoteExpiredWebSearchReplay(params, expired) {
|
|
83
|
+
if (expired.size === 0)
|
|
84
|
+
return params;
|
|
85
|
+
let changed = false;
|
|
86
|
+
const messages = params.messages.map((message) => {
|
|
87
|
+
if (message.role !== "assistant" || !Array.isArray(message.content))
|
|
88
|
+
return message;
|
|
89
|
+
let messageChanged = false;
|
|
90
|
+
const content = message.content.map((block) => {
|
|
91
|
+
const demoted = demoteBlock(block, expired);
|
|
92
|
+
if (demoted === undefined)
|
|
93
|
+
return block;
|
|
94
|
+
messageChanged = true;
|
|
95
|
+
return demoted;
|
|
96
|
+
});
|
|
97
|
+
if (!messageChanged)
|
|
98
|
+
return message;
|
|
99
|
+
changed = true;
|
|
100
|
+
return { ...message, content };
|
|
101
|
+
});
|
|
102
|
+
return changed ? { ...params, messages } : params;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=anthropic-web-search-expiry.js.map
|
|
@@ -322,6 +322,25 @@ export function convertResponsesMessages(model, context, allowedToolCallProvider
|
|
|
322
322
|
// Signed foreign blocks with no text are intentionally dropped.
|
|
323
323
|
}
|
|
324
324
|
else if (block.type === "providerNative") {
|
|
325
|
+
// Responses accepts its completed web_search_call output items
|
|
326
|
+
// in the next input. Keep them only on the originating route/model;
|
|
327
|
+
// encrypted/native state must never cross provider boundaries.
|
|
328
|
+
// https://developers.openai.com/api/docs/guides/conversation-state
|
|
329
|
+
// Verified against the ChatGPT subscription endpoint for senpi#3122.
|
|
330
|
+
const raw = block.raw;
|
|
331
|
+
if (isSameModel &&
|
|
332
|
+
block.subtype === "web_search_call" &&
|
|
333
|
+
raw !== null &&
|
|
334
|
+
typeof raw === "object" &&
|
|
335
|
+
"type" in raw &&
|
|
336
|
+
raw.type === "web_search_call" &&
|
|
337
|
+
"id" in raw &&
|
|
338
|
+
typeof raw.id === "string" &&
|
|
339
|
+
"action" in raw &&
|
|
340
|
+
"status" in raw &&
|
|
341
|
+
(raw.status === "completed" || raw.status === "failed")) {
|
|
342
|
+
output.push(raw);
|
|
343
|
+
}
|
|
325
344
|
}
|
|
326
345
|
else if (block.type === "text") {
|
|
327
346
|
const textBlock = block;
|
|
@@ -63,6 +63,7 @@ function getCompat(model, env) {
|
|
|
63
63
|
supportsWebSocket: model.compat?.supportsWebSocket ?? isNativeEndpoint,
|
|
64
64
|
supportsRemoteCompactionV2: model.compat?.supportsRemoteCompactionV2 ?? isNativeEndpoint,
|
|
65
65
|
supportsWebSearchPreview: model.compat?.supportsWebSearchPreview ?? isNativeEndpoint,
|
|
66
|
+
webSearchToolType: model.compat?.webSearchToolType ?? "web_search_preview",
|
|
66
67
|
supportsImageGeneration: model.compat?.supportsImageGeneration ?? isNativeEndpoint,
|
|
67
68
|
supportsStrictMode: model.compat?.supportsStrictMode ?? false,
|
|
68
69
|
supportsOpenAIGrammarTools: model.compat?.supportsOpenAIGrammarTools ?? false,
|
|
@@ -102,7 +103,10 @@ function isRecord(value) {
|
|
|
102
103
|
return typeof value === "object" && value !== null;
|
|
103
104
|
}
|
|
104
105
|
function isOpenAiWebSearchPreviewTool(value) {
|
|
105
|
-
return isRecord(value) &&
|
|
106
|
+
return (isRecord(value) &&
|
|
107
|
+
(value.type === "web_search" ||
|
|
108
|
+
value.type === "web_search_preview" ||
|
|
109
|
+
value.type === "web_search_preview_2025_03_11"));
|
|
106
110
|
}
|
|
107
111
|
function sanitizeUnsupportedNativeTools(params, compat) {
|
|
108
112
|
if (compat.supportsWebSearchPreview) {
|
package/dist/model.d.ts
CHANGED
|
@@ -44,8 +44,12 @@ export interface Model<TApi extends Api> extends BaseModel<TApi> {
|
|
|
44
44
|
* probe; `modelSupportsAssistantPrefill` also applies the request settings.
|
|
45
45
|
*/
|
|
46
46
|
supportsAssistantPrefill?: boolean;
|
|
47
|
-
/**
|
|
48
|
-
compat?: TApi extends "openai-completions" ? OpenAICompletionsCompat : TApi extends "openai-responses" | "openai-codex-responses" | "azure-openai-responses" ? OpenAIResponsesCompat : TApi extends "anthropic-messages" ? AnthropicMessagesCompat : TApi extends "bedrock-converse-stream" ? BedrockCompat : TApi extends "mistral-conversations" ? MistralConversationsCompat : TApi extends "cursor-agent" ? CursorAgentCompat : TApi extends "devin-agent" ? DevinAgentCompat : never;
|
|
47
|
+
/** API compatibility overrides. Hosted search on unverified routes requires explicit opt-in. */
|
|
48
|
+
compat?: TApi extends "openai-completions" ? OpenAICompletionsCompat : TApi extends "openai-responses" | "openai-codex-responses" | "azure-openai-responses" ? OpenAIResponsesCompat : TApi extends "anthropic-messages" ? AnthropicMessagesCompat : TApi extends "bedrock-converse-stream" ? BedrockCompat : TApi extends "mistral-conversations" ? MistralConversationsCompat : TApi extends "cursor-agent" ? CursorAgentCompat : TApi extends "devin-agent" ? DevinAgentCompat : TApi extends "google-generative-ai" | "google-vertex" ? GoogleCompat : never;
|
|
49
|
+
}
|
|
50
|
+
export interface GoogleCompat {
|
|
51
|
+
/** Opt in to Google Search grounding, including mixed function-tool requests. */
|
|
52
|
+
supportsWebSearch?: boolean;
|
|
49
53
|
}
|
|
50
54
|
/** Devin (Cascade) model metadata the transport branches on. */
|
|
51
55
|
export interface DevinAgentCompat {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"schemaVersion":6,"generatedAt":"2026-10-
|
|
1
|
+
{"schemaVersion":6,"generatedAt":"2026-10-13T13:12:39.341Z","structureHash":"0afd01a0893138b8b6b57ae1ecb8f1d138de39fa8665a66073e13b4d418c35a0","files":{"alibaba-token-plan.json":"16b3c6c78d72c73ca34a1e79ce71c0cf36ba409d02e83a17396ce0949a1d5363","amazon-bedrock.json":"4a95e6bf974e5ca967d5c050df580262e13573e42b3186edb448fa11a302f09f","ant-ling.json":"628feab3ef6c7d80ce96f75999804e1d9f05fec3851e5302e30fff3b69db90f6","anthropic.json":"111384e9f5845e0247bd34edf2400ef7720fcbdac140b944cf8878192d8bd6ab","azure-openai-responses.json":"a3e52bd78f0220aa32bce519e13955b8e6e567860416343844cbe19902f4a682","bai.json":"7e6861aaf27d8af34e1771c6092602194fcec0a0046e6adfab9c3ee54d32b99f","baseten.json":"1fcdab8daf2881bd099eb08f9416f7df5bf39975937d2a63b841542bd032f2e6","cerebras.json":"3eccb58c5de3d0dc7ba49dc6e477ba29f00cb3150ce0a35cc90d59e6b5ab368e","chatgpt-subscription.json":"42b88c93d717b2a7469b71da7da5adf87347d0d9cc0f264b39117f0f940b51c0","cloudflare-ai-gateway.json":"673ac36e4531995c43e49e132577cdde18a0ba088648ecae5a904020f400abc2","cloudflare-workers-ai.json":"f351e80493dea7b7a2c31dceb086ee804a88d67c5f8a3c823ea907991fe5d659","deepseek.json":"10a296fb3e898f7715c80890c8af0af4f5fd58f22dbcd6612fdc5d762157ccdc","fireworks.json":"c3dc21dc2e95428fc9a38e3840c05bbf90f94e4c25dee0eedf0b385b49ff65a4","github-copilot.json":"2c5a84983477a7c4f6c001088a1e043ee959ce58c99fea6eb843e0286228664b","google-vertex.json":"96ecc19bf20a706f99ae658b021a0ee5438146119e1e99de5fd824a01fcfd81b","google.json":"c0f5a633e5e5a5044432db3e537f4bc674d4de18df09325c6349547fcc13cabe","groq.json":"910952ad9dad07856983459d63fce0148dba1203edd7b10d801a77c5824f3405","huggingface.json":"6399e958790a9d90c21fb55d1cbef8569427a1ceb28de86dadeb3b4454ecfade","meta.json":"1dbea20a92b0127190092e85fc4deab800d11e94dc2f8a3388c498f0121b29e8","minimax-cn.json":"f962c20ee6a6a342820f778e9dc363a18a524bf11998f6e2c57f2989915d62e5","minimax.json":"17a432f80d0ea6dd26eade80a4314250d3a855678601728a5f70dd8fa0d21dd2","mistral.json":"27aa66c71a0504b53dc87ec194286fa7e70b99747fc8df9b709b23b3548afe50","moonshotai-cn.json":"18954b55dc5edecc174d8a07a2c5993416dd9d3ce52e411438035c16ee982e8e","moonshotai.json":"62d08fd5beead025a56d5f2e4227d8516b9c5034275b1b36d761a8625918f2b9","nvidia.json":"a75e963dc81779e3eec01c051b12277c15ec37c5b94ab51ece9e9ccb3b16407c","openai.json":"9104538118053eed57f90852332b6ad274a4e4792389bdbc9cfca1ae400c705a","opencode-go.json":"48ff011f3e171f977cfed2928272667e9361f13a508e5994f00f2241f6a23419","opencode.json":"85e0eb8fef0984d56baf4a558b0efc74749ef37a5e8e486b75465214ef796d1d","opengateway.json":"67301348bfc620eb70b3dbb33e5a8f54cc8bb2a096a1cd6361847515fd2b6a5d","openrouter.json":"71649b2a757254226dac5071090fefc31518c9125f321367cc70828796d02e5c","qwen-token-plan-cn.json":"bd36a6d9cc2048deafacc85f7f0f2e51868c8b7aa09502e3e4e60e3e8b76f690","qwen-token-plan-individual.json":"35ffb0ba1a1ce3f68fc73262dd5e07fbcb5bf5911ad1e6e17bc0621e2894676a","qwen-token-plan.json":"ca27e49e62965221badead8e867b2007035c6b6a15a97c47219bd23d2c83c2b1","radius.json":"cbb5f3f94c5ef46a13650f4cd9a9c4a04421c925d98a95a11f79beb9c54258e7","together.json":"bbd7fad86cacfefe3d3d61f6638f22e9b6f5d575722fd1112a16aafe0485c46d","typesafe.json":"a4429a2f696d5cc5273969af61a7310bda07d6bdcd27bf9ac9c76c534cc43716","venice.json":"3604c88c46dd3e4151b833927c6f87bcc165d0999a4339b32db2ad74a9ff173f","vercel-ai-gateway.json":"7cd41726d00f807c2e71ef5ddeef35b948152fbe15cefe54e7a6a6f8381df395","xai.json":"d7771a1450b21c1f6aee887ca6fea61b6e7b3fc13b4dee5cf72a71134fe623bc","xiaomi-token-plan-ams.json":"4dd8f838e21d7aa7caf3122d0b0b1e5a02a33bc012b96e6a75bd119334152d7a","xiaomi-token-plan-cn.json":"46414ff49973b95a0c14bd33e8992cc1938a50ce2c7cea9e2e5cc02de8f1df31","xiaomi-token-plan-sgp.json":"f5b3f66bf7bb7238592d4a9667cad983a008d7539a66e9896b02422622800935","xiaomi.json":"8b62c158d55d5a165f35cd72c1fc82ab89ff8e73f08960105fc41b822f236f69","zai-coding-cn.json":"2a7f59d15f588a97d218997883f2506dfc52d3ee320697d39f14213a050fc912","zai.json":"e9cc408e1756de8abc70148d63d89d11a63c65b7ef06ad0661cd1328e4209ba3"}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"openai-codex-responses":{"chat:gpt-5.3-codex-spark":{"id":"gpt-5.3-codex-spark","name":"GPT-5.3 Codex Spark","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text"],"cost":{"input":1.75,"output":14,"cacheRead":0.175,"cacheWrite":0},"contextWindow":128000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","minimal":"low"},"compat":{"supportsOpenAIGrammarTools":true},"type":"chat"},"chat:gpt-5.5":{"id":"gpt-5.5","name":"GPT-5.5","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":30,"cacheRead":0.5,"cacheWrite":0,"tiers":[{"inputTokensAbove":272000,"input":10,"output":45,"cacheRead":1,"cacheWrite":0}]},"contextWindow":272000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","minimal":"low"},"compat":{"supportsOpenAIGrammarTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:gpt-5.6-luna":{"id":"gpt-5.6-luna","name":"GPT-5.6 Luna","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":0.2,"output":1.2,"cacheRead":0.02,"cacheWrite":0.25,"tiers":[{"inputTokensAbove":272000,"input":0.4,"output":1.8,"cacheRead":0.04,"cacheWrite":0.5}]},"contextWindow":272000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max","minimal":"low"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:gpt-5.6-luna-fast":{"id":"gpt-5.6-luna-fast","name":"GPT-5.6 Luna Fast","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":0.2,"output":1.2,"cacheRead":0.02,"cacheWrite":0.25,"tiers":[{"inputTokensAbove":272000,"input":0.4,"output":1.8,"cacheRead":0.04,"cacheWrite":0.5}]},"contextWindow":272000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max","minimal":"low"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"upstreamModelId":"gpt-5.6-luna","serviceTier":"priority","type":"chat"},"chat:gpt-5.6-sol":{"id":"gpt-5.6-sol","name":"GPT-5.6 Sol","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":4,"output":20,"cacheRead":0.4,"cacheWrite":5,"tiers":[{"inputTokensAbove":272000,"input":8,"output":30,"cacheRead":0.8,"cacheWrite":10}]},"contextWindow":650000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max","minimal":"low"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:gpt-5.6-sol-fast":{"id":"gpt-5.6-sol-fast","name":"GPT-5.6 Sol Fast","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":4,"output":20,"cacheRead":0.4,"cacheWrite":5,"tiers":[{"inputTokensAbove":272000,"input":8,"output":30,"cacheRead":0.8,"cacheWrite":10}]},"contextWindow":650000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max","minimal":"low"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"upstreamModelId":"gpt-5.6-sol","serviceTier":"priority","type":"chat"},"chat:gpt-5.6-terra":{"id":"gpt-5.6-terra","name":"GPT-5.6 Terra","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":12,"cacheRead":0.2,"cacheWrite":2.5,"tiers":[{"inputTokensAbove":272000,"input":4,"output":18,"cacheRead":0.4,"cacheWrite":5}]},"contextWindow":272000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max","minimal":"low"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:gpt-5.6-terra-fast":{"id":"gpt-5.6-terra-fast","name":"GPT-5.6 Terra Fast","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":12,"cacheRead":0.2,"cacheWrite":2.5,"tiers":[{"inputTokensAbove":272000,"input":4,"output":18,"cacheRead":0.4,"cacheWrite":5}]},"contextWindow":272000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max","minimal":"low"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"upstreamModelId":"gpt-5.6-terra","serviceTier":"priority","type":"chat"},"chat:gpt-6-astra":{"id":"gpt-6-astra","name":"GPT-6 Astra","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":10,"output":50,"cacheRead":1,"cacheWrite":12.5,"tiers":[{"inputTokensAbove":272000,"input":20,"output":75,"cacheRead":2,"cacheWrite":25}]},"contextWindow":600000,"maxTokens":128000,"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true,"supportsConfigurationUpdate":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:gpt-6-astra-fast":{"id":"gpt-6-astra-fast","name":"GPT-6 Astra Fast","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":10,"output":50,"cacheRead":1,"cacheWrite":12.5,"tiers":[{"inputTokensAbove":272000,"input":20,"output":75,"cacheRead":2,"cacheWrite":25}]},"contextWindow":600000,"maxTokens":128000,"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true,"supportsConfigurationUpdate":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"upstreamModelId":"gpt-6-astra","serviceTier":"priority","type":"chat"},"chat:gpt-6-luna":{"id":"gpt-6-luna","name":"GPT-6 Luna","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":0.1,"output":0.5,"cacheRead":0.01,"cacheWrite":0.125,"tiers":[{"inputTokensAbove":272000,"input":0.2,"output":0.75,"cacheRead":0.02,"cacheWrite":0.25}]},"contextWindow":922000,"maxTokens":128000,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:gpt-6-luna-fast":{"id":"gpt-6-luna-fast","name":"GPT-6 Luna Fast","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":0.1,"output":0.5,"cacheRead":0.01,"cacheWrite":0.125,"tiers":[{"inputTokensAbove":272000,"input":0.2,"output":0.75,"cacheRead":0.02,"cacheWrite":0.25}]},"contextWindow":922000,"maxTokens":128000,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"upstreamModelId":"gpt-6-luna","serviceTier":"priority","type":"chat"},"chat:gpt-6-sol":{"id":"gpt-6-sol","name":"GPT-6 Sol","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":10,"cacheRead":0.2,"cacheWrite":2.5,"tiers":[{"inputTokensAbove":272000,"input":4,"output":15,"cacheRead":0.4,"cacheWrite":5}]},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:gpt-6-sol-fast":{"id":"gpt-6-sol-fast","name":"GPT-6 Sol Fast","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":10,"cacheRead":0.2,"cacheWrite":2.5,"tiers":[{"inputTokensAbove":272000,"input":4,"output":15,"cacheRead":0.4,"cacheWrite":5}]},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"upstreamModelId":"gpt-6-sol","serviceTier":"priority","type":"chat"},"chat:gpt-6.1-sol":{"id":"gpt-6.1-sol","name":"GPT-6.1 Sol","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":10,"cacheRead":0.1,"cacheWrite":2.5,"tiers":[{"inputTokensAbove":272000,"input":4,"output":15,"cacheRead":0.2,"cacheWrite":5}]},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true,"supportsConfigurationUpdate":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:gpt-6.1-sol-fast":{"id":"gpt-6.1-sol-fast","name":"GPT-6.1 Sol Fast","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":10,"cacheRead":0.1,"cacheWrite":2.5,"tiers":[{"inputTokensAbove":272000,"input":4,"output":15,"cacheRead":0.2,"cacheWrite":5}]},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true,"supportsConfigurationUpdate":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"upstreamModelId":"gpt-6.1-sol","serviceTier":"priority","type":"chat"},"chat:gpt-6.1-sol-ultrafast":{"id":"gpt-6.1-sol-ultrafast","name":"GPT-6.1 Sol Ultrafast","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":10,"cacheRead":0.1,"cacheWrite":2.5,"tiers":[{"inputTokensAbove":272000,"input":4,"output":15,"cacheRead":0.2,"cacheWrite":5}]},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true,"supportsConfigurationUpdate":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"upstreamModelId":"gpt-6.1-sol","serviceTier":"ultrafast","defaultThinkingLevel":"xhigh","type":"chat"}}}
|
|
1
|
+
{"openai-codex-responses":{"chat:gpt-5.3-codex-spark":{"id":"gpt-5.3-codex-spark","name":"GPT-5.3 Codex Spark","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text"],"cost":{"input":1.75,"output":14,"cacheRead":0.175,"cacheWrite":0},"contextWindow":128000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","minimal":"low"},"compat":{"supportsOpenAIGrammarTools":true},"type":"chat"},"chat:gpt-5.5":{"id":"gpt-5.5","name":"GPT-5.5","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":5,"output":30,"cacheRead":0.5,"cacheWrite":0,"tiers":[{"inputTokensAbove":272000,"input":10,"output":45,"cacheRead":1,"cacheWrite":0}]},"contextWindow":272000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","minimal":"low"},"compat":{"supportsOpenAIGrammarTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:gpt-5.6-luna":{"id":"gpt-5.6-luna","name":"GPT-5.6 Luna","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":0.2,"output":1.2,"cacheRead":0.02,"cacheWrite":0.25,"tiers":[{"inputTokensAbove":272000,"input":0.4,"output":1.8,"cacheRead":0.04,"cacheWrite":0.5}]},"contextWindow":272000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max","minimal":"low"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:gpt-5.6-luna-fast":{"id":"gpt-5.6-luna-fast","name":"GPT-5.6 Luna Fast","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":0.2,"output":1.2,"cacheRead":0.02,"cacheWrite":0.25,"tiers":[{"inputTokensAbove":272000,"input":0.4,"output":1.8,"cacheRead":0.04,"cacheWrite":0.5}]},"contextWindow":272000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max","minimal":"low"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"upstreamModelId":"gpt-5.6-luna","serviceTier":"priority","type":"chat"},"chat:gpt-5.6-sol":{"id":"gpt-5.6-sol","name":"GPT-5.6 Sol","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":4,"output":20,"cacheRead":0.4,"cacheWrite":5,"tiers":[{"inputTokensAbove":272000,"input":8,"output":30,"cacheRead":0.8,"cacheWrite":10}]},"contextWindow":650000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max","minimal":"low"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:gpt-5.6-sol-fast":{"id":"gpt-5.6-sol-fast","name":"GPT-5.6 Sol Fast","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":4,"output":20,"cacheRead":0.4,"cacheWrite":5,"tiers":[{"inputTokensAbove":272000,"input":8,"output":30,"cacheRead":0.8,"cacheWrite":10}]},"contextWindow":650000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max","minimal":"low"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"upstreamModelId":"gpt-5.6-sol","serviceTier":"priority","type":"chat"},"chat:gpt-5.6-terra":{"id":"gpt-5.6-terra","name":"GPT-5.6 Terra","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":12,"cacheRead":0.2,"cacheWrite":2.5,"tiers":[{"inputTokensAbove":272000,"input":4,"output":18,"cacheRead":0.4,"cacheWrite":5}]},"contextWindow":272000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max","minimal":"low"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:gpt-5.6-terra-fast":{"id":"gpt-5.6-terra-fast","name":"GPT-5.6 Terra Fast","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":12,"cacheRead":0.2,"cacheWrite":2.5,"tiers":[{"inputTokensAbove":272000,"input":4,"output":18,"cacheRead":0.4,"cacheWrite":5}]},"contextWindow":272000,"maxTokens":128000,"thinkingLevelMap":{"xhigh":"xhigh","max":"max","minimal":"low"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"upstreamModelId":"gpt-5.6-terra","serviceTier":"priority","type":"chat"},"chat:gpt-6-astra":{"id":"gpt-6-astra","name":"GPT-6 Astra","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":10,"output":50,"cacheRead":1,"cacheWrite":12.5,"tiers":[{"inputTokensAbove":272000,"input":20,"output":75,"cacheRead":2,"cacheWrite":25}]},"contextWindow":600000,"maxTokens":128000,"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true,"supportsConfigurationUpdate":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:gpt-6-astra-fast":{"id":"gpt-6-astra-fast","name":"GPT-6 Astra Fast","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":10,"output":50,"cacheRead":1,"cacheWrite":12.5,"tiers":[{"inputTokensAbove":272000,"input":20,"output":75,"cacheRead":2,"cacheWrite":25}]},"contextWindow":600000,"maxTokens":128000,"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true,"supportsConfigurationUpdate":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"upstreamModelId":"gpt-6-astra","serviceTier":"priority","type":"chat"},"chat:gpt-6-luna":{"id":"gpt-6-luna","name":"GPT-6 Luna","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":0.1,"output":0.5,"cacheRead":0.01,"cacheWrite":0.125,"tiers":[{"inputTokensAbove":272000,"input":0.2,"output":0.75,"cacheRead":0.02,"cacheWrite":0.25}]},"contextWindow":922000,"maxTokens":128000,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:gpt-6-luna-fast":{"id":"gpt-6-luna-fast","name":"GPT-6 Luna Fast","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":0.1,"output":0.5,"cacheRead":0.01,"cacheWrite":0.125,"tiers":[{"inputTokensAbove":272000,"input":0.2,"output":0.75,"cacheRead":0.02,"cacheWrite":0.25}]},"contextWindow":922000,"maxTokens":128000,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"upstreamModelId":"gpt-6-luna","serviceTier":"priority","type":"chat"},"chat:gpt-6-sol":{"id":"gpt-6-sol","name":"GPT-6 Sol","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":10,"cacheRead":0.2,"cacheWrite":2.5,"tiers":[{"inputTokensAbove":272000,"input":4,"output":15,"cacheRead":0.4,"cacheWrite":5}]},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:gpt-6-sol-fast":{"id":"gpt-6-sol-fast","name":"GPT-6 Sol Fast","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":10,"cacheRead":0.2,"cacheWrite":2.5,"tiers":[{"inputTokensAbove":272000,"input":4,"output":15,"cacheRead":0.4,"cacheWrite":5}]},"contextWindow":400000,"maxTokens":128000,"thinkingLevelMap":{"off":"none","minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"upstreamModelId":"gpt-6-sol","serviceTier":"priority","type":"chat"},"chat:gpt-6.1-sol":{"id":"gpt-6.1-sol","name":"GPT-6.1 Sol","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":10,"cacheRead":0.1,"cacheWrite":2.5,"tiers":[{"inputTokensAbove":272000,"input":4,"output":15,"cacheRead":0.2,"cacheWrite":5}]},"contextWindow":500000,"maxTokens":128000,"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true,"supportsConfigurationUpdate":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"type":"chat"},"chat:gpt-6.1-sol-fast":{"id":"gpt-6.1-sol-fast","name":"GPT-6.1 Sol Fast","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":10,"cacheRead":0.1,"cacheWrite":2.5,"tiers":[{"inputTokensAbove":272000,"input":4,"output":15,"cacheRead":0.2,"cacheWrite":5}]},"contextWindow":500000,"maxTokens":128000,"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true,"supportsConfigurationUpdate":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"upstreamModelId":"gpt-6.1-sol","serviceTier":"priority","type":"chat"},"chat:gpt-6.1-sol-ultrafast":{"id":"gpt-6.1-sol-ultrafast","name":"GPT-6.1 Sol Ultrafast","api":"openai-codex-responses","provider":"chatgpt-subscription","baseUrl":"https://chatgpt.com/backend-api","reasoning":true,"input":["text","image"],"cost":{"input":2,"output":10,"cacheRead":0.1,"cacheWrite":2.5,"tiers":[{"inputTokensAbove":272000,"input":4,"output":15,"cacheRead":0.2,"cacheWrite":5}]},"contextWindow":500000,"maxTokens":128000,"thinkingLevelMap":{"off":null,"minimal":null,"low":"low","medium":"medium","high":"high","xhigh":"xhigh","max":"max"},"compat":{"supportsOpenAIGrammarTools":true,"supportsAdditionalTools":true,"supportsToolSearch":true,"supportsMidConvoSystemMessages":true,"supportsConfigurationUpdate":true},"inputLimits":{"images":{"resize":{"maxWidth":2000,"maxHeight":2000,"maxBytes":4718592,"jpegQuality":80}}},"upstreamModelId":"gpt-6.1-sol","serviceTier":"ultrafast","defaultThinkingLevel":"xhigh","type":"chat"}}}
|