diff --git a/packages/llm/src/index.ts b/packages/llm/src/index.ts index f2983af..a9795b4 100644 --- a/packages/llm/src/index.ts +++ b/packages/llm/src/index.ts @@ -1,3 +1,4 @@ export * from "./llm.js"; export * from "./config.js"; export * from "./providers/google-genai.js"; +export * from "./providers/mock.js"; diff --git a/packages/llm/src/providers/mock.ts b/packages/llm/src/providers/mock.ts new file mode 100644 index 0000000..9fd7015 --- /dev/null +++ b/packages/llm/src/providers/mock.ts @@ -0,0 +1,24 @@ +import { z } from "zod"; +import { ILLMProvider, LLMRequest, LLMResponse } from "../llm.js"; + +export class MockLLMProvider implements ILLMProvider { + providerName = "mock"; + private callCount = 0; + + constructor(private responses: unknown[]) {} + + async generateStructuredResponse( + request: LLMRequest, + ): Promise>> { + const next = this.responses[this.callCount++]; + if (next === undefined) { + return { success: false, error: "Mock responses exhausted" }; + } + try { + const parsed = request.schema.parse(next); + return { success: true, data: parsed }; + } catch (e) { + return { success: false, error: e instanceof Error ? e.message : String(e) }; + } + } +} diff --git a/packages/llm/tests/mock.test.ts b/packages/llm/tests/mock.test.ts new file mode 100644 index 0000000..fa7c8dc --- /dev/null +++ b/packages/llm/tests/mock.test.ts @@ -0,0 +1,63 @@ +import { describe, test, expect } from "vitest"; +import { z } from "zod"; +import { MockLLMProvider } from "../src/providers/mock.js"; + +describe("MockLLMProvider Unit Tests (Tier 1)", () => { + test("returns parsed matching data for valid mock response", async () => { + const TestSchema = z.object({ + name: z.string(), + age: z.number(), + }); + + const mockData = { name: "Alice", age: 30 }; + const provider = new MockLLMProvider([mockData]); + + const response = await provider.generateStructuredResponse({ + systemPrompt: "system prompt", + userContext: "user context", + schema: TestSchema, + }); + + expect(response.success).toBe(true); + expect(response.data).toEqual(mockData); + }); + + test("returns failure for malformed mock response", async () => { + const TestSchema = z.object({ + name: z.string(), + age: z.number(), + }); + + // age is a string instead of a number, which violates schema + const malformedData = { name: "Alice", age: "thirty" }; + const provider = new MockLLMProvider([malformedData]); + + const response = await provider.generateStructuredResponse({ + systemPrompt: "system prompt", + userContext: "user context", + schema: TestSchema, + }); + + expect(response.success).toBe(false); + expect(response.error).toBeDefined(); + expect(response.data).toBeUndefined(); + }); + + test("returns failure when mock responses are exhausted", async () => { + const TestSchema = z.object({ + name: z.string(), + }); + + const provider = new MockLLMProvider([]); + + const response = await provider.generateStructuredResponse({ + systemPrompt: "system prompt", + userContext: "user context", + schema: TestSchema, + }); + + expect(response.success).toBe(false); + expect(response.error).toBe("Mock responses exhausted"); + expect(response.data).toBeUndefined(); + }); +});