Bläddra i källkod

fix: surface agent upstream errors

fanghanjun 6 månader sedan
förälder
incheckning
7e78ec0e37
2 ändrade filer med 66 tillägg och 0 borttagningar
  1. 56 0
      packages/studio/src/api/server.test.ts
  2. 10 0
      packages/studio/src/api/server.ts

+ 56 - 0
packages/studio/src/api/server.test.ts

@@ -1953,6 +1953,62 @@ describe("createStudioServer daemon lifecycle", () => {
     });
     });
   });
   });
 
 
+  it("returns the agent final assistant error without replacing it with an empty-response probe", async () => {
+    const upstreamError = "400 The `reasoning_content` in the thinking mode must be passed back to the API.";
+    runAgentSessionMock.mockResolvedValueOnce({
+      responseText: "",
+      errorMessage: upstreamError,
+      messages: [{ role: "assistant", content: [], stopReason: "error", errorMessage: upstreamError }],
+    });
+
+    const { createStudioServer } = await import("./server.js");
+    const app = createStudioServer(cloneProjectConfig() as never, root);
+
+    const response = await app.request("http://localhost/api/v1/agent", {
+      method: "POST",
+      headers: { "Content-Type": "application/json" },
+      body: JSON.stringify({ instruction: "nihao", activeBookId: "demo-book", sessionId: "agent-session-1" }),
+    });
+
+    expect(response.status).toBe(502);
+    await expect(response.json()).resolves.toEqual({
+      error: {
+        code: "AGENT_LLM_ERROR",
+        message: upstreamError,
+      },
+      response: upstreamError,
+    });
+    expect(chatCompletionMock).not.toHaveBeenCalled();
+  });
+
+  it("returns malformed Gemini function-call errors without replacing them with an empty-response probe", async () => {
+    const upstreamError = "Provider finish_reason: function_call_filter: MALFORMED_FUNCTION_CALL";
+    runAgentSessionMock.mockResolvedValueOnce({
+      responseText: "",
+      errorMessage: upstreamError,
+      messages: [{ role: "assistant", content: [], stopReason: "error", errorMessage: upstreamError }],
+    });
+
+    const { createStudioServer } = await import("./server.js");
+    const app = createStudioServer(cloneProjectConfig() as never, root);
+
+    const response = await app.request("http://localhost/api/v1/agent", {
+      method: "POST",
+      headers: { "Content-Type": "application/json" },
+      body: JSON.stringify({ instruction: "nihao", activeBookId: "demo-book", sessionId: "agent-session-1" }),
+    });
+
+    expect(response.status).toBe(502);
+    await expect(response.json()).resolves.toEqual({
+      error: {
+        code: "AGENT_LLM_ERROR",
+        message: upstreamError,
+      },
+      response: upstreamError,
+    });
+    expect(chatCompletionMock).not.toHaveBeenCalled();
+  });
+
   it("falls back to plain chat when the tool-agent returns empty text", async () => {
   it("falls back to plain chat when the tool-agent returns empty text", async () => {
     runAgentSessionMock.mockResolvedValueOnce({
     runAgentSessionMock.mockResolvedValueOnce({
       responseText: "",
       responseText: "",

+ 10 - 0
packages/studio/src/api/server.ts

@@ -1928,6 +1928,16 @@ export function createStudioServer(initialConfig: ProjectConfig, root: string) {
       };
       };
 
 
       if (!result.responseText) {
       if (!result.responseText) {
+        if (result.errorMessage) {
+          if (resolveCreatedBookIdFromToolExecs(collectedToolExecs)) {
+            await finalizeCreatedBook();
+          }
+          return c.json({
+            error: { code: "AGENT_LLM_ERROR", message: result.errorMessage },
+            response: result.errorMessage,
+          }, 502);
+        }
+
         try {
         try {
           const fallbackClient = createLLMClient({
           const fallbackClient = createLLMClient({
             ...config.llm,
             ...config.llm,