@inflexa-ai/harness 0.14.1 → 0.15.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 -1
- package/dist/agents/conversation-agent.d.ts +7 -0
- package/dist/agents/conversation-agent.js +5 -3
- package/dist/app/synthesize-run.d.ts +3 -0
- package/dist/app/synthesize-run.js +2 -1
- package/dist/billing/noop-usage-recorder.d.ts +7 -0
- package/dist/billing/noop-usage-recorder.js +10 -0
- package/dist/billing/usage-recorder.d.ts +63 -0
- package/dist/billing/usage-recorder.js +15 -0
- package/dist/contracts/chat-events.d.ts +44 -3
- package/dist/contracts/chat-parts.d.ts +38 -2
- package/dist/contracts/chat-parts.js +1 -1
- package/dist/contracts/data-profile.d.ts +26 -0
- package/dist/contracts/data-profile.js +26 -0
- package/dist/contracts/index.d.ts +3 -1
- package/dist/contracts/index.js +1 -0
- package/dist/contracts/message.d.ts +21 -1
- package/dist/contracts/part-registry.js +1 -0
- package/dist/contracts/schemas/chat-events.d.ts +48 -2
- package/dist/contracts/schemas/chat-events.js +8 -1
- package/dist/contracts/schemas/chat-parts.d.ts +46 -3
- package/dist/contracts/schemas/chat-parts.js +13 -0
- package/dist/contracts/schemas/index.d.ts +2 -1
- package/dist/contracts/schemas/index.js +2 -1
- package/dist/contracts/schemas/usage.d.ts +11 -0
- package/dist/contracts/schemas/usage.js +18 -0
- package/dist/contracts/usage.d.ts +51 -0
- package/dist/contracts/usage.js +41 -0
- package/dist/execution/artifact-metadata.d.ts +3 -0
- package/dist/execution/artifact-metadata.js +1 -0
- package/dist/execution/dbos-workflow-purger.d.ts +30 -0
- package/dist/execution/dbos-workflow-purger.js +193 -0
- package/dist/execution/post-step-pipeline.d.ts +3 -0
- package/dist/execution/post-step-pipeline.js +2 -0
- package/dist/execution/reconcile-manifest.js +28 -15
- package/dist/execution/report-runner.d.ts +16 -0
- package/dist/execution/report-runner.js +4 -0
- package/dist/execution/run-synthesis.d.ts +3 -0
- package/dist/execution/run-synthesis.js +2 -0
- package/dist/execution/step-summary.d.ts +3 -0
- package/dist/execution/step-summary.js +1 -0
- package/dist/execution/workflow-purger.d.ts +38 -0
- package/dist/execution/workflow-purger.js +24 -0
- package/dist/index.d.ts +25 -3
- package/dist/index.js +28 -1
- package/dist/lib/metrics.js +2 -1
- package/dist/loop/metrics.d.ts +18 -0
- package/dist/loop/metrics.js +18 -1
- package/dist/loop/run-agent.d.ts +35 -0
- package/dist/loop/run-agent.js +149 -23
- package/dist/loop/run-to-terminal.d.ts +5 -2
- package/dist/loop/run-to-terminal.js +36 -2
- package/dist/loop/tool-detail.d.ts +54 -0
- package/dist/loop/tool-detail.js +105 -0
- package/dist/loop/tool-outcome.d.ts +21 -0
- package/dist/loop/tool-outcome.js +25 -0
- package/dist/loop/types.d.ts +15 -1
- package/dist/memory/content-to-cortex.d.ts +28 -4
- package/dist/memory/content-to-cortex.js +70 -14
- package/dist/memory/thread-history.d.ts +21 -1
- package/dist/memory/thread-history.js +29 -7
- package/dist/memory/thread-store.d.ts +65 -8
- package/dist/memory/thread-store.js +81 -17
- package/dist/provenance/collector.d.ts +5 -3
- package/dist/provenance/collector.js +5 -3
- package/dist/providers/ai-sdk.d.ts +18 -0
- package/dist/providers/ai-sdk.js +129 -16
- package/dist/providers/anthropic.d.ts +2 -0
- package/dist/providers/anthropic.js +1 -0
- package/dist/providers/streaming-chat.js +5 -0
- package/dist/providers/types.d.ts +23 -0
- package/dist/providers/types.js +1 -0
- package/dist/runtime/assemble.d.ts +25 -8
- package/dist/runtime/assemble.js +7 -4
- package/dist/sandbox/sandbox-step-translate.d.ts +22 -1
- package/dist/sandbox/sandbox-step-translate.js +23 -36
- package/dist/schemas/run-synthesis.d.ts +2 -2
- package/dist/state/index.d.ts +2 -0
- package/dist/state/index.js +3 -0
- package/dist/state/init.js +28 -0
- package/dist/state/purge-analysis.d.ts +74 -0
- package/dist/state/purge-analysis.js +205 -0
- package/dist/state/vector-store.d.ts +6 -0
- package/dist/state/vector-store.js +22 -2
- package/dist/tasks/data-profile-activity.d.ts +14 -2
- package/dist/tasks/data-profile-activity.js +1 -1
- package/dist/tasks/data-profile.d.ts +3 -0
- package/dist/tasks/data-profile.js +9 -3
- package/dist/tools/bio/pubmed.js +15 -0
- package/dist/tools/bio/search-gene.js +12 -0
- package/dist/tools/define-tool.d.ts +41 -3
- package/dist/tools/define-tool.js +4 -1
- package/dist/tools/detail-resolver.d.ts +35 -0
- package/dist/tools/detail-resolver.js +39 -0
- package/dist/tools/execute-analysis.js +3 -0
- package/dist/tools/iterate-report.d.ts +3 -0
- package/dist/tools/iterate-report.js +3 -0
- package/dist/tools/memory/update-working-memory.js +8 -0
- package/dist/tools/research/generate-analogy-report.d.ts +3 -0
- package/dist/tools/research/generate-analogy-report.js +6 -0
- package/dist/tools/research/generate-plan.d.ts +3 -0
- package/dist/tools/research/generate-plan.js +6 -0
- package/dist/tools/research/inspect-run.js +4 -0
- package/dist/tools/research/literature-reviewer.d.ts +3 -0
- package/dist/tools/research/literature-reviewer.js +6 -0
- package/dist/tools/workspace/edit-file.js +2 -0
- package/dist/tools/workspace/execute-command.js +25 -0
- package/dist/tools/workspace/grep.js +3 -0
- package/dist/tools/workspace/list-files.js +2 -0
- package/dist/tools/workspace/read-file.js +7 -0
- package/dist/tools/workspace/workspace-search.js +1 -0
- package/dist/tools/workspace/write-file.js +3 -0
- package/dist/workflows/execute-analysis.d.ts +7 -0
- package/dist/workflows/execute-analysis.js +31 -2
- package/dist/workflows/execute-target-assessment.d.ts +3 -0
- package/dist/workflows/sandbox-step.d.ts +23 -0
- package/dist/workflows/sandbox-step.js +33 -2
- package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.d.ts +3 -0
- package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.js +1 -0
- package/package.json +2 -2
|
@@ -13,11 +13,25 @@ export declare const RETRY_MAX_RETRIES = 10;
|
|
|
13
13
|
export declare const RETRY_INITIAL_DELAY_MS = 2000;
|
|
14
14
|
export declare const RETRY_BACKOFF_FACTOR = 2;
|
|
15
15
|
export declare const RETRY_MAX_DELAY_MS = 30000;
|
|
16
|
+
/**
|
|
17
|
+
* Output-token ceiling requested when a config names none — a floor against a
|
|
18
|
+
* provider default, not a target. `@ai-sdk/anthropic` resolves an unset value
|
|
19
|
+
* from a capability table that lags Anthropic's releases and falls back to 4096
|
|
20
|
+
* for ids it does not know, so the newest models silently truncate mid
|
|
21
|
+
* tool-call. The SDK clamps this down per known model (opus-4-x to 32k,
|
|
22
|
+
* claude-3-haiku to 4096), so it cannot over-request one it recognizes.
|
|
23
|
+
*
|
|
24
|
+
* Lower it per-config for servers that validate `prompt + max_tokens <= context`
|
|
25
|
+
* (vLLM and similar) against a small-context model.
|
|
26
|
+
*/
|
|
27
|
+
export declare const DEFAULT_MAX_OUTPUT_TOKENS = 64000;
|
|
16
28
|
export interface AiSdkProviderDeps {
|
|
17
29
|
readonly model: LanguageModel;
|
|
18
30
|
readonly resolveBilling: ResolveBilling;
|
|
19
31
|
readonly capabilities?: Partial<ProviderCapabilities>;
|
|
20
32
|
readonly logger?: Logger;
|
|
33
|
+
/** Output-token ceiling per request. Defaults to {@link DEFAULT_MAX_OUTPUT_TOKENS}. */
|
|
34
|
+
readonly maxOutputTokens?: number;
|
|
21
35
|
}
|
|
22
36
|
/**
|
|
23
37
|
* Connection + model configuration for one `ChatProvider`, discriminated by
|
|
@@ -36,6 +50,8 @@ export type AiSdkProviderConfig = {
|
|
|
36
50
|
readonly model: string;
|
|
37
51
|
readonly fetch?: FetchLike;
|
|
38
52
|
readonly capabilities?: Partial<ProviderCapabilities>;
|
|
53
|
+
/** Output-token ceiling per request. Defaults to {@link DEFAULT_MAX_OUTPUT_TOKENS}. */
|
|
54
|
+
readonly maxOutputTokens?: number;
|
|
39
55
|
} | {
|
|
40
56
|
readonly kind: "openai-compatible";
|
|
41
57
|
readonly name: string;
|
|
@@ -44,6 +60,8 @@ export type AiSdkProviderConfig = {
|
|
|
44
60
|
readonly model: string;
|
|
45
61
|
readonly fetch?: FetchLike;
|
|
46
62
|
readonly capabilities?: Partial<ProviderCapabilities>;
|
|
63
|
+
/** Output-token ceiling per request. Defaults to {@link DEFAULT_MAX_OUTPUT_TOKENS}. */
|
|
64
|
+
readonly maxOutputTokens?: number;
|
|
47
65
|
};
|
|
48
66
|
export interface ConfiguredAiSdkProviderDeps {
|
|
49
67
|
readonly config: AiSdkProviderConfig;
|
package/dist/providers/ai-sdk.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { generateText, streamText } from "ai";
|
|
1
|
+
import { generateText, streamText, wrapLanguageModel } from "ai";
|
|
2
2
|
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
3
3
|
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
4
4
|
import { APICallError } from "@ai-sdk/provider";
|
|
@@ -18,6 +18,38 @@ export const RETRY_MAX_RETRIES = 10;
|
|
|
18
18
|
export const RETRY_INITIAL_DELAY_MS = 2_000;
|
|
19
19
|
export const RETRY_BACKOFF_FACTOR = 2;
|
|
20
20
|
export const RETRY_MAX_DELAY_MS = 30_000;
|
|
21
|
+
/**
|
|
22
|
+
* Output-token ceiling requested when a config names none — a floor against a
|
|
23
|
+
* provider default, not a target. `@ai-sdk/anthropic` resolves an unset value
|
|
24
|
+
* from a capability table that lags Anthropic's releases and falls back to 4096
|
|
25
|
+
* for ids it does not know, so the newest models silently truncate mid
|
|
26
|
+
* tool-call. The SDK clamps this down per known model (opus-4-x to 32k,
|
|
27
|
+
* claude-3-haiku to 4096), so it cannot over-request one it recognizes.
|
|
28
|
+
*
|
|
29
|
+
* Lower it per-config for servers that validate `prompt + max_tokens <= context`
|
|
30
|
+
* (vLLM and similar) against a small-context model.
|
|
31
|
+
*/
|
|
32
|
+
export const DEFAULT_MAX_OUTPUT_TOKENS = 64_000;
|
|
33
|
+
/**
|
|
34
|
+
* Point the AI SDK's warning logger at the harness `Logger`.
|
|
35
|
+
*
|
|
36
|
+
* The SDK writes warnings itself — `process.emitWarning`, else `console.warn` —
|
|
37
|
+
* and offers only this process-global hook to redirect them. Left alone it
|
|
38
|
+
* bypasses the Logger seam entirely and lands on the stdout a TUI host owns;
|
|
39
|
+
* `maxOutputTokens` makes that routine, since the SDK warns on every request
|
|
40
|
+
* whose ceiling it clamps. Installed once: the handler is global, and every
|
|
41
|
+
* provider over a boot shares one sink.
|
|
42
|
+
*/
|
|
43
|
+
function routeSdkWarningsTo(logger) {
|
|
44
|
+
const g = globalThis;
|
|
45
|
+
if (g.AI_SDK_LOG_WARNINGS !== undefined)
|
|
46
|
+
return;
|
|
47
|
+
g.AI_SDK_LOG_WARNINGS = (options) => {
|
|
48
|
+
for (const warning of options.warnings) {
|
|
49
|
+
logger.warn("ai sdk warning", { provider: options.provider, model: options.model, warning });
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
21
53
|
function workloadOf(session) {
|
|
22
54
|
return `${session.scope.kind}:${scopeWorkloadId(session.scope)}`;
|
|
23
55
|
}
|
|
@@ -190,6 +222,10 @@ function createRetry(signal, logger) {
|
|
|
190
222
|
* vendor's own snake_case.
|
|
191
223
|
*
|
|
192
224
|
* `inputTokens` is the total billed prefix, cache reads included.
|
|
225
|
+
*
|
|
226
|
+
* `reasoningTokens` rides the neutral `outputTokenDetails` beside the text
|
|
227
|
+
* count and is copied verbatim: providers disagree on whether reasoning tokens
|
|
228
|
+
* are already inside the output total, so the harness reconciles nothing.
|
|
193
229
|
*/
|
|
194
230
|
function toChatUsage(usage) {
|
|
195
231
|
if (usage === undefined)
|
|
@@ -199,22 +235,72 @@ function toChatUsage(usage) {
|
|
|
199
235
|
outputTokens: usage.outputTokens,
|
|
200
236
|
cacheCreationInputTokens: usage.inputTokenDetails?.cacheWriteTokens,
|
|
201
237
|
cacheReadInputTokens: usage.inputTokenDetails?.cacheReadTokens,
|
|
238
|
+
reasoningTokens: usage.outputTokenDetails?.reasoningTokens,
|
|
202
239
|
};
|
|
203
240
|
}
|
|
204
|
-
|
|
241
|
+
/** The id of the model bound into this provider — a bare id string is its own id. */
|
|
242
|
+
function requestedModelIdOf(model) {
|
|
243
|
+
return typeof model === "string" ? model : model.modelId;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Capture the served model id at the raw model boundary, because that is the
|
|
247
|
+
* only place it is still distinguishable from the requested one: `generateText`
|
|
248
|
+
* and `streamText` both coalesce an unreported response model id to the bound
|
|
249
|
+
* model's own id, so reading `result.response.modelId` would silently republish
|
|
250
|
+
* the requested id as an endpoint's claim. A middleware sees the provider's
|
|
251
|
+
* result before that fallback applies, so "reported nothing" stays absent.
|
|
252
|
+
*
|
|
253
|
+
* Built per call, never per provider: one provider instance serves concurrent
|
|
254
|
+
* calls, and a shared mutable slot would hand one call another's answer. A bare
|
|
255
|
+
* model-id string has no wire result to observe, so it captures nothing.
|
|
256
|
+
*/
|
|
257
|
+
function captureServedModelId(model) {
|
|
258
|
+
if (typeof model === "string")
|
|
259
|
+
return { model, servedModelId: () => undefined };
|
|
260
|
+
let served;
|
|
261
|
+
const wrapped = wrapLanguageModel({
|
|
262
|
+
model,
|
|
263
|
+
middleware: {
|
|
264
|
+
wrapGenerate: async ({ doGenerate }) => {
|
|
265
|
+
const result = await doGenerate();
|
|
266
|
+
served = result.response?.modelId;
|
|
267
|
+
return result;
|
|
268
|
+
},
|
|
269
|
+
wrapStream: async ({ doStream }) => {
|
|
270
|
+
const result = await doStream();
|
|
271
|
+
return {
|
|
272
|
+
...result,
|
|
273
|
+
stream: result.stream.pipeThrough(new TransformStream({
|
|
274
|
+
transform(chunk, controller) {
|
|
275
|
+
if (chunk.type === "response-metadata" && chunk.modelId !== undefined)
|
|
276
|
+
served = chunk.modelId;
|
|
277
|
+
controller.enqueue(chunk);
|
|
278
|
+
},
|
|
279
|
+
})),
|
|
280
|
+
};
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
return { model: wrapped, servedModelId: () => served };
|
|
285
|
+
}
|
|
286
|
+
function responseFromMessages(input) {
|
|
287
|
+
const { messages, fallbackText, finishReason, rawFinishReason, requestedModelId, servedModelId } = input;
|
|
288
|
+
const usage = toChatUsage(input.usage);
|
|
205
289
|
const message = [...messages].reverse().find((m) => m.role === "assistant");
|
|
206
290
|
if (message === undefined) {
|
|
207
|
-
return {
|
|
208
|
-
message: { role: "assistant", content: fallbackText },
|
|
209
|
-
finishReason,
|
|
210
|
-
rawFinishReason,
|
|
211
|
-
usage: toChatUsage(usage),
|
|
212
|
-
};
|
|
291
|
+
return { message: { role: "assistant", content: fallbackText }, finishReason, rawFinishReason, usage, requestedModelId, servedModelId };
|
|
213
292
|
}
|
|
214
|
-
return { message, finishReason, rawFinishReason, usage
|
|
293
|
+
return { message, finishReason, rawFinishReason, usage, requestedModelId, servedModelId };
|
|
215
294
|
}
|
|
216
|
-
function responseFromGenerate(result) {
|
|
217
|
-
return responseFromMessages(
|
|
295
|
+
function responseFromGenerate(result, identity) {
|
|
296
|
+
return responseFromMessages({
|
|
297
|
+
messages: result.responseMessages,
|
|
298
|
+
fallbackText: result.text,
|
|
299
|
+
finishReason: result.finishReason,
|
|
300
|
+
rawFinishReason: result.rawFinishReason,
|
|
301
|
+
usage: result.usage,
|
|
302
|
+
...identity,
|
|
303
|
+
});
|
|
218
304
|
}
|
|
219
305
|
/**
|
|
220
306
|
* Drop empty text parts — and messages left with no content at all — before
|
|
@@ -246,20 +332,25 @@ function sanitizeMessages(messages) {
|
|
|
246
332
|
export function createAiSdkProvider(deps) {
|
|
247
333
|
const capabilities = { toolCalling: deps.capabilities?.toolCalling ?? true };
|
|
248
334
|
const logger = (deps.logger ?? createNoopLogger()).named("providers.ai-sdk");
|
|
335
|
+
const maxOutputTokens = deps.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS;
|
|
336
|
+
const requestedModelId = requestedModelIdOf(deps.model);
|
|
337
|
+
routeSdkWarningsTo(logger);
|
|
249
338
|
function chat(req, session, signal) {
|
|
250
339
|
const run = async () => {
|
|
251
340
|
const retry = createRetry(signal, logger);
|
|
341
|
+
const capture = captureServedModelId(deps.model);
|
|
252
342
|
try {
|
|
253
343
|
const result = await retry(async () => {
|
|
254
344
|
// Attribution headers are time-limited; resolving them inside the
|
|
255
345
|
// retried closure keeps them fresh across a multi-minute window.
|
|
256
346
|
const headers = await resolveBillingHeaders(deps.resolveBilling, session);
|
|
257
347
|
return generateText({
|
|
258
|
-
model:
|
|
348
|
+
model: capture.model,
|
|
259
349
|
system: req.system,
|
|
260
350
|
messages: sanitizeMessages(req.messages),
|
|
261
351
|
tools: req.tools,
|
|
262
352
|
toolChoice: req.toolChoice ?? "auto",
|
|
353
|
+
maxOutputTokens,
|
|
263
354
|
stopWhen: [],
|
|
264
355
|
// The harness envelope owns retries; leaving the SDK default in
|
|
265
356
|
// place would multiply attempts (10 outer × 3 inner).
|
|
@@ -269,7 +360,7 @@ export function createAiSdkProvider(deps) {
|
|
|
269
360
|
providerOptions: req.providerOptions,
|
|
270
361
|
});
|
|
271
362
|
});
|
|
272
|
-
return ok(responseFromGenerate(result));
|
|
363
|
+
return ok(responseFromGenerate(result, { requestedModelId, servedModelId: capture.servedModelId() }));
|
|
273
364
|
}
|
|
274
365
|
catch (e) {
|
|
275
366
|
if (isAbortError(e) || signal?.aborted)
|
|
@@ -281,6 +372,7 @@ export function createAiSdkProvider(deps) {
|
|
|
281
372
|
}
|
|
282
373
|
async function* chatStream(req, session, signal) {
|
|
283
374
|
const retry = createRetry(signal, logger);
|
|
375
|
+
const capture = captureServedModelId(deps.model);
|
|
284
376
|
try {
|
|
285
377
|
// Retry covers only stream establishment: streamText defers wire errors
|
|
286
378
|
// to consumption, so a failure is not visible until the stream is read.
|
|
@@ -293,11 +385,12 @@ export function createAiSdkProvider(deps) {
|
|
|
293
385
|
const opened = await retry(async () => {
|
|
294
386
|
const headers = await resolveBillingHeaders(deps.resolveBilling, session);
|
|
295
387
|
const result = streamText({
|
|
296
|
-
model:
|
|
388
|
+
model: capture.model,
|
|
297
389
|
system: req.system,
|
|
298
390
|
messages: sanitizeMessages(req.messages),
|
|
299
391
|
tools: req.tools,
|
|
300
392
|
toolChoice: req.toolChoice ?? "auto",
|
|
393
|
+
maxOutputTokens,
|
|
301
394
|
stopWhen: [],
|
|
302
395
|
maxRetries: 0,
|
|
303
396
|
headers,
|
|
@@ -328,7 +421,15 @@ export function createAiSdkProvider(deps) {
|
|
|
328
421
|
return { kind: "streaming", result, iterator, firstDelta: first.value };
|
|
329
422
|
});
|
|
330
423
|
if (opened.kind === "completed") {
|
|
331
|
-
const response = responseFromMessages(
|
|
424
|
+
const response = responseFromMessages({
|
|
425
|
+
messages: opened.messages,
|
|
426
|
+
fallbackText: "",
|
|
427
|
+
finishReason: opened.finishReason,
|
|
428
|
+
rawFinishReason: opened.rawFinishReason,
|
|
429
|
+
usage: opened.usage,
|
|
430
|
+
requestedModelId,
|
|
431
|
+
servedModelId: capture.servedModelId(),
|
|
432
|
+
});
|
|
332
433
|
yield { type: "done", response };
|
|
333
434
|
return;
|
|
334
435
|
}
|
|
@@ -341,7 +442,17 @@ export function createAiSdkProvider(deps) {
|
|
|
341
442
|
text += next.value;
|
|
342
443
|
yield { type: "text-delta", text: next.value };
|
|
343
444
|
}
|
|
344
|
-
const response = responseFromMessages(
|
|
445
|
+
const response = responseFromMessages({
|
|
446
|
+
messages: await result.responseMessages,
|
|
447
|
+
fallbackText: text,
|
|
448
|
+
finishReason: await result.finishReason,
|
|
449
|
+
rawFinishReason: await result.rawFinishReason,
|
|
450
|
+
usage: await result.usage,
|
|
451
|
+
requestedModelId,
|
|
452
|
+
// Read after the drain: the metadata chunk carrying the served id
|
|
453
|
+
// reaches the capture only as the stream is consumed.
|
|
454
|
+
servedModelId: capture.servedModelId(),
|
|
455
|
+
});
|
|
345
456
|
yield { type: "done", response };
|
|
346
457
|
}
|
|
347
458
|
catch (e) {
|
|
@@ -372,6 +483,7 @@ export function createConfiguredAiSdkProvider(deps) {
|
|
|
372
483
|
resolveBilling: deps.resolveBilling,
|
|
373
484
|
capabilities: config.capabilities,
|
|
374
485
|
logger: deps.logger,
|
|
486
|
+
...(config.maxOutputTokens !== undefined ? { maxOutputTokens: config.maxOutputTokens } : {}),
|
|
375
487
|
});
|
|
376
488
|
}
|
|
377
489
|
const provider = createOpenAICompatible({
|
|
@@ -385,5 +497,6 @@ export function createConfiguredAiSdkProvider(deps) {
|
|
|
385
497
|
resolveBilling: deps.resolveBilling,
|
|
386
498
|
capabilities: config.capabilities,
|
|
387
499
|
logger: deps.logger,
|
|
500
|
+
...(config.maxOutputTokens !== undefined ? { maxOutputTokens: config.maxOutputTokens } : {}),
|
|
388
501
|
});
|
|
389
502
|
}
|
|
@@ -14,6 +14,8 @@ export interface AnthropicProviderDeps {
|
|
|
14
14
|
readonly resolveBilling: ResolveBilling;
|
|
15
15
|
readonly fetch?: FetchLike;
|
|
16
16
|
readonly logger?: Logger;
|
|
17
|
+
/** Output-token ceiling per request. Defaults to `DEFAULT_MAX_OUTPUT_TOKENS`. */
|
|
18
|
+
readonly maxOutputTokens?: number;
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
19
21
|
* Convenience over the `anthropic` arm of the public `AiSdkProviderConfig`
|
|
@@ -67,6 +67,11 @@ export function createStreamingChat(provider, onText) {
|
|
|
67
67
|
// response is authoritative, so return it rather than the assembled deltas.
|
|
68
68
|
if (final !== undefined)
|
|
69
69
|
return ok(final);
|
|
70
|
+
// The partial carries no model identity and no usage: it is
|
|
71
|
+
// assembled from deltas rather than from a provider response, so
|
|
72
|
+
// nothing here reported which model answered or what it cost.
|
|
73
|
+
// Stamping the requested id would manufacture a claim the
|
|
74
|
+
// endpoint never made.
|
|
70
75
|
return ok({ message: { role: "assistant", content: partial }, finishReason: "aborted" });
|
|
71
76
|
}
|
|
72
77
|
return err(toProviderError(e, workload));
|
|
@@ -46,6 +46,11 @@ export type PromptCachePolicy = {
|
|
|
46
46
|
* seeds the cache (billed at a premium; it only pays for itself once a later
|
|
47
47
|
* call reads it back).
|
|
48
48
|
*
|
|
49
|
+
* `reasoningTokens` is exactly what the provider reported — never derived from,
|
|
50
|
+
* nor reconciled against, `outputTokens`. Whether reasoning tokens are counted
|
|
51
|
+
* inside the output total varies by provider, so any arithmetic between the two
|
|
52
|
+
* would be a guess dressed as a figure.
|
|
53
|
+
*
|
|
49
54
|
* Every field is optional: a provider that reports no usage at all, or reports
|
|
50
55
|
* totals without a cache breakdown, is legitimate. Absent means "not reported",
|
|
51
56
|
* never "zero".
|
|
@@ -55,6 +60,7 @@ export interface ChatUsage {
|
|
|
55
60
|
readonly outputTokens?: number;
|
|
56
61
|
readonly cacheCreationInputTokens?: number;
|
|
57
62
|
readonly cacheReadInputTokens?: number;
|
|
63
|
+
readonly reasoningTokens?: number;
|
|
58
64
|
}
|
|
59
65
|
export interface ChatResponse {
|
|
60
66
|
readonly message: Extract<ModelMessage, {
|
|
@@ -72,6 +78,23 @@ export interface ChatResponse {
|
|
|
72
78
|
readonly finishReason: FinishReason | "aborted";
|
|
73
79
|
readonly rawFinishReason?: string;
|
|
74
80
|
readonly usage?: ChatUsage;
|
|
81
|
+
/**
|
|
82
|
+
* The id of the model this provider instance is bound to — what the caller
|
|
83
|
+
* asked for. Absent when the binding names no id.
|
|
84
|
+
*/
|
|
85
|
+
readonly requestedModelId?: string;
|
|
86
|
+
/**
|
|
87
|
+
* The model id the provider itself reported as having answered. Absent
|
|
88
|
+
* whenever the provider reported none — it is NEVER filled in from
|
|
89
|
+
* `requestedModelId`, because a claim about which model answered is only
|
|
90
|
+
* worth anything when the endpoint actually made it.
|
|
91
|
+
*
|
|
92
|
+
* A value differing from `requestedModelId` is not an error: the pair exists
|
|
93
|
+
* so a consumer can observe an endpoint or proxy serving a different model
|
|
94
|
+
* version than the one configured. Treat the comparison as best-effort
|
|
95
|
+
* diagnostics, never as an invariant.
|
|
96
|
+
*/
|
|
97
|
+
readonly servedModelId?: string;
|
|
75
98
|
}
|
|
76
99
|
export type ChatStreamEvent = {
|
|
77
100
|
readonly type: "text-delta";
|
package/dist/providers/types.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* before launch — a blue/green drain treats them as a single cohort.
|
|
16
16
|
*/
|
|
17
17
|
import { type ConversationAgentDeps } from "../agents/conversation-agent.js";
|
|
18
|
+
import type { UsageRecorder } from "../billing/usage-recorder.js";
|
|
18
19
|
import type { ResourcePolicy } from "../config/resource-limits.js";
|
|
19
20
|
import type { AgentDefinition } from "../loop/types.js";
|
|
20
21
|
import { type ExecuteAnalysisDeps, type ExecuteAnalysisInput, type ExecuteAnalysisResult } from "../workflows/execute-analysis.js";
|
|
@@ -27,12 +28,20 @@ export type SandboxStepCallable = (input: SandboxStepInput) => Promise<SandboxSt
|
|
|
27
28
|
* Deps bundles for the durable workflows. `executeAnalysis` is a builder
|
|
28
29
|
* because its `sandboxStepCallable` is the registered sandbox-step callable,
|
|
29
30
|
* which does not exist until registration runs inside `assembleCoreRuntime`.
|
|
31
|
+
*
|
|
32
|
+
* Each bag omits `usageRecorder` for the same reason `ConversationAssemblyDeps`
|
|
33
|
+
* does: the recorder is resolved once below and stamped onto every bag this
|
|
34
|
+
* call registers, so one runtime reports to one ledger. Leaving it settable
|
|
35
|
+
* here would let an embedder wire a recorder the workflows use and the
|
|
36
|
+
* conversation agent does not — a half-wired ledger that reads as a complete
|
|
37
|
+
* one. The protection has to hold on both sides of the assembly, not just the
|
|
38
|
+
* conversation side.
|
|
30
39
|
*/
|
|
31
40
|
export interface CoreWorkflowDeps {
|
|
32
|
-
readonly sandboxStep: SandboxStepDeps
|
|
33
|
-
readonly buildExecuteAnalysis: (sandboxStep: SandboxStepCallable) => ExecuteAnalysisDeps
|
|
34
|
-
readonly executeTargetAssessment: ExecuteTargetAssessmentDeps
|
|
35
|
-
readonly dataProfile: DataProfileDeps
|
|
41
|
+
readonly sandboxStep: Omit<SandboxStepDeps, "usageRecorder">;
|
|
42
|
+
readonly buildExecuteAnalysis: (sandboxStep: SandboxStepCallable) => Omit<ExecuteAnalysisDeps, "usageRecorder">;
|
|
43
|
+
readonly executeTargetAssessment: Omit<ExecuteTargetAssessmentDeps, "usageRecorder">;
|
|
44
|
+
readonly dataProfile: Omit<DataProfileDeps, "usageRecorder">;
|
|
36
45
|
}
|
|
37
46
|
/** The registered, callable workflow handles. */
|
|
38
47
|
export interface RegisteredWorkflows {
|
|
@@ -42,11 +51,12 @@ export interface RegisteredWorkflows {
|
|
|
42
51
|
readonly dataProfile: (input: DataProfileWorkflowInput) => Promise<void>;
|
|
43
52
|
}
|
|
44
53
|
/**
|
|
45
|
-
* Conversation-agent deps minus the workflow callable
|
|
46
|
-
*
|
|
47
|
-
* a stale callable
|
|
54
|
+
* Conversation-agent deps minus the workflow callable, the resource policy, and
|
|
55
|
+
* the usage recorder — `assembleCoreRuntime` supplies those itself so a caller
|
|
56
|
+
* cannot wire a stale callable, a policy that diverges from the one the
|
|
57
|
+
* workflows see, or a recorder that only half the agent tree reports to.
|
|
48
58
|
*/
|
|
49
|
-
export type ConversationAssemblyDeps = Omit<ConversationAgentDeps, "executeAnalysisWorkflow" | "resourcePolicy">;
|
|
59
|
+
export type ConversationAssemblyDeps = Omit<ConversationAgentDeps, "executeAnalysisWorkflow" | "resourcePolicy" | "usageRecorder">;
|
|
50
60
|
export interface CoreRuntimeDeps {
|
|
51
61
|
readonly conversation: ConversationAssemblyDeps;
|
|
52
62
|
readonly workflows: CoreWorkflowDeps;
|
|
@@ -55,6 +65,13 @@ export interface CoreRuntimeDeps {
|
|
|
55
65
|
* point for planner/routing guidance and workflow-input budget snapshots.
|
|
56
66
|
*/
|
|
57
67
|
readonly resourcePolicy?: ResourcePolicy;
|
|
68
|
+
/**
|
|
69
|
+
* LLM usage-accounting seam. Resolved once here and stamped onto the
|
|
70
|
+
* conversation agent and every workflow deps bag this call registers, so
|
|
71
|
+
* one runtime reports to one ledger. Omitted falls back to
|
|
72
|
+
* `createNoopUsageRecorder()`.
|
|
73
|
+
*/
|
|
74
|
+
readonly usageRecorder?: UsageRecorder;
|
|
58
75
|
}
|
|
59
76
|
export interface CoreRuntime {
|
|
60
77
|
readonly conversationAgent: AgentDefinition;
|
package/dist/runtime/assemble.js
CHANGED
|
@@ -15,20 +15,23 @@
|
|
|
15
15
|
* before launch — a blue/green drain treats them as a single cohort.
|
|
16
16
|
*/
|
|
17
17
|
import { createConversationAgent } from "../agents/conversation-agent.js";
|
|
18
|
+
import { createNoopUsageRecorder } from "../billing/noop-usage-recorder.js";
|
|
18
19
|
import { registerExecuteAnalysis } from "../workflows/execute-analysis.js";
|
|
19
20
|
import { registerSandboxStep } from "../workflows/sandbox-step.js";
|
|
20
21
|
import { registerExecuteTargetAssessment, } from "../workflows/execute-target-assessment.js";
|
|
21
22
|
import { registerDataProfileWorkflow } from "../tasks/data-profile.js";
|
|
22
23
|
export function assembleCoreRuntime(deps) {
|
|
23
24
|
const { conversation, workflows: wf, resourcePolicy } = deps;
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
25
|
+
const usageRecorder = deps.usageRecorder ?? createNoopUsageRecorder();
|
|
26
|
+
const sandboxStep = registerSandboxStep({ ...wf.sandboxStep, usageRecorder });
|
|
27
|
+
const executeAnalysis = registerExecuteAnalysis({ ...wf.buildExecuteAnalysis(sandboxStep), usageRecorder });
|
|
28
|
+
const executeTargetAssessment = registerExecuteTargetAssessment({ ...wf.executeTargetAssessment, usageRecorder });
|
|
29
|
+
const dataProfile = registerDataProfileWorkflow({ ...wf.dataProfile, usageRecorder });
|
|
28
30
|
const conversationAgent = createConversationAgent({
|
|
29
31
|
...conversation,
|
|
30
32
|
executeAnalysisWorkflow: executeAnalysis,
|
|
31
33
|
resourcePolicy,
|
|
34
|
+
usageRecorder,
|
|
32
35
|
});
|
|
33
36
|
return {
|
|
34
37
|
conversationAgent,
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* into its emitters.
|
|
7
7
|
*/
|
|
8
8
|
import type { ChatDataPart, EmitFn } from "../loop/types.js";
|
|
9
|
+
import type { ToolDetailResolver } from "../tools/detail-resolver.js";
|
|
9
10
|
/**
|
|
10
11
|
* Stable reconciliation id for a step's reconciling parts
|
|
11
12
|
* (`data-step-activity`, `data-step-file-tree`). One id per (runId, stepId)
|
|
@@ -18,7 +19,27 @@ import type { ChatDataPart, EmitFn } from "../loop/types.js";
|
|
|
18
19
|
* producers construct it identically.
|
|
19
20
|
*/
|
|
20
21
|
export declare function stepPartId(kind: string, runId: string, stepId: string): string;
|
|
21
|
-
|
|
22
|
+
/**
|
|
23
|
+
* The live-activity phrase for one tool call: the tool's name, then its own
|
|
24
|
+
* description of this call when it has one.
|
|
25
|
+
*
|
|
26
|
+
* The description comes from the tool's `describeCall` hook, resolved through
|
|
27
|
+
* `resolveDetail` — the same resolver the chat chip and the reloaded transcript
|
|
28
|
+
* use. A caller supplies the resolver for the agent whose calls it is
|
|
29
|
+
* describing, because a sandbox agent's tool list is not the conversation
|
|
30
|
+
* roster's and no single list serves both.
|
|
31
|
+
*
|
|
32
|
+
* The name always leads, and that is load-bearing rather than cosmetic. This
|
|
33
|
+
* phrase is rendered alone, with no tool name beside it — unlike the chat chip,
|
|
34
|
+
* which prints the name itself and can afford a bare detail. `write_file` and
|
|
35
|
+
* `edit_file` both describe a call by its path, so a detail on its own would
|
|
36
|
+
* render a write and an edit of the same file identically.
|
|
37
|
+
*
|
|
38
|
+
* A tool with no hook, an unknown tool, and an input its schema rejects all fall
|
|
39
|
+
* back to the name alone. No verb is added: the phrase is carried on a part that
|
|
40
|
+
* already states the phase, so "Running" would only restate it.
|
|
41
|
+
*/
|
|
42
|
+
export declare function activityForTool(name: string, input?: unknown, resolveDetail?: ToolDetailResolver): string;
|
|
22
43
|
/** On-change working-tree delta the sandbox executor posts per exec (paths
|
|
23
44
|
* only; the executor skips directories). */
|
|
24
45
|
export interface SandboxTreeDelta {
|
|
@@ -19,42 +19,29 @@
|
|
|
19
19
|
export function stepPartId(kind, runId, stepId) {
|
|
20
20
|
return `${kind}-${runId}-${stepId}`;
|
|
21
21
|
}
|
|
22
|
-
/**
|
|
23
|
-
*
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (name === "execute_command") {
|
|
46
|
-
const cmd = obj.command;
|
|
47
|
-
if (!Array.isArray(cmd))
|
|
48
|
-
return null;
|
|
49
|
-
const script = cmd.find((a) => typeof a === "string" && /\.(py|R|r|sh|js|ts|ipynb)$/.test(a));
|
|
50
|
-
return script ? baseName(script) : null;
|
|
51
|
-
}
|
|
52
|
-
return typeof obj.path === "string" && obj.path.length > 0 ? baseName(obj.path) : null;
|
|
53
|
-
}
|
|
54
|
-
export function activityForTool(name, input) {
|
|
55
|
-
const label = TOOL_ACTIVITY[name] ?? `Running ${name}`;
|
|
56
|
-
const file = activityFileName(name, input);
|
|
57
|
-
return file ? `${label} ${file}` : label;
|
|
22
|
+
/**
|
|
23
|
+
* The live-activity phrase for one tool call: the tool's name, then its own
|
|
24
|
+
* description of this call when it has one.
|
|
25
|
+
*
|
|
26
|
+
* The description comes from the tool's `describeCall` hook, resolved through
|
|
27
|
+
* `resolveDetail` — the same resolver the chat chip and the reloaded transcript
|
|
28
|
+
* use. A caller supplies the resolver for the agent whose calls it is
|
|
29
|
+
* describing, because a sandbox agent's tool list is not the conversation
|
|
30
|
+
* roster's and no single list serves both.
|
|
31
|
+
*
|
|
32
|
+
* The name always leads, and that is load-bearing rather than cosmetic. This
|
|
33
|
+
* phrase is rendered alone, with no tool name beside it — unlike the chat chip,
|
|
34
|
+
* which prints the name itself and can afford a bare detail. `write_file` and
|
|
35
|
+
* `edit_file` both describe a call by its path, so a detail on its own would
|
|
36
|
+
* render a write and an edit of the same file identically.
|
|
37
|
+
*
|
|
38
|
+
* A tool with no hook, an unknown tool, and an input its schema rejects all fall
|
|
39
|
+
* back to the name alone. No verb is added: the phrase is carried on a part that
|
|
40
|
+
* already states the phase, so "Running" would only restate it.
|
|
41
|
+
*/
|
|
42
|
+
export function activityForTool(name, input, resolveDetail) {
|
|
43
|
+
const detail = resolveDetail?.(name, input);
|
|
44
|
+
return detail === undefined ? name : `${name} ${detail}`;
|
|
58
45
|
}
|
|
59
46
|
/** Extract the tree delta from a `data-sandbox-event` part, or null when the
|
|
60
47
|
* part is some other sandbox event. The wrapper shape is set by
|
|
@@ -57,6 +57,7 @@ export declare const SynthesizedFindingSchema: z.ZodObject<{
|
|
|
57
57
|
"clinical-evidence": "clinical-evidence";
|
|
58
58
|
}>;
|
|
59
59
|
actionType: z.ZodOptional<z.ZodEnum<{
|
|
60
|
+
none: "none";
|
|
60
61
|
"therapeutic-target": "therapeutic-target";
|
|
61
62
|
"predictive-biomarker": "predictive-biomarker";
|
|
62
63
|
"prognostic-biomarker": "prognostic-biomarker";
|
|
@@ -65,7 +66,6 @@ export declare const SynthesizedFindingSchema: z.ZodObject<{
|
|
|
65
66
|
"resistance-mechanism": "resistance-mechanism";
|
|
66
67
|
"patient-stratification": "patient-stratification";
|
|
67
68
|
"mechanism-of-action": "mechanism-of-action";
|
|
68
|
-
none: "none";
|
|
69
69
|
}>>;
|
|
70
70
|
rationale: z.ZodString;
|
|
71
71
|
}, z.core.$strip>>;
|
|
@@ -123,6 +123,7 @@ export declare const RunSynthesisSchema: z.ZodObject<{
|
|
|
123
123
|
"clinical-evidence": "clinical-evidence";
|
|
124
124
|
}>;
|
|
125
125
|
actionType: z.ZodOptional<z.ZodEnum<{
|
|
126
|
+
none: "none";
|
|
126
127
|
"therapeutic-target": "therapeutic-target";
|
|
127
128
|
"predictive-biomarker": "predictive-biomarker";
|
|
128
129
|
"prognostic-biomarker": "prognostic-biomarker";
|
|
@@ -131,7 +132,6 @@ export declare const RunSynthesisSchema: z.ZodObject<{
|
|
|
131
132
|
"resistance-mechanism": "resistance-mechanism";
|
|
132
133
|
"patient-stratification": "patient-stratification";
|
|
133
134
|
"mechanism-of-action": "mechanism-of-action";
|
|
134
|
-
none: "none";
|
|
135
135
|
}>>;
|
|
136
136
|
rationale: z.ZodString;
|
|
137
137
|
}, z.core.$strip>>;
|
package/dist/state/index.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export type { InsertPlanInput, UpsertPlanInput } from "./plans.js";
|
|
|
23
23
|
export { tryStartDataProfile, tryRetryDataProfile, tryRerunDataProfile, completeDataProfile, failDataProfile, expireStaleDataProfile, reconcileOrphanedDataProfile, clearDataProfile, loadDataProfileStatus, loadSeedInputFileIds, recordDataProfileWorkflowId, } from "./data-profile.js";
|
|
24
24
|
export type { DataProfileStatus, DataProfileResult, DataProfileInputFile, DataProfileFile, DataProfileQualityAssessment } from "./data-profile.js";
|
|
25
25
|
export { queryRunCountsByAnalyses, queryThreadCountsByAnalyses, queryDataProfileStatusByAnalyses } from "./analyses-metrics.js";
|
|
26
|
+
export { createAnalysisPurge } from "./purge-analysis.js";
|
|
27
|
+
export type { AnalysisPurge, AnalysisPurgeDeps, AnalysisPurgeOutcome } from "./purge-analysis.js";
|
|
26
28
|
export { AnalysisStateRowSchema, ArtifactRole, ArtifactRowSchema, RunStatus, StepExecutionStatus, SynthesisStatus, CortexRunRowSchema, StepExecutionRowSchema, CortexPlanRowSchema, } from "./schema.js";
|
|
27
29
|
export type { AnalysisStateRow, ArtifactRow, CortexRunRow, StepExecutionRow, CortexPlanRow } from "./schema.js";
|
|
28
30
|
export { insertAssessment, updateProgress, setDossier, markFailed, getAssessment, listAssessmentsByOrg, softDeleteAssessment, TargetAssessmentStatusSchema, TargetAssessmentErrorSchema, TargetAssessmentRowSchema, } from "./target-assessments.js";
|
package/dist/state/index.js
CHANGED
|
@@ -16,5 +16,8 @@ export { setSandboxRef, setActiveExecId, clearSandboxRef, queryActiveSandboxes,
|
|
|
16
16
|
export { insertPlan, upsertPlan, loadPlan } from "./plans.js";
|
|
17
17
|
export { tryStartDataProfile, tryRetryDataProfile, tryRerunDataProfile, completeDataProfile, failDataProfile, expireStaleDataProfile, reconcileOrphanedDataProfile, clearDataProfile, loadDataProfileStatus, loadSeedInputFileIds, recordDataProfileWorkflowId, } from "./data-profile.js";
|
|
18
18
|
export { queryRunCountsByAnalyses, queryThreadCountsByAnalyses, queryDataProfileStatusByAnalyses } from "./analyses-metrics.js";
|
|
19
|
+
// Not per-entity: one analysis's whole Postgres footprint, reached from its id
|
|
20
|
+
// alone, with the workflow ledger behind an injected seam.
|
|
21
|
+
export { createAnalysisPurge } from "./purge-analysis.js";
|
|
19
22
|
export { AnalysisStateRowSchema, ArtifactRole, ArtifactRowSchema, RunStatus, StepExecutionStatus, SynthesisStatus, CortexRunRowSchema, StepExecutionRowSchema, CortexPlanRowSchema, } from "./schema.js";
|
|
20
23
|
export { insertAssessment, updateProgress, setDossier, markFailed, getAssessment, listAssessmentsByOrg, softDeleteAssessment, TargetAssessmentStatusSchema, TargetAssessmentErrorSchema, TargetAssessmentRowSchema, } from "./target-assessments.js";
|