PlanOpticon

planopticon / video_processor / providers / anthropic_provider.py
Blame History Raw 140 lines
1
"""Anthropic provider implementation."""
2
3
import base64
4
import logging
5
import os
6
from pathlib import Path
7
from typing import Optional
8
9
import anthropic
10
from dotenv import load_dotenv
11
12
from video_processor.providers.base import BaseProvider, ModelInfo, ProviderRegistry
13
14
load_dotenv()
15
logger = logging.getLogger(__name__)
16
17
18
class AnthropicProvider(BaseProvider):
19
"""Anthropic Claude API provider."""
20
21
provider_name = "anthropic"
22
23
def __init__(self, api_key: Optional[str] = None):
24
self.api_key = api_key or os.getenv("ANTHROPIC_API_KEY")
25
if not self.api_key:
26
raise ValueError("ANTHROPIC_API_KEY not set")
27
self.client = anthropic.Anthropic(api_key=self.api_key)
28
29
def chat(
30
self,
31
messages: list[dict],
32
max_tokens: int = 4096,
33
temperature: float = 0.7,
34
model: Optional[str] = None,
35
) -> str:
36
model = model or "claude-haiku-4-5-20251001"
37
38
# Anthropic requires system messages as a top-level parameter
39
system_parts = []
40
chat_messages = []
41
for msg in messages:
42
if msg.get("role") == "system":
43
system_parts.append(msg["content"])
44
else:
45
chat_messages.append(msg)
46
47
kwargs = {
48
"model": model,
49
"messages": chat_messages,
50
"max_tokens": max_tokens,
51
"temperature": temperature,
52
}
53
if system_parts:
54
kwargs["system"] = "\n\n".join(system_parts)
55
56
response = self.client.messages.create(**kwargs)
57
self._last_usage = {
58
"input_tokens": getattr(response.usage, "input_tokens", 0),
59
"output_tokens": getattr(response.usage, "output_tokens", 0),
60
}
61
return response.content[0].text
62
63
def analyze_image(
64
self,
65
image_bytes: bytes,
66
prompt: str,
67
max_tokens: int = 4096,
68
model: Optional[str] = None,
69
) -> str:
70
model = model or "claude-haiku-4-5-20251001"
71
b64 = base64.b64encode(image_bytes).decode()
72
response = self.client.messages.create(
73
model=model,
74
messages=[
75
{
76
"role": "user",
77
"content": [
78
{
79
"type": "image",
80
"source": {
81
"type": "base64",
82
"media_type": "image/jpeg",
83
"data": b64,
84
},
85
},
86
{"type": "text", "text": prompt},
87
],
88
}
89
],
90
max_tokens=max_tokens,
91
)
92
self._last_usage = {
93
"input_tokens": getattr(response.usage, "input_tokens", 0),
94
"output_tokens": getattr(response.usage, "output_tokens", 0),
95
}
96
return response.content[0].text
97
98
def transcribe_audio(
99
self,
100
audio_path: str | Path,
101
language: Optional[str] = None,
102
model: Optional[str] = None,
103
) -> dict:
104
raise NotImplementedError(
105
"Anthropic does not provide a dedicated transcription API. "
106
"Use OpenAI Whisper or Gemini for transcription."
107
)
108
109
def list_models(self) -> list[ModelInfo]:
110
models = []
111
try:
112
page = self.client.models.list(limit=100)
113
for m in page.data:
114
mid = m.id
115
caps = ["chat", "vision"] # All Claude models support chat + vision
116
models.append(
117
ModelInfo(
118
id=mid,
119
provider="anthropic",
120
display_name=getattr(m, "display_name", mid),
121
capabilities=caps,
122
)
123
)
124
except Exception as e:
125
logger.warning(f"Failed to list Anthropic models: {e}")
126
return sorted(models, key=lambda m: m.id)
127
128
129
ProviderRegistry.register(
130
name="anthropic",
131
provider_class=AnthropicProvider,
132
env_var="ANTHROPIC_API_KEY",
133
model_prefixes=["claude-"],
134
default_models={
135
"chat": "claude-haiku-4-5-20251001",
136
"vision": "claude-haiku-4-5-20251001",
137
"audio": "",
138
},
139
)
140

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button