fix(runtime): classify run usage status
This commit is contained in:
1 parent
d971ca7929
commit
eefb515aa2
17 files changed
+308
-26
No files matched your search
@@ -107,6 +107,7 @@ function cancelRun(runId: string) {
|
||||
exitCode: null,
|
||||
signal: null,
|
||||
model: null,
|
||||
usageStatus: "aborted_before_usage",
|
||||
unattributedCost: true,
|
||||
inputTokens: null,
|
||||
outputTokens: null,
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { z } from "zod";
|
||||
import type { AppDatabase } from "../../database";
|
||||
import type { DesktopEvent, RunRecord } from "@shared/types";
|
||||
import { RUN_USAGE_STATUSES, type DesktopEvent, type RunRecord } from "@shared/types";
|
||||
import type { NotificationOptions } from "../../notification-service";
|
||||
import type { createBudgetEnforcementService } from "../../budget-enforcement-service";
|
||||
import type { createSessionManagementService } from "../../session-management-service";
|
||||
import type { RuntimeBridge, RuntimeMessageInjector } from "../../agent-runtime";
|
||||
|
||||
const runStatusSchema = z.enum(["queued", "running", "succeeded", "failed", "cancelled", "timed_out", "interrupted"]);
|
||||
const runUsageStatusSchema = z.enum(RUN_USAGE_STATUSES);
|
||||
|
||||
/**
|
||||
* Single source of truth for worker → main RPC method names.
|
||||
@@ -51,6 +52,7 @@ export const workerMessageSchema = z.union([
|
||||
exitCode: z.number().int().nullable(),
|
||||
signal: z.string().nullable(),
|
||||
model: z.string().nullable(),
|
||||
usageStatus: runUsageStatusSchema,
|
||||
unattributedCost: z.boolean(),
|
||||
inputTokens: z.number().nullable(),
|
||||
outputTokens: z.number().nullable(),
|
||||
|
||||
@@ -47,6 +47,7 @@ export function finalizeRun(
|
||||
exitCode: event.exitCode,
|
||||
signal: event.signal,
|
||||
model: event.model,
|
||||
usageStatus: event.usageStatus,
|
||||
unattributedCost: event.unattributedCost,
|
||||
inputTokens: event.inputTokens,
|
||||
outputTokens: event.outputTokens,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
||||
import type { RunStatus } from "@shared/types";
|
||||
import type { RunStatus, RunUsageStatus } from "@shared/types";
|
||||
|
||||
export interface RunJob {
|
||||
runId: string;
|
||||
@@ -64,6 +64,7 @@ export type ChildMessage =
|
||||
exitCode: number | null;
|
||||
signal: string | null;
|
||||
model: string | null;
|
||||
usageStatus: RunUsageStatus;
|
||||
unattributedCost: boolean;
|
||||
inputTokens: number | null;
|
||||
outputTokens: number | null;
|
||||
|
||||
@@ -8,6 +8,12 @@ import { createPiAgent, resolveThinkingLevel } from "./pi-agent-factory";
|
||||
import type { WorkerRpcClient } from "../entry/rpc";
|
||||
import { createHireAgentTool, HIRE_AGENT_TOOL_NAME } from "./internal-tools/hire-agent-tool";
|
||||
import { createFileApprovalTool, FILE_APPROVAL_TOOL_NAME } from "./internal-tools/file-approval-tool";
|
||||
import {
|
||||
classifyRunUsage,
|
||||
hasAttributableAssistantUsage,
|
||||
isTerminalAssistantStopReason,
|
||||
type TerminalAssistantStopReason,
|
||||
} from "../../run-usage-status";
|
||||
|
||||
interface PrepareRunResult {
|
||||
provider: string;
|
||||
@@ -100,6 +106,8 @@ export class WorkerRunner {
|
||||
inputTokens: null,
|
||||
outputTokens: null,
|
||||
};
|
||||
let sawAttributableUsage = false;
|
||||
let terminalStopReason: TerminalAssistantStopReason | null = null;
|
||||
let errorMessage: string | null = null;
|
||||
|
||||
// Inject the AgentCompany job env (AGENT_COMPANY_API_URL / API_KEY /
|
||||
@@ -217,12 +225,12 @@ export class WorkerRunner {
|
||||
errorMessage?: string;
|
||||
usage?: { input?: number; output?: number; cost?: { total?: number } };
|
||||
};
|
||||
if (
|
||||
maybeAssistant.role === "assistant"
|
||||
&& (maybeAssistant.stopReason === "error" || maybeAssistant.stopReason === "aborted")
|
||||
) {
|
||||
if (maybeAssistant.role === "assistant" && isTerminalAssistantStopReason(maybeAssistant.stopReason)) {
|
||||
terminalStopReason = maybeAssistant.stopReason;
|
||||
errorMessage = maybeAssistant.errorMessage ?? `Agent stopped with ${maybeAssistant.stopReason}.`;
|
||||
} else if (maybeAssistant.role === "assistant" && maybeAssistant.usage) {
|
||||
}
|
||||
if (hasAttributableAssistantUsage(maybeAssistant)) {
|
||||
sawAttributableUsage = true;
|
||||
finalUsage = {
|
||||
inputTokens: typeof maybeAssistant.usage.input === "number" ? maybeAssistant.usage.input : finalUsage.inputTokens,
|
||||
outputTokens: typeof maybeAssistant.usage.output === "number" ? maybeAssistant.usage.output : finalUsage.outputTokens,
|
||||
@@ -265,6 +273,12 @@ export class WorkerRunner {
|
||||
: errorMessage
|
||||
? "failed"
|
||||
: "succeeded";
|
||||
const usageStatus = classifyRunUsage({
|
||||
runStatus: status,
|
||||
errorMessage,
|
||||
sawAttributableUsage,
|
||||
terminalStopReason,
|
||||
});
|
||||
|
||||
post({
|
||||
type: "run-finished",
|
||||
@@ -275,12 +289,8 @@ export class WorkerRunner {
|
||||
exitCode: null,
|
||||
signal: null,
|
||||
model: resolvedModelId,
|
||||
// Worker no longer owns cost attribution — CostWriter (main-side)
|
||||
// writes per-message rows, finishRun sums them. This flag stays true
|
||||
// only for run paths that never had a chance to emit usage (errors
|
||||
// before the first message_end); per-message drift detection belongs
|
||||
// elsewhere.
|
||||
unattributedCost: finalUsage.inputTokens === null,
|
||||
usageStatus,
|
||||
unattributedCost: usageStatus !== "ok",
|
||||
inputTokens: finalUsage.inputTokens,
|
||||
outputTokens: finalUsage.outputTokens,
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user