Spaces:
Sleeping
Sleeping
"""Claude API 客户端""" | |
import json | |
from typing import AsyncGenerator | |
from app.utils.logger import logger | |
from .base_client import BaseClient | |
class ClaudeClient(BaseClient): | |
def __init__(self, api_key: str, api_url: str = "https://api.anthropic.com/v1/messages", provider: str = "anthropic"): | |
"""初始化 Claude 客户端 | |
Args: | |
api_key: Claude API密钥 | |
api_url: Claude API地址 | |
is_openrouter: 是否使用 OpenRouter API | |
""" | |
super().__init__(api_key, api_url) | |
self.provider = provider | |
async def stream_chat( | |
self, | |
messages: list, | |
model_arg: tuple[float, float, float, float], | |
model: str, | |
stream: bool = True | |
) -> AsyncGenerator[tuple[str, str], None]: | |
"""流式或非流式对话 | |
Args: | |
messages: 消息列表 | |
model_arg: 模型参数元组[temperature, top_p, presence_penalty, frequency_penalty] | |
model: 模型名称。如果是 OpenRouter, 会自动转换为 'anthropic/claude-3.5-sonnet' 格式 | |
stream: 是否使用流式输出,默认为 True | |
Yields: | |
tuple[str, str]: (内容类型, 内容) | |
内容类型: "answer" | |
内容: 实际的文本内容 | |
""" | |
if self.provider == "openrouter": | |
# 转换模型名称为 OpenRouter 格式 | |
model = "anthropic/claude-3.5-sonnet" | |
headers = { | |
"Authorization": f"Bearer {self.api_key}", | |
"Content-Type": "application/json", | |
"HTTP-Referer": "https://github.com/ErlichLiu/DeepClaude", # OpenRouter 需要 | |
"X-Title": "DeepClaude" # OpenRouter 需要 | |
} | |
data = { | |
"model": model, # OpenRouter 使用 anthropic/claude-3.5-sonnet 格式 | |
"messages": messages, | |
"stream": stream, | |
"temperature": 1 if model_arg[0] < 0 or model_arg[0] > 1 else model_arg[0], | |
"top_p": model_arg[1], | |
"presence_penalty": model_arg[2], | |
"frequency_penalty": model_arg[3] | |
} | |
elif self.provider == "oneapi": | |
headers = { | |
"Authorization": f"Bearer {self.api_key}", | |
"Content-Type": "application/json" | |
} | |
data = { | |
"model": model, | |
"messages": messages, | |
"stream": stream, | |
"temperature": 1 if model_arg[0] < 0 or model_arg[0] > 1 else model_arg[0], | |
"top_p": model_arg[1], | |
"presence_penalty": model_arg[2], | |
"frequency_penalty": model_arg[3] | |
} | |
elif self.provider == "anthropic": | |
headers = { | |
"x-api-key": self.api_key, | |
"anthropic-version": "2023-06-01", | |
"content-type": "application/json", | |
"accept": "text/event-stream" if stream else "application/json", | |
} | |
data = { | |
"model": model, | |
"messages": messages, | |
"max_tokens": 8192, | |
"stream": stream, | |
"temperature": 1 if model_arg[0] < 0 or model_arg[0] > 1 else model_arg[0], # Claude仅支持temperature与top_p | |
"top_p": model_arg[1] | |
} | |
else: | |
raise ValueError(f"不支持的Claude Provider: {self.provider}") | |
logger.debug(f"开始对话:{data}") | |
if stream: | |
async for chunk in self._make_request(headers, data): | |
chunk_str = chunk.decode('utf-8') | |
if not chunk_str.strip(): | |
continue | |
for line in chunk_str.split('\n'): | |
if line.startswith('data: '): | |
json_str = line[6:] # 去掉 'data: ' 前缀 | |
if json_str.strip() == '[DONE]': | |
return | |
try: | |
data = json.loads(json_str) | |
if self.provider in ("openrouter", "oneapi"): | |
# OpenRouter/OneApi 格式 | |
content = data.get('choices', [{}])[0].get('delta', {}).get('content', '') | |
if content: | |
yield "answer", content | |
elif self.provider == "anthropic": | |
# Anthropic 格式 | |
if data.get('type') == 'content_block_delta': | |
content = data.get('delta', {}).get('text', '') | |
if content: | |
yield "answer", content | |
else: | |
raise ValueError(f"不支持的Claude Provider: {self.provider}") | |
except json.JSONDecodeError: | |
continue | |
else: | |
# 非流式输出 | |
async for chunk in self._make_request(headers, data): | |
try: | |
response = json.loads(chunk.decode('utf-8')) | |
if self.provider in ("openrouter", "oneapi"): | |
content = response.get('choices', [{}])[0].get('message', {}).get('content', '') | |
if content: | |
yield "answer", content | |
elif self.provider == "anthropic": | |
content = response.get('content', [{}])[0].get('text', '') | |
if content: | |
yield "answer", content | |
else: | |
raise ValueError(f"不支持的Claude Provider: {self.provider}") | |
except json.JSONDecodeError: | |
continue | |