@orka-js/test 1.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Orka Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.cjs ADDED
@@ -0,0 +1,275 @@
1
+ 'use strict';
2
+
3
+ var core = require('@orka-js/core');
4
+
5
+ // src/mock-llm.ts
6
+ var MockLLMAdapter = class {
7
+ name = "mock";
8
+ supportsStreaming = true;
9
+ responses;
10
+ calls = [];
11
+ defaultOutput;
12
+ constructor(responses = [], defaultOutput = "Mock response") {
13
+ this.responses = responses;
14
+ this.defaultOutput = defaultOutput;
15
+ }
16
+ findResponse(prompt) {
17
+ for (const response of this.responses) {
18
+ if (!response.when) return response;
19
+ if (typeof response.when === "string") {
20
+ if (prompt.includes(response.when)) return response;
21
+ } else if (response.when instanceof RegExp) {
22
+ if (response.when.test(prompt)) return response;
23
+ } else if (typeof response.when === "function") {
24
+ if (response.when(prompt)) return response;
25
+ }
26
+ }
27
+ return null;
28
+ }
29
+ getPromptText(prompt, options) {
30
+ const messagesText = options?.messages?.map((m) => typeof m.content === "string" ? m.content : JSON.stringify(m.content)).join(" ") ?? "";
31
+ return prompt + " " + messagesText;
32
+ }
33
+ async generate(prompt, options) {
34
+ const fullPrompt = this.getPromptText(prompt, options);
35
+ const response = this.findResponse(fullPrompt);
36
+ this.calls.push({ prompt: fullPrompt, options, timestamp: Date.now(), response });
37
+ if (response?.latencyMs) {
38
+ await new Promise((resolve) => setTimeout(resolve, response.latencyMs));
39
+ }
40
+ if (response?.error) throw response.error;
41
+ const output = response?.output ?? this.defaultOutput;
42
+ return {
43
+ content: output,
44
+ usage: { promptTokens: 100, completionTokens: 50, totalTokens: 150 },
45
+ model: "mock-model",
46
+ finishReason: "stop",
47
+ cost: 0
48
+ };
49
+ }
50
+ async *stream(prompt, options) {
51
+ const fullPrompt = prompt + " " + (options?.messages?.map(
52
+ (m) => typeof m.content === "string" ? m.content : JSON.stringify(m.content)
53
+ ).join(" ") ?? "");
54
+ const response = this.findResponse(fullPrompt);
55
+ this.calls.push({ prompt: fullPrompt, options, timestamp: Date.now(), response });
56
+ if (response?.latencyMs) {
57
+ await new Promise((resolve) => setTimeout(resolve, response.latencyMs));
58
+ }
59
+ if (response?.error) {
60
+ yield core.createStreamEvent("error", {
61
+ error: response.error,
62
+ message: response.error.message
63
+ });
64
+ return;
65
+ }
66
+ if (response?.toolCall) {
67
+ const callId = response.toolCall.id ?? `call_mock_${Date.now()}`;
68
+ yield core.createStreamEvent("tool_call", {
69
+ toolCallId: callId,
70
+ name: response.toolCall.name,
71
+ arguments: JSON.stringify(response.toolCall.args)
72
+ });
73
+ yield core.createStreamEvent("done", {
74
+ content: "",
75
+ finishReason: "tool_calls"
76
+ });
77
+ return;
78
+ }
79
+ const output = response?.output ?? this.defaultOutput;
80
+ const tokens = output.split(" ");
81
+ let index = 0;
82
+ for (const token of tokens) {
83
+ const tokenStr = index === 0 ? token : " " + token;
84
+ yield core.createStreamEvent("token", { token: tokenStr, index });
85
+ index++;
86
+ }
87
+ yield core.createStreamEvent("usage", {
88
+ usage: { promptTokens: 100, completionTokens: tokens.length, totalTokens: 100 + tokens.length }
89
+ });
90
+ yield core.createStreamEvent("done", {
91
+ content: output,
92
+ finishReason: "stop",
93
+ usage: { promptTokens: 100, completionTokens: tokens.length, totalTokens: 100 + tokens.length },
94
+ cost: 0
95
+ });
96
+ }
97
+ async streamGenerate(prompt, options) {
98
+ let content = "";
99
+ const startTime = Date.now();
100
+ let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
101
+ for await (const event of this.stream(prompt, options)) {
102
+ if (event.type === "token") content += event.token;
103
+ if (event.type === "done") {
104
+ content = event.content || content;
105
+ if (event.usage) usage = event.usage;
106
+ }
107
+ if (event.type === "error") throw event.error;
108
+ }
109
+ return {
110
+ content,
111
+ usage,
112
+ model: "mock-model",
113
+ finishReason: "stop",
114
+ durationMs: Date.now() - startTime
115
+ };
116
+ }
117
+ async embed(text) {
118
+ const inputs = Array.isArray(text) ? text : [text];
119
+ return inputs.map(() => Array.from({ length: 1536 }, (_, i) => Math.sin(i * 0.1)));
120
+ }
121
+ async generateObject(schema, prompt, options) {
122
+ const result = await this.generate(prompt, options);
123
+ try {
124
+ return schema.parse(JSON.parse(result.content));
125
+ } catch {
126
+ return schema.parse({});
127
+ }
128
+ }
129
+ // ---- Assertion helpers ----
130
+ getCalls() {
131
+ return [...this.calls];
132
+ }
133
+ getCallCount() {
134
+ return this.calls.length;
135
+ }
136
+ wasCalledWith(pattern) {
137
+ return this.calls.some(
138
+ (c) => typeof pattern === "string" ? c.prompt.includes(pattern) : pattern.test(c.prompt)
139
+ );
140
+ }
141
+ getLastCall() {
142
+ return this.calls[this.calls.length - 1];
143
+ }
144
+ reset() {
145
+ this.calls = [];
146
+ }
147
+ };
148
+ function mockLLM(responses = [], defaultOutput) {
149
+ return new MockLLMAdapter(responses, defaultOutput);
150
+ }
151
+
152
+ // src/test-bed.ts
153
+ var AgentTestBed = class {
154
+ agent;
155
+ llm;
156
+ constructor(config) {
157
+ this.agent = config.agent;
158
+ this.llm = config.llm ?? new MockLLMAdapter();
159
+ }
160
+ async run(input) {
161
+ this.llm.reset();
162
+ const events = [];
163
+ const toolCalls = [];
164
+ const streamAgent = this.agent;
165
+ let output = "";
166
+ let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
167
+ let steps = 0;
168
+ if (typeof streamAgent.runStream === "function") {
169
+ for await (const event of streamAgent.runStream(input)) {
170
+ events.push(event);
171
+ if (event.type === "tool_call") {
172
+ let args = {};
173
+ try {
174
+ args = JSON.parse(event.arguments);
175
+ } catch {
176
+ args = {};
177
+ }
178
+ toolCalls.push({ name: event.name, args, callId: event.toolCallId });
179
+ steps++;
180
+ }
181
+ if (event.type === "done") {
182
+ output = event.content;
183
+ if (event.usage) usage = event.usage;
184
+ }
185
+ }
186
+ } else {
187
+ const result2 = await this.agent.run(input);
188
+ output = result2.output;
189
+ steps = result2.steps.length;
190
+ usage = { promptTokens: 0, completionTokens: 0, totalTokens: result2.totalTokens };
191
+ for (const step of result2.steps) {
192
+ toolCalls.push({ name: step.action, args: step.actionInput, callId: "" });
193
+ }
194
+ }
195
+ const result = { output, toolCalls, steps, usage, events };
196
+ return Object.assign(result, makeAssertions(result));
197
+ }
198
+ getLLMMock() {
199
+ return this.llm;
200
+ }
201
+ reset() {
202
+ this.llm.reset();
203
+ }
204
+ };
205
+ function makeAssertions(result) {
206
+ return {
207
+ toHaveCalledTool(name) {
208
+ const called = result.toolCalls.some((tc) => tc.name === name);
209
+ if (!called) {
210
+ const calledTools = result.toolCalls.map((tc) => tc.name).join(", ") || "none";
211
+ throw new Error(
212
+ "Expected agent to have called tool " + JSON.stringify(name) + ", but it called: " + calledTools
213
+ );
214
+ }
215
+ return result;
216
+ },
217
+ toHaveOutput(pattern) {
218
+ const matches = typeof pattern === "string" ? result.output.includes(pattern) : pattern.test(result.output);
219
+ if (!matches) {
220
+ throw new Error(
221
+ "Expected output to match " + String(pattern) + ", got: " + JSON.stringify(result.output.slice(0, 200))
222
+ );
223
+ }
224
+ return result;
225
+ },
226
+ not: {
227
+ toHaveCalledTool(name) {
228
+ const called = result.toolCalls.some((tc) => tc.name === name);
229
+ if (called) throw new Error("Expected agent NOT to have called tool " + JSON.stringify(name) + ", but it did");
230
+ return result;
231
+ }
232
+ }
233
+ };
234
+ }
235
+
236
+ // src/matchers.ts
237
+ function extendExpect(expect) {
238
+ expect.extend({
239
+ toHaveCalledTool(received, toolName) {
240
+ const called = received.toolCalls?.some((tc) => tc.name === toolName);
241
+ return {
242
+ pass: called,
243
+ message: () => called ? "Expected agent NOT to have called tool " + JSON.stringify(toolName) : "Expected agent to have called tool " + JSON.stringify(toolName) + ". Called tools: [" + (received.toolCalls?.map((t) => t.name).join(", ") || "none") + "]"
244
+ };
245
+ },
246
+ toHaveOutput(received, pattern) {
247
+ const matches = typeof pattern === "string" ? received.output?.includes(pattern) : pattern.test(received.output ?? "");
248
+ return {
249
+ pass: matches,
250
+ message: () => matches ? "Expected output NOT to match " + String(pattern) : "Expected output to match " + String(pattern) + ". Got: " + JSON.stringify(received.output?.slice(0, 200))
251
+ };
252
+ },
253
+ toHaveBeenCalledWithPrompt(received, pattern) {
254
+ const called = received.wasCalledWith(pattern);
255
+ return {
256
+ pass: called,
257
+ message: () => called ? "Expected MockLLM NOT to have been called with " + String(pattern) : "Expected MockLLM to have been called with " + String(pattern)
258
+ };
259
+ },
260
+ toHaveCallCount(received, count) {
261
+ const actual = received.getCallCount();
262
+ return {
263
+ pass: actual === count,
264
+ message: () => "Expected MockLLM call count to be " + count + ", got " + actual
265
+ };
266
+ }
267
+ });
268
+ }
269
+
270
+ exports.AgentTestBed = AgentTestBed;
271
+ exports.MockLLMAdapter = MockLLMAdapter;
272
+ exports.extendExpect = extendExpect;
273
+ exports.mockLLM = mockLLM;
274
+ //# sourceMappingURL=index.cjs.map
275
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mock-llm.ts","../src/test-bed.ts","../src/matchers.ts"],"names":["createStreamEvent","result"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n LLMAdapter,\n LLMGenerateOptions,\n LLMResult,\n StreamingLLMAdapter,\n StreamGenerateOptions,\n StreamResult,\n LLMStreamEvent,\n} from '@orka-js/core';\nimport { createStreamEvent } from '@orka-js/core';\nimport type { MockResponse, MockCall } from './types.js';\n\nexport class MockLLMAdapter implements LLMAdapter, StreamingLLMAdapter {\n readonly name = 'mock';\n readonly supportsStreaming = true;\n private responses: MockResponse[];\n private calls: MockCall[] = [];\n private defaultOutput: string;\n\n constructor(responses: MockResponse[] = [], defaultOutput = 'Mock response') {\n this.responses = responses;\n this.defaultOutput = defaultOutput;\n }\n\n private findResponse(prompt: string): MockResponse | null {\n for (const response of this.responses) {\n if (!response.when) return response;\n if (typeof response.when === 'string') {\n if (prompt.includes(response.when)) return response;\n } else if (response.when instanceof RegExp) {\n if (response.when.test(prompt)) return response;\n } else if (typeof response.when === 'function') {\n if (response.when(prompt)) return response;\n }\n }\n return null;\n }\n\n private getPromptText(prompt: string, options?: LLMGenerateOptions): string {\n // Also search in messages\n const messagesText = options?.messages\n ?.map(m => (typeof m.content === 'string' ? m.content : JSON.stringify(m.content)))\n .join(' ') ?? '';\n return prompt + ' ' + messagesText;\n }\n\n async generate(prompt: string, options?: LLMGenerateOptions): Promise<LLMResult> {\n const fullPrompt = this.getPromptText(prompt, options);\n const response = this.findResponse(fullPrompt);\n\n this.calls.push({ prompt: fullPrompt, options, timestamp: Date.now(), response });\n\n if (response?.latencyMs) {\n await new Promise(resolve => setTimeout(resolve, response.latencyMs));\n }\n\n if (response?.error) throw response.error;\n\n const output = response?.output ?? this.defaultOutput;\n\n return {\n content: output,\n usage: { promptTokens: 100, completionTokens: 50, totalTokens: 150 },\n model: 'mock-model',\n finishReason: 'stop',\n cost: 0,\n };\n }\n\n async *stream(prompt: string, options?: StreamGenerateOptions): AsyncIterable<LLMStreamEvent> {\n const fullPrompt = prompt + ' ' + (options?.messages?.map(m =>\n typeof m.content === 'string' ? m.content : JSON.stringify(m.content)\n ).join(' ') ?? '');\n\n const response = this.findResponse(fullPrompt);\n this.calls.push({ prompt: fullPrompt, options, timestamp: Date.now(), response });\n\n if (response?.latencyMs) {\n await new Promise(resolve => setTimeout(resolve, response.latencyMs));\n }\n\n if (response?.error) {\n yield createStreamEvent('error', {\n error: response.error,\n message: response.error.message,\n } as never);\n return;\n }\n\n // Simulate tool call if configured\n if (response?.toolCall) {\n const callId = response.toolCall.id ?? `call_mock_${Date.now()}`;\n yield createStreamEvent('tool_call', {\n toolCallId: callId,\n name: response.toolCall.name,\n arguments: JSON.stringify(response.toolCall.args),\n } as never);\n yield createStreamEvent('done', {\n content: '',\n finishReason: 'tool_calls',\n } as never);\n return;\n }\n\n // Stream tokens one by one\n const output = response?.output ?? this.defaultOutput;\n const tokens = output.split(' ');\n let index = 0;\n\n for (const token of tokens) {\n const tokenStr = index === 0 ? token : ' ' + token;\n yield createStreamEvent('token', { token: tokenStr, index } as never);\n index++;\n }\n\n yield createStreamEvent('usage', {\n usage: { promptTokens: 100, completionTokens: tokens.length, totalTokens: 100 + tokens.length },\n } as never);\n\n yield createStreamEvent('done', {\n content: output,\n finishReason: 'stop',\n usage: { promptTokens: 100, completionTokens: tokens.length, totalTokens: 100 + tokens.length },\n cost: 0,\n } as never);\n }\n\n async streamGenerate(prompt: string, options?: StreamGenerateOptions): Promise<StreamResult> {\n let content = '';\n const startTime = Date.now();\n let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };\n\n for await (const event of this.stream(prompt, options)) {\n if (event.type === 'token') content += event.token;\n if (event.type === 'done') {\n content = event.content || content;\n if (event.usage) usage = event.usage;\n }\n if (event.type === 'error') throw event.error;\n }\n\n return {\n content,\n usage,\n model: 'mock-model',\n finishReason: 'stop',\n durationMs: Date.now() - startTime,\n };\n }\n\n async embed(text: string | string[]): Promise<number[][]> {\n const inputs = Array.isArray(text) ? text : [text];\n // Return deterministic mock embeddings\n return inputs.map(() => Array.from({ length: 1536 }, (_, i) => Math.sin(i * 0.1)));\n }\n\n async generateObject<T>(schema: { parse(d: unknown): T }, prompt: string, options?: LLMGenerateOptions): Promise<T> {\n const result = await this.generate(prompt, options);\n try {\n return schema.parse(JSON.parse(result.content));\n } catch {\n return schema.parse({});\n }\n }\n\n // ---- Assertion helpers ----\n\n getCalls(): MockCall[] {\n return [...this.calls];\n }\n\n getCallCount(): number {\n return this.calls.length;\n }\n\n wasCalledWith(pattern: string | RegExp): boolean {\n return this.calls.some(c =>\n typeof pattern === 'string' ? c.prompt.includes(pattern) : pattern.test(c.prompt)\n );\n }\n\n getLastCall(): MockCall | undefined {\n return this.calls[this.calls.length - 1];\n }\n\n reset(): void {\n this.calls = [];\n }\n}\n\nexport function mockLLM(responses: MockResponse[] = [], defaultOutput?: string): MockLLMAdapter {\n return new MockLLMAdapter(responses, defaultOutput);\n}\n","import type { LLMStreamEvent } from '@orka-js/core';\nimport type { BaseAgent, AgentResult } from '@orka-js/agent';\nimport type { AgentTestResult } from './types.js';\nimport { MockLLMAdapter } from './mock-llm.js';\n\nexport interface AgentTestBedConfig {\n agent: BaseAgent;\n llm?: MockLLMAdapter;\n}\n\nexport class AgentTestBed {\n private agent: BaseAgent;\n private llm: MockLLMAdapter;\n\n constructor(config: AgentTestBedConfig) {\n this.agent = config.agent;\n this.llm = config.llm ?? new MockLLMAdapter();\n }\n\n async run(input: string): Promise<AgentTestResult & AgentAssertions> {\n this.llm.reset();\n const events: LLMStreamEvent[] = [];\n const toolCalls: AgentTestResult['toolCalls'] = [];\n\n // Check if agent has runStream (StreamingToolAgent)\n const streamAgent = this.agent as BaseAgent & { runStream?: (input: string) => AsyncIterable<LLMStreamEvent> };\n\n let output = '';\n let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };\n let steps = 0;\n\n if (typeof streamAgent.runStream === 'function') {\n for await (const event of streamAgent.runStream(input)) {\n events.push(event);\n if (event.type === 'tool_call') {\n let args: Record<string, unknown> = {};\n try { args = JSON.parse(event.arguments); } catch { args = {}; }\n toolCalls.push({ name: event.name, args, callId: event.toolCallId });\n steps++;\n }\n if (event.type === 'done') {\n output = event.content;\n if (event.usage) usage = event.usage;\n }\n }\n } else {\n const result: AgentResult = await this.agent.run(input);\n output = result.output;\n steps = result.steps.length;\n usage = { promptTokens: 0, completionTokens: 0, totalTokens: result.totalTokens };\n for (const step of result.steps) {\n toolCalls.push({ name: step.action, args: step.actionInput, callId: '' });\n }\n }\n\n const result: AgentTestResult = { output, toolCalls, steps, usage, events };\n return Object.assign(result, makeAssertions(result));\n }\n\n getLLMMock(): MockLLMAdapter {\n return this.llm;\n }\n\n reset(): void {\n this.llm.reset();\n }\n}\n\nfunction makeAssertions(result: AgentTestResult): AgentAssertions {\n return {\n toHaveCalledTool(name: string) {\n const called = result.toolCalls.some(tc => tc.name === name);\n if (!called) {\n const calledTools = result.toolCalls.map(tc => tc.name).join(', ') || 'none';\n throw new Error(\n 'Expected agent to have called tool ' + JSON.stringify(name) + ', but it called: ' + calledTools\n );\n }\n return result as AgentTestResult & AgentAssertions;\n },\n toHaveOutput(pattern: string | RegExp) {\n const matches = typeof pattern === 'string'\n ? result.output.includes(pattern)\n : pattern.test(result.output);\n if (!matches) {\n throw new Error(\n 'Expected output to match ' + String(pattern) + ', got: ' + JSON.stringify(result.output.slice(0, 200))\n );\n }\n return result as AgentTestResult & AgentAssertions;\n },\n not: {\n toHaveCalledTool(name: string) {\n const called = result.toolCalls.some(tc => tc.name === name);\n if (called) throw new Error('Expected agent NOT to have called tool ' + JSON.stringify(name) + ', but it did');\n return result as AgentTestResult & AgentAssertions;\n },\n },\n };\n}\n\nexport interface AgentAssertions {\n toHaveCalledTool(name: string): AgentTestResult & AgentAssertions;\n toHaveOutput(pattern: string | RegExp): AgentTestResult & AgentAssertions;\n not: {\n toHaveCalledTool(name: string): AgentTestResult & AgentAssertions;\n };\n}","import type { AgentTestResult } from './types.js';\nimport type { MockLLMAdapter } from './mock-llm.js';\n\n/**\n * Extend Vitest/Jest expect with OrkaJS agent matchers.\n * \n * @example\n * ```typescript\n * import { expect } from 'vitest'\n * import { extendExpect } from '@orka-js/test'\n * extendExpect(expect)\n * \n * const result = await bed.run('hello')\n * expect(result).toHaveCalledTool('search')\n * expect(result).toHaveOutput(/hello/)\n * ```\n */\nexport function extendExpect(expect: {\n extend(matchers: Record<string, unknown>): void;\n}): void {\n expect.extend({\n toHaveCalledTool(received: AgentTestResult, toolName: string) {\n const called = received.toolCalls?.some(tc => tc.name === toolName);\n return {\n pass: called,\n message: () => called\n ? 'Expected agent NOT to have called tool ' + JSON.stringify(toolName)\n : 'Expected agent to have called tool ' + JSON.stringify(toolName) + '. Called tools: [' + (received.toolCalls?.map(t => t.name).join(', ') || 'none') + ']',\n };\n },\n\n toHaveOutput(received: AgentTestResult, pattern: string | RegExp) {\n const matches = typeof pattern === 'string'\n ? received.output?.includes(pattern)\n : pattern.test(received.output ?? '');\n return {\n pass: matches,\n message: () => matches\n ? 'Expected output NOT to match ' + String(pattern)\n : 'Expected output to match ' + String(pattern) + '. Got: ' + JSON.stringify(received.output?.slice(0, 200)),\n };\n },\n\n toHaveBeenCalledWithPrompt(received: MockLLMAdapter, pattern: string | RegExp) {\n const called = received.wasCalledWith(pattern);\n return {\n pass: called,\n message: () => called\n ? 'Expected MockLLM NOT to have been called with ' + String(pattern)\n : 'Expected MockLLM to have been called with ' + String(pattern),\n };\n },\n\n toHaveCallCount(received: MockLLMAdapter, count: number) {\n const actual = received.getCallCount();\n return {\n pass: actual === count,\n message: () => 'Expected MockLLM call count to be ' + count + ', got ' + actual,\n };\n },\n });\n}"]}
@@ -0,0 +1,108 @@
1
+ import { LLMStreamEvent, LLMAdapter, StreamingLLMAdapter, LLMGenerateOptions, LLMResult, StreamGenerateOptions, StreamResult } from '@orka-js/core';
2
+ import { BaseAgent } from '@orka-js/agent';
3
+
4
+ interface MockResponse {
5
+ /** Match condition — string (exact/contains), RegExp, or function */
6
+ when?: string | RegExp | ((prompt: string) => boolean);
7
+ /** Text output to return */
8
+ output?: string;
9
+ /** Tool call to simulate */
10
+ toolCall?: {
11
+ name: string;
12
+ args: Record<string, unknown>;
13
+ id?: string;
14
+ };
15
+ /** Error to throw */
16
+ error?: Error;
17
+ /** Simulate latency in ms */
18
+ latencyMs?: number;
19
+ }
20
+ interface MockCall {
21
+ prompt: string;
22
+ options?: unknown;
23
+ timestamp: number;
24
+ response: MockResponse | null;
25
+ }
26
+ interface AgentTestResult {
27
+ output: string;
28
+ toolCalls: Array<{
29
+ name: string;
30
+ args: Record<string, unknown>;
31
+ callId: string;
32
+ }>;
33
+ steps: number;
34
+ usage: {
35
+ promptTokens: number;
36
+ completionTokens: number;
37
+ totalTokens: number;
38
+ };
39
+ events: LLMStreamEvent[];
40
+ }
41
+ interface AgentSnapshot {
42
+ output: string;
43
+ toolCalls: string[];
44
+ }
45
+
46
+ declare class MockLLMAdapter implements LLMAdapter, StreamingLLMAdapter {
47
+ readonly name = "mock";
48
+ readonly supportsStreaming = true;
49
+ private responses;
50
+ private calls;
51
+ private defaultOutput;
52
+ constructor(responses?: MockResponse[], defaultOutput?: string);
53
+ private findResponse;
54
+ private getPromptText;
55
+ generate(prompt: string, options?: LLMGenerateOptions): Promise<LLMResult>;
56
+ stream(prompt: string, options?: StreamGenerateOptions): AsyncIterable<LLMStreamEvent>;
57
+ streamGenerate(prompt: string, options?: StreamGenerateOptions): Promise<StreamResult>;
58
+ embed(text: string | string[]): Promise<number[][]>;
59
+ generateObject<T>(schema: {
60
+ parse(d: unknown): T;
61
+ }, prompt: string, options?: LLMGenerateOptions): Promise<T>;
62
+ getCalls(): MockCall[];
63
+ getCallCount(): number;
64
+ wasCalledWith(pattern: string | RegExp): boolean;
65
+ getLastCall(): MockCall | undefined;
66
+ reset(): void;
67
+ }
68
+ declare function mockLLM(responses?: MockResponse[], defaultOutput?: string): MockLLMAdapter;
69
+
70
+ interface AgentTestBedConfig {
71
+ agent: BaseAgent;
72
+ llm?: MockLLMAdapter;
73
+ }
74
+ declare class AgentTestBed {
75
+ private agent;
76
+ private llm;
77
+ constructor(config: AgentTestBedConfig);
78
+ run(input: string): Promise<AgentTestResult & AgentAssertions>;
79
+ getLLMMock(): MockLLMAdapter;
80
+ reset(): void;
81
+ }
82
+ interface AgentAssertions {
83
+ toHaveCalledTool(name: string): AgentTestResult & AgentAssertions;
84
+ toHaveOutput(pattern: string | RegExp): AgentTestResult & AgentAssertions;
85
+ not: {
86
+ toHaveCalledTool(name: string): AgentTestResult & AgentAssertions;
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Extend Vitest/Jest expect with OrkaJS agent matchers.
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * import { expect } from 'vitest'
96
+ * import { extendExpect } from '@orka-js/test'
97
+ * extendExpect(expect)
98
+ *
99
+ * const result = await bed.run('hello')
100
+ * expect(result).toHaveCalledTool('search')
101
+ * expect(result).toHaveOutput(/hello/)
102
+ * ```
103
+ */
104
+ declare function extendExpect(expect: {
105
+ extend(matchers: Record<string, unknown>): void;
106
+ }): void;
107
+
108
+ export { type AgentAssertions, type AgentSnapshot, AgentTestBed, type AgentTestResult, type MockCall, MockLLMAdapter, type MockResponse, extendExpect, mockLLM };
@@ -0,0 +1,108 @@
1
+ import { LLMStreamEvent, LLMAdapter, StreamingLLMAdapter, LLMGenerateOptions, LLMResult, StreamGenerateOptions, StreamResult } from '@orka-js/core';
2
+ import { BaseAgent } from '@orka-js/agent';
3
+
4
+ interface MockResponse {
5
+ /** Match condition — string (exact/contains), RegExp, or function */
6
+ when?: string | RegExp | ((prompt: string) => boolean);
7
+ /** Text output to return */
8
+ output?: string;
9
+ /** Tool call to simulate */
10
+ toolCall?: {
11
+ name: string;
12
+ args: Record<string, unknown>;
13
+ id?: string;
14
+ };
15
+ /** Error to throw */
16
+ error?: Error;
17
+ /** Simulate latency in ms */
18
+ latencyMs?: number;
19
+ }
20
+ interface MockCall {
21
+ prompt: string;
22
+ options?: unknown;
23
+ timestamp: number;
24
+ response: MockResponse | null;
25
+ }
26
+ interface AgentTestResult {
27
+ output: string;
28
+ toolCalls: Array<{
29
+ name: string;
30
+ args: Record<string, unknown>;
31
+ callId: string;
32
+ }>;
33
+ steps: number;
34
+ usage: {
35
+ promptTokens: number;
36
+ completionTokens: number;
37
+ totalTokens: number;
38
+ };
39
+ events: LLMStreamEvent[];
40
+ }
41
+ interface AgentSnapshot {
42
+ output: string;
43
+ toolCalls: string[];
44
+ }
45
+
46
+ declare class MockLLMAdapter implements LLMAdapter, StreamingLLMAdapter {
47
+ readonly name = "mock";
48
+ readonly supportsStreaming = true;
49
+ private responses;
50
+ private calls;
51
+ private defaultOutput;
52
+ constructor(responses?: MockResponse[], defaultOutput?: string);
53
+ private findResponse;
54
+ private getPromptText;
55
+ generate(prompt: string, options?: LLMGenerateOptions): Promise<LLMResult>;
56
+ stream(prompt: string, options?: StreamGenerateOptions): AsyncIterable<LLMStreamEvent>;
57
+ streamGenerate(prompt: string, options?: StreamGenerateOptions): Promise<StreamResult>;
58
+ embed(text: string | string[]): Promise<number[][]>;
59
+ generateObject<T>(schema: {
60
+ parse(d: unknown): T;
61
+ }, prompt: string, options?: LLMGenerateOptions): Promise<T>;
62
+ getCalls(): MockCall[];
63
+ getCallCount(): number;
64
+ wasCalledWith(pattern: string | RegExp): boolean;
65
+ getLastCall(): MockCall | undefined;
66
+ reset(): void;
67
+ }
68
+ declare function mockLLM(responses?: MockResponse[], defaultOutput?: string): MockLLMAdapter;
69
+
70
+ interface AgentTestBedConfig {
71
+ agent: BaseAgent;
72
+ llm?: MockLLMAdapter;
73
+ }
74
+ declare class AgentTestBed {
75
+ private agent;
76
+ private llm;
77
+ constructor(config: AgentTestBedConfig);
78
+ run(input: string): Promise<AgentTestResult & AgentAssertions>;
79
+ getLLMMock(): MockLLMAdapter;
80
+ reset(): void;
81
+ }
82
+ interface AgentAssertions {
83
+ toHaveCalledTool(name: string): AgentTestResult & AgentAssertions;
84
+ toHaveOutput(pattern: string | RegExp): AgentTestResult & AgentAssertions;
85
+ not: {
86
+ toHaveCalledTool(name: string): AgentTestResult & AgentAssertions;
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Extend Vitest/Jest expect with OrkaJS agent matchers.
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * import { expect } from 'vitest'
96
+ * import { extendExpect } from '@orka-js/test'
97
+ * extendExpect(expect)
98
+ *
99
+ * const result = await bed.run('hello')
100
+ * expect(result).toHaveCalledTool('search')
101
+ * expect(result).toHaveOutput(/hello/)
102
+ * ```
103
+ */
104
+ declare function extendExpect(expect: {
105
+ extend(matchers: Record<string, unknown>): void;
106
+ }): void;
107
+
108
+ export { type AgentAssertions, type AgentSnapshot, AgentTestBed, type AgentTestResult, type MockCall, MockLLMAdapter, type MockResponse, extendExpect, mockLLM };
package/dist/index.js ADDED
@@ -0,0 +1,270 @@
1
+ import { createStreamEvent } from '@orka-js/core';
2
+
3
+ // src/mock-llm.ts
4
+ var MockLLMAdapter = class {
5
+ name = "mock";
6
+ supportsStreaming = true;
7
+ responses;
8
+ calls = [];
9
+ defaultOutput;
10
+ constructor(responses = [], defaultOutput = "Mock response") {
11
+ this.responses = responses;
12
+ this.defaultOutput = defaultOutput;
13
+ }
14
+ findResponse(prompt) {
15
+ for (const response of this.responses) {
16
+ if (!response.when) return response;
17
+ if (typeof response.when === "string") {
18
+ if (prompt.includes(response.when)) return response;
19
+ } else if (response.when instanceof RegExp) {
20
+ if (response.when.test(prompt)) return response;
21
+ } else if (typeof response.when === "function") {
22
+ if (response.when(prompt)) return response;
23
+ }
24
+ }
25
+ return null;
26
+ }
27
+ getPromptText(prompt, options) {
28
+ const messagesText = options?.messages?.map((m) => typeof m.content === "string" ? m.content : JSON.stringify(m.content)).join(" ") ?? "";
29
+ return prompt + " " + messagesText;
30
+ }
31
+ async generate(prompt, options) {
32
+ const fullPrompt = this.getPromptText(prompt, options);
33
+ const response = this.findResponse(fullPrompt);
34
+ this.calls.push({ prompt: fullPrompt, options, timestamp: Date.now(), response });
35
+ if (response?.latencyMs) {
36
+ await new Promise((resolve) => setTimeout(resolve, response.latencyMs));
37
+ }
38
+ if (response?.error) throw response.error;
39
+ const output = response?.output ?? this.defaultOutput;
40
+ return {
41
+ content: output,
42
+ usage: { promptTokens: 100, completionTokens: 50, totalTokens: 150 },
43
+ model: "mock-model",
44
+ finishReason: "stop",
45
+ cost: 0
46
+ };
47
+ }
48
+ async *stream(prompt, options) {
49
+ const fullPrompt = prompt + " " + (options?.messages?.map(
50
+ (m) => typeof m.content === "string" ? m.content : JSON.stringify(m.content)
51
+ ).join(" ") ?? "");
52
+ const response = this.findResponse(fullPrompt);
53
+ this.calls.push({ prompt: fullPrompt, options, timestamp: Date.now(), response });
54
+ if (response?.latencyMs) {
55
+ await new Promise((resolve) => setTimeout(resolve, response.latencyMs));
56
+ }
57
+ if (response?.error) {
58
+ yield createStreamEvent("error", {
59
+ error: response.error,
60
+ message: response.error.message
61
+ });
62
+ return;
63
+ }
64
+ if (response?.toolCall) {
65
+ const callId = response.toolCall.id ?? `call_mock_${Date.now()}`;
66
+ yield createStreamEvent("tool_call", {
67
+ toolCallId: callId,
68
+ name: response.toolCall.name,
69
+ arguments: JSON.stringify(response.toolCall.args)
70
+ });
71
+ yield createStreamEvent("done", {
72
+ content: "",
73
+ finishReason: "tool_calls"
74
+ });
75
+ return;
76
+ }
77
+ const output = response?.output ?? this.defaultOutput;
78
+ const tokens = output.split(" ");
79
+ let index = 0;
80
+ for (const token of tokens) {
81
+ const tokenStr = index === 0 ? token : " " + token;
82
+ yield createStreamEvent("token", { token: tokenStr, index });
83
+ index++;
84
+ }
85
+ yield createStreamEvent("usage", {
86
+ usage: { promptTokens: 100, completionTokens: tokens.length, totalTokens: 100 + tokens.length }
87
+ });
88
+ yield createStreamEvent("done", {
89
+ content: output,
90
+ finishReason: "stop",
91
+ usage: { promptTokens: 100, completionTokens: tokens.length, totalTokens: 100 + tokens.length },
92
+ cost: 0
93
+ });
94
+ }
95
+ async streamGenerate(prompt, options) {
96
+ let content = "";
97
+ const startTime = Date.now();
98
+ let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
99
+ for await (const event of this.stream(prompt, options)) {
100
+ if (event.type === "token") content += event.token;
101
+ if (event.type === "done") {
102
+ content = event.content || content;
103
+ if (event.usage) usage = event.usage;
104
+ }
105
+ if (event.type === "error") throw event.error;
106
+ }
107
+ return {
108
+ content,
109
+ usage,
110
+ model: "mock-model",
111
+ finishReason: "stop",
112
+ durationMs: Date.now() - startTime
113
+ };
114
+ }
115
+ async embed(text) {
116
+ const inputs = Array.isArray(text) ? text : [text];
117
+ return inputs.map(() => Array.from({ length: 1536 }, (_, i) => Math.sin(i * 0.1)));
118
+ }
119
+ async generateObject(schema, prompt, options) {
120
+ const result = await this.generate(prompt, options);
121
+ try {
122
+ return schema.parse(JSON.parse(result.content));
123
+ } catch {
124
+ return schema.parse({});
125
+ }
126
+ }
127
+ // ---- Assertion helpers ----
128
+ getCalls() {
129
+ return [...this.calls];
130
+ }
131
+ getCallCount() {
132
+ return this.calls.length;
133
+ }
134
+ wasCalledWith(pattern) {
135
+ return this.calls.some(
136
+ (c) => typeof pattern === "string" ? c.prompt.includes(pattern) : pattern.test(c.prompt)
137
+ );
138
+ }
139
+ getLastCall() {
140
+ return this.calls[this.calls.length - 1];
141
+ }
142
+ reset() {
143
+ this.calls = [];
144
+ }
145
+ };
146
+ function mockLLM(responses = [], defaultOutput) {
147
+ return new MockLLMAdapter(responses, defaultOutput);
148
+ }
149
+
150
+ // src/test-bed.ts
151
+ var AgentTestBed = class {
152
+ agent;
153
+ llm;
154
+ constructor(config) {
155
+ this.agent = config.agent;
156
+ this.llm = config.llm ?? new MockLLMAdapter();
157
+ }
158
+ async run(input) {
159
+ this.llm.reset();
160
+ const events = [];
161
+ const toolCalls = [];
162
+ const streamAgent = this.agent;
163
+ let output = "";
164
+ let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
165
+ let steps = 0;
166
+ if (typeof streamAgent.runStream === "function") {
167
+ for await (const event of streamAgent.runStream(input)) {
168
+ events.push(event);
169
+ if (event.type === "tool_call") {
170
+ let args = {};
171
+ try {
172
+ args = JSON.parse(event.arguments);
173
+ } catch {
174
+ args = {};
175
+ }
176
+ toolCalls.push({ name: event.name, args, callId: event.toolCallId });
177
+ steps++;
178
+ }
179
+ if (event.type === "done") {
180
+ output = event.content;
181
+ if (event.usage) usage = event.usage;
182
+ }
183
+ }
184
+ } else {
185
+ const result2 = await this.agent.run(input);
186
+ output = result2.output;
187
+ steps = result2.steps.length;
188
+ usage = { promptTokens: 0, completionTokens: 0, totalTokens: result2.totalTokens };
189
+ for (const step of result2.steps) {
190
+ toolCalls.push({ name: step.action, args: step.actionInput, callId: "" });
191
+ }
192
+ }
193
+ const result = { output, toolCalls, steps, usage, events };
194
+ return Object.assign(result, makeAssertions(result));
195
+ }
196
+ getLLMMock() {
197
+ return this.llm;
198
+ }
199
+ reset() {
200
+ this.llm.reset();
201
+ }
202
+ };
203
+ function makeAssertions(result) {
204
+ return {
205
+ toHaveCalledTool(name) {
206
+ const called = result.toolCalls.some((tc) => tc.name === name);
207
+ if (!called) {
208
+ const calledTools = result.toolCalls.map((tc) => tc.name).join(", ") || "none";
209
+ throw new Error(
210
+ "Expected agent to have called tool " + JSON.stringify(name) + ", but it called: " + calledTools
211
+ );
212
+ }
213
+ return result;
214
+ },
215
+ toHaveOutput(pattern) {
216
+ const matches = typeof pattern === "string" ? result.output.includes(pattern) : pattern.test(result.output);
217
+ if (!matches) {
218
+ throw new Error(
219
+ "Expected output to match " + String(pattern) + ", got: " + JSON.stringify(result.output.slice(0, 200))
220
+ );
221
+ }
222
+ return result;
223
+ },
224
+ not: {
225
+ toHaveCalledTool(name) {
226
+ const called = result.toolCalls.some((tc) => tc.name === name);
227
+ if (called) throw new Error("Expected agent NOT to have called tool " + JSON.stringify(name) + ", but it did");
228
+ return result;
229
+ }
230
+ }
231
+ };
232
+ }
233
+
234
+ // src/matchers.ts
235
+ function extendExpect(expect) {
236
+ expect.extend({
237
+ toHaveCalledTool(received, toolName) {
238
+ const called = received.toolCalls?.some((tc) => tc.name === toolName);
239
+ return {
240
+ pass: called,
241
+ message: () => called ? "Expected agent NOT to have called tool " + JSON.stringify(toolName) : "Expected agent to have called tool " + JSON.stringify(toolName) + ". Called tools: [" + (received.toolCalls?.map((t) => t.name).join(", ") || "none") + "]"
242
+ };
243
+ },
244
+ toHaveOutput(received, pattern) {
245
+ const matches = typeof pattern === "string" ? received.output?.includes(pattern) : pattern.test(received.output ?? "");
246
+ return {
247
+ pass: matches,
248
+ message: () => matches ? "Expected output NOT to match " + String(pattern) : "Expected output to match " + String(pattern) + ". Got: " + JSON.stringify(received.output?.slice(0, 200))
249
+ };
250
+ },
251
+ toHaveBeenCalledWithPrompt(received, pattern) {
252
+ const called = received.wasCalledWith(pattern);
253
+ return {
254
+ pass: called,
255
+ message: () => called ? "Expected MockLLM NOT to have been called with " + String(pattern) : "Expected MockLLM to have been called with " + String(pattern)
256
+ };
257
+ },
258
+ toHaveCallCount(received, count) {
259
+ const actual = received.getCallCount();
260
+ return {
261
+ pass: actual === count,
262
+ message: () => "Expected MockLLM call count to be " + count + ", got " + actual
263
+ };
264
+ }
265
+ });
266
+ }
267
+
268
+ export { AgentTestBed, MockLLMAdapter, extendExpect, mockLLM };
269
+ //# sourceMappingURL=index.js.map
270
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mock-llm.ts","../src/test-bed.ts","../src/matchers.ts"],"names":["result"],"mappings":"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","file":"index.js","sourcesContent":["import type {\n LLMAdapter,\n LLMGenerateOptions,\n LLMResult,\n StreamingLLMAdapter,\n StreamGenerateOptions,\n StreamResult,\n LLMStreamEvent,\n} from '@orka-js/core';\nimport { createStreamEvent } from '@orka-js/core';\nimport type { MockResponse, MockCall } from './types.js';\n\nexport class MockLLMAdapter implements LLMAdapter, StreamingLLMAdapter {\n readonly name = 'mock';\n readonly supportsStreaming = true;\n private responses: MockResponse[];\n private calls: MockCall[] = [];\n private defaultOutput: string;\n\n constructor(responses: MockResponse[] = [], defaultOutput = 'Mock response') {\n this.responses = responses;\n this.defaultOutput = defaultOutput;\n }\n\n private findResponse(prompt: string): MockResponse | null {\n for (const response of this.responses) {\n if (!response.when) return response;\n if (typeof response.when === 'string') {\n if (prompt.includes(response.when)) return response;\n } else if (response.when instanceof RegExp) {\n if (response.when.test(prompt)) return response;\n } else if (typeof response.when === 'function') {\n if (response.when(prompt)) return response;\n }\n }\n return null;\n }\n\n private getPromptText(prompt: string, options?: LLMGenerateOptions): string {\n // Also search in messages\n const messagesText = options?.messages\n ?.map(m => (typeof m.content === 'string' ? m.content : JSON.stringify(m.content)))\n .join(' ') ?? '';\n return prompt + ' ' + messagesText;\n }\n\n async generate(prompt: string, options?: LLMGenerateOptions): Promise<LLMResult> {\n const fullPrompt = this.getPromptText(prompt, options);\n const response = this.findResponse(fullPrompt);\n\n this.calls.push({ prompt: fullPrompt, options, timestamp: Date.now(), response });\n\n if (response?.latencyMs) {\n await new Promise(resolve => setTimeout(resolve, response.latencyMs));\n }\n\n if (response?.error) throw response.error;\n\n const output = response?.output ?? this.defaultOutput;\n\n return {\n content: output,\n usage: { promptTokens: 100, completionTokens: 50, totalTokens: 150 },\n model: 'mock-model',\n finishReason: 'stop',\n cost: 0,\n };\n }\n\n async *stream(prompt: string, options?: StreamGenerateOptions): AsyncIterable<LLMStreamEvent> {\n const fullPrompt = prompt + ' ' + (options?.messages?.map(m =>\n typeof m.content === 'string' ? m.content : JSON.stringify(m.content)\n ).join(' ') ?? '');\n\n const response = this.findResponse(fullPrompt);\n this.calls.push({ prompt: fullPrompt, options, timestamp: Date.now(), response });\n\n if (response?.latencyMs) {\n await new Promise(resolve => setTimeout(resolve, response.latencyMs));\n }\n\n if (response?.error) {\n yield createStreamEvent('error', {\n error: response.error,\n message: response.error.message,\n } as never);\n return;\n }\n\n // Simulate tool call if configured\n if (response?.toolCall) {\n const callId = response.toolCall.id ?? `call_mock_${Date.now()}`;\n yield createStreamEvent('tool_call', {\n toolCallId: callId,\n name: response.toolCall.name,\n arguments: JSON.stringify(response.toolCall.args),\n } as never);\n yield createStreamEvent('done', {\n content: '',\n finishReason: 'tool_calls',\n } as never);\n return;\n }\n\n // Stream tokens one by one\n const output = response?.output ?? this.defaultOutput;\n const tokens = output.split(' ');\n let index = 0;\n\n for (const token of tokens) {\n const tokenStr = index === 0 ? token : ' ' + token;\n yield createStreamEvent('token', { token: tokenStr, index } as never);\n index++;\n }\n\n yield createStreamEvent('usage', {\n usage: { promptTokens: 100, completionTokens: tokens.length, totalTokens: 100 + tokens.length },\n } as never);\n\n yield createStreamEvent('done', {\n content: output,\n finishReason: 'stop',\n usage: { promptTokens: 100, completionTokens: tokens.length, totalTokens: 100 + tokens.length },\n cost: 0,\n } as never);\n }\n\n async streamGenerate(prompt: string, options?: StreamGenerateOptions): Promise<StreamResult> {\n let content = '';\n const startTime = Date.now();\n let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };\n\n for await (const event of this.stream(prompt, options)) {\n if (event.type === 'token') content += event.token;\n if (event.type === 'done') {\n content = event.content || content;\n if (event.usage) usage = event.usage;\n }\n if (event.type === 'error') throw event.error;\n }\n\n return {\n content,\n usage,\n model: 'mock-model',\n finishReason: 'stop',\n durationMs: Date.now() - startTime,\n };\n }\n\n async embed(text: string | string[]): Promise<number[][]> {\n const inputs = Array.isArray(text) ? text : [text];\n // Return deterministic mock embeddings\n return inputs.map(() => Array.from({ length: 1536 }, (_, i) => Math.sin(i * 0.1)));\n }\n\n async generateObject<T>(schema: { parse(d: unknown): T }, prompt: string, options?: LLMGenerateOptions): Promise<T> {\n const result = await this.generate(prompt, options);\n try {\n return schema.parse(JSON.parse(result.content));\n } catch {\n return schema.parse({});\n }\n }\n\n // ---- Assertion helpers ----\n\n getCalls(): MockCall[] {\n return [...this.calls];\n }\n\n getCallCount(): number {\n return this.calls.length;\n }\n\n wasCalledWith(pattern: string | RegExp): boolean {\n return this.calls.some(c =>\n typeof pattern === 'string' ? c.prompt.includes(pattern) : pattern.test(c.prompt)\n );\n }\n\n getLastCall(): MockCall | undefined {\n return this.calls[this.calls.length - 1];\n }\n\n reset(): void {\n this.calls = [];\n }\n}\n\nexport function mockLLM(responses: MockResponse[] = [], defaultOutput?: string): MockLLMAdapter {\n return new MockLLMAdapter(responses, defaultOutput);\n}\n","import type { LLMStreamEvent } from '@orka-js/core';\nimport type { BaseAgent, AgentResult } from '@orka-js/agent';\nimport type { AgentTestResult } from './types.js';\nimport { MockLLMAdapter } from './mock-llm.js';\n\nexport interface AgentTestBedConfig {\n agent: BaseAgent;\n llm?: MockLLMAdapter;\n}\n\nexport class AgentTestBed {\n private agent: BaseAgent;\n private llm: MockLLMAdapter;\n\n constructor(config: AgentTestBedConfig) {\n this.agent = config.agent;\n this.llm = config.llm ?? new MockLLMAdapter();\n }\n\n async run(input: string): Promise<AgentTestResult & AgentAssertions> {\n this.llm.reset();\n const events: LLMStreamEvent[] = [];\n const toolCalls: AgentTestResult['toolCalls'] = [];\n\n // Check if agent has runStream (StreamingToolAgent)\n const streamAgent = this.agent as BaseAgent & { runStream?: (input: string) => AsyncIterable<LLMStreamEvent> };\n\n let output = '';\n let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };\n let steps = 0;\n\n if (typeof streamAgent.runStream === 'function') {\n for await (const event of streamAgent.runStream(input)) {\n events.push(event);\n if (event.type === 'tool_call') {\n let args: Record<string, unknown> = {};\n try { args = JSON.parse(event.arguments); } catch { args = {}; }\n toolCalls.push({ name: event.name, args, callId: event.toolCallId });\n steps++;\n }\n if (event.type === 'done') {\n output = event.content;\n if (event.usage) usage = event.usage;\n }\n }\n } else {\n const result: AgentResult = await this.agent.run(input);\n output = result.output;\n steps = result.steps.length;\n usage = { promptTokens: 0, completionTokens: 0, totalTokens: result.totalTokens };\n for (const step of result.steps) {\n toolCalls.push({ name: step.action, args: step.actionInput, callId: '' });\n }\n }\n\n const result: AgentTestResult = { output, toolCalls, steps, usage, events };\n return Object.assign(result, makeAssertions(result));\n }\n\n getLLMMock(): MockLLMAdapter {\n return this.llm;\n }\n\n reset(): void {\n this.llm.reset();\n }\n}\n\nfunction makeAssertions(result: AgentTestResult): AgentAssertions {\n return {\n toHaveCalledTool(name: string) {\n const called = result.toolCalls.some(tc => tc.name === name);\n if (!called) {\n const calledTools = result.toolCalls.map(tc => tc.name).join(', ') || 'none';\n throw new Error(\n 'Expected agent to have called tool ' + JSON.stringify(name) + ', but it called: ' + calledTools\n );\n }\n return result as AgentTestResult & AgentAssertions;\n },\n toHaveOutput(pattern: string | RegExp) {\n const matches = typeof pattern === 'string'\n ? result.output.includes(pattern)\n : pattern.test(result.output);\n if (!matches) {\n throw new Error(\n 'Expected output to match ' + String(pattern) + ', got: ' + JSON.stringify(result.output.slice(0, 200))\n );\n }\n return result as AgentTestResult & AgentAssertions;\n },\n not: {\n toHaveCalledTool(name: string) {\n const called = result.toolCalls.some(tc => tc.name === name);\n if (called) throw new Error('Expected agent NOT to have called tool ' + JSON.stringify(name) + ', but it did');\n return result as AgentTestResult & AgentAssertions;\n },\n },\n };\n}\n\nexport interface AgentAssertions {\n toHaveCalledTool(name: string): AgentTestResult & AgentAssertions;\n toHaveOutput(pattern: string | RegExp): AgentTestResult & AgentAssertions;\n not: {\n toHaveCalledTool(name: string): AgentTestResult & AgentAssertions;\n };\n}","import type { AgentTestResult } from './types.js';\nimport type { MockLLMAdapter } from './mock-llm.js';\n\n/**\n * Extend Vitest/Jest expect with OrkaJS agent matchers.\n * \n * @example\n * ```typescript\n * import { expect } from 'vitest'\n * import { extendExpect } from '@orka-js/test'\n * extendExpect(expect)\n * \n * const result = await bed.run('hello')\n * expect(result).toHaveCalledTool('search')\n * expect(result).toHaveOutput(/hello/)\n * ```\n */\nexport function extendExpect(expect: {\n extend(matchers: Record<string, unknown>): void;\n}): void {\n expect.extend({\n toHaveCalledTool(received: AgentTestResult, toolName: string) {\n const called = received.toolCalls?.some(tc => tc.name === toolName);\n return {\n pass: called,\n message: () => called\n ? 'Expected agent NOT to have called tool ' + JSON.stringify(toolName)\n : 'Expected agent to have called tool ' + JSON.stringify(toolName) + '. Called tools: [' + (received.toolCalls?.map(t => t.name).join(', ') || 'none') + ']',\n };\n },\n\n toHaveOutput(received: AgentTestResult, pattern: string | RegExp) {\n const matches = typeof pattern === 'string'\n ? received.output?.includes(pattern)\n : pattern.test(received.output ?? '');\n return {\n pass: matches,\n message: () => matches\n ? 'Expected output NOT to match ' + String(pattern)\n : 'Expected output to match ' + String(pattern) + '. Got: ' + JSON.stringify(received.output?.slice(0, 200)),\n };\n },\n\n toHaveBeenCalledWithPrompt(received: MockLLMAdapter, pattern: string | RegExp) {\n const called = received.wasCalledWith(pattern);\n return {\n pass: called,\n message: () => called\n ? 'Expected MockLLM NOT to have been called with ' + String(pattern)\n : 'Expected MockLLM to have been called with ' + String(pattern),\n };\n },\n\n toHaveCallCount(received: MockLLMAdapter, count: number) {\n const actual = received.getCallCount();\n return {\n pass: actual === count,\n message: () => 'Expected MockLLM call count to be ' + count + ', got ' + actual,\n };\n },\n });\n}"]}
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@orka-js/test",
3
+ "version": "1.1.0",
4
+ "description": "Testing utilities for OrkaJS agents — mock LLM, AgentTestBed, Vitest matchers",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "sideEffects": false,
20
+ "keywords": [
21
+ "orkajs",
22
+ "orka",
23
+ "testing",
24
+ "mock",
25
+ "llm",
26
+ "ai",
27
+ "test",
28
+ "vitest"
29
+ ],
30
+ "author": "OrkaJS Team",
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/orka-js/orkajs.git",
35
+ "directory": "packages/test"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/orka-js/orkajs/issues"
39
+ },
40
+ "homepage": "https://orkajs.com/en/test",
41
+ "engines": {
42
+ "node": ">=18.0.0"
43
+ },
44
+ "dependencies": {
45
+ "@orka-js/core": "^1.5.0",
46
+ "@orka-js/agent": "^1.5.2"
47
+ },
48
+ "devDependencies": {
49
+ "tsup": "^8.0.1",
50
+ "typescript": "^5.3.3",
51
+ "vitest": "^1.6.0"
52
+ },
53
+ "peerDependencies": {
54
+ "vitest": ">=1.0.0"
55
+ },
56
+ "peerDependenciesMeta": {
57
+ "vitest": {
58
+ "optional": true
59
+ }
60
+ },
61
+ "publishConfig": {
62
+ "access": "public"
63
+ },
64
+ "scripts": {
65
+ "build": "tsup",
66
+ "dev": "tsup --watch",
67
+ "typecheck": "tsc --noEmit",
68
+ "test": "vitest run --passWithNoTests"
69
+ }
70
+ }