/
passthrough.ts
150 lines (138 loc) Β· 4.22 KB
/
passthrough.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import { concat } from "../utils/stream.js";
import {
Runnable,
RunnableAssign,
RunnableMap,
RunnableMapLike,
} from "./base.js";
import { ensureConfig, type RunnableConfig } from "./config.js";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type RunnablePassthroughFunc<RunInput = any> =
| ((input: RunInput) => void)
| ((input: RunInput, config?: RunnableConfig) => void)
| ((input: RunInput) => Promise<void>)
| ((input: RunInput, config?: RunnableConfig) => Promise<void>);
/**
* A runnable to passthrough inputs unchanged or with additional keys.
*
* This runnable behaves almost like the identity function, except that it
* can be configured to add additional keys to the output, if the input is
* an object.
*
* The example below demonstrates how to use `RunnablePassthrough to
* passthrough the input from the `.invoke()`
*
* @example
* ```typescript
* const chain = RunnableSequence.from([
* {
* question: new RunnablePassthrough(),
* context: async () => loadContextFromStore(),
* },
* prompt,
* llm,
* outputParser,
* ]);
* const response = await chain.invoke(
* "I can pass a single string instead of an object since I'm using `RunnablePassthrough`."
* );
* ```
*/
export class RunnablePassthrough<RunInput> extends Runnable<
RunInput,
RunInput
> {
static lc_name() {
return "RunnablePassthrough";
}
lc_namespace = ["langchain_core", "runnables"];
lc_serializable = true;
func?: RunnablePassthroughFunc<RunInput>;
constructor(fields?: { func?: RunnablePassthroughFunc<RunInput> }) {
super(fields);
if (fields) {
this.func = fields.func;
}
}
async invoke(
input: RunInput,
options?: Partial<RunnableConfig>
): Promise<RunInput> {
const config = ensureConfig(options);
if (this.func) {
await this.func(input, config);
}
return this._callWithConfig(
(input: RunInput) => Promise.resolve(input),
input,
config
);
}
async *transform(
generator: AsyncGenerator<RunInput>,
options: Partial<RunnableConfig>
): AsyncGenerator<RunInput> {
const config = ensureConfig(options);
let finalOutput: RunInput | undefined;
let finalOutputSupported = true;
for await (const chunk of this._transformStreamWithConfig(
generator,
(input: AsyncGenerator<RunInput>) => input,
config
)) {
yield chunk;
if (finalOutputSupported) {
if (finalOutput === undefined) {
finalOutput = chunk;
} else {
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
finalOutput = concat(finalOutput, chunk as any);
} catch {
finalOutput = undefined;
finalOutputSupported = false;
}
}
}
}
if (this.func && finalOutput !== undefined) {
await this.func(finalOutput, config);
}
}
/**
* A runnable that assigns key-value pairs to the input.
*
* The example below shows how you could use it with an inline function.
*
* @example
* ```typescript
* const prompt =
* PromptTemplate.fromTemplate(`Write a SQL query to answer the question using the following schema: {schema}
* Question: {question}
* SQL Query:`);
*
* // The `RunnablePassthrough.assign()` is used here to passthrough the input from the `.invoke()`
* // call (in this example it's the question), along with any inputs passed to the `.assign()` method.
* // In this case, we're passing the schema.
* const sqlQueryGeneratorChain = RunnableSequence.from([
* RunnablePassthrough.assign({
* schema: async () => db.getTableInfo(),
* }),
* prompt,
* new ChatOpenAI({}).bind({ stop: ["\nSQLResult:"] }),
* new StringOutputParser(),
* ]);
* const result = await sqlQueryGeneratorChain.invoke({
* question: "How many employees are there?",
* });
* ```
*/
static assign<
RunInput extends Record<string, unknown> = Record<string, unknown>,
RunOutput extends Record<string, unknown> = Record<string, unknown>
>(
mapping: RunnableMapLike<RunInput, RunOutput>
): RunnableAssign<RunInput, RunInput & RunOutput> {
return new RunnableAssign(new RunnableMap({ steps: mapping }));
}
}