feat(consult): add free model-classified smalltalk fast path
Independent Staging Quality Gate / validate (push) Successful in 9m35s
Independent Staging Quality Gate / publish (push) Successful in 3m52s

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>
This commit is contained in:
jesse-ux
2026-09-20 12:58:34 +08:00
co-authored by Claude Code
parent 03cba4780a
commit 539d4daee4
33 changed files with 960 additions and 30 deletions
+19 -10
View File
@@ -734,6 +734,7 @@ export function useConsultationRun(params: ConsultationRunParams) {
setConsultationPhase("streaming");
}
setStreamingReply({ sessionId, text: "", timeline: [] });
let responseKind: "smalltalk" | undefined;
let latestPartialReply = "";
let thinkingSections: PublicThinkingSection[] = [];
let streamedThinking = "";
@@ -751,6 +752,7 @@ export function useConsultationRun(params: ConsultationRunParams) {
setStreamingReply({
sessionId,
text: partialReply,
responseKind,
thinkingText: frame.thinking.trim() || undefined,
thinkingSections: thinkingSections.length ? thinkingSections : undefined,
timeline: timelineState.rows,
@@ -807,6 +809,7 @@ export function useConsultationRun(params: ConsultationRunParams) {
if (!response.body) {
throw new ConsultationResponseError(502, "浏览器未收到可读取的回答流");
}
responseKind = response.headers.get("x-jyotish-response-kind") === "smalltalk" ? "smalltalk" : undefined;
let techniqueTruth = response.headers.get("x-jyotish-technique-truth") ?? "unknown";
let workflowReceipt: AgentExecutionReceipt["workflow"] = {
route: response.headers.get("x-jyotish-workflow-route") ?? "unknown",
@@ -859,7 +862,7 @@ export function useConsultationRun(params: ConsultationRunParams) {
if ((response.headers.get("content-type") ?? "").includes("application/x-ndjson")) {
const parser = createNdjsonParser((event) => {
timelineState = reduceConsultationTimeline(timelineState, event);
if (responseKind !== "smalltalk") timelineState = reduceConsultationTimeline(timelineState, event);
frames.touch();
if (event.type === "answer.delta") {
answer += event.text;
@@ -892,9 +895,13 @@ export function useConsultationRun(params: ConsultationRunParams) {
}
if (event.type === "run.completed") {
runCompleted = true;
agentExecutionReceipt = event.receipt;
workflowReceipt = event.receipt.workflow;
techniqueTruth = event.receipt.techniqueTruth ?? "unknown";
if (event.responseKind === "smalltalk") {
responseKind = "smalltalk";
} else if (event.receipt) {
agentExecutionReceipt = event.receipt;
workflowReceipt = event.receipt.workflow;
techniqueTruth = event.receipt.techniqueTruth ?? "unknown";
}
}
if (event.type === "run.failed") {
if (event.code === "answer_truncated") {
@@ -908,7 +915,7 @@ export function useConsultationRun(params: ConsultationRunParams) {
}
throw new ConsultationResponseError(502, event.message);
}
updateActivity(event);
if (responseKind !== "smalltalk") updateActivity(event);
});
while (true) {
const { done, value } = await reader.read();
@@ -983,11 +990,13 @@ export function useConsultationRun(params: ConsultationRunParams) {
messages: [...userSession.messages, {
role: "assistant",
text: reply.text,
...(streamedThinking.trim() ? { thinkingText: streamedThinking.trim().slice(0, 4000) } : {}),
...(thinkingSections.length ? { thinkingSections } : {}),
techniqueTruth,
workflowReceipt,
agentExecutionReceipt,
...(responseKind === "smalltalk" ? { responseKind } : {
...(streamedThinking.trim() ? { thinkingText: streamedThinking.trim().slice(0, 4000) } : {}),
...(thinkingSections.length ? { thinkingSections } : {}),
techniqueTruth,
workflowReceipt,
agentExecutionReceipt,
}),
}],
updatedAt: timestamp(),
};