test: add agent evals and safe observability

This commit is contained in:
Jesse_Chen
2026-08-15 07:06:19 +08:00
parent 6d192ad175
commit e58bc36909
8 changed files with 4385 additions and 35 deletions
+93 -33
View File
@@ -10,6 +10,12 @@ import {
import { blocksPromptExtraction } from "@/lib/consult-safety";
import { consultationDomainSchema } from "@/lib/consultation-domain-registry";
import { parseAgentReply } from "@/lib/agent-reply";
import {
logAgentObservability,
settlementTelemetryOutcome,
toAgentObservabilityErrorCode,
type AgentSettlementResult,
} from "@/lib/agent-observability";
import {
consultationEntrypointSchema,
resolveConsultationQuestion,
@@ -355,7 +361,7 @@ export async function POST(request: Request) {
}
async function cancel() {
async function cancel(): Promise<AgentSettlementResult> {
try {
const result = await retryDetachedSettlement(async () => {
const { data, error } = await accounting.rpc("cancel_consultation_credit", {
@@ -371,11 +377,13 @@ export async function POST(request: Request) {
if (!result.success && result.error_code !== "request_completed") {
throw new CreditRpcError(result.error_code || "cancellation_rejected");
}
return "cancelled";
} catch (error) {
const reason = error instanceof Error ? error.name : "UnknownError";
console.error(
`[billing] cancellation failed request=${requestId} reason=${reason}`,
);
return "failed";
}
}
@@ -404,7 +412,7 @@ export async function POST(request: Request) {
techniqueTruth: string,
workflowReceipt: WorkflowReceipt,
agentExecutionReceipt?: AgentExecutionReceipt,
) {
): Promise<AgentSettlementResult> {
try {
const reply = parseAgentReply(rawTransformedText, consultationTheme);
if (!reply.text) throw new Error("empty_agent_reply");
@@ -434,17 +442,21 @@ export async function POST(request: Request) {
if (!completion.success && completion.error_code !== "request_cancelled") {
throw new CreditRpcError(completion.error_code || "completion_rejected");
}
return "completed";
} catch (error) {
await cancel();
throw error;
}
}
let settlement: Promise<void> | null = null;
function settle(action: () => Promise<void>) {
let settlement: Promise<AgentSettlementResult> | null = null;
function settleResult(action: () => Promise<AgentSettlementResult>) {
settlement ??= action();
return settlement;
}
async function settle(action: () => Promise<AgentSettlementResult>): Promise<void> {
await settleResult(action);
}
async function runAgenticConsultation(
consultationMode: ConsultationBirthTimeMode,
@@ -460,32 +472,82 @@ export async function POST(request: Request) {
let logged = false;
const markFirstActivity = () => { if (firstActivityMs < 0) firstActivityMs = Date.now() - agentStartedAt; };
const markFirstText = () => { if (firstTextMs < 0) firstTextMs = Date.now() - agentStartedAt; };
const logRun = (finishReason: string, settlementResult: string) => {
const logRun = async (
errorCode: string | undefined,
settlementResult: AgentSettlementResult,
) => {
if (logged) return;
logged = true;
console.info([
"[consult-agentic]",
`request_id=${requestId}`,
`run_id=${requestId}`,
`session_id=${sessionId}`,
`model_id=${selectedModel.id}`,
`skill_loaded=${state.jyotishSkillLoaded}`,
`skill_reference_read_count=${state.skillReferenceReadCount}`,
`consultation_tool_call_count=${state.consultationToolCallCount}`,
`consultation_tool_duration_ms=${state.consultationToolDurationMs ?? -1}`,
`time_to_first_activity_ms=${firstActivityMs}`,
`time_to_first_text_ms=${firstTextMs}`,
`total_duration_ms=${Date.now() - agentStartedAt}`,
`finish_reason=${finishReason}`,
`settlement_result=${settlementResult}`,
].join(" "));
const resolvedUsage: Usage = await mergeUsage(usages).catch(() => ({}));
const inputTokens = Math.max(0, Math.trunc(resolvedUsage.inputTokens ?? 0));
const outputTokens = Math.max(0, Math.trunc(resolvedUsage.outputTokens ?? 0));
const skillStep = state.steps.find((step) => step.kind === "skill");
const runStatus = errorCode === undefined
? "completed"
: errorCode === "cancelled"
? "cancelled"
: "failed";
logAgentObservability({
runId: requestId,
requestId,
sessionId,
agentVersion: "consultation-agentic-v1",
modelVersion: String(selectedModel.configVersion),
policyVersion: "consultation-runtime-contract-v1",
toolCalls: state.steps
.filter((step) => step.kind === "tool")
.map((step) => ({
name: step.name,
durationMs: Math.max(0, Math.trunc(step.durationMs ?? 0)),
status: step.status,
})),
contractPhases: [
{
phase: "skill.load",
...(skillStep?.durationMs === undefined
? {}
: { durationMs: Math.max(0, Math.trunc(skillStep.durationMs)) }),
status: state.jyotishSkillLoaded ? "completed" : "failed",
},
{
phase: "answer.first_activity",
...(firstActivityMs < 0 ? {} : { durationMs: firstActivityMs }),
status: firstActivityMs < 0 ? "skipped" : "completed",
},
{
phase: "answer.first_output",
...(firstTextMs < 0 ? {} : { durationMs: firstTextMs }),
status: firstTextMs < 0 ? "skipped" : "completed",
},
{
phase: "run.total",
durationMs: Date.now() - agentStartedAt,
status: runStatus,
},
{
phase: "billing.settled",
status: settlementResult,
},
],
retryCount: Math.max(0, usages.length - 1),
...(errorCode === undefined ? {} : { errorCode }),
inputTokens,
outputTokens,
themeCoverage: state.workflowReceipt?.domains ?? [consultationTheme],
billingSettlementResult: settlementResult,
});
};
const settleRun = async (action: () => Promise<void>, finishReason: string, settlementResult: string) => {
const settleRun = async (
action: () => Promise<AgentSettlementResult>,
errorCode: string | undefined,
) => {
try {
await settle(action);
logRun(finishReason, settlementResult);
const actualSettlementResult = await settleResult(action);
const outcome = settlementTelemetryOutcome(actualSettlementResult, errorCode);
await logRun(outcome.errorCode, outcome.billingSettlementResult);
} catch (error) {
logRun("settlement_failed", "failed");
const outcome = settlementTelemetryOutcome("failed", errorCode);
await logRun(outcome.errorCode, outcome.billingSettlementResult);
throw error;
}
};
@@ -557,13 +619,12 @@ export async function POST(request: Request) {
"not-applicable",
workflowReceipt,
agentExecutionReceipt,
), "completed", "completed"),
), undefined),
onError: (error) => settleRun(
cancel,
error instanceof Error ? error.message : "failed",
"cancelled",
toAgentObservabilityErrorCode(error),
),
onCancel: () => settleRun(cancel, "cancelled", "cancelled"),
onCancel: () => settleRun(cancel, "cancelled"),
});
}
@@ -622,13 +683,12 @@ export async function POST(request: Request) {
state.techniqueTruth ?? "unknown",
state.workflowReceipt ?? workflowReceipt,
agentExecutionReceipt,
), "completed", "completed"),
), undefined),
onError: (error) => settleRun(
cancel,
error instanceof Error ? error.message : "failed",
"cancelled",
toAgentObservabilityErrorCode(error),
),
onCancel: () => settleRun(cancel, "cancelled", "cancelled"),
onCancel: () => settleRun(cancel, "cancelled"),
});
}