picogent-py/picogent/providers/anthropic.py
Markov 5417980b76 Initial implementation of PicoGent - minimal AI coding agent
- Implemented ReAct loop in agent.py
- Added Anthropic provider using httpx (no SDK)
- Created tools: read, write, edit, bash
- Added session management with JSONL format
- Included configuration system with env var support
- Added CLI interface and example usage
- Minimal dependencies: only httpx
2026-02-22 23:18:02 +01:00

118 lines
3.6 KiB
Python

"""
Anthropic provider using httpx
"""
import httpx
import json
import uuid
from typing import List, Dict, Any, Optional
from .base import BaseProvider
class AnthropicProvider(BaseProvider):
"""Anthropic provider using httpx directly"""
API_BASE = "https://api.anthropic.com/v1"
API_VERSION = "2023-06-01"
def __init__(self, config):
super().__init__(config)
self.client = httpx.AsyncClient()
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
await self.client.aclose()
async def chat_completion(
self,
messages: List[Dict[str, Any]],
tools: Optional[List[Dict[str, Any]]] = None,
system_prompt: Optional[str] = None
) -> Dict[str, Any]:
"""Get chat completion from Anthropic API"""
headers = {
"x-api-key": self.config.api_key,
"anthropic-version": self.API_VERSION,
"content-type": "application/json"
}
payload = {
"model": self.config.model,
"max_tokens": self.config.max_tokens,
"messages": messages
}
if system_prompt:
payload["system"] = system_prompt
if tools:
payload["tools"] = tools
try:
response = await self.client.post(
f"{self.API_BASE}/messages",
headers=headers,
json=payload,
timeout=60.0
)
if response.status_code != 200:
error_text = response.text
return {
"error": f"API Error {response.status_code}: {error_text}",
"content": f"Error: Anthropic API returned {response.status_code}"
}
result = response.json()
# Extract content and tool calls
content_blocks = result.get("content", [])
text_content = ""
tool_calls = []
for block in content_blocks:
if block.get("type") == "text":
text_content += block.get("text", "")
elif block.get("type") == "tool_use":
tool_calls.append({
"id": block.get("id"),
"name": block.get("name"),
"arguments": block.get("input", {})
})
return {
"content": text_content,
"tool_calls": tool_calls,
"raw_response": result
}
except httpx.TimeoutException:
return {
"error": "Request timed out",
"content": "Error: Request to Anthropic API timed out"
}
except Exception as e:
return {
"error": str(e),
"content": f"Error: {str(e)}"
}
def parse_tool_calls(self, response: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Parse tool calls from Anthropic response"""
return response.get("tool_calls", [])
def format_tool_result(self, tool_call_id: str, result: str) -> Dict[str, Any]:
"""Format tool result for Anthropic API"""
return {
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": tool_call_id,
"content": result
}
]
}