@flink-app/test-utils 1.0.0 → 2.0.0-alpha.49
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 +12 -0
- package/dist/ai/conversation.d.ts +27 -0
- package/dist/ai/conversation.js +30 -0
- package/dist/ai/index.d.ts +4 -0
- package/dist/ai/index.js +20 -0
- package/dist/ai/mockContext.d.ts +16 -0
- package/dist/ai/mockContext.js +39 -0
- package/dist/ai/mockLLMAdapter.d.ts +90 -0
- package/dist/ai/mockLLMAdapter.js +252 -0
- package/dist/ai/mockTool.d.ts +64 -0
- package/dist/ai/mockTool.js +133 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +9 -5
- package/spec/ai/conversation.spec.ts +280 -0
- package/spec/ai/mockLLMAdapter.spec.ts +533 -0
- package/spec/ai/mockTool.spec.ts +313 -0
- package/spec/support/jasmine.json +7 -0
- package/src/ai/conversation.ts +54 -0
- package/src/ai/index.ts +4 -0
- package/src/ai/mockContext.ts +41 -0
- package/src/ai/mockLLMAdapter.ts +238 -0
- package/src/ai/mockTool.ts +135 -0
- package/src/index.ts +1 -0
- package/tsconfig.dist.json +4 -0
- package/tsconfig.json +6 -5
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
13
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.mockTool = mockTool;
|
|
40
|
+
/**
|
|
41
|
+
* Creates a mock tool with tracking and canned responses
|
|
42
|
+
*
|
|
43
|
+
* Features:
|
|
44
|
+
* - Simple canned responses
|
|
45
|
+
* - Error simulation
|
|
46
|
+
* - Custom function support
|
|
47
|
+
* - Automatic invocation tracking
|
|
48
|
+
* - Validation helpers
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* // Simple canned response
|
|
52
|
+
* const weatherTool = mockTool({
|
|
53
|
+
* name: "get_weather",
|
|
54
|
+
* inputSchema: z.object({ city: z.string() }),
|
|
55
|
+
* response: { temperature: 22, conditions: "sunny" }
|
|
56
|
+
* });
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // Custom function with tracking
|
|
60
|
+
* const calculatorTool = mockTool({
|
|
61
|
+
* name: "calculate",
|
|
62
|
+
* inputSchema: z.object({ a: z.number(), b: z.number() }),
|
|
63
|
+
* fn: async ({ input }) => ({
|
|
64
|
+
* success: true,
|
|
65
|
+
* data: { result: input.a + input.b }
|
|
66
|
+
* })
|
|
67
|
+
* });
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* // Error simulation
|
|
71
|
+
* const failingTool = mockTool({
|
|
72
|
+
* name: "fail",
|
|
73
|
+
* inputSchema: z.object({}),
|
|
74
|
+
* error: { error: "Tool failed", code: "MOCK_ERROR" }
|
|
75
|
+
* });
|
|
76
|
+
*/
|
|
77
|
+
function mockTool(config) {
|
|
78
|
+
var _this = this;
|
|
79
|
+
var invocations = [];
|
|
80
|
+
// Create the tool props
|
|
81
|
+
var props = {
|
|
82
|
+
id: config.name,
|
|
83
|
+
description: config.description || "Mock tool: ".concat(config.name),
|
|
84
|
+
inputSchema: config.inputSchema,
|
|
85
|
+
outputSchema: config.outputSchema,
|
|
86
|
+
permissions: config.permissions,
|
|
87
|
+
};
|
|
88
|
+
// Create the tool function with invocation tracking
|
|
89
|
+
var fn = function (_a) { return __awaiter(_this, [_a], void 0, function (_b) {
|
|
90
|
+
var input = _b.input, ctx = _b.ctx, user = _b.user;
|
|
91
|
+
return __generator(this, function (_c) {
|
|
92
|
+
// Track invocation
|
|
93
|
+
invocations.push({ input: input, user: user });
|
|
94
|
+
// If custom function provided, use it
|
|
95
|
+
if (config.fn) {
|
|
96
|
+
return [2 /*return*/, config.fn({ input: input, ctx: ctx, user: user })];
|
|
97
|
+
}
|
|
98
|
+
// If error configured, return error
|
|
99
|
+
if (config.error) {
|
|
100
|
+
return [2 /*return*/, {
|
|
101
|
+
success: false,
|
|
102
|
+
error: config.error.error,
|
|
103
|
+
code: config.error.code,
|
|
104
|
+
}];
|
|
105
|
+
}
|
|
106
|
+
// If response configured, return success with data
|
|
107
|
+
if (config.response !== undefined) {
|
|
108
|
+
return [2 /*return*/, {
|
|
109
|
+
success: true,
|
|
110
|
+
data: config.response,
|
|
111
|
+
}];
|
|
112
|
+
}
|
|
113
|
+
// Default: return empty success
|
|
114
|
+
return [2 /*return*/, {
|
|
115
|
+
success: true,
|
|
116
|
+
data: {},
|
|
117
|
+
}];
|
|
118
|
+
});
|
|
119
|
+
}); };
|
|
120
|
+
var getLastInvocation = function () {
|
|
121
|
+
return invocations[invocations.length - 1];
|
|
122
|
+
};
|
|
123
|
+
var reset = function () {
|
|
124
|
+
invocations.length = 0;
|
|
125
|
+
};
|
|
126
|
+
return {
|
|
127
|
+
props: props,
|
|
128
|
+
fn: fn,
|
|
129
|
+
invocations: invocations,
|
|
130
|
+
getLastInvocation: getLastInvocation,
|
|
131
|
+
reset: reset,
|
|
132
|
+
};
|
|
133
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -16,3 +16,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./http"), exports);
|
|
18
18
|
__exportStar(require("./mocks"), exports);
|
|
19
|
+
__exportStar(require("./ai"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flink-app/test-utils",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-alpha.49",
|
|
4
4
|
"description": "Test utils for Flink",
|
|
5
5
|
"author": "joel@frost.se",
|
|
6
6
|
"license": "MIT",
|
|
@@ -11,20 +11,24 @@
|
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"got": "^9.6.0",
|
|
14
|
-
"qs": "^6.7.0"
|
|
14
|
+
"qs": "^6.7.0",
|
|
15
|
+
"zod": "^4.3.6"
|
|
15
16
|
},
|
|
16
17
|
"devDependencies": {
|
|
17
18
|
"@types/got": "^9.6.12",
|
|
19
|
+
"@types/jasmine": "^3.7.1",
|
|
18
20
|
"@types/node": "22.13.10",
|
|
19
21
|
"@types/qs": "^6.9.7",
|
|
22
|
+
"jasmine": "^3.10.0",
|
|
23
|
+
"jasmine-ts": "^0.3.3",
|
|
20
24
|
"ts-node": "^10.9.2",
|
|
21
25
|
"tsc-watch": "^4.2.9",
|
|
22
|
-
"@flink-app/flink": "
|
|
26
|
+
"@flink-app/flink": "2.0.0-alpha.49"
|
|
23
27
|
},
|
|
24
28
|
"gitHead": "4243e3b3cd6d4e1ca001a61baa8436bf2bbe4113",
|
|
25
29
|
"scripts": {
|
|
26
|
-
"test": "
|
|
27
|
-
"build": "tsc",
|
|
30
|
+
"test": "jasmine-ts --config=./spec/support/jasmine.json",
|
|
31
|
+
"build": "tsc --project tsconfig.dist.json",
|
|
28
32
|
"watch": "tsc-watch",
|
|
29
33
|
"clean": "rimraf dist .flink"
|
|
30
34
|
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { createConversation } from "../../src/ai/conversation";
|
|
2
|
+
|
|
3
|
+
describe("createConversation", () => {
|
|
4
|
+
describe("user messages", () => {
|
|
5
|
+
it("should add user message", () => {
|
|
6
|
+
const conversation = createConversation().user("Hello").build();
|
|
7
|
+
|
|
8
|
+
expect(conversation.length).toBe(1);
|
|
9
|
+
expect(conversation[0]).toEqual({
|
|
10
|
+
role: "user",
|
|
11
|
+
content: "Hello",
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should add multiple user messages", () => {
|
|
16
|
+
const conversation = createConversation()
|
|
17
|
+
.user("First message")
|
|
18
|
+
.user("Second message")
|
|
19
|
+
.build();
|
|
20
|
+
|
|
21
|
+
expect(conversation.length).toBe(2);
|
|
22
|
+
expect(conversation[0]).toEqual({
|
|
23
|
+
role: "user",
|
|
24
|
+
content: "First message",
|
|
25
|
+
});
|
|
26
|
+
expect(conversation[1]).toEqual({
|
|
27
|
+
role: "user",
|
|
28
|
+
content: "Second message",
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("assistant messages", () => {
|
|
34
|
+
it("should add assistant message without tool calls", () => {
|
|
35
|
+
const conversation = createConversation()
|
|
36
|
+
.assistant("I can help with that")
|
|
37
|
+
.build();
|
|
38
|
+
|
|
39
|
+
expect(conversation.length).toBe(1);
|
|
40
|
+
expect(conversation[0]).toEqual({
|
|
41
|
+
role: "assistant",
|
|
42
|
+
content: "I can help with that",
|
|
43
|
+
toolCalls: undefined,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should add assistant message with tool calls", () => {
|
|
48
|
+
const conversation = createConversation()
|
|
49
|
+
.assistant("Let me check", [
|
|
50
|
+
{ id: "1", name: "get_weather", input: { city: "Stockholm" } },
|
|
51
|
+
])
|
|
52
|
+
.build();
|
|
53
|
+
|
|
54
|
+
expect(conversation.length).toBe(1);
|
|
55
|
+
expect(conversation[0]).toEqual({
|
|
56
|
+
role: "assistant",
|
|
57
|
+
content: "Let me check",
|
|
58
|
+
toolCalls: [
|
|
59
|
+
{ id: "1", name: "get_weather", input: { city: "Stockholm" } },
|
|
60
|
+
],
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should add multiple assistant messages", () => {
|
|
65
|
+
const conversation = createConversation()
|
|
66
|
+
.assistant("First response")
|
|
67
|
+
.assistant("Second response")
|
|
68
|
+
.build();
|
|
69
|
+
|
|
70
|
+
expect(conversation.length).toBe(2);
|
|
71
|
+
expect(conversation[0].role).toBe("assistant");
|
|
72
|
+
expect(conversation[1].role).toBe("assistant");
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe("tool messages", () => {
|
|
77
|
+
it("should add tool message", () => {
|
|
78
|
+
const conversation = createConversation()
|
|
79
|
+
.tool("1", "get_weather", JSON.stringify({ temp: 22 }))
|
|
80
|
+
.build();
|
|
81
|
+
|
|
82
|
+
expect(conversation.length).toBe(1);
|
|
83
|
+
expect(conversation[0]).toEqual({
|
|
84
|
+
role: "tool",
|
|
85
|
+
toolCallId: "1",
|
|
86
|
+
toolName: "get_weather",
|
|
87
|
+
result: JSON.stringify({ temp: 22 }),
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should add multiple tool messages", () => {
|
|
92
|
+
const conversation = createConversation()
|
|
93
|
+
.tool("1", "tool_one", "result 1")
|
|
94
|
+
.tool("2", "tool_two", "result 2")
|
|
95
|
+
.build();
|
|
96
|
+
|
|
97
|
+
expect(conversation.length).toBe(2);
|
|
98
|
+
expect(conversation[0].role).toBe("tool");
|
|
99
|
+
expect(conversation[1].role).toBe("tool");
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("fluent API", () => {
|
|
104
|
+
it("should chain different message types", () => {
|
|
105
|
+
const conversation = createConversation()
|
|
106
|
+
.user("What's the weather?")
|
|
107
|
+
.assistant("Let me check", [
|
|
108
|
+
{ id: "1", name: "get_weather", input: { city: "Stockholm" } },
|
|
109
|
+
])
|
|
110
|
+
.tool("1", "get_weather", JSON.stringify({ temp: 22 }))
|
|
111
|
+
.assistant("It's 22°C in Stockholm")
|
|
112
|
+
.build();
|
|
113
|
+
|
|
114
|
+
expect(conversation.length).toBe(4);
|
|
115
|
+
expect(conversation[0].role).toBe("user");
|
|
116
|
+
expect(conversation[1].role).toBe("assistant");
|
|
117
|
+
expect(conversation[2].role).toBe("tool");
|
|
118
|
+
expect(conversation[3].role).toBe("assistant");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("should return builder instance for chaining", () => {
|
|
122
|
+
const builder = createConversation();
|
|
123
|
+
const result1 = builder.user("test");
|
|
124
|
+
const result2 = result1.assistant("response");
|
|
125
|
+
const result3 = result2.tool("1", "tool", "result");
|
|
126
|
+
|
|
127
|
+
expect(result1).toBe(builder);
|
|
128
|
+
expect(result2).toBe(builder);
|
|
129
|
+
expect(result3).toBe(builder);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe("multi-turn conversations", () => {
|
|
134
|
+
it("should build complex multi-turn conversation", () => {
|
|
135
|
+
const conversation = createConversation()
|
|
136
|
+
.user("Book a flight to Paris")
|
|
137
|
+
.assistant("When would you like to travel?")
|
|
138
|
+
.user("Next Monday")
|
|
139
|
+
.assistant("Let me search for flights", [
|
|
140
|
+
{
|
|
141
|
+
id: "search_1",
|
|
142
|
+
name: "search_flights",
|
|
143
|
+
input: { destination: "Paris", date: "next Monday" },
|
|
144
|
+
},
|
|
145
|
+
])
|
|
146
|
+
.tool(
|
|
147
|
+
"search_1",
|
|
148
|
+
"search_flights",
|
|
149
|
+
JSON.stringify({ flights: ["AF123", "BA456"] })
|
|
150
|
+
)
|
|
151
|
+
.assistant("I found these flights: AF123 and BA456")
|
|
152
|
+
.user("Book AF123")
|
|
153
|
+
.assistant("Booking flight AF123", [
|
|
154
|
+
{ id: "book_1", name: "book_flight", input: { flightId: "AF123" } },
|
|
155
|
+
])
|
|
156
|
+
.tool("book_1", "book_flight", JSON.stringify({ success: true }))
|
|
157
|
+
.assistant("Your flight has been booked!")
|
|
158
|
+
.build();
|
|
159
|
+
|
|
160
|
+
expect(conversation.length).toBe(10);
|
|
161
|
+
|
|
162
|
+
// Verify structure
|
|
163
|
+
expect(conversation[0]).toEqual({
|
|
164
|
+
role: "user",
|
|
165
|
+
content: "Book a flight to Paris",
|
|
166
|
+
});
|
|
167
|
+
expect(conversation[1]).toEqual({
|
|
168
|
+
role: "assistant",
|
|
169
|
+
content: "When would you like to travel?",
|
|
170
|
+
toolCalls: undefined,
|
|
171
|
+
});
|
|
172
|
+
expect(conversation[9]).toEqual({
|
|
173
|
+
role: "assistant",
|
|
174
|
+
content: "Your flight has been booked!",
|
|
175
|
+
toolCalls: undefined,
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe("edge cases", () => {
|
|
181
|
+
it("should build empty conversation", () => {
|
|
182
|
+
const conversation = createConversation().build();
|
|
183
|
+
|
|
184
|
+
expect(conversation).toEqual([]);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("should handle empty strings", () => {
|
|
188
|
+
const conversation = createConversation()
|
|
189
|
+
.user("")
|
|
190
|
+
.assistant("")
|
|
191
|
+
.tool("1", "test", "")
|
|
192
|
+
.build();
|
|
193
|
+
|
|
194
|
+
expect(conversation.length).toBe(3);
|
|
195
|
+
expect((conversation[0] as any).content).toBe("");
|
|
196
|
+
expect((conversation[1] as any).content).toBe("");
|
|
197
|
+
expect((conversation[2] as any).result).toBe("");
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it("should handle special characters in content", () => {
|
|
201
|
+
const specialContent = 'Hello "world" with \n newlines and \t tabs';
|
|
202
|
+
const conversation = createConversation()
|
|
203
|
+
.user(specialContent)
|
|
204
|
+
.assistant(specialContent)
|
|
205
|
+
.build();
|
|
206
|
+
|
|
207
|
+
expect((conversation[0] as any).content).toBe(specialContent);
|
|
208
|
+
expect((conversation[1] as any).content).toBe(specialContent);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("should handle complex tool inputs", () => {
|
|
212
|
+
const conversation = createConversation()
|
|
213
|
+
.assistant("Processing", [
|
|
214
|
+
{
|
|
215
|
+
id: "complex_1",
|
|
216
|
+
name: "complex_tool",
|
|
217
|
+
input: {
|
|
218
|
+
nested: {
|
|
219
|
+
array: [1, 2, 3],
|
|
220
|
+
object: { key: "value" },
|
|
221
|
+
},
|
|
222
|
+
special: 'chars "quotes" \n newlines',
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
])
|
|
226
|
+
.build();
|
|
227
|
+
|
|
228
|
+
const toolCall = (conversation[0] as any).toolCalls[0];
|
|
229
|
+
expect(toolCall.input.nested.array).toEqual([1, 2, 3]);
|
|
230
|
+
expect(toolCall.input.special).toContain("quotes");
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe("real-world usage patterns", () => {
|
|
235
|
+
it("should support weather agent conversation", () => {
|
|
236
|
+
const conversation = createConversation()
|
|
237
|
+
.user("What's the weather in Stockholm?")
|
|
238
|
+
.assistant("Let me check the weather for you", [
|
|
239
|
+
{ id: "w1", name: "get_weather", input: { city: "Stockholm" } },
|
|
240
|
+
])
|
|
241
|
+
.tool(
|
|
242
|
+
"w1",
|
|
243
|
+
"get_weather",
|
|
244
|
+
JSON.stringify({ temperature: 22, conditions: "sunny" })
|
|
245
|
+
)
|
|
246
|
+
.assistant("It's currently 22°C and sunny in Stockholm")
|
|
247
|
+
.build();
|
|
248
|
+
|
|
249
|
+
expect(conversation.length).toBe(4);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it("should support calculator agent conversation", () => {
|
|
253
|
+
const conversation = createConversation()
|
|
254
|
+
.user("What is 15 + 27?")
|
|
255
|
+
.assistant("Let me calculate that", [
|
|
256
|
+
{ id: "c1", name: "calculate", input: { a: 15, b: 27 } },
|
|
257
|
+
])
|
|
258
|
+
.tool("c1", "calculate", JSON.stringify({ result: 42 }))
|
|
259
|
+
.assistant("15 + 27 = 42")
|
|
260
|
+
.build();
|
|
261
|
+
|
|
262
|
+
expect(conversation.length).toBe(4);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("should support multi-tool agent conversation", () => {
|
|
266
|
+
const conversation = createConversation()
|
|
267
|
+
.user("Get me the weather and news for Stockholm")
|
|
268
|
+
.assistant("I'll fetch both for you", [
|
|
269
|
+
{ id: "w1", name: "get_weather", input: { city: "Stockholm" } },
|
|
270
|
+
{ id: "n1", name: "get_news", input: { city: "Stockholm" } },
|
|
271
|
+
])
|
|
272
|
+
.tool("w1", "get_weather", JSON.stringify({ temp: 22 }))
|
|
273
|
+
.tool("n1", "get_news", JSON.stringify({ headlines: ["News 1"] }))
|
|
274
|
+
.assistant("Weather: 22°C. Top news: News 1")
|
|
275
|
+
.build();
|
|
276
|
+
|
|
277
|
+
expect(conversation.length).toBe(5);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
});
|