@quantumai/quantum-cli-core
Version:
Quantum CLI Core - Multi-LLM Collaboration System
177 lines • 7.63 kB
JavaScript
/**
* @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