Spaces:
Sleeping
Sleeping
File size: 6,020 Bytes
5693654 |
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 |
"""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
|