Skip to main content
Glama
prompt-agent.test.js15.4 kB
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { handlePromptAgent, promptAgentToolDefinition, } from '../../../tools/agents/prompt-agent.js'; import { createMockLettaServer } from '../../utils/mock-server.js'; import { fixtures } from '../../utils/test-fixtures.js'; import { expectValidToolResponse } from '../../utils/test-helpers.js'; describe('Prompt Agent', () => { let mockServer; beforeEach(() => { mockServer = createMockLettaServer(); }); afterEach(() => { vi.restoreAllMocks(); }); describe('Tool Definition', () => { it('should have correct tool definition', () => { expect(promptAgentToolDefinition.name).toBe('prompt_agent'); expect(promptAgentToolDefinition.description).toContain('Send a message to an agent'); expect(promptAgentToolDefinition.inputSchema.required).toEqual(['agent_id', 'message']); expect(promptAgentToolDefinition.inputSchema.properties).toHaveProperty('agent_id'); expect(promptAgentToolDefinition.inputSchema.properties).toHaveProperty('message'); }); it('should have proper parameter descriptions', () => { const agentIdProp = promptAgentToolDefinition.inputSchema.properties.agent_id; const messageProp = promptAgentToolDefinition.inputSchema.properties.message; expect(agentIdProp.type).toBe('string'); expect(agentIdProp.description).toContain('ID of the agent'); expect(messageProp.type).toBe('string'); expect(messageProp.description).toContain('Message to send'); }); }); describe('Functionality Tests', () => { it('should prompt agent successfully with assistant message', async () => { const agentData = { ...fixtures.agent.basic, name: 'Test Assistant' }; mockServer.api.get.mockResolvedValueOnce({ data: agentData }); // Mock SSE response with assistant message const sseResponse = [ 'data: {"message_type": "assistant_message", "content": "Hello! How can I help you today?"}', '', 'data: [DONE]', '', ].join('\n'); mockServer.api.post.mockResolvedValueOnce({ data: sseResponse, headers: { 'content-type': 'text/event-stream' }, }); const result = await handlePromptAgent(mockServer, { agent_id: 'agent-123', message: 'Hello, how are you?', }); // Verify API calls expect(mockServer.api.get).toHaveBeenCalledWith( '/agents/agent-123', expect.objectContaining({ headers: expect.any(Object) }), ); expect(mockServer.api.post).toHaveBeenCalledWith( '/agents/agent-123/messages/stream', { messages: [{ role: 'user', content: 'Hello, how are you?' }], stream_steps: false, stream_tokens: false, }, expect.objectContaining({ headers: expect.any(Object), responseType: 'text', }), ); // Verify response const data = expectValidToolResponse(result); expect(data.agent_id).toBe('agent-123'); expect(data.agent_name).toBe('Test Assistant'); expect(data.message).toBe('Hello, how are you?'); expect(data.response).toBe('Hello! How can I help you today?'); }); it('should handle reasoning messages', async () => { const agentData = fixtures.agent.basic; mockServer.api.get.mockResolvedValueOnce({ data: agentData }); const sseResponse = [ 'data: {"message_type": "reasoning_message", "reasoning": "The user is asking about the weather. I should provide helpful information."}', 'data: {"message_type": "assistant_message", "content": "I\'d be happy to help with weather information! Could you tell me which location you\'re interested in?"}', 'data: [DONE]', ].join('\n'); mockServer.api.post.mockResolvedValueOnce({ data: sseResponse }); const result = await handlePromptAgent(mockServer, { agent_id: 'agent-123', message: "What's the weather like?", }); const data = expectValidToolResponse(result); expect(data.response).toBe( "I'd be happy to help with weather information! Could you tell me which location you're interested in?", ); }); it('should handle streaming delta updates', async () => { const agentData = fixtures.agent.basic; mockServer.api.get.mockResolvedValueOnce({ data: agentData }); const sseResponse = [ 'data: {"delta": {"content": "I am "}}', 'data: {"delta": {"content": "processing "}}', 'data: {"delta": {"content": "your request."}}', 'data: [DONE]', ].join('\n'); mockServer.api.post.mockResolvedValueOnce({ data: sseResponse }); const result = await handlePromptAgent(mockServer, { agent_id: 'agent-123', message: 'Process this request', }); const data = expectValidToolResponse(result); // The response includes [DONE] because it's parsed as a delta expect(data.response).toBe('I am \nprocessing \nyour request.\n[DONE]'); }); it('should handle mixed message types', async () => { const agentData = fixtures.agent.basic; mockServer.api.get.mockResolvedValueOnce({ data: agentData }); const sseResponse = [ 'data: {"message_type": "reasoning_message", "reasoning": "Analyzing the question..."}', 'data: {"delta": {"content": "Based on "}}', 'data: {"delta": {"content": "my analysis..."}}', 'data: {"message_type": "assistant_message", "content": "Based on my analysis, here is the answer."}', 'data: [DONE]', ].join('\n'); mockServer.api.post.mockResolvedValueOnce({ data: sseResponse }); const result = await handlePromptAgent(mockServer, { agent_id: 'agent-123', message: 'Analyze this', }); const data = expectValidToolResponse(result); // Should use the assistant_message when available expect(data.response).toBe('Based on my analysis, here is the answer.'); }); it('should handle empty SSE response', async () => { const agentData = fixtures.agent.basic; mockServer.api.get.mockResolvedValueOnce({ data: agentData }); mockServer.api.post.mockResolvedValueOnce({ data: '' }); const result = await handlePromptAgent(mockServer, { agent_id: 'agent-123', message: 'Hello', }); const data = expectValidToolResponse(result); expect(data.response).toBe("Received response but couldn't extract message content"); }); it('should handle non-string response data', async () => { const agentData = fixtures.agent.basic; mockServer.api.get.mockResolvedValueOnce({ data: agentData }); // Non-SSE response (unexpected) mockServer.api.post.mockResolvedValueOnce({ data: { message: 'Direct response object' }, }); const result = await handlePromptAgent(mockServer, { agent_id: 'agent-123', message: 'Hello', }); const data = expectValidToolResponse(result); expect(data.response).toBe('{"message":"Direct response object"}'); }); }); describe('Error Handling', () => { it('should throw error for missing agent_id', async () => { await expect( handlePromptAgent(mockServer, { message: 'Hello', }), ).rejects.toThrow('Missing required arguments: agent_id and message'); }); it('should throw error for missing message', async () => { await expect( handlePromptAgent(mockServer, { agent_id: 'agent-123', }), ).rejects.toThrow('Missing required arguments: agent_id and message'); }); it('should throw error for missing both arguments', async () => { await expect(handlePromptAgent(mockServer, {})).rejects.toThrow( 'Missing required arguments: agent_id and message', ); }); it('should handle agent not found', async () => { const error = new Error('Not found'); error.response = { status: 404 }; mockServer.api.get.mockRejectedValueOnce(error); await expect( handlePromptAgent(mockServer, { agent_id: 'nonexistent', message: 'Hello', }), ).rejects.toThrow('Not found'); }); it('should handle API error during message sending', async () => { const agentData = fixtures.agent.basic; mockServer.api.get.mockResolvedValueOnce({ data: agentData }); const error = new Error('Server error'); error.response = { status: 500 }; mockServer.api.post.mockRejectedValueOnce(error); await expect( handlePromptAgent(mockServer, { agent_id: 'agent-123', message: 'Hello', }), ).rejects.toThrow('Server error'); }); it('should handle malformed SSE data', async () => { const agentData = fixtures.agent.basic; mockServer.api.get.mockResolvedValueOnce({ data: agentData }); const sseResponse = [ 'data: {invalid json', 'data: {"valid": "json", "but": "no message content"}', 'data: null', 'not-data-line: {"ignored": true}', ].join('\n'); mockServer.api.post.mockResolvedValueOnce({ data: sseResponse }); const result = await handlePromptAgent(mockServer, { agent_id: 'agent-123', message: 'Hello', }); const data = expectValidToolResponse(result); // Should handle parse errors gracefully expect(data.response).toContain('{invalid json'); }); }); describe('Edge Cases', () => { it('should handle very long messages', async () => { const agentData = fixtures.agent.basic; mockServer.api.get.mockResolvedValueOnce({ data: agentData }); const longMessage = 'A'.repeat(10000); const sseResponse = 'data: {"message_type": "assistant_message", "content": "Received long message"}\n'; mockServer.api.post.mockResolvedValueOnce({ data: sseResponse }); const result = await handlePromptAgent(mockServer, { agent_id: 'agent-123', message: longMessage, }); expect(mockServer.api.post).toHaveBeenCalledWith( expect.any(String), expect.objectContaining({ messages: [{ role: 'user', content: longMessage }], }), expect.any(Object), ); const data = expectValidToolResponse(result); expect(data.message).toBe(longMessage); expect(data.response).toBe('Received long message'); }); it('should handle unicode in messages', async () => { const agentData = fixtures.agent.basic; mockServer.api.get.mockResolvedValueOnce({ data: agentData }); const unicodeMessage = 'Hello 🤖 世界 🌍'; const sseResponse = 'data: {"message_type": "assistant_message", "content": "Hello! I see emojis and Chinese characters: 🤖 世界 🌍"}\n'; mockServer.api.post.mockResolvedValueOnce({ data: sseResponse }); const result = await handlePromptAgent(mockServer, { agent_id: 'agent-123', message: unicodeMessage, }); const data = expectValidToolResponse(result); expect(data.message).toBe(unicodeMessage); expect(data.response).toContain('🤖 世界 🌍'); }); it('should handle multiple data lines without proper message', async () => { const agentData = fixtures.agent.basic; mockServer.api.get.mockResolvedValueOnce({ data: agentData }); const sseResponse = [ 'data: {"type": "start"}', 'data: {"type": "thinking"}', 'data: {"type": "end"}', '', ].join('\n'); mockServer.api.post.mockResolvedValueOnce({ data: sseResponse }); const result = await handlePromptAgent(mockServer, { agent_id: 'agent-123', message: 'Hello', }); const data = expectValidToolResponse(result); expect(data.response).toBe("Received response but couldn't extract message content"); }); it('should preserve agent name from initial lookup', async () => { const agentData = { ...fixtures.agent.basic, name: 'Custom Agent Name 🤖', }; mockServer.api.get.mockResolvedValueOnce({ data: agentData }); const sseResponse = 'data: {"message_type": "assistant_message", "content": "Hello!"}\n'; mockServer.api.post.mockResolvedValueOnce({ data: sseResponse }); const result = await handlePromptAgent(mockServer, { agent_id: 'agent-123', message: 'Hi', }); const data = expectValidToolResponse(result); expect(data.agent_name).toBe('Custom Agent Name 🤖'); }); it('should handle SSE with extra whitespace', async () => { const agentData = fixtures.agent.basic; mockServer.api.get.mockResolvedValueOnce({ data: agentData }); const sseResponse = [ ' data: {"message_type": "assistant_message", "content": "Response with spaces"} ', '', ' data: [DONE] ', ].join('\n'); mockServer.api.post.mockResolvedValueOnce({ data: sseResponse }); const result = await handlePromptAgent(mockServer, { agent_id: 'agent-123', message: 'Test', }); const data = expectValidToolResponse(result); // The parsing fails due to leading spaces, so raw lines are added expect(data.response).toContain('Response with spaces'); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/oculairmedia/Letta-MCP-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server