feat: 添加非流式对话接口和相应的集成测试
This commit is contained in:
parent
4ad4321992
commit
0836fd8d4f
@ -342,6 +342,150 @@ async def check_and_handle_interrupts(
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
async def agent_chat(
|
||||
*,
|
||||
agent_id: str,
|
||||
query: str,
|
||||
config: dict,
|
||||
meta: dict,
|
||||
image_content: str | None,
|
||||
current_user,
|
||||
db,
|
||||
) -> dict:
|
||||
"""非流式对话,返回完整响应"""
|
||||
start_time = asyncio.get_event_loop().time()
|
||||
|
||||
if image_content:
|
||||
human_message = HumanMessage(
|
||||
content=[
|
||||
{"type": "text", "text": query},
|
||||
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{image_content}"}},
|
||||
]
|
||||
)
|
||||
message_type = "multimodal_image"
|
||||
else:
|
||||
human_message = HumanMessage(content=query)
|
||||
message_type = "text"
|
||||
|
||||
init_msg = {"role": "user", "content": query, "type": "human"}
|
||||
if image_content:
|
||||
init_msg["message_type"] = "multimodal_image"
|
||||
init_msg["image_content"] = image_content
|
||||
else:
|
||||
init_msg["message_type"] = "text"
|
||||
|
||||
if conf.enable_content_guard and await content_guard.check(query):
|
||||
return {
|
||||
"status": "error",
|
||||
"error_type": "content_guard_blocked",
|
||||
"error_message": "输入内容包含敏感词",
|
||||
"request_id": meta.get("request_id"),
|
||||
}
|
||||
|
||||
try:
|
||||
agent = agent_manager.get_agent(agent_id)
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting agent {agent_id}: {e}, {traceback.format_exc()}")
|
||||
return {
|
||||
"status": "error",
|
||||
"error_type": "agent_error",
|
||||
"error_message": f"智能体 {agent_id} 获取失败: {str(e)}",
|
||||
"request_id": meta.get("request_id"),
|
||||
}
|
||||
|
||||
messages = [human_message]
|
||||
|
||||
user_id = str(current_user.id)
|
||||
agent_config_id = config.get("agent_config_id")
|
||||
try:
|
||||
agent_config = await _resolve_agent_config(db, agent_id, current_user, agent_config_id)
|
||||
except ValueError as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"error_type": "invalid_config",
|
||||
"error_message": str(e),
|
||||
"request_id": meta.get("request_id"),
|
||||
}
|
||||
|
||||
if not (thread_id := config.get("thread_id")):
|
||||
thread_id = str(uuid.uuid4())
|
||||
logger.warning(f"No thread_id provided, generated new thread_id: {thread_id}")
|
||||
|
||||
input_context = agent_config | {"user_id": user_id, "thread_id": thread_id}
|
||||
|
||||
try:
|
||||
conv_repo = ConversationRepository(db)
|
||||
|
||||
try:
|
||||
await conv_repo.add_message_by_thread_id(
|
||||
thread_id=thread_id,
|
||||
role="user",
|
||||
content=query,
|
||||
message_type=message_type,
|
||||
image_content=image_content,
|
||||
extra_metadata={"raw_message": human_message.model_dump()},
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error saving user message: {e}")
|
||||
|
||||
langgraph_config = {"configurable": {"thread_id": thread_id, "user_id": user_id}}
|
||||
|
||||
full_msg = None
|
||||
accumulated_content: list[str] = []
|
||||
|
||||
async for msg, metadata in agent.stream_messages(messages, input_context=input_context):
|
||||
if isinstance(msg, AIMessageChunk):
|
||||
accumulated_content.append(msg.content)
|
||||
else:
|
||||
msg_dict = msg.model_dump() if hasattr(msg, "model_dump") else {}
|
||||
if msg_dict.get("type") == "ai":
|
||||
full_msg = msg
|
||||
|
||||
full_msg = _ensure_full_msg(full_msg, accumulated_content)
|
||||
full_content = "".join(accumulated_content) if accumulated_content else (full_msg.content if full_msg else "")
|
||||
|
||||
if conf.enable_content_guard and await content_guard.check(full_content):
|
||||
await save_partial_message(conv_repo, thread_id, full_msg, "content_guard_blocked")
|
||||
return {
|
||||
"status": "interrupted",
|
||||
"message": "检测到敏感内容,已中断输出",
|
||||
"request_id": meta.get("request_id"),
|
||||
"time_cost": asyncio.get_event_loop().time() - start_time,
|
||||
}
|
||||
|
||||
try:
|
||||
graph = await agent.get_graph()
|
||||
state = await graph.aget_state(langgraph_config)
|
||||
agent_state = extract_agent_state(getattr(state, "values", {})) if state else {}
|
||||
except Exception:
|
||||
agent_state = {}
|
||||
|
||||
await save_messages_from_langgraph_state(
|
||||
agent_instance=agent,
|
||||
thread_id=thread_id,
|
||||
conv_repo=conv_repo,
|
||||
config_dict=langgraph_config,
|
||||
)
|
||||
|
||||
return {
|
||||
"status": "finished",
|
||||
"response": full_content,
|
||||
"request_id": meta.get("request_id"),
|
||||
"thread_id": thread_id,
|
||||
"agent_state": agent_state,
|
||||
"time_cost": asyncio.get_event_loop().time() - start_time,
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in agent_chat: {e}, {traceback.format_exc()}")
|
||||
return {
|
||||
"status": "error",
|
||||
"error_type": "unexpected_error",
|
||||
"error_message": str(e),
|
||||
"request_id": meta.get("request_id"),
|
||||
}
|
||||
|
||||
|
||||
async def stream_agent_chat(
|
||||
*,
|
||||
agent_id: str,
|
||||
@ -11,7 +11,7 @@ from dataclasses import dataclass, field
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.exc import OperationalError
|
||||
from yuxi.repositories.agent_run_repository import TERMINAL_RUN_STATUSES, AgentRunRepository
|
||||
from yuxi.services.chat_stream_service import stream_agent_chat
|
||||
from yuxi.services.chat_service import stream_agent_chat
|
||||
from yuxi.services.run_queue_service import (
|
||||
append_run_stream_event,
|
||||
clear_cancel_signal,
|
||||
|
||||
@ -14,7 +14,7 @@ from server.utils.auth_middleware import get_db, get_required_user
|
||||
from yuxi import config as conf
|
||||
from yuxi.agents.buildin import agent_manager
|
||||
from yuxi.models import select_model
|
||||
from yuxi.services.chat_stream_service import get_agent_state_view, stream_agent_chat, stream_agent_resume
|
||||
from yuxi.services.chat_service import get_agent_state_view, stream_agent_chat, stream_agent_resume, agent_chat
|
||||
from yuxi.services.agent_run_service import (
|
||||
cancel_agent_run_view,
|
||||
create_agent_run_view,
|
||||
@ -412,6 +412,48 @@ async def chat_agent(
|
||||
)
|
||||
|
||||
|
||||
@chat.post("/agent/{agent_id}/sync")
|
||||
async def chat_agent_sync(
|
||||
agent_id: str,
|
||||
query: str = Body(...),
|
||||
config: dict = Body({}),
|
||||
meta: dict = Body({}),
|
||||
image_content: str | None = Body(None),
|
||||
current_user: User = Depends(get_required_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""使用特定智能体进行非流式对话(需要登录)"""
|
||||
logger.info(f"[sync] agent_id: {agent_id}, query: {query}, config: {config}, meta: {meta}")
|
||||
logger.info(f"[sync] image_content present: {image_content is not None}")
|
||||
if image_content:
|
||||
logger.info(f"[sync] image_content length: {len(image_content)}")
|
||||
|
||||
# 确保 request_id 存在
|
||||
if "request_id" not in meta or not meta.get("request_id"):
|
||||
meta["request_id"] = str(uuid.uuid4())
|
||||
|
||||
meta.update(
|
||||
{
|
||||
"query": query,
|
||||
"agent_id": agent_id,
|
||||
"server_model_name": config.get("model", agent_id),
|
||||
"thread_id": config.get("thread_id"),
|
||||
"user_id": current_user.id,
|
||||
"has_image": bool(image_content),
|
||||
}
|
||||
)
|
||||
|
||||
return await agent_chat(
|
||||
agent_id=agent_id,
|
||||
query=query,
|
||||
config=config,
|
||||
meta=meta,
|
||||
image_content=image_content,
|
||||
current_user=current_user,
|
||||
db=db,
|
||||
)
|
||||
|
||||
|
||||
@chat.post("/agent/{agent_id}/runs")
|
||||
async def create_agent_run(
|
||||
agent_id: str,
|
||||
|
||||
192
backend/test/api/test_chat_agent_sync.py
Normal file
192
backend/test/api/test_chat_agent_sync.py
Normal file
@ -0,0 +1,192 @@
|
||||
"""
|
||||
Integration tests for chat_agent_sync non-streaming endpoint.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
pytestmark = [pytest.mark.asyncio, pytest.mark.integration]
|
||||
|
||||
|
||||
async def test_chat_agent_sync_requires_authentication(test_client):
|
||||
"""非流式端点需要认证"""
|
||||
response = await test_client.post("/api/chat/agent/test_agent/sync", json={"query": "hello"})
|
||||
assert response.status_code == 401
|
||||
|
||||
|
||||
async def test_chat_agent_sync_basic_conversation(test_client, admin_headers):
|
||||
"""测试非流式对话基本功能"""
|
||||
# 先获取可用智能体
|
||||
agents_response = await test_client.get("/api/chat/agent", headers=admin_headers)
|
||||
assert agents_response.status_code == 200, agents_response.text
|
||||
agents = agents_response.json().get("agents", [])
|
||||
|
||||
if not agents:
|
||||
pytest.skip("No agents are registered in the system.")
|
||||
|
||||
agent_id = agents[0].get("id")
|
||||
if not agent_id:
|
||||
pytest.skip("Agent payload missing id field.")
|
||||
|
||||
# 调用非流式端点
|
||||
response = await test_client.post(
|
||||
f"/api/chat/agent/{agent_id}/sync",
|
||||
json={"query": "Hello, say 'Hi' back to me"},
|
||||
headers=admin_headers,
|
||||
)
|
||||
|
||||
assert response.status_code == 200, response.text
|
||||
payload = response.json()
|
||||
|
||||
# 验证响应结构
|
||||
assert "status" in payload, f"Missing 'status' in response: {payload}"
|
||||
assert payload["status"] in ("finished", "error", "interrupted"), f"Unexpected status: {payload['status']}"
|
||||
assert "request_id" in payload, f"Missing 'request_id' in response: {payload}"
|
||||
|
||||
# 如果成功完成,验证响应内容
|
||||
if payload["status"] == "finished":
|
||||
assert "response" in payload, f"Missing 'response' in finished status: {payload}"
|
||||
assert isinstance(payload["response"], str), f"response should be str, got: {type(payload['response'])}"
|
||||
assert len(payload["response"]) > 0, "response should not be empty"
|
||||
# thread_id 应该存在
|
||||
assert "thread_id" in payload, f"Missing 'thread_id' in response: {payload}"
|
||||
# time_cost 应该存在
|
||||
assert "time_cost" in payload, f"Missing 'time_cost' in response: {payload}"
|
||||
assert isinstance(payload["time_cost"], float), f"time_cost should be float: {type(payload['time_cost'])}"
|
||||
|
||||
|
||||
async def test_chat_agent_sync_with_thread_id(test_client, admin_headers):
|
||||
"""测试非流式对话指定 thread_id"""
|
||||
# 获取可用智能体
|
||||
agents_response = await test_client.get("/api/chat/agent", headers=admin_headers)
|
||||
assert agents_response.status_code == 200, agents_response.text
|
||||
agents = agents_response.json().get("agents", [])
|
||||
|
||||
if not agents:
|
||||
pytest.skip("No agents are registered in the system.")
|
||||
|
||||
agent_id = agents[0].get("id")
|
||||
if not agent_id:
|
||||
pytest.skip("Agent payload missing id field.")
|
||||
|
||||
import uuid
|
||||
thread_id = str(uuid.uuid4())
|
||||
|
||||
response = await test_client.post(
|
||||
f"/api/chat/agent/{agent_id}/sync",
|
||||
json={
|
||||
"query": "Hello",
|
||||
"config": {"thread_id": thread_id},
|
||||
},
|
||||
headers=admin_headers,
|
||||
)
|
||||
|
||||
assert response.status_code == 200, response.text
|
||||
payload = response.json()
|
||||
|
||||
# 验证 thread_id 是否保持一致
|
||||
if payload["status"] == "finished":
|
||||
assert payload.get("thread_id") == thread_id, f"thread_id mismatch: expected {thread_id}, got {payload.get('thread_id')}"
|
||||
|
||||
|
||||
async def test_chat_agent_sync_with_meta(test_client, admin_headers):
|
||||
"""测试非流式对话传递 meta 参数"""
|
||||
agents_response = await test_client.get("/api/chat/agent", headers=admin_headers)
|
||||
assert agents_response.status_code == 200, agents_response.text
|
||||
agents = agents_response.json().get("agents", [])
|
||||
|
||||
if not agents:
|
||||
pytest.skip("No agents are registered in the system.")
|
||||
|
||||
agent_id = agents[0].get("id")
|
||||
if not agent_id:
|
||||
pytest.skip("Agent payload missing id field.")
|
||||
|
||||
import uuid
|
||||
request_id = str(uuid.uuid4())
|
||||
|
||||
response = await test_client.post(
|
||||
f"/api/chat/agent/{agent_id}/sync",
|
||||
json={
|
||||
"query": "Hello",
|
||||
"meta": {"request_id": request_id},
|
||||
},
|
||||
headers=admin_headers,
|
||||
)
|
||||
|
||||
assert response.status_code == 200, response.text
|
||||
payload = response.json()
|
||||
|
||||
# 验证 request_id 是否保持一致
|
||||
assert payload.get("request_id") == request_id, f"request_id mismatch: expected {request_id}, got {payload.get('request_id')}"
|
||||
|
||||
|
||||
async def test_chat_agent_sync_vs_streaming_consistency(test_client, admin_headers):
|
||||
"""对比测试:非流式与流式端点行为一致性"""
|
||||
# 获取可用智能体
|
||||
agents_response = await test_client.get("/api/chat/agent", headers=admin_headers)
|
||||
assert agents_response.status_code == 200, agents_response.text
|
||||
agents = agents_response.json().get("agents", [])
|
||||
|
||||
if not agents:
|
||||
pytest.skip("No agents are registered in the system.")
|
||||
|
||||
agent_id = agents[0].get("id")
|
||||
if not agent_id:
|
||||
pytest.skip("Agent payload missing id field.")
|
||||
|
||||
query = "What is 1+1?"
|
||||
|
||||
# 调用流式端点
|
||||
import uuid
|
||||
thread_id = str(uuid.uuid4())
|
||||
request_id = str(uuid.uuid4())
|
||||
|
||||
streaming_response = await test_client.post(
|
||||
f"/api/chat/agent/{agent_id}",
|
||||
json={
|
||||
"query": query,
|
||||
"config": {"thread_id": thread_id},
|
||||
"meta": {"request_id": request_id},
|
||||
},
|
||||
headers=admin_headers,
|
||||
)
|
||||
|
||||
assert streaming_response.status_code == 200, streaming_response.text
|
||||
|
||||
# 收集流式响应
|
||||
streaming_content = []
|
||||
async for line in streaming_response.aiter_lines():
|
||||
if line:
|
||||
import json as json_lib
|
||||
try:
|
||||
data = json_lib.loads(line)
|
||||
if data.get("response"):
|
||||
streaming_content.append(data["response"])
|
||||
except:
|
||||
pass
|
||||
|
||||
# 调用非流式端点
|
||||
thread_id2 = str(uuid.uuid4())
|
||||
request_id2 = str(uuid.uuid4())
|
||||
|
||||
sync_response = await test_client.post(
|
||||
f"/api/chat/agent/{agent_id}/sync",
|
||||
json={
|
||||
"query": query,
|
||||
"config": {"thread_id": thread_id2},
|
||||
"meta": {"request_id": request_id2},
|
||||
},
|
||||
headers=admin_headers,
|
||||
)
|
||||
|
||||
assert sync_response.status_code == 200, sync_response.text
|
||||
sync_payload = sync_response.json()
|
||||
|
||||
# 两者都应该成功
|
||||
assert sync_payload["status"] == "finished", f"Sync failed: {sync_payload}"
|
||||
|
||||
# 非流式响应应该有内容
|
||||
assert "response" in sync_payload, f"Missing response in sync payload: {sync_payload}"
|
||||
assert len(streaming_content) > 0, "Streaming should have collected content"
|
||||
@ -1,4 +1,4 @@
|
||||
"""测试 chat_stream_service 中的 interrupt 相关函数"""
|
||||
"""测试 chat_service 中的 interrupt 相关函数"""
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
@ -6,7 +6,7 @@ import os
|
||||
|
||||
sys.path.insert(0, os.getcwd())
|
||||
|
||||
from yuxi.services.chat_stream_service import (
|
||||
from yuxi.services.chat_service import (
|
||||
_normalize_interrupt_options,
|
||||
_normalize_interrupt_questions,
|
||||
_build_ask_user_question_payload,
|
||||
|
||||
@ -5,7 +5,7 @@ from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from yuxi.services import chat_stream_service as chat_svc
|
||||
from yuxi.services import chat_service as chat_svc
|
||||
from yuxi.services import conversation_service as svc
|
||||
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
version = 1
|
||||
revision = 2
|
||||
revision = 3
|
||||
requires-python = ">=3.12, <3.14"
|
||||
resolution-markers = [
|
||||
"(python_full_version >= '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
|
||||
@ -905,8 +905,8 @@ dependencies = [
|
||||
{ name = "scipy" },
|
||||
{ name = "torch", version = "2.8.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
|
||||
{ name = "torch", version = "2.8.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" },
|
||||
{ name = "torchvision", version = "0.23.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or sys_platform == 'darwin'" },
|
||||
{ name = "torchvision", version = "0.23.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
{ name = "torchvision", version = "0.23.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or sys_platform == 'darwin'" },
|
||||
{ name = "torchvision", version = "0.23.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (platform_python_implementation != 'CPython' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
{ name = "tqdm" },
|
||||
{ name = "typer" },
|
||||
]
|
||||
@ -964,8 +964,8 @@ dependencies = [
|
||||
{ name = "safetensors", extra = ["torch"] },
|
||||
{ name = "torch", version = "2.8.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
|
||||
{ name = "torch", version = "2.8.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" },
|
||||
{ name = "torchvision", version = "0.23.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or sys_platform == 'darwin'" },
|
||||
{ name = "torchvision", version = "0.23.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
{ name = "torchvision", version = "0.23.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or sys_platform == 'darwin'" },
|
||||
{ name = "torchvision", version = "0.23.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (platform_python_implementation != 'CPython' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
{ name = "tqdm" },
|
||||
{ name = "transformers" },
|
||||
]
|
||||
@ -1272,6 +1272,7 @@ dependencies = [
|
||||
{ name = "griffecli" },
|
||||
{ name = "griffelib" },
|
||||
]
|
||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/56/28a0accac339c164b52a92c6cfc45a903acc0c174caa5c1713803467b533/griffe-2.0.0.tar.gz", hash = "sha256:c68979cd8395422083a51ea7cf02f9c119d889646d99b7b656ee43725de1b80f", size = 293906, upload-time = "2026-03-23T21:06:53.402Z" }
|
||||
wheels = [
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/8b/94/ee21d41e7eb4f823b94603b9d40f86d3c7fde80eacc2c3c71845476dddaa/griffe-2.0.0-py3-none-any.whl", hash = "sha256:5418081135a391c3e6e757a7f3f156f1a1a746cc7b4023868ff7d5e2f9a980aa", size = 5214, upload-time = "2026-02-09T19:09:44.105Z" },
|
||||
]
|
||||
@ -1284,6 +1285,7 @@ dependencies = [
|
||||
{ name = "colorama" },
|
||||
{ name = "griffelib" },
|
||||
]
|
||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/f8/2e129fd4a86e52e58eefe664de05e7d502decf766e7316cc9e70fdec3e18/griffecli-2.0.0.tar.gz", hash = "sha256:312fa5ebb4ce6afc786356e2d0ce85b06c1c20d45abc42d74f0cda65e159f6ef", size = 56213, upload-time = "2026-03-23T21:06:54.8Z" }
|
||||
wheels = [
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/ed/d93f7a447bbf7a935d8868e9617cbe1cadf9ee9ee6bd275d3040fbf93d60/griffecli-2.0.0-py3-none-any.whl", hash = "sha256:9f7cd9ee9b21d55e91689358978d2385ae65c22f307a63fb3269acf3f21e643d", size = 9345, upload-time = "2026-02-09T19:09:42.554Z" },
|
||||
]
|
||||
@ -1292,6 +1294,7 @@ wheels = [
|
||||
name = "griffelib"
|
||||
version = "2.0.0"
|
||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ad/06/eccbd311c9e2b3ca45dbc063b93134c57a1ccc7607c5e545264ad092c4a9/griffelib-2.0.0.tar.gz", hash = "sha256:e504d637a089f5cab9b5daf18f7645970509bf4f53eda8d79ed71cce8bd97934", size = 166312, upload-time = "2026-03-23T21:06:55.954Z" }
|
||||
wheels = [
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl", hash = "sha256:01284878c966508b6d6f1dbff9b6fa607bc062d8261c5c7253cb285b06422a7f", size = 142004, upload-time = "2026-02-09T19:09:40.561Z" },
|
||||
]
|
||||
@ -4974,18 +4977,16 @@ name = "torchvision"
|
||||
version = "0.23.0"
|
||||
source = { registry = "https://download.pytorch.org/whl/cpu" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_python_implementation != 'CPython' and sys_platform == 'linux'",
|
||||
"python_full_version < '3.13' and platform_machine == 'aarch64' and platform_python_implementation != 'CPython' and sys_platform == 'linux'",
|
||||
"python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'linux'",
|
||||
"python_full_version < '3.13' and platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'linux'",
|
||||
"python_full_version >= '3.13' and sys_platform == 'darwin'",
|
||||
"python_full_version < '3.13' and sys_platform == 'darwin'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "numpy", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or sys_platform == 'darwin'" },
|
||||
{ name = "pillow", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or sys_platform == 'darwin'" },
|
||||
{ name = "numpy", marker = "(platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or sys_platform == 'darwin'" },
|
||||
{ name = "pillow", marker = "(platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or sys_platform == 'darwin'" },
|
||||
{ name = "torch", version = "2.8.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
|
||||
{ name = "torch", version = "2.8.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "platform_machine == 'aarch64' and sys_platform == 'linux'" },
|
||||
{ name = "torch", version = "2.8.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'linux'" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torchvision-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e0e2c04a91403e8dd3af9756c6a024a1d9c0ed9c0d592a8314ded8f4fe30d440" },
|
||||
@ -5002,12 +5003,14 @@ version = "0.23.0+cpu"
|
||||
source = { registry = "https://download.pytorch.org/whl/cpu" }
|
||||
resolution-markers = [
|
||||
"(python_full_version >= '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
|
||||
"python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_python_implementation != 'CPython' and sys_platform == 'linux'",
|
||||
"(python_full_version < '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')",
|
||||
"python_full_version < '3.13' and platform_machine == 'aarch64' and platform_python_implementation != 'CPython' and sys_platform == 'linux'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "numpy", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
{ name = "pillow", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
{ name = "torch", version = "2.8.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
{ name = "numpy", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (platform_python_implementation != 'CPython' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
{ name = "pillow", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (platform_python_implementation != 'CPython' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
{ name = "torch", version = "2.8.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (platform_python_implementation != 'CPython' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://download-r2.pytorch.org/whl/cpu/torchvision-0.23.0%2Bcpu-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ae459d4509d3b837b978dc6c66106601f916b6d2cda75c137e3f5f48324ce1da" },
|
||||
@ -5673,12 +5676,12 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "yuxi"
|
||||
version = "0.6.0.dev0"
|
||||
version = "0.6.0.dev2"
|
||||
source = { virtual = "package/yuxi" }
|
||||
|
||||
[[package]]
|
||||
name = "yuxi-workspace"
|
||||
version = "0.6.0.dev0"
|
||||
version = "0.6.0.dev2"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "agent-sandbox" },
|
||||
@ -5744,8 +5747,8 @@ dependencies = [
|
||||
{ name = "tomli-w" },
|
||||
{ name = "torch", version = "2.8.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform == 'darwin'" },
|
||||
{ name = "torch", version = "2.8.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "sys_platform != 'darwin'" },
|
||||
{ name = "torchvision", version = "0.23.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or sys_platform == 'darwin'" },
|
||||
{ name = "torchvision", version = "0.23.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
{ name = "torchvision", version = "0.23.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or sys_platform == 'darwin'" },
|
||||
{ name = "torchvision", version = "0.23.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (platform_python_implementation != 'CPython' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
{ name = "tqdm" },
|
||||
{ name = "typer" },
|
||||
{ name = "unstructured" },
|
||||
|
||||
@ -174,7 +174,7 @@ Context 的价值不只在“配置页面”。它贯穿了从配置加载到实
|
||||
|
||||
在聊天请求进入后端时,服务会先解析 `agent_config_id`,再加载对应配置。
|
||||
|
||||
当前主流程在 `chat_stream_service.py` 中:
|
||||
当前主流程在 `chat_service.py` 中:
|
||||
|
||||
1. 通过 `agent_config_id` 查找配置
|
||||
2. 若未指定,则获取该部门下该 Agent 的默认配置
|
||||
|
||||
Loading…
Reference in New Issue
Block a user