@ai-sdk/langchain 0.0.0-4115c213-20260122152721 → 0.0.0-4caafb2a-20260122145312
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -9
- package/package.json +3 -7
- package/src/__fixtures__/langgraph.ts +16543 -0
- package/src/__snapshots__/langgraph-hitl-request-1.json +508 -0
- package/src/__snapshots__/langgraph-hitl-request-2.json +173 -0
- package/src/__snapshots__/react-agent-tool-calling.json +859 -0
- package/src/adapter.test.ts +2043 -0
- package/src/stream-callbacks.test.ts +196 -0
- package/src/transport.test.ts +41 -0
- package/src/utils.test.ts +1982 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createCallbacksTransformer,
|
|
3
|
+
StreamCallbacks,
|
|
4
|
+
} from './stream-callbacks';
|
|
5
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
6
|
+
|
|
7
|
+
describe('createCallbacksTransformer', () => {
|
|
8
|
+
async function processStream(
|
|
9
|
+
input: string[],
|
|
10
|
+
callbacks?: StreamCallbacks,
|
|
11
|
+
): Promise<string[]> {
|
|
12
|
+
const transformer = createCallbacksTransformer(callbacks);
|
|
13
|
+
const readable = new ReadableStream({
|
|
14
|
+
start(controller) {
|
|
15
|
+
for (const chunk of input) {
|
|
16
|
+
controller.enqueue(chunk);
|
|
17
|
+
}
|
|
18
|
+
controller.close();
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const output: string[] = [];
|
|
23
|
+
const writable = new WritableStream({
|
|
24
|
+
write(chunk) {
|
|
25
|
+
output.push(chunk);
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
await readable.pipeThrough(transformer).pipeTo(writable);
|
|
30
|
+
return output;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
it('should pass through messages without callbacks', async () => {
|
|
34
|
+
const input = ['Hello', ' ', 'World'];
|
|
35
|
+
const output = await processStream(input);
|
|
36
|
+
|
|
37
|
+
expect(output).toEqual(input);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should pass through messages with empty callbacks object', async () => {
|
|
41
|
+
const input = ['Hello', ' ', 'World'];
|
|
42
|
+
const output = await processStream(input, {});
|
|
43
|
+
|
|
44
|
+
expect(output).toEqual(input);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should call onStart once at the beginning', async () => {
|
|
48
|
+
const onStart = vi.fn();
|
|
49
|
+
const input = ['Hello', ' ', 'World'];
|
|
50
|
+
|
|
51
|
+
await processStream(input, { onStart });
|
|
52
|
+
|
|
53
|
+
expect(onStart).toHaveBeenCalledTimes(1);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should call async onStart', async () => {
|
|
57
|
+
const onStart = vi.fn().mockResolvedValue(undefined);
|
|
58
|
+
const input = ['Hello'];
|
|
59
|
+
|
|
60
|
+
await processStream(input, { onStart });
|
|
61
|
+
|
|
62
|
+
expect(onStart).toHaveBeenCalledTimes(1);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should call onToken for each message', async () => {
|
|
66
|
+
const onToken = vi.fn();
|
|
67
|
+
const input = ['Hello', ' ', 'World'];
|
|
68
|
+
|
|
69
|
+
await processStream(input, { onToken });
|
|
70
|
+
|
|
71
|
+
expect(onToken).toHaveBeenCalledTimes(3);
|
|
72
|
+
expect(onToken).toHaveBeenNthCalledWith(1, 'Hello');
|
|
73
|
+
expect(onToken).toHaveBeenNthCalledWith(2, ' ');
|
|
74
|
+
expect(onToken).toHaveBeenNthCalledWith(3, 'World');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should call async onToken', async () => {
|
|
78
|
+
const onToken = vi.fn().mockResolvedValue(undefined);
|
|
79
|
+
const input = ['Hello', 'World'];
|
|
80
|
+
|
|
81
|
+
await processStream(input, { onToken });
|
|
82
|
+
|
|
83
|
+
expect(onToken).toHaveBeenCalledTimes(2);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should call onText for each string message', async () => {
|
|
87
|
+
const onText = vi.fn();
|
|
88
|
+
const input = ['Hello', ' ', 'World'];
|
|
89
|
+
|
|
90
|
+
await processStream(input, { onText });
|
|
91
|
+
|
|
92
|
+
expect(onText).toHaveBeenCalledTimes(3);
|
|
93
|
+
expect(onText).toHaveBeenNthCalledWith(1, 'Hello');
|
|
94
|
+
expect(onText).toHaveBeenNthCalledWith(2, ' ');
|
|
95
|
+
expect(onText).toHaveBeenNthCalledWith(3, 'World');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should call async onText', async () => {
|
|
99
|
+
const onText = vi.fn().mockResolvedValue(undefined);
|
|
100
|
+
const input = ['Hello'];
|
|
101
|
+
|
|
102
|
+
await processStream(input, { onText });
|
|
103
|
+
|
|
104
|
+
expect(onText).toHaveBeenCalledTimes(1);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should call onFinal with aggregated response', async () => {
|
|
108
|
+
const onFinal = vi.fn();
|
|
109
|
+
const input = ['Hello', ' ', 'World'];
|
|
110
|
+
|
|
111
|
+
await processStream(input, { onFinal });
|
|
112
|
+
|
|
113
|
+
expect(onFinal).toHaveBeenCalledTimes(1);
|
|
114
|
+
expect(onFinal).toHaveBeenCalledWith('Hello World');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should call async onFinal', async () => {
|
|
118
|
+
const onFinal = vi.fn().mockResolvedValue(undefined);
|
|
119
|
+
const input = ['Hello', 'World'];
|
|
120
|
+
|
|
121
|
+
await processStream(input, { onFinal });
|
|
122
|
+
|
|
123
|
+
expect(onFinal).toHaveBeenCalledTimes(1);
|
|
124
|
+
expect(onFinal).toHaveBeenCalledWith('HelloWorld');
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should call onFinal with empty string when no messages', async () => {
|
|
128
|
+
const onFinal = vi.fn();
|
|
129
|
+
const input: string[] = [];
|
|
130
|
+
|
|
131
|
+
await processStream(input, { onFinal });
|
|
132
|
+
|
|
133
|
+
expect(onFinal).toHaveBeenCalledTimes(1);
|
|
134
|
+
expect(onFinal).toHaveBeenCalledWith('');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should call all callbacks in correct order', async () => {
|
|
138
|
+
const callOrder: string[] = [];
|
|
139
|
+
|
|
140
|
+
const callbacks: StreamCallbacks = {
|
|
141
|
+
onStart: () => {
|
|
142
|
+
callOrder.push('start');
|
|
143
|
+
},
|
|
144
|
+
onToken: token => {
|
|
145
|
+
callOrder.push(`token:${token}`);
|
|
146
|
+
},
|
|
147
|
+
onText: text => {
|
|
148
|
+
callOrder.push(`text:${text}`);
|
|
149
|
+
},
|
|
150
|
+
onFinal: completion => {
|
|
151
|
+
callOrder.push(`final:${completion}`);
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const input = ['A', 'B'];
|
|
156
|
+
await processStream(input, callbacks);
|
|
157
|
+
|
|
158
|
+
expect(callOrder).toEqual([
|
|
159
|
+
'start',
|
|
160
|
+
'token:A',
|
|
161
|
+
'text:A',
|
|
162
|
+
'token:B',
|
|
163
|
+
'text:B',
|
|
164
|
+
'final:AB',
|
|
165
|
+
]);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should handle single character messages', async () => {
|
|
169
|
+
const onToken = vi.fn();
|
|
170
|
+
const onFinal = vi.fn();
|
|
171
|
+
const input = ['a', 'b', 'c'];
|
|
172
|
+
|
|
173
|
+
await processStream(input, { onToken, onFinal });
|
|
174
|
+
|
|
175
|
+
expect(onToken).toHaveBeenCalledTimes(3);
|
|
176
|
+
expect(onFinal).toHaveBeenCalledWith('abc');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should handle messages with special characters', async () => {
|
|
180
|
+
const onFinal = vi.fn();
|
|
181
|
+
const input = ['Hello\n', 'World\t', '!'];
|
|
182
|
+
|
|
183
|
+
await processStream(input, { onFinal });
|
|
184
|
+
|
|
185
|
+
expect(onFinal).toHaveBeenCalledWith('Hello\nWorld\t!');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should handle unicode characters', async () => {
|
|
189
|
+
const onFinal = vi.fn();
|
|
190
|
+
const input = ['こんにちは', ' ', '🌍'];
|
|
191
|
+
|
|
192
|
+
await processStream(input, { onFinal });
|
|
193
|
+
|
|
194
|
+
expect(onFinal).toHaveBeenCalledWith('こんにちは 🌍');
|
|
195
|
+
});
|
|
196
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { LangSmithDeploymentTransport } from './transport';
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
|
|
4
|
+
describe('LangSmithDeploymentTransport', () => {
|
|
5
|
+
it('should create transport with options', () => {
|
|
6
|
+
const transport = new LangSmithDeploymentTransport({
|
|
7
|
+
url: 'https://test.langsmith.app',
|
|
8
|
+
apiKey: 'test-key',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
expect('sendMessages' in transport).toBe(true);
|
|
12
|
+
expect('reconnectToStream' in transport).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should create transport with only url', () => {
|
|
16
|
+
const transport = new LangSmithDeploymentTransport({
|
|
17
|
+
url: 'https://test.langsmith.app',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
expect('sendMessages' in transport).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should create transport with custom graphId', () => {
|
|
24
|
+
const transport = new LangSmithDeploymentTransport({
|
|
25
|
+
url: 'https://test.langsmith.app',
|
|
26
|
+
graphId: 'custom-agent',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
expect('sendMessages' in transport).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should throw error for reconnectToStream', async () => {
|
|
33
|
+
const transport = new LangSmithDeploymentTransport({
|
|
34
|
+
url: 'https://test.langsmith.app',
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
await expect(
|
|
38
|
+
transport.reconnectToStream({ chatId: 'chat-1' }),
|
|
39
|
+
).rejects.toThrow('Method not implemented.');
|
|
40
|
+
});
|
|
41
|
+
});
|