UNPKG

@quantumai/quantum-cli-core

Version:

Quantum CLI Core - Multi-LLM Collaboration System

177 lines 7.63 kB
/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { AnthropicProvider } from './anthropic-provider.js'; // Mock the Anthropic SDK vi.mock('@anthropic-ai/sdk', () => ({ default: vi.fn().mockImplementation(() => ({ messages: { create: vi.fn(), }, })), })); describe('AnthropicProvider', () => { let provider; let mockConfig; beforeEach(() => { // Set up mock API key process.env.ANTHROPIC_API_KEY = 'test-api-key'; mockConfig = { id: 'anthropic-test', name: 'Anthropic Test', type: 'anthropic', model: 'claude-3-5-sonnet-20241022', maxTokens: 4000, temperature: 0.7, safetySettings: { enabled: true, filterLevel: 'medium', }, }; provider = new AnthropicProvider(mockConfig); }); describe('constructor', () => { it('should initialize with correct default values', () => { expect(provider.id).toBe('anthropic'); expect(provider.capabilities).toContain('text-generation'); expect(provider.capabilities).toContain('streaming'); expect(provider.capabilities).toContain('safety-filtering'); }); it('should throw error when API key is missing', () => { delete process.env.ANTHROPIC_API_KEY; expect(() => new AnthropicProvider(mockConfig)).toThrow('Anthropic API key not found'); }); }); describe('generate', () => { it('should generate response with token usage and cost', async () => { const mockResponse = { content: [{ text: 'Test response from Claude' }], usage: { input_tokens: 10, output_tokens: 20, }, stop_reason: 'end_turn', }; // @ts-ignore - Access private client for mocking provider.client.messages.create.mockResolvedValue(mockResponse); const result = await provider.generate('Test prompt'); expect(result.content).toBe('Test response from Claude'); expect(result.tokenUsage).toEqual({ inputTokens: 10, outputTokens: 20, totalTokens: 30, }); expect(result.cost).toBeGreaterThan(0); expect(result.confidence).toBe(0.88); expect(result.safetyResult).toBeDefined(); }); it('should handle API errors gracefully', async () => { const mockError = new Error('API Error'); // @ts-ignore provider.client.messages.create.mockRejectedValue(mockError); const result = await provider.generate('Test prompt'); expect(result.error).toBe('API Error'); expect(result.content).toBe(''); expect(result.confidence).toBe(0); }); }); describe('calculateCost', () => { it('should calculate cost based on Claude-3 pricing', () => { const tokenUsage = { inputTokens: 1000, outputTokens: 500, totalTokens: 1500, }; const cost = provider.calculateCost(tokenUsage); // Should calculate based on Claude-3-5-Sonnet pricing: $3/1M input, $15/1M output // 1000 * 3/1000000 + 500 * 15/1000000 = 0.003 + 0.0075 = 0.0105 expect(cost).toBeCloseTo(0.0105, 4); }); it('should handle legacy number input', () => { const cost = provider.calculateCost(1000); expect(typeof cost).toBe('number'); }); }); describe('applySafetyFiltering', () => { it('should pass safe content', async () => { const result = await provider.applySafetyFiltering('This is a safe message'); expect(result.isSafe).toBe(true); expect(result.confidence).toBe(0.95); expect(result.categories).toEqual([]); }); it('should detect potentially harmful content', async () => { const result = await provider.applySafetyFiltering('How to hack into systems'); expect(result.isSafe).toBe(false); expect(result.confidence).toBe(0.6); expect(result.categories).toContain('content-policy'); expect(result.details).toContain('hack'); }); it('should respect disabled safety settings', async () => { const configWithDisabledSafety = { ...mockConfig, safetySettings: { enabled: false, filterLevel: 'medium', }, }; const providerWithDisabledSafety = new AnthropicProvider(configWithDisabledSafety); const result = await providerWithDisabledSafety.applySafetyFiltering('How to hack'); expect(result.isSafe).toBe(true); expect(result.details).toBe('Safety filtering disabled'); }); }); describe('validateCredentials', () => { it('should return true for valid credentials', async () => { // @ts-ignore provider.client.messages.create.mockResolvedValue({ content: [{ text: 'Hello' }], usage: { input_tokens: 1, output_tokens: 1 }, }); const isValid = await provider.validateCredentials(); expect(isValid).toBe(true); }); it('should return false for invalid credentials', async () => { // @ts-ignore provider.client.messages.create.mockRejectedValue(new Error('Invalid API key')); const isValid = await provider.validateCredentials(); expect(isValid).toBe(false); }); }); describe('streaming support', () => { it('should generate streaming response', async () => { const mockStream = [ { type: 'message_start', message: { usage: { input_tokens: 10 } } }, { type: 'content_block_delta', delta: { text: 'Hello' } }, { type: 'content_block_delta', delta: { text: ' world' } }, { type: 'message_delta', usage: { output_tokens: 5 } }, { type: 'message_stop' }, ]; // Mock async iterator const mockAsyncIterator = async function* () { for (const chunk of mockStream) { yield chunk; } }; // @ts-ignore provider.client.messages.create.mockResolvedValue(mockAsyncIterator()); const streamResponse = await provider.generateStream('Test prompt'); const chunks = []; for await (const chunk of streamResponse) { chunks.push(chunk); } expect(chunks.length).toBeGreaterThan(0); // Find content chunks const contentChunks = chunks.filter((chunk) => !chunk.isComplete); expect(contentChunks.some((chunk) => chunk.content === 'Hello')).toBe(true); expect(contentChunks.some((chunk) => chunk.content === ' world')).toBe(true); // Find completion chunk const completionChunk = chunks.find((chunk) => chunk.isComplete); expect(completionChunk).toBeDefined(); expect(completionChunk?.metadata?.provider).toBe('anthropic'); }); }); }); //# sourceMappingURL=anthropic-provider.test.js.map