fix(runtime): derive unattributed cost from usage status
This commit is contained in:
1 parent
f107415f4f
commit
ebcc15c68d
8 files changed
+61
-7
No files matched your search
@@ -56,6 +56,10 @@ export function classifyRunUsage(input: RunUsageClassificationInput): RunUsageSt
|
||||
return "pre_first_message";
|
||||
}
|
||||
|
||||
export function isUsageStatusUnattributed(status: RunUsageStatus): boolean {
|
||||
return status !== "ok";
|
||||
}
|
||||
|
||||
export function deriveLegacyRunUsageStatus(input: {
|
||||
runStatus: RunStatus;
|
||||
unattributedCost: boolean;
|
||||
|
||||
@@ -108,7 +108,6 @@ function cancelRun(runId: string) {
|
||||
signal: null,
|
||||
model: null,
|
||||
usageStatus: "aborted_before_usage",
|
||||
unattributedCost: true,
|
||||
inputTokens: null,
|
||||
outputTokens: null,
|
||||
});
|
||||
|
||||
@@ -53,10 +53,9 @@ export const workerMessageSchema = z.union([
|
||||
signal: z.string().nullable(),
|
||||
model: z.string().nullable(),
|
||||
usageStatus: runUsageStatusSchema,
|
||||
unattributedCost: z.boolean(),
|
||||
inputTokens: z.number().nullable(),
|
||||
outputTokens: z.number().nullable(),
|
||||
}),
|
||||
}).strict(),
|
||||
z.object({
|
||||
type: z.literal("agent-event"),
|
||||
runId: z.string().uuid(),
|
||||
|
||||
@@ -4,6 +4,7 @@ import { handleFailureEscalation, handleSuccessReset } from "../observability/fa
|
||||
import { persistRunEvent } from "../observability/run-event-recorder";
|
||||
import type { workerMessageSchema } from "../entry/types";
|
||||
import type { HandlerContext, HandlerDeps, WorkerMessageEvent } from "./handler-context";
|
||||
import { isUsageStatusUnattributed } from "../../run-usage-status";
|
||||
|
||||
type RunFinishedEvent = Extract<WorkerMessageEvent, { type: "run-finished" }>;
|
||||
type RunRecord = ReturnType<HandlerDeps["db"]["runs"]["getRun"]>;
|
||||
@@ -33,6 +34,8 @@ export function finalizeRun(
|
||||
return null;
|
||||
}
|
||||
|
||||
const unattributedCost = isUsageStatusUnattributed(event.usageStatus);
|
||||
|
||||
persistRunEvent(db, logger, {
|
||||
companyId: run.companyId,
|
||||
runId: run.id,
|
||||
@@ -48,7 +51,7 @@ export function finalizeRun(
|
||||
signal: event.signal,
|
||||
model: event.model,
|
||||
usageStatus: event.usageStatus,
|
||||
unattributedCost: event.unattributedCost,
|
||||
unattributedCost,
|
||||
inputTokens: event.inputTokens,
|
||||
outputTokens: event.outputTokens,
|
||||
},
|
||||
@@ -67,7 +70,7 @@ export function finalizeRun(
|
||||
}
|
||||
|
||||
leaseRenewalMap.delete(event.runId);
|
||||
const runOutcome = db.runs.finishRun(event);
|
||||
const runOutcome = db.runs.finishRun({ ...event, unattributedCost });
|
||||
if (run.workspaceId) {
|
||||
db.workspacesMod.releaseWorkspaceLock(run.workspaceId, run.id);
|
||||
}
|
||||
|
||||
@@ -65,7 +65,6 @@ export type ChildMessage =
|
||||
signal: string | null;
|
||||
model: string | null;
|
||||
usageStatus: RunUsageStatus;
|
||||
unattributedCost: boolean;
|
||||
inputTokens: number | null;
|
||||
outputTokens: number | null;
|
||||
}
|
||||
|
||||
@@ -290,7 +290,6 @@ export class WorkerRunner {
|
||||
signal: null,
|
||||
model: resolvedModelId,
|
||||
usageStatus,
|
||||
unattributedCost: usageStatus !== "ok",
|
||||
inputTokens: finalUsage.inputTokens,
|
||||
outputTokens: finalUsage.outputTokens,
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
classifyRunUsage,
|
||||
deriveLegacyRunUsageStatus,
|
||||
hasAttributableAssistantUsage,
|
||||
isUsageStatusUnattributed,
|
||||
} from "../../src/main/run-usage-status";
|
||||
|
||||
describe("run usage status", () => {
|
||||
@@ -81,4 +82,11 @@ describe("run usage status", () => {
|
||||
);
|
||||
expect(deriveLegacyRunUsageStatus({ runStatus: "failed", unattributedCost: true })).toBe("pre_first_message");
|
||||
});
|
||||
|
||||
it("derives the legacy unattributed-cost flag from usage status", () => {
|
||||
expect(isUsageStatusUnattributed("ok")).toBe(false);
|
||||
expect(isUsageStatusUnattributed("pre_first_message")).toBe(true);
|
||||
expect(isUsageStatusUnattributed("error_stop")).toBe(true);
|
||||
expect(isUsageStatusUnattributed("aborted_before_usage")).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { workerMessageSchema } from "../../src/main/worker/entry/types";
|
||||
|
||||
const RUN_ID = "00000000-0000-4000-8000-000000000001";
|
||||
|
||||
describe("worker message schema", () => {
|
||||
it("accepts run-finished messages keyed by usage status", () => {
|
||||
expect(
|
||||
workerMessageSchema.safeParse({
|
||||
type: "run-finished",
|
||||
runId: RUN_ID,
|
||||
status: "failed",
|
||||
summary: null,
|
||||
errorMessage: "Agent stopped with error.",
|
||||
exitCode: null,
|
||||
signal: null,
|
||||
model: "gpt-5.4",
|
||||
usageStatus: "error_stop",
|
||||
inputTokens: 12,
|
||||
outputTokens: 1,
|
||||
}).success,
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects legacy unattributed-cost truth in run-finished messages", () => {
|
||||
expect(
|
||||
workerMessageSchema.safeParse({
|
||||
type: "run-finished",
|
||||
runId: RUN_ID,
|
||||
status: "failed",
|
||||
summary: null,
|
||||
errorMessage: "Agent stopped with error.",
|
||||
exitCode: null,
|
||||
signal: null,
|
||||
model: "gpt-5.4",
|
||||
usageStatus: "error_stop",
|
||||
unattributedCost: false,
|
||||
inputTokens: 12,
|
||||
outputTokens: 1,
|
||||
}).success,
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user