Keep full consultation tool contracts unchanged. Persist short replies and refund the original reservation atomically while recording actual model usage. Verify Linux frontend 3566/3566, database 40/40, Static home and gzip +0.0493%. Co-Authored-By: Claude Code <noreply@anthropic.com>
40 lines
1.6 KiB
TypeScript
40 lines
1.6 KiB
TypeScript
import { consultationAgentPublicEventSchema, type ConsultationAgentPublicEvent } from "./consultation-agent-events.ts";
|
|
|
|
/** Same NDJSON public envelope as consultation; no fake workflow or skill receipt. */
|
|
export function streamSmalltalkResponse(input: {
|
|
requestId: string;
|
|
reply: string;
|
|
complete: () => Promise<void>;
|
|
onError: () => Promise<void>;
|
|
}) {
|
|
const encoder = new TextEncoder();
|
|
let disconnected = false;
|
|
const body = new ReadableStream<Uint8Array>({
|
|
async start(controller) {
|
|
const send = (event: ConsultationAgentPublicEvent) => {
|
|
if (!disconnected) controller.enqueue(encoder.encode(`${JSON.stringify(consultationAgentPublicEventSchema.parse(event))}\n`));
|
|
};
|
|
try {
|
|
// Persist/refund before emitting success or text; a rejected/failed RPC is not a reply.
|
|
await input.complete();
|
|
send({ type: "answer.delta", text: input.reply });
|
|
send({ type: "run.completed", responseKind: "smalltalk" });
|
|
} catch {
|
|
await input.onError().catch(() => {});
|
|
send({ type: "run.failed", code: "calculation_failed", message: "这次回复没能保存,请重试。" });
|
|
} finally {
|
|
if (!disconnected) controller.close();
|
|
}
|
|
},
|
|
cancel() { disconnected = true; }, // Server settlement continues, as on the main stream.
|
|
});
|
|
return new Response(body, { headers: {
|
|
"content-type": "application/x-ndjson; charset=utf-8",
|
|
"cache-control": "no-cache, no-transform",
|
|
"x-accel-buffering": "no",
|
|
"x-ayanam-mode": "mastra-agentic",
|
|
"x-ayanam-request-id": input.requestId,
|
|
"x-jyotish-response-kind": "smalltalk",
|
|
} });
|
|
}
|