-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
output_parser.ts
112 lines (102 loc) · 3.28 KB
/
output_parser.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import { AgentAction, AgentFinish } from "@langchain/core/agents";
import { renderTemplate } from "@langchain/core/prompts";
import { AgentActionOutputParser } from "../types.js";
import { FORMAT_INSTRUCTIONS } from "./prompt.js";
const FINAL_ANSWER_ACTION = "Final Answer:";
const FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE =
"Parsing LLM output produced both a final answer and a parse-able action:";
/**
* Parses ReAct-style LLM calls that have a single tool input.
*
* Expects output to be in one of two formats.
*
* If the output signals that an action should be taken,
* should be in the below format. This will result in an AgentAction
* being returned.
*
* ```
* Thought: agent thought here
* Action: search
* Action Input: what is the temperature in SF?
* ```
*
* If the output signals that a final answer should be given,
* should be in the below format. This will result in an AgentFinish
* being returned.
*
* ```
* Thought: agent thought here
* Final Answer: The temperature is 100 degrees
* ```
* @example
* ```typescript
*
* const runnableAgent = RunnableSequence.from([
* ...rest of runnable
* new ReActSingleInputOutputParser({ toolNames: ["SerpAPI", "Calculator"] }),
* ]);
* const agent = AgentExecutor.fromAgentAndTools({
* agent: runnableAgent,
* tools: [new SerpAPI(), new Calculator()],
* });
* const result = await agent.invoke({
* input: "whats the weather in pomfret?",
* });
* ```
*/
export class ReActSingleInputOutputParser extends AgentActionOutputParser {
lc_namespace = ["langchain", "agents", "react"];
private toolNames: string[];
constructor(fields: { toolNames: string[] }) {
super(...arguments);
this.toolNames = fields.toolNames;
}
/**
* Parses the given text into an AgentAction or AgentFinish object. If an
* output fixing parser is defined, uses it to parse the text.
* @param text Text to parse.
* @returns Promise that resolves to an AgentAction or AgentFinish object.
*/
async parse(text: string): Promise<AgentAction | AgentFinish> {
const includesAnswer = text.includes(FINAL_ANSWER_ACTION);
const regex =
/Action\s*\d*\s*:[\s]*(.*?)[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)/;
const actionMatch = text.match(regex);
if (actionMatch) {
if (includesAnswer) {
throw new Error(
`${FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE}: ${text}`
);
}
const action = actionMatch[1];
const actionInput = actionMatch[2];
const toolInput = actionInput.trim().replace(/"/g, "");
return {
tool: action,
toolInput,
log: text,
};
}
if (includesAnswer) {
const finalAnswerText = text.split(FINAL_ANSWER_ACTION)[1].trim();
return {
returnValues: {
output: finalAnswerText,
},
log: text,
};
}
throw new Error(`Could not parse LLM output: ${text}`);
}
/**
* Returns the format instructions as a string. If the 'raw' option is
* true, returns the raw FORMAT_INSTRUCTIONS.
* @param options Options for getting the format instructions.
* @returns Format instructions as a string.
*/
getFormatInstructions(): string {
return renderTemplate(FORMAT_INSTRUCTIONS, "f-string", {
tool_names: this.toolNames.join(", "),
});
}
}