@inflexa-ai/harness 0.39.2 → 0.41.0
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/README.md +2 -3
- package/dist/agents/conversation-agent.d.ts +3 -0
- package/dist/agents/conversation-agent.js +13 -2
- package/dist/agents/report-session-agent.d.ts +3 -0
- package/dist/agents/report-session-agent.js +2 -0
- package/dist/agents/sandbox/cheminformatics-agent.js +0 -3
- package/dist/agents/sandbox/shared.d.ts +11 -2
- package/dist/agents/sandbox/shared.js +4 -0
- package/dist/app/chat-turn.d.ts +104 -12
- package/dist/app/chat-turn.js +180 -21
- package/dist/app/message-assembly.d.ts +16 -52
- package/dist/app/message-assembly.js +41 -68
- package/dist/app/run-activity.d.ts +4 -6
- package/dist/app/run-activity.js +7 -27
- package/dist/app/spawn-report-session.d.ts +6 -11
- package/dist/app/spawn-report-session.js +5 -6
- package/dist/app/synthesize-run.d.ts +3 -0
- package/dist/app/synthesize-run.js +1 -0
- package/dist/billing/usage-recorder.d.ts +3 -4
- package/dist/billing/usage-recorder.js +3 -4
- package/dist/contracts/chat-parts.d.ts +11 -1
- package/dist/contracts/chat-parts.js +1 -0
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/part-registry.js +1 -0
- package/dist/contracts/report-blocks.d.ts +8 -8
- package/dist/contracts/schemas/chat-parts.d.ts +32 -9
- package/dist/contracts/schemas/chat-parts.js +10 -0
- package/dist/execution/artifact-metadata.d.ts +26 -33
- package/dist/execution/artifact-metadata.js +44 -106
- package/dist/execution/post-step-pipeline.d.ts +23 -16
- package/dist/execution/post-step-pipeline.js +19 -21
- package/dist/execution/run-synthesis.d.ts +3 -0
- package/dist/execution/run-synthesis.js +11 -1
- package/dist/execution/step-summary.d.ts +21 -30
- package/dist/execution/step-summary.js +30 -57
- package/dist/index.d.ts +9 -8
- package/dist/index.js +15 -34
- package/dist/lib/zod-issues.js +2 -4
- package/dist/loop/compaction.d.ts +20 -0
- package/dist/loop/compaction.js +2 -0
- package/dist/loop/continue-agent.d.ts +40 -0
- package/dist/loop/continue-agent.js +38 -0
- package/dist/loop/metrics.d.ts +30 -22
- package/dist/loop/metrics.js +51 -22
- package/dist/loop/run-agent.d.ts +111 -13
- package/dist/loop/run-agent.js +467 -220
- package/dist/loop/run-to-terminal.d.ts +16 -17
- package/dist/loop/run-to-terminal.js +24 -35
- package/dist/loop/tool-mask.d.ts +23 -0
- package/dist/loop/tool-mask.js +46 -0
- package/dist/loop/tool-output.d.ts +48 -0
- package/dist/loop/tool-output.js +79 -0
- package/dist/loop/types.d.ts +6 -3
- package/dist/memory/ai-sdk-message-storage.d.ts +49 -2
- package/dist/memory/ai-sdk-message-storage.js +78 -2
- package/dist/memory/conversation-display-recorder.d.ts +6 -0
- package/dist/memory/conversation-display-recorder.js +64 -22
- package/dist/memory/conversation-display-replay.d.ts +5 -14
- package/dist/memory/conversation-display-replay.js +66 -32
- package/dist/memory/conversation-display-storage.d.ts +2 -1
- package/dist/memory/conversation-display-storage.js +2 -1
- package/dist/memory/conversation-view.d.ts +53 -0
- package/dist/memory/conversation-view.js +130 -0
- package/dist/memory/count-tokens.d.ts +5 -7
- package/dist/memory/count-tokens.js +7 -9
- package/dist/memory/thread-history.d.ts +74 -102
- package/dist/memory/thread-history.js +181 -246
- package/dist/memory/thread-store.d.ts +3 -3
- package/dist/memory/thread-store.js +7 -2
- package/dist/memory/tool-call-integrity.d.ts +15 -21
- package/dist/memory/tool-call-integrity.js +31 -38
- package/dist/memory/working-memory.d.ts +2 -5
- package/dist/memory/working-memory.js +2 -5
- package/dist/prompts/SOUL.d.ts +2 -2
- package/dist/prompts/SOUL.js +2 -7
- package/dist/prompts/analogical-reasoner.d.ts +1 -1
- package/dist/prompts/analogical-reasoner.js +16 -32
- package/dist/prompts/briefing.js +1 -1
- package/dist/prompts/compaction.d.ts +4 -0
- package/dist/prompts/compaction.js +24 -0
- package/dist/prompts/conversation.d.ts +1 -1
- package/dist/prompts/conversation.js +26 -35
- package/dist/prompts/execute-analysis/step-summary.js +7 -5
- package/dist/prompts/literature-reviewer.d.ts +1 -1
- package/dist/prompts/literature-reviewer.js +29 -34
- package/dist/prompts/planner.js +15 -29
- package/dist/prompts/report-session.d.ts +1 -1
- package/dist/prompts/report-session.js +2 -33
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +1 -2
- package/dist/prompts/sandbox/cheminformatics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/cheminformatics-agent.js +10 -29
- package/dist/prompts/sandbox/chromatin-agent.d.ts +1 -1
- package/dist/prompts/sandbox/chromatin-agent.js +1 -2
- package/dist/prompts/sandbox/data-profiler.js +6 -3
- package/dist/prompts/sandbox/dna-methylation-agent.d.ts +1 -1
- package/dist/prompts/sandbox/dna-methylation-agent.js +2 -7
- package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
- package/dist/prompts/sandbox/drug-repurposing-agent.js +20 -24
- package/dist/prompts/sandbox/enrichment-agent.d.ts +1 -1
- package/dist/prompts/sandbox/enrichment-agent.js +3 -2
- package/dist/prompts/sandbox/genomic-variant-agent.d.ts +1 -1
- package/dist/prompts/sandbox/genomic-variant-agent.js +0 -2
- package/dist/prompts/sandbox/immune-profiling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/immune-profiling-agent.js +7 -14
- package/dist/prompts/sandbox/metabolomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/metabolomics-agent.js +0 -2
- package/dist/prompts/sandbox/microbiome-agent.d.ts +1 -1
- package/dist/prompts/sandbox/microbiome-agent.js +14 -21
- package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multi-omics-integration-agent.js +9 -19
- package/dist/prompts/sandbox/multimodal-sc-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multimodal-sc-agent.js +4 -5
- package/dist/prompts/sandbox/network-agent.d.ts +1 -1
- package/dist/prompts/sandbox/network-agent.js +7 -8
- package/dist/prompts/sandbox/pkpd-clinical-response-agent.d.ts +1 -1
- package/dist/prompts/sandbox/pkpd-clinical-response-agent.js +19 -19
- package/dist/prompts/sandbox/proteomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/proteomics-agent.js +3 -4
- package/dist/prompts/sandbox/scientific-executor.d.ts +1 -1
- package/dist/prompts/sandbox/scientific-executor.js +7 -10
- package/dist/prompts/sandbox/single-cell-agent.d.ts +1 -1
- package/dist/prompts/sandbox/single-cell-agent.js +2 -4
- package/dist/prompts/sandbox/spatial-omics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/spatial-omics-agent.js +3 -5
- package/dist/prompts/sandbox/statistical-modeling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/statistical-modeling-agent.js +1 -1
- package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
- package/dist/prompts/sandbox/translational-safety-agent.js +10 -19
- package/dist/prompts/sandbox-standards.d.ts +7 -7
- package/dist/prompts/sandbox-standards.js +74 -76
- package/dist/prompts/synthesis-agent.d.ts +1 -1
- package/dist/prompts/synthesis-agent.js +16 -42
- package/dist/providers/ai-sdk.d.ts +39 -6
- package/dist/providers/ai-sdk.js +117 -18
- package/dist/providers/errors.d.ts +2 -0
- package/dist/providers/errors.js +5 -0
- package/dist/providers/prompt-cache.d.ts +43 -41
- package/dist/providers/prompt-cache.js +47 -41
- package/dist/providers/reasoning.d.ts +10 -21
- package/dist/providers/reasoning.js +10 -21
- package/dist/providers/types.d.ts +21 -5
- package/dist/report-model/authoring-grammar.d.ts +2 -2
- package/dist/runtime/assemble.d.ts +9 -7
- package/dist/runtime/assemble.js +7 -3
- package/dist/sandbox/create-sandbox.d.ts +2 -2
- package/dist/sandbox/submit-exec.d.ts +1 -1
- package/dist/schemas/artifact-manifest.d.ts +4 -4
- package/dist/state/init.js +39 -4
- package/dist/state/purge-analysis.js +4 -0
- package/dist/state/tool-outputs.d.ts +4 -0
- package/dist/state/tool-outputs.js +30 -0
- package/dist/tasks/data-profile.d.ts +3 -0
- package/dist/tasks/data-profile.js +2 -0
- package/dist/tools/ad-hoc-router.d.ts +8 -0
- package/dist/tools/ad-hoc-router.js +21 -2
- package/dist/tools/display/show-user.d.ts +1 -1
- package/dist/tools/execute-analysis.d.ts +3 -0
- package/dist/tools/execute-analysis.js +9 -1
- package/dist/tools/read-tool-output.d.ts +7 -0
- package/dist/tools/read-tool-output.js +130 -0
- package/dist/tools/research/generate-analogy-report.d.ts +10 -26
- package/dist/tools/research/generate-analogy-report.js +76 -163
- package/dist/tools/research/generate-plan.d.ts +3 -0
- package/dist/tools/research/generate-plan.js +4 -1
- package/dist/tools/research/literature-reviewer.d.ts +3 -0
- package/dist/tools/research/literature-reviewer.js +3 -0
- package/dist/tools/sandbox/report-blocker.d.ts +18 -5
- package/dist/tools/sandbox/report-blocker.js +54 -6
- package/dist/tools/sandbox/submit-file-metadata.d.ts +30 -0
- package/dist/tools/sandbox/submit-file-metadata.js +67 -0
- package/dist/tools/start-report-session.d.ts +1 -3
- package/dist/tools/start-report-session.js +0 -2
- package/dist/tools/workspace/execute-command.js +7 -4
- package/dist/tools/workspace/result-bounds.d.ts +4 -4
- package/dist/tools/workspace/result-bounds.js +6 -5
- package/dist/tools/workspace/workspace-search.d.ts +1 -1
- package/dist/workflows/execute-analysis.d.ts +3 -0
- package/dist/workflows/execute-analysis.js +1 -0
- package/dist/workflows/sandbox-step.d.ts +30 -9
- package/dist/workflows/sandbox-step.js +37 -8
- package/package.json +9 -9
- package/dist/providers/anthropic.d.ts +0 -25
- package/dist/providers/anthropic.js +0 -28
package/dist/providers/ai-sdk.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { streamText, wrapLanguageModel, } from "ai";
|
|
2
3
|
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
4
|
+
// Any `providerOptions.anthropic.thinking` bypasses this table's thinking
|
|
5
|
+
// selection (see `thinkingWithBinding`).
|
|
6
|
+
import { getModelCapabilities } from "@ai-sdk/anthropic/internal";
|
|
3
7
|
import { createOpenAI } from "@ai-sdk/openai";
|
|
4
8
|
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
5
9
|
import { OpenTelemetry } from "@ai-sdk/otel";
|
|
@@ -11,6 +15,7 @@ import { scopeWorkloadId } from "../auth/types.js";
|
|
|
11
15
|
import { createNoopLogger } from "../lib/console-logger.js";
|
|
12
16
|
import { passThroughTracer } from "../lib/otel-spans.js";
|
|
13
17
|
import { DEFAULT_SUSPEND_ON, classifyProviderError, RequestTimeoutError, toProviderError } from "./errors.js";
|
|
18
|
+
import { DEFAULT_REASONING } from "./reasoning.js";
|
|
14
19
|
import { headersRefusalError, requestHeadersFor } from "./request-headers.js";
|
|
15
20
|
/**
|
|
16
21
|
* The harness-owned retry envelope. The AI SDK's built-in retry exposes only a
|
|
@@ -25,11 +30,10 @@ export const RETRY_BACKOFF_FACTOR = 2;
|
|
|
25
30
|
export const RETRY_MAX_DELAY_MS = 30_000;
|
|
26
31
|
/**
|
|
27
32
|
* Output-token ceiling requested when a config names none — a floor against a
|
|
28
|
-
* provider default, not a target. `@ai-sdk/anthropic`
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* tool-call.
|
|
32
|
-
* claude-3-haiku to 4096), so it cannot over-request one it recognizes.
|
|
33
|
+
* provider default, not a target. For an unset value, `@ai-sdk/anthropic`
|
|
34
|
+
* defaults to 128000 for an unknown `claude-*` id and 4096 for a non-Claude
|
|
35
|
+
* id. The anthropic arm always sends a value, so it never truncates mid
|
|
36
|
+
* tool-call.
|
|
33
37
|
*
|
|
34
38
|
* Lower it per-config for servers that validate `prompt + max_tokens <= context`
|
|
35
39
|
* (vLLM and similar) against a small-context model.
|
|
@@ -63,6 +67,50 @@ function routeSdkWarningsTo(logger) {
|
|
|
63
67
|
function workloadOf(session) {
|
|
64
68
|
return `${session.scope.kind}:${scopeWorkloadId(session.scope)}`;
|
|
65
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Identifiers only, never a name or address. The run frame wins over the
|
|
72
|
+
* thread so parallel steps of one run keep separate gateway cache accounts.
|
|
73
|
+
*/
|
|
74
|
+
export function sessionKeyOf(session) {
|
|
75
|
+
const { scope, runFrame } = session;
|
|
76
|
+
if (runFrame !== undefined) {
|
|
77
|
+
return runFrame.stepId === undefined ? `${scope.analysisId}:${runFrame.runId}` : `${scope.analysisId}:${runFrame.runId}:${runFrame.stepId}`;
|
|
78
|
+
}
|
|
79
|
+
return scope.threadId === undefined ? scope.analysisId : `${scope.analysisId}:${scope.threadId}`;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Base64url SHA-256 digest of {@link sessionKeyOf}, always 43 characters.
|
|
83
|
+
* OpenAI and Azure reject a `prompt_cache_key` over 64 characters with a
|
|
84
|
+
* non-retryable 400.
|
|
85
|
+
*/
|
|
86
|
+
export function sessionKeyDigestOf(session) {
|
|
87
|
+
return createHash("sha256").update(sessionKeyOf(session)).digest("base64url");
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* CAUTION: setting `providerOptions.anthropic.thinking` without a `type`
|
|
91
|
+
* turns thinking off. Call only for a model whose capability row says
|
|
92
|
+
* `rejectsThinkingDisabled`.
|
|
93
|
+
*/
|
|
94
|
+
function thinkingWithBinding(reasoning, mode) {
|
|
95
|
+
const blockBinding = { prefixMismatchBehavior: mode };
|
|
96
|
+
if (reasoning === "none" || reasoning === "provider-default")
|
|
97
|
+
return { blockBinding };
|
|
98
|
+
return { type: "adaptive", display: "summarized", blockBinding };
|
|
99
|
+
}
|
|
100
|
+
/** Reads `providerMetadata.anthropic.inputTransformations`; empty for another vendor. */
|
|
101
|
+
function inputTransformationsOf(metadata) {
|
|
102
|
+
const entries = metadata?.["anthropic"]?.["inputTransformations"];
|
|
103
|
+
if (!Array.isArray(entries))
|
|
104
|
+
return [];
|
|
105
|
+
return entries.map((entry) => {
|
|
106
|
+
const fields = typeof entry === "object" && entry !== null ? entry : {};
|
|
107
|
+
const text = (key) => {
|
|
108
|
+
const value = fields[key];
|
|
109
|
+
return typeof value === "string" ? value : undefined;
|
|
110
|
+
};
|
|
111
|
+
return { type: text("type"), path: text("path"), reason: text("reason") };
|
|
112
|
+
});
|
|
113
|
+
}
|
|
66
114
|
function isAbortError(value) {
|
|
67
115
|
if (value instanceof DOMException && value.name === "AbortError")
|
|
68
116
|
return true;
|
|
@@ -252,6 +300,10 @@ function toChatUsage(usage) {
|
|
|
252
300
|
function requestedModelIdOf(model) {
|
|
253
301
|
return typeof model === "string" ? model : model.modelId;
|
|
254
302
|
}
|
|
303
|
+
/** The provider id of the model bound into this provider, as the AI SDK names it — a bare id string names no provider. */
|
|
304
|
+
function providerIdOf(model) {
|
|
305
|
+
return typeof model === "string" ? undefined : model.provider;
|
|
306
|
+
}
|
|
255
307
|
/**
|
|
256
308
|
* Capture the served model id at the raw model boundary, because that is the
|
|
257
309
|
* only place it is still distinguishable from the requested one: `streamText`
|
|
@@ -294,13 +346,13 @@ function captureServedModelId(model) {
|
|
|
294
346
|
return { model: wrapped, servedModelId: () => served };
|
|
295
347
|
}
|
|
296
348
|
function responseFromMessages(input) {
|
|
297
|
-
const { messages, fallbackText, finishReason, rawFinishReason, requestedModelId, servedModelId } = input;
|
|
349
|
+
const { messages, fallbackText, finishReason, rawFinishReason, requestedModelId, servedModelId, provider } = input;
|
|
298
350
|
const usage = toChatUsage(input.usage);
|
|
299
351
|
const message = [...messages].reverse().find((m) => m.role === "assistant");
|
|
300
352
|
if (message === undefined) {
|
|
301
|
-
return { message: { role: "assistant", content: fallbackText }, finishReason, rawFinishReason, usage, requestedModelId, servedModelId };
|
|
353
|
+
return { message: { role: "assistant", content: fallbackText }, finishReason, rawFinishReason, usage, requestedModelId, servedModelId, provider };
|
|
302
354
|
}
|
|
303
|
-
return { message, finishReason, rawFinishReason, usage, requestedModelId, servedModelId };
|
|
355
|
+
return { message, finishReason, rawFinishReason, usage, requestedModelId, servedModelId, provider };
|
|
304
356
|
}
|
|
305
357
|
/**
|
|
306
358
|
* Drop empty text parts — and messages left with no content at all — before
|
|
@@ -435,6 +487,8 @@ export function createAiSdkProvider(deps) {
|
|
|
435
487
|
const suspendOn = deps.suspendOn ?? DEFAULT_SUSPEND_ON;
|
|
436
488
|
const requestTimeoutMs = deps.requestTimeoutMs;
|
|
437
489
|
const requestedModelId = requestedModelIdOf(deps.model);
|
|
490
|
+
const providerId = providerIdOf(deps.model);
|
|
491
|
+
const effortOf = (req) => req.reasoning ?? deps.reasoning ?? DEFAULT_REASONING;
|
|
438
492
|
// Each model call emits OpenTelemetry GenAI spans through the AI SDK, with no
|
|
439
493
|
// prompt or completion text on them. The integration rides on each call
|
|
440
494
|
// instead of the global registry, so the harness never traces the embedder's
|
|
@@ -461,7 +515,18 @@ export function createAiSdkProvider(deps) {
|
|
|
461
515
|
...logger.errorFields(thrown),
|
|
462
516
|
});
|
|
463
517
|
}
|
|
518
|
+
/**
|
|
519
|
+
* Logs each dropped thinking block; the drop does not fail the call.
|
|
520
|
+
* `reason` is `prefix_binding_mismatch` or `model_binding_mismatch`.
|
|
521
|
+
*/
|
|
522
|
+
function logDroppedThinkingBlocks(session, metadata) {
|
|
523
|
+
for (const drop of inputTransformationsOf(metadata)) {
|
|
524
|
+
logger.warn("thinking block dropped", { workload: workloadOf(session), type: drop.type, path: drop.path, reason: drop.reason });
|
|
525
|
+
}
|
|
526
|
+
}
|
|
464
527
|
function chat(req, session, signal) {
|
|
528
|
+
const reasoning = effortOf(req);
|
|
529
|
+
const providerOptions = deps.providerOptionsFor?.({ session, reasoning });
|
|
465
530
|
const run = async () => {
|
|
466
531
|
const hookCall = { unsettled: false };
|
|
467
532
|
const retry = createRetry(signal, logger, maxRetries, suspendOn, hookCall);
|
|
@@ -504,8 +569,8 @@ export function createAiSdkProvider(deps) {
|
|
|
504
569
|
// carries the one configured bound, and a turn that writes
|
|
505
570
|
// steadily runs as long as it needs.
|
|
506
571
|
...(requestTimeoutMs !== undefined ? { timeout: { firstChunkMs: requestTimeoutMs, chunkMs: requestTimeoutMs } } : {}),
|
|
507
|
-
providerOptions
|
|
508
|
-
reasoning
|
|
572
|
+
providerOptions,
|
|
573
|
+
reasoning,
|
|
509
574
|
});
|
|
510
575
|
// The drain sits inside the retried closure, thus a failure at any
|
|
511
576
|
// point of the stream re-runs the whole attempt and the envelope
|
|
@@ -530,14 +595,20 @@ export function createAiSdkProvider(deps) {
|
|
|
530
595
|
finishReason: await result.finishReason,
|
|
531
596
|
rawFinishReason: await result.rawFinishReason,
|
|
532
597
|
usage: await result.usage,
|
|
598
|
+
providerMetadata: (await result.finalStep).providerMetadata,
|
|
533
599
|
};
|
|
534
600
|
});
|
|
601
|
+
// Logged past the envelope, thus only the attempt that succeeded
|
|
602
|
+
// reports its drops.
|
|
603
|
+
const { providerMetadata: metadata, ...reply } = collected;
|
|
604
|
+
logDroppedThinkingBlocks(session, metadata);
|
|
535
605
|
return ok(responseFromMessages({
|
|
536
|
-
...
|
|
606
|
+
...reply,
|
|
537
607
|
requestedModelId,
|
|
538
608
|
// Read after the drain: the metadata chunk that carries the
|
|
539
609
|
// served id reaches the capture only as the stream is consumed.
|
|
540
610
|
servedModelId: capture.servedModelId(),
|
|
611
|
+
provider: providerId,
|
|
541
612
|
}));
|
|
542
613
|
}
|
|
543
614
|
catch (e) {
|
|
@@ -551,6 +622,8 @@ export function createAiSdkProvider(deps) {
|
|
|
551
622
|
return new ResultAsync(run());
|
|
552
623
|
}
|
|
553
624
|
async function* chatStream(req, session, signal) {
|
|
625
|
+
const reasoning = effortOf(req);
|
|
626
|
+
const providerOptions = deps.providerOptionsFor?.({ session, reasoning });
|
|
554
627
|
const hookCall = { unsettled: false };
|
|
555
628
|
const retry = createRetry(signal, logger, maxRetries, suspendOn, hookCall);
|
|
556
629
|
const capture = captureServedModelId(deps.model);
|
|
@@ -583,8 +656,8 @@ export function createAiSdkProvider(deps) {
|
|
|
583
656
|
// no content, for example a keep-alive comment, produces no part,
|
|
584
657
|
// thus it feeds neither bound.
|
|
585
658
|
...(requestTimeoutMs !== undefined ? { timeout: { firstChunkMs: requestTimeoutMs, chunkMs: requestTimeoutMs } } : {}),
|
|
586
|
-
providerOptions
|
|
587
|
-
reasoning
|
|
659
|
+
providerOptions,
|
|
660
|
+
reasoning,
|
|
588
661
|
});
|
|
589
662
|
const iterator = result.fullStream[Symbol.asyncIterator]();
|
|
590
663
|
const first = await pullNextDelta(iterator);
|
|
@@ -612,11 +685,13 @@ export function createAiSdkProvider(deps) {
|
|
|
612
685
|
finishReason: await result.finishReason,
|
|
613
686
|
rawFinishReason: await result.rawFinishReason,
|
|
614
687
|
usage: await result.usage,
|
|
688
|
+
providerMetadata: (await result.finalStep).providerMetadata,
|
|
615
689
|
};
|
|
616
690
|
}
|
|
617
691
|
return { kind: "streaming", result, iterator, firstDelta: first.text };
|
|
618
692
|
});
|
|
619
693
|
if (opened.kind === "completed") {
|
|
694
|
+
logDroppedThinkingBlocks(session, opened.providerMetadata);
|
|
620
695
|
const response = responseFromMessages({
|
|
621
696
|
messages: opened.messages,
|
|
622
697
|
fallbackText: "",
|
|
@@ -625,6 +700,7 @@ export function createAiSdkProvider(deps) {
|
|
|
625
700
|
usage: opened.usage,
|
|
626
701
|
requestedModelId,
|
|
627
702
|
servedModelId: capture.servedModelId(),
|
|
703
|
+
provider: providerId,
|
|
628
704
|
});
|
|
629
705
|
yield { type: "done", response };
|
|
630
706
|
return;
|
|
@@ -655,7 +731,9 @@ export function createAiSdkProvider(deps) {
|
|
|
655
731
|
// Read after the drain: the metadata chunk carrying the served id
|
|
656
732
|
// reaches the capture only as the stream is consumed.
|
|
657
733
|
servedModelId: capture.servedModelId(),
|
|
734
|
+
provider: providerId,
|
|
658
735
|
});
|
|
736
|
+
logDroppedThinkingBlocks(session, (await result.finalStep).providerMetadata);
|
|
659
737
|
yield { type: "done", response };
|
|
660
738
|
}
|
|
661
739
|
catch (e) {
|
|
@@ -729,11 +807,10 @@ export function wrapFetchWithRequestTimeout(fetchImpl, requestTimeoutMs) {
|
|
|
729
807
|
*
|
|
730
808
|
* The middleware merges the value into the `openai` namespace of
|
|
731
809
|
* `providerOptions`. It keeps each other namespace, and it keeps each other key
|
|
732
|
-
* of the `openai` namespace, thus
|
|
733
|
-
* through untouched. The
|
|
734
|
-
*
|
|
735
|
-
*
|
|
736
|
-
* never stored.
|
|
810
|
+
* of the `openai` namespace, thus the session key of the call
|
|
811
|
+
* (`promptCacheKey`) rides through untouched. The retention mode belongs to the
|
|
812
|
+
* connection and not to one turn: a thread that mixes the two modes replays a
|
|
813
|
+
* reference onto an item that the server never stored.
|
|
737
814
|
*/
|
|
738
815
|
function withStoreDirective(model, store) {
|
|
739
816
|
return wrapLanguageModel({
|
|
@@ -783,6 +860,9 @@ export function createConfiguredAiSdkProvider(deps) {
|
|
|
783
860
|
// must declare the capability. A config value overrides this default in
|
|
784
861
|
// both directions.
|
|
785
862
|
const pictureDefault = config.baseURL === undefined ? { imageToolResults: true } : {};
|
|
863
|
+
// A model that can run without thinking gets no binding (see `thinkingWithBinding`).
|
|
864
|
+
const bindingMode = config.thinkingBinding ?? "drop_block";
|
|
865
|
+
const binding = bindingMode !== "off" && getModelCapabilities(config.model).rejectsThinkingDisabled ? bindingMode : undefined;
|
|
786
866
|
return createAiSdkProvider({
|
|
787
867
|
model: provider.chat(config.model),
|
|
788
868
|
...hostPolicyOf(deps),
|
|
@@ -791,6 +871,15 @@ export function createConfiguredAiSdkProvider(deps) {
|
|
|
791
871
|
...(config.maxOutputTokens !== undefined ? { maxOutputTokens: config.maxOutputTokens } : {}),
|
|
792
872
|
...(config.maxRetries !== undefined ? { maxRetries: config.maxRetries } : {}),
|
|
793
873
|
...(requestTimeoutMs !== undefined ? { requestTimeoutMs } : {}),
|
|
874
|
+
...(config.reasoning !== undefined ? { reasoning: config.reasoning } : {}),
|
|
875
|
+
// The package sends the key as `metadata.user_id`, and the binding as
|
|
876
|
+
// `thinking.block_binding`.
|
|
877
|
+
providerOptionsFor: ({ session, reasoning }) => ({
|
|
878
|
+
anthropic: {
|
|
879
|
+
metadata: { userId: sessionKeyDigestOf(session) },
|
|
880
|
+
...(binding !== undefined ? { thinking: thinkingWithBinding(reasoning, binding) } : {}),
|
|
881
|
+
},
|
|
882
|
+
}),
|
|
794
883
|
});
|
|
795
884
|
}
|
|
796
885
|
if (config.kind === "openai") {
|
|
@@ -823,6 +912,10 @@ export function createConfiguredAiSdkProvider(deps) {
|
|
|
823
912
|
maxOutputTokens: config.maxOutputTokens ?? "provider-maximum",
|
|
824
913
|
...(config.maxRetries !== undefined ? { maxRetries: config.maxRetries } : {}),
|
|
825
914
|
...(requestTimeoutMs !== undefined ? { requestTimeoutMs } : {}),
|
|
915
|
+
...(config.reasoning !== undefined ? { reasoning: config.reasoning } : {}),
|
|
916
|
+
// The package sends the key as `prompt_cache_key`. The store
|
|
917
|
+
// directive merges into the same namespace, and it keeps this key.
|
|
918
|
+
providerOptionsFor: ({ session }) => ({ openai: { promptCacheKey: sessionKeyDigestOf(session) } }),
|
|
826
919
|
});
|
|
827
920
|
}
|
|
828
921
|
const provider = createOpenAICompatible({
|
|
@@ -830,7 +923,12 @@ export function createConfiguredAiSdkProvider(deps) {
|
|
|
830
923
|
baseURL: config.baseURL,
|
|
831
924
|
apiKey: config.apiKey,
|
|
832
925
|
fetch: effectiveFetch,
|
|
926
|
+
// Each call of the provider streams, `chat` included. Without this flag
|
|
927
|
+
// the package asks for no usage on a stream, and the call reports none.
|
|
928
|
+
includeUsage: true,
|
|
833
929
|
});
|
|
930
|
+
// The compatible wire has no standard field for a session key, thus this arm
|
|
931
|
+
// gives no provider options of its own.
|
|
834
932
|
return createAiSdkProvider({
|
|
835
933
|
// The compatible package reads the neutral `reasoning` of the call, thus
|
|
836
934
|
// the seam needs no middleware to carry the depth into its namespace.
|
|
@@ -841,5 +939,6 @@ export function createConfiguredAiSdkProvider(deps) {
|
|
|
841
939
|
...(config.maxOutputTokens !== undefined ? { maxOutputTokens: config.maxOutputTokens } : {}),
|
|
842
940
|
...(config.maxRetries !== undefined ? { maxRetries: config.maxRetries } : {}),
|
|
843
941
|
...(requestTimeoutMs !== undefined ? { requestTimeoutMs } : {}),
|
|
942
|
+
...(config.reasoning !== undefined ? { reasoning: config.reasoning } : {}),
|
|
844
943
|
});
|
|
845
944
|
}
|
|
@@ -89,6 +89,8 @@ export type SuspendError = Extract<ProviderError, {
|
|
|
89
89
|
* and the non-enumerable `.cause` does not. Thus the walk also reads `.value`.
|
|
90
90
|
*/
|
|
91
91
|
export declare function findSuspendError(err: unknown): SuspendError | undefined;
|
|
92
|
+
/** Find a `ProviderError` of any kind, by the walk of {@link findSuspendError}. */
|
|
93
|
+
export declare function findProviderError(err: unknown): ProviderError | undefined;
|
|
92
94
|
/**
|
|
93
95
|
* Turn a caught SDK throwable into a `ProviderError` value. Routes through
|
|
94
96
|
* `classifyProviderError` with the `suspendOn` map of the provider, so the
|
package/dist/providers/errors.js
CHANGED
|
@@ -63,6 +63,11 @@ export function findSuspendError(err) {
|
|
|
63
63
|
const asSuspend = (value) => (isProviderError(value) && value.type === "suspend" ? value : undefined);
|
|
64
64
|
return findInCauseChain(err, (link) => asSuspend(link) ?? (typeof link === "object" && link !== null ? asSuspend(link.value) : undefined));
|
|
65
65
|
}
|
|
66
|
+
/** Find a `ProviderError` of any kind, by the walk of {@link findSuspendError}. */
|
|
67
|
+
export function findProviderError(err) {
|
|
68
|
+
const asProviderError = (value) => (isProviderError(value) ? value : undefined);
|
|
69
|
+
return findInCauseChain(err, (link) => asProviderError(link) ?? (typeof link === "object" && link !== null ? asProviderError(link.value) : undefined));
|
|
70
|
+
}
|
|
66
71
|
/**
|
|
67
72
|
* Turn a caught SDK throwable into a `ProviderError` value. Routes through
|
|
68
73
|
* `classifyProviderError` with the `suspendOn` map of the provider, so the
|
|
@@ -15,29 +15,17 @@
|
|
|
15
15
|
* error — on an OpenAI-compatible model, which is exactly what we want: that
|
|
16
16
|
* family does automatic server-side prefix caching and needs no directive.
|
|
17
17
|
*
|
|
18
|
-
* ## What the Anthropic namespace does, and where the
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* `
|
|
28
|
-
*
|
|
29
|
-
* right, and it is why this module used to do it. It is also invisible: an
|
|
30
|
-
* intermediary that counts breakpoints to stay under Anthropic's cap of four
|
|
31
|
-
* counts BLOCKS, so a top-level field is a breakpoint nothing on the way
|
|
32
|
-
* upstream can see. CLIProxyAPI — what the OSS CLI routes through on the Claude
|
|
33
|
-
* OAuth path — injects up to four block markers of its own and trims to four by
|
|
34
|
-
* that blind count, so the harness's invisible fifth breakpoint made every
|
|
35
|
-
* request past a thread's first turn fail with `A maximum of 4 blocks with
|
|
36
|
-
* cache_control may be provided. Found 5.` (HTTP 400, non-retryable — the thread
|
|
37
|
-
* is wedged, because the next turn rebuilds the same shape).
|
|
38
|
-
*
|
|
39
|
-
* A marker on a message block is counted, trimmed, and reasoned about by every
|
|
40
|
-
* hop. Keep it there. {@link withPromptCacheBreakpoint} is the only writer.
|
|
18
|
+
* ## What the Anthropic namespace does, and where the markers must go
|
|
19
|
+
*
|
|
20
|
+
* A request carries two markers, each on a block, never on the request itself:
|
|
21
|
+
* {@link withSystemPromptBreakpoint} at the end of the system prompt, and
|
|
22
|
+
* {@link withPromptCacheBreakpoint} at the last eligible message.
|
|
23
|
+
*
|
|
24
|
+
* A REQUEST-level `cacheControl` emits an invisible top-level field instead.
|
|
25
|
+
* CLIProxyAPI (the Claude OAuth path) counts BLOCKS toward Anthropic's cap of
|
|
26
|
+
* four, not this field, so the uncounted marker overflows it and fails with
|
|
27
|
+
* `A maximum of 4 blocks with cache_control may be provided. Found 5.`
|
|
28
|
+
* (HTTP 400, non-retryable — the thread stays wedged).
|
|
41
29
|
*
|
|
42
30
|
* ## Which vendors need a marker at all
|
|
43
31
|
*
|
|
@@ -54,9 +42,9 @@
|
|
|
54
42
|
* cache RESOURCE created out of band and referenced by name, which is not a
|
|
55
43
|
* marker and does not fit a ttl policy.
|
|
56
44
|
*
|
|
57
|
-
* So both markers ride together on
|
|
58
|
-
* its own namespace, thus the pair is free to whichever vendor is not
|
|
59
|
-
* the call, and the placement never learns which one that is.
|
|
45
|
+
* So both vendor markers ride together on each chosen message. A provider reads
|
|
46
|
+
* only its own namespace, thus the pair is free to whichever vendor is not
|
|
47
|
+
* serving the call, and the placement never learns which one that is.
|
|
60
48
|
*
|
|
61
49
|
* The asymmetry worth remembering: under-marking silently costs money, while
|
|
62
50
|
* over-marking hard-fails. Anthropic answers 400 past four breakpoints, and the
|
|
@@ -65,20 +53,18 @@
|
|
|
65
53
|
*
|
|
66
54
|
* ## Cache defeaters — what silently kills the hit rate
|
|
67
55
|
*
|
|
68
|
-
* The cache keys on an *exact prefix
|
|
69
|
-
* request invalidates everything after it.
|
|
70
|
-
*
|
|
56
|
+
* The cache keys on an *exact prefix*, so anything that perturbs the head of the
|
|
57
|
+
* request invalidates everything after it. The wrap-up of `runAgent`, the
|
|
58
|
+
* salvage of `runToTerminal`, and a step's summary and file-metadata
|
|
59
|
+
* continuations extend one conversation under a tool mask, thus they keep its
|
|
60
|
+
* prefix (`loop/run-agent.ts`, `loop/continue-agent.ts`). A tool choice that
|
|
61
|
+
* forbids a call would defeat the cache — see the CAUTION on `ChatRequest.toolChoice`.
|
|
71
62
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
63
|
+
* `loadRecent` gives the view of the latest compaction marker
|
|
64
|
+
* (`memory/conversation-view.ts`). A compaction moves the start of the view one
|
|
65
|
+
* time, and the message prefix holds still between two compactions.
|
|
75
66
|
*
|
|
76
|
-
*
|
|
77
|
-
* window one turn per turn, shifting the message prefix every request. It now
|
|
78
|
-
* evicts in whole blocks so the prefix holds still between block boundaries
|
|
79
|
-
* (`memory/thread-history.ts`).
|
|
80
|
-
*
|
|
81
|
-
* A sandbox agent's system prompt is NOT one of them: it is a pure function of
|
|
67
|
+
* A sandbox agent's system prompt is NOT a defeater: it is a pure function of
|
|
82
68
|
* its agent type, byte-identical across every step of every run, and the per-step
|
|
83
69
|
* paths ride in the step's briefing instead (`agents/sandbox/shared.ts`,
|
|
84
70
|
* `prompts/briefing.ts`). Keep it that way — one interpolated id or path there
|
|
@@ -106,6 +92,7 @@
|
|
|
106
92
|
* flat-zero read counter beside a non-zero write counter is still the signature
|
|
107
93
|
* of an endpoint billing for writes and serving no reads.
|
|
108
94
|
*/
|
|
95
|
+
import type { SystemModelMessage } from "ai";
|
|
109
96
|
import type { ModelMessage, PromptCachePolicy, ProviderOptions } from "./types.js";
|
|
110
97
|
/**
|
|
111
98
|
* The default policy: cache with the 5-minute TTL.
|
|
@@ -120,6 +107,12 @@ import type { ModelMessage, PromptCachePolicy, ProviderOptions } from "./types.j
|
|
|
120
107
|
* premium for a cache nothing ever reads.
|
|
121
108
|
*/
|
|
122
109
|
export declare const DEFAULT_PROMPT_CACHE: PromptCachePolicy;
|
|
110
|
+
/**
|
|
111
|
+
* The policy of the root loop of a chat turn only: a person can reply 5 to 60 minutes later, and a
|
|
112
|
+
* 5-minute entry does not last that gap. Each other loop keeps {@link DEFAULT_PROMPT_CACHE}, because
|
|
113
|
+
* its requests start less than 5 minutes apart.
|
|
114
|
+
*/
|
|
115
|
+
export declare const CONVERSATION_PROMPT_CACHE: PromptCachePolicy;
|
|
123
116
|
/**
|
|
124
117
|
* Translate a neutral cache policy into provider wire options.
|
|
125
118
|
*
|
|
@@ -141,11 +134,20 @@ export declare const DEFAULT_PROMPT_CACHE: PromptCachePolicy;
|
|
|
141
134
|
*
|
|
142
135
|
* CAUTION: this is the marker's VALUE, not its placement. Attaching it to a
|
|
143
136
|
* `ChatRequest` emits the invisible top-level field the module header warns
|
|
144
|
-
* about. Place it with {@link
|
|
137
|
+
* about. Place it with {@link withSystemPromptBreakpoint} and
|
|
138
|
+
* {@link withPromptCacheBreakpoint}.
|
|
145
139
|
*/
|
|
146
140
|
export declare function promptCacheProviderOptions(policy: PromptCachePolicy): ProviderOptions | undefined;
|
|
147
141
|
/**
|
|
148
|
-
*
|
|
142
|
+
* Marks the end of the system prompt, the sibling marker of {@link withPromptCacheBreakpoint}.
|
|
143
|
+
*
|
|
144
|
+
* Skips an empty prompt — the Anthropic package renders it as an empty text
|
|
145
|
+
* block, which cannot carry a marker.
|
|
146
|
+
*/
|
|
147
|
+
export declare function withSystemPromptBreakpoint(system: string, policy: PromptCachePolicy): string | SystemModelMessage;
|
|
148
|
+
/**
|
|
149
|
+
* Places the message-level breakpoint on the last eligible message — the sibling
|
|
150
|
+
* marker sits at the end of the system prompt ({@link withSystemPromptBreakpoint}).
|
|
149
151
|
*
|
|
150
152
|
* Returns a copy — the caller's array is the transcript the loop keeps pushing
|
|
151
153
|
* onto and the host later persists, and a marker written into it would ride into
|
|
@@ -160,7 +162,7 @@ export declare function promptCacheProviderOptions(policy: PromptCachePolicy): P
|
|
|
160
162
|
* what an agent loop needs.
|
|
161
163
|
*
|
|
162
164
|
* A marker already on any other message is REMOVED — the invariant is exactly one
|
|
163
|
-
* breakpoint
|
|
165
|
+
* breakpoint among the messages, and a stored message that carries one from an older
|
|
164
166
|
* build (`memory/ai-sdk-message-storage.ts` reads `cache_control` back off a
|
|
165
167
|
* stored block) would otherwise spend a slot that the harness never budgeted.
|
|
166
168
|
* `"off"` strips and places nothing, so turning caching off really does send no
|
|
@@ -15,29 +15,17 @@
|
|
|
15
15
|
* error — on an OpenAI-compatible model, which is exactly what we want: that
|
|
16
16
|
* family does automatic server-side prefix caching and needs no directive.
|
|
17
17
|
*
|
|
18
|
-
* ## What the Anthropic namespace does, and where the
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* `
|
|
28
|
-
*
|
|
29
|
-
* right, and it is why this module used to do it. It is also invisible: an
|
|
30
|
-
* intermediary that counts breakpoints to stay under Anthropic's cap of four
|
|
31
|
-
* counts BLOCKS, so a top-level field is a breakpoint nothing on the way
|
|
32
|
-
* upstream can see. CLIProxyAPI — what the OSS CLI routes through on the Claude
|
|
33
|
-
* OAuth path — injects up to four block markers of its own and trims to four by
|
|
34
|
-
* that blind count, so the harness's invisible fifth breakpoint made every
|
|
35
|
-
* request past a thread's first turn fail with `A maximum of 4 blocks with
|
|
36
|
-
* cache_control may be provided. Found 5.` (HTTP 400, non-retryable — the thread
|
|
37
|
-
* is wedged, because the next turn rebuilds the same shape).
|
|
38
|
-
*
|
|
39
|
-
* A marker on a message block is counted, trimmed, and reasoned about by every
|
|
40
|
-
* hop. Keep it there. {@link withPromptCacheBreakpoint} is the only writer.
|
|
18
|
+
* ## What the Anthropic namespace does, and where the markers must go
|
|
19
|
+
*
|
|
20
|
+
* A request carries two markers, each on a block, never on the request itself:
|
|
21
|
+
* {@link withSystemPromptBreakpoint} at the end of the system prompt, and
|
|
22
|
+
* {@link withPromptCacheBreakpoint} at the last eligible message.
|
|
23
|
+
*
|
|
24
|
+
* A REQUEST-level `cacheControl` emits an invisible top-level field instead.
|
|
25
|
+
* CLIProxyAPI (the Claude OAuth path) counts BLOCKS toward Anthropic's cap of
|
|
26
|
+
* four, not this field, so the uncounted marker overflows it and fails with
|
|
27
|
+
* `A maximum of 4 blocks with cache_control may be provided. Found 5.`
|
|
28
|
+
* (HTTP 400, non-retryable — the thread stays wedged).
|
|
41
29
|
*
|
|
42
30
|
* ## Which vendors need a marker at all
|
|
43
31
|
*
|
|
@@ -54,9 +42,9 @@
|
|
|
54
42
|
* cache RESOURCE created out of band and referenced by name, which is not a
|
|
55
43
|
* marker and does not fit a ttl policy.
|
|
56
44
|
*
|
|
57
|
-
* So both markers ride together on
|
|
58
|
-
* its own namespace, thus the pair is free to whichever vendor is not
|
|
59
|
-
* the call, and the placement never learns which one that is.
|
|
45
|
+
* So both vendor markers ride together on each chosen message. A provider reads
|
|
46
|
+
* only its own namespace, thus the pair is free to whichever vendor is not
|
|
47
|
+
* serving the call, and the placement never learns which one that is.
|
|
60
48
|
*
|
|
61
49
|
* The asymmetry worth remembering: under-marking silently costs money, while
|
|
62
50
|
* over-marking hard-fails. Anthropic answers 400 past four breakpoints, and the
|
|
@@ -65,20 +53,18 @@
|
|
|
65
53
|
*
|
|
66
54
|
* ## Cache defeaters — what silently kills the hit rate
|
|
67
55
|
*
|
|
68
|
-
* The cache keys on an *exact prefix
|
|
69
|
-
* request invalidates everything after it.
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* the cache from scratch (`loop/run-agent.ts`).
|
|
56
|
+
* The cache keys on an *exact prefix*, so anything that perturbs the head of the
|
|
57
|
+
* request invalidates everything after it. The wrap-up of `runAgent`, the
|
|
58
|
+
* salvage of `runToTerminal`, and a step's summary and file-metadata
|
|
59
|
+
* continuations extend one conversation under a tool mask, thus they keep its
|
|
60
|
+
* prefix (`loop/run-agent.ts`, `loop/continue-agent.ts`). A tool choice that
|
|
61
|
+
* forbids a call would defeat the cache — see the CAUTION on `ChatRequest.toolChoice`.
|
|
75
62
|
*
|
|
76
|
-
* `loadRecent`
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* (`memory/thread-history.ts`).
|
|
63
|
+
* `loadRecent` gives the view of the latest compaction marker
|
|
64
|
+
* (`memory/conversation-view.ts`). A compaction moves the start of the view one
|
|
65
|
+
* time, and the message prefix holds still between two compactions.
|
|
80
66
|
*
|
|
81
|
-
* A sandbox agent's system prompt is NOT
|
|
67
|
+
* A sandbox agent's system prompt is NOT a defeater: it is a pure function of
|
|
82
68
|
* its agent type, byte-identical across every step of every run, and the per-step
|
|
83
69
|
* paths ride in the step's briefing instead (`agents/sandbox/shared.ts`,
|
|
84
70
|
* `prompts/briefing.ts`). Keep it that way — one interpolated id or path there
|
|
@@ -119,6 +105,12 @@
|
|
|
119
105
|
* premium for a cache nothing ever reads.
|
|
120
106
|
*/
|
|
121
107
|
export const DEFAULT_PROMPT_CACHE = { ttl: "5m" };
|
|
108
|
+
/**
|
|
109
|
+
* The policy of the root loop of a chat turn only: a person can reply 5 to 60 minutes later, and a
|
|
110
|
+
* 5-minute entry does not last that gap. Each other loop keeps {@link DEFAULT_PROMPT_CACHE}, because
|
|
111
|
+
* its requests start less than 5 minutes apart.
|
|
112
|
+
*/
|
|
113
|
+
export const CONVERSATION_PROMPT_CACHE = { ttl: "1h" };
|
|
122
114
|
/**
|
|
123
115
|
* Translate a neutral cache policy into provider wire options.
|
|
124
116
|
*
|
|
@@ -140,7 +132,8 @@ export const DEFAULT_PROMPT_CACHE = { ttl: "5m" };
|
|
|
140
132
|
*
|
|
141
133
|
* CAUTION: this is the marker's VALUE, not its placement. Attaching it to a
|
|
142
134
|
* `ChatRequest` emits the invisible top-level field the module header warns
|
|
143
|
-
* about. Place it with {@link
|
|
135
|
+
* about. Place it with {@link withSystemPromptBreakpoint} and
|
|
136
|
+
* {@link withPromptCacheBreakpoint}.
|
|
144
137
|
*/
|
|
145
138
|
export function promptCacheProviderOptions(policy) {
|
|
146
139
|
if (policy === "off")
|
|
@@ -149,6 +142,18 @@ export function promptCacheProviderOptions(policy) {
|
|
|
149
142
|
const bedrock = { cachePoint: { type: "default", ttl: policy.ttl } };
|
|
150
143
|
return { anthropic, bedrock };
|
|
151
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Marks the end of the system prompt, the sibling marker of {@link withPromptCacheBreakpoint}.
|
|
147
|
+
*
|
|
148
|
+
* Skips an empty prompt — the Anthropic package renders it as an empty text
|
|
149
|
+
* block, which cannot carry a marker.
|
|
150
|
+
*/
|
|
151
|
+
export function withSystemPromptBreakpoint(system, policy) {
|
|
152
|
+
const providerOptions = promptCacheProviderOptions(policy);
|
|
153
|
+
if (providerOptions === undefined || system === "")
|
|
154
|
+
return system;
|
|
155
|
+
return { role: "system", content: system, providerOptions };
|
|
156
|
+
}
|
|
152
157
|
/**
|
|
153
158
|
* Every namespace that can carry a breakpoint, and the key it carries it under.
|
|
154
159
|
*
|
|
@@ -229,7 +234,8 @@ function withoutBreakpoint(message) {
|
|
|
229
234
|
return { ...message, providerOptions: remaining };
|
|
230
235
|
}
|
|
231
236
|
/**
|
|
232
|
-
*
|
|
237
|
+
* Places the message-level breakpoint on the last eligible message — the sibling
|
|
238
|
+
* marker sits at the end of the system prompt ({@link withSystemPromptBreakpoint}).
|
|
233
239
|
*
|
|
234
240
|
* Returns a copy — the caller's array is the transcript the loop keeps pushing
|
|
235
241
|
* onto and the host later persists, and a marker written into it would ride into
|
|
@@ -244,7 +250,7 @@ function withoutBreakpoint(message) {
|
|
|
244
250
|
* what an agent loop needs.
|
|
245
251
|
*
|
|
246
252
|
* A marker already on any other message is REMOVED — the invariant is exactly one
|
|
247
|
-
* breakpoint
|
|
253
|
+
* breakpoint among the messages, and a stored message that carries one from an older
|
|
248
254
|
* build (`memory/ai-sdk-message-storage.ts` reads `cache_control` back off a
|
|
249
255
|
* stored block) would otherwise spend a slot that the harness never budgeted.
|
|
250
256
|
* `"off"` strips and places nothing, so turning caching off really does send no
|
|
@@ -1,35 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The reasoning depth of a
|
|
2
|
+
* The reasoning depth of a call.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The effort resolves in order: `ChatRequest.reasoning` (set only through
|
|
5
|
+
* `RunAgentOptions.reasoning`), the provider configuration's `reasoning`, then
|
|
6
|
+
* {@link DEFAULT_REASONING}. Anthropic discards its message cache when the
|
|
7
|
+
* top-level effort changes between calls.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* The harness wrote `providerOptions.anthropic.effort` before. A value on that
|
|
12
|
-
* key turns the per-model table of the Anthropic package off, thus the raw name
|
|
13
|
-
* reached the wire. A model that accepts `high` but not `xhigh` answered 400.
|
|
14
|
-
* The neutral field has no such hazard, because the package resolves the name
|
|
15
|
-
* for the model that it is bound to.
|
|
16
|
-
*
|
|
17
|
-
* ## Where the policy belongs
|
|
18
|
-
*
|
|
19
|
-
* The policy rides on the run, not on the provider, for the same reason that the
|
|
20
|
-
* cache policy does. A one-shot LLM call elsewhere in the harness has its own
|
|
21
|
-
* depth needs, and it must not inherit the depth of an agent loop.
|
|
9
|
+
* Do not set `providerOptions.anthropic.effort` directly. It bypasses the
|
|
10
|
+
* per-model table, and a model that rejects the raw name answers 400.
|
|
22
11
|
*/
|
|
23
12
|
import type { ReasoningPolicy } from "./types.js";
|
|
24
13
|
/**
|
|
25
|
-
* The
|
|
14
|
+
* The last source of the effort: the deepest rung of the neutral ladder.
|
|
26
15
|
*
|
|
27
16
|
* Each agent of the harness drives tools over many iterations. A shallow turn
|
|
28
17
|
* there wastes more calls than a deeper turn costs in tokens. The Anthropic
|
|
29
18
|
* package sends `xhigh` to a model that accepts it, and `max` to a model that
|
|
30
19
|
* does not. The OpenAI-compatible package sends the name as it is.
|
|
31
20
|
*
|
|
32
|
-
* A host that wants a cheaper
|
|
33
|
-
* no reasoning support
|
|
21
|
+
* A host that wants a cheaper role sets a lower name on that role's provider
|
|
22
|
+
* configuration. With no reasoning support, a host sets `"provider-default"`.
|
|
34
23
|
*/
|
|
35
24
|
export declare const DEFAULT_REASONING: ReasoningPolicy;
|