create-agent.test.js•12.5 kB
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import {
handleCreateAgent,
createAgentToolDefinition,
} from '../../../tools/agents/create-agent.js';
import { createMockLettaServer } from '../../utils/mock-server.js';
import { fixtures } from '../../utils/test-fixtures.js';
import { expectValidToolResponse } from '../../utils/test-helpers.js';
describe('Create Agent', () => {
let mockServer;
beforeEach(() => {
mockServer = createMockLettaServer();
});
afterEach(() => {
vi.restoreAllMocks();
});
describe('Tool Definition', () => {
it('should have correct tool definition', () => {
expect(createAgentToolDefinition.name).toBe('create_agent');
expect(createAgentToolDefinition.description).toContain('Create a new Letta agent');
expect(createAgentToolDefinition.inputSchema.required).toEqual(['name', 'description']);
expect(createAgentToolDefinition.inputSchema.properties).toHaveProperty('name');
expect(createAgentToolDefinition.inputSchema.properties).toHaveProperty('description');
expect(createAgentToolDefinition.inputSchema.properties).toHaveProperty('model');
expect(createAgentToolDefinition.inputSchema.properties).toHaveProperty('embedding');
});
it('should have correct default values', () => {
expect(createAgentToolDefinition.inputSchema.properties.model.default).toBe(
'openai/gpt-4',
);
expect(createAgentToolDefinition.inputSchema.properties.embedding.default).toBe(
'openai/text-embedding-ada-002',
);
});
});
describe('Functionality Tests', () => {
it('should create agent successfully with minimal args', async () => {
const createdAgent = { ...fixtures.agent.basic, id: 'new-agent-123' };
const agentWithTools = {
...createdAgent,
tools: [{ name: 'tool1' }, { name: 'tool2' }],
};
// Mock successful agent creation
mockServer.api.post.mockResolvedValueOnce({ data: createdAgent });
mockServer.api.get.mockResolvedValueOnce({ data: agentWithTools });
const result = await handleCreateAgent(mockServer, {
name: 'Test Agent',
description: 'A test agent for unit tests',
});
// Verify API calls
expect(mockServer.api.post).toHaveBeenCalledWith(
'/agents/',
expect.objectContaining({
name: 'Test Agent',
description: 'A test agent for unit tests',
agent_type: 'memgpt_agent',
model: 'openai/gpt-4',
embedding: 'openai/text-embedding-ada-002',
}),
expect.objectContaining({ headers: expect.any(Object) }),
);
expect(mockServer.api.get).toHaveBeenCalledWith(
'/agents/new-agent-123',
expect.objectContaining({ headers: expect.any(Object) }),
);
// Verify response
const data = expectValidToolResponse(result);
expect(data.agent_id).toBe('new-agent-123');
expect(data.capabilities).toEqual(['tool1', 'tool2']);
});
it('should create agent with custom model and embedding', async () => {
const createdAgent = { ...fixtures.agent.basic, id: 'custom-agent-123' };
mockServer.api.post.mockResolvedValueOnce({ data: createdAgent });
mockServer.api.get.mockResolvedValueOnce({ data: createdAgent });
const result = await handleCreateAgent(mockServer, {
name: 'Custom Model Agent',
description: 'An agent with custom models',
model: 'anthropic/claude-3',
embedding: 'openai/text-embedding-3-large',
});
// Verify custom model configuration
expect(mockServer.api.post).toHaveBeenCalledWith(
'/agents/',
expect.objectContaining({
model: 'anthropic/claude-3',
embedding: 'openai/text-embedding-3-large',
llm_config: expect.objectContaining({
model: 'claude-3',
model_endpoint_type: 'anthropic',
}),
}),
expect.any(Object),
);
const data = expectValidToolResponse(result);
expect(data.agent_id).toBe('custom-agent-123');
});
it('should handle agent without tools', async () => {
const agentWithoutTools = {
...fixtures.agent.basic,
id: 'no-tools-agent',
tools: undefined,
};
mockServer.api.post.mockResolvedValueOnce({ data: agentWithoutTools });
mockServer.api.get.mockResolvedValueOnce({ data: agentWithoutTools });
const result = await handleCreateAgent(mockServer, {
name: 'No Tools Agent',
description: 'An agent without tools',
});
const data = expectValidToolResponse(result);
expect(data.agent_id).toBe('no-tools-agent');
expect(data.capabilities).toEqual([]);
});
it('should include user_id header when retrieving agent info', async () => {
const createdAgent = { ...fixtures.agent.basic, id: 'header-test-agent' };
mockServer.api.post.mockResolvedValueOnce({ data: createdAgent });
mockServer.api.get.mockResolvedValueOnce({ data: createdAgent });
await handleCreateAgent(mockServer, {
name: 'Header Test Agent',
description: 'Testing headers',
});
// Verify that get request includes user_id header
expect(mockServer.api.get).toHaveBeenCalledWith(
'/agents/header-test-agent',
expect.objectContaining({
headers: expect.objectContaining({
user_id: 'header-test-agent',
}),
}),
);
});
});
describe('Error Handling', () => {
it('should throw error for missing name', async () => {
await expect(
handleCreateAgent(mockServer, {
description: 'Missing name',
}),
).rejects.toThrow('Invalid arguments: name and description must be strings');
});
it('should throw error for missing description', async () => {
await expect(
handleCreateAgent(mockServer, {
name: 'Missing Description',
}),
).rejects.toThrow('Invalid arguments: name and description must be strings');
});
it('should throw error for non-string name', async () => {
await expect(
handleCreateAgent(mockServer, {
name: 123,
description: 'Valid description',
}),
).rejects.toThrow('Invalid arguments: name and description must be strings');
});
it('should throw error for non-string description', async () => {
await expect(
handleCreateAgent(mockServer, {
name: 'Valid name',
description: { invalid: 'description' },
}),
).rejects.toThrow('Invalid arguments: name and description must be strings');
});
it('should handle API error during agent creation', async () => {
const error = new Error('Server error');
error.response = { status: 500, data: { error: 'Internal server error' } };
mockServer.api.post.mockRejectedValueOnce(error);
await expect(
handleCreateAgent(mockServer, {
name: 'Failed Agent',
description: 'This will fail',
}),
).rejects.toThrow('Server error');
});
it('should handle API error when retrieving agent info', async () => {
const createdAgent = { ...fixtures.agent.basic, id: 'error-agent' };
mockServer.api.post.mockResolvedValueOnce({ data: createdAgent });
const error = new Error('Failed to retrieve agent');
error.response = { status: 404 };
mockServer.api.get.mockRejectedValueOnce(error);
await expect(
handleCreateAgent(mockServer, {
name: 'Retrieve Error Agent',
description: 'Will fail on retrieval',
}),
).rejects.toThrow('Failed to retrieve agent');
});
});
describe('Agent Configuration', () => {
it('should create agent with correct llm_config structure', async () => {
const createdAgent = { ...fixtures.agent.basic, id: 'config-test-agent' };
mockServer.api.post.mockResolvedValueOnce({ data: createdAgent });
mockServer.api.get.mockResolvedValueOnce({ data: createdAgent });
await handleCreateAgent(mockServer, {
name: 'Config Test Agent',
description: 'Testing configuration',
model: 'openai/gpt-4-turbo',
});
expect(mockServer.api.post).toHaveBeenCalledWith(
'/agents/',
expect.objectContaining({
llm_config: {
model: 'gpt-4-turbo',
model_endpoint_type: 'openai',
model_endpoint: 'https://api.openai.com/v1',
context_window: 16000,
max_tokens: 1000,
temperature: 0.7,
frequency_penalty: 0.5,
presence_penalty: 0.5,
functions_config: {
allow: true,
functions: [],
},
},
parameters: {
context_window: 16000,
max_tokens: 1000,
temperature: 0.7,
presence_penalty: 0.5,
frequency_penalty: 0.5,
},
}),
expect.any(Object),
);
});
it('should handle letta-free model correctly', async () => {
const createdAgent = { ...fixtures.agent.basic, id: 'letta-free-agent' };
const agentWithTools = {
...createdAgent,
tools: [{ name: 'tool1' }, { name: 'tool2' }],
};
// Mock successful agent creation
mockServer.api.post.mockResolvedValueOnce({ data: createdAgent });
mockServer.api.get.mockResolvedValueOnce({ data: agentWithTools });
const result = await handleCreateAgent(mockServer, {
name: 'Letta Free Agent',
description: 'Testing letta free model',
model: 'letta/letta-free',
embedding: 'letta/letta-free',
});
expect(mockServer.api.post).toHaveBeenCalledWith(
'/agents/',
expect.objectContaining({
model: 'letta/letta-free',
llm_config: expect.objectContaining({
model: 'letta-free',
model_endpoint_type: 'openai',
model_endpoint: 'https://inference.letta.com',
}),
}),
expect.any(Object),
);
expect(result).toEqual({
content: [
{
type: 'text',
text: JSON.stringify({
agent_id: 'letta-free-agent',
capabilities: ['tool1', 'tool2'],
}),
},
],
structuredContent: {
agent_id: 'letta-free-agent',
capabilities: ['tool1', 'tool2'],
},
});
});
});
});