/**
 * Anthropic Provider for SpecVerse
 * Supports Claude 3 (Opus, Sonnet, Haiku) and other Anthropic models
 */
import { LLMProvider } from './llm-provider.js';
export class AnthropicProvider extends LLMProvider {
    baseURL;
    headers;
    constructor(config) {
        super(config);
        this.validateConfig();
        this.baseURL = config.baseURL || 'https://api.anthropic.com';
        this.headers = {
            'Content-Type': 'application/json',
            'x-api-key': config.apiKey,
            'anthropic-version': config.version || '2023-06-01',
        };
    }
    async complete(options) {
        const mergedOptions = this.mergeOptions(options);
        const { systemMessage, userMessages } = this.separateMessages(mergedOptions.messages);
        const request = {
            model: this.config.model,
            max_tokens: mergedOptions.max_tokens || 4000,
            messages: userMessages,
            system: systemMessage,
            temperature: mergedOptions.temperature,
            top_p: mergedOptions.top_p,
            stop_sequences: mergedOptions.stop,
            stream: false,
        };
        try {
            const response = await this.makeRequest('/v1/messages', request);
            return this.convertResponse(response);
        }
        catch (error) {
            this.handleError(error);
        }
    }
    async *stream(options) {
        const mergedOptions = this.mergeOptions(options);
        const { systemMessage, userMessages } = this.separateMessages(mergedOptions.messages);
        const request = {
            model: this.config.model,
            max_tokens: mergedOptions.max_tokens || 4000,
            messages: userMessages,
            system: systemMessage,
            temperature: mergedOptions.temperature,
            top_p: mergedOptions.top_p,
            stop_sequences: mergedOptions.stop,
            stream: true,
        };
        try {
            const response = await fetch(`${this.baseURL}/v1/messages`, {
                method: 'POST',
                headers: this.headers,
                body: JSON.stringify(request),
            });
            if (!response.ok) {
                const error = await response.json().catch(() => ({ error: { message: response.statusText } }));
                throw new Error(error.error?.message || response.statusText);
            }
            const reader = response.body?.getReader();
            if (!reader) {
                throw new Error('No response body');
            }
            const decoder = new TextDecoder();
            let buffer = '';
            while (true) {
                const { done, value } = await reader.read();
                if (done)
                    break;
                buffer += decoder.decode(value, { stream: true });
                const lines = buffer.split('\n');
                buffer = lines.pop() || '';
                for (const line of lines) {
                    const trimmed = line.trim();
                    if (trimmed.startsWith('data: ')) {
                        const data = trimmed.slice(6);
                        try {
                            const parsed = JSON.parse(data);
                            const chunk = this.convertStreamChunk(parsed);
                            if (chunk) {
                                yield chunk;
                            }
                        }
                        catch (error) {
                            console.warn('Failed to parse SSE data:', data);
                        }
                    }
                }
            }
        }
        catch (error) {
            this.handleError(error);
        }
    }
    async test() {
        try {
            const response = await this.complete({
                messages: [{ role: 'user', content: 'Say "test" if you can hear me.' }],
                max_tokens: 10,
            });
            return response.content.toLowerCase().includes('test');
        }
        catch (error) {
            return false;
        }
    }
    getInfo() {
        return {
            name: 'Anthropic',
            version: '1.0.0',
            models: [
                'claude-3-opus-20240229',
                'claude-3-sonnet-20240229',
                'claude-3-haiku-20240307',
                'claude-2.1',
                'claude-2.0',
                'claude-instant-1.2',
            ],
            capabilities: [
                'completion',
                'streaming',
                'long-context',
                'function-calling',
            ],
        };
    }
    async makeRequest(endpoint, data) {
        const response = await fetch(`${this.baseURL}${endpoint}`, {
            method: 'POST',
            headers: this.headers,
            body: JSON.stringify(data),
        });
        if (!response.ok) {
            const error = await response.json().catch(() => ({ error: { message: response.statusText } }));
            throw new Error(error.error?.message || response.statusText);
        }
        return response.json();
    }
    separateMessages(messages) {
        let systemMessage;
        const userMessages = [];
        for (const message of messages) {
            if (message.role === 'system') {
                // Anthropic handles system messages separately
                systemMessage = systemMessage
                    ? `${systemMessage}\n\n${message.content}`
                    : message.content;
            }
            else {
                userMessages.push({
                    role: message.role,
                    content: message.content,
                });
            }
        }
        return { systemMessage, userMessages };
    }
    convertResponse(response) {
        const content = response.content
            .filter(block => block.type === 'text')
            .map(block => block.text)
            .join('');
        return {
            content,
            usage: {
                prompt_tokens: response.usage.input_tokens,
                completion_tokens: response.usage.output_tokens,
                total_tokens: response.usage.input_tokens + response.usage.output_tokens,
            },
            finish_reason: this.mapFinishReason(response.stop_reason),
            model: response.model,
        };
    }
    convertStreamChunk(response) {
        switch (response.type) {
            case 'content_block_delta':
                return {
                    content: response.delta?.text || '',
                    finished: false,
                };
            case 'message_stop':
                return {
                    content: '',
                    finished: true,
                    usage: response.message?.usage ? {
                        prompt_tokens: response.message.usage.input_tokens,
                        completion_tokens: response.message.usage.output_tokens,
                        total_tokens: response.message.usage.input_tokens + response.message.usage.output_tokens,
                    } : undefined,
                };
            default:
                return null;
        }
    }
    mapFinishReason(reason) {
        switch (reason) {
            case 'end_turn':
                return 'stop';
            case 'max_tokens':
                return 'length';
            case 'stop_sequence':
                return 'stop';
            default:
                return 'stop';
        }
    }
    validateConfig() {
        super.validateConfig();
        if (!this.config.apiKey) {
            throw new Error('Anthropic API key is required');
        }
        // Validate model format
        const validModels = this.getInfo().models;
        if (!validModels.includes(this.config.model)) {
            console.warn(`Warning: Model '${this.config.model}' is not in the known models list`);
        }
    }
}
/**
 * Factory function to create Anthropic provider
 */
export function createAnthropicProvider(config) {
    return new AnthropicProvider({
        model: config.model || 'claude-3-sonnet-20240229',
        apiKey: config.apiKey,
        version: config.version,
        baseURL: config.baseURL,
        timeout: 30000,
        retries: 3,
    });
}
//# sourceMappingURL=anthropic-provider.js.map