Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions app/api/routes/anthropic.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from app.services.plugin_api_service import PluginAPIService
from app.services.kiro_service import KiroService
from app.services.anthropic_adapter import AnthropicAdapter
from app.utils.kiro_converters import apply_thinking_to_request, is_thinking_enabled
from app.schemas.anthropic import (
AnthropicMessagesRequest,
AnthropicMessagesResponse,
Expand Down Expand Up @@ -174,6 +175,14 @@ async def create_message(

# 将Anthropic请求转换为OpenAI格式
openai_request = AnthropicAdapter.anthropic_to_openai_request(request)

# 提取thinking配置
thinking_config = getattr(request, 'thinking', None)
thinking_enabled = is_thinking_enabled(thinking_config)

# 如果是Kiro服务,应用thinking配置
if use_kiro:
openai_request = apply_thinking_to_request(openai_request, thinking_config)

# 准备额外的请求头
extra_headers = {}
Expand Down Expand Up @@ -203,7 +212,8 @@ async def generate():
async for event in AnthropicAdapter.convert_openai_stream_to_anthropic(
openai_stream,
model=request.model,
request_id=request_id
request_id=request_id,
thinking_enabled=thinking_enabled
):
yield event

Expand Down Expand Up @@ -258,7 +268,8 @@ async def generate():

# 收集流式响应并转换为完整的OpenAI响应
openai_response = await AnthropicAdapter.collect_openai_stream_to_response(
openai_stream
openai_stream,
thinking_enabled=thinking_enabled
)

# 转换响应为Anthropic格式
Expand Down
69 changes: 68 additions & 1 deletion app/api/routes/kiro.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
提供Kiro账号的管理操作,通过插件API实现
仅对beta用户开放
"""
import secrets
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, status, Query
from fastapi.responses import JSONResponse
Expand Down Expand Up @@ -129,6 +130,72 @@ async def create_account(
- **client_secret**: IdC客户端密钥(IdC认证时必填)
"""
try:
if "refresh_token" not in account_data and "refreshToken" in account_data:
account_data["refresh_token"] = account_data.get("refreshToken")
if "auth_method" not in account_data and "authMethod" in account_data:
account_data["auth_method"] = account_data.get("authMethod")
if "account_name" not in account_data and "accountName" in account_data:
account_data["account_name"] = account_data.get("accountName")
if "client_id" not in account_data and "clientId" in account_data:
account_data["client_id"] = account_data.get("clientId")
if "client_secret" not in account_data and "clientSecret" in account_data:
account_data["client_secret"] = account_data.get("clientSecret")
if "machineid" not in account_data and "machineId" in account_data:
account_data["machineid"] = account_data.get("machineId")
if "is_shared" not in account_data and "isShared" in account_data:
account_data["is_shared"] = account_data.get("isShared")

auth_method = (account_data.get("auth_method") or "Social").strip()
if auth_method.lower() == "social":
auth_method = "Social"
elif auth_method.lower() == "idc":
auth_method = "IdC"
account_data["auth_method"] = auth_method

refresh_token = account_data.get("refresh_token")
if not refresh_token:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="missing refresh_token"
)

if auth_method not in ("Social", "IdC"):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="auth_method must be Social or IdC"
)

if auth_method == "IdC" and (not account_data.get("client_id") or not account_data.get("client_secret")):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="IdC requires client_id and client_secret"
)

if not account_data.get("machineid"):
account_data["machineid"] = secrets.token_hex(32)

is_shared = account_data.get("is_shared")
if is_shared is None:
is_shared = 0
if isinstance(is_shared, bool):
is_shared = 1 if is_shared else 0
try:
is_shared = int(is_shared)
except Exception:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="is_shared must be 0 or 1"
)
if is_shared not in (0, 1):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="is_shared must be 0 or 1"
)
account_data["is_shared"] = is_shared

if not account_data.get("account_name"):
account_data["account_name"] = "Kiro Account"

result = await service.create_account(current_user.id, account_data)
return result
except ValueError as e:
Expand Down Expand Up @@ -423,4 +490,4 @@ async def delete_account(
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"删除账号失败: {str(e)}"
)
)
14 changes: 10 additions & 4 deletions app/schemas/anthropic.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,22 +114,28 @@ class AnthropicMessagesRequest(BaseModel):
model: str = Field(..., description="模型名称")
messages: List[AnthropicMessage] = Field(..., description="消息列表")
max_tokens: int = Field(..., description="最大生成token数")

# 可选参数
system: Optional[Union[str, List[AnthropicTextContent]]] = Field(None, description="系统提示")
stop_sequences: Optional[List[str]] = Field(None, description="停止序列")
stream: bool = Field(False, description="是否流式输出")
temperature: Optional[float] = Field(None, ge=0, le=1, description="温度参数")
top_p: Optional[float] = Field(None, ge=0, le=1, description="Top-p采样")
top_k: Optional[int] = Field(None, ge=0, description="Top-k采样")


# Extended Thinking 支持
thinking: Optional[Union[Dict[str, Any], bool, str]] = Field(
None,
description="Extended Thinking 配置。可以是 bool、'enabled' 或 dict 格式如 {'type': 'enabled', 'budget_tokens': 10000}"
)

# 工具相关
tools: Optional[List[AnthropicTool]] = Field(None, description="可用工具列表")
tool_choice: Optional[Union[AnthropicToolChoice, Dict[str, Any]]] = Field(None, description="工具选择策略")

# 元数据
metadata: Optional[AnthropicMetadata] = Field(None, description="请求元数据")

model_config = {"extra": "allow"}


Expand Down
Loading