@spendgraph/workflows 0.2.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 +149 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/noesis/attribution.d.ts +24 -0
- package/dist/noesis/attribution.js +43 -0
- package/dist/noesis/context.d.ts +18 -0
- package/dist/noesis/context.js +16 -0
- package/dist/noesis/draft/draft.d.ts +18 -0
- package/dist/noesis/draft/draft.js +50 -0
- package/dist/noesis/draft/index.d.ts +2 -0
- package/dist/noesis/draft/index.js +1 -0
- package/dist/noesis/draft/types.d.ts +27 -0
- package/dist/noesis/draft/types.js +1 -0
- package/dist/noesis/events.d.ts +158 -0
- package/dist/noesis/events.js +24 -0
- package/dist/noesis/execute/execute.d.ts +22 -0
- package/dist/noesis/execute/execute.js +85 -0
- package/dist/noesis/execute/index.d.ts +3 -0
- package/dist/noesis/execute/index.js +2 -0
- package/dist/noesis/execute/order.d.ts +11 -0
- package/dist/noesis/execute/order.js +28 -0
- package/dist/noesis/execute/types.d.ts +37 -0
- package/dist/noesis/execute/types.js +1 -0
- package/dist/noesis/gates/closure-checklist/index.d.ts +3 -0
- package/dist/noesis/gates/closure-checklist/index.js +2 -0
- package/dist/noesis/gates/closure-checklist/invoke.d.ts +5 -0
- package/dist/noesis/gates/closure-checklist/invoke.js +6 -0
- package/dist/noesis/gates/closure-checklist/prompt.d.ts +5 -0
- package/dist/noesis/gates/closure-checklist/prompt.js +32 -0
- package/dist/noesis/gates/closure-checklist/types.d.ts +20 -0
- package/dist/noesis/gates/closure-checklist/types.js +1 -0
- package/dist/noesis/gates/cqot-gate/index.d.ts +3 -0
- package/dist/noesis/gates/cqot-gate/index.js +2 -0
- package/dist/noesis/gates/cqot-gate/invoke.d.ts +5 -0
- package/dist/noesis/gates/cqot-gate/invoke.js +6 -0
- package/dist/noesis/gates/cqot-gate/prompt.d.ts +5 -0
- package/dist/noesis/gates/cqot-gate/prompt.js +27 -0
- package/dist/noesis/gates/cqot-gate/types.d.ts +23 -0
- package/dist/noesis/gates/cqot-gate/types.js +1 -0
- package/dist/noesis/gates/premortem/index.d.ts +3 -0
- package/dist/noesis/gates/premortem/index.js +2 -0
- package/dist/noesis/gates/premortem/invoke.d.ts +5 -0
- package/dist/noesis/gates/premortem/invoke.js +6 -0
- package/dist/noesis/gates/premortem/prompt.d.ts +5 -0
- package/dist/noesis/gates/premortem/prompt.js +37 -0
- package/dist/noesis/gates/premortem/types.d.ts +28 -0
- package/dist/noesis/gates/premortem/types.js +1 -0
- package/dist/noesis/gates/red-team/index.d.ts +3 -0
- package/dist/noesis/gates/red-team/index.js +2 -0
- package/dist/noesis/gates/red-team/invoke.d.ts +5 -0
- package/dist/noesis/gates/red-team/invoke.js +6 -0
- package/dist/noesis/gates/red-team/prompt.d.ts +5 -0
- package/dist/noesis/gates/red-team/prompt.js +25 -0
- package/dist/noesis/gates/red-team/types.d.ts +21 -0
- package/dist/noesis/gates/red-team/types.js +1 -0
- package/dist/noesis/gates/standards-review/index.d.ts +3 -0
- package/dist/noesis/gates/standards-review/index.js +2 -0
- package/dist/noesis/gates/standards-review/invoke.d.ts +5 -0
- package/dist/noesis/gates/standards-review/invoke.js +6 -0
- package/dist/noesis/gates/standards-review/prompt.d.ts +5 -0
- package/dist/noesis/gates/standards-review/prompt.js +26 -0
- package/dist/noesis/gates/standards-review/types.d.ts +20 -0
- package/dist/noesis/gates/standards-review/types.js +1 -0
- package/dist/noesis/gates/think-checkpoint/index.d.ts +3 -0
- package/dist/noesis/gates/think-checkpoint/index.js +2 -0
- package/dist/noesis/gates/think-checkpoint/invoke.d.ts +5 -0
- package/dist/noesis/gates/think-checkpoint/invoke.js +6 -0
- package/dist/noesis/gates/think-checkpoint/prompt.d.ts +5 -0
- package/dist/noesis/gates/think-checkpoint/prompt.js +28 -0
- package/dist/noesis/gates/think-checkpoint/types.d.ts +21 -0
- package/dist/noesis/gates/think-checkpoint/types.js +1 -0
- package/dist/noesis/index.d.ts +51 -0
- package/dist/noesis/index.js +27 -0
- package/dist/noesis/producers/cove-answers/index.d.ts +3 -0
- package/dist/noesis/producers/cove-answers/index.js +2 -0
- package/dist/noesis/producers/cove-answers/invoke.d.ts +5 -0
- package/dist/noesis/producers/cove-answers/invoke.js +6 -0
- package/dist/noesis/producers/cove-answers/prompt.d.ts +5 -0
- package/dist/noesis/producers/cove-answers/prompt.js +27 -0
- package/dist/noesis/producers/cove-answers/types.d.ts +32 -0
- package/dist/noesis/producers/cove-answers/types.js +1 -0
- package/dist/noesis/producers/cove-questions/index.d.ts +3 -0
- package/dist/noesis/producers/cove-questions/index.js +2 -0
- package/dist/noesis/producers/cove-questions/invoke.d.ts +5 -0
- package/dist/noesis/producers/cove-questions/invoke.js +6 -0
- package/dist/noesis/producers/cove-questions/prompt.d.ts +5 -0
- package/dist/noesis/producers/cove-questions/prompt.js +26 -0
- package/dist/noesis/producers/cove-questions/types.d.ts +17 -0
- package/dist/noesis/producers/cove-questions/types.js +1 -0
- package/dist/noesis/producers/decompose/index.d.ts +3 -0
- package/dist/noesis/producers/decompose/index.js +2 -0
- package/dist/noesis/producers/decompose/invoke.d.ts +5 -0
- package/dist/noesis/producers/decompose/invoke.js +6 -0
- package/dist/noesis/producers/decompose/prompt.d.ts +22 -0
- package/dist/noesis/producers/decompose/prompt.js +68 -0
- package/dist/noesis/producers/decompose/types.d.ts +41 -0
- package/dist/noesis/producers/decompose/types.js +1 -0
- package/dist/noesis/producers/discriminating-test/index.d.ts +3 -0
- package/dist/noesis/producers/discriminating-test/index.js +2 -0
- package/dist/noesis/producers/discriminating-test/invoke.d.ts +5 -0
- package/dist/noesis/producers/discriminating-test/invoke.js +6 -0
- package/dist/noesis/producers/discriminating-test/prompt.d.ts +5 -0
- package/dist/noesis/producers/discriminating-test/prompt.js +67 -0
- package/dist/noesis/producers/discriminating-test/types.d.ts +32 -0
- package/dist/noesis/producers/discriminating-test/types.js +1 -0
- package/dist/noesis/producers/plan-candidates/index.d.ts +3 -0
- package/dist/noesis/producers/plan-candidates/index.js +2 -0
- package/dist/noesis/producers/plan-candidates/invoke.d.ts +5 -0
- package/dist/noesis/producers/plan-candidates/invoke.js +6 -0
- package/dist/noesis/producers/plan-candidates/prompt.d.ts +5 -0
- package/dist/noesis/producers/plan-candidates/prompt.js +46 -0
- package/dist/noesis/producers/plan-candidates/types.d.ts +32 -0
- package/dist/noesis/producers/plan-candidates/types.js +1 -0
- package/dist/noesis/producers/qn-brief/index.d.ts +3 -0
- package/dist/noesis/producers/qn-brief/index.js +2 -0
- package/dist/noesis/producers/qn-brief/invoke.d.ts +5 -0
- package/dist/noesis/producers/qn-brief/invoke.js +6 -0
- package/dist/noesis/producers/qn-brief/prompt.d.ts +12 -0
- package/dist/noesis/producers/qn-brief/prompt.js +65 -0
- package/dist/noesis/producers/qn-brief/types.d.ts +42 -0
- package/dist/noesis/producers/qn-brief/types.js +1 -0
- package/dist/noesis/producers/toulmin/answer.d.ts +18 -0
- package/dist/noesis/producers/toulmin/answer.js +87 -0
- package/dist/noesis/producers/toulmin/index.d.ts +5 -0
- package/dist/noesis/producers/toulmin/index.js +4 -0
- package/dist/noesis/producers/toulmin/invoke.d.ts +18 -0
- package/dist/noesis/producers/toulmin/invoke.js +53 -0
- package/dist/noesis/producers/toulmin/prompt.d.ts +12 -0
- package/dist/noesis/producers/toulmin/prompt.js +60 -0
- package/dist/noesis/producers/toulmin/render.d.ts +14 -0
- package/dist/noesis/producers/toulmin/render.js +82 -0
- package/dist/noesis/producers/toulmin/types.d.ts +126 -0
- package/dist/noesis/producers/toulmin/types.js +1 -0
- package/dist/noesis/question.d.ts +24 -0
- package/dist/noesis/question.js +37 -0
- package/dist/noesis/repair/reflection/index.d.ts +3 -0
- package/dist/noesis/repair/reflection/index.js +2 -0
- package/dist/noesis/repair/reflection/invoke.d.ts +5 -0
- package/dist/noesis/repair/reflection/invoke.js +6 -0
- package/dist/noesis/repair/reflection/prompt.d.ts +5 -0
- package/dist/noesis/repair/reflection/prompt.js +26 -0
- package/dist/noesis/repair/reflection/types.d.ts +17 -0
- package/dist/noesis/repair/reflection/types.js +1 -0
- package/dist/noesis/retry.d.ts +25 -0
- package/dist/noesis/retry.js +60 -0
- package/dist/noesis/route.d.ts +29 -0
- package/dist/noesis/route.js +69 -0
- package/dist/noesis/router/triage/index.d.ts +3 -0
- package/dist/noesis/router/triage/index.js +2 -0
- package/dist/noesis/router/triage/invoke.d.ts +31 -0
- package/dist/noesis/router/triage/invoke.js +76 -0
- package/dist/noesis/router/triage/prompt.d.ts +11 -0
- package/dist/noesis/router/triage/prompt.js +34 -0
- package/dist/noesis/router/triage/types.d.ts +70 -0
- package/dist/noesis/router/triage/types.js +1 -0
- package/dist/noesis/run/artifacts.d.ts +12 -0
- package/dist/noesis/run/artifacts.js +31 -0
- package/dist/noesis/run/errors.d.ts +38 -0
- package/dist/noesis/run/errors.js +55 -0
- package/dist/noesis/run/index.d.ts +6 -0
- package/dist/noesis/run/index.js +4 -0
- package/dist/noesis/run/ledger.d.ts +110 -0
- package/dist/noesis/run/ledger.js +184 -0
- package/dist/noesis/run/loop.d.ts +15 -0
- package/dist/noesis/run/loop.js +91 -0
- package/dist/noesis/run/plan.d.ts +18 -0
- package/dist/noesis/run/plan.js +39 -0
- package/dist/noesis/run/produce.d.ts +50 -0
- package/dist/noesis/run/produce.js +114 -0
- package/dist/noesis/run/redo.d.ts +24 -0
- package/dist/noesis/run/redo.js +41 -0
- package/dist/noesis/run/result.d.ts +5 -0
- package/dist/noesis/run/result.js +51 -0
- package/dist/noesis/run/run.d.ts +4 -0
- package/dist/noesis/run/run.js +73 -0
- package/dist/noesis/run/stages/classify.d.ts +7 -0
- package/dist/noesis/run/stages/classify.js +23 -0
- package/dist/noesis/run/stages/direct.d.ts +6 -0
- package/dist/noesis/run/stages/direct.js +8 -0
- package/dist/noesis/run/stages/full.d.ts +14 -0
- package/dist/noesis/run/stages/full.js +21 -0
- package/dist/noesis/run/stages/index.d.ts +5 -0
- package/dist/noesis/run/stages/index.js +5 -0
- package/dist/noesis/run/stages/retrieve.d.ts +13 -0
- package/dist/noesis/run/stages/retrieve.js +38 -0
- package/dist/noesis/run/stages/standard.d.ts +12 -0
- package/dist/noesis/run/stages/standard.js +19 -0
- package/dist/noesis/run/state.d.ts +26 -0
- package/dist/noesis/run/state.js +13 -0
- package/dist/noesis/run/stream.d.ts +28 -0
- package/dist/noesis/run/stream.js +33 -0
- package/dist/noesis/run/verify.d.ts +27 -0
- package/dist/noesis/run/verify.js +78 -0
- package/dist/noesis/slugs.d.ts +16 -0
- package/dist/noesis/slugs.js +46 -0
- package/dist/noesis/stage.d.ts +56 -0
- package/dist/noesis/stage.js +52 -0
- package/dist/noesis/started.d.ts +18 -0
- package/dist/noesis/started.js +39 -0
- package/dist/noesis/types.d.ts +167 -0
- package/dist/noesis/types.js +1 -0
- package/dist/noesis/usage.d.ts +49 -0
- package/dist/noesis/usage.js +56 -0
- package/dist/noesis/verify/index.d.ts +2 -0
- package/dist/noesis/verify/index.js +1 -0
- package/dist/noesis/verify/types.d.ts +54 -0
- package/dist/noesis/verify/types.js +1 -0
- package/dist/noesis/verify/verify.d.ts +36 -0
- package/dist/noesis/verify/verify.js +94 -0
- package/package.json +59 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Llm } from "@spendgraph/llms";
|
|
2
|
+
import type { PromptClient } from "@spendgraph/prompt";
|
|
3
|
+
import type { NoesisEvent } from "../events.js";
|
|
4
|
+
import type { NoesisOptions, NoesisResult } from "../types.js";
|
|
5
|
+
/** A run as it happens, and the same run once it is over. */
|
|
6
|
+
export interface NoesisStream extends AsyncIterable<NoesisEvent> {
|
|
7
|
+
/**
|
|
8
|
+
* The finished run.
|
|
9
|
+
*
|
|
10
|
+
* Resolves whether or not anyone iterated, and rejects where `run()` would
|
|
11
|
+
* have — watching a run changes what you can see, not what happens.
|
|
12
|
+
*/
|
|
13
|
+
result: Promise<NoesisResult>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* `run()`, watched.
|
|
17
|
+
*
|
|
18
|
+
* The run starts on the call rather than on the first read, so a client that
|
|
19
|
+
* connects late still receives everything that already happened, and a caller
|
|
20
|
+
* who only wants the answer never has to iterate.
|
|
21
|
+
*
|
|
22
|
+
* Events arrive unwrapped — a consumer switches on `event.type` directly rather
|
|
23
|
+
* than reaching through the harness envelope the queue underneath uses.
|
|
24
|
+
*
|
|
25
|
+
* `opts.onEvent` still works and is called first, so a caller can log and
|
|
26
|
+
* stream from the same run.
|
|
27
|
+
*/
|
|
28
|
+
export declare function runStream(client: PromptClient, llm: Llm, question: string, opts?: NoesisOptions): NoesisStream;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { streamed } from "@spendgraph/harness";
|
|
2
|
+
import { run } from "./run.js";
|
|
3
|
+
/**
|
|
4
|
+
* `run()`, watched.
|
|
5
|
+
*
|
|
6
|
+
* The run starts on the call rather than on the first read, so a client that
|
|
7
|
+
* connects late still receives everything that already happened, and a caller
|
|
8
|
+
* who only wants the answer never has to iterate.
|
|
9
|
+
*
|
|
10
|
+
* Events arrive unwrapped — a consumer switches on `event.type` directly rather
|
|
11
|
+
* than reaching through the harness envelope the queue underneath uses.
|
|
12
|
+
*
|
|
13
|
+
* `opts.onEvent` still works and is called first, so a caller can log and
|
|
14
|
+
* stream from the same run.
|
|
15
|
+
*/
|
|
16
|
+
export function runStream(client, llm, question, opts = {}) {
|
|
17
|
+
const inner = streamed((_emit, note) => run(client, llm, question, {
|
|
18
|
+
...opts,
|
|
19
|
+
onEvent: (event) => {
|
|
20
|
+
opts.onEvent?.(event);
|
|
21
|
+
note(event);
|
|
22
|
+
},
|
|
23
|
+
}));
|
|
24
|
+
return {
|
|
25
|
+
result: inner.result,
|
|
26
|
+
async *[Symbol.asyncIterator]() {
|
|
27
|
+
for await (const wrapped of inner) {
|
|
28
|
+
if (wrapped.type === "note")
|
|
29
|
+
yield wrapped.data;
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Llm } from "@spendgraph/llms";
|
|
2
|
+
import type { PromptClient } from "@spendgraph/prompt";
|
|
3
|
+
import { type Draft } from "../draft/index.js";
|
|
4
|
+
import type { NoesisOptions } from "../types.js";
|
|
5
|
+
import type { StageId } from "./ledger.js";
|
|
6
|
+
import type { RunState } from "./state.js";
|
|
7
|
+
/**
|
|
8
|
+
* Stages 5A, 5B and the pairing between them.
|
|
9
|
+
*
|
|
10
|
+
* 5B is given `blind()` rather than the questions themselves, so it answers
|
|
11
|
+
* without sight of the claims it settles. Putting the two back together is the
|
|
12
|
+
* step in between, and it stays in code for the same reason.
|
|
13
|
+
*
|
|
14
|
+
* A 5A that found nothing to check skips 5B rather than sending it an empty
|
|
15
|
+
* turn, which every provider rejects.
|
|
16
|
+
*/
|
|
17
|
+
export declare function checks(client: PromptClient, llm: Llm, opts: NoesisOptions, state: RunState, draft: Draft): Promise<string>;
|
|
18
|
+
/**
|
|
19
|
+
* Stage R. The earliest wrong decision, and the stage to redo from.
|
|
20
|
+
*
|
|
21
|
+
* The gate says where the problem surfaced; this says where it started, which
|
|
22
|
+
* is rarely the same place and is the only one worth returning to. The gate's
|
|
23
|
+
* own suggestion is the fallback, not the answer.
|
|
24
|
+
*/
|
|
25
|
+
export declare function reflect(client: PromptClient, llm: Llm, question: string, opts: NoesisOptions, state: RunState, gate: StageId, reason: string, hint?: string | null): Promise<StageId | undefined>;
|
|
26
|
+
/** Stage 6C. Everything the ledger holds, compiled into a claim. */
|
|
27
|
+
export declare function compile(client: PromptClient, llm: Llm, question: string, opts: NoesisOptions, state: RunState): Promise<string>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { unattributed } from "../attribution.js";
|
|
2
|
+
import { render as renderDraft } from "../draft/index.js";
|
|
3
|
+
import { invoke as coveAnswers } from "../producers/cove-answers/index.js";
|
|
4
|
+
import { invoke as coveQuestions } from "../producers/cove-questions/index.js";
|
|
5
|
+
import { answer } from "../producers/toulmin/index.js";
|
|
6
|
+
import { invoke as reflection } from "../repair/reflection/index.js";
|
|
7
|
+
import { blind, compare, render as renderChecks } from "../verify/index.js";
|
|
8
|
+
import { soFar } from "./produce.js";
|
|
9
|
+
import { parseStage, resumeAt } from "./redo.js";
|
|
10
|
+
/**
|
|
11
|
+
* Stages 5A, 5B and the pairing between them.
|
|
12
|
+
*
|
|
13
|
+
* 5B is given `blind()` rather than the questions themselves, so it answers
|
|
14
|
+
* without sight of the claims it settles. Putting the two back together is the
|
|
15
|
+
* step in between, and it stays in code for the same reason.
|
|
16
|
+
*
|
|
17
|
+
* A 5A that found nothing to check skips 5B rather than sending it an empty
|
|
18
|
+
* turn, which every provider rejects.
|
|
19
|
+
*/
|
|
20
|
+
export async function checks(client, llm, opts, state, draft) {
|
|
21
|
+
const drafted = renderDraft(draft);
|
|
22
|
+
const questions = state.ledger.record("5A", await coveQuestions(client, llm, { question: drafted, ...opts.context }, opts.stage));
|
|
23
|
+
const answers = questions.questions.length
|
|
24
|
+
? state.ledger.record("5B", await coveAnswers(client, llm, { question: blind(questions), ...opts.context }, opts.stage))
|
|
25
|
+
: { answers: [] };
|
|
26
|
+
const verification = compare(questions, answers, opts.verify?.confidenceFloor);
|
|
27
|
+
state.emit?.({
|
|
28
|
+
type: "checks.paired",
|
|
29
|
+
checked: verification.checked.length,
|
|
30
|
+
settled: verification.checked.filter((one) => one.settled).length,
|
|
31
|
+
unsettled: verification.unsettled,
|
|
32
|
+
unanswered: verification.unanswered,
|
|
33
|
+
unasked: verification.unasked,
|
|
34
|
+
overclaimed: verification.overclaimed,
|
|
35
|
+
});
|
|
36
|
+
state.ledger.record("5", {
|
|
37
|
+
data: verification,
|
|
38
|
+
rolloutIds: [],
|
|
39
|
+
model: "",
|
|
40
|
+
inputTokens: 0,
|
|
41
|
+
outputTokens: 0,
|
|
42
|
+
latencyMs: 0,
|
|
43
|
+
});
|
|
44
|
+
return `${drafted}\n\n${renderChecks(verification)}`;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Stage R. The earliest wrong decision, and the stage to redo from.
|
|
48
|
+
*
|
|
49
|
+
* The gate says where the problem surfaced; this says where it started, which
|
|
50
|
+
* is rarely the same place and is the only one worth returning to. The gate's
|
|
51
|
+
* own suggestion is the fallback, not the answer.
|
|
52
|
+
*/
|
|
53
|
+
export async function reflect(client, llm, question, opts, state, gate, reason, hint) {
|
|
54
|
+
const looked = state.ledger.record("R", await reflection(client, llm, { question: `${soFar(state, question)}\n\nThe gate said: ${reason}`, ...opts.context }, opts.stage));
|
|
55
|
+
const redoFrom = parseStage(looked.redo_from_stage) ?? parseStage(hint ?? "");
|
|
56
|
+
state.emit?.({
|
|
57
|
+
type: "reflect.pointed",
|
|
58
|
+
from: gate,
|
|
59
|
+
redoFrom,
|
|
60
|
+
resumeAt: resumeAt(redoFrom),
|
|
61
|
+
lesson: looked.lesson,
|
|
62
|
+
});
|
|
63
|
+
return redoFrom;
|
|
64
|
+
}
|
|
65
|
+
/** Stage 6C. Everything the ledger holds, compiled into a claim. */
|
|
66
|
+
export async function compile(client, llm, question, opts, state) {
|
|
67
|
+
const answered = await answer(client, llm, { question, ...state.ledger.artifacts() }, opts.answer ?? {});
|
|
68
|
+
state.answered = answered;
|
|
69
|
+
const loose = unattributed(answered.answer, state.ledger.byStage().map((one) => one.stage));
|
|
70
|
+
state.unattributed = loose;
|
|
71
|
+
state.emit?.({
|
|
72
|
+
type: "grounds.checked",
|
|
73
|
+
cited: answered.answer.grounds?.length ?? 0,
|
|
74
|
+
unattributed: loose,
|
|
75
|
+
});
|
|
76
|
+
state.text = state.ledger.record("6C", { ...answered, data: answered.answer }).claim;
|
|
77
|
+
return state.text;
|
|
78
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { StageId } from "./run/ledger.js";
|
|
2
|
+
/**
|
|
3
|
+
* Which stage a slug belongs to.
|
|
4
|
+
*
|
|
5
|
+
* The inverse of what the fifteen modules already declare, and the reason
|
|
6
|
+
* `runStage` can announce a stage it was only ever told the slug of. A slug
|
|
7
|
+
* nobody claims is not an error — it means a caller reached `runStage` with a
|
|
8
|
+
* prompt of their own, and an event for it would be a lie.
|
|
9
|
+
*
|
|
10
|
+
* Imported from each `prompt.ts` rather than from the module's `index.ts`,
|
|
11
|
+
* which would be a cycle: an index re-exports `invoke`, `invoke` reaches
|
|
12
|
+
* `announce`, and `announce` reads this map. A computed key whose value is
|
|
13
|
+
* still uninitialised becomes the literal string "undefined" — no error, just a
|
|
14
|
+
* stage that silently never announces itself.
|
|
15
|
+
*/
|
|
16
|
+
export declare const STAGE_OF_SLUG: Record<string, StageId>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { CLOSURE_SLUG } from "./gates/closure-checklist/prompt.js";
|
|
2
|
+
import { GATE_SLUG } from "./gates/cqot-gate/prompt.js";
|
|
3
|
+
import { PREMORTEM_SLUG } from "./gates/premortem/prompt.js";
|
|
4
|
+
import { RED_TEAM_SLUG } from "./gates/red-team/prompt.js";
|
|
5
|
+
import { STANDARDS_SLUG } from "./gates/standards-review/prompt.js";
|
|
6
|
+
import { CHECKPOINT_SLUG } from "./gates/think-checkpoint/prompt.js";
|
|
7
|
+
import { COVE_ANSWERS_SLUG } from "./producers/cove-answers/prompt.js";
|
|
8
|
+
import { COVE_QUESTIONS_SLUG } from "./producers/cove-questions/prompt.js";
|
|
9
|
+
import { DECOMPOSE_SLUG } from "./producers/decompose/prompt.js";
|
|
10
|
+
import { TEST_SLUG } from "./producers/discriminating-test/prompt.js";
|
|
11
|
+
import { PLAN_SLUG } from "./producers/plan-candidates/prompt.js";
|
|
12
|
+
import { BRIEF_SLUG } from "./producers/qn-brief/prompt.js";
|
|
13
|
+
import { TOULMIN_SLUG } from "./producers/toulmin/prompt.js";
|
|
14
|
+
import { REFLECTION_SLUG } from "./repair/reflection/prompt.js";
|
|
15
|
+
import { TRIAGE_SLUG } from "./router/triage/prompt.js";
|
|
16
|
+
/**
|
|
17
|
+
* Which stage a slug belongs to.
|
|
18
|
+
*
|
|
19
|
+
* The inverse of what the fifteen modules already declare, and the reason
|
|
20
|
+
* `runStage` can announce a stage it was only ever told the slug of. A slug
|
|
21
|
+
* nobody claims is not an error — it means a caller reached `runStage` with a
|
|
22
|
+
* prompt of their own, and an event for it would be a lie.
|
|
23
|
+
*
|
|
24
|
+
* Imported from each `prompt.ts` rather than from the module's `index.ts`,
|
|
25
|
+
* which would be a cycle: an index re-exports `invoke`, `invoke` reaches
|
|
26
|
+
* `announce`, and `announce` reads this map. A computed key whose value is
|
|
27
|
+
* still uninitialised becomes the literal string "undefined" — no error, just a
|
|
28
|
+
* stage that silently never announces itself.
|
|
29
|
+
*/
|
|
30
|
+
export const STAGE_OF_SLUG = {
|
|
31
|
+
[TRIAGE_SLUG]: "0",
|
|
32
|
+
[BRIEF_SLUG]: "1",
|
|
33
|
+
[DECOMPOSE_SLUG]: "2",
|
|
34
|
+
[PLAN_SLUG]: "3A",
|
|
35
|
+
[PREMORTEM_SLUG]: "3B",
|
|
36
|
+
[GATE_SLUG]: "3C",
|
|
37
|
+
[CHECKPOINT_SLUG]: "4A",
|
|
38
|
+
[TEST_SLUG]: "4B",
|
|
39
|
+
[COVE_QUESTIONS_SLUG]: "5A",
|
|
40
|
+
[COVE_ANSWERS_SLUG]: "5B",
|
|
41
|
+
[STANDARDS_SLUG]: "5C",
|
|
42
|
+
[CLOSURE_SLUG]: "6A",
|
|
43
|
+
[RED_TEAM_SLUG]: "6B",
|
|
44
|
+
[TOULMIN_SLUG]: "6C",
|
|
45
|
+
[REFLECTION_SLUG]: "R",
|
|
46
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { JsonSchema, Llm } from "@spendgraph/llms";
|
|
2
|
+
import type { PromptClient } from "@spendgraph/prompt";
|
|
3
|
+
import { type Asked } from "./context.js";
|
|
4
|
+
import type { Emit } from "./events.js";
|
|
5
|
+
import { type Usage } from "./usage.js";
|
|
6
|
+
/** The reply came back, but not as the object the stage is supposed to return. */
|
|
7
|
+
export declare class StageReplyError extends Error {
|
|
8
|
+
readonly slug: string;
|
|
9
|
+
readonly raw: string;
|
|
10
|
+
constructor(slug: string, raw: string);
|
|
11
|
+
}
|
|
12
|
+
export interface StageOptions {
|
|
13
|
+
/**
|
|
14
|
+
* Attempts for this call, counting the first. Default 2.
|
|
15
|
+
*
|
|
16
|
+
* Separate from `maxRetries`, the loop's budget for a gate sending work
|
|
17
|
+
* backwards. This one covers a call that produced nothing to think about, and
|
|
18
|
+
* the two must not share an allowance.
|
|
19
|
+
*/
|
|
20
|
+
attempts?: number;
|
|
21
|
+
/** Overrides the model the `Llm` was built with. */
|
|
22
|
+
model?: string;
|
|
23
|
+
/** Merged into the request body. Where `temperature` goes, and it is per model. */
|
|
24
|
+
params?: Record<string, unknown>;
|
|
25
|
+
/**
|
|
26
|
+
* Where this stage says what it is doing.
|
|
27
|
+
*
|
|
28
|
+
* Set by `run()` on the options it threads to every stage, which is why no
|
|
29
|
+
* stage had to be edited to announce itself.
|
|
30
|
+
*/
|
|
31
|
+
emit?: Emit;
|
|
32
|
+
}
|
|
33
|
+
export interface StageOutcome<T> extends Usage {
|
|
34
|
+
data: T;
|
|
35
|
+
rolloutId: string;
|
|
36
|
+
model: string;
|
|
37
|
+
latencyMs: number;
|
|
38
|
+
/**
|
|
39
|
+
* What the server will price this call at, still in flight.
|
|
40
|
+
*
|
|
41
|
+
* Not awaited here. A stage that stalled on a report round trip would pay it
|
|
42
|
+
* once per stage over a run, for a number nothing reads until the end — so
|
|
43
|
+
* the ledger keeps these and `settle()` folds them in once.
|
|
44
|
+
*/
|
|
45
|
+
pricing?: Promise<number | undefined>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* One stored prompt, called and parsed.
|
|
49
|
+
*
|
|
50
|
+
* Every stage does the same four things — pull by slug, put the request through
|
|
51
|
+
* `{question}`, force the schema, hand back the object with what it cost — so
|
|
52
|
+
* they do it here rather than thirteen times over. The call is recorded once,
|
|
53
|
+
* as the prompt's rollout: the model client is told not to trace, or the same
|
|
54
|
+
* tokens land again as usage and every run prices double.
|
|
55
|
+
*/
|
|
56
|
+
export declare function runStage<T>(client: PromptClient, llm: Llm, slug: string, schema: JsonSchema, input: Asked, opts?: StageOptions): Promise<StageOutcome<T>>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { asked } from "./context.js";
|
|
2
|
+
import { askThrough, QUESTION_FIELD } from "./question.js";
|
|
3
|
+
import { attempting } from "./retry.js";
|
|
4
|
+
import { announce, failed } from "./started.js";
|
|
5
|
+
import { usageOf } from "./usage.js";
|
|
6
|
+
/** The reply came back, but not as the object the stage is supposed to return. */
|
|
7
|
+
export class StageReplyError extends Error {
|
|
8
|
+
slug;
|
|
9
|
+
raw;
|
|
10
|
+
constructor(slug, raw) {
|
|
11
|
+
super(`${slug} returned no object matching its schema.`);
|
|
12
|
+
this.slug = slug;
|
|
13
|
+
this.raw = raw;
|
|
14
|
+
this.name = "StageReplyError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* One stored prompt, called and parsed.
|
|
19
|
+
*
|
|
20
|
+
* Every stage does the same four things — pull by slug, put the request through
|
|
21
|
+
* `{question}`, force the schema, hand back the object with what it cost — so
|
|
22
|
+
* they do it here rather than thirteen times over. The call is recorded once,
|
|
23
|
+
* as the prompt's rollout: the model client is told not to trace, or the same
|
|
24
|
+
* tokens land again as usage and every run prices double.
|
|
25
|
+
*/
|
|
26
|
+
export async function runStage(client, llm, slug, schema, input, opts = {}) {
|
|
27
|
+
announce(opts.emit, slug, opts.model);
|
|
28
|
+
return attempting(async () => {
|
|
29
|
+
const prompt = await client.pull(slug);
|
|
30
|
+
const question = asked(input);
|
|
31
|
+
let reply;
|
|
32
|
+
const outcome = await prompt.call({ [QUESTION_FIELD]: question }, async ({ messages }) => {
|
|
33
|
+
reply = await llm.call(askThrough(messages, question, slug), {
|
|
34
|
+
model: opts.model,
|
|
35
|
+
schema,
|
|
36
|
+
trace: false,
|
|
37
|
+
params: opts.params,
|
|
38
|
+
});
|
|
39
|
+
return reply;
|
|
40
|
+
});
|
|
41
|
+
if (reply?.data === undefined)
|
|
42
|
+
throw new StageReplyError(slug, outcome.output);
|
|
43
|
+
return {
|
|
44
|
+
...usageOf(outcome),
|
|
45
|
+
...(outcome.pricing ? { pricing: outcome.pricing } : {}),
|
|
46
|
+
data: reply.data,
|
|
47
|
+
rolloutId: outcome.rolloutId,
|
|
48
|
+
model: outcome.model,
|
|
49
|
+
latencyMs: reply.latencyMs ?? 0,
|
|
50
|
+
};
|
|
51
|
+
}, { attempts: opts.attempts, onFailure: (err) => failed(opts.emit, slug, err) });
|
|
52
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Emit } from "./events.js";
|
|
2
|
+
/**
|
|
3
|
+
* Say a stage has begun, if anyone is listening and the slug is one of ours.
|
|
4
|
+
*
|
|
5
|
+
* Shared by the three call paths — `runStage` and the copies triage and 6C keep
|
|
6
|
+
* — so the three of them cannot drift on what a `stage.started` looks like.
|
|
7
|
+
*/
|
|
8
|
+
export declare function announce(emit: Emit | undefined, slug: string, model?: string): void;
|
|
9
|
+
/**
|
|
10
|
+
* Say a stage did not produce what it was asked for.
|
|
11
|
+
*
|
|
12
|
+
* The kind is read off the error's name rather than by importing the three
|
|
13
|
+
* reply errors, which would be a cycle — `stage.ts` reaches this file, and the
|
|
14
|
+
* errors live beside the calls that throw them. Every one of them ends in
|
|
15
|
+
* `ReplyError`, and that is the distinction worth drawing: a reply that came
|
|
16
|
+
* back wrong is the prompt's problem, anything else is the provider's.
|
|
17
|
+
*/
|
|
18
|
+
export declare function failed(emit: Emit | undefined, slug: string, err: unknown): void;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { STAGE_NAMES } from "./run/ledger.js";
|
|
2
|
+
import { STAGE_OF_SLUG } from "./slugs.js";
|
|
3
|
+
/**
|
|
4
|
+
* Say a stage has begun, if anyone is listening and the slug is one of ours.
|
|
5
|
+
*
|
|
6
|
+
* Shared by the three call paths — `runStage` and the copies triage and 6C keep
|
|
7
|
+
* — so the three of them cannot drift on what a `stage.started` looks like.
|
|
8
|
+
*/
|
|
9
|
+
export function announce(emit, slug, model) {
|
|
10
|
+
if (!emit)
|
|
11
|
+
return;
|
|
12
|
+
const stage = STAGE_OF_SLUG[slug];
|
|
13
|
+
if (!stage)
|
|
14
|
+
return;
|
|
15
|
+
emit({ type: "stage.started", stage, name: STAGE_NAMES[stage], slug, model });
|
|
16
|
+
}
|
|
17
|
+
const message = (err) => (err instanceof Error ? err.message : String(err));
|
|
18
|
+
/**
|
|
19
|
+
* Say a stage did not produce what it was asked for.
|
|
20
|
+
*
|
|
21
|
+
* The kind is read off the error's name rather than by importing the three
|
|
22
|
+
* reply errors, which would be a cycle — `stage.ts` reaches this file, and the
|
|
23
|
+
* errors live beside the calls that throw them. Every one of them ends in
|
|
24
|
+
* `ReplyError`, and that is the distinction worth drawing: a reply that came
|
|
25
|
+
* back wrong is the prompt's problem, anything else is the provider's.
|
|
26
|
+
*/
|
|
27
|
+
export function failed(emit, slug, err) {
|
|
28
|
+
if (!emit)
|
|
29
|
+
return;
|
|
30
|
+
const stage = STAGE_OF_SLUG[slug];
|
|
31
|
+
if (!stage)
|
|
32
|
+
return;
|
|
33
|
+
emit({
|
|
34
|
+
type: "stage.failed",
|
|
35
|
+
stage,
|
|
36
|
+
kind: err instanceof Error && err.name.endsWith("ReplyError") ? "reply" : "transport",
|
|
37
|
+
error: message(err),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { ToolBus } from "@spendgraph/tools";
|
|
2
|
+
import type { Asked } from "./context.js";
|
|
3
|
+
import type { NoesisEvent } from "./events.js";
|
|
4
|
+
import type { SubQuestionTool } from "./producers/decompose/index.js";
|
|
5
|
+
import type { AnswerOptions, AnswerResult } from "./producers/toulmin/index.js";
|
|
6
|
+
import type { TriageEstimates, TriageInput, TriageOptions } from "./router/triage/index.js";
|
|
7
|
+
import type { Escalation, StageUsage } from "./run/ledger.js";
|
|
8
|
+
import type { StageOptions } from "./stage.js";
|
|
9
|
+
import type { Usage } from "./usage.js";
|
|
10
|
+
/**
|
|
11
|
+
* How much of the loop a question earns.
|
|
12
|
+
*
|
|
13
|
+
* Derived in code from the triage estimates, which is what the router prompt
|
|
14
|
+
* itself says: it returns signals and never a route.
|
|
15
|
+
*/
|
|
16
|
+
export type Route = "direct" | "retrieve" | "standard" | "full";
|
|
17
|
+
/** What the estimates decide, before any of it is acted on. */
|
|
18
|
+
export interface Routing {
|
|
19
|
+
route: Route;
|
|
20
|
+
/** True where 6B runs before the answer is allowed out. */
|
|
21
|
+
redTeam: boolean;
|
|
22
|
+
/** Which signal decided it, for the record and for tuning the thresholds. */
|
|
23
|
+
reason: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* What a stage wants a person for, while the run is still going.
|
|
27
|
+
*
|
|
28
|
+
* Returning a replacement question restarts the framing with it; returning
|
|
29
|
+
* nothing carries on with the question as asked. The only stage that asks is 1,
|
|
30
|
+
* and only when it says the asker aimed at the wrong thing.
|
|
31
|
+
*/
|
|
32
|
+
export type Ask = (asked: AskedOfYou) => Promise<string | undefined> | string | undefined;
|
|
33
|
+
export interface AskedOfYou {
|
|
34
|
+
/** The question as it was put. */
|
|
35
|
+
question: string;
|
|
36
|
+
/** What stage 1 thinks should have been asked instead. */
|
|
37
|
+
better: string;
|
|
38
|
+
/** Why it says so. */
|
|
39
|
+
reason: string;
|
|
40
|
+
}
|
|
41
|
+
export interface RetrieveOptions {
|
|
42
|
+
/**
|
|
43
|
+
* What a lookup reaches for. Default `"deeprecall"`.
|
|
44
|
+
*
|
|
45
|
+
* Triage says how much a question needs a tool and never which one, so the
|
|
46
|
+
* route picks. The enum is ordered cheapest first and the knowledge base
|
|
47
|
+
* comes before the open web, which is the same preference.
|
|
48
|
+
*/
|
|
49
|
+
tool?: SubQuestionTool;
|
|
50
|
+
}
|
|
51
|
+
export interface VerifyOptions {
|
|
52
|
+
/**
|
|
53
|
+
* The confidence a blind check has to reach before it settles a claim.
|
|
54
|
+
* Default 0.6.
|
|
55
|
+
*
|
|
56
|
+
* Exposed for the same reason `route.ts` exposes its thresholds: it decides
|
|
57
|
+
* how much of stage 5 comes back `unsettled`, which is what 5C and 6A read,
|
|
58
|
+
* and moving it is a calibration rather than a rewrite.
|
|
59
|
+
*/
|
|
60
|
+
confidenceFloor?: number;
|
|
61
|
+
}
|
|
62
|
+
export interface NoesisOptions {
|
|
63
|
+
/**
|
|
64
|
+
* Everything triage takes except the question, which `run()` supplies.
|
|
65
|
+
*
|
|
66
|
+
* `currentTask` is the one worth passing on a second turn — it seeds the run
|
|
67
|
+
* and comes back on the result, updated.
|
|
68
|
+
*/
|
|
69
|
+
triage?: Omit<TriageInput, "question"> & TriageOptions;
|
|
70
|
+
/** The tools any route that looks something up may reach for. */
|
|
71
|
+
tools?: ToolBus;
|
|
72
|
+
/**
|
|
73
|
+
* The `now` and `locale` every stage is given rather than asked for.
|
|
74
|
+
*
|
|
75
|
+
* Without them anything turning on the date or the jurisdiction comes back as
|
|
76
|
+
* something the stage does not know.
|
|
77
|
+
*/
|
|
78
|
+
context?: Omit<Asked, "question">;
|
|
79
|
+
/** Model and params for every stage between triage and the answer. */
|
|
80
|
+
stage?: StageOptions;
|
|
81
|
+
/**
|
|
82
|
+
* Reached when a stage wants a person before it can go on.
|
|
83
|
+
*
|
|
84
|
+
* Without one the run proceeds on the question as asked, which is what it did
|
|
85
|
+
* before anybody could be asked at all.
|
|
86
|
+
*/
|
|
87
|
+
onAsk?: Ask;
|
|
88
|
+
/** Reached once an answer is trustworthy. Never on an abstention or an escalation. */
|
|
89
|
+
onDeliver?: (result: NoesisResult) => Promise<unknown> | unknown;
|
|
90
|
+
/**
|
|
91
|
+
* Reached as the run happens, for a progress display.
|
|
92
|
+
*
|
|
93
|
+
* Synchronous and fire-and-forget: a listener that throws is swallowed, and a
|
|
94
|
+
* slow one blocks the loop, so write to a queue rather than to a socket. For
|
|
95
|
+
* an async iterable instead of a callback, use `runStream()`.
|
|
96
|
+
*/
|
|
97
|
+
onEvent?: (event: NoesisEvent) => void;
|
|
98
|
+
/**
|
|
99
|
+
* How many times a gate may send work backwards before the run asks for a
|
|
100
|
+
* person. Default 5, counted across every gate together.
|
|
101
|
+
*/
|
|
102
|
+
maxRetries?: number;
|
|
103
|
+
retrieve?: RetrieveOptions;
|
|
104
|
+
/** How the blind checks in stage 5 are read. */
|
|
105
|
+
verify?: VerifyOptions;
|
|
106
|
+
answer?: AnswerOptions;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Whether the answer can be acted on.
|
|
110
|
+
*
|
|
111
|
+
* `abstained` is 6C saying the grounds supported no claim, or no tier clearing
|
|
112
|
+
* the confidence bar. `escalated` is a gate or a spent retry budget asking for
|
|
113
|
+
* a person. Both still carry `text`, because saying what is not known is an
|
|
114
|
+
* answer — it is just not one to build on.
|
|
115
|
+
*/
|
|
116
|
+
export type Status = "answered" | "abstained" | "escalated";
|
|
117
|
+
export interface NoesisResult {
|
|
118
|
+
/** The reply, as a person reads it. */
|
|
119
|
+
text: string;
|
|
120
|
+
status: Status;
|
|
121
|
+
/** Set where a gate or the retry budget asked for a person. */
|
|
122
|
+
escalation?: Escalation;
|
|
123
|
+
route: Route;
|
|
124
|
+
routing: Routing;
|
|
125
|
+
estimates: TriageEstimates;
|
|
126
|
+
/** The open task after this run. Pass it to the next one as `opts.triage.currentTask`. */
|
|
127
|
+
currentTask: string;
|
|
128
|
+
answer: AnswerResult;
|
|
129
|
+
/** Every rollout the run recorded, triage first. */
|
|
130
|
+
rolloutIds: string[];
|
|
131
|
+
/**
|
|
132
|
+
* `source_stage` values the answer cited that no stage in this run produced.
|
|
133
|
+
*
|
|
134
|
+
* Empty on a healthy run. A non-empty list is the answer citing evidence the
|
|
135
|
+
* loop never gathered, which is worth seeing before the claim is acted on.
|
|
136
|
+
*/
|
|
137
|
+
unattributed: string[];
|
|
138
|
+
/**
|
|
139
|
+
* Every token the run was billed for.
|
|
140
|
+
*
|
|
141
|
+
* Six numbers, not two: a cached read and a cache write are priced apart from
|
|
142
|
+
* a fresh input token, and a grounded or thinking model bills citations and
|
|
143
|
+
* reasoning on their own lines. `byModel` is the split a price list can
|
|
144
|
+
* actually be applied to — 6C cascades, so one run can span two models at
|
|
145
|
+
* different rates.
|
|
146
|
+
*
|
|
147
|
+
* A back edge does not refund anything. Retries are counted here in full even
|
|
148
|
+
* where `dropFrom` threw the artifacts away.
|
|
149
|
+
*
|
|
150
|
+
* Tool spend is not here. A tool call is no rollout and no tokens of ours;
|
|
151
|
+
* `deep_recall` reports `costMicroUsd` and `web_search` its own token counts,
|
|
152
|
+
* each inside its own output under the stage `4` artifact.
|
|
153
|
+
*/
|
|
154
|
+
usage: RunUsage;
|
|
155
|
+
/** @deprecated Read `usage.inputTokens`. Kept so existing callers still work. */
|
|
156
|
+
inputTokens: number;
|
|
157
|
+
/** @deprecated Read `usage.outputTokens`. Kept so existing callers still work. */
|
|
158
|
+
outputTokens: number;
|
|
159
|
+
latencyMs: number;
|
|
160
|
+
}
|
|
161
|
+
export interface RunUsage extends Usage {
|
|
162
|
+
/** Summed over every time the loop ran each stage. */
|
|
163
|
+
byStage: StageUsage[];
|
|
164
|
+
byModel: Record<string, Usage>;
|
|
165
|
+
/** Model calls made, counting a stage the loop ran more than once each time. */
|
|
166
|
+
calls: number;
|
|
167
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What one call cost, in tokens.
|
|
3
|
+
*
|
|
4
|
+
* Six numbers rather than two because four of them are billed separately: a
|
|
5
|
+
* cached read is a fraction of a fresh input token and a cache write is more
|
|
6
|
+
* than one, and a grounded or thinking model bills citations and reasoning on
|
|
7
|
+
* their own lines. Summing only `inputTokens` prices a cached run too high and
|
|
8
|
+
* a grounded one too low.
|
|
9
|
+
*/
|
|
10
|
+
export interface Usage {
|
|
11
|
+
inputTokens: number;
|
|
12
|
+
outputTokens: number;
|
|
13
|
+
cacheReadTokens: number;
|
|
14
|
+
cacheWriteTokens: number;
|
|
15
|
+
citationTokens: number;
|
|
16
|
+
reasoningTokens: number;
|
|
17
|
+
/**
|
|
18
|
+
* What the calls cost, in micro-USD, where the server priced them.
|
|
19
|
+
*
|
|
20
|
+
* Undefined rather than zero when nothing priced it, because the two are not
|
|
21
|
+
* the same claim: zero says the call was free, undefined says nobody knows.
|
|
22
|
+
* A run summed as zero would read as a free run, which is the one wrong
|
|
23
|
+
* answer a cost report must not give.
|
|
24
|
+
*
|
|
25
|
+
* This is the number to compare routes on. Tokens cannot be: 6C cascades
|
|
26
|
+
* cheap to expensive, so adding a haiku token to a sonnet token adds two
|
|
27
|
+
* different prices as though they were one unit, and an escalation reads as
|
|
28
|
+
* twice the tokens while costing far more than twice the money.
|
|
29
|
+
*/
|
|
30
|
+
costMicros?: number;
|
|
31
|
+
}
|
|
32
|
+
/** What a call reports, where every field is optional. */
|
|
33
|
+
export type PartialUsage = Partial<Usage>;
|
|
34
|
+
export declare const NO_USAGE: Usage;
|
|
35
|
+
/** Every token field filled, so a caller never has to guard one. Cost stays absent when it is. */
|
|
36
|
+
export declare const usageOf: (reported: PartialUsage) => Usage;
|
|
37
|
+
export declare const addUsage: (total: Usage, next: PartialUsage) => Usage;
|
|
38
|
+
/** Every token the call is billed for, however the provider split them. */
|
|
39
|
+
export declare const billableTokens: (usage: Usage) => number;
|
|
40
|
+
/** What the run is known to have cost, in micro-USD, or undefined where nothing priced it. */
|
|
41
|
+
export declare const spend: (usage: Usage) => number | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* One price for several calls: undefined unless at least one of them was priced.
|
|
44
|
+
*
|
|
45
|
+
* The cascade in 6C runs a tier at a time, so a run can hold several prices
|
|
46
|
+
* still in flight. Summing them with `?? 0` alone would turn "nobody priced
|
|
47
|
+
* any of these" into a confident zero.
|
|
48
|
+
*/
|
|
49
|
+
export declare const totalCost: (costs: (number | undefined)[]) => number | undefined;
|