File size: 2,988 Bytes
28b6ae2
 
 
 
bb31829
28b6ae2
 
 
 
 
bb31829
 
28b6ae2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bb31829
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
"""Adaptateur LLM — Anthropic (Claude Sonnet, Claude Haiku)."""

from __future__ import annotations

import logging
import os
from typing import Optional

from picarones.llm.base import BaseLLMAdapter

logger = logging.getLogger(__name__)


class AnthropicAdapter(BaseLLMAdapter):
    """Adaptateur pour les modèles Anthropic Claude.

    Clé API via la variable d'environnement ``ANTHROPIC_API_KEY``.

    Modes supportés : text_only, text_and_image, zero_shot.
    """

    @property
    def name(self) -> str:
        return "anthropic"

    @property
    def default_model(self) -> str:
        return "claude-sonnet-4-6"

    def __init__(
        self,
        model: Optional[str] = None,
        config: Optional[dict] = None,
    ) -> None:
        super().__init__(model, config)
        self._api_key = os.environ.get("ANTHROPIC_API_KEY")

    def _call(self, prompt: str, image_b64: Optional[str] = None) -> str:
        if not self._api_key:
            raise RuntimeError(
                "Clé API Anthropic manquante — définissez la variable d'environnement ANTHROPIC_API_KEY"
            )
        try:
            import anthropic
        except ImportError as exc:
            raise RuntimeError(
                "Le package 'anthropic' n'est pas installé. Lancez : pip install anthropic"
            ) from exc

        client = anthropic.Anthropic(api_key=self._api_key)
        temperature = float(self.config.get("temperature", 0.0))
        max_tokens = int(self.config.get("max_tokens", 4096))

        if image_b64:
            content: list | str = [
                {
                    "type": "image",
                    "source": {
                        "type": "base64",
                        "media_type": "image/png",
                        "data": image_b64,
                    },
                },
                {"type": "text", "text": prompt},
            ]
        else:
            content = prompt

        try:
            response = client.messages.create(
                model=self.model,
                max_tokens=max_tokens,
                temperature=temperature,
                messages=[{"role": "user", "content": content}],
            )
        except Exception as exc:
            logger.warning(
                "[AnthropicAdapter] erreur API (modèle=%s) : %s",
                self.model, exc,
            )
            raise

        if not response.content:
            logger.warning(
                "[AnthropicAdapter] réponse vide (modèle=%s, stop_reason=%s).",
                self.model, getattr(response, "stop_reason", None),
            )
            return ""

        block = response.content[0]
        text = getattr(block, "text", None)
        if text is None:
            logger.warning(
                "[AnthropicAdapter] bloc de type '%s' sans texte (modèle=%s).",
                getattr(block, "type", "unknown"), self.model,
            )
            return ""
        return text