Stream model events to activity panel
This commit is contained in:
@@ -32,13 +32,16 @@ type ClawChatResponse = {
|
|||||||
type ClawRuntimeEvent = {
|
type ClawRuntimeEvent = {
|
||||||
type?: string;
|
type?: string;
|
||||||
run_id?: string;
|
run_id?: string;
|
||||||
|
tool_call_index?: number | null;
|
||||||
tool_name?: string;
|
tool_name?: string;
|
||||||
tool_call_id?: string;
|
tool_call_id?: string;
|
||||||
arguments?: unknown;
|
arguments?: unknown;
|
||||||
|
arguments_delta?: string;
|
||||||
assistant_content?: string;
|
assistant_content?: string;
|
||||||
metadata?: Record<string, unknown>;
|
metadata?: Record<string, unknown>;
|
||||||
ok?: boolean;
|
ok?: boolean;
|
||||||
delta?: string;
|
delta?: string;
|
||||||
|
finish_reason?: string | null;
|
||||||
elapsed_ms?: number;
|
elapsed_ms?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,6 +55,7 @@ type StreamState = {
|
|||||||
textStreamed: boolean;
|
textStreamed: boolean;
|
||||||
phase: "waiting" | "queued" | "running" | "done";
|
phase: "waiting" | "queued" | "running" | "done";
|
||||||
runId?: string;
|
runId?: string;
|
||||||
|
announcedToolCallKeys: Set<string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ChatRequestBody = {
|
type ChatRequestBody = {
|
||||||
@@ -107,6 +111,7 @@ export async function POST(req: Request) {
|
|||||||
textStarted: false,
|
textStarted: false,
|
||||||
textStreamed: false,
|
textStreamed: false,
|
||||||
phase: "waiting",
|
phase: "waiting",
|
||||||
|
announcedToolCallKeys: new Set<string>(),
|
||||||
};
|
};
|
||||||
writer.write({ type: "start" });
|
writer.write({ type: "start" });
|
||||||
writer.write({ type: "start-step" });
|
writer.write({ type: "start-step" });
|
||||||
@@ -491,6 +496,28 @@ function writeRuntimeEvent(
|
|||||||
delta: `\n${phase}${elapsed}。`,
|
delta: `\n${phase}${elapsed}。`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (event.type === "content_delta" && event.delta) {
|
||||||
|
writer.write({
|
||||||
|
type: "reasoning-delta",
|
||||||
|
id: "reasoning-1",
|
||||||
|
delta: event.delta,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (event.type === "tool_call_delta") {
|
||||||
|
const key =
|
||||||
|
event.tool_call_id ??
|
||||||
|
(event.tool_call_index === null || event.tool_call_index === undefined
|
||||||
|
? undefined
|
||||||
|
: `index:${event.tool_call_index}`);
|
||||||
|
if (key && !streamState?.announcedToolCallKeys.has(key)) {
|
||||||
|
streamState?.announcedToolCallKeys.add(key);
|
||||||
|
writer.write({
|
||||||
|
type: "reasoning-delta",
|
||||||
|
id: "reasoning-1",
|
||||||
|
delta: `\n准备调用 ${event.tool_name ?? "工具"}。`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
if (event.type === "final_text_start") {
|
if (event.type === "final_text_start") {
|
||||||
if (!streamState?.textStarted) {
|
if (!streamState?.textStarted) {
|
||||||
writer.write({ type: "text-start", id: "text-1" });
|
writer.write({ type: "text-start", id: "text-1" });
|
||||||
|
|||||||
+16
-3
@@ -692,7 +692,11 @@ class LocalCodingAgent:
|
|||||||
self.last_run_result = result
|
self.last_run_result = result
|
||||||
return result
|
return result
|
||||||
try:
|
try:
|
||||||
turn, turn_events = self._query_model(session, tool_specs)
|
turn, turn_events = self._query_model(
|
||||||
|
session,
|
||||||
|
tool_specs,
|
||||||
|
stream_events=stream_events,
|
||||||
|
)
|
||||||
except OpenAICompatError as exc:
|
except OpenAICompatError as exc:
|
||||||
if self._is_prompt_too_long_error(exc) and self._reactive_compact_session(
|
if self._is_prompt_too_long_error(exc) and self._reactive_compact_session(
|
||||||
session,
|
session,
|
||||||
@@ -700,7 +704,11 @@ class LocalCodingAgent:
|
|||||||
turn_index=turn_index,
|
turn_index=turn_index,
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
turn, turn_events = self._query_model(session, tool_specs)
|
turn, turn_events = self._query_model(
|
||||||
|
session,
|
||||||
|
tool_specs,
|
||||||
|
stream_events=stream_events,
|
||||||
|
)
|
||||||
except OpenAICompatError as retry_exc:
|
except OpenAICompatError as retry_exc:
|
||||||
exc = retry_exc
|
exc = retry_exc
|
||||||
else:
|
else:
|
||||||
@@ -1479,6 +1487,8 @@ class LocalCodingAgent:
|
|||||||
self,
|
self,
|
||||||
session: AgentSessionState,
|
session: AgentSessionState,
|
||||||
tool_specs: list[dict[str, object]],
|
tool_specs: list[dict[str, object]],
|
||||||
|
*,
|
||||||
|
stream_events: list[dict[str, object]] | None = None,
|
||||||
) -> tuple[AssistantTurn, tuple[StreamEvent, ...]]:
|
) -> tuple[AssistantTurn, tuple[StreamEvent, ...]]:
|
||||||
if not self.runtime_config.stream_model_responses:
|
if not self.runtime_config.stream_model_responses:
|
||||||
turn = self.client.complete(
|
turn = self.client.complete(
|
||||||
@@ -1520,7 +1530,10 @@ class LocalCodingAgent:
|
|||||||
tool_specs,
|
tool_specs,
|
||||||
output_schema=self.runtime_config.output_schema,
|
output_schema=self.runtime_config.output_schema,
|
||||||
):
|
):
|
||||||
events.append(event)
|
if stream_events is None:
|
||||||
|
events.append(event)
|
||||||
|
else:
|
||||||
|
stream_events.append(event.to_dict())
|
||||||
if event.type == 'content_delta':
|
if event.type == 'content_delta':
|
||||||
session.append_assistant_delta(assistant_index, event.delta)
|
session.append_assistant_delta(assistant_index, event.delta)
|
||||||
elif event.type == 'tool_call_delta':
|
elif event.type == 'tool_call_delta':
|
||||||
|
|||||||
Reference in New Issue
Block a user