|
@@ -15,10 +15,7 @@ Trace 控制 API — 新建 / 运行 / 停止 / 反思
|
|
|
|
|
|
|
|
import asyncio
|
|
import asyncio
|
|
|
import logging
|
|
import logging
|
|
|
-import re
|
|
|
|
|
-import uuid
|
|
|
|
|
import os
|
|
import os
|
|
|
-from datetime import datetime
|
|
|
|
|
from typing import Any, Dict, List, Optional
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
|
|
|
|
from fastapi import APIRouter, HTTPException
|
|
from fastapi import APIRouter, HTTPException
|
|
@@ -35,6 +32,7 @@ experiences_router = APIRouter(prefix="/api", tags=["experiences"])
|
|
|
# ===== 全局 Runner(由 api_server.py 注入)=====
|
|
# ===== 全局 Runner(由 api_server.py 注入)=====
|
|
|
|
|
|
|
|
_runner = None
|
|
_runner = None
|
|
|
|
|
+_project_environment_resolver = None
|
|
|
|
|
|
|
|
|
|
|
|
|
def set_runner(runner):
|
|
def set_runner(runner):
|
|
@@ -43,6 +41,12 @@ def set_runner(runner):
|
|
|
_runner = runner
|
|
_runner = runner
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+def set_project_environment_resolver(resolver):
|
|
|
|
|
+ """Inject optional Host adapter; the framework never imports projects."""
|
|
|
|
|
+ global _project_environment_resolver
|
|
|
|
|
+ _project_environment_resolver = resolver
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
def _get_runner():
|
|
def _get_runner():
|
|
|
if _runner is None:
|
|
if _runner is None:
|
|
|
raise HTTPException(
|
|
raise HTTPException(
|
|
@@ -174,6 +178,11 @@ async def _run_in_background(trace_id: str, messages: List[Dict], config, runner
|
|
|
_running_tasks.pop(trace_id, None)
|
|
_running_tasks.pop(trace_id, None)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+async def _run_trace_background(runner, messages: List[Dict], config):
|
|
|
|
|
+ """Compatibility wrapper used by reflect/compact background endpoints."""
|
|
|
|
|
+ await _run_in_background(config.trace_id, messages, config, runner_instance=runner)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
async def _run_with_trace_signal(
|
|
async def _run_with_trace_signal(
|
|
|
messages: List[Dict], config, trace_id_future: asyncio.Future, runner_instance=None
|
|
messages: List[Dict], config, trace_id_future: asyncio.Future, runner_instance=None
|
|
|
):
|
|
):
|
|
@@ -207,7 +216,6 @@ async def create_and_run(req: CreateRequest):
|
|
|
立即返回 trace_id,后台异步执行。
|
|
立即返回 trace_id,后台异步执行。
|
|
|
通过 WebSocket /api/traces/{trace_id}/watch 监听实时更新。
|
|
通过 WebSocket /api/traces/{trace_id}/watch 监听实时更新。
|
|
|
"""
|
|
"""
|
|
|
- import importlib
|
|
|
|
|
from agent.core.runner import RunConfig
|
|
from agent.core.runner import RunConfig
|
|
|
|
|
|
|
|
runner = None
|
|
runner = None
|
|
@@ -216,13 +224,14 @@ async def create_and_run(req: CreateRequest):
|
|
|
|
|
|
|
|
if req.project_name:
|
|
if req.project_name:
|
|
|
try:
|
|
try:
|
|
|
- # 动态加载对应 example 的 run.py
|
|
|
|
|
- module_name = f"examples.{req.project_name}.run"
|
|
|
|
|
- example_module = importlib.import_module(module_name)
|
|
|
|
|
- if hasattr(example_module, "init_project_env"):
|
|
|
|
|
|
|
+ environment = None
|
|
|
|
|
+ if _project_environment_resolver:
|
|
|
|
|
+ environment = await _project_environment_resolver.resolve(req.project_name, req.messages)
|
|
|
|
|
+ if environment:
|
|
|
# 获取该 example 专属的 runner, 带上下文 messages, 以及默认 config
|
|
# 获取该 example 专属的 runner, 带上下文 messages, 以及默认 config
|
|
|
- runner, example_messages, default_config = await example_module.init_project_env(req.messages)
|
|
|
|
|
- messages = example_messages
|
|
|
|
|
|
|
+ runner = environment.runner
|
|
|
|
|
+ default_config = environment.default_config
|
|
|
|
|
+ messages = environment.messages
|
|
|
|
|
|
|
|
# 合并请求配置和 example 默认配置
|
|
# 合并请求配置和 example 默认配置
|
|
|
config = RunConfig(
|
|
config = RunConfig(
|
|
@@ -235,13 +244,7 @@ async def create_and_run(req: CreateRequest):
|
|
|
enable_research_flow=default_config.enable_research_flow,
|
|
enable_research_flow=default_config.enable_research_flow,
|
|
|
context={"project_name": req.project_name}
|
|
context={"project_name": req.project_name}
|
|
|
)
|
|
)
|
|
|
- except ImportError as e:
|
|
|
|
|
- if getattr(e, "name", None) == module_name:
|
|
|
|
|
- logger.warning(f"Project '{req.project_name}' has no custom run.py, falling back to default.")
|
|
|
|
|
- else:
|
|
|
|
|
- import traceback
|
|
|
|
|
- logger.error(f"Error INSIDE {module_name}:\n{traceback.format_exc()}")
|
|
|
|
|
- except Exception as e:
|
|
|
|
|
|
|
+ except Exception:
|
|
|
import traceback
|
|
import traceback
|
|
|
logger.error(f"Unexpected error loading project environment for {req.project_name}:\n{traceback.format_exc()}")
|
|
logger.error(f"Unexpected error loading project environment for {req.project_name}:\n{traceback.format_exc()}")
|
|
|
|
|
|
|
@@ -387,7 +390,6 @@ async def run_trace(trace_id: str, req: TraceRunRequest):
|
|
|
但缺少对应的 tool responses),框架会自动检测并调整插入位置,确保不会产生不一致的状态。
|
|
但缺少对应的 tool responses),框架会自动检测并调整插入位置,确保不会产生不一致的状态。
|
|
|
"""
|
|
"""
|
|
|
from agent.core.runner import RunConfig
|
|
from agent.core.runner import RunConfig
|
|
|
- import importlib
|
|
|
|
|
|
|
|
|
|
runner = _get_runner()
|
|
runner = _get_runner()
|
|
|
|
|
|
|
@@ -437,19 +439,13 @@ async def run_trace(trace_id: str, req: TraceRunRequest):
|
|
|
|
|
|
|
|
if project_name:
|
|
if project_name:
|
|
|
try:
|
|
try:
|
|
|
- module_name = f"examples.{project_name}.run"
|
|
|
|
|
- example_module = importlib.import_module(module_name)
|
|
|
|
|
- if hasattr(example_module, "init_project_env"):
|
|
|
|
|
|
|
+ environment = None
|
|
|
|
|
+ if _project_environment_resolver:
|
|
|
|
|
+ environment = await _project_environment_resolver.resolve(project_name)
|
|
|
|
|
+ if environment:
|
|
|
logger.info(f"Trace {trace_id} 绑定了项目 {project_name},动态加载执行环境...")
|
|
logger.info(f"Trace {trace_id} 绑定了项目 {project_name},动态加载执行环境...")
|
|
|
- project_runner, project_msgs, default_config = await example_module.init_project_env()
|
|
|
|
|
- runner = project_runner # 发生替换
|
|
|
|
|
- except ImportError as e:
|
|
|
|
|
- if getattr(e, "name", None) == module_name:
|
|
|
|
|
- logger.warning(f"Project '{project_name}' has no custom run.py, keeping default runner.")
|
|
|
|
|
- else:
|
|
|
|
|
- import traceback
|
|
|
|
|
- logger.error(f"Error INSIDE {module_name} during resume:\n{traceback.format_exc()}")
|
|
|
|
|
- except Exception as e:
|
|
|
|
|
|
|
+ runner = environment.runner
|
|
|
|
|
+ except Exception:
|
|
|
import traceback
|
|
import traceback
|
|
|
logger.error(f"Unexpected error loading run.py environment for project {project_name} in trace {trace_id}:\n{traceback.format_exc()}")
|
|
logger.error(f"Unexpected error loading run.py environment for project {project_name} in trace {trace_id}:\n{traceback.format_exc()}")
|
|
|
|
|
|
|
@@ -682,7 +678,7 @@ async def compact_trace(trace_id: str):
|
|
|
@router.get("/running", tags=["run"])
|
|
@router.get("/running", tags=["run"])
|
|
|
async def list_running():
|
|
async def list_running():
|
|
|
"""列出正在运行的 Trace(包含活跃状态判断)"""
|
|
"""列出正在运行的 Trace(包含活跃状态判断)"""
|
|
|
- from datetime import datetime, timedelta
|
|
|
|
|
|
|
+ from datetime import datetime
|
|
|
|
|
|
|
|
runner = _get_runner()
|
|
runner = _get_runner()
|
|
|
running = []
|
|
running = []
|