2025-03-31 22:20:29 +08:00
|
|
|
|
import os
|
2024-10-02 20:11:28 +08:00
|
|
|
|
import json
|
2024-10-14 22:10:56 +08:00
|
|
|
|
import asyncio
|
2025-03-17 19:58:00 +08:00
|
|
|
|
import traceback
|
2025-03-25 05:40:07 +08:00
|
|
|
|
import uuid
|
2025-04-03 16:02:14 +08:00
|
|
|
|
from fastapi import APIRouter, Body, Depends, HTTPException
|
2025-03-25 05:40:07 +08:00
|
|
|
|
from fastapi.responses import StreamingResponse
|
|
|
|
|
|
from langchain_core.messages import AIMessageChunk
|
|
|
|
|
|
|
2025-03-16 01:11:13 +08:00
|
|
|
|
from src import executor, config, retriever
|
2025-03-25 05:40:07 +08:00
|
|
|
|
from src.core import HistoryManager
|
|
|
|
|
|
from src.agents import agent_manager
|
2025-03-16 01:11:13 +08:00
|
|
|
|
from src.models import select_model
|
2025-02-27 19:35:25 +08:00
|
|
|
|
from src.utils.logging_config import logger
|
2025-04-05 17:27:52 +08:00
|
|
|
|
from src.agents.tools_factory import get_all_tools
|
2025-05-02 23:56:59 +08:00
|
|
|
|
from server.routers.auth_router import get_admin_user
|
|
|
|
|
|
from server.utils.auth_middleware import get_required_user
|
|
|
|
|
|
from server.models.user_model import User
|
2024-10-02 20:11:28 +08:00
|
|
|
|
|
|
|
|
|
|
chat = APIRouter(prefix="/chat")
|
2025-03-06 23:55:20 +08:00
|
|
|
|
|
2025-05-02 23:56:59 +08:00
|
|
|
|
@chat.get("/default_agent")
|
|
|
|
|
|
async def get_default_agent(current_user: User = Depends(get_required_user)):
|
|
|
|
|
|
"""获取默认智能体ID(需要登录)"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
default_agent_id = config.default_agent_id
|
|
|
|
|
|
# 如果没有设置默认智能体,尝试获取第一个可用的智能体
|
|
|
|
|
|
if not default_agent_id:
|
|
|
|
|
|
agents = [agent.get_info() for agent in agent_manager.agents.values()]
|
|
|
|
|
|
if agents:
|
|
|
|
|
|
default_agent_id = agents[0].get("name", "")
|
|
|
|
|
|
|
|
|
|
|
|
return {"default_agent_id": default_agent_id}
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"获取默认智能体出错: {e}")
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"获取默认智能体出错: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
@chat.post("/set_default_agent")
|
|
|
|
|
|
async def set_default_agent(agent_id: str = Body(..., embed=True), current_user = Depends(get_admin_user)):
|
|
|
|
|
|
"""设置默认智能体ID (仅管理员)"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 验证智能体是否存在
|
|
|
|
|
|
agents = [agent.get_info() for agent in agent_manager.agents.values()]
|
|
|
|
|
|
agent_ids = [agent.get("name", "") for agent in agents]
|
|
|
|
|
|
|
|
|
|
|
|
if agent_id not in agent_ids:
|
|
|
|
|
|
raise HTTPException(status_code=404, detail=f"智能体 {agent_id} 不存在")
|
|
|
|
|
|
|
|
|
|
|
|
# 设置默认智能体ID
|
|
|
|
|
|
config.default_agent_id = agent_id
|
|
|
|
|
|
# 保存配置
|
|
|
|
|
|
config.save()
|
|
|
|
|
|
|
|
|
|
|
|
return {"success": True, "default_agent_id": agent_id}
|
|
|
|
|
|
except HTTPException as he:
|
|
|
|
|
|
raise he
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"设置默认智能体出错: {e}")
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"设置默认智能体出错: {str(e)}")
|
|
|
|
|
|
|
2024-10-02 20:11:28 +08:00
|
|
|
|
@chat.get("/")
|
2025-05-02 23:56:59 +08:00
|
|
|
|
async def chat_get(current_user: User = Depends(get_required_user)):
|
|
|
|
|
|
"""聊天服务健康检查(需要登录)"""
|
2024-10-02 20:11:28 +08:00
|
|
|
|
return "Chat Get!"
|
|
|
|
|
|
|
|
|
|
|
|
@chat.post("/")
|
2025-04-23 21:18:39 +08:00
|
|
|
|
async def chat_post(
|
2024-10-14 22:10:56 +08:00
|
|
|
|
query: str = Body(...),
|
|
|
|
|
|
meta: dict = Body(None),
|
2025-04-08 00:19:06 +08:00
|
|
|
|
history: list[dict] | None = Body(None),
|
2025-05-02 23:56:59 +08:00
|
|
|
|
thread_id: str | None = Body(None),
|
|
|
|
|
|
current_user: User = Depends(get_required_user)):
|
|
|
|
|
|
"""处理聊天请求的主要端点(需要登录)"""
|
2024-10-02 20:11:28 +08:00
|
|
|
|
|
2025-03-29 17:33:09 +08:00
|
|
|
|
model = select_model()
|
2025-03-16 01:11:13 +08:00
|
|
|
|
meta["server_model_name"] = model.model_name
|
2025-04-08 00:19:06 +08:00
|
|
|
|
history_manager = HistoryManager(history, system_prompt=meta.get("system_prompt"))
|
2025-02-28 00:27:58 +08:00
|
|
|
|
logger.debug(f"Received query: {query} with meta: {meta}")
|
2024-10-02 20:11:28 +08:00
|
|
|
|
|
2025-02-23 16:39:52 +08:00
|
|
|
|
def make_chunk(content=None, **kwargs):
|
2024-10-15 00:38:24 +08:00
|
|
|
|
return json.dumps({
|
|
|
|
|
|
"response": content,
|
2024-10-23 16:24:29 +08:00
|
|
|
|
"meta": meta,
|
2025-02-23 16:39:52 +08:00
|
|
|
|
**kwargs
|
2024-10-15 00:38:24 +08:00
|
|
|
|
}, ensure_ascii=False).encode('utf-8') + b"\n"
|
2024-10-02 20:11:28 +08:00
|
|
|
|
|
2025-02-24 19:43:40 +08:00
|
|
|
|
def need_retrieve(meta):
|
2025-03-20 19:51:46 +08:00
|
|
|
|
return meta.get("use_web") or meta.get("use_graph") or meta.get("db_id")
|
2025-02-24 19:43:40 +08:00
|
|
|
|
|
2024-10-14 22:10:56 +08:00
|
|
|
|
def generate_response():
|
2025-02-20 01:26:12 +08:00
|
|
|
|
modified_query = query
|
2025-02-23 16:39:52 +08:00
|
|
|
|
refs = None
|
2024-10-15 00:38:24 +08:00
|
|
|
|
|
2025-02-20 01:26:12 +08:00
|
|
|
|
# 处理知识库检索
|
2025-02-24 19:43:40 +08:00
|
|
|
|
if meta and need_retrieve(meta):
|
2025-02-20 01:26:12 +08:00
|
|
|
|
chunk = make_chunk(status="searching")
|
2024-10-15 00:38:24 +08:00
|
|
|
|
yield chunk
|
|
|
|
|
|
|
2025-02-23 16:39:52 +08:00
|
|
|
|
try:
|
2025-03-16 01:11:13 +08:00
|
|
|
|
modified_query, refs = retriever(modified_query, history_manager.messages, meta)
|
2025-02-23 16:39:52 +08:00
|
|
|
|
except Exception as e:
|
2025-03-18 04:58:20 +08:00
|
|
|
|
logger.error(f"Retriever error: {e}, {traceback.format_exc()}")
|
2025-02-23 16:39:52 +08:00
|
|
|
|
yield make_chunk(message=f"Retriever error: {e}", status="error")
|
|
|
|
|
|
return
|
2024-10-15 00:38:24 +08:00
|
|
|
|
|
2025-02-25 21:26:55 +08:00
|
|
|
|
yield make_chunk(status="generating")
|
|
|
|
|
|
|
2025-02-20 01:26:12 +08:00
|
|
|
|
messages = history_manager.get_history_with_msg(modified_query, max_rounds=meta.get('history_round'))
|
|
|
|
|
|
history_manager.add_user(query) # 注意这里使用原始查询
|
2024-10-15 00:38:24 +08:00
|
|
|
|
|
2024-10-02 20:11:28 +08:00
|
|
|
|
content = ""
|
2025-02-20 01:26:12 +08:00
|
|
|
|
reasoning_content = ""
|
2025-02-25 21:26:55 +08:00
|
|
|
|
try:
|
2025-03-16 01:11:13 +08:00
|
|
|
|
for delta in model.predict(messages, stream=True):
|
2025-02-25 21:26:55 +08:00
|
|
|
|
if not delta.content and hasattr(delta, 'reasoning_content'):
|
|
|
|
|
|
reasoning_content += delta.reasoning_content or ""
|
|
|
|
|
|
chunk = make_chunk(reasoning_content=reasoning_content, status="reasoning")
|
|
|
|
|
|
yield chunk
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
# 文心一言
|
|
|
|
|
|
if hasattr(delta, 'is_full') and delta.is_full:
|
|
|
|
|
|
content = delta.content
|
|
|
|
|
|
else:
|
|
|
|
|
|
content += delta.content or ""
|
|
|
|
|
|
|
2025-03-25 05:40:07 +08:00
|
|
|
|
chunk = make_chunk(content=delta.content, status="loading")
|
2025-02-20 01:26:12 +08:00
|
|
|
|
yield chunk
|
2024-10-02 20:11:28 +08:00
|
|
|
|
|
2025-02-25 21:26:55 +08:00
|
|
|
|
logger.debug(f"Final response: {content}")
|
|
|
|
|
|
logger.debug(f"Final reasoning response: {reasoning_content}")
|
2025-03-25 16:28:23 +08:00
|
|
|
|
yield make_chunk(status="finished",
|
2025-02-25 21:26:55 +08:00
|
|
|
|
history=history_manager.update_ai(content),
|
|
|
|
|
|
refs=refs)
|
|
|
|
|
|
except Exception as e:
|
2025-03-17 19:58:00 +08:00
|
|
|
|
logger.error(f"Model error: {e}, {traceback.format_exc()}")
|
2025-02-25 21:26:55 +08:00
|
|
|
|
yield make_chunk(message=f"Model error: {e}", status="error")
|
|
|
|
|
|
return
|
2025-02-20 01:26:12 +08:00
|
|
|
|
|
2024-10-02 20:11:28 +08:00
|
|
|
|
return StreamingResponse(generate_response(), media_type='application/json')
|
|
|
|
|
|
|
|
|
|
|
|
@chat.post("/call")
|
2025-05-02 23:56:59 +08:00
|
|
|
|
async def call(query: str = Body(...), meta: dict = Body(None), current_user: User = Depends(get_required_user)):
|
|
|
|
|
|
"""调用模型进行简单问答(需要登录)"""
|
2025-04-10 11:41:01 +08:00
|
|
|
|
meta = meta or {}
|
2025-03-29 17:33:09 +08:00
|
|
|
|
model = select_model(model_provider=meta.get("model_provider"), model_name=meta.get("model_name"))
|
2024-10-14 22:10:56 +08:00
|
|
|
|
async def predict_async(query):
|
|
|
|
|
|
loop = asyncio.get_event_loop()
|
2025-03-16 01:11:13 +08:00
|
|
|
|
return await loop.run_in_executor(executor, model.predict, query)
|
2024-10-14 22:10:56 +08:00
|
|
|
|
|
|
|
|
|
|
response = await predict_async(query)
|
2024-10-02 20:11:28 +08:00
|
|
|
|
logger.debug({"query": query, "response": response.content})
|
|
|
|
|
|
|
2024-10-14 22:10:56 +08:00
|
|
|
|
return {"response": response.content}
|
|
|
|
|
|
|
2025-03-25 05:40:07 +08:00
|
|
|
|
@chat.get("/agent")
|
2025-05-02 23:56:59 +08:00
|
|
|
|
async def get_agent(current_user: User = Depends(get_required_user)):
|
|
|
|
|
|
"""获取所有可用智能体(需要登录)"""
|
2025-04-01 22:16:07 +08:00
|
|
|
|
agents = [agent.get_info() for agent in agent_manager.agents.values()]
|
2025-03-25 05:40:07 +08:00
|
|
|
|
return {"agents": agents}
|
|
|
|
|
|
|
|
|
|
|
|
@chat.post("/agent/{agent_name}")
|
|
|
|
|
|
def chat_agent(agent_name: str,
|
|
|
|
|
|
query: str = Body(...),
|
|
|
|
|
|
history: list = Body(...),
|
2025-04-02 13:00:25 +08:00
|
|
|
|
config: dict = Body({}),
|
2025-05-02 23:56:59 +08:00
|
|
|
|
meta: dict = Body({}),
|
|
|
|
|
|
current_user: User = Depends(get_required_user)):
|
|
|
|
|
|
"""使用特定智能体进行对话(需要登录)"""
|
2025-04-02 13:00:25 +08:00
|
|
|
|
|
|
|
|
|
|
meta.update({
|
|
|
|
|
|
"query": query,
|
|
|
|
|
|
"agent_name": agent_name,
|
2025-05-02 23:56:59 +08:00
|
|
|
|
"server_model_name": config.get("model", agent_name),
|
2025-04-02 13:00:25 +08:00
|
|
|
|
"thread_id": config.get("thread_id"),
|
|
|
|
|
|
})
|
2025-03-31 22:20:29 +08:00
|
|
|
|
|
2025-04-02 00:00:04 +08:00
|
|
|
|
# 将meta和thread_id整合到config中
|
2025-03-31 22:20:29 +08:00
|
|
|
|
def make_chunk(content=None, **kwargs):
|
2025-04-02 00:00:04 +08:00
|
|
|
|
|
2025-03-31 22:20:29 +08:00
|
|
|
|
return json.dumps({
|
2025-04-02 13:00:25 +08:00
|
|
|
|
"request_id": meta.get("request_id"),
|
2025-03-31 22:20:29 +08:00
|
|
|
|
"response": content,
|
|
|
|
|
|
**kwargs
|
|
|
|
|
|
}, ensure_ascii=False).encode('utf-8') + b"\n"
|
|
|
|
|
|
|
2025-03-25 05:40:07 +08:00
|
|
|
|
def stream_messages():
|
2025-04-09 11:47:08 +08:00
|
|
|
|
|
|
|
|
|
|
# 代表服务端已经收到了请求
|
2025-04-02 22:20:56 +08:00
|
|
|
|
yield make_chunk(status="init", meta=meta)
|
2025-04-09 11:47:08 +08:00
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
agent = agent_manager.get_runnable_agent(agent_name)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"Error getting agent {agent_name}: {e}, {traceback.format_exc()}")
|
|
|
|
|
|
yield make_chunk(message=f"Error getting agent {agent_name}: {e}", status="error")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 从config中获取history_round
|
|
|
|
|
|
history_round = config.get("history_round")
|
|
|
|
|
|
history_manager = HistoryManager(history)
|
|
|
|
|
|
messages = history_manager.get_history_with_msg(query, max_rounds=history_round)
|
|
|
|
|
|
history_manager.add_user(query)
|
|
|
|
|
|
|
|
|
|
|
|
# 构造运行时配置,如果没有thread_id则生成一个
|
|
|
|
|
|
if "thread_id" not in config or not config["thread_id"]:
|
|
|
|
|
|
config["thread_id"] = str(uuid.uuid4())
|
|
|
|
|
|
|
|
|
|
|
|
runnable_config = {"configurable": {**config}}
|
|
|
|
|
|
|
|
|
|
|
|
content = ""
|
|
|
|
|
|
|
2025-04-05 02:23:32 +08:00
|
|
|
|
try:
|
|
|
|
|
|
for msg, metadata in agent.stream_messages(messages, config_schema=runnable_config):
|
|
|
|
|
|
if isinstance(msg, AIMessageChunk) and msg.content != "<tool_call>":
|
|
|
|
|
|
content += msg.content
|
|
|
|
|
|
yield make_chunk(content=msg.content,
|
|
|
|
|
|
msg=msg.model_dump(),
|
|
|
|
|
|
metadata=metadata,
|
|
|
|
|
|
status="loading")
|
|
|
|
|
|
else:
|
|
|
|
|
|
yield make_chunk(msg=msg.model_dump(),
|
|
|
|
|
|
metadata=metadata,
|
|
|
|
|
|
status="loading")
|
|
|
|
|
|
|
|
|
|
|
|
yield make_chunk(status="finished",
|
|
|
|
|
|
history=history_manager.update_ai(content),
|
|
|
|
|
|
meta=meta)
|
|
|
|
|
|
except Exception as e:
|
2025-04-09 11:47:08 +08:00
|
|
|
|
logger.error(f"Error streaming messages: {e}, {traceback.format_exc()}")
|
2025-04-05 02:23:32 +08:00
|
|
|
|
yield make_chunk(message=f"Error streaming messages: {e}", status="error")
|
2025-03-25 05:40:07 +08:00
|
|
|
|
|
|
|
|
|
|
return StreamingResponse(stream_messages(), media_type='application/json')
|
2025-04-01 00:39:54 +08:00
|
|
|
|
|
|
|
|
|
|
@chat.get("/models")
|
2025-05-02 23:56:59 +08:00
|
|
|
|
async def get_chat_models(model_provider: str, current_user: User = Depends(get_admin_user)):
|
|
|
|
|
|
"""获取指定模型提供商的模型列表(需要登录)"""
|
2025-04-01 00:39:54 +08:00
|
|
|
|
model = select_model(model_provider=model_provider)
|
|
|
|
|
|
return {"models": model.get_models()}
|
2025-04-05 17:27:52 +08:00
|
|
|
|
|
2025-04-06 20:33:15 +08:00
|
|
|
|
@chat.post("/models/update")
|
2025-05-02 23:56:59 +08:00
|
|
|
|
async def update_chat_models(model_provider: str, model_names: list[str], current_user = Depends(get_admin_user)):
|
|
|
|
|
|
"""更新指定模型提供商的模型列表 (仅管理员)"""
|
2025-04-06 20:33:15 +08:00
|
|
|
|
config.model_names[model_provider]["models"] = model_names
|
|
|
|
|
|
config._save_models_to_file()
|
|
|
|
|
|
return {"models": config.model_names[model_provider]["models"]}
|
|
|
|
|
|
|
2025-04-05 17:27:52 +08:00
|
|
|
|
@chat.get("/tools")
|
2025-05-02 23:56:59 +08:00
|
|
|
|
async def get_tools(current_user: User = Depends(get_admin_user)):
|
|
|
|
|
|
"""获取所有可用工具(需要登录)"""
|
2025-04-05 17:27:52 +08:00
|
|
|
|
return {"tools": list(get_all_tools().keys())}
|
2025-05-13 19:49:54 +08:00
|
|
|
|
|
|
|
|
|
|
@chat.post("/agent/{agent_name}/config")
|
|
|
|
|
|
async def save_agent_config(
|
|
|
|
|
|
agent_name: str,
|
|
|
|
|
|
config: dict = Body(...),
|
|
|
|
|
|
current_user: User = Depends(get_admin_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""保存智能体配置到YAML文件(需要管理员权限)"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 获取Agent实例和配置类
|
|
|
|
|
|
agent = agent_manager.get_agent(agent_name)
|
|
|
|
|
|
if not agent:
|
|
|
|
|
|
raise HTTPException(status_code=404, detail=f"智能体 {agent_name} 不存在")
|
|
|
|
|
|
|
|
|
|
|
|
# 使用配置类的save_to_file方法保存配置
|
|
|
|
|
|
config_cls = agent.config_schema
|
|
|
|
|
|
result = config_cls.save_to_file(config, agent_name)
|
|
|
|
|
|
|
|
|
|
|
|
if result:
|
|
|
|
|
|
return {"success": True, "message": f"智能体 {agent_name} 配置已保存"}
|
|
|
|
|
|
else:
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"保存智能体配置失败")
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"保存智能体配置出错: {e}, {traceback.format_exc()}")
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"保存智能体配置出错: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
@chat.get("/agent/{agent_name}/config")
|
|
|
|
|
|
async def get_agent_config(
|
|
|
|
|
|
agent_name: str,
|
|
|
|
|
|
current_user: User = Depends(get_admin_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""从YAML文件加载智能体配置(需要管理员权限)"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 检查智能体是否存在
|
|
|
|
|
|
if not (agent := agent_manager.get_agent(agent_name)):
|
|
|
|
|
|
raise HTTPException(status_code=404, detail=f"智能体 {agent_name} 不存在")
|
|
|
|
|
|
|
|
|
|
|
|
config = agent.config_schema.from_runnable_config(config={}, agent_name=agent_name)
|
|
|
|
|
|
return {"success": True, "config": config}
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"加载智能体配置出错: {e}, {traceback.format_exc()}")
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"加载智能体配置出错: {str(e)}")
|