feat: Add evaluation hooks, skill adaptation and team pipeline config

- Add EvaluationHook for post-execution agent evaluation
- Add SkillAdaptationHook for dynamic skill adaptation
- Add team/ directory with team coordination logic
- Add TEAM_PIPELINE.yaml for smoke_fullstack pipeline config
- Update RuntimeView, TraderView and RuntimeSettingsPanel UI
- Add runtimeApi and websocket services
- Add runtime_state.json to smoke_fullstack state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-19 18:52:12 +08:00
parent f4a2b7f3af
commit 4b5ac86b83
87 changed files with 5042 additions and 744 deletions

View File

@@ -8,6 +8,7 @@ and frontend development server.
"""
# flake8: noqa: E501
# pylint: disable=R0912, R0915
import logging
import os
import shutil
import subprocess
@@ -17,7 +18,10 @@ from pathlib import Path
from typing import Optional
from zoneinfo import ZoneInfo
logger = logging.getLogger(__name__)
import typer
import yaml
from rich.console import Console
from rich.panel import Panel
from rich.prompt import Confirm
@@ -27,7 +31,12 @@ from dotenv import load_dotenv
from backend.agents.agent_workspace import load_agent_workspace_config
from backend.agents.prompt_loader import PromptLoader
from backend.agents.skills_manager import SkillsManager
from backend.agents.team_pipeline_config import (
ensure_team_pipeline_config,
load_team_pipeline_config,
)
from backend.agents.workspace_manager import WorkspaceManager
from backend.config.constants import ANALYST_TYPES
from backend.data.market_ingest import ingest_symbols
from backend.data.market_store import MarketStore
from backend.enrich.llm_enricher import get_explain_model_info, llm_enrichment_enabled
@@ -42,6 +51,8 @@ ingest_app = typer.Typer(help="Ingest Polygon market data into the research ware
app.add_typer(ingest_app, name="ingest")
skills_app = typer.Typer(help="Inspect and manage per-agent skills.")
app.add_typer(skills_app, name="skills")
team_app = typer.Typer(help="Inspect and manage run-scoped team pipeline config.")
app.add_typer(team_app, name="team")
console = Console()
_prompt_loader = PromptLoader()
@@ -95,8 +106,8 @@ def handle_history_cleanup(config_name: str, auto_clean: bool = False) -> None:
)
else:
console.print(f" Directory size: [cyan]{size_mb:.1f} MB[/cyan]")
except Exception:
pass
except Exception as e:
logger.debug(f"Could not calculate directory size: {e}")
# Show last modified time
state_dir = base_data_dir / "state"
@@ -197,7 +208,8 @@ def run_data_updater(project_root: Path) -> None:
console.print(
"[yellow] Data updater module not available, skipping update[/yellow]\n",
)
except Exception:
except Exception as e:
logger.debug(f"Data updater check failed: {e}")
console.print(
"[yellow] Data updater check failed, skipping update[/yellow]\n",
)
@@ -777,6 +789,78 @@ def skills_disable(
console.print(f"Disabled skills: {', '.join(result['disabled_skills']) or '-'}")
@skills_app.command("install")
def skills_install(
agent_id: str = typer.Option(..., "--agent-id", "-a", help="Target agent id."),
source: str = typer.Option(
...,
"--source",
"-s",
help="External skill source: directory path, zip path, or http(s) zip URL.",
),
config_name: str = typer.Option(
"default",
"--config-name",
"-c",
help="Run config name.",
),
name: Optional[str] = typer.Option(
None,
"--name",
help="Optional override skill name.",
),
activate: bool = typer.Option(
True,
"--activate/--no-activate",
help="Enable the skill for this agent immediately.",
),
):
"""Install an external skill into one agent's local skill directory."""
_require_agent_asset_dir(config_name, agent_id)
skills_manager = SkillsManager(project_root=get_project_root())
result = skills_manager.install_external_skill_for_agent(
config_name=config_name,
agent_id=agent_id,
source=source,
skill_name=name,
activate=activate,
)
console.print(
f"[green]Installed[/green] `{result['skill_name']}` to `{agent_id}`",
)
console.print(f"Path: {result['target_dir']}")
console.print(f"Activated: {result['activated']}")
warnings = result.get("warnings") or []
if warnings:
console.print(f"Warnings: {'; '.join(warnings)}")
@team_app.command("show")
def team_show(
config_name: str = typer.Option(
"default",
"--config-name",
"-c",
help="Run config name.",
),
):
"""Show TEAM_PIPELINE.yaml for one run."""
project_root = get_project_root()
ensure_team_pipeline_config(
project_root=project_root,
config_name=config_name,
default_analysts=list(ANALYST_TYPES.keys()),
)
config = load_team_pipeline_config(project_root, config_name)
console.print(
Panel.fit(
yaml.safe_dump(config, allow_unicode=True, sort_keys=False),
title=f"TEAM_PIPELINE ({config_name})",
border_style="cyan",
),
)
@app.command()
def backtest(
start: Optional[str] = typer.Option(