@gajae-code/ai 0.9.3 → 0.9.5
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/CHANGELOG.md +15 -0
- package/package.json +2 -2
- package/src/model-thinking.ts +33 -2
- package/src/models.json +159 -0
- package/src/models.ts +10 -6
- package/src/providers/anthropic.ts +37 -15
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.9.5] - 2026-07-09
|
|
6
|
+
### Added
|
|
7
|
+
|
|
8
|
+
- Added GPT-5.6 Sol, Terra, and Luna catalog/parser support for OpenAI and OpenAI code transports, including `low` through canonical `max` reasoning efforts, verified pricing/limits, and GPT-5.6 cache-write pricing (#1925; OmX #3103).
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Stopped requesting `strict: true` tool use on Anthropic OAuth requests: the Claude Code OAuth surface mishandles strict tools, returning tool calls with empty/undefined arguments and occasionally corrupted tool names. API-key requests keep strict tool use; `PI_NO_STRICT=1` is no longer needed as a workaround.
|
|
13
|
+
|
|
14
|
+
## [0.9.4] - 2026-07-09
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Preserved Anthropic OAuth tool-call names and streamed arguments across interleaved tool-use blocks, preventing prefixed tool names and partial JSON deltas from being dropped or misattributed.
|
|
18
|
+
- Embedded `models.json` via a `with { type: "file" }` import so compiled release binaries load the bundled model catalog from bunfs instead of crashing at startup with `Cannot find module './packages/ai/src/models.json'` (v0.9.3 regression, #1914).
|
|
19
|
+
|
|
5
20
|
## [0.9.2] - 2026-07-09
|
|
6
21
|
### Added
|
|
7
22
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/ai",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.5",
|
|
5
5
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
6
6
|
"homepage": "https://gajae-code.com",
|
|
7
7
|
"author": "Yeachan-Heo and Gajae Code Contributors",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@anthropic-ai/sdk": "^0.94.0",
|
|
42
42
|
"@bufbuild/protobuf": "^2.12.0",
|
|
43
|
-
"@gajae-code/utils": "0.9.
|
|
43
|
+
"@gajae-code/utils": "0.9.5",
|
|
44
44
|
"openai": "^6.36.0",
|
|
45
45
|
"partial-json": "^0.1.7",
|
|
46
46
|
"zod": "4.4.3"
|
package/src/model-thinking.ts
CHANGED
|
@@ -47,6 +47,7 @@ const DEFAULT_REASONING_EFFORTS_WITH_XHIGH_AND_MAX: readonly Effort[] = [
|
|
|
47
47
|
const GEMINI_3_PRO_EFFORTS: readonly Effort[] = [Effort.Low, Effort.High];
|
|
48
48
|
const GEMINI_3_FLASH_EFFORTS: readonly Effort[] = [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High];
|
|
49
49
|
const GPT_5_2_PLUS_EFFORTS: readonly Effort[] = [Effort.Low, Effort.Medium, Effort.High, Effort.XHigh];
|
|
50
|
+
const GPT_5_6_PLUS_EFFORTS: readonly Effort[] = [Effort.Low, Effort.Medium, Effort.High, Effort.XHigh, Effort.Max];
|
|
50
51
|
const GPT_5_5_DEFAULT_EFFORT = Effort.XHigh;
|
|
51
52
|
|
|
52
53
|
const GPT_5_1_CODEX_MINI_EFFORTS: readonly Effort[] = [Effort.Medium, Effort.High];
|
|
@@ -60,7 +61,18 @@ type SemVer = {
|
|
|
60
61
|
|
|
61
62
|
type GeminiKind = "pro" | "flash";
|
|
62
63
|
type AnthropicKind = "opus" | "sonnet";
|
|
63
|
-
type OpenAIVariant =
|
|
64
|
+
type OpenAIVariant =
|
|
65
|
+
| "base"
|
|
66
|
+
| "codex"
|
|
67
|
+
| "codex-max"
|
|
68
|
+
| "codex-mini"
|
|
69
|
+
| "codex-spark"
|
|
70
|
+
| "luna"
|
|
71
|
+
| "mini"
|
|
72
|
+
| "max"
|
|
73
|
+
| "nano"
|
|
74
|
+
| "sol"
|
|
75
|
+
| "terra";
|
|
64
76
|
|
|
65
77
|
const CODEX_GPT_5_4_PRIORITY_BY_VARIANT: Partial<Record<OpenAIVariant, number>> = {
|
|
66
78
|
base: 0,
|
|
@@ -465,11 +477,24 @@ function applyGpt55ContextWindow(model: ApiModel<Api>, parsedModel: OpenAIModel)
|
|
|
465
477
|
}
|
|
466
478
|
return false;
|
|
467
479
|
}
|
|
480
|
+
const GPT_5_6_TIER_VARIANTS: ReadonlySet<OpenAIVariant> = new Set(["base", "sol", "terra", "luna"]);
|
|
481
|
+
|
|
482
|
+
function applyGpt56ContextWindow(model: ApiModel<Api>, parsedModel: OpenAIModel): boolean {
|
|
483
|
+
if (!semverGte(parsedModel.version, "5.6") || !GPT_5_6_TIER_VARIANTS.has(parsedModel.variant)) {
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
model.contextWindow =
|
|
487
|
+
model.provider === "openai-codex" || model.api === "openai-codex-responses" ? 272_000 : 1_050_000;
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
468
490
|
|
|
469
491
|
function applyOpenAICatalogPolicy(model: ApiModel<Api>, parsedModel: OpenAIModel): void {
|
|
470
492
|
if (applyGpt55ContextWindow(model, parsedModel)) {
|
|
471
493
|
return;
|
|
472
494
|
}
|
|
495
|
+
if (applyGpt56ContextWindow(model, parsedModel)) {
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
473
498
|
// OpenAI code backend models: 400K figure includes output budget; input window is 272K.
|
|
474
499
|
if (parsedModel.variant.startsWith("codex") && parsedModel.variant !== "codex-spark") {
|
|
475
500
|
model.contextWindow = 272000;
|
|
@@ -582,6 +607,9 @@ function inferOpenAISupportedEfforts(model: OpenAIModel): readonly Effort[] {
|
|
|
582
607
|
if (model.variant === "codex-mini" && semverEqual(model.version, "5.1")) {
|
|
583
608
|
return GPT_5_1_CODEX_MINI_EFFORTS;
|
|
584
609
|
}
|
|
610
|
+
if (semverGte(model.version, "5.6")) {
|
|
611
|
+
return GPT_5_6_PLUS_EFFORTS;
|
|
612
|
+
}
|
|
585
613
|
if (semverGte(model.version, "5.2")) {
|
|
586
614
|
return GPT_5_2_PLUS_EFFORTS;
|
|
587
615
|
}
|
|
@@ -714,7 +742,10 @@ function parseAnthropicModel(modelId: string): AnthropicModel | null {
|
|
|
714
742
|
}
|
|
715
743
|
|
|
716
744
|
function parseOpenAIModel(modelId: string): OpenAIModel | null {
|
|
717
|
-
const match =
|
|
745
|
+
const match =
|
|
746
|
+
/gpt-(\d+(?:\.\d+){0,2})(?:-(codex-spark|codex-mini|codex-max|codex|luna|mini|max|nano|sol|terra))?$/.exec(
|
|
747
|
+
modelId,
|
|
748
|
+
);
|
|
718
749
|
if (!match) {
|
|
719
750
|
return null;
|
|
720
751
|
}
|
package/src/models.json
CHANGED
|
@@ -56291,6 +56291,84 @@
|
|
|
56291
56291
|
"maxLevel": "xhigh"
|
|
56292
56292
|
}
|
|
56293
56293
|
},
|
|
56294
|
+
"gpt-5.6-luna": {
|
|
56295
|
+
"id": "gpt-5.6-luna",
|
|
56296
|
+
"name": "GPT-5.6 Luna",
|
|
56297
|
+
"api": "openai-responses",
|
|
56298
|
+
"provider": "openai",
|
|
56299
|
+
"baseUrl": "",
|
|
56300
|
+
"reasoning": true,
|
|
56301
|
+
"input": [
|
|
56302
|
+
"text",
|
|
56303
|
+
"image"
|
|
56304
|
+
],
|
|
56305
|
+
"cost": {
|
|
56306
|
+
"input": 1,
|
|
56307
|
+
"output": 6,
|
|
56308
|
+
"cacheRead": 0.1,
|
|
56309
|
+
"cacheWrite": 1.25
|
|
56310
|
+
},
|
|
56311
|
+
"contextWindow": 1050000,
|
|
56312
|
+
"maxTokens": 128000,
|
|
56313
|
+
"thinking": {
|
|
56314
|
+
"mode": "effort",
|
|
56315
|
+
"minLevel": "low",
|
|
56316
|
+
"maxLevel": "max"
|
|
56317
|
+
},
|
|
56318
|
+
"applyPatchToolType": "freeform"
|
|
56319
|
+
},
|
|
56320
|
+
"gpt-5.6-sol": {
|
|
56321
|
+
"id": "gpt-5.6-sol",
|
|
56322
|
+
"name": "GPT-5.6 Sol",
|
|
56323
|
+
"api": "openai-responses",
|
|
56324
|
+
"provider": "openai",
|
|
56325
|
+
"baseUrl": "",
|
|
56326
|
+
"reasoning": true,
|
|
56327
|
+
"input": [
|
|
56328
|
+
"text",
|
|
56329
|
+
"image"
|
|
56330
|
+
],
|
|
56331
|
+
"cost": {
|
|
56332
|
+
"input": 5,
|
|
56333
|
+
"output": 30,
|
|
56334
|
+
"cacheRead": 0.5,
|
|
56335
|
+
"cacheWrite": 6.25
|
|
56336
|
+
},
|
|
56337
|
+
"contextWindow": 1050000,
|
|
56338
|
+
"maxTokens": 128000,
|
|
56339
|
+
"thinking": {
|
|
56340
|
+
"mode": "effort",
|
|
56341
|
+
"minLevel": "low",
|
|
56342
|
+
"maxLevel": "max"
|
|
56343
|
+
},
|
|
56344
|
+
"applyPatchToolType": "freeform"
|
|
56345
|
+
},
|
|
56346
|
+
"gpt-5.6-terra": {
|
|
56347
|
+
"id": "gpt-5.6-terra",
|
|
56348
|
+
"name": "GPT-5.6 Terra",
|
|
56349
|
+
"api": "openai-responses",
|
|
56350
|
+
"provider": "openai",
|
|
56351
|
+
"baseUrl": "",
|
|
56352
|
+
"reasoning": true,
|
|
56353
|
+
"input": [
|
|
56354
|
+
"text",
|
|
56355
|
+
"image"
|
|
56356
|
+
],
|
|
56357
|
+
"cost": {
|
|
56358
|
+
"input": 2.5,
|
|
56359
|
+
"output": 15,
|
|
56360
|
+
"cacheRead": 0.25,
|
|
56361
|
+
"cacheWrite": 3.125
|
|
56362
|
+
},
|
|
56363
|
+
"contextWindow": 1050000,
|
|
56364
|
+
"maxTokens": 128000,
|
|
56365
|
+
"thinking": {
|
|
56366
|
+
"mode": "effort",
|
|
56367
|
+
"minLevel": "low",
|
|
56368
|
+
"maxLevel": "max"
|
|
56369
|
+
},
|
|
56370
|
+
"applyPatchToolType": "freeform"
|
|
56371
|
+
},
|
|
56294
56372
|
"o1": {
|
|
56295
56373
|
"id": "o1",
|
|
56296
56374
|
"name": "o1",
|
|
@@ -56938,6 +57016,87 @@
|
|
|
56938
57016
|
"defaultLevel": "xhigh"
|
|
56939
57017
|
},
|
|
56940
57018
|
"applyPatchToolType": "freeform"
|
|
57019
|
+
},
|
|
57020
|
+
"gpt-5.6-luna": {
|
|
57021
|
+
"id": "gpt-5.6-luna",
|
|
57022
|
+
"name": "GPT-5.6 Luna",
|
|
57023
|
+
"api": "openai-codex-responses",
|
|
57024
|
+
"provider": "openai-codex",
|
|
57025
|
+
"baseUrl": "https://chatgpt.com/backend-api",
|
|
57026
|
+
"reasoning": true,
|
|
57027
|
+
"input": [
|
|
57028
|
+
"text",
|
|
57029
|
+
"image"
|
|
57030
|
+
],
|
|
57031
|
+
"cost": {
|
|
57032
|
+
"input": 1,
|
|
57033
|
+
"output": 6,
|
|
57034
|
+
"cacheRead": 0.1,
|
|
57035
|
+
"cacheWrite": 1.25
|
|
57036
|
+
},
|
|
57037
|
+
"contextWindow": 272000,
|
|
57038
|
+
"maxTokens": 128000,
|
|
57039
|
+
"preferWebsockets": true,
|
|
57040
|
+
"thinking": {
|
|
57041
|
+
"mode": "effort",
|
|
57042
|
+
"minLevel": "low",
|
|
57043
|
+
"maxLevel": "max"
|
|
57044
|
+
},
|
|
57045
|
+
"applyPatchToolType": "freeform"
|
|
57046
|
+
},
|
|
57047
|
+
"gpt-5.6-sol": {
|
|
57048
|
+
"id": "gpt-5.6-sol",
|
|
57049
|
+
"name": "GPT-5.6 Sol",
|
|
57050
|
+
"api": "openai-codex-responses",
|
|
57051
|
+
"provider": "openai-codex",
|
|
57052
|
+
"baseUrl": "https://chatgpt.com/backend-api",
|
|
57053
|
+
"reasoning": true,
|
|
57054
|
+
"input": [
|
|
57055
|
+
"text",
|
|
57056
|
+
"image"
|
|
57057
|
+
],
|
|
57058
|
+
"cost": {
|
|
57059
|
+
"input": 5,
|
|
57060
|
+
"output": 30,
|
|
57061
|
+
"cacheRead": 0.5,
|
|
57062
|
+
"cacheWrite": 6.25
|
|
57063
|
+
},
|
|
57064
|
+
"contextWindow": 272000,
|
|
57065
|
+
"maxTokens": 128000,
|
|
57066
|
+
"preferWebsockets": true,
|
|
57067
|
+
"thinking": {
|
|
57068
|
+
"mode": "effort",
|
|
57069
|
+
"minLevel": "low",
|
|
57070
|
+
"maxLevel": "max"
|
|
57071
|
+
},
|
|
57072
|
+
"applyPatchToolType": "freeform"
|
|
57073
|
+
},
|
|
57074
|
+
"gpt-5.6-terra": {
|
|
57075
|
+
"id": "gpt-5.6-terra",
|
|
57076
|
+
"name": "GPT-5.6 Terra",
|
|
57077
|
+
"api": "openai-codex-responses",
|
|
57078
|
+
"provider": "openai-codex",
|
|
57079
|
+
"baseUrl": "https://chatgpt.com/backend-api",
|
|
57080
|
+
"reasoning": true,
|
|
57081
|
+
"input": [
|
|
57082
|
+
"text",
|
|
57083
|
+
"image"
|
|
57084
|
+
],
|
|
57085
|
+
"cost": {
|
|
57086
|
+
"input": 2.5,
|
|
57087
|
+
"output": 15,
|
|
57088
|
+
"cacheRead": 0.25,
|
|
57089
|
+
"cacheWrite": 3.125
|
|
57090
|
+
},
|
|
57091
|
+
"contextWindow": 272000,
|
|
57092
|
+
"maxTokens": 128000,
|
|
57093
|
+
"preferWebsockets": true,
|
|
57094
|
+
"thinking": {
|
|
57095
|
+
"mode": "effort",
|
|
57096
|
+
"minLevel": "low",
|
|
57097
|
+
"maxLevel": "max"
|
|
57098
|
+
},
|
|
57099
|
+
"applyPatchToolType": "freeform"
|
|
56941
57100
|
}
|
|
56942
57101
|
},
|
|
56943
57102
|
"opencode": {
|
package/src/models.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
2
|
import { isRetiredModelKey } from "./model-retirements";
|
|
3
3
|
import { applyGeneratedModelPolicies, enrichModelThinking } from "./model-thinking";
|
|
4
|
+
// `with { type: "file" }` is embedded by `bun build --compile` and resolves to
|
|
5
|
+
// the bunfs path inside standalone binaries (and to the on-disk path in dev).
|
|
6
|
+
// A plain `createRequire` of a `.json` listed as an extra compile entrypoint is
|
|
7
|
+
// NOT emitted into the bunfs, and its cwd-fallback masks the failure whenever
|
|
8
|
+
// the process runs inside a repo checkout — see PR body for the minimal repro.
|
|
9
|
+
import modelsJsonPath from "./models.json" with { type: "file" };
|
|
4
10
|
import type { Api, KnownProvider, Model, Usage } from "./types";
|
|
5
11
|
import { isClaudeForcedToolChoiceIncapableModelId } from "./utils/tool-choice-capability";
|
|
6
12
|
|
|
@@ -14,16 +20,14 @@ import { isClaudeForcedToolChoiceIncapableModelId } from "./utils/tool-choice-ca
|
|
|
14
20
|
*/
|
|
15
21
|
type BundledCatalog = typeof import("./models.json");
|
|
16
22
|
|
|
17
|
-
const require = createRequire(import.meta.url);
|
|
18
|
-
const COMPILED_MODELS_PATH = "./packages/ai/src/models.json";
|
|
19
23
|
let bundledCatalog: BundledCatalog | undefined;
|
|
20
24
|
let providerNames: KnownProvider[] | undefined;
|
|
21
25
|
const providerModelRegistry: Map<string, Map<string, Model<Api>>> = new Map();
|
|
22
26
|
|
|
23
27
|
function getBundledCatalog(): BundledCatalog {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
) as BundledCatalog;
|
|
28
|
+
// TS types a .json import as its contents; at runtime `with { type: "file" }`
|
|
29
|
+
// yields the file path (bunfs path in compiled binaries, disk path in dev).
|
|
30
|
+
bundledCatalog ??= JSON.parse(readFileSync(modelsJsonPath as unknown as string, "utf8")) as BundledCatalog;
|
|
27
31
|
return bundledCatalog;
|
|
28
32
|
}
|
|
29
33
|
|
|
@@ -586,15 +586,12 @@ const ANTHROPIC_BUILTIN_TOOL_NAMES = new Set(["web_search", "code_execution", "t
|
|
|
586
586
|
export const applyClaudeToolPrefix = (name: string, prefixOverride: string = claudeToolPrefix) => {
|
|
587
587
|
if (!prefixOverride) return name;
|
|
588
588
|
if (ANTHROPIC_BUILTIN_TOOL_NAMES.has(name.toLowerCase())) return name;
|
|
589
|
-
const prefix = prefixOverride.toLowerCase();
|
|
590
|
-
if (name.toLowerCase().startsWith(prefix)) return name;
|
|
591
589
|
return `${prefixOverride}${name}`;
|
|
592
590
|
};
|
|
593
591
|
|
|
594
592
|
export const stripClaudeToolPrefix = (name: string, prefixOverride: string = claudeToolPrefix) => {
|
|
595
593
|
if (!prefixOverride) return name;
|
|
596
|
-
|
|
597
|
-
if (!name.toLowerCase().startsWith(prefix)) return name;
|
|
594
|
+
if (!name.startsWith(prefixOverride)) return name;
|
|
598
595
|
return name.slice(prefixOverride.length);
|
|
599
596
|
};
|
|
600
597
|
|
|
@@ -1325,6 +1322,25 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1325
1322
|
| (ToolCall & { partialJson: string })
|
|
1326
1323
|
) & { index: number };
|
|
1327
1324
|
const blocks = output.content as Block[];
|
|
1325
|
+
const blocksByAnthropicIndex = new Map<number, Block>();
|
|
1326
|
+
const getBlockByAnthropicIndex = (anthropicIndex: number) => {
|
|
1327
|
+
const block = blocksByAnthropicIndex.get(anthropicIndex);
|
|
1328
|
+
if (!block) return { block: undefined, contentIndex: -1 };
|
|
1329
|
+
return { block, contentIndex: blocks.indexOf(block) };
|
|
1330
|
+
};
|
|
1331
|
+
const trackBlockByAnthropicIndex = (anthropicIndex: number, block: Block) => {
|
|
1332
|
+
// A duplicate start for an active index is a provider-envelope violation;
|
|
1333
|
+
// finalize the orphaned block so no internal stream fields leak into output.
|
|
1334
|
+
const orphaned = blocksByAnthropicIndex.get(anthropicIndex);
|
|
1335
|
+
if (orphaned) {
|
|
1336
|
+
if (orphaned.type === "toolCall" && orphaned.partialJson.trim()) {
|
|
1337
|
+
orphaned.arguments = parseStreamingJson(orphaned.partialJson);
|
|
1338
|
+
}
|
|
1339
|
+
delete (orphaned as { index?: number }).index;
|
|
1340
|
+
delete (orphaned as { partialJson?: string }).partialJson;
|
|
1341
|
+
}
|
|
1342
|
+
blocksByAnthropicIndex.set(anthropicIndex, block);
|
|
1343
|
+
};
|
|
1328
1344
|
const idleTimeoutMs = options?.streamIdleTimeoutMs ?? getStreamIdleTimeoutMs();
|
|
1329
1345
|
const firstEventTimeoutMs = options?.streamFirstEventTimeoutMs ?? getStreamFirstEventTimeoutMs(idleTimeoutMs);
|
|
1330
1346
|
stream.push({ type: "start", partial: output });
|
|
@@ -1334,6 +1350,8 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1334
1350
|
let providerRetryAttempt = 0;
|
|
1335
1351
|
let thinkingRepairAttempted = false;
|
|
1336
1352
|
while (true) {
|
|
1353
|
+
// Retries reset output.content; drop stale block correlations from the aborted attempt.
|
|
1354
|
+
blocksByAnthropicIndex.clear();
|
|
1337
1355
|
activeAbortTracker = createAbortSourceTracker(options?.signal);
|
|
1338
1356
|
const firstEventTimeoutAbortError = new Error(
|
|
1339
1357
|
"Anthropic stream timed out while waiting for the first event",
|
|
@@ -1403,6 +1421,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1403
1421
|
index: event.index,
|
|
1404
1422
|
};
|
|
1405
1423
|
output.content.push(block);
|
|
1424
|
+
trackBlockByAnthropicIndex(event.index, block);
|
|
1406
1425
|
stream.push({
|
|
1407
1426
|
type: "text_start",
|
|
1408
1427
|
contentIndex: output.content.length - 1,
|
|
@@ -1416,6 +1435,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1416
1435
|
index: event.index,
|
|
1417
1436
|
};
|
|
1418
1437
|
output.content.push(block);
|
|
1438
|
+
trackBlockByAnthropicIndex(event.index, block);
|
|
1419
1439
|
stream.push({
|
|
1420
1440
|
type: "thinking_start",
|
|
1421
1441
|
contentIndex: output.content.length - 1,
|
|
@@ -1428,6 +1448,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1428
1448
|
index: event.index,
|
|
1429
1449
|
};
|
|
1430
1450
|
output.content.push(block);
|
|
1451
|
+
trackBlockByAnthropicIndex(event.index, block);
|
|
1431
1452
|
} else if (event.content_block.type === "tool_use") {
|
|
1432
1453
|
streamedReplayUnsafeContent = true;
|
|
1433
1454
|
const block: Block = {
|
|
@@ -1441,6 +1462,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1441
1462
|
index: event.index,
|
|
1442
1463
|
};
|
|
1443
1464
|
output.content.push(block);
|
|
1465
|
+
trackBlockByAnthropicIndex(event.index, block);
|
|
1444
1466
|
stream.push({
|
|
1445
1467
|
type: "toolcall_start",
|
|
1446
1468
|
contentIndex: output.content.length - 1,
|
|
@@ -1449,8 +1471,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1449
1471
|
}
|
|
1450
1472
|
} else if (event.type === "content_block_delta") {
|
|
1451
1473
|
if (event.delta.type === "text_delta") {
|
|
1452
|
-
const
|
|
1453
|
-
const block = blocks[index];
|
|
1474
|
+
const { block, contentIndex: index } = getBlockByAnthropicIndex(event.index);
|
|
1454
1475
|
if (block && block.type === "text") {
|
|
1455
1476
|
block.text += event.delta.text;
|
|
1456
1477
|
stream.push({
|
|
@@ -1461,8 +1482,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1461
1482
|
});
|
|
1462
1483
|
}
|
|
1463
1484
|
} else if (event.delta.type === "thinking_delta") {
|
|
1464
|
-
const
|
|
1465
|
-
const block = blocks[index];
|
|
1485
|
+
const { block, contentIndex: index } = getBlockByAnthropicIndex(event.index);
|
|
1466
1486
|
if (block && block.type === "thinking") {
|
|
1467
1487
|
block.thinking += event.delta.thinking;
|
|
1468
1488
|
stream.push({
|
|
@@ -1473,8 +1493,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1473
1493
|
});
|
|
1474
1494
|
}
|
|
1475
1495
|
} else if (event.delta.type === "input_json_delta") {
|
|
1476
|
-
const
|
|
1477
|
-
const block = blocks[index];
|
|
1496
|
+
const { block, contentIndex: index } = getBlockByAnthropicIndex(event.index);
|
|
1478
1497
|
if (block && block.type === "toolCall") {
|
|
1479
1498
|
block.partialJson += event.delta.partial_json;
|
|
1480
1499
|
block.arguments = parseStreamingJson(block.partialJson);
|
|
@@ -1486,17 +1505,16 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1486
1505
|
});
|
|
1487
1506
|
}
|
|
1488
1507
|
} else if (event.delta.type === "signature_delta") {
|
|
1489
|
-
const
|
|
1490
|
-
const block = blocks[index];
|
|
1508
|
+
const { block } = getBlockByAnthropicIndex(event.index);
|
|
1491
1509
|
if (block && block.type === "thinking") {
|
|
1492
1510
|
block.thinkingSignature = block.thinkingSignature || "";
|
|
1493
1511
|
block.thinkingSignature += event.delta.signature;
|
|
1494
1512
|
}
|
|
1495
1513
|
}
|
|
1496
1514
|
} else if (event.type === "content_block_stop") {
|
|
1497
|
-
const
|
|
1498
|
-
const block = blocks[index];
|
|
1515
|
+
const { block, contentIndex: index } = getBlockByAnthropicIndex(event.index);
|
|
1499
1516
|
if (block) {
|
|
1517
|
+
blocksByAnthropicIndex.delete(event.index);
|
|
1500
1518
|
delete (block as { index?: number }).index;
|
|
1501
1519
|
if (block.type === "text") {
|
|
1502
1520
|
stream.push({
|
|
@@ -2245,7 +2263,11 @@ function buildParams(
|
|
|
2245
2263
|
params.tools = convertTools(
|
|
2246
2264
|
context.tools,
|
|
2247
2265
|
isOAuthToken,
|
|
2248
|
-
|
|
2266
|
+
// The Claude Code OAuth surface mishandles `strict: true` tools:
|
|
2267
|
+
// streamed tool_use blocks arrive with empty/undefined arguments and
|
|
2268
|
+
// occasionally corrupted names (works with PI_NO_STRICT=1). Never
|
|
2269
|
+
// request strict tool use on OAuth requests.
|
|
2270
|
+
disableStrictTools || isOAuthToken || model.provider === "github-copilot",
|
|
2249
2271
|
getAnthropicCompat(model).supportsEagerToolInputStreaming,
|
|
2250
2272
|
);
|
|
2251
2273
|
}
|