mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-09-24 23:10:56 +08:00
feat: release v0.84.0 with full pending changes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+232
-269
@@ -3,11 +3,33 @@
|
||||
*
|
||||
* 基于 Aster 框架的 Agent 状态管理
|
||||
* 参考 Claude-Cowork 的设计模式
|
||||
*
|
||||
* @deprecated 当前仓库已迁移到 `useAgentChat` / `useAsterAgentChat` 主链路;
|
||||
* 请不要在新代码中继续依赖这个遗留 store。
|
||||
*/
|
||||
|
||||
import { create } from "zustand";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
|
||||
import type { UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { safeListen } from "@/lib/dev-bridge";
|
||||
import {
|
||||
confirmAsterAction,
|
||||
createAsterSession,
|
||||
deleteAsterSession,
|
||||
getAsterSession,
|
||||
initAsterAgent,
|
||||
listAsterSessions,
|
||||
sendAsterMessageStream,
|
||||
stopAsterSession,
|
||||
submitAsterElicitationResponse,
|
||||
type AsterSessionDetail,
|
||||
} from "@/lib/api/agentRuntime";
|
||||
import {
|
||||
parseStreamEvent,
|
||||
type StreamEvent,
|
||||
type ToolExecutionResult,
|
||||
type TokenUsage,
|
||||
} from "@/lib/api/agentStream";
|
||||
import { requireDefaultProjectId } from "@/lib/api/project";
|
||||
import {
|
||||
isAsterSessionNotFoundError,
|
||||
resolveRestorableSessionId,
|
||||
@@ -22,11 +44,8 @@ export interface MessageImage {
|
||||
}
|
||||
|
||||
/** 工具调用结果 */
|
||||
export interface ToolResult {
|
||||
success: boolean;
|
||||
output?: string;
|
||||
error?: string;
|
||||
}
|
||||
export type ToolResult = ToolExecutionResult;
|
||||
export type { TokenUsage } from "@/lib/api/agentStream";
|
||||
|
||||
/** 工具调用状态 */
|
||||
export interface ToolCallState {
|
||||
@@ -39,13 +58,6 @@ export interface ToolCallState {
|
||||
endTime?: Date;
|
||||
}
|
||||
|
||||
/** Token 使用量 */
|
||||
export interface TokenUsage {
|
||||
input_tokens: number;
|
||||
output_tokens: number;
|
||||
total_tokens: number;
|
||||
}
|
||||
|
||||
/** 内容片段类型 */
|
||||
export type ContentPart =
|
||||
| { type: "text"; text: string }
|
||||
@@ -106,48 +118,18 @@ export interface ConfirmResponse {
|
||||
// ============ Tauri 事件类型 ============
|
||||
|
||||
/** Tauri Agent 事件 */
|
||||
export type TauriAgentEvent =
|
||||
| { type: "text_delta"; text: string }
|
||||
| { type: "thinking_delta"; text: string }
|
||||
| {
|
||||
type: "tool_start";
|
||||
tool_name: string;
|
||||
tool_id: string;
|
||||
arguments?: string;
|
||||
}
|
||||
| { type: "tool_end"; tool_id: string; result: ToolResult }
|
||||
| {
|
||||
type: "action_required";
|
||||
request_id: string;
|
||||
action_type: string;
|
||||
data: Record<string, unknown>;
|
||||
}
|
||||
| { type: "model_change"; model: string; mode: string }
|
||||
| { type: "done"; usage?: TokenUsage }
|
||||
| { type: "final_done"; usage?: TokenUsage }
|
||||
| { type: "warning"; code?: string; message: string }
|
||||
| { type: "error"; message: string }
|
||||
| { type: "message"; message: unknown };
|
||||
export type TauriAgentEvent = StreamEvent;
|
||||
|
||||
// ============ Store 状态类型 ============
|
||||
|
||||
interface AgentState {
|
||||
// 会话状态
|
||||
currentSessionId: string | null;
|
||||
sessions: SessionInfo[];
|
||||
messages: Message[];
|
||||
|
||||
// 流式状态
|
||||
isStreaming: boolean;
|
||||
currentAssistantMsgId: string | null;
|
||||
|
||||
// 权限确认
|
||||
pendingActions: ActionRequired[];
|
||||
|
||||
// 配置
|
||||
isInitialized: boolean;
|
||||
|
||||
// Actions
|
||||
initialize: () => Promise<void>;
|
||||
sendMessage: (content: string, images?: MessageImage[]) => Promise<void>;
|
||||
stopStreaming: () => Promise<void>;
|
||||
@@ -157,19 +139,46 @@ interface AgentState {
|
||||
deleteSession: (sessionId: string) => Promise<void>;
|
||||
clearMessages: () => void;
|
||||
loadSessions: () => Promise<void>;
|
||||
|
||||
// 内部方法
|
||||
_handleEvent: (event: TauriAgentEvent) => void;
|
||||
_handleEvent: (event: StreamEvent) => void;
|
||||
_cleanup: () => void;
|
||||
}
|
||||
|
||||
// ============ Store 实现 ============
|
||||
|
||||
// 事件监听器引用
|
||||
let eventUnlisten: UnlistenFn | null = null;
|
||||
|
||||
const resolveDefaultWorkspaceId = async (): Promise<string> => {
|
||||
return requireDefaultProjectId("未找到默认工作区,请先创建并设为默认工作区");
|
||||
};
|
||||
|
||||
const toRuntimeImages = (images?: MessageImage[]) =>
|
||||
images?.map((image) => ({
|
||||
data: image.data,
|
||||
media_type: image.mediaType,
|
||||
}));
|
||||
|
||||
const extractSessionMessageText = (
|
||||
content: AsterSessionDetail["messages"][number]["content"],
|
||||
): string => {
|
||||
const segments = content
|
||||
.map((block) => {
|
||||
if (typeof block.text === "string" && block.text.trim()) {
|
||||
return block.text.trim();
|
||||
}
|
||||
if (typeof block.output === "string" && block.output.trim()) {
|
||||
return block.output.trim();
|
||||
}
|
||||
if (typeof block.error === "string" && block.error.trim()) {
|
||||
return `错误: ${block.error.trim()}`;
|
||||
}
|
||||
return "";
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
return segments.join("\n\n");
|
||||
};
|
||||
|
||||
export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
// 初始状态
|
||||
currentSessionId: null,
|
||||
sessions: [],
|
||||
messages: [],
|
||||
@@ -178,14 +187,11 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
pendingActions: [],
|
||||
isInitialized: false,
|
||||
|
||||
// 初始化 Agent
|
||||
initialize: async () => {
|
||||
try {
|
||||
await invoke("aster_agent_init");
|
||||
await initAsterAgent();
|
||||
set({ isInitialized: true });
|
||||
console.log("[AgentStore] Agent 初始化成功");
|
||||
|
||||
// 加载会话列表
|
||||
await get().loadSessions();
|
||||
} catch (error) {
|
||||
console.error("[AgentStore] Agent 初始化失败:", error);
|
||||
@@ -193,22 +199,18 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
}
|
||||
},
|
||||
|
||||
// 发送消息
|
||||
sendMessage: async (content: string, images?: MessageImage[]) => {
|
||||
const state = get();
|
||||
|
||||
// 确保已初始化
|
||||
if (!state.isInitialized) {
|
||||
await state.initialize();
|
||||
}
|
||||
|
||||
// 确保有会话
|
||||
let sessionId = state.currentSessionId;
|
||||
if (!sessionId) {
|
||||
sessionId = await state.createSession();
|
||||
}
|
||||
|
||||
// 创建用户消息
|
||||
const userMsg: Message = {
|
||||
id: crypto.randomUUID(),
|
||||
role: "user",
|
||||
@@ -217,7 +219,6 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
timestamp: new Date(),
|
||||
};
|
||||
|
||||
// 创建助手消息占位符
|
||||
const assistantMsgId = crypto.randomUUID();
|
||||
const assistantMsg: Message = {
|
||||
id: assistantMsgId,
|
||||
@@ -229,47 +230,45 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
contentParts: [],
|
||||
};
|
||||
|
||||
// 更新状态
|
||||
set((s) => ({
|
||||
messages: [...s.messages, userMsg, assistantMsg],
|
||||
set((currentState) => ({
|
||||
messages: [...currentState.messages, userMsg, assistantMsg],
|
||||
isStreaming: true,
|
||||
currentAssistantMsgId: assistantMsgId,
|
||||
}));
|
||||
|
||||
// 创建唯一事件名称
|
||||
const eventName = `aster_stream_${assistantMsgId}`;
|
||||
|
||||
try {
|
||||
// 设置事件监听器
|
||||
eventUnlisten = await listen<TauriAgentEvent>(eventName, (event) => {
|
||||
get()._handleEvent(event.payload);
|
||||
const workspaceId = await resolveDefaultWorkspaceId();
|
||||
|
||||
get()._cleanup();
|
||||
eventUnlisten = await safeListen<unknown>(eventName, (event) => {
|
||||
const parsedEvent = parseStreamEvent(event.payload);
|
||||
if (!parsedEvent) {
|
||||
return;
|
||||
}
|
||||
get()._handleEvent(parsedEvent);
|
||||
});
|
||||
|
||||
// 发送请求
|
||||
await invoke("aster_agent_chat_stream", {
|
||||
request: {
|
||||
message: content,
|
||||
session_id: sessionId,
|
||||
event_name: eventName,
|
||||
images: images?.map((img) => ({
|
||||
data: img.data,
|
||||
media_type: img.mediaType,
|
||||
})),
|
||||
},
|
||||
});
|
||||
await sendAsterMessageStream(
|
||||
content,
|
||||
sessionId,
|
||||
eventName,
|
||||
workspaceId,
|
||||
toRuntimeImages(images),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("[AgentStore] 发送消息失败:", error);
|
||||
|
||||
// 更新消息状态为错误
|
||||
set((s) => ({
|
||||
messages: s.messages.map((msg) =>
|
||||
msg.id === assistantMsgId
|
||||
set((currentState) => ({
|
||||
messages: currentState.messages.map((message) =>
|
||||
message.id === assistantMsgId
|
||||
? {
|
||||
...msg,
|
||||
...message,
|
||||
isThinking: false,
|
||||
content: `错误: ${error}`,
|
||||
}
|
||||
: msg,
|
||||
: message,
|
||||
),
|
||||
isStreaming: false,
|
||||
currentAssistantMsgId: null,
|
||||
@@ -280,29 +279,25 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
}
|
||||
},
|
||||
|
||||
// 停止流式响应
|
||||
stopStreaming: async () => {
|
||||
const state = get();
|
||||
if (!state.currentSessionId) return;
|
||||
|
||||
try {
|
||||
await invoke("aster_agent_stop", {
|
||||
sessionId: state.currentSessionId,
|
||||
});
|
||||
await stopAsterSession(state.currentSessionId);
|
||||
} catch (error) {
|
||||
console.error("[AgentStore] 停止失败:", error);
|
||||
}
|
||||
|
||||
// 更新消息状态
|
||||
set((s) => ({
|
||||
messages: s.messages.map((msg) =>
|
||||
msg.id === s.currentAssistantMsgId
|
||||
set((currentState) => ({
|
||||
messages: currentState.messages.map((message) =>
|
||||
message.id === currentState.currentAssistantMsgId
|
||||
? {
|
||||
...msg,
|
||||
...message,
|
||||
isThinking: false,
|
||||
content: msg.content || "(已停止生成)",
|
||||
content: message.content || "(已停止生成)",
|
||||
}
|
||||
: msg,
|
||||
: message,
|
||||
),
|
||||
isStreaming: false,
|
||||
currentAssistantMsgId: null,
|
||||
@@ -311,14 +306,14 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
get()._cleanup();
|
||||
},
|
||||
|
||||
// 确认权限请求
|
||||
confirmAction: async (response: ConfirmResponse) => {
|
||||
try {
|
||||
const state = get();
|
||||
const actionType =
|
||||
response.actionType ||
|
||||
state.pendingActions.find((a) => a.requestId === response.requestId)
|
||||
?.actionType;
|
||||
state.pendingActions.find(
|
||||
(item) => item.requestId === response.requestId,
|
||||
)?.actionType;
|
||||
|
||||
if (actionType === "elicitation" || actionType === "ask_user") {
|
||||
if (!state.currentSessionId) {
|
||||
@@ -345,27 +340,22 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
userData = "";
|
||||
}
|
||||
|
||||
await invoke("aster_agent_submit_elicitation_response", {
|
||||
sessionId: state.currentSessionId,
|
||||
request: {
|
||||
request_id: response.requestId,
|
||||
user_data: userData,
|
||||
},
|
||||
});
|
||||
await submitAsterElicitationResponse(
|
||||
state.currentSessionId,
|
||||
response.requestId,
|
||||
userData,
|
||||
);
|
||||
} else {
|
||||
await invoke("aster_agent_confirm", {
|
||||
request: {
|
||||
request_id: response.requestId,
|
||||
confirmed: response.confirmed,
|
||||
response: response.response,
|
||||
},
|
||||
});
|
||||
await confirmAsterAction(
|
||||
response.requestId,
|
||||
response.confirmed,
|
||||
response.response,
|
||||
);
|
||||
}
|
||||
|
||||
// 移除已处理的请求
|
||||
set((s) => ({
|
||||
pendingActions: s.pendingActions.filter(
|
||||
(a) => a.requestId !== response.requestId,
|
||||
set((currentState) => ({
|
||||
pendingActions: currentState.pendingActions.filter(
|
||||
(item) => item.requestId !== response.requestId,
|
||||
),
|
||||
}));
|
||||
} catch (error) {
|
||||
@@ -374,43 +364,28 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
}
|
||||
},
|
||||
|
||||
// 切换会话
|
||||
switchSession: async (sessionId: string) => {
|
||||
try {
|
||||
const detail = await invoke<{
|
||||
id: string;
|
||||
name?: string;
|
||||
messages: Array<{
|
||||
role: string;
|
||||
content: string;
|
||||
timestamp: string;
|
||||
}>;
|
||||
}>("aster_session_get", { sessionId });
|
||||
|
||||
// 转换消息格式
|
||||
const messages: Message[] = detail.messages.map((msg, index) => ({
|
||||
const detail = await getAsterSession(sessionId);
|
||||
const messages: Message[] = detail.messages.map((message, index) => ({
|
||||
id: `${sessionId}-${index}`,
|
||||
role: msg.role as "user" | "assistant",
|
||||
content: msg.content,
|
||||
timestamp: new Date(msg.timestamp),
|
||||
role: message.role === "user" ? "user" : "assistant",
|
||||
content: extractSessionMessageText(message.content),
|
||||
timestamp: new Date(message.timestamp),
|
||||
}));
|
||||
|
||||
set({
|
||||
currentSessionId: sessionId,
|
||||
messages,
|
||||
pendingActions: [],
|
||||
isStreaming: false,
|
||||
currentAssistantMsgId: null,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("[AgentStore] 切换会话失败:", error);
|
||||
if (isAsterSessionNotFoundError(error)) {
|
||||
try {
|
||||
const sessions = await invoke<
|
||||
Array<{
|
||||
id: string;
|
||||
created_at: number;
|
||||
updated_at: number;
|
||||
}>
|
||||
>("aster_session_list");
|
||||
const sessions = await listAsterSessions();
|
||||
const recoveredSessionId = resolveRestorableSessionId({
|
||||
candidateSessionId: null,
|
||||
sessions: sessions.map((item) => ({
|
||||
@@ -429,6 +404,8 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
set({
|
||||
messages: [],
|
||||
pendingActions: [],
|
||||
isStreaming: false,
|
||||
currentAssistantMsgId: null,
|
||||
});
|
||||
return;
|
||||
} catch (recoveryError) {
|
||||
@@ -439,17 +416,17 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
}
|
||||
},
|
||||
|
||||
// 创建会话
|
||||
createSession: async (name?: string) => {
|
||||
try {
|
||||
const sessionId = await invoke<string>("aster_session_create", {
|
||||
workingDir: null,
|
||||
name,
|
||||
});
|
||||
const workspaceId = await resolveDefaultWorkspaceId();
|
||||
const sessionId = await createAsterSession(workspaceId, undefined, name);
|
||||
|
||||
set((s) => ({
|
||||
set((currentState) => ({
|
||||
currentSessionId: sessionId,
|
||||
messages: [],
|
||||
pendingActions: [],
|
||||
isStreaming: false,
|
||||
currentAssistantMsgId: null,
|
||||
sessions: [
|
||||
{
|
||||
id: sessionId,
|
||||
@@ -458,7 +435,7 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
updatedAt: new Date(),
|
||||
messagesCount: 0,
|
||||
},
|
||||
...s.sessions,
|
||||
...currentState.sessions,
|
||||
],
|
||||
}));
|
||||
|
||||
@@ -469,46 +446,45 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
}
|
||||
},
|
||||
|
||||
// 删除会话
|
||||
deleteSession: async (sessionId: string) => {
|
||||
// TODO: 实现后端删除接口
|
||||
set((s) => ({
|
||||
sessions: s.sessions.filter((sess) => sess.id !== sessionId),
|
||||
...(s.currentSessionId === sessionId
|
||||
? { currentSessionId: null, messages: [] }
|
||||
await deleteAsterSession(sessionId);
|
||||
|
||||
set((currentState) => ({
|
||||
sessions: currentState.sessions.filter(
|
||||
(session) => session.id !== sessionId,
|
||||
),
|
||||
...(currentState.currentSessionId === sessionId
|
||||
? {
|
||||
currentSessionId: null,
|
||||
messages: [],
|
||||
pendingActions: [],
|
||||
isStreaming: false,
|
||||
currentAssistantMsgId: null,
|
||||
}
|
||||
: {}),
|
||||
}));
|
||||
},
|
||||
|
||||
// 清空消息
|
||||
clearMessages: () => {
|
||||
set({
|
||||
messages: [],
|
||||
currentSessionId: null,
|
||||
pendingActions: [],
|
||||
isStreaming: false,
|
||||
currentAssistantMsgId: null,
|
||||
});
|
||||
},
|
||||
|
||||
// 加载会话列表
|
||||
loadSessions: async () => {
|
||||
try {
|
||||
const sessions = await invoke<
|
||||
Array<{
|
||||
id: string;
|
||||
name?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
messages_count: number;
|
||||
}>
|
||||
>("aster_session_list");
|
||||
|
||||
const sessions = await listAsterSessions();
|
||||
set({
|
||||
sessions: sessions.map((s) => ({
|
||||
id: s.id,
|
||||
name: s.name,
|
||||
createdAt: new Date(s.created_at),
|
||||
updatedAt: new Date(s.updated_at),
|
||||
messagesCount: s.messages_count,
|
||||
sessions: sessions.map((session) => ({
|
||||
id: session.id,
|
||||
name: session.name,
|
||||
createdAt: new Date(session.created_at),
|
||||
updatedAt: new Date(session.updated_at),
|
||||
messagesCount: session.messages_count || 0,
|
||||
})),
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -516,8 +492,7 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
}
|
||||
},
|
||||
|
||||
// 处理事件
|
||||
_handleEvent: (event: TauriAgentEvent) => {
|
||||
_handleEvent: (event: StreamEvent) => {
|
||||
const state = get();
|
||||
const msgId = state.currentAssistantMsgId;
|
||||
if (!msgId) return;
|
||||
@@ -526,55 +501,55 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
|
||||
switch (event.type) {
|
||||
case "text_delta":
|
||||
set((s) => ({
|
||||
messages: s.messages.map((msg) => {
|
||||
if (msg.id !== msgId) return msg;
|
||||
set((currentState) => ({
|
||||
messages: currentState.messages.map((message) => {
|
||||
if (message.id !== msgId) return message;
|
||||
|
||||
const newContent = msg.content + event.text;
|
||||
const newParts = [...(msg.contentParts || [])];
|
||||
const nextContent = message.content + event.text;
|
||||
const nextParts = [...(message.contentParts || [])];
|
||||
const lastPart = nextParts[nextParts.length - 1];
|
||||
|
||||
// 追加到最后一个 text 类型,或创建新的
|
||||
const lastPart = newParts[newParts.length - 1];
|
||||
if (lastPart && lastPart.type === "text") {
|
||||
newParts[newParts.length - 1] = {
|
||||
nextParts[nextParts.length - 1] = {
|
||||
type: "text",
|
||||
text: lastPart.text + event.text,
|
||||
};
|
||||
} else {
|
||||
newParts.push({ type: "text", text: event.text });
|
||||
nextParts.push({ type: "text", text: event.text });
|
||||
}
|
||||
|
||||
return {
|
||||
...msg,
|
||||
content: newContent,
|
||||
...message,
|
||||
content: nextContent,
|
||||
isThinking: false,
|
||||
thinkingContent: undefined,
|
||||
contentParts: newParts,
|
||||
contentParts: nextParts,
|
||||
};
|
||||
}),
|
||||
}));
|
||||
break;
|
||||
|
||||
case "thinking_delta":
|
||||
set((s) => ({
|
||||
messages: s.messages.map((msg) => {
|
||||
if (msg.id !== msgId) return msg;
|
||||
set((currentState) => ({
|
||||
messages: currentState.messages.map((message) => {
|
||||
if (message.id !== msgId) return message;
|
||||
|
||||
const nextParts = [...(message.contentParts || [])];
|
||||
const lastPart = nextParts[nextParts.length - 1];
|
||||
|
||||
const newParts = [...(msg.contentParts || [])];
|
||||
const lastPart = newParts[newParts.length - 1];
|
||||
if (lastPart && lastPart.type === "thinking") {
|
||||
newParts[newParts.length - 1] = {
|
||||
nextParts[nextParts.length - 1] = {
|
||||
type: "thinking",
|
||||
text: lastPart.text + event.text,
|
||||
};
|
||||
} else {
|
||||
newParts.push({ type: "thinking", text: event.text });
|
||||
nextParts.push({ type: "thinking", text: event.text });
|
||||
}
|
||||
|
||||
return {
|
||||
...msg,
|
||||
thinkingContent: (msg.thinkingContent || "") + event.text,
|
||||
contentParts: newParts,
|
||||
...message,
|
||||
thinkingContent: (message.thinkingContent || "") + event.text,
|
||||
contentParts: nextParts,
|
||||
};
|
||||
}),
|
||||
}));
|
||||
@@ -589,21 +564,23 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
startTime: new Date(),
|
||||
};
|
||||
|
||||
set((s) => ({
|
||||
messages: s.messages.map((msg) => {
|
||||
if (msg.id !== msgId) return msg;
|
||||
|
||||
// 检查是否已存在
|
||||
if (msg.toolCalls?.find((tc) => tc.id === event.tool_id)) {
|
||||
return msg;
|
||||
set((currentState) => ({
|
||||
messages: currentState.messages.map((message) => {
|
||||
if (message.id !== msgId) return message;
|
||||
if (
|
||||
message.toolCalls?.find(
|
||||
(toolCall) => toolCall.id === event.tool_id,
|
||||
)
|
||||
) {
|
||||
return message;
|
||||
}
|
||||
|
||||
return {
|
||||
...msg,
|
||||
toolCalls: [...(msg.toolCalls || []), newToolCall],
|
||||
...message,
|
||||
toolCalls: [...(message.toolCalls || []), newToolCall],
|
||||
contentParts: [
|
||||
...(msg.contentParts || []),
|
||||
{ type: "tool_use" as const, toolCall: newToolCall },
|
||||
...(message.contentParts || []),
|
||||
{ type: "tool_use", toolCall: newToolCall },
|
||||
],
|
||||
};
|
||||
}),
|
||||
@@ -612,75 +589,63 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
}
|
||||
|
||||
case "tool_end":
|
||||
set((s) => ({
|
||||
messages: s.messages.map((msg) => {
|
||||
if (msg.id !== msgId) return msg;
|
||||
set((currentState) => ({
|
||||
messages: currentState.messages.map((message) => {
|
||||
if (message.id !== msgId) return message;
|
||||
|
||||
const updatedToolCalls = (msg.toolCalls || []).map((tc) =>
|
||||
tc.id === event.tool_id
|
||||
const nextToolCalls = (message.toolCalls || []).map((toolCall) =>
|
||||
toolCall.id === event.tool_id
|
||||
? {
|
||||
...tc,
|
||||
status: event.result.success
|
||||
? ("completed" as const)
|
||||
: ("failed" as const),
|
||||
...toolCall,
|
||||
status: event.result.success ? "completed" : "failed",
|
||||
result: event.result,
|
||||
endTime: new Date(),
|
||||
}
|
||||
: tc,
|
||||
: toolCall,
|
||||
);
|
||||
|
||||
const updatedContentParts = (msg.contentParts || []).map((part) => {
|
||||
if (
|
||||
part.type === "tool_use" &&
|
||||
part.toolCall.id === event.tool_id
|
||||
) {
|
||||
const nextContentParts = (message.contentParts || []).map(
|
||||
(part) => {
|
||||
if (
|
||||
part.type !== "tool_use" ||
|
||||
part.toolCall.id !== event.tool_id
|
||||
) {
|
||||
return part;
|
||||
}
|
||||
|
||||
return {
|
||||
...part,
|
||||
toolCall: {
|
||||
...part.toolCall,
|
||||
status: event.result.success
|
||||
? ("completed" as const)
|
||||
: ("failed" as const),
|
||||
status: event.result.success ? "completed" : "failed",
|
||||
result: event.result,
|
||||
endTime: new Date(),
|
||||
},
|
||||
};
|
||||
}
|
||||
return part;
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
...msg,
|
||||
toolCalls: updatedToolCalls,
|
||||
contentParts: updatedContentParts,
|
||||
...message,
|
||||
toolCalls: nextToolCalls,
|
||||
contentParts: nextContentParts,
|
||||
};
|
||||
}),
|
||||
}));
|
||||
break;
|
||||
|
||||
case "action_required":
|
||||
set((s) => ({
|
||||
set((currentState) => ({
|
||||
pendingActions: [
|
||||
...s.pendingActions,
|
||||
...currentState.pendingActions,
|
||||
{
|
||||
requestId: event.request_id,
|
||||
actionType: event.action_type as ActionRequired["actionType"],
|
||||
toolName: event.data.tool_name as string | undefined,
|
||||
arguments: event.data.arguments as
|
||||
| Record<string, unknown>
|
||||
| undefined,
|
||||
prompt:
|
||||
(event.data.prompt as string | undefined) ||
|
||||
(event.data.message as string | undefined),
|
||||
requestedSchema: event.data.requested_schema as
|
||||
| Record<string, unknown>
|
||||
| undefined,
|
||||
options: event.data.options as
|
||||
| Array<{
|
||||
label: string;
|
||||
description?: string;
|
||||
}>
|
||||
| undefined,
|
||||
toolName: event.tool_name,
|
||||
arguments: event.arguments,
|
||||
prompt: event.prompt || event.questions?.[0]?.question,
|
||||
requestedSchema: event.requested_schema,
|
||||
options: event.questions?.[0]?.options,
|
||||
timestamp: new Date(),
|
||||
},
|
||||
],
|
||||
@@ -688,20 +653,19 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
break;
|
||||
|
||||
case "done":
|
||||
// 单次响应完成,但工具循环可能继续
|
||||
console.log("[AgentStore] done 事件,等待 final_done...");
|
||||
break;
|
||||
|
||||
case "final_done":
|
||||
set((s) => ({
|
||||
messages: s.messages.map((msg) =>
|
||||
msg.id === msgId
|
||||
set((currentState) => ({
|
||||
messages: currentState.messages.map((message) =>
|
||||
message.id === msgId
|
||||
? {
|
||||
...msg,
|
||||
...message,
|
||||
isThinking: false,
|
||||
usage: event.usage,
|
||||
}
|
||||
: msg,
|
||||
: message,
|
||||
),
|
||||
isStreaming: false,
|
||||
currentAssistantMsgId: null,
|
||||
@@ -710,15 +674,15 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
break;
|
||||
|
||||
case "error":
|
||||
set((s) => ({
|
||||
messages: s.messages.map((msg) =>
|
||||
msg.id === msgId
|
||||
set((currentState) => ({
|
||||
messages: currentState.messages.map((message) =>
|
||||
message.id === msgId
|
||||
? {
|
||||
...msg,
|
||||
...message,
|
||||
isThinking: false,
|
||||
content: msg.content || `错误: ${event.message}`,
|
||||
content: message.content || `错误: ${event.message}`,
|
||||
}
|
||||
: msg,
|
||||
: message,
|
||||
),
|
||||
isStreaming: false,
|
||||
currentAssistantMsgId: null,
|
||||
@@ -732,17 +696,16 @@ export const useAgentStore = create<AgentState>((set, get) => ({
|
||||
}
|
||||
},
|
||||
|
||||
// 清理资源
|
||||
_cleanup: () => {
|
||||
if (eventUnlisten) {
|
||||
eventUnlisten();
|
||||
eventUnlisten = null;
|
||||
}
|
||||
const cleanup = eventUnlisten;
|
||||
eventUnlisten = null;
|
||||
cleanup?.();
|
||||
},
|
||||
}));
|
||||
|
||||
// 导出便捷 hooks
|
||||
export const useAgentMessages = () => useAgentStore((s) => s.messages);
|
||||
export const useAgentStreaming = () => useAgentStore((s) => s.isStreaming);
|
||||
export const useAgentSessions = () => useAgentStore((s) => s.sessions);
|
||||
export const usePendingActions = () => useAgentStore((s) => s.pendingActions);
|
||||
export const useAgentMessages = () => useAgentStore((state) => state.messages);
|
||||
export const useAgentStreaming = () =>
|
||||
useAgentStore((state) => state.isStreaming);
|
||||
export const useAgentSessions = () => useAgentStore((state) => state.sessions);
|
||||
export const usePendingActions = () =>
|
||||
useAgentStore((state) => state.pendingActions);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/**
|
||||
* Stores 导出
|
||||
*
|
||||
* @deprecated `agentStore` 属于遗留导出,请不要在新代码中继续依赖。
|
||||
*/
|
||||
|
||||
// Aster Agent Store
|
||||
|
||||
Reference in New Issue
Block a user