@cjhyy/code-shell-core 0.9.2 → 0.9.4
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/cli/agent-server-stdio.js +26 -1
- package/dist/cli/agent-server-tcp.js +58 -7
- package/dist/engine/engine.d.ts +8 -0
- package/dist/engine/engine.js +16 -12
- package/dist/engine/model-facade.d.ts +3 -0
- package/dist/engine/model-facade.js +2 -0
- package/dist/engine/run-tooling.js +8 -8
- package/dist/engine/streaming-tool-queue.d.ts +4 -1
- package/dist/engine/streaming-tool-queue.js +17 -2
- package/dist/engine/turn-loop.d.ts +14 -5
- package/dist/engine/turn-loop.js +92 -37
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/llm/prompt-cache.d.ts +48 -0
- package/dist/llm/prompt-cache.js +100 -0
- package/dist/llm/providers/anthropic.d.ts +3 -0
- package/dist/llm/providers/anthropic.js +77 -53
- package/dist/llm/providers/openai.d.ts +7 -27
- package/dist/llm/providers/openai.js +120 -68
- package/dist/llm/types.d.ts +3 -0
- package/dist/onboarding.js +72 -49
- package/dist/panel-apps/manifest.d.ts +12 -12
- package/dist/profile/types.d.ts +26 -26
- package/dist/protocol/background-result-wakeup.d.ts +3 -1
- package/dist/protocol/background-result-wakeup.js +5 -5
- package/dist/protocol/server.d.ts +13 -0
- package/dist/protocol/server.js +22 -3
- package/dist/services/index.d.ts +0 -1
- package/dist/services/index.js +0 -1
- package/dist/session/memory.js +2 -2
- package/dist/session/session-manager.js +30 -1
- package/dist/tool-system/builtin/agent-notifications.d.ts +25 -1
- package/dist/tool-system/builtin/agent-notifications.js +334 -2
- package/dist/tool-system/context.d.ts +9 -4
- package/dist/tool-system/external-tool-exposure.js +11 -10
- package/package.json +2 -1
- package/dist/services/notifier.d.ts +0 -33
- package/dist/services/notifier.js +0 -83
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-neutral prompt-cache planning.
|
|
3
|
+
*
|
|
4
|
+
* This module owns the semantic cache policy (session affinity and reusable
|
|
5
|
+
* prefix boundaries). Provider clients only translate the plan to their wire
|
|
6
|
+
* format: OpenAI `prompt_cache_*` fields or Anthropic `cache_control` blocks.
|
|
7
|
+
*/
|
|
8
|
+
export type PromptCacheStrategy = "openai-explicit" | "openai-implicit" | "anthropic-explicit" | "provider-managed";
|
|
9
|
+
export type PromptCacheBreakpoint = "system" | "tools" | "stable-history" | "rolling-history";
|
|
10
|
+
export interface PromptCacheRequestContext {
|
|
11
|
+
/** Stable run/session namespace used for provider cache affinity. */
|
|
12
|
+
scopeId?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Number of source messages before the first volatile context message.
|
|
15
|
+
* Providers use this to retain a reusable durable-history breakpoint while
|
|
16
|
+
* also advancing a rolling breakpoint over the append-only in-run tail.
|
|
17
|
+
*/
|
|
18
|
+
stablePrefixMessageCount?: number;
|
|
19
|
+
}
|
|
20
|
+
export interface PromptCachePolicy {
|
|
21
|
+
strategy: PromptCacheStrategy;
|
|
22
|
+
layoutVersion: string;
|
|
23
|
+
breakpoints: readonly PromptCacheBreakpoint[];
|
|
24
|
+
/** Opaque and <=64 chars, as required by OpenAI's prompt_cache_key. */
|
|
25
|
+
cacheKey?: string;
|
|
26
|
+
/** GPT-5.6+ explicit-cache request mode. */
|
|
27
|
+
promptCacheOptions?: {
|
|
28
|
+
mode: "explicit";
|
|
29
|
+
ttl: "30m";
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export interface ResolvePromptCachePolicyInput {
|
|
33
|
+
provider: string;
|
|
34
|
+
providerKind?: string;
|
|
35
|
+
model: string;
|
|
36
|
+
request?: PromptCacheRequestContext;
|
|
37
|
+
/** Sticky compatibility fallback after an endpoint rejects explicit fields. */
|
|
38
|
+
explicitDisabled?: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Produce a privacy-preserving stable affinity key without leaking a raw
|
|
42
|
+
* session id to the provider. The prefix plus 48 hex chars is 51 characters.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createPromptCacheKey(scopeId: string, namespace: string): string;
|
|
45
|
+
/** Resolve one cache policy from the actual provider route and model family. */
|
|
46
|
+
export declare function resolvePromptCachePolicy(input: ResolvePromptCachePolicyInput): PromptCachePolicy;
|
|
47
|
+
/** Deduplicate semantic boundaries while preserving their left-to-right order. */
|
|
48
|
+
export declare function uniquePromptCacheBreakpointIndexes(indexes: readonly (number | undefined)[]): number[];
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-neutral prompt-cache planning.
|
|
3
|
+
*
|
|
4
|
+
* This module owns the semantic cache policy (session affinity and reusable
|
|
5
|
+
* prefix boundaries). Provider clients only translate the plan to their wire
|
|
6
|
+
* format: OpenAI `prompt_cache_*` fields or Anthropic `cache_control` blocks.
|
|
7
|
+
*/
|
|
8
|
+
import { createHash } from "node:crypto";
|
|
9
|
+
const OPENAI_EXPLICIT_BREAKPOINTS = ["system", "stable-history", "rolling-history"];
|
|
10
|
+
const ANTHROPIC_BREAKPOINTS = ["system", "tools", "stable-history", "rolling-history"];
|
|
11
|
+
const OPENROUTER_ANTHROPIC_BREAKPOINTS = ["system", "stable-history", "rolling-history"];
|
|
12
|
+
function normalizedModel(model) {
|
|
13
|
+
return model.replace(/^~/, "");
|
|
14
|
+
}
|
|
15
|
+
function isAnthropicModel(model) {
|
|
16
|
+
return /^anthropic\/claude-/i.test(normalizedModel(model));
|
|
17
|
+
}
|
|
18
|
+
function isOpenAIModel(model) {
|
|
19
|
+
const normalized = normalizedModel(model);
|
|
20
|
+
return /^openai\//i.test(normalized) || /^(?:gpt-|o\d)/i.test(normalized);
|
|
21
|
+
}
|
|
22
|
+
/** GPT-5.6 and later 5.x releases support explicit prompt-cache breakpoints. */
|
|
23
|
+
function supportsOpenAIExplicitCaching(model) {
|
|
24
|
+
const normalized = normalizedModel(model).replace(/^openai\//i, "");
|
|
25
|
+
const match = /^gpt-5\.(\d+)(?:[-.]|$)/i.exec(normalized);
|
|
26
|
+
return match !== null && Number(match[1]) >= 6;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Produce a privacy-preserving stable affinity key without leaking a raw
|
|
30
|
+
* session id to the provider. The prefix plus 48 hex chars is 51 characters.
|
|
31
|
+
*/
|
|
32
|
+
export function createPromptCacheKey(scopeId, namespace) {
|
|
33
|
+
const digest = createHash("sha256")
|
|
34
|
+
.update("codeshell-prompt-cache-v1\0")
|
|
35
|
+
.update(namespace)
|
|
36
|
+
.update("\0")
|
|
37
|
+
.update(scopeId)
|
|
38
|
+
.digest("hex")
|
|
39
|
+
.slice(0, 48);
|
|
40
|
+
return `cs:${digest}`;
|
|
41
|
+
}
|
|
42
|
+
/** Resolve one cache policy from the actual provider route and model family. */
|
|
43
|
+
export function resolvePromptCachePolicy(input) {
|
|
44
|
+
const kind = (input.providerKind ?? input.provider).toLowerCase();
|
|
45
|
+
const model = normalizedModel(input.model);
|
|
46
|
+
const key = input.request?.scopeId
|
|
47
|
+
? createPromptCacheKey(input.request.scopeId, `${kind}:${model}`)
|
|
48
|
+
: undefined;
|
|
49
|
+
if (input.provider === "anthropic" || kind === "anthropic") {
|
|
50
|
+
return {
|
|
51
|
+
strategy: "anthropic-explicit",
|
|
52
|
+
layoutVersion: "system-tools-stable-rolling-v2",
|
|
53
|
+
breakpoints: ANTHROPIC_BREAKPOINTS,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (kind === "openrouter" && isAnthropicModel(model)) {
|
|
57
|
+
return {
|
|
58
|
+
strategy: "anthropic-explicit",
|
|
59
|
+
// OpenRouter/Anthropic includes tools in the system-prefix cache entry,
|
|
60
|
+
// so a separate tool marker is unnecessary and preserves one slot.
|
|
61
|
+
layoutVersion: "system-stable-rolling-v2",
|
|
62
|
+
breakpoints: OPENROUTER_ANTHROPIC_BREAKPOINTS,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const openAIRoute = kind === "openai" || (kind === "openrouter" && isOpenAIModel(model));
|
|
66
|
+
if (openAIRoute && supportsOpenAIExplicitCaching(model) && input.explicitDisabled !== true) {
|
|
67
|
+
return {
|
|
68
|
+
strategy: "openai-explicit",
|
|
69
|
+
layoutVersion: "system-stable-rolling-v1",
|
|
70
|
+
breakpoints: OPENAI_EXPLICIT_BREAKPOINTS,
|
|
71
|
+
...(key ? { cacheKey: key } : {}),
|
|
72
|
+
promptCacheOptions: { mode: "explicit", ttl: "30m" },
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (openAIRoute) {
|
|
76
|
+
return {
|
|
77
|
+
strategy: "openai-implicit",
|
|
78
|
+
layoutVersion: "implicit-affinity-v1",
|
|
79
|
+
breakpoints: [],
|
|
80
|
+
...(key ? { cacheKey: key } : {}),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
strategy: "provider-managed",
|
|
85
|
+
layoutVersion: "append-only-v1",
|
|
86
|
+
breakpoints: [],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/** Deduplicate semantic boundaries while preserving their left-to-right order. */
|
|
90
|
+
export function uniquePromptCacheBreakpointIndexes(indexes) {
|
|
91
|
+
const seen = new Set();
|
|
92
|
+
const result = [];
|
|
93
|
+
for (const index of indexes) {
|
|
94
|
+
if (index === undefined || index < 0 || seen.has(index))
|
|
95
|
+
continue;
|
|
96
|
+
seen.add(index);
|
|
97
|
+
result.push(index);
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
@@ -16,6 +16,7 @@ export declare class AnthropicClient extends LLMClientBase {
|
|
|
16
16
|
*/
|
|
17
17
|
private _capability;
|
|
18
18
|
private get capability();
|
|
19
|
+
private promptCachePolicy;
|
|
19
20
|
getPromptCacheConfigIdentity(): Readonly<Record<string, unknown>>;
|
|
20
21
|
/**
|
|
21
22
|
* Translate the resolved ReasoningSetting into Anthropic's `thinking` field,
|
|
@@ -44,7 +45,9 @@ export declare class AnthropicClient extends LLMClientBase {
|
|
|
44
45
|
private nonStreamMessage;
|
|
45
46
|
private streamMessage;
|
|
46
47
|
private processResponse;
|
|
48
|
+
private buildSystem;
|
|
47
49
|
private buildMessages;
|
|
50
|
+
private markAnthropicCacheBreakpoint;
|
|
48
51
|
private convertTools;
|
|
49
52
|
private handleApiError;
|
|
50
53
|
}
|
|
@@ -9,6 +9,7 @@ import { countTokens } from "../token-counter.js";
|
|
|
9
9
|
import { capabilitiesFor } from "../capabilities/index.js";
|
|
10
10
|
import { resolveApiKey, resolveHeaders } from "../provider-auth.js";
|
|
11
11
|
import { stripVisionFromHistory } from "../strip-vision.js";
|
|
12
|
+
import { resolvePromptCachePolicy, uniquePromptCacheBreakpointIndexes, } from "../prompt-cache.js";
|
|
12
13
|
/**
|
|
13
14
|
* Anthropic's `max_tokens` is required, so unlike OpenAI we can't omit it when
|
|
14
15
|
* the model's ceiling is unknown. Use a conservative floor in that rare case
|
|
@@ -75,12 +76,22 @@ export class AnthropicClient extends LLMClientBase {
|
|
|
75
76
|
}
|
|
76
77
|
return this._capability;
|
|
77
78
|
}
|
|
79
|
+
promptCachePolicy(request) {
|
|
80
|
+
return resolvePromptCachePolicy({
|
|
81
|
+
provider: this.provider,
|
|
82
|
+
providerKind: this.config.providerKind,
|
|
83
|
+
model: this.model,
|
|
84
|
+
request,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
78
87
|
getPromptCacheConfigIdentity() {
|
|
88
|
+
const cachePolicy = this.promptCachePolicy();
|
|
79
89
|
return {
|
|
80
90
|
...super.getPromptCacheConfigIdentity(),
|
|
81
|
-
cacheStrategy:
|
|
82
|
-
cacheLayoutVersion:
|
|
83
|
-
|
|
91
|
+
cacheStrategy: cachePolicy.strategy,
|
|
92
|
+
cacheLayoutVersion: cachePolicy.layoutVersion,
|
|
93
|
+
cacheBreakpoints: cachePolicy.breakpoints,
|
|
94
|
+
breakpointCount: cachePolicy.breakpoints.length,
|
|
84
95
|
reasoningShape: this.capability.reasoning,
|
|
85
96
|
};
|
|
86
97
|
}
|
|
@@ -133,8 +144,9 @@ export class AnthropicClient extends LLMClientBase {
|
|
|
133
144
|
}
|
|
134
145
|
async createMessage(options) {
|
|
135
146
|
return this.withRetry(async (requestSignal) => {
|
|
136
|
-
const
|
|
137
|
-
const
|
|
147
|
+
const cachePolicy = this.promptCachePolicy(options.promptCache);
|
|
148
|
+
const messages = this.buildMessages(options.messages, options.promptCache, cachePolicy);
|
|
149
|
+
const tools = options.tools ? this.convertTools(options.tools, cachePolicy) : undefined;
|
|
138
150
|
// One span per outbound LLM request. Begin emits debug-level so the
|
|
139
151
|
// info log isn't spammed during normal operation; end emits info with
|
|
140
152
|
// duration_ms + usage so `--debug=llm` already gives a quick latency
|
|
@@ -146,6 +158,7 @@ export class AnthropicClient extends LLMClientBase {
|
|
|
146
158
|
stream: !!(options.stream && options.onChunk),
|
|
147
159
|
messageCount: messages.length,
|
|
148
160
|
toolCount: tools?.length ?? 0,
|
|
161
|
+
cacheStrategy: cachePolicy.strategy,
|
|
149
162
|
});
|
|
150
163
|
try {
|
|
151
164
|
const response = options.stream && options.onChunk
|
|
@@ -176,13 +189,7 @@ export class AnthropicClient extends LLMClientBase {
|
|
|
176
189
|
const response = await this.client.messages.create({
|
|
177
190
|
model: this.model,
|
|
178
191
|
max_tokens: maxTokens,
|
|
179
|
-
system:
|
|
180
|
-
{
|
|
181
|
-
type: "text",
|
|
182
|
-
text: options.systemPrompt,
|
|
183
|
-
cache_control: { type: "ephemeral" },
|
|
184
|
-
},
|
|
185
|
-
],
|
|
192
|
+
system: this.buildSystem(options.systemPrompt, this.promptCachePolicy(options.promptCache)),
|
|
186
193
|
messages,
|
|
187
194
|
...(tools?.length ? { tools } : {}),
|
|
188
195
|
...(thinking ? { thinking } : {}),
|
|
@@ -207,13 +214,7 @@ export class AnthropicClient extends LLMClientBase {
|
|
|
207
214
|
const stream = this.client.messages.stream({
|
|
208
215
|
model: this.model,
|
|
209
216
|
max_tokens: maxTokens,
|
|
210
|
-
system:
|
|
211
|
-
{
|
|
212
|
-
type: "text",
|
|
213
|
-
text: options.systemPrompt,
|
|
214
|
-
cache_control: { type: "ephemeral" },
|
|
215
|
-
},
|
|
216
|
-
],
|
|
217
|
+
system: this.buildSystem(options.systemPrompt, this.promptCachePolicy(options.promptCache)),
|
|
217
218
|
messages,
|
|
218
219
|
...(tools?.length ? { tools } : {}),
|
|
219
220
|
...(thinking ? { thinking } : {}),
|
|
@@ -316,10 +317,27 @@ export class AnthropicClient extends LLMClientBase {
|
|
|
316
317
|
stopReason: response.stop_reason ?? undefined,
|
|
317
318
|
};
|
|
318
319
|
}
|
|
319
|
-
|
|
320
|
+
buildSystem(systemPrompt, policy) {
|
|
321
|
+
return [
|
|
322
|
+
{
|
|
323
|
+
type: "text",
|
|
324
|
+
text: systemPrompt,
|
|
325
|
+
...(policy.breakpoints.includes("system")
|
|
326
|
+
? { cache_control: { type: "ephemeral" } }
|
|
327
|
+
: {}),
|
|
328
|
+
},
|
|
329
|
+
];
|
|
330
|
+
}
|
|
331
|
+
buildMessages(messages, promptCache, policy) {
|
|
320
332
|
messages = stripVisionFromHistory(messages, this.capability.supportsVision);
|
|
321
333
|
const result = [];
|
|
322
|
-
|
|
334
|
+
const stablePrefixMessageCount = Math.max(0, Math.min(messages.length, promptCache?.stablePrefixMessageCount ?? messages.length));
|
|
335
|
+
let stablePrefixEndMessage;
|
|
336
|
+
for (let sourceIndex = 0; sourceIndex < messages.length; sourceIndex++) {
|
|
337
|
+
if (sourceIndex === stablePrefixMessageCount) {
|
|
338
|
+
stablePrefixEndMessage = result[result.length - 1];
|
|
339
|
+
}
|
|
340
|
+
const msg = messages[sourceIndex];
|
|
323
341
|
if (msg.role === "system")
|
|
324
342
|
continue;
|
|
325
343
|
const role = msg.role === "tool" ? "user" : msg.role;
|
|
@@ -390,40 +408,45 @@ export class AnthropicClient extends LLMClientBase {
|
|
|
390
408
|
}
|
|
391
409
|
}
|
|
392
410
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
lastMsg.content = [
|
|
406
|
-
{
|
|
407
|
-
type: "text",
|
|
408
|
-
text: lastMsg.content,
|
|
409
|
-
cache_control: { type: "ephemeral" },
|
|
410
|
-
},
|
|
411
|
-
];
|
|
412
|
-
}
|
|
413
|
-
else {
|
|
414
|
-
const lastBlock = lastMsg.content[lastMsg.content.length - 1];
|
|
415
|
-
// Skip thinking/redacted_thinking blocks: they reject cache_control and
|
|
416
|
-
// marking them would 400. buildMessages never emits them today (thinking
|
|
417
|
-
// is a top-level request field, not history content), but guard anyway
|
|
418
|
-
// so a future block type can't silently break the request.
|
|
419
|
-
if (lastBlock && lastBlock.type !== "thinking" && lastBlock.type !== "redacted_thinking") {
|
|
420
|
-
lastBlock.cache_control = { type: "ephemeral" };
|
|
421
|
-
}
|
|
422
|
-
}
|
|
411
|
+
stablePrefixEndMessage ??= result[result.length - 1];
|
|
412
|
+
const stablePrefixEndIndex = stablePrefixEndMessage
|
|
413
|
+
? result.indexOf(stablePrefixEndMessage)
|
|
414
|
+
: undefined;
|
|
415
|
+
const breakpointIndexes = uniquePromptCacheBreakpointIndexes([
|
|
416
|
+
policy.breakpoints.includes("stable-history") && stablePrefixEndIndex !== undefined
|
|
417
|
+
? stablePrefixEndIndex
|
|
418
|
+
: undefined,
|
|
419
|
+
policy.breakpoints.includes("rolling-history") ? result.length - 1 : undefined,
|
|
420
|
+
]);
|
|
421
|
+
for (const index of breakpointIndexes) {
|
|
422
|
+
this.markAnthropicCacheBreakpoint(result[index]);
|
|
423
423
|
}
|
|
424
424
|
return result;
|
|
425
425
|
}
|
|
426
|
-
|
|
426
|
+
markAnthropicCacheBreakpoint(message) {
|
|
427
|
+
if (!message)
|
|
428
|
+
return;
|
|
429
|
+
if (typeof message.content === "string") {
|
|
430
|
+
message.content = [
|
|
431
|
+
{
|
|
432
|
+
type: "text",
|
|
433
|
+
text: message.content,
|
|
434
|
+
cache_control: { type: "ephemeral" },
|
|
435
|
+
},
|
|
436
|
+
];
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
for (let index = message.content.length - 1; index >= 0; index--) {
|
|
440
|
+
const block = message.content[index];
|
|
441
|
+
// Thinking blocks reject cache_control. Walk backward so a preceding
|
|
442
|
+
// cacheable text/tool block can still anchor the prefix.
|
|
443
|
+
if (block.type === "thinking" || block.type === "redacted_thinking")
|
|
444
|
+
continue;
|
|
445
|
+
block.cache_control = { type: "ephemeral" };
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
convertTools(tools, policy) {
|
|
427
450
|
const converted = tools.map((t) => ({
|
|
428
451
|
name: t.name,
|
|
429
452
|
description: t.description,
|
|
@@ -436,8 +459,9 @@ export class AnthropicClient extends LLMClientBase {
|
|
|
436
459
|
// not per-tool); a second marker here would waste a scarce cache_control
|
|
437
460
|
// slot (max 4) and risk KV eviction. See docs/todo/prompt-cache-optimization.md.
|
|
438
461
|
const last = converted[converted.length - 1];
|
|
439
|
-
if (last)
|
|
462
|
+
if (last && policy.breakpoints.includes("tools")) {
|
|
440
463
|
last.cache_control = { type: "ephemeral" };
|
|
464
|
+
}
|
|
441
465
|
return converted;
|
|
442
466
|
}
|
|
443
467
|
handleApiError(err) {
|
|
@@ -52,6 +52,9 @@ export declare class OpenAIClient extends LLMClientBase {
|
|
|
52
52
|
private readonly dangerouslyAllowBrowser;
|
|
53
53
|
private _forceMaxCompletionTokens;
|
|
54
54
|
private _dropReasoningEffort;
|
|
55
|
+
/** Compatibility fallbacks for OpenAI-compatible gateways that lag the API. */
|
|
56
|
+
private _disableExplicitPromptCache;
|
|
57
|
+
private _disablePromptCacheKey;
|
|
55
58
|
constructor(config: LLMConfig, defaults?: ClientDefaults, runtimeOptions?: {
|
|
56
59
|
dangerouslyAllowBrowser?: boolean;
|
|
57
60
|
});
|
|
@@ -66,20 +69,7 @@ export declare class OpenAIClient extends LLMClientBase {
|
|
|
66
69
|
*/
|
|
67
70
|
private _capability;
|
|
68
71
|
private get capability();
|
|
69
|
-
|
|
70
|
-
* True when this client routes an Anthropic-family model through OpenRouter's
|
|
71
|
-
* OpenAI-compatible endpoint. Anthropic caching is EXPLICIT — nothing is
|
|
72
|
-
* cached unless the request carries `cache_control` breakpoints (verified live
|
|
73
|
-
* 2026-07-02: plain requests to anthropic/claude-opus-4.7-fast via OpenRouter
|
|
74
|
-
* report cached_tokens 0 on every repeat; a single system-block breakpoint
|
|
75
|
-
* turns the whole stable prefix — tools + system — into a cache hit, ~89%
|
|
76
|
-
* cheaper on the follow-up). OpenAI and other OpenRouter models cache
|
|
77
|
-
* automatically, so they must NOT get breakpoints. The slug arrives resolved
|
|
78
|
-
* (e.g. "anthropic/claude-opus-4.7-fast") or as the router alias
|
|
79
|
-
* ("~anthropic/claude-opus-latest") — both start with an optional "~" then
|
|
80
|
-
* "anthropic/".
|
|
81
|
-
*/
|
|
82
|
-
private get isOpenRouterAnthropic();
|
|
72
|
+
private promptCachePolicy;
|
|
83
73
|
getPromptCacheConfigIdentity(): Readonly<Record<string, unknown>>;
|
|
84
74
|
createMessage(options: CreateMessageOptions): Promise<LLMResponse>;
|
|
85
75
|
/**
|
|
@@ -93,20 +83,10 @@ export declare class OpenAIClient extends LLMClientBase {
|
|
|
93
83
|
private processChoice;
|
|
94
84
|
private buildMessages;
|
|
95
85
|
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* 1. System block — the stable prefix. Anthropic sees tools BEFORE the
|
|
99
|
-
* system prompt, so one marker on the system block caches tools too
|
|
100
|
-
* (verified live: system-only marker cached 3511/3952 prompt tokens
|
|
101
|
-
* including tool defs).
|
|
102
|
-
* 2. Last message — one rolling breakpoint so the growing conversation
|
|
103
|
-
* history becomes a cached prefix. Not scrolled: as history grows the
|
|
104
|
-
* "last message" naturally advances and its tail is the next write.
|
|
105
|
-
* A string `content` is lifted to a single-element `[{type:"text",...}]`
|
|
106
|
-
* array so it can carry `cache_control`; OpenRouter accepts this OpenAI
|
|
107
|
-
* multimodal wire form for text.
|
|
86
|
+
* Translate semantic prefix boundaries to the active wire format. Both
|
|
87
|
+
* formats annotate content blocks without reordering messages.
|
|
108
88
|
*/
|
|
109
|
-
private
|
|
89
|
+
private applyPromptCacheBreakpoints;
|
|
110
90
|
private convertTools;
|
|
111
91
|
private handleApiError;
|
|
112
92
|
}
|