refactor: integrate SessionManager into daemon and agent
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,79 +1,69 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { describe, it, expect, vi, beforeEach } 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.',
|
||||
});
|
||||
const createMockClient = (): ModelClient => ({
|
||||
chat: vi.fn().mockResolvedValue({
|
||||
content: 'Hello!',
|
||||
stopReason: 'end_turn',
|
||||
usage: { inputTokens: 10, outputTokens: 5 },
|
||||
} satisfies ChatResponse),
|
||||
});
|
||||
|
||||
it('maintains conversation history', async () => {
|
||||
const mockClient: ModelClient = {
|
||||
chat: vi.fn().mockResolvedValue({
|
||||
content: 'Response',
|
||||
stopReason: 'end_turn',
|
||||
usage: { inputTokens: 10, outputTokens: 5 },
|
||||
} satisfies ChatResponse),
|
||||
};
|
||||
|
||||
it('processes messages and maintains history', async () => {
|
||||
const mockClient = createMockClient();
|
||||
const agent = new NativeAgent({
|
||||
modelClient: mockClient,
|
||||
systemPrompt: 'System',
|
||||
systemPrompt: 'You are helpful.',
|
||||
});
|
||||
|
||||
await agent.process('First message');
|
||||
await agent.process('Second message');
|
||||
const response = await agent.process('Hi');
|
||||
|
||||
expect(mockClient.chat).toHaveBeenLastCalledWith({
|
||||
messages: [
|
||||
{ role: 'user', content: 'First message' },
|
||||
{ role: 'assistant', content: 'Response' },
|
||||
{ role: 'user', content: 'Second message' },
|
||||
],
|
||||
system: 'System',
|
||||
expect(response).toBe('Hello!');
|
||||
expect(mockClient.chat).toHaveBeenCalledWith({
|
||||
messages: [{ role: 'user', content: 'Hi' }],
|
||||
system: 'You are helpful.',
|
||||
});
|
||||
|
||||
const history = agent.getHistory();
|
||||
expect(history).toHaveLength(2);
|
||||
expect(history[0]).toEqual({ role: 'user', content: 'Hi' });
|
||||
expect(history[1]).toEqual({ role: 'assistant', content: 'Hello!' });
|
||||
});
|
||||
|
||||
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 mockClient = createMockClient();
|
||||
const agent = new NativeAgent({
|
||||
modelClient: mockClient,
|
||||
systemPrompt: 'You are helpful.',
|
||||
});
|
||||
|
||||
await agent.process('Hi');
|
||||
agent.reset();
|
||||
|
||||
expect(agent.getHistory()).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('uses session when provided', async () => {
|
||||
const mockClient = createMockClient();
|
||||
const mockSession = {
|
||||
id: 'test-session',
|
||||
getHistory: vi.fn().mockReturnValue([]),
|
||||
addMessage: vi.fn(),
|
||||
clear: vi.fn(),
|
||||
};
|
||||
|
||||
const agent = new NativeAgent({
|
||||
modelClient: mockClient,
|
||||
systemPrompt: 'System',
|
||||
systemPrompt: 'You are helpful.',
|
||||
session: mockSession,
|
||||
});
|
||||
|
||||
await agent.process('Message 1');
|
||||
agent.reset();
|
||||
await agent.process('Message 2');
|
||||
await agent.process('Hi');
|
||||
|
||||
expect(mockClient.chat).toHaveBeenLastCalledWith({
|
||||
messages: [{ role: 'user', content: 'Message 2' }],
|
||||
system: 'System',
|
||||
});
|
||||
expect(mockSession.addMessage).toHaveBeenCalledTimes(2);
|
||||
expect(mockSession.addMessage).toHaveBeenNthCalledWith(1, { role: 'user', content: 'Hi' });
|
||||
expect(mockSession.addMessage).toHaveBeenNthCalledWith(2, { role: 'assistant', content: 'Hello!' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,59 +1,58 @@
|
||||
import type { ModelClient, Message } from '../../models/types.js';
|
||||
import type { SessionStore } from '../../session/index.js';
|
||||
import type { Session } from '../../session/index.js';
|
||||
|
||||
export interface NativeAgentConfig {
|
||||
modelClient: ModelClient;
|
||||
systemPrompt: string;
|
||||
sessionStore?: SessionStore;
|
||||
sessionId?: string;
|
||||
session?: Session;
|
||||
}
|
||||
|
||||
export class NativeAgent {
|
||||
private modelClient: ModelClient;
|
||||
private systemPrompt: string;
|
||||
private sessionStore?: SessionStore;
|
||||
private sessionId: string;
|
||||
private history: Message[] = [];
|
||||
private session?: Session;
|
||||
private inMemoryHistory: Message[] = [];
|
||||
|
||||
constructor(config: NativeAgentConfig) {
|
||||
this.modelClient = config.modelClient;
|
||||
this.systemPrompt = config.systemPrompt;
|
||||
this.sessionStore = config.sessionStore;
|
||||
this.sessionId = config.sessionId ?? 'default';
|
||||
this.session = config.session;
|
||||
}
|
||||
|
||||
// Load existing history from store
|
||||
if (this.sessionStore) {
|
||||
this.history = this.sessionStore.getMessages(this.sessionId);
|
||||
}
|
||||
private get history(): Message[] {
|
||||
return this.session?.getHistory() ?? [...this.inMemoryHistory];
|
||||
}
|
||||
|
||||
async process(userMessage: string): Promise<string> {
|
||||
const userMsg: Message = { role: 'user', content: userMessage };
|
||||
this.history.push(userMsg);
|
||||
|
||||
if (this.sessionStore) {
|
||||
this.sessionStore.addMessage(this.sessionId, userMsg);
|
||||
if (this.session) {
|
||||
this.session.addMessage(userMsg);
|
||||
} else {
|
||||
this.inMemoryHistory.push(userMsg);
|
||||
}
|
||||
|
||||
const response = await this.modelClient.chat({
|
||||
messages: [...this.history],
|
||||
messages: this.history,
|
||||
system: this.systemPrompt,
|
||||
});
|
||||
|
||||
const assistantMsg: Message = { role: 'assistant', content: response.content };
|
||||
this.history.push(assistantMsg);
|
||||
|
||||
if (this.sessionStore) {
|
||||
this.sessionStore.addMessage(this.sessionId, assistantMsg);
|
||||
if (this.session) {
|
||||
this.session.addMessage(assistantMsg);
|
||||
} else {
|
||||
this.inMemoryHistory.push(assistantMsg);
|
||||
}
|
||||
|
||||
return response.content;
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.history = [];
|
||||
if (this.sessionStore) {
|
||||
this.sessionStore.clearSession(this.sessionId);
|
||||
if (this.session) {
|
||||
this.session.clear();
|
||||
} else {
|
||||
this.inMemoryHistory = [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user