参数
{{ JSON.stringify(part.args, null, 2) }}
@@ -119,7 +126,9 @@ const collapsed = ref(false);
color: var(--color-muted);
}
+.tool-state.preparing { color: var(--color-accent-amber); }
.tool-state.call { color: var(--color-accent-amber); }
+.tool-state.interrupted { color: var(--color-accent-red, #e74c3c); }
.tool-state.result { color: var(--color-accent-teal); }
.tool-state.approval-requested { color: var(--color-accent-amber); }
.tool-state.approval-responded { color: var(--color-body); }
@@ -224,4 +233,22 @@ const collapsed = ref(false);
font-size: 12px;
color: var(--color-body);
}
+
+.tool-preparing {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ padding: 6px 0;
+ font-size: 12px;
+ color: var(--color-muted);
+}
+
+.tool-preparing .spin {
+ font-size: 13px;
+ animation: spin 0.8s linear infinite;
+}
+
+@keyframes spin {
+ to { transform: rotate(360deg); }
+}
diff --git a/app/composables/useAgentChat.ts b/app/composables/useAgentChat.ts
index 165d524..4abd59d 100644
--- a/app/composables/useAgentChat.ts
+++ b/app/composables/useAgentChat.ts
@@ -286,7 +286,7 @@ export function useAgentChat(options: UseAgentChatOptions) {
errorMessage.value = chunk.errorText || "流式响应出错";
break;
}
- case "tool-input-available": {
+ case "tool-input-start": {
const msg = messages.value[assistantIdx];
if (!msg) break;
updateLastReasoningDuration(msg);
@@ -295,11 +295,36 @@ export function useAgentChat(options: UseAgentChatOptions) {
type: "tool-call",
toolName: chunk.toolName,
toolCallId: chunk.toolCallId,
- args: chunk.input,
- state: "call",
+ args: undefined,
+ state: "preparing",
});
break;
}
+ case "tool-input-delta": {
+ break;
+ }
+ case "tool-input-available": {
+ const msg = messages.value[assistantIdx];
+ if (!msg) break;
+ updateLastReasoningDuration(msg);
+ const existing = msg.parts?.find(
+ (p) => p.type === "tool-call" && p.toolCallId === chunk.toolCallId && p.state === "preparing"
+ );
+ if (existing) {
+ existing.args = chunk.input;
+ existing.state = "call";
+ } else {
+ appendPart(msg, {
+ id: generateId(),
+ type: "tool-call",
+ toolName: chunk.toolName,
+ toolCallId: chunk.toolCallId,
+ args: chunk.input,
+ state: "call",
+ });
+ }
+ break;
+ }
case "tool-output-available": {
const msg = messages.value[assistantIdx];
if (!msg || !msg.parts) break;
@@ -361,7 +386,14 @@ export function useAgentChat(options: UseAgentChatOptions) {
}
const msg = messages.value[assistantIdx];
- if (msg) updateLastReasoningDuration(msg);
+ if (msg) {
+ updateLastReasoningDuration(msg);
+ for (const p of msg.parts ?? []) {
+ if (p.type === "tool-call" && p.state === "preparing") {
+ p.state = "interrupted";
+ }
+ }
+ }
}
function validateAssistantContent(assistantIdx: number, isApprovalContinue = false) {
diff --git a/app/types/chat.ts b/app/types/chat.ts
index 695c5c4..f0cb839 100644
--- a/app/types/chat.ts
+++ b/app/types/chat.ts
@@ -8,7 +8,7 @@ export interface MessagePart {
toolCallId?: string
args?: unknown
result?: unknown
- state?: 'call' | 'result' | 'approval-requested' | 'approval-responded'
+ state?: 'preparing' | 'call' | 'result' | 'approval-requested' | 'approval-responded' | 'interrupted'
approvalId?: string
approved?: boolean
approvalReason?: string
diff --git a/e2e/global-setup.ts b/e2e/global-setup.ts
index a248956..ed50d94 100644
--- a/e2e/global-setup.ts
+++ b/e2e/global-setup.ts
@@ -313,7 +313,7 @@ async function seedLlmData(dbPath: string) {
// 插入 default agent(seed 脚本只做 bootstrap admin,不含 agent seed)
await client.execute({
- sql: `INSERT INTO agents (slug, name, description, system_prompt, default_model_id, title_strategy, enable_thinking, enable_tools, max_step_count, is_default, is_callable, sort_order, enabled)
+ sql: `INSERT OR IGNORE INTO agents (slug, name, description, system_prompt, default_model_id, title_strategy, enable_thinking, enable_tools, max_step_count, is_default, is_callable, sort_order, enabled)
VALUES ('default', '默认助手', '通用 AI 助手', 'You are a helpful AI assistant.', ?, 'llm', 0, 1, 6, 1, 0, 0, 1)`,
args: [Number(modelId)],
});
diff --git a/e2e/specs/agent-documents.spec.ts b/e2e/specs/agent-documents.spec.ts
index 78639fe..f1ce6b0 100644
--- a/e2e/specs/agent-documents.spec.ts
+++ b/e2e/specs/agent-documents.spec.ts
@@ -189,8 +189,9 @@ test.describe("Agent 文档", () => {
await expect(docLink).toBeVisible({ timeout: 10000 });
});
- test("带 agentSlug 创建的文档绑定到该 agent", async ({ page, request }) => {
+ test("带 agentSlug 创建的文档绑定到该 agent", async ({ page }) => {
await loginViaUI(page);
+ const request = page.request;
const title = `E2E绑定Agent_${Date.now()}`;
const res = await request.post("/api/agent-documents", {
@@ -210,8 +211,9 @@ test.describe("Agent 文档", () => {
await request.delete(`/api/agent-documents/${docId}`);
});
- test("不带 agentSlug 创建的文档为全局共享", async ({ page, request }) => {
+ test("不带 agentSlug 创建的文档为全局共享", async ({ page }) => {
await loginViaUI(page);
+ const request = page.request;
const title = `E2E全局文档_${Date.now()}`;
const res = await request.post("/api/agent-documents", {
@@ -235,8 +237,9 @@ test.describe("Agent 文档", () => {
await request.delete(`/api/agent-documents/${docId}`);
});
- test("PATCH 切换文档归属(全局 ↔ agent 专属)", async ({ page, request }) => {
+ test("PATCH 切换文档归属(全局 ↔ agent 专属)", async ({ page }) => {
await loginViaUI(page);
+ const request = page.request;
// 先创建全局文档
const createRes = await request.post("/api/agent-documents", {
diff --git a/packages/drizzle-pkg/db.sqlite b/packages/drizzle-pkg/db.sqlite
index 6c5fa39..fa66cd8 100644
Binary files a/packages/drizzle-pkg/db.sqlite and b/packages/drizzle-pkg/db.sqlite differ
diff --git a/server/service/agent-tool/index.ts b/server/service/agent-tool/index.ts
index e5904d3..32f071e 100644
--- a/server/service/agent-tool/index.ts
+++ b/server/service/agent-tool/index.ts
@@ -509,15 +509,15 @@ export async function getAgentToolsForChat(params: {
: { success: false as const, error: r.error };
},
}),
- execute: async (input: unknown) => {
- const execResult = await executeAgentTool(agentTool.id, input, userId);
- if (!execResult.success) {
- return `工具执行失败: ${execResult.error ?? "未知错误"}。请停止调用此工具,基于已有信息回答用户或告知用户此工具不可用。`;
- }
- const meta = execResult.metadata;
- const sizeInfo = meta?.responseSize ? `${meta.responseSize} bytes` : `未知大小`;
- const statusInfo = meta?.statusCode ? `HTTP ${meta.statusCode}` : "";
- const dataStr = typeof execResult.data === "string" ? execResult.data : JSON.stringify(execResult.data);
+ execute: async (input: unknown) => {
+ const execResult = await executeAgentTool(agentTool.id, input, userId, undefined, agentId);
+ if (!execResult.success) {
+ return `工具执行失败: ${execResult.error ?? "未知错误"}。请停止调用此工具,基于已有信息回答用户或告知用户此工具不可用。`;
+ }
+ const meta = execResult.metadata;
+ const sizeInfo = meta?.responseSize ? `${meta.responseSize} bytes` : `未知大小`;
+ const statusInfo = meta?.statusCode ? `HTTP ${meta.statusCode}` : "";
+ const dataStr = typeof execResult.data === "string" ? execResult.data : JSON.stringify(execResult.data);
return `[${agentTool.type} 结果 ${statusInfo} ${sizeInfo}]\n${dataStr}`;
},
needsApproval: false,
@@ -602,7 +602,7 @@ export async function getAgentToolsForChatByAgentId(params: {
},
}),
execute: async (input: unknown) => {
- const execResult = await executeAgentTool(agentTool.id, input, userId);
+ const execResult = await executeAgentTool(agentTool.id, input, userId, undefined, agentId);
if (!execResult.success) {
return `工具执行失败: ${execResult.error ?? "未知错误"}。请停止调用此工具,基于已有信息回答用户或告知用户此工具不可用。`;
}