-
Notifications
You must be signed in to change notification settings - Fork 41
/
anthropic_api.py
152 lines (129 loc) · 5.2 KB
/
anthropic_api.py
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#anthropic_api.py
import requests
import io
import base64
import json
import logging
import asyncio
from anthropic import AsyncAnthropic
import logging
import aiohttp
logger = logging.getLogger(__name__)
async def send_anthropic_request(api_key, model, system_message, user_message, messages, temperature, max_tokens, base64_images, tools=None, tool_choice=None):
client = AsyncAnthropic(
api_key=api_key,
base_url="https://api.anthropic.com",
default_headers={
"anthropic-version": "2023-06-01",
"anthropic-beta": "prompt-caching-2024-07-31"
}
)
anthropic_messages = prepare_anthropic_messages(user_message, messages, base64_images)
data = {
"model": model,
"messages": anthropic_messages,
"temperature": temperature,
"max_tokens": max_tokens
}
if system_message:
data["system"] = system_message
if tools:
data["tools"] = tools
if tool_choice:
data["tool_choice"] = tool_choice
try:
response = await client.messages.create(**data)
if tools:
# If tools were used, return the full response
return response
else:
# If no tools were used, format the response to match the specified structure
generated_text = response.content[0].text if response.content else ""
return {
"choices": [{
"message": {
"content": generated_text
}
}]
}
except Exception as e:
error_msg = f"Error: An exception occurred while processing the Anthropic request: {str(e)}"
logger.error(error_msg)
return {"choices": [{"message": {"content": error_msg}}]}
def detect_image_type(base64_string):
"""
Detect the image type from a base64 string.
"""
try:
# Decode a small portion of the base64 string
header = base64.b64decode(base64_string[:32])
# Check for PNG signature
if header.startswith(b'\x89PNG\r\n\x1a\n'):
return 'image/png'
# Check for JPEG signature
elif header.startswith(b'\xff\xd8'):
return 'image/jpeg'
# Add more image type checks as needed
else:
return 'application/octet-stream' # Default to binary data
except:
return 'application/octet-stream' # If detection fails, assume binary data
def prepare_anthropic_messages(user_message, messages, base64_images=None):
"""
Prepares messages for the Anthropic API, ensuring all images are included.
Args:
user_message (str): The user's message.
messages (List[Dict[str, Any]]): Previous messages.
base64_images (List[str], optional): Base64-encoded images.
Returns:
List[Dict[str, Any]]: Formatted messages.
"""
anthropic_messages = []
has_images = base64_images is not None and len(base64_images) > 0
# Prepare the user message with all images
user_content = []
if user_message:
user_content.append({"type": "text", "text": user_message})
if has_images:
for image_data in base64_images:
media_type = detect_image_type(image_data)
user_content.append({
"type": "image",
"source": {
"type": "base64",
"media_type": media_type,
"data": image_data
}
})
# Ensure the first message is from the user
if not messages or messages[0]["role"] != "user":
if user_content:
anthropic_messages.append({"role": "user", "content": user_content})
else:
# If there's no user message and no images, add a dummy user message
anthropic_messages.append({"role": "user", "content": [{"type": "text", "text": "Hello"}]})
else:
# Add previous messages
for message in messages:
role = message["role"]
content = message["content"]
if role == "system":
continue # Skip system messages as they're handled separately
new_message = {"role": role, "content": []}
if isinstance(content, str):
new_message["content"].append({"type": "text", "text": content})
elif isinstance(content, list):
new_message["content"] = content
if not has_images:
if role == "assistant":
new_message["cache_control"] = {"type": "permanent"}
elif role == "user":
new_message["cache_control"] = {"type": "ephemeral"}
anthropic_messages.append(new_message)
# Add the new user message with images if it's not empty
if user_content:
new_user_message = {"role": "user", "content": user_content}
if not has_images:
new_user_message["cache_control"] = {"type": "ephemeral"}
anthropic_messages.append(new_user_message)
return anthropic_messages