@deepstrike/sdk 0.2.60 → 0.2.61
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/os/public.d.ts +1 -1
- package/dist/os/public.js +1 -1
- package/dist/providers/anthropic-adapter.d.ts +13 -1
- package/dist/providers/anthropic-adapter.js +106 -20
- package/dist/providers/anthropic.d.ts +1 -0
- package/dist/providers/anthropic.js +10 -3
- package/dist/providers/base.d.ts +2 -2
- package/dist/providers/base.js +7 -5
- package/dist/providers/catalog.js +5 -20
- package/dist/providers/factories.js +17 -7
- package/dist/providers/gemini-adapter.js +6 -14
- package/dist/providers/model-registry.d.ts +22 -1
- package/dist/providers/model-registry.js +72 -8
- package/dist/providers/ollama-adapter.js +2 -2
- package/dist/providers/openai-chat.js +4 -6
- package/dist/providers/openai-responses-adapter.js +12 -10
- package/dist/providers/openai-responses.d.ts +6 -1
- package/dist/providers/openai-responses.js +33 -0
- package/dist/providers/protocol-adapter.d.ts +6 -1
- package/dist/providers/protocol-adapter.js +6 -2
- package/dist/providers/provider-error.js +5 -1
- package/dist/providers/request-plan.d.ts +4 -1
- package/dist/providers/request-plan.js +25 -1
- package/dist/providers/usage-normalizer.js +58 -15
- package/dist/runtime/provider-replay.d.ts +4 -0
- package/dist/runtime/provider-replay.js +15 -1
- package/dist/runtime/runner.d.ts +5 -5
- package/dist/runtime/runner.js +33 -0
- package/dist/types.d.ts +8 -5
- package/package.json +3 -3
package/dist/os/public.d.ts
CHANGED
|
@@ -15,5 +15,5 @@ export { ReplayProvider } from "../runtime/replay-provider.js";
|
|
|
15
15
|
export type { ReplayProviderOpts } from "../runtime/replay-provider.js";
|
|
16
16
|
export { extractRecordedMessages } from "../runtime/replay-fixture.js";
|
|
17
17
|
export { ProviderReplayValidationError, DEGRADED_REASONING_PLACEHOLDER } from "../providers/replay-validator.js";
|
|
18
|
-
export { assessProviderReplayability, peekProviderReplay, seedProviderReplayFromEvents, isReplayCompatibleWithProvider, } from "../runtime/provider-replay.js";
|
|
18
|
+
export { ProviderReplayProtocolMismatchError, assessProviderReplayability, peekProviderReplay, seedProviderReplayFromEvents, isReplayCompatibleWithProvider, } from "../runtime/provider-replay.js";
|
|
19
19
|
export type { ReplayabilityAssessment } from "../types.js";
|
package/dist/os/public.js
CHANGED
|
@@ -12,4 +12,4 @@ export { PermissionManager, PermissionMode } from "../safety/permissions.js";
|
|
|
12
12
|
export { ReplayProvider } from "../runtime/replay-provider.js";
|
|
13
13
|
export { extractRecordedMessages } from "../runtime/replay-fixture.js";
|
|
14
14
|
export { ProviderReplayValidationError, DEGRADED_REASONING_PLACEHOLDER } from "../providers/replay-validator.js";
|
|
15
|
-
export { assessProviderReplayability, peekProviderReplay, seedProviderReplayFromEvents, isReplayCompatibleWithProvider, } from "../runtime/provider-replay.js";
|
|
15
|
+
export { ProviderReplayProtocolMismatchError, assessProviderReplayability, peekProviderReplay, seedProviderReplayFromEvents, isReplayCompatibleWithProvider, } from "../runtime/provider-replay.js";
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import type { Message, ProviderReplay, ProviderUsage, ToolCall } from "../types.js";
|
|
2
2
|
import type { CanonicalAdapterInput } from "./content-normalization.js";
|
|
3
3
|
import { type AdapterDecodeInput, type AdapterOutput, type AdapterStreamInput, type CanonicalStopReason, type ProtocolAdapter } from "./protocol-adapter.js";
|
|
4
|
+
export declare const ANTHROPIC_TEXTUAL_TOOL_CALL_START_MARKER = "<\uFF5C\uFF5CDSML\uFF5C\uFF5Ctool_calls>";
|
|
5
|
+
type TextualToolCallPolicy = "off" | "reject";
|
|
6
|
+
interface TextualToolCallGuardState {
|
|
7
|
+
policy: TextualToolCallPolicy;
|
|
8
|
+
mode: "passthrough" | "candidate";
|
|
9
|
+
tail: string;
|
|
10
|
+
capturedLength: number;
|
|
11
|
+
sawNativeToolCall: boolean;
|
|
12
|
+
}
|
|
4
13
|
export interface AnthropicRequestPlan {
|
|
5
14
|
transport: "stable" | "beta";
|
|
6
15
|
params: Record<string, unknown>;
|
|
@@ -25,17 +34,19 @@ export interface AnthropicStreamState {
|
|
|
25
34
|
}>;
|
|
26
35
|
readonly nativeBlocks: Record<number, Record<string, unknown>>;
|
|
27
36
|
readonly finalToolCalls: ToolCall[];
|
|
37
|
+
readonly textualToolCallGuard: TextualToolCallGuardState;
|
|
28
38
|
finalText: string;
|
|
29
39
|
uncachedInput: number;
|
|
30
40
|
cacheReadTokens: number;
|
|
31
41
|
cacheCreationTokens: number;
|
|
42
|
+
cacheTelemetryMeasured: boolean;
|
|
32
43
|
outputTokens: number;
|
|
33
44
|
}
|
|
34
45
|
export declare class AnthropicMessagesAdapter implements ProtocolAdapter<AnthropicRequestPlan, Record<string, any>, AnthropicStreamChunk, AnthropicStreamState, undefined> {
|
|
35
46
|
readonly protocol: "anthropic-messages";
|
|
36
47
|
readonly protocolCapabilities: import("./protocol-capabilities.js").ProtocolRuntimeCapabilities;
|
|
37
48
|
buildRequest(input: CanonicalAdapterInput): AnthropicRequestPlan;
|
|
38
|
-
decodeComplete(raw: Record<string, any>,
|
|
49
|
+
decodeComplete(raw: Record<string, any>, decodeInput: AdapterDecodeInput): {
|
|
39
50
|
message: Message;
|
|
40
51
|
replay?: ProviderReplay;
|
|
41
52
|
};
|
|
@@ -45,3 +56,4 @@ export declare class AnthropicMessagesAdapter implements ProtocolAdapter<Anthrop
|
|
|
45
56
|
normalizeUsage(raw: unknown): ProviderUsage | undefined;
|
|
46
57
|
normalizeStopReason(raw: string | undefined): CanonicalStopReason | undefined;
|
|
47
58
|
}
|
|
59
|
+
export {};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { normalizeToolCall } from "./base.js";
|
|
2
2
|
import { ProtocolResponseError, } from "./protocol-adapter.js";
|
|
3
3
|
import { ANTHROPIC_PROTOCOL_CAPABILITIES } from "./protocol-capabilities.js";
|
|
4
|
+
import { endpointProfiles } from "./endpoints.js";
|
|
5
|
+
export const ANTHROPIC_TEXTUAL_TOOL_CALL_START_MARKER = "<||DSML||tool_calls>";
|
|
6
|
+
const TEXTUAL_TOOL_CALL_CAPTURE_LIMIT = 64 * 1024;
|
|
4
7
|
const CACHE_BREAKPOINT_STRATEGIES = new Set([
|
|
5
8
|
"default", "tools-only", "system-only", "frozen-prefix", "none",
|
|
6
9
|
]);
|
|
@@ -13,10 +16,63 @@ function cacheStrategy(extensions) {
|
|
|
13
16
|
function extensionsForWire(extensions) {
|
|
14
17
|
const blocked = new Set([
|
|
15
18
|
"model", "messages", "system", "tools", "max_tokens", "stream",
|
|
16
|
-
"__deepstrikeThinkingEnabled", "degradeMissingReasoningReplay",
|
|
19
|
+
"__deepstrikeThinkingEnabled", "degradeMissingReasoningReplay", "textualToolCallPolicy",
|
|
17
20
|
]);
|
|
18
21
|
return Object.fromEntries(Object.entries(extensions).filter(([key]) => !blocked.has(key)));
|
|
19
22
|
}
|
|
23
|
+
function textualToolCallPolicy(input) {
|
|
24
|
+
if (input.tools.length === 0)
|
|
25
|
+
return "off";
|
|
26
|
+
const explicit = input.extensions.textualToolCallPolicy;
|
|
27
|
+
if (explicit === "off" || explicit === "reject")
|
|
28
|
+
return explicit;
|
|
29
|
+
const official = endpointProfiles["anthropic.messages"];
|
|
30
|
+
const endpoint = input.resolved.endpoint;
|
|
31
|
+
const isOfficial = input.resolved.identity.endpointId === official.id
|
|
32
|
+
&& (!endpoint || endpoint.baseURL === official.baseURL);
|
|
33
|
+
return isOfficial ? "off" : "reject";
|
|
34
|
+
}
|
|
35
|
+
function textualToolCallError() {
|
|
36
|
+
return new ProtocolResponseError("anthropic-messages", "Provider emitted a tool call as text instead of a native tool block", { providerCode: "textual_tool_call", retryable: true });
|
|
37
|
+
}
|
|
38
|
+
function hasTextualToolCall(text, input) {
|
|
39
|
+
return textualToolCallPolicy(input) === "reject"
|
|
40
|
+
&& text.includes(ANTHROPIC_TEXTUAL_TOOL_CALL_START_MARKER);
|
|
41
|
+
}
|
|
42
|
+
function markerTailLength(text) {
|
|
43
|
+
const max = Math.min(text.length, ANTHROPIC_TEXTUAL_TOOL_CALL_START_MARKER.length - 1);
|
|
44
|
+
for (let length = max; length > 0; length -= 1) {
|
|
45
|
+
if (text.endsWith(ANTHROPIC_TEXTUAL_TOOL_CALL_START_MARKER.slice(0, length)))
|
|
46
|
+
return length;
|
|
47
|
+
}
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
function utf8Length(text) {
|
|
51
|
+
return Buffer.byteLength(text, "utf8");
|
|
52
|
+
}
|
|
53
|
+
function guardTextDelta(text, guard) {
|
|
54
|
+
if (guard.policy === "off")
|
|
55
|
+
return text;
|
|
56
|
+
if (guard.mode === "candidate") {
|
|
57
|
+
guard.capturedLength += utf8Length(text);
|
|
58
|
+
if (guard.capturedLength > TEXTUAL_TOOL_CALL_CAPTURE_LIMIT)
|
|
59
|
+
throw textualToolCallError();
|
|
60
|
+
return "";
|
|
61
|
+
}
|
|
62
|
+
const combined = guard.tail + text;
|
|
63
|
+
const markerIndex = combined.indexOf(ANTHROPIC_TEXTUAL_TOOL_CALL_START_MARKER);
|
|
64
|
+
if (markerIndex >= 0) {
|
|
65
|
+
guard.mode = "candidate";
|
|
66
|
+
guard.tail = "";
|
|
67
|
+
guard.capturedLength = utf8Length(combined.slice(markerIndex));
|
|
68
|
+
if (guard.capturedLength > TEXTUAL_TOOL_CALL_CAPTURE_LIMIT)
|
|
69
|
+
throw textualToolCallError();
|
|
70
|
+
return combined.slice(0, markerIndex);
|
|
71
|
+
}
|
|
72
|
+
const tailLength = markerTailLength(combined);
|
|
73
|
+
guard.tail = tailLength > 0 ? combined.slice(-tailLength) : "";
|
|
74
|
+
return tailLength > 0 ? combined.slice(0, -tailLength) : combined;
|
|
75
|
+
}
|
|
20
76
|
function systemBlocks(context, strategy) {
|
|
21
77
|
if (!context.systemStable && !context.systemKnowledge) {
|
|
22
78
|
return context.systemText || undefined;
|
|
@@ -206,22 +262,12 @@ function assertCacheBudget(params) {
|
|
|
206
262
|
throw new Error(`Anthropic cache_control budget exceeded: ${count} > 4`);
|
|
207
263
|
}
|
|
208
264
|
}
|
|
209
|
-
function estimateCacheRead(cacheRead, slots) {
|
|
210
|
-
if (cacheRead <= 0)
|
|
211
|
-
return undefined;
|
|
212
|
-
const keys = ["system", "tools", "messages"].filter(key => slots[key]);
|
|
213
|
-
if (!keys.length)
|
|
214
|
-
return undefined;
|
|
215
|
-
const share = Math.floor(cacheRead / keys.length);
|
|
216
|
-
const remainder = cacheRead - share * keys.length;
|
|
217
|
-
return Object.fromEntries(keys.map((key, index) => [key, share + (index === 0 ? remainder : 0)]));
|
|
218
|
-
}
|
|
219
265
|
function numeric(raw, field) {
|
|
220
266
|
const value = raw[field];
|
|
221
267
|
if (value === undefined || value === null)
|
|
222
268
|
return undefined;
|
|
223
|
-
if (typeof value !== "number" || !Number.
|
|
224
|
-
throw new ProtocolResponseError("anthropic-messages", `usage.${field}
|
|
269
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
270
|
+
throw new ProtocolResponseError("anthropic-messages", `usage.${field} must be a non-negative safe integer`);
|
|
225
271
|
}
|
|
226
272
|
return value;
|
|
227
273
|
}
|
|
@@ -265,7 +311,7 @@ export class AnthropicMessagesAdapter {
|
|
|
265
311
|
cacheSlots: countSlots(system, tools, messages),
|
|
266
312
|
};
|
|
267
313
|
}
|
|
268
|
-
decodeComplete(raw,
|
|
314
|
+
decodeComplete(raw, decodeInput) {
|
|
269
315
|
let content = "";
|
|
270
316
|
const toolCalls = [];
|
|
271
317
|
for (const block of raw.content ?? []) {
|
|
@@ -277,6 +323,8 @@ export class AnthropicMessagesAdapter {
|
|
|
277
323
|
toolCalls.push(call);
|
|
278
324
|
}
|
|
279
325
|
}
|
|
326
|
+
if (hasTextualToolCall(content, decodeInput.input))
|
|
327
|
+
throw textualToolCallError();
|
|
280
328
|
const usage = this.normalizeUsage(raw.usage);
|
|
281
329
|
const blocks = raw.content;
|
|
282
330
|
return {
|
|
@@ -297,10 +345,18 @@ export class AnthropicMessagesAdapter {
|
|
|
297
345
|
toolBlocks: {},
|
|
298
346
|
nativeBlocks: {},
|
|
299
347
|
finalToolCalls: [],
|
|
348
|
+
textualToolCallGuard: {
|
|
349
|
+
policy: textualToolCallPolicy(input.input),
|
|
350
|
+
mode: "passthrough",
|
|
351
|
+
tail: "",
|
|
352
|
+
capturedLength: 0,
|
|
353
|
+
sawNativeToolCall: false,
|
|
354
|
+
},
|
|
300
355
|
finalText: "",
|
|
301
356
|
uncachedInput: 0,
|
|
302
357
|
cacheReadTokens: 0,
|
|
303
358
|
cacheCreationTokens: 0,
|
|
359
|
+
cacheTelemetryMeasured: false,
|
|
304
360
|
outputTokens: 0,
|
|
305
361
|
};
|
|
306
362
|
}
|
|
@@ -309,20 +365,26 @@ export class AnthropicMessagesAdapter {
|
|
|
309
365
|
if (chunk.type === "message_start" || chunk.type === "message_delta") {
|
|
310
366
|
const raw = chunk.usage ?? chunk.message?.usage;
|
|
311
367
|
if (raw) {
|
|
368
|
+
if (Object.prototype.hasOwnProperty.call(raw, "cache_read_input_tokens")
|
|
369
|
+
|| Object.prototype.hasOwnProperty.call(raw, "cache_creation_input_tokens")) {
|
|
370
|
+
state.cacheTelemetryMeasured = true;
|
|
371
|
+
}
|
|
312
372
|
state.uncachedInput = Math.max(state.uncachedInput, numeric(raw, "input_tokens") ?? 0);
|
|
313
373
|
state.cacheReadTokens = Math.max(state.cacheReadTokens, numeric(raw, "cache_read_input_tokens") ?? 0);
|
|
314
374
|
state.cacheCreationTokens = Math.max(state.cacheCreationTokens, numeric(raw, "cache_creation_input_tokens") ?? 0);
|
|
315
375
|
state.outputTokens = Math.max(state.outputTokens, numeric(raw, "output_tokens") ?? 0);
|
|
316
376
|
const inputTokens = state.uncachedInput + state.cacheReadTokens + state.cacheCreationTokens;
|
|
377
|
+
const cacheTelemetryStatus = state.cacheTelemetryMeasured ? "measured" : "unavailable";
|
|
317
378
|
const providerUsage = {
|
|
318
379
|
inputTokens,
|
|
319
380
|
outputTokens: state.outputTokens,
|
|
320
381
|
...(state.cacheReadTokens ? { cacheReadInputTokens: state.cacheReadTokens } : {}),
|
|
321
382
|
...(state.cacheCreationTokens ? { cacheCreationInputTokens: state.cacheCreationTokens } : {}),
|
|
383
|
+
cacheTelemetryStatus,
|
|
384
|
+
...(state.cacheTelemetryMeasured ? { cacheTelemetrySource: "anthropic_usage" } : {}),
|
|
322
385
|
};
|
|
323
386
|
const rawStopReason = chunk.delta?.stop_reason;
|
|
324
387
|
const stopReason = this.normalizeStopReason(rawStopReason);
|
|
325
|
-
const bySlot = estimateCacheRead(state.cacheReadTokens, state.cacheSlots);
|
|
326
388
|
events.push({
|
|
327
389
|
type: "usage",
|
|
328
390
|
totalTokens: inputTokens + state.outputTokens,
|
|
@@ -330,7 +392,8 @@ export class AnthropicMessagesAdapter {
|
|
|
330
392
|
outputTokens: state.outputTokens,
|
|
331
393
|
cacheReadInputTokens: state.cacheReadTokens,
|
|
332
394
|
cacheCreationInputTokens: state.cacheCreationTokens,
|
|
333
|
-
|
|
395
|
+
cacheTelemetryStatus,
|
|
396
|
+
...(state.cacheTelemetryMeasured ? { cacheTelemetrySource: "anthropic_usage" } : {}),
|
|
334
397
|
...(stopReason ? { stopReason } : {}),
|
|
335
398
|
...(rawStopReason ? { rawStopReason } : {}),
|
|
336
399
|
providerUsage,
|
|
@@ -339,7 +402,15 @@ export class AnthropicMessagesAdapter {
|
|
|
339
402
|
}
|
|
340
403
|
else if (chunk.type === "content_block_start") {
|
|
341
404
|
state.nativeBlocks[chunk.index] = { ...chunk.content_block };
|
|
342
|
-
if (chunk.content_block.type === "
|
|
405
|
+
if (chunk.content_block.type === "text") {
|
|
406
|
+
const initialText = String(chunk.content_block.text ?? "");
|
|
407
|
+
const visibleText = guardTextDelta(initialText, state.textualToolCallGuard);
|
|
408
|
+
state.finalText += visibleText;
|
|
409
|
+
if (visibleText)
|
|
410
|
+
events.push({ type: "text_delta", delta: visibleText });
|
|
411
|
+
}
|
|
412
|
+
else if (chunk.content_block.type === "tool_use") {
|
|
413
|
+
state.textualToolCallGuard.sawNativeToolCall = true;
|
|
343
414
|
state.toolBlocks[chunk.index] = {
|
|
344
415
|
id: chunk.content_block.id,
|
|
345
416
|
name: chunk.content_block.name,
|
|
@@ -350,12 +421,14 @@ export class AnthropicMessagesAdapter {
|
|
|
350
421
|
else if (chunk.type === "content_block_delta") {
|
|
351
422
|
const delta = chunk.delta;
|
|
352
423
|
if (delta.type === "text_delta") {
|
|
353
|
-
|
|
424
|
+
const visibleText = guardTextDelta(delta.text, state.textualToolCallGuard);
|
|
425
|
+
state.finalText += visibleText;
|
|
354
426
|
state.nativeBlocks[chunk.index] = {
|
|
355
427
|
...state.nativeBlocks[chunk.index],
|
|
356
428
|
text: String(state.nativeBlocks[chunk.index]?.text ?? "") + delta.text,
|
|
357
429
|
};
|
|
358
|
-
|
|
430
|
+
if (visibleText)
|
|
431
|
+
events.push({ type: "text_delta", delta: visibleText });
|
|
359
432
|
}
|
|
360
433
|
else if (delta.type === "thinking_delta") {
|
|
361
434
|
state.nativeBlocks[chunk.index] = {
|
|
@@ -400,12 +473,21 @@ export class AnthropicMessagesAdapter {
|
|
|
400
473
|
return { events };
|
|
401
474
|
}
|
|
402
475
|
finishStream(state) {
|
|
476
|
+
if (state.textualToolCallGuard.mode === "candidate")
|
|
477
|
+
throw textualToolCallError();
|
|
478
|
+
const events = [];
|
|
479
|
+
if (state.textualToolCallGuard.tail) {
|
|
480
|
+
const tail = state.textualToolCallGuard.tail;
|
|
481
|
+
state.textualToolCallGuard.tail = "";
|
|
482
|
+
state.finalText += tail;
|
|
483
|
+
events.push({ type: "text_delta", delta: tail });
|
|
484
|
+
}
|
|
403
485
|
const blocks = Object.keys(state.nativeBlocks)
|
|
404
486
|
.map(Number)
|
|
405
487
|
.sort((left, right) => left - right)
|
|
406
488
|
.map(index => state.nativeBlocks[index]);
|
|
407
489
|
return {
|
|
408
|
-
events
|
|
490
|
+
events,
|
|
409
491
|
...(blocks.length ? { replay: { protocol: "anthropic-messages", native_blocks: blocks } } : {}),
|
|
410
492
|
};
|
|
411
493
|
}
|
|
@@ -428,6 +510,10 @@ export class AnthropicMessagesAdapter {
|
|
|
428
510
|
outputTokens: output ?? 0,
|
|
429
511
|
...(cacheRead ? { cacheReadInputTokens: cacheRead } : {}),
|
|
430
512
|
...(cacheCreation ? { cacheCreationInputTokens: cacheCreation } : {}),
|
|
513
|
+
...(Object.prototype.hasOwnProperty.call(record, "cache_read_input_tokens")
|
|
514
|
+
|| Object.prototype.hasOwnProperty.call(record, "cache_creation_input_tokens")
|
|
515
|
+
? { cacheTelemetryStatus: "measured", cacheTelemetrySource: "anthropic_usage" }
|
|
516
|
+
: { cacheTelemetryStatus: "unavailable" }),
|
|
431
517
|
};
|
|
432
518
|
}
|
|
433
519
|
normalizeStopReason(raw) {
|
|
@@ -22,6 +22,7 @@ export declare class AnthropicProvider implements LLMProvider {
|
|
|
22
22
|
private readonly nativeAssistantBlocks;
|
|
23
23
|
private readonly resolvedRuntimePolicy;
|
|
24
24
|
private readonly directNativeTokenCounting;
|
|
25
|
+
private readonly defaultTextualToolCallPolicy;
|
|
25
26
|
private resolvedRuntime?;
|
|
26
27
|
constructor(config: AnthropicProviderConfig);
|
|
27
28
|
runtimePolicy(): RuntimePolicy;
|
|
@@ -16,6 +16,7 @@ export class AnthropicProvider {
|
|
|
16
16
|
nativeAssistantBlocks = new Map();
|
|
17
17
|
resolvedRuntimePolicy;
|
|
18
18
|
directNativeTokenCounting;
|
|
19
|
+
defaultTextualToolCallPolicy;
|
|
19
20
|
resolvedRuntime;
|
|
20
21
|
constructor(config) {
|
|
21
22
|
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
@@ -40,8 +41,11 @@ export class AnthropicProvider {
|
|
|
40
41
|
this.maxRetries = c.retry?.maxRetries ?? 3;
|
|
41
42
|
this.baseDelay = c.retry?.baseDelay ?? 1000;
|
|
42
43
|
this.resolvedRuntimePolicy = c.runtimePolicy ?? {};
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
const configuredBaseURL = c.baseURL?.replace(/\/+$/, "");
|
|
45
|
+
const officialBaseURL = endpointProfiles["anthropic.messages"].baseURL.replace(/\/+$/, "");
|
|
46
|
+
this.directNativeTokenCounting = configuredBaseURL === undefined
|
|
47
|
+
|| configuredBaseURL === officialBaseURL;
|
|
48
|
+
this.defaultTextualToolCallPolicy = this.directNativeTokenCounting ? "off" : "reject";
|
|
45
49
|
}
|
|
46
50
|
runtimePolicy() {
|
|
47
51
|
return this.resolvedRuntimePolicy;
|
|
@@ -107,7 +111,10 @@ export class AnthropicProvider {
|
|
|
107
111
|
context,
|
|
108
112
|
tools,
|
|
109
113
|
resolved,
|
|
110
|
-
extensions
|
|
114
|
+
extensions: {
|
|
115
|
+
textualToolCallPolicy: this.defaultTextualToolCallPolicy,
|
|
116
|
+
...extensions,
|
|
117
|
+
},
|
|
111
118
|
replayForMessage: message => this.peekProviderReplay(message),
|
|
112
119
|
});
|
|
113
120
|
}
|
package/dist/providers/base.d.ts
CHANGED
|
@@ -18,8 +18,8 @@ export declare const INTERNAL_EXTENSION_KEYS: readonly string[];
|
|
|
18
18
|
export declare function omitExtensionKeys(extensions: Record<string, unknown> | undefined, keys: readonly string[]): Record<string, unknown>;
|
|
19
19
|
/**
|
|
20
20
|
* Cached-prompt-token count from an OpenAI-compatible usage object. Covers the
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* Chat `prompt_tokens_details.cached_tokens`, Responses
|
|
22
|
+
* `input_tokens_details.cached_tokens`, and DeepSeek's `prompt_cache_hit_tokens`. These caches bill reads only,
|
|
23
23
|
* so there is no separate cache-creation count. The figure is a subset of
|
|
24
24
|
* `prompt_tokens` (the full prompt), surfaced for cost visibility — it must not
|
|
25
25
|
* be subtracted from the input count the kernel uses for context accounting.
|
package/dist/providers/base.js
CHANGED
|
@@ -44,8 +44,8 @@ export function omitExtensionKeys(extensions, keys) {
|
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
46
46
|
* Cached-prompt-token count from an OpenAI-compatible usage object. Covers the
|
|
47
|
-
*
|
|
48
|
-
*
|
|
47
|
+
* Chat `prompt_tokens_details.cached_tokens`, Responses
|
|
48
|
+
* `input_tokens_details.cached_tokens`, and DeepSeek's `prompt_cache_hit_tokens`. These caches bill reads only,
|
|
49
49
|
* so there is no separate cache-creation count. The figure is a subset of
|
|
50
50
|
* `prompt_tokens` (the full prompt), surfaced for cost visibility — it must not
|
|
51
51
|
* be subtracted from the input count the kernel uses for context accounting.
|
|
@@ -54,10 +54,12 @@ export function openAICachedPromptTokens(usage) {
|
|
|
54
54
|
if (!usage || typeof usage !== "object")
|
|
55
55
|
return 0;
|
|
56
56
|
const u = usage;
|
|
57
|
-
const
|
|
58
|
-
const
|
|
57
|
+
const promptDetails = u.prompt_tokens_details;
|
|
58
|
+
const inputDetails = u.input_tokens_details;
|
|
59
|
+
const standard = typeof promptDetails?.cached_tokens === "number" ? promptDetails.cached_tokens : 0;
|
|
60
|
+
const responses = typeof inputDetails?.cached_tokens === "number" ? inputDetails.cached_tokens : 0;
|
|
59
61
|
const deepseek = typeof u.prompt_cache_hit_tokens === "number" ? u.prompt_cache_hit_tokens : 0;
|
|
60
|
-
return Math.max(standard, deepseek);
|
|
62
|
+
return Math.max(standard, responses, deepseek);
|
|
61
63
|
}
|
|
62
64
|
/**
|
|
63
65
|
* Prompt-cache hit rate for one usage record: the fraction of the full prompt
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PROVIDER_REGISTRY, providerRegistryKey, supportsBearerCredential } from "./registry.js";
|
|
2
|
-
import { endpointCapabilitiesFor, generationProtocol, modelRegistry, normalizeModelId, resolveEffectiveModelCapabilities, } from "./model-registry.js";
|
|
2
|
+
import { defaultEndpointForProvider, endpointCapabilitiesFor, generationProtocol, modelRegistry, normalizeModelId, resolveEffectiveModelCapabilities, } from "./model-registry.js";
|
|
3
3
|
import { endpointProfiles } from "./endpoints.js";
|
|
4
4
|
import { resolveCredential, resolveCredentialSync, CredentialResolutionError, } from "./credentials.js";
|
|
5
5
|
export function createProvider(options) {
|
|
@@ -38,7 +38,9 @@ async function resolveRuntimeDraftAsync(options) {
|
|
|
38
38
|
}
|
|
39
39
|
function resolveRuntimeDraftWithRegistration(options, parsedProviderId, initialRegistration) {
|
|
40
40
|
const providerHint = options.provider ?? parsedProviderId;
|
|
41
|
-
const endpointId = (options.endpoint
|
|
41
|
+
const endpointId = (options.endpoint
|
|
42
|
+
?? initialRegistration?.defaultEndpointId
|
|
43
|
+
?? (providerHint ? defaultEndpointForProvider(providerHint) : undefined));
|
|
42
44
|
if (!endpointId) {
|
|
43
45
|
throw new Error(`Unknown model profile: ${options.model}. Pass provider or endpoint for custom model names.`);
|
|
44
46
|
}
|
|
@@ -85,7 +87,7 @@ function constructResolvedRuntime(draft, credential, options) {
|
|
|
85
87
|
effectiveCapabilities: resolveEffectiveModelCapabilities({
|
|
86
88
|
model: draft.registration.descriptor,
|
|
87
89
|
protocol,
|
|
88
|
-
endpointCapabilities: endpointCapabilitiesFor(draft.endpointId, options.baseURL === undefined || options.endpoint !== undefined),
|
|
90
|
+
endpointCapabilities: endpointCapabilitiesFor(draft.endpointId, options.baseURL === undefined || options.endpoint !== undefined, options.baseURL === undefined),
|
|
89
91
|
}),
|
|
90
92
|
...(draft.registration.recommendedRuntimePolicy ? { runtimePolicy: draft.registration.recommendedRuntimePolicy } : {}),
|
|
91
93
|
};
|
|
@@ -132,20 +134,3 @@ function providerPrefix(model) {
|
|
|
132
134
|
function providerIds() {
|
|
133
135
|
return Array.from(new Set(Object.values(endpointProfiles).map(endpoint => endpoint.providerId)));
|
|
134
136
|
}
|
|
135
|
-
function defaultEndpointForProvider(providerId) {
|
|
136
|
-
if (!providerId)
|
|
137
|
-
return undefined;
|
|
138
|
-
const defaults = {
|
|
139
|
-
anthropic: "anthropic.messages",
|
|
140
|
-
openai: "openai.chat",
|
|
141
|
-
minimax: "minimax.anthropic",
|
|
142
|
-
deepseek: "deepseek.anthropic",
|
|
143
|
-
kimi: "kimi.anthropic",
|
|
144
|
-
qwen: "qwen.anthropic",
|
|
145
|
-
gemini: "gemini.google",
|
|
146
|
-
glm: "glm.anthropic",
|
|
147
|
-
baai: "baai.self-hosted.embeddings",
|
|
148
|
-
ollama: "ollama.local",
|
|
149
|
-
};
|
|
150
|
-
return defaults[providerId];
|
|
151
|
-
}
|
|
@@ -1,31 +1,41 @@
|
|
|
1
1
|
import { PROVIDER_REGISTRY } from "./registry.js";
|
|
2
2
|
import { OllamaProvider } from "./ollama.js";
|
|
3
|
-
import { defaultModelForProvider, getRuntimePolicy, isKnownProviderId } from "./model-registry.js";
|
|
4
|
-
|
|
3
|
+
import { defaultEndpointForProvider, defaultModelForProvider, getRuntimePolicy, isKnownProviderId } from "./model-registry.js";
|
|
4
|
+
import { endpointProfiles } from "./endpoints.js";
|
|
5
|
+
function build(providerId, o) {
|
|
5
6
|
if (!isKnownProviderId(providerId))
|
|
6
7
|
throw new Error(`Unknown provider: ${providerId}`);
|
|
8
|
+
const defaultProtocol = endpointProfiles[defaultEndpointForProvider(providerId)].protocol;
|
|
9
|
+
const protocol = o.protocol === "openai"
|
|
10
|
+
? "openai-chat"
|
|
11
|
+
: o.protocol === "anthropic"
|
|
12
|
+
? "anthropic-messages"
|
|
13
|
+
: defaultProtocol;
|
|
14
|
+
if (protocol !== "openai-chat" && protocol !== "anthropic-messages") {
|
|
15
|
+
throw new Error(`Provider ${providerId} does not use an OpenAI- or Anthropic-compatible default`);
|
|
16
|
+
}
|
|
7
17
|
const model = o.model ?? defaultModelForProvider(providerId);
|
|
8
18
|
return PROVIDER_REGISTRY[`${providerId}:${protocol}`](o.apiKey, model, o.retry, o.baseURL, getRuntimePolicy(providerId, model));
|
|
9
19
|
}
|
|
10
20
|
/** DeepSeek. Defaults to the OpenAI-compatible wire (richer reasoning-replay handling). */
|
|
11
21
|
export function deepseek(o) {
|
|
12
|
-
return build("deepseek", o
|
|
22
|
+
return build("deepseek", o);
|
|
13
23
|
}
|
|
14
24
|
/** Moonshot Kimi. Defaults to the OpenAI-compatible wire. */
|
|
15
25
|
export function kimi(o) {
|
|
16
|
-
return build("kimi", o
|
|
26
|
+
return build("kimi", o);
|
|
17
27
|
}
|
|
18
28
|
/** Alibaba Qwen / DashScope. Defaults to the OpenAI-compatible (DashScope) wire. */
|
|
19
29
|
export function qwen(o) {
|
|
20
|
-
return build("qwen", o
|
|
30
|
+
return build("qwen", o);
|
|
21
31
|
}
|
|
22
32
|
/** Zhipu GLM. Defaults to the OpenAI-compatible wire. */
|
|
23
33
|
export function glm(o) {
|
|
24
|
-
return build("glm", o
|
|
34
|
+
return build("glm", o);
|
|
25
35
|
}
|
|
26
36
|
/** MiniMax. Defaults to the Anthropic-compatible wire (the primary M2.x path). */
|
|
27
37
|
export function minimax(o) {
|
|
28
|
-
return build("minimax", o
|
|
38
|
+
return build("minimax", o);
|
|
29
39
|
}
|
|
30
40
|
/** Google Gemini (single wire). */
|
|
31
41
|
export function gemini(o) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { projectToolOutputToText } from "./content-normalization.js";
|
|
2
2
|
import { normalizeToolCall } from "./base.js";
|
|
3
|
+
import { normalizeGeminiUsage } from "./usage-normalizer.js";
|
|
3
4
|
import { ProtocolResponseError, GEMINI_PROTOCOL_CAPABILITIES, } from "./protocol-adapter.js";
|
|
4
5
|
// Google Generate Content streams response chunks, while @google/generative-ai 0.24.1 exposes
|
|
5
6
|
// a separate promise for the aggregated response. Candidate finishReason and aggregate usage are
|
|
@@ -145,8 +146,8 @@ function numberField(raw, field) {
|
|
|
145
146
|
const value = raw[field];
|
|
146
147
|
if (value === undefined)
|
|
147
148
|
return undefined;
|
|
148
|
-
if (typeof value !== "number" || !Number.
|
|
149
|
-
throw new ProtocolResponseError("gemini", `usage.${field} must be a non-negative
|
|
149
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
150
|
+
throw new ProtocolResponseError("gemini", `usage.${field} must be a non-negative safe integer`);
|
|
150
151
|
}
|
|
151
152
|
return value;
|
|
152
153
|
}
|
|
@@ -222,6 +223,8 @@ export class GeminiAdapter {
|
|
|
222
223
|
...(usage.cacheReadInputTokens
|
|
223
224
|
? { cacheReadInputTokens: usage.cacheReadInputTokens }
|
|
224
225
|
: {}),
|
|
226
|
+
...(usage.cacheTelemetryStatus ? { cacheTelemetryStatus: usage.cacheTelemetryStatus } : {}),
|
|
227
|
+
...(usage.cacheTelemetrySource ? { cacheTelemetrySource: usage.cacheTelemetrySource } : {}),
|
|
225
228
|
providerUsage: usage,
|
|
226
229
|
...(stopReason ? { stopReason } : {}),
|
|
227
230
|
...(rawStopReason ? { rawStopReason } : {}),
|
|
@@ -236,19 +239,8 @@ export class GeminiAdapter {
|
|
|
236
239
|
throw new ProtocolResponseError("gemini", "usage must be an object");
|
|
237
240
|
}
|
|
238
241
|
const usage = raw;
|
|
239
|
-
const inputTokens = numberField(usage, "promptTokenCount");
|
|
240
|
-
const outputTokens = numberField(usage, "candidatesTokenCount");
|
|
241
242
|
numberField(usage, "totalTokenCount");
|
|
242
|
-
|
|
243
|
-
if (inputTokens === undefined
|
|
244
|
-
&& outputTokens === undefined
|
|
245
|
-
&& cacheReadInputTokens === undefined)
|
|
246
|
-
return undefined;
|
|
247
|
-
return {
|
|
248
|
-
inputTokens: inputTokens ?? 0,
|
|
249
|
-
outputTokens: outputTokens ?? 0,
|
|
250
|
-
...(cacheReadInputTokens ? { cacheReadInputTokens } : {}),
|
|
251
|
-
};
|
|
243
|
+
return normalizeGeminiUsage(usage);
|
|
252
244
|
}
|
|
253
245
|
normalizeStopReason(raw) {
|
|
254
246
|
if (raw === undefined)
|
|
@@ -29,8 +29,26 @@ export interface DynamicModelDescriptorResolver {
|
|
|
29
29
|
}
|
|
30
30
|
export interface EndpointRuntimeCapabilities {
|
|
31
31
|
nativeTokenCounting?: boolean;
|
|
32
|
+
promptCaching?: boolean;
|
|
32
33
|
protocolOverrides?: ProtocolRuntimeCapabilityOverrides;
|
|
33
34
|
}
|
|
35
|
+
export interface CacheCapabilityEvidence {
|
|
36
|
+
endpointId: EndpointProfileId;
|
|
37
|
+
source: string;
|
|
38
|
+
verifiedAt: "2026-08-26";
|
|
39
|
+
classification: "documentation" | "live_probe";
|
|
40
|
+
usageFields: readonly string[];
|
|
41
|
+
}
|
|
42
|
+
export interface TokenMeasurementEvidence {
|
|
43
|
+
endpointId: EndpointProfileId;
|
|
44
|
+
source: string;
|
|
45
|
+
verifiedAt: "2026-08-26";
|
|
46
|
+
providerApiState: "supported" | "unsupported" | "unknown";
|
|
47
|
+
adapterState: "available" | "unavailable";
|
|
48
|
+
method: "provider_preflight" | "official_local_tokenizer" | "postflight" | "heuristic";
|
|
49
|
+
coverage: readonly string[];
|
|
50
|
+
sdk: string;
|
|
51
|
+
}
|
|
34
52
|
export type CapabilityEvidenceLayer = "model" | "protocol" | "endpoint";
|
|
35
53
|
export interface EffectiveCapability<T = boolean> {
|
|
36
54
|
state: CapabilityState;
|
|
@@ -74,7 +92,10 @@ export interface RegistryRuleEvidence {
|
|
|
74
92
|
verifiedAt: "2026-08-12";
|
|
75
93
|
}
|
|
76
94
|
export declare const registryEvidence: readonly RegistryRuleEvidence[];
|
|
95
|
+
export declare const cacheCapabilityEvidence: readonly CacheCapabilityEvidence[];
|
|
96
|
+
export declare const tokenMeasurementEvidence: readonly TokenMeasurementEvidence[];
|
|
77
97
|
export declare function defaultModelForProvider(providerId: ProviderId): string;
|
|
98
|
+
export declare function defaultEndpointForProvider(providerId: ProviderId): EndpointProfileId;
|
|
78
99
|
export declare class ModelRegistry {
|
|
79
100
|
resolve(modelId: string, providerId?: ProviderId): ModelRegistration | undefined;
|
|
80
101
|
}
|
|
@@ -94,5 +115,5 @@ export declare function resolveEffectiveModelCapabilities(input: {
|
|
|
94
115
|
endpointCapabilities?: EndpointRuntimeCapabilities;
|
|
95
116
|
}): EffectiveModelCapabilities;
|
|
96
117
|
export declare function generationProtocol(protocol: EndpointProtocol): GenerationProtocol | undefined;
|
|
97
|
-
export declare function endpointCapabilitiesFor(endpointId: EndpointProfileId, preserveEndpointIdentity: boolean): EndpointRuntimeCapabilities | undefined;
|
|
118
|
+
export declare function endpointCapabilitiesFor(endpointId: EndpointProfileId, preserveEndpointIdentity: boolean, preserveCacheEvidence?: boolean): EndpointRuntimeCapabilities | undefined;
|
|
98
119
|
export declare function isKnownProviderId(value: string): value is ProviderId;
|