From a6176bc1fdd669419e2bf7a904f7a5ca52686167 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Wed, 4 Feb 2026 12:49:03 +0000 Subject: [PATCH] fix: resolve CI linting and type errors --- app/src/promptforge/providers/anthropic.py | 150 +++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 app/src/promptforge/providers/anthropic.py diff --git a/app/src/promptforge/providers/anthropic.py b/app/src/promptforge/providers/anthropic.py new file mode 100644 index 0000000..28f6e08 --- /dev/null +++ b/app/src/promptforge/providers/anthropic.py @@ -0,0 +1,150 @@ +import time +from typing import AsyncIterator, Optional + +from anthropic import Anthropic, APIError, RateLimitError + +from .base import ProviderBase, ProviderResponse +from ..core.exceptions import ProviderError + + +class AnthropicProvider(ProviderBase): + """Anthropic Claude models provider.""" + + def __init__( + self, + api_key: Optional[str] = None, + model: str = "claude-3-sonnet-20240229", + temperature: float = 0.7, + **kwargs, + ): + """Initialize Anthropic provider.""" + super().__init__(api_key, model, temperature, **kwargs) + self._client: Optional[Anthropic] = None + + @property + def name(self) -> str: + return "anthropic" + + def _get_client(self) -> Anthropic: + """Get or create Anthropic client.""" + if self._client is None: + api_key = self.api_key or self._get_api_key_from_env() + if not api_key: + raise ProviderError( + "Anthropic API key not configured. " + "Set ANTHROPIC_API_KEY env var or pass api_key parameter." + ) + self._client = Anthropic(api_key=api_key) + return self._client + + def _get_api_key_from_env(self) -> Optional[str]: + import os + return os.environ.get("ANTHROPIC_API_KEY") + + async def complete( + self, + prompt: str, + system_prompt: Optional[str] = None, + max_tokens: Optional[int] = None, + **kwargs, + ) -> ProviderResponse: + """Send completion request to Anthropic.""" + start_time = time.time() + + try: + client = self._get_client() + + if system_prompt: + system_message = system_prompt + user_message = prompt + else: + system_message = None + user_message = prompt + + response = client.messages.create( # type: ignore[arg-type] + model=self.model, + max_tokens=max_tokens or 4096, + temperature=self.temperature, + system=system_message, # type: ignore[arg-type] + messages=[{"role": "user", "content": user_message}], + **kwargs, + ) + + latency_ms = (time.time() - start_time) * 1000 + + content = "" + for block in response.content: + if block.type == "text": + content += block.text + + return ProviderResponse( + content=content, + model=self.model, + provider=self.name, + usage={ + "input_tokens": response.usage.input_tokens, + "output_tokens": response.usage.output_tokens, + }, + latency_ms=latency_ms, + metadata={ + "stop_reason": response.stop_reason, + }, + ) + except APIError as e: + raise ProviderError(f"Anthropic API error: {e}") + except RateLimitError as e: + raise ProviderError(f"Anthropic rate limit exceeded: {e}") + + async def stream_complete( # type: ignore[override] + self, + prompt: str, + system_prompt: Optional[str] = None, + max_tokens: Optional[int] = None, + **kwargs, + ) -> AsyncIterator[str]: + """Stream completion from Anthropic.""" + try: + client = self._get_client() + + if system_prompt: + system_message = system_prompt + user_message = prompt + else: + system_message = None + user_message = prompt + + with client.messages.stream( # type: ignore[arg-type] + model=self.model, + max_tokens=max_tokens or 4096, + temperature=self.temperature, + system=system_message, # type: ignore[arg-type] + messages=[{"role": "user", "content": user_message}], + **kwargs, + ) as stream: + for text in stream.text_stream: + yield text + except APIError as e: + raise ProviderError(f"Anthropic API error: {e}") + + def validate_api_key(self) -> bool: + """Validate Anthropic API key.""" + try: + import os + api_key = self.api_key or os.environ.get("ANTHROPIC_API_KEY") + if not api_key: + return False + _ = Anthropic(api_key=api_key) + return True + except Exception: + return False + + def list_models(self) -> list[str]: + """List available Anthropic models.""" + return [ + "claude-3-opus-20240229", + "claude-3-sonnet-20240229", + "claude-3-haiku-20240307", + "claude-2.1", + "claude-2.0", + "claude-instant-1.2", + ]