broapp 0.4.4 → 0.4.5
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/package.json +1 -1
- package/src/ai/host/create-ai.ts +10 -0
- package/src/ai/host/run.ts +18 -0
package/package.json
CHANGED
package/src/ai/host/create-ai.ts
CHANGED
|
@@ -39,6 +39,8 @@ export interface RunEndDetail {
|
|
|
39
39
|
/** Tool round trips the turn made. */
|
|
40
40
|
readonly steps: number;
|
|
41
41
|
readonly ms: number;
|
|
42
|
+
/** The model the turn was sent to; absent when the turn ended before one was resolved. */
|
|
43
|
+
readonly modelId?: string;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
/**
|
|
@@ -165,6 +167,13 @@ export interface CreateAiOptions {
|
|
|
165
167
|
* reason to fail it.
|
|
166
168
|
*/
|
|
167
169
|
readonly onContext?: (runId: string, delivered: DeliveredContext) => void;
|
|
170
|
+
/**
|
|
171
|
+
* Called after each completed model step of a turn, with the tokens its
|
|
172
|
+
* completed steps have used so far: what a running turn has cost, before
|
|
173
|
+
* `onRunEnd` says what it cost in all. A hook that throws is logged and
|
|
174
|
+
* ignored, like the others.
|
|
175
|
+
*/
|
|
176
|
+
readonly onUsageSoFar?: (runId: string, soFar: { inputTokens: number; outputTokens: number }) => void;
|
|
168
177
|
}
|
|
169
178
|
|
|
170
179
|
/**
|
|
@@ -334,6 +343,7 @@ export function createAi(options: CreateAiOptions): Ai {
|
|
|
334
343
|
approvals,
|
|
335
344
|
...(options.onRunEnd === undefined ? {} : { onRunEnd: options.onRunEnd }),
|
|
336
345
|
...(options.onContext === undefined ? {} : { onContext: options.onContext }),
|
|
346
|
+
...(options.onUsageSoFar === undefined ? {} : { onUsageSoFar: options.onUsageSoFar }),
|
|
337
347
|
transcripts: {
|
|
338
348
|
save: (runId, messages) => {
|
|
339
349
|
threadStore().saveTranscript(runId, messages);
|
package/src/ai/host/run.ts
CHANGED
|
@@ -75,6 +75,13 @@ export interface RunDeps {
|
|
|
75
75
|
) => void;
|
|
76
76
|
/** Called once per turn with what the model is about to be given. */
|
|
77
77
|
readonly onContext?: (runId: string, delivered: DeliveredContext) => void;
|
|
78
|
+
/**
|
|
79
|
+
* Called after each model step completes, with what the turn's completed
|
|
80
|
+
* steps have used so far. Not the SDK's `onStepEnd`, which this layer
|
|
81
|
+
* already hands to `streamText`: this is the turn's running subtotal, for
|
|
82
|
+
* somebody who wants to say what a turn has cost before it ends.
|
|
83
|
+
*/
|
|
84
|
+
readonly onUsageSoFar?: (runId: string, soFar: { inputTokens: number; outputTokens: number }) => void;
|
|
78
85
|
/**
|
|
79
86
|
* The turn transcripts. Absent, every history turn is text and nothing is
|
|
80
87
|
* written, which is exactly the layer before transcripts existed.
|
|
@@ -95,6 +102,8 @@ interface TurnTally {
|
|
|
95
102
|
stepsEnded: number;
|
|
96
103
|
stepInput: number;
|
|
97
104
|
stepOutput: number;
|
|
105
|
+
/** The model the turn was sent to, once it is resolved. */
|
|
106
|
+
modelId?: string;
|
|
98
107
|
}
|
|
99
108
|
|
|
100
109
|
/**
|
|
@@ -798,6 +807,7 @@ export async function runChat(
|
|
|
798
807
|
steps: tally.steps,
|
|
799
808
|
ms: Date.now() - started,
|
|
800
809
|
...(tally.usage === undefined ? {} : { usage: tally.usage }),
|
|
810
|
+
...(tally.modelId === undefined ? {} : { modelId: tally.modelId }),
|
|
801
811
|
};
|
|
802
812
|
safely(deps.logger, 'onRunEnd', () =>
|
|
803
813
|
onRunEnd(params.runId, status, params.message.slice(0, SUMMARY_CHARS), detail),
|
|
@@ -860,6 +870,9 @@ async function runTurn(
|
|
|
860
870
|
// after the provider and key checks, so the vision check below and the model
|
|
861
871
|
// instance built later both follow it without a second code path.
|
|
862
872
|
const resolved = await deps.registry.resolve({ modelId: params.modelId });
|
|
873
|
+
// Known here and nowhere later: a listener writing down what the turn used
|
|
874
|
+
// is told which model used it, not left to guess from settings since changed.
|
|
875
|
+
tally.modelId = resolved.modelId;
|
|
863
876
|
|
|
864
877
|
// Both checks come before anything is emitted, so a turn that cannot carry
|
|
865
878
|
// its images fails as a whole rather than half-answering.
|
|
@@ -930,6 +943,11 @@ async function runTurn(
|
|
|
930
943
|
tally.stepsEnded += 1;
|
|
931
944
|
tally.stepInput += step.usage.inputTokens ?? 0;
|
|
932
945
|
tally.stepOutput += step.usage.outputTokens ?? 0;
|
|
946
|
+
const onUsageSoFar = deps.onUsageSoFar;
|
|
947
|
+
if (onUsageSoFar !== undefined) {
|
|
948
|
+
const soFar = { inputTokens: tally.stepInput, outputTokens: tally.stepOutput };
|
|
949
|
+
safely(deps.logger, 'onUsageSoFar', () => onUsageSoFar(params.runId, soFar));
|
|
950
|
+
}
|
|
933
951
|
},
|
|
934
952
|
});
|
|
935
953
|
|