feat: 增强 core/agents 工具和 API

- 新增 loop.py Agent 运行循环
- 优化 memory.py 记忆模块
- 扩展 api/routes.py 接口
- 更新 tools 模块:builtin.py, manager.py, __init__.py
- 新增 .env.example 配置示例
- 更新 requirements.txt 依赖

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-15 19:49:40 +08:00
parent 31f0feafb5
commit 1afa88e812
8 changed files with 231 additions and 17 deletions

View File

@@ -20,7 +20,7 @@ class ChatRequest(BaseModel):
Fields aligned with server/internal/service/agent_service.go::AgentChatRequest
"""
agent_id: int
agent_id: str # 支持 UUID 字符串
message: str
user_id: int = 0
session_id: str | None = None
@@ -37,7 +37,7 @@ class ChatResponse(BaseModel):
Fields aligned with server/internal/service/agent_service.go::AgentChatResponse
"""
agent_id: int
agent_id: str # 支持 UUID 字符串
response: str
tool_calls: list = []
tokens_used: int = 0
@@ -209,7 +209,10 @@ async def chat_stream(request: ChatRequest):
Yields:
Streaming response chunks in SSE format
"""
logger.info(f"[chat_stream] Received request: agent_id={request.agent_id}, message={request.message[:50]}...")
if _agent is None:
logger.error("[chat_stream] Agent not initialized!")
raise HTTPException(status_code=500, detail="Agent not initialized")
session_id = request.session_id or f"session_{request.agent_id}_{int(time.time())}"
@@ -217,6 +220,8 @@ async def chat_stream(request: ChatRequest):
async def generate() -> AsyncGenerator[str, None]:
"""Generate streaming response."""
try:
logger.info(f"[chat_stream] Starting stream for session: {session_id}")
# Prepare kwargs for agent.chat()
kwargs = {
"message": request.message,
@@ -225,28 +230,38 @@ async def chat_stream(request: ChatRequest):
if request.model_id:
kwargs["model_id"] = request.model_id
logger.info(f"[chat_stream] Using model_id: {request.model_id}")
if request.model_name:
kwargs["model_name"] = request.model_name
logger.info(f"[chat_stream] Using model_name: {request.model_name}")
if request.model_provider:
kwargs["model_provider"] = request.model_provider
logger.info(f"[chat_stream] Using model_provider: {request.model_provider}")
if request.api_key:
kwargs["api_key"] = request.api_key
logger.info(f"[chat_stream] Using api_key: {request.api_key[:10]}...")
if request.base_url:
kwargs["base_url"] = request.base_url
logger.info(f"[chat_stream] Using base_url: {request.base_url}")
if request.use_xbot:
kwargs["use_xbot"] = request.use_xbot
logger.info(f"[chat_stream] Using use_xbot: {request.use_xbot}")
# Process with streaming
chunk_count = 0
async for chunk in _agent.chat_stream(**kwargs):
# SSE format: "data: <json>\n\n"
yield f"data: {json.dumps(chunk)}\n\n"
chunk_count += 1
logger.info(f"[chat_stream] Yielding chunk {chunk_count}: {chunk}")
# SSE format: "data: <json>\n\n" - ensure_ascii=False to output UTF-8 characters directly
yield f"data: {json.dumps(chunk, ensure_ascii=False)}\n\n"
logger.info(f"[chat_stream] Stream complete, yielded {chunk_count} chunks")
# Send final message
yield f"data: {json.dumps({'done': True, 'session_id': session_id})}\n\n"
yield f"data: {json.dumps({'done': True, 'session_id': session_id}, ensure_ascii=False)}\n\n"
except Exception as e:
logger.exception(f"Error in streaming chat: {e}")
yield f"data: {json.dumps({'error': str(e)})}\n\n"
yield f"data: {json.dumps({'error': str(e)}, ensure_ascii=False)}\n\n"
from fastapi.responses import StreamingResponse