Merge pull request #38 from langchain-ai/brace/custom-parsing-anthropic
fix: Run formatting
This commit is contained in:
@@ -23,7 +23,13 @@ ${allToolDescriptions}
|
|||||||
`;
|
`;
|
||||||
const routerSchema = z.object({
|
const routerSchema = z.object({
|
||||||
route: z
|
route: z
|
||||||
.enum(["stockbroker", "tripPlanner", "openCode", "orderPizza", "generalInput"])
|
.enum([
|
||||||
|
"stockbroker",
|
||||||
|
"tripPlanner",
|
||||||
|
"openCode",
|
||||||
|
"orderPizza",
|
||||||
|
"generalInput",
|
||||||
|
])
|
||||||
.describe(routerDescription),
|
.describe(routerDescription),
|
||||||
});
|
});
|
||||||
const routerTool = {
|
const routerTool = {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { v4 as uuidv4 } from "uuid";
|
|||||||
|
|
||||||
const PizzaOrdererAnnotation = Annotation.Root({
|
const PizzaOrdererAnnotation = Annotation.Root({
|
||||||
messages: GenerativeUIAnnotation.spec.messages,
|
messages: GenerativeUIAnnotation.spec.messages,
|
||||||
})
|
});
|
||||||
|
|
||||||
async function sleep(ms = 5000) {
|
async function sleep(ms = 5000) {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
@@ -15,71 +15,99 @@ async function sleep(ms = 5000) {
|
|||||||
|
|
||||||
const workflow = new StateGraph(PizzaOrdererAnnotation)
|
const workflow = new StateGraph(PizzaOrdererAnnotation)
|
||||||
.addNode("findStore", async (state) => {
|
.addNode("findStore", async (state) => {
|
||||||
const findShopSchema = z.object({
|
const findShopSchema = z
|
||||||
location: z.string().describe("The location the user is in. E.g. 'San Francisco' or 'New York'"),
|
.object({
|
||||||
pizza_company: z.string().optional().describe("The name of the pizza company. E.g. 'Dominos' or 'Papa John's'. Optional, if not defined it will search for all pizza shops"),
|
location: z
|
||||||
}).describe("The schema for finding a pizza shop for the user")
|
.string()
|
||||||
const model = new ChatAnthropic({ model: "claude-3-5-sonnet-latest", temperature: 0 }).withStructuredOutput(findShopSchema, {
|
.describe(
|
||||||
|
"The location the user is in. E.g. 'San Francisco' or 'New York'",
|
||||||
|
),
|
||||||
|
pizza_company: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
"The name of the pizza company. E.g. 'Dominos' or 'Papa John's'. Optional, if not defined it will search for all pizza shops",
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.describe("The schema for finding a pizza shop for the user");
|
||||||
|
const model = new ChatAnthropic({
|
||||||
|
model: "claude-3-5-sonnet-latest",
|
||||||
|
temperature: 0,
|
||||||
|
}).withStructuredOutput(findShopSchema, {
|
||||||
name: "find_pizza_shop",
|
name: "find_pizza_shop",
|
||||||
includeRaw: true,
|
includeRaw: true,
|
||||||
})
|
});
|
||||||
|
|
||||||
const response = await model.invoke([
|
const response = await model.invoke([
|
||||||
{
|
{
|
||||||
role: "system",
|
role: "system",
|
||||||
content: "You are a helpful AI assistant, tasked with extracting information from the conversation between you, and the user, in order to find a pizza shop for them."
|
content:
|
||||||
|
"You are a helpful AI assistant, tasked with extracting information from the conversation between you, and the user, in order to find a pizza shop for them.",
|
||||||
},
|
},
|
||||||
...state.messages,
|
...state.messages,
|
||||||
])
|
]);
|
||||||
|
|
||||||
await sleep();
|
await sleep();
|
||||||
|
|
||||||
const toolResponse: ToolMessage = {
|
const toolResponse: ToolMessage = {
|
||||||
type: "tool",
|
type: "tool",
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
content: "I've found a pizza shop at 1119 19th St, San Francisco, CA 94107. The phone number for the shop is 415-555-1234.",
|
content:
|
||||||
tool_call_id: (response.raw as unknown as AIMessage).tool_calls?.[0].id ?? "",
|
"I've found a pizza shop at 1119 19th St, San Francisco, CA 94107. The phone number for the shop is 415-555-1234.",
|
||||||
}
|
tool_call_id:
|
||||||
|
(response.raw as unknown as AIMessage).tool_calls?.[0].id ?? "",
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
messages: [response.raw, toolResponse]
|
messages: [response.raw, toolResponse],
|
||||||
}
|
};
|
||||||
})
|
})
|
||||||
.addNode("orderPizza", async (state) => {
|
.addNode("orderPizza", async (state) => {
|
||||||
await sleep(1500);
|
await sleep(1500);
|
||||||
|
|
||||||
const placeOrderSchema = z.object({
|
const placeOrderSchema = z
|
||||||
address: z.string().describe("The address of the store to order the pizza from"),
|
.object({
|
||||||
phone_number: z.string().describe("The phone number of the store to order the pizza from"),
|
address: z
|
||||||
|
.string()
|
||||||
|
.describe("The address of the store to order the pizza from"),
|
||||||
|
phone_number: z
|
||||||
|
.string()
|
||||||
|
.describe("The phone number of the store to order the pizza from"),
|
||||||
order: z.string().describe("The full pizza order for the user"),
|
order: z.string().describe("The full pizza order for the user"),
|
||||||
}).describe("The schema for ordering a pizza for the user")
|
})
|
||||||
const model = new ChatAnthropic({ model: "claude-3-5-sonnet-latest", temperature: 0 }).withStructuredOutput(placeOrderSchema, {
|
.describe("The schema for ordering a pizza for the user");
|
||||||
|
const model = new ChatAnthropic({
|
||||||
|
model: "claude-3-5-sonnet-latest",
|
||||||
|
temperature: 0,
|
||||||
|
}).withStructuredOutput(placeOrderSchema, {
|
||||||
name: "place_pizza_order",
|
name: "place_pizza_order",
|
||||||
includeRaw: true,
|
includeRaw: true,
|
||||||
})
|
});
|
||||||
|
|
||||||
const response = await model.invoke([
|
const response = await model.invoke([
|
||||||
{
|
{
|
||||||
role: "system",
|
role: "system",
|
||||||
content: "You are a helpful AI assistant, tasked with placing an order for a pizza for the user."
|
content:
|
||||||
|
"You are a helpful AI assistant, tasked with placing an order for a pizza for the user.",
|
||||||
},
|
},
|
||||||
...state.messages,
|
...state.messages,
|
||||||
])
|
]);
|
||||||
|
|
||||||
const toolResponse: ToolMessage = {
|
const toolResponse: ToolMessage = {
|
||||||
type: "tool",
|
type: "tool",
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
content: "Pizza order successfully placed.",
|
content: "Pizza order successfully placed.",
|
||||||
tool_call_id: (response.raw as unknown as AIMessage).tool_calls?.[0].id ?? "",
|
tool_call_id:
|
||||||
}
|
(response.raw as unknown as AIMessage).tool_calls?.[0].id ?? "",
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
messages: [response.raw, toolResponse]
|
messages: [response.raw, toolResponse],
|
||||||
}
|
};
|
||||||
})
|
})
|
||||||
.addEdge(START, "findStore")
|
.addEdge(START, "findStore")
|
||||||
.addEdge("findStore", "orderPizza")
|
.addEdge("findStore", "orderPizza")
|
||||||
.addEdge("orderPizza", END)
|
.addEdge("orderPizza", END);
|
||||||
|
|
||||||
export const graph = workflow.compile()
|
export const graph = workflow.compile();
|
||||||
graph.name = "Order Pizza Graph";
|
graph.name = "Order Pizza Graph";
|
||||||
|
|||||||
6411
pnpm-lock.yaml
generated
6411
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -46,15 +46,17 @@ function CustomComponent({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseAnthropicStreamedToolCalls(content: MessageContentComplex[]): AIMessage["tool_calls"] {
|
function parseAnthropicStreamedToolCalls(
|
||||||
|
content: MessageContentComplex[],
|
||||||
|
): AIMessage["tool_calls"] {
|
||||||
const toolCallContents = content.filter((c) => c.type === "tool_use" && c.id);
|
const toolCallContents = content.filter((c) => c.type === "tool_use" && c.id);
|
||||||
|
|
||||||
return toolCallContents.map((tc) => {
|
return toolCallContents.map((tc) => {
|
||||||
const toolCall = tc as Record<string, any>
|
const toolCall = tc as Record<string, any>;
|
||||||
let json: Record<string, any> = {};
|
let json: Record<string, any> = {};
|
||||||
if (toolCall?.input) {
|
if (toolCall?.input) {
|
||||||
try {
|
try {
|
||||||
json = parsePartialJson(toolCall.input) ?? {}
|
json = parsePartialJson(toolCall.input) ?? {};
|
||||||
} catch {
|
} catch {
|
||||||
// Pass
|
// Pass
|
||||||
}
|
}
|
||||||
@@ -64,8 +66,8 @@ function parseAnthropicStreamedToolCalls(content: MessageContentComplex[]): AIMe
|
|||||||
id: toolCall.id ?? "",
|
id: toolCall.id ?? "",
|
||||||
args: json,
|
args: json,
|
||||||
type: "tool_call",
|
type: "tool_call",
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AssistantMessage({
|
export function AssistantMessage({
|
||||||
@@ -82,13 +84,19 @@ export function AssistantMessage({
|
|||||||
const thread = useStreamContext();
|
const thread = useStreamContext();
|
||||||
const meta = thread.getMessagesMetadata(message);
|
const meta = thread.getMessagesMetadata(message);
|
||||||
const parentCheckpoint = meta?.firstSeenState?.parent_checkpoint;
|
const parentCheckpoint = meta?.firstSeenState?.parent_checkpoint;
|
||||||
const anthropicStreamedToolCalls = Array.isArray(message.content) ? parseAnthropicStreamedToolCalls(message.content) : undefined;
|
const anthropicStreamedToolCalls = Array.isArray(message.content)
|
||||||
|
? parseAnthropicStreamedToolCalls(message.content)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const hasToolCalls =
|
const hasToolCalls =
|
||||||
("tool_calls" in message &&
|
"tool_calls" in message &&
|
||||||
message.tool_calls &&
|
message.tool_calls &&
|
||||||
message.tool_calls.length > 0);
|
message.tool_calls.length > 0;
|
||||||
const toolCallsHaveContents = hasToolCalls && message.tool_calls?.some((tc) => tc.args && Object.keys(tc.args).length > 0);
|
const toolCallsHaveContents =
|
||||||
|
hasToolCalls &&
|
||||||
|
message.tool_calls?.some(
|
||||||
|
(tc) => tc.args && Object.keys(tc.args).length > 0,
|
||||||
|
);
|
||||||
const hasAnthropicToolCalls = !!anthropicStreamedToolCalls?.length;
|
const hasAnthropicToolCalls = !!anthropicStreamedToolCalls?.length;
|
||||||
const isToolResult = message.type === "tool";
|
const isToolResult = message.type === "tool";
|
||||||
|
|
||||||
@@ -103,8 +111,12 @@ export function AssistantMessage({
|
|||||||
<MarkdownText>{contentString}</MarkdownText>
|
<MarkdownText>{contentString}</MarkdownText>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{(hasToolCalls && toolCallsHaveContents && <ToolCalls toolCalls={message.tool_calls} />) ||
|
{(hasToolCalls && toolCallsHaveContents && (
|
||||||
(hasAnthropicToolCalls && <ToolCalls toolCalls={anthropicStreamedToolCalls} />) ||
|
<ToolCalls toolCalls={message.tool_calls} />
|
||||||
|
)) ||
|
||||||
|
(hasAnthropicToolCalls && (
|
||||||
|
<ToolCalls toolCalls={anthropicStreamedToolCalls} />
|
||||||
|
)) ||
|
||||||
(hasToolCalls && <ToolCalls toolCalls={message.tool_calls} />)}
|
(hasToolCalls && <ToolCalls toolCalls={message.tool_calls} />)}
|
||||||
<CustomComponent message={message} thread={thread} />
|
<CustomComponent message={message} thread={thread} />
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in New Issue
Block a user