diff --git a/src/backends/index.ts b/src/backends/index.ts new file mode 100644 index 0000000..580066d --- /dev/null +++ b/src/backends/index.ts @@ -0,0 +1 @@ +export { NativeAgent, type NativeAgentConfig } from './native/index.js'; diff --git a/src/backends/native/agent.test.ts b/src/backends/native/agent.test.ts new file mode 100644 index 0000000..59a7c68 --- /dev/null +++ b/src/backends/native/agent.test.ts @@ -0,0 +1,79 @@ +import { describe, it, expect, vi } from 'vitest'; +import { NativeAgent } from './agent.js'; +import type { ModelClient, ChatResponse } from '../../models/types.js'; + +describe('NativeAgent', () => { + it('processes message and returns response', async () => { + const mockClient: ModelClient = { + chat: vi.fn().mockResolvedValue({ + content: 'Hello! How can I help you?', + stopReason: 'end_turn', + usage: { inputTokens: 10, outputTokens: 8 }, + } satisfies ChatResponse), + }; + + const agent = new NativeAgent({ + modelClient: mockClient, + systemPrompt: 'You are Flynn, a helpful assistant.', + }); + + const response = await agent.process('Hello'); + + expect(response).toBe('Hello! How can I help you?'); + expect(mockClient.chat).toHaveBeenCalledWith({ + messages: [{ role: 'user', content: 'Hello' }], + system: 'You are Flynn, a helpful assistant.', + }); + }); + + it('maintains conversation history', async () => { + const mockClient: ModelClient = { + chat: vi.fn().mockResolvedValue({ + content: 'Response', + stopReason: 'end_turn', + usage: { inputTokens: 10, outputTokens: 5 }, + } satisfies ChatResponse), + }; + + const agent = new NativeAgent({ + modelClient: mockClient, + systemPrompt: 'System', + }); + + await agent.process('First message'); + await agent.process('Second message'); + + expect(mockClient.chat).toHaveBeenLastCalledWith({ + messages: [ + { role: 'user', content: 'First message' }, + { role: 'assistant', content: 'Response' }, + { role: 'user', content: 'Second message' }, + ], + system: 'System', + }); + }); + + it('resets conversation history', async () => { + const mockClient: ModelClient = { + chat: vi.fn().mockResolvedValue({ + content: 'Response', + stopReason: 'end_turn', + usage: { inputTokens: 10, outputTokens: 5 }, + } satisfies ChatResponse), + }; + + const agent = new NativeAgent({ + modelClient: mockClient, + systemPrompt: 'System', + }); + + await agent.process('Message 1'); + agent.reset(); + await agent.process('Message 2'); + + expect(mockClient.chat).toHaveBeenLastCalledWith({ + messages: [{ role: 'user', content: 'Message 2' }], + system: 'System', + }); + }); +}); diff --git a/src/backends/native/agent.ts b/src/backends/native/agent.ts new file mode 100644 index 0000000..ac540c3 --- /dev/null +++ b/src/backends/native/agent.ts @@ -0,0 +1,38 @@ +import type { ModelClient, Message } from '../../models/types.js'; + +export interface NativeAgentConfig { + modelClient: ModelClient; + systemPrompt: string; +} + +export class NativeAgent { + private modelClient: ModelClient; + private systemPrompt: string; + private history: Message[] = []; + + constructor(config: NativeAgentConfig) { + this.modelClient = config.modelClient; + this.systemPrompt = config.systemPrompt; + } + + async process(userMessage: string): Promise { + this.history.push({ role: 'user', content: userMessage }); + + const response = await this.modelClient.chat({ + messages: [...this.history], + system: this.systemPrompt, + }); + + this.history.push({ role: 'assistant', content: response.content }); + + return response.content; + } + + reset(): void { + this.history = []; + } + + getHistory(): Message[] { + return [...this.history]; + } +} diff --git a/src/backends/native/index.ts b/src/backends/native/index.ts new file mode 100644 index 0000000..651b762 --- /dev/null +++ b/src/backends/native/index.ts @@ -0,0 +1 @@ +export { NativeAgent, type NativeAgentConfig } from './agent.js';