@@ -13,16 +13,11 @@ import {
1313} from "@langchain/core/language_models/chat_models" ;
1414import {
1515 type StructuredOutputMethodOptions ,
16- type BaseLanguageModelInput ,
1716 isOpenAITool ,
1817} from "@langchain/core/language_models/base" ;
1918import { toJsonSchema } from "@langchain/core/utils/json_schema" ;
2019import { BaseLLMOutputParser } from "@langchain/core/output_parsers" ;
21- import {
22- Runnable ,
23- RunnablePassthrough ,
24- RunnableSequence ,
25- } from "@langchain/core/runnables" ;
20+ import { RunnableLambda } from "@langchain/core/runnables" ;
2621import {
2722 InteropZodType ,
2823 isInteropZodSchema ,
@@ -622,9 +617,10 @@ function extractToken(chunk: AIMessageChunk): string | undefined {
622617 * <br />
623618 */
624619export class ChatAnthropicMessages <
625- CallOptions extends ChatAnthropicCallOptions = ChatAnthropicCallOptions
620+ CallOptions extends ChatAnthropicCallOptions = ChatAnthropicCallOptions ,
621+ RunOutput = AIMessageChunk
626622 >
627- extends BaseChatModel < CallOptions , AIMessageChunk >
623+ extends BaseChatModel < CallOptions , RunOutput >
628624 implements AnthropicInput
629625{
630626 static lc_name ( ) {
@@ -791,7 +787,7 @@ export class ChatAnthropicMessages<
791787 override bindTools (
792788 tools : ChatAnthropicToolType [ ] ,
793789 kwargs ?: Partial < CallOptions >
794- ) : Runnable < BaseLanguageModelInput , AIMessageChunk , CallOptions > {
790+ ) : this {
795791 return this . withConfig ( {
796792 tools : this . formatStructuredToolToAnthropic ( tools ) ,
797793 ...kwargs ,
@@ -1093,7 +1089,7 @@ export class ChatAnthropicMessages<
10931089 // eslint-disable-next-line @typescript-eslint/no-explicit-any
10941090 | Record < string , any > ,
10951091 config ?: StructuredOutputMethodOptions < false >
1096- ) : Runnable < BaseLanguageModelInput , RunOutput > ;
1092+ ) : ChatAnthropicMessages < CallOptions , RunOutput > ;
10971093
10981094 withStructuredOutput <
10991095 // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -1104,7 +1100,10 @@ export class ChatAnthropicMessages<
11041100 // eslint-disable-next-line @typescript-eslint/no-explicit-any
11051101 | Record < string , any > ,
11061102 config ?: StructuredOutputMethodOptions < true >
1107- ) : Runnable < BaseLanguageModelInput , { raw : BaseMessage ; parsed : RunOutput } > ;
1103+ ) : ChatAnthropicMessages <
1104+ CallOptions ,
1105+ { raw : AIMessageChunk ; parsed : RunOutput }
1106+ > ;
11081107
11091108 withStructuredOutput <
11101109 // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -1116,10 +1115,10 @@ export class ChatAnthropicMessages<
11161115 | Record < string , any > ,
11171116 config ?: StructuredOutputMethodOptions < boolean >
11181117 ) :
1119- | Runnable < BaseLanguageModelInput , RunOutput >
1120- | Runnable <
1121- BaseLanguageModelInput ,
1122- { raw : BaseMessage ; parsed : RunOutput }
1118+ | BaseChatModel < CallOptions , RunOutput >
1119+ | BaseChatModel <
1120+ CallOptions ,
1121+ { raw : AIMessageChunk ; parsed : RunOutput | null }
11231122 > {
11241123 // eslint-disable-next-line @typescript-eslint/no-explicit-any
11251124 const schema : InteropZodType < RunOutput > | Record < string , any > =
@@ -1130,6 +1129,12 @@ export class ChatAnthropicMessages<
11301129 if ( method === "jsonMode" ) {
11311130 throw new Error ( `Anthropic only supports "functionCalling" as a method.` ) ;
11321131 }
1132+ const thinkingAdmonition =
1133+ "Anthropic structured output relies on forced tool calling, " +
1134+ "which is not supported when `thinking` is enabled. This method will raise " +
1135+ "OutputParserException if tool calls are not " +
1136+ "generated. Consider disabling `thinking` or adjust your prompt to ensure " +
1137+ "the tool is called." ;
11331138
11341139 let functionName = name ?? "extract" ;
11351140 let outputParser : BaseLLMOutputParser < RunOutput > ;
@@ -1148,6 +1153,8 @@ export class ChatAnthropicMessages<
11481153 returnSingle : true ,
11491154 keyName : functionName ,
11501155 zodSchema : schema ,
1156+ errorMsgIfNoToolCalls :
1157+ this . thinking ?. type === "enabled" ? thinkingAdmonition : undefined ,
11511158 } ) ;
11521159 } else {
11531160 let anthropicTools : Anthropic . Messages . Tool ;
@@ -1170,35 +1177,20 @@ export class ChatAnthropicMessages<
11701177 outputParser = new AnthropicToolsOutputParser < RunOutput > ( {
11711178 returnSingle : true ,
11721179 keyName : functionName ,
1180+ errorMsgIfNoToolCalls :
1181+ this . thinking ?. type === "enabled" ? thinkingAdmonition : undefined ,
11731182 } ) ;
11741183 }
1175- let llm ;
1184+ let llm : this ;
11761185 if ( this . thinking ?. type === "enabled" ) {
1177- const thinkingAdmonition =
1178- "Anthropic structured output relies on forced tool calling, " +
1179- "which is not supported when `thinking` is enabled. This method will raise " +
1180- "OutputParserException if tool calls are not " +
1181- "generated. Consider disabling `thinking` or adjust your prompt to ensure " +
1182- "the tool is called." ;
1183-
11841186 console . warn ( thinkingAdmonition ) ;
1185-
11861187 llm = this . withConfig ( {
11871188 tools,
11881189 ls_structured_output_format : {
11891190 kwargs : { method : "functionCalling" } ,
11901191 schema : toJsonSchema ( schema ) ,
11911192 } ,
11921193 } as Partial < CallOptions > ) ;
1193-
1194- const raiseIfNoToolCalls = ( message : AIMessageChunk ) => {
1195- if ( ! message . tool_calls || message . tool_calls . length === 0 ) {
1196- throw new Error ( thinkingAdmonition ) ;
1197- }
1198- return message ;
1199- } ;
1200-
1201- llm = llm . pipe ( raiseIfNoToolCalls ) ;
12021194 } else {
12031195 llm = this . withConfig ( {
12041196 tools,
@@ -1214,32 +1206,16 @@ export class ChatAnthropicMessages<
12141206 }
12151207
12161208 if ( ! includeRaw ) {
1217- return llm . pipe ( outputParser ) . withConfig ( {
1218- runName : "ChatAnthropicStructuredOutput" ,
1219- } ) as Runnable < BaseLanguageModelInput , RunOutput > ;
1209+ return llm . withOutputParser ( outputParser ) ;
12201210 }
12211211
1222- const parserAssign = RunnablePassthrough . assign ( {
1223- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1224- parsed : ( input : any , config ) => outputParser . invoke ( input . raw , config ) ,
1225- } ) ;
1226- const parserNone = RunnablePassthrough . assign ( {
1227- parsed : ( ) => null ,
1228- } ) ;
1229- const parsedWithFallback = parserAssign . withFallbacks ( {
1230- fallbacks : [ parserNone ] ,
1231- } ) ;
1232- return RunnableSequence . from <
1233- BaseLanguageModelInput ,
1234- { raw : BaseMessage ; parsed : RunOutput }
1235- > ( [
1236- {
1237- raw : llm ,
1238- } ,
1239- parsedWithFallback ,
1240- ] ) . withConfig ( {
1241- runName : "StructuredOutputRunnable" ,
1242- } ) ;
1212+ const parserWithRaw = RunnableLambda . from (
1213+ async ( message : AIMessageChunk , config ) => ( {
1214+ raw : message ,
1215+ parsed : await outputParser . invoke ( message , config ) . catch ( ( ) => null ) ,
1216+ } )
1217+ ) ;
1218+ return llm . withOutputParser ( parserWithRaw ) ;
12431219 }
12441220}
12451221
0 commit comments