Skip to content

Commit

Permalink
feat(openai): Wrap some common OpenAI errors (#7003)
Browse files Browse the repository at this point in the history
  • Loading branch information
jacoblee93 authored Oct 17, 2024
1 parent 9a29419 commit 3ac6e4e
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 1 deletion.
12 changes: 12 additions & 0 deletions libs/langchain-openai/src/tests/chat_models-extended.int.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint-disable no-promise-executor-return */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { test, expect, jest } from "@jest/globals";
import { AIMessage, HumanMessage, ToolMessage } from "@langchain/core/messages";
import { concat } from "@langchain/core/utils/stream";
Expand Down Expand Up @@ -166,6 +167,17 @@ test("Test ChatOpenAI tool calling with ToolMessages", async () => {
),
})
);
let toolError;
try {
await chat.invoke([
["human", "What's the weather like in San Francisco, Tokyo, and Paris?"],
res,
]);
} catch (e) {
toolError = e;
}
expect(toolError).toBeDefined();
expect((toolError as any)?.lc_error_code).toEqual("INVALID_TOOL_RESULTS");
// @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
// @ts-expect-error unused var
const finalResponse = await chat.invoke([
Expand Down
37 changes: 36 additions & 1 deletion libs/langchain-openai/src/tests/chat_models.int.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable no-process-env */

/* eslint-disable @typescript-eslint/no-explicit-any */
import { test, jest, expect } from "@jest/globals";
import {
AIMessageChunk,
Expand Down Expand Up @@ -43,6 +43,41 @@ test("Test ChatOpenAI Generate", async () => {
// console.log({ res });
});

test("Test ChatOpenAI invoke fails with proper error", async () => {
const chat = new ChatOpenAI({
model: "gpt-4o-mini",
maxTokens: 10,
n: 2,
apiKey: "bad",
});
const message = new HumanMessage("Hello!");
let authError;
try {
await chat.invoke([message]);
} catch (e) {
authError = e;
}
expect(authError).toBeDefined();
expect((authError as any)?.lc_error_code).toEqual("MODEL_AUTHENTICATION");
});

test("Test ChatOpenAI invoke to unknown model fails with proper error", async () => {
const chat = new ChatOpenAI({
model: "badbadbad",
maxTokens: 10,
n: 2,
});
const message = new HumanMessage("Hello!");
let authError;
try {
await chat.invoke([message]);
} catch (e) {
authError = e;
}
expect(authError).toBeDefined();
expect((authError as any)?.lc_error_code).toEqual("MODEL_NOT_FOUND");
});

test("Test ChatOpenAI Generate throws when one of the calls fails", async () => {
const chat = new ChatOpenAI({
modelName: "gpt-3.5-turbo",
Expand Down
22 changes: 22 additions & 0 deletions libs/langchain-openai/src/utils/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable no-param-reassign */

// Duplicate of core
// TODO: Remove once we stop supporting 0.2.x core versions
export type LangChainErrorCodes =
| "INVALID_PROMPT_INPUT"
| "INVALID_TOOL_RESULTS"
| "MESSAGE_COERCION_FAILURE"
| "MODEL_AUTHENTICATION"
| "MODEL_NOT_FOUND"
| "MODEL_RATE_LIMIT"
| "OUTPUT_PARSING_FAILURE";

export function addLangChainErrorFields(
error: any,
lc_error_code: LangChainErrorCodes
) {
(error as any).lc_error_code = lc_error_code;
error.message = `${error.message}\n\nTroubleshooting URL: https://js.langchain.com/docs/troubleshooting/errors/${lc_error_code}/\n`;
return error;
}
9 changes: 9 additions & 0 deletions libs/langchain-openai/src/utils/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
convertToOpenAIFunction,
convertToOpenAITool,
} from "@langchain/core/utils/function_calling";
import { addLangChainErrorFields } from "./errors.js";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function wrapOpenAIClientError(e: any) {
Expand All @@ -19,6 +20,14 @@ export function wrapOpenAIClientError(e: any) {
} else if (e.constructor.name === APIUserAbortError.name) {
error = new Error(e.message);
error.name = "AbortError";
} else if (e.status === 400 && e.message.includes("tool_calls")) {
error = addLangChainErrorFields(e, "INVALID_TOOL_RESULTS");
} else if (e.status === 401) {
error = addLangChainErrorFields(e, "MODEL_AUTHENTICATION");
} else if (e.status === 429) {
error = addLangChainErrorFields(e, "MODEL_RATE_LIMIT");
} else if (e.status === 404) {
error = addLangChainErrorFields(e, "MODEL_NOT_FOUND");
} else {
error = e;
}
Expand Down

0 comments on commit 3ac6e4e

Please sign in to comment.