refactor(cleanup): remove legacy runtime directories and fix API semantics
Task 1: Clean up root-level runtime directories
- Backup live/, backtest/, production/ to runs/_legacy/
- Remove legacy directories from repo root
Task 2: API route semantics cleanup
- Create /api/runs/{run_id}/agents/* routes for runtime agent operations
- Keep /api/workspaces/{id}/agents/* for design-time (deprecated)
- Update frontend runtimeApi.js to use new /runs/ prefix
- Update legacy-inventory.md with completion status
New files:
- backend/api/runs.py - Runtime agent routes with proper run_id semantics
Modified:
- backend/api/__init__.py - Export runs_router
- backend/apps/agent_service.py - Include runs_router, update scope docs
- frontend/src/services/runtimeApi.js - Use /runs/ instead of /workspaces/
- docs/legacy-inventory.md - Mark cleanup as completed
Constraint: Maintain backward compatibility with old /workspaces/ routes
Rejected: Remove old routes entirely | need backward compatibility during transition
Confidence: high
Scope-risk: moderate
Directive: Old /api/workspaces/ routes remain functional but deprecated
Not-tested: Full integration test with active runtime
This commit is contained in:
@@ -13,6 +13,7 @@ from .workspaces import router as workspaces_router
|
||||
from .guard import router as guard_router
|
||||
from .openclaw import router as openclaw_router
|
||||
from .runtime import router as runtime_router
|
||||
from .runs import router as runs_router
|
||||
|
||||
__all__ = [
|
||||
"agents_router",
|
||||
@@ -20,4 +21,5 @@ __all__ = [
|
||||
"guard_router",
|
||||
"openclaw_router",
|
||||
"runtime_router",
|
||||
"runs_router",
|
||||
]
|
||||
|
||||
547
backend/api/runs.py
Normal file
547
backend/api/runs.py
Normal file
@@ -0,0 +1,547 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Run-scoped Agent API Routes
|
||||
|
||||
Provides REST API endpoints for runtime agent asset access under `runs/<run_id>/`.
|
||||
|
||||
This module separates runtime concerns from design-time workspace management:
|
||||
- `/api/runs/{run_id}/agents/*` - Runtime agent assets and configuration
|
||||
- `/api/workspaces/{workspace_id}/agents/*` - Design-time workspace registry (deprecated)
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Depends, Body, UploadFile, File, Form
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from backend.agents.workspace_manager import RunWorkspaceManager
|
||||
from backend.agents.agent_workspace import load_agent_workspace_config
|
||||
from backend.agents.skills_manager import SkillsManager
|
||||
from backend.agents.toolkit_factory import load_agent_profiles
|
||||
from backend.config.bootstrap_config import get_bootstrap_config_for_run
|
||||
from backend.llm.models import get_agent_model_info
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/api/runs/{run_id}/agents", tags=["runs"])
|
||||
|
||||
|
||||
# Request/Response Models
|
||||
class InstallExternalSkillRequest(BaseModel):
|
||||
"""Request to install an external skill for one agent."""
|
||||
source: str = Field(..., description="Directory path, zip path, or http(s) zip URL")
|
||||
name: Optional[str] = Field(None, description="Optional override skill name")
|
||||
activate: bool = Field(True, description="Whether to enable skill immediately")
|
||||
|
||||
|
||||
class LocalSkillRequest(BaseModel):
|
||||
skill_name: str = Field(..., description="Local skill name")
|
||||
|
||||
|
||||
class LocalSkillContentRequest(BaseModel):
|
||||
content: str = Field(..., description="Updated SKILL.md content")
|
||||
|
||||
|
||||
class AgentFileResponse(BaseModel):
|
||||
"""Agent file content response."""
|
||||
filename: str
|
||||
content: str
|
||||
scope_type: str = "runtime_run"
|
||||
scope_note: Optional[str] = None
|
||||
|
||||
|
||||
class AgentProfileResponse(BaseModel):
|
||||
agent_id: str
|
||||
run_id: str
|
||||
profile: Dict[str, Any]
|
||||
scope_type: str = "runtime_run"
|
||||
scope_note: Optional[str] = None
|
||||
|
||||
|
||||
class AgentSkillsResponse(BaseModel):
|
||||
agent_id: str
|
||||
run_id: str
|
||||
skills: List[Dict[str, Any]]
|
||||
scope_type: str = "runtime_run"
|
||||
scope_note: Optional[str] = None
|
||||
|
||||
|
||||
class SkillDetailResponse(BaseModel):
|
||||
agent_id: str
|
||||
run_id: str
|
||||
skill: Dict[str, Any]
|
||||
scope_type: str = "runtime_run"
|
||||
scope_note: Optional[str] = None
|
||||
|
||||
|
||||
# Dependencies
|
||||
def get_workspace_manager():
|
||||
"""Get run-scoped asset manager for one runtime workspace/run id."""
|
||||
return RunWorkspaceManager()
|
||||
|
||||
|
||||
def get_skills_manager():
|
||||
"""Get SkillsManager instance."""
|
||||
return SkillsManager()
|
||||
|
||||
|
||||
# Runtime Routes
|
||||
@router.get("/{agent_id}/profile", response_model=AgentProfileResponse)
|
||||
async def get_agent_profile(
|
||||
run_id: str,
|
||||
agent_id: str,
|
||||
skills_manager: SkillsManager = Depends(get_skills_manager),
|
||||
):
|
||||
"""
|
||||
Get agent profile from runtime assets under `runs/<run_id>/`.
|
||||
|
||||
Args:
|
||||
run_id: Run identifier (e.g., "smoke_fullstack")
|
||||
agent_id: Agent identifier
|
||||
|
||||
Returns:
|
||||
Agent profile with model config, skills, and tool groups
|
||||
"""
|
||||
asset_dir = skills_manager.get_agent_asset_dir(run_id, agent_id)
|
||||
agent_config = load_agent_workspace_config(asset_dir / "agent.yaml")
|
||||
profiles = load_agent_profiles()
|
||||
profile = profiles.get(agent_id, {})
|
||||
bootstrap = get_bootstrap_config_for_run(skills_manager.project_root, run_id)
|
||||
override = bootstrap.agent_override(agent_id)
|
||||
active_tool_groups = override.get("active_tool_groups", agent_config.active_tool_groups or profile.get("active_tool_groups", []))
|
||||
if not isinstance(active_tool_groups, list):
|
||||
active_tool_groups = []
|
||||
disabled_tool_groups = agent_config.disabled_tool_groups
|
||||
if disabled_tool_groups:
|
||||
disabled_set = set(disabled_tool_groups)
|
||||
active_tool_groups = [group_name for group_name in active_tool_groups if group_name not in disabled_set]
|
||||
|
||||
default_skills = profile.get("skills", [])
|
||||
if not isinstance(default_skills, list):
|
||||
default_skills = []
|
||||
resolved_skills = skills_manager.resolve_agent_skill_names(
|
||||
config_name=run_id,
|
||||
agent_id=agent_id,
|
||||
default_skills=default_skills,
|
||||
)
|
||||
prompt_files = agent_config.prompt_files or ["SOUL.md", "PROFILE.md", "AGENTS.md", "POLICY.md", "MEMORY.md"]
|
||||
model_name, model_provider = get_agent_model_info(agent_id)
|
||||
|
||||
return AgentProfileResponse(
|
||||
agent_id=agent_id,
|
||||
run_id=run_id,
|
||||
profile={
|
||||
"model_name": model_name,
|
||||
"model_provider": model_provider,
|
||||
"prompt_files": prompt_files,
|
||||
"default_skills": default_skills,
|
||||
"resolved_skills": resolved_skills,
|
||||
"active_tool_groups": active_tool_groups,
|
||||
"disabled_tool_groups": disabled_tool_groups,
|
||||
"enabled_skills": agent_config.enabled_skills,
|
||||
"disabled_skills": agent_config.disabled_skills,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{agent_id}/skills", response_model=AgentSkillsResponse)
|
||||
async def get_agent_skills(
|
||||
run_id: str,
|
||||
agent_id: str,
|
||||
skills_manager: SkillsManager = Depends(get_skills_manager),
|
||||
):
|
||||
"""
|
||||
Get agent skills from runtime assets under `runs/<run_id>/`.
|
||||
|
||||
Args:
|
||||
run_id: Run identifier
|
||||
agent_id: Agent identifier
|
||||
|
||||
Returns:
|
||||
List of skills with their status (active/enabled/disabled/available)
|
||||
"""
|
||||
agent_asset_dir = skills_manager.get_agent_asset_dir(run_id, agent_id)
|
||||
agent_config = load_agent_workspace_config(agent_asset_dir / "agent.yaml")
|
||||
resolved_skills = set(skills_manager.resolve_agent_skill_names(config_name=run_id, agent_id=agent_id, default_skills=[]))
|
||||
enabled = set(agent_config.enabled_skills)
|
||||
disabled = set(agent_config.disabled_skills)
|
||||
|
||||
payload = []
|
||||
for item in skills_manager.list_agent_skill_catalog(run_id, agent_id):
|
||||
if item.skill_name in disabled:
|
||||
status = "disabled"
|
||||
elif item.skill_name in enabled:
|
||||
status = "enabled"
|
||||
elif item.skill_name in resolved_skills:
|
||||
status = "active"
|
||||
else:
|
||||
status = "available"
|
||||
payload.append({
|
||||
"skill_name": item.skill_name,
|
||||
"name": item.name,
|
||||
"description": item.description,
|
||||
"version": item.version,
|
||||
"source": item.source,
|
||||
"tools": item.tools,
|
||||
"status": status,
|
||||
})
|
||||
|
||||
return AgentSkillsResponse(
|
||||
agent_id=agent_id,
|
||||
run_id=run_id,
|
||||
skills=payload,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{agent_id}/skills/{skill_name}", response_model=SkillDetailResponse)
|
||||
async def get_agent_skill_detail(
|
||||
run_id: str,
|
||||
agent_id: str,
|
||||
skill_name: str,
|
||||
skills_manager: SkillsManager = Depends(get_skills_manager),
|
||||
):
|
||||
"""
|
||||
Get detailed skill information from runtime assets.
|
||||
|
||||
Args:
|
||||
run_id: Run identifier
|
||||
agent_id: Agent identifier
|
||||
skill_name: Skill name
|
||||
|
||||
Returns:
|
||||
Skill detail information
|
||||
"""
|
||||
try:
|
||||
detail = skills_manager.load_agent_skill_document(
|
||||
config_name=run_id,
|
||||
agent_id=agent_id,
|
||||
skill_name=skill_name,
|
||||
)
|
||||
except FileNotFoundError:
|
||||
raise HTTPException(status_code=404, detail=f"Unknown skill: {skill_name}")
|
||||
|
||||
return SkillDetailResponse(
|
||||
agent_id=agent_id,
|
||||
run_id=run_id,
|
||||
skill=detail,
|
||||
)
|
||||
|
||||
|
||||
@router.post("/{agent_id}/skills/{skill_name}/enable")
|
||||
async def enable_skill(
|
||||
run_id: str,
|
||||
agent_id: str,
|
||||
skill_name: str,
|
||||
skills_manager: SkillsManager = Depends(get_skills_manager),
|
||||
):
|
||||
"""
|
||||
Enable a skill for an agent in runtime assets.
|
||||
|
||||
Args:
|
||||
run_id: Run identifier
|
||||
agent_id: Agent identifier
|
||||
skill_name: Skill name to enable
|
||||
|
||||
Returns:
|
||||
Success message with updated enabled skills list
|
||||
"""
|
||||
result = skills_manager.update_agent_skill_overrides(
|
||||
config_name=run_id,
|
||||
agent_id=agent_id,
|
||||
enable=[skill_name],
|
||||
)
|
||||
|
||||
return {
|
||||
"message": f"Skill '{skill_name}' enabled for agent '{agent_id}'",
|
||||
"enabled_skills": result["enabled_skills"],
|
||||
}
|
||||
|
||||
|
||||
@router.post("/{agent_id}/skills/{skill_name}/disable")
|
||||
async def disable_skill(
|
||||
run_id: str,
|
||||
agent_id: str,
|
||||
skill_name: str,
|
||||
skills_manager: SkillsManager = Depends(get_skills_manager),
|
||||
):
|
||||
"""
|
||||
Disable a skill for an agent in runtime assets.
|
||||
|
||||
Args:
|
||||
run_id: Run identifier
|
||||
agent_id: Agent identifier
|
||||
skill_name: Skill name to disable
|
||||
|
||||
Returns:
|
||||
Success message with updated disabled skills list
|
||||
"""
|
||||
result = skills_manager.update_agent_skill_overrides(
|
||||
config_name=run_id,
|
||||
agent_id=agent_id,
|
||||
disable=[skill_name],
|
||||
)
|
||||
|
||||
return {
|
||||
"message": f"Skill '{skill_name}' disabled for agent '{agent_id}'",
|
||||
"disabled_skills": result["disabled_skills"],
|
||||
}
|
||||
|
||||
|
||||
@router.post("/{agent_id}/skills/install")
|
||||
async def install_external_skill(
|
||||
run_id: str,
|
||||
agent_id: str,
|
||||
request: InstallExternalSkillRequest,
|
||||
skills_manager: SkillsManager = Depends(get_skills_manager),
|
||||
):
|
||||
"""
|
||||
Install an external skill into one agent's local skills under `runs/<run_id>/`.
|
||||
|
||||
Args:
|
||||
run_id: Run identifier
|
||||
agent_id: Agent identifier
|
||||
request: Installation parameters
|
||||
|
||||
Returns:
|
||||
Success message with installed skill details
|
||||
"""
|
||||
try:
|
||||
result = skills_manager.install_external_skill_for_agent(
|
||||
config_name=run_id,
|
||||
agent_id=agent_id,
|
||||
source=request.source,
|
||||
skill_name=request.name,
|
||||
activate=request.activate,
|
||||
)
|
||||
except (FileNotFoundError, ValueError) as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc))
|
||||
|
||||
return {
|
||||
"message": f"Installed external skill '{result['skill_name']}' for '{agent_id}'",
|
||||
**result,
|
||||
}
|
||||
|
||||
|
||||
@router.post("/{agent_id}/skills/local")
|
||||
async def create_local_skill(
|
||||
run_id: str,
|
||||
agent_id: str,
|
||||
request: LocalSkillRequest,
|
||||
skills_manager: SkillsManager = Depends(get_skills_manager),
|
||||
):
|
||||
"""
|
||||
Create a new local skill for an agent under `runs/<run_id>/`.
|
||||
|
||||
Args:
|
||||
run_id: Run identifier
|
||||
agent_id: Agent identifier
|
||||
request: Local skill creation parameters
|
||||
|
||||
Returns:
|
||||
Success message
|
||||
"""
|
||||
try:
|
||||
skills_manager.create_agent_local_skill(
|
||||
config_name=run_id,
|
||||
agent_id=agent_id,
|
||||
skill_name=request.skill_name,
|
||||
)
|
||||
except (ValueError, FileExistsError) as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc))
|
||||
|
||||
return {"message": f"Created local skill '{request.skill_name}' for '{agent_id}'"}
|
||||
|
||||
|
||||
@router.put("/{agent_id}/skills/local/{skill_name}")
|
||||
async def update_local_skill(
|
||||
run_id: str,
|
||||
agent_id: str,
|
||||
skill_name: str,
|
||||
request: LocalSkillContentRequest,
|
||||
skills_manager: SkillsManager = Depends(get_skills_manager),
|
||||
):
|
||||
"""
|
||||
Update a local skill's SKILL.md content under `runs/<run_id>/`.
|
||||
|
||||
Args:
|
||||
run_id: Run identifier
|
||||
agent_id: Agent identifier
|
||||
skill_name: Skill name
|
||||
request: Updated content
|
||||
|
||||
Returns:
|
||||
Success message
|
||||
"""
|
||||
try:
|
||||
skills_manager.update_agent_local_skill(
|
||||
config_name=run_id,
|
||||
agent_id=agent_id,
|
||||
skill_name=skill_name,
|
||||
content=request.content,
|
||||
)
|
||||
except (ValueError, FileNotFoundError) as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc))
|
||||
|
||||
return {"message": f"Updated local skill '{skill_name}' for '{agent_id}'"}
|
||||
|
||||
|
||||
@router.delete("/{agent_id}/skills/local/{skill_name}")
|
||||
async def delete_local_skill(
|
||||
run_id: str,
|
||||
agent_id: str,
|
||||
skill_name: str,
|
||||
skills_manager: SkillsManager = Depends(get_skills_manager),
|
||||
):
|
||||
"""
|
||||
Delete a local skill under `runs/<run_id>/`.
|
||||
|
||||
Args:
|
||||
run_id: Run identifier
|
||||
agent_id: Agent identifier
|
||||
skill_name: Skill name to delete
|
||||
|
||||
Returns:
|
||||
Success message
|
||||
"""
|
||||
try:
|
||||
skills_manager.delete_agent_local_skill(
|
||||
config_name=run_id,
|
||||
agent_id=agent_id,
|
||||
skill_name=skill_name,
|
||||
)
|
||||
skills_manager.forget_agent_skill_overrides(
|
||||
config_name=run_id,
|
||||
agent_id=agent_id,
|
||||
skill_names=[skill_name],
|
||||
)
|
||||
except (ValueError, FileNotFoundError) as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc))
|
||||
|
||||
return {"message": f"Deleted local skill '{skill_name}' for '{agent_id}'"}
|
||||
|
||||
|
||||
@router.post("/{agent_id}/skills/upload")
|
||||
async def upload_external_skill(
|
||||
run_id: str,
|
||||
agent_id: str,
|
||||
file: UploadFile = File(...),
|
||||
name: Optional[str] = Form(None),
|
||||
activate: bool = Form(True),
|
||||
skills_manager: SkillsManager = Depends(get_skills_manager),
|
||||
):
|
||||
"""
|
||||
Upload a zip skill package and install for one agent under `runs/<run_id>/`.
|
||||
|
||||
Args:
|
||||
run_id: Run identifier
|
||||
agent_id: Agent identifier
|
||||
file: Zip file to upload
|
||||
name: Optional skill name override
|
||||
activate: Whether to enable skill immediately
|
||||
|
||||
Returns:
|
||||
Success message with installed skill details
|
||||
"""
|
||||
original_name = (file.filename or "").strip()
|
||||
if not original_name.lower().endswith(".zip"):
|
||||
raise HTTPException(status_code=400, detail="Uploaded file must be a .zip archive")
|
||||
|
||||
suffix = Path(original_name).suffix or ".zip"
|
||||
temp_path: Optional[str] = None
|
||||
try:
|
||||
with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as tmp:
|
||||
temp_path = tmp.name
|
||||
content = await file.read()
|
||||
tmp.write(content)
|
||||
|
||||
result = skills_manager.install_external_skill_for_agent(
|
||||
config_name=run_id,
|
||||
agent_id=agent_id,
|
||||
source=temp_path,
|
||||
skill_name=name,
|
||||
activate=activate,
|
||||
)
|
||||
except (FileNotFoundError, ValueError) as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc))
|
||||
finally:
|
||||
try:
|
||||
await file.close()
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to close uploaded file: {e}")
|
||||
if temp_path and os.path.exists(temp_path):
|
||||
os.remove(temp_path)
|
||||
|
||||
return {
|
||||
"message": f"Uploaded and installed external skill '{result['skill_name']}' for '{agent_id}'",
|
||||
**result,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/{agent_id}/files/{filename}", response_model=AgentFileResponse)
|
||||
async def get_agent_file(
|
||||
run_id: str,
|
||||
agent_id: str,
|
||||
filename: str,
|
||||
workspace_manager: RunWorkspaceManager = Depends(get_workspace_manager),
|
||||
):
|
||||
"""
|
||||
Read an agent file from the run-scoped asset tree under `runs/<run_id>/`.
|
||||
|
||||
Args:
|
||||
run_id: Run identifier
|
||||
agent_id: Agent identifier
|
||||
filename: File to read (e.g., SOUL.md, PROFILE.md)
|
||||
|
||||
Returns:
|
||||
File content
|
||||
"""
|
||||
try:
|
||||
content = workspace_manager.load_agent_file(
|
||||
config_name=run_id,
|
||||
agent_id=agent_id,
|
||||
filename=filename,
|
||||
)
|
||||
return AgentFileResponse(
|
||||
filename=filename,
|
||||
content=content,
|
||||
)
|
||||
except FileNotFoundError:
|
||||
raise HTTPException(status_code=404, detail=f"File '{filename}' not found")
|
||||
|
||||
|
||||
@router.put("/{agent_id}/files/{filename}", response_model=AgentFileResponse)
|
||||
async def update_agent_file(
|
||||
run_id: str,
|
||||
agent_id: str,
|
||||
filename: str,
|
||||
content: str = Body(..., media_type="text/plain"),
|
||||
workspace_manager: RunWorkspaceManager = Depends(get_workspace_manager),
|
||||
):
|
||||
"""
|
||||
Update an agent file in the run-scoped asset tree under `runs/<run_id>/`.
|
||||
|
||||
Args:
|
||||
run_id: Run identifier
|
||||
agent_id: Agent identifier
|
||||
filename: File to update
|
||||
content: New file content
|
||||
|
||||
Returns:
|
||||
Updated file information
|
||||
"""
|
||||
try:
|
||||
workspace_manager.update_agent_file(
|
||||
config_name=run_id,
|
||||
agent_id=agent_id,
|
||||
filename=filename,
|
||||
content=content,
|
||||
)
|
||||
return AgentFileResponse(
|
||||
filename=filename,
|
||||
content=content,
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
@@ -11,7 +11,7 @@ from fastapi import FastAPI
|
||||
|
||||
from backend.apps.cors import add_cors_middleware
|
||||
|
||||
from backend.api import agents_router, guard_router, workspaces_router
|
||||
from backend.api import agents_router, guard_router, workspaces_router, runs_router
|
||||
from backend.agents import AgentFactory, WorkspaceManager, get_registry
|
||||
|
||||
# Global instances (initialized on startup)
|
||||
@@ -30,9 +30,9 @@ def _build_scope_payload(project_root: Path) -> dict[str, object]:
|
||||
"meaning": "Run-scoped runtime state and agent assets",
|
||||
},
|
||||
"agent_route_note": (
|
||||
"On `/api/workspaces/{workspace_id}/agents/...`, design-time CRUD "
|
||||
"routes still use `workspaces/`, while profile/skills/file routes "
|
||||
"use `workspace_id` as a run id under `runs/<run_id>/`."
|
||||
"Runtime routes use `/api/runs/{run_id}/agents/...`. "
|
||||
"Legacy `/api/workspaces/{workspace_id}/agents/...` routes are deprecated "
|
||||
"but remain for backward compatibility."
|
||||
),
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ def create_app(project_root: Path | None = None) -> FastAPI:
|
||||
|
||||
app.include_router(workspaces_router)
|
||||
app.include_router(agents_router)
|
||||
app.include_router(runs_router)
|
||||
app.include_router(guard_router)
|
||||
return app
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,474 +0,0 @@
|
||||
{
|
||||
"baseline_state": {
|
||||
"initialized": true,
|
||||
"initial_allocation": {
|
||||
"AAPL": 52.82787621372046,
|
||||
"MSFT": 27.48283353510314,
|
||||
"GOOGL": 50.62714374311787,
|
||||
"NVDA": 68.65491294557039,
|
||||
"TSLA": 31.329007841650665,
|
||||
"META": 21.77700348432056,
|
||||
"AMZN": 55.94343000358038
|
||||
}
|
||||
},
|
||||
"baseline_vw_state": {
|
||||
"initialized": true,
|
||||
"initial_allocation": {
|
||||
"AAPL": 68.50435598171448,
|
||||
"MSFT": 28.26372943269579,
|
||||
"GOOGL": 64.10562703513074,
|
||||
"NVDA": 105.43488803941372,
|
||||
"TSLA": 16.283886873554753,
|
||||
"META": 12.29869945153529,
|
||||
"AMZN": 44.10358298129591
|
||||
}
|
||||
},
|
||||
"momentum_state": {
|
||||
"positions": {
|
||||
"AAPL": 123.26504449868106,
|
||||
"MSFT": 64.12661158190733,
|
||||
"GOOGL": 118.13000206727504
|
||||
},
|
||||
"cash": 0.0,
|
||||
"initialized": true,
|
||||
"last_rebalance_date": "2025-11-03"
|
||||
},
|
||||
"equity_history": [
|
||||
{
|
||||
"t": 1762070400000,
|
||||
"v": 100000.0
|
||||
},
|
||||
{
|
||||
"t": 1762156800000,
|
||||
"v": 99785.98
|
||||
},
|
||||
{
|
||||
"t": 1762243200000,
|
||||
"v": 99590.68
|
||||
},
|
||||
{
|
||||
"t": 1762329600000,
|
||||
"v": 99298.78
|
||||
},
|
||||
{
|
||||
"t": 1762416000000,
|
||||
"v": 98425.78
|
||||
},
|
||||
{
|
||||
"t": 1762502400000,
|
||||
"v": 98434.93
|
||||
}
|
||||
],
|
||||
"baseline_history": [
|
||||
{
|
||||
"t": 1762070400000,
|
||||
"v": 100000.0
|
||||
},
|
||||
{
|
||||
"t": 1762156800000,
|
||||
"v": 99760.66
|
||||
},
|
||||
{
|
||||
"t": 1762243200000,
|
||||
"v": 97620.18
|
||||
},
|
||||
{
|
||||
"t": 1762329600000,
|
||||
"v": 98327.37
|
||||
},
|
||||
{
|
||||
"t": 1762416000000,
|
||||
"v": 96286.86
|
||||
},
|
||||
{
|
||||
"t": 1762502400000,
|
||||
"v": 95539.06
|
||||
}
|
||||
],
|
||||
"baseline_vw_history": [
|
||||
{
|
||||
"t": 1762070400000,
|
||||
"v": 100000.0
|
||||
},
|
||||
{
|
||||
"t": 1762156800000,
|
||||
"v": 99716.91
|
||||
},
|
||||
{
|
||||
"t": 1762243200000,
|
||||
"v": 97721.94
|
||||
},
|
||||
{
|
||||
"t": 1762329600000,
|
||||
"v": 98028.19
|
||||
},
|
||||
{
|
||||
"t": 1762416000000,
|
||||
"v": 96206.83
|
||||
},
|
||||
{
|
||||
"t": 1762502400000,
|
||||
"v": 95565.33
|
||||
}
|
||||
],
|
||||
"momentum_history": [
|
||||
{
|
||||
"t": 1762070400000,
|
||||
"v": 100000.0
|
||||
},
|
||||
{
|
||||
"t": 1762156800000,
|
||||
"v": 99835.69
|
||||
},
|
||||
{
|
||||
"t": 1762243200000,
|
||||
"v": 99054.53
|
||||
},
|
||||
{
|
||||
"t": 1762329600000,
|
||||
"v": 99406.81
|
||||
},
|
||||
{
|
||||
"t": 1762416000000,
|
||||
"v": 98768.07
|
||||
},
|
||||
{
|
||||
"t": 1762502400000,
|
||||
"v": 97890.54
|
||||
}
|
||||
],
|
||||
"price_history": {
|
||||
"AAPL": [
|
||||
{
|
||||
"date": "2025-11-03",
|
||||
"price": 269.05
|
||||
},
|
||||
{
|
||||
"date": "2025-11-04",
|
||||
"price": 270.04
|
||||
},
|
||||
{
|
||||
"date": "2025-11-05",
|
||||
"price": 270.14
|
||||
},
|
||||
{
|
||||
"date": "2025-11-06",
|
||||
"price": 269.77
|
||||
},
|
||||
{
|
||||
"date": "2025-11-07",
|
||||
"price": 268.47
|
||||
}
|
||||
],
|
||||
"MSFT": [
|
||||
{
|
||||
"date": "2025-11-03",
|
||||
"price": 517.03
|
||||
},
|
||||
{
|
||||
"date": "2025-11-04",
|
||||
"price": 514.33
|
||||
},
|
||||
{
|
||||
"date": "2025-11-05",
|
||||
"price": 507.16
|
||||
},
|
||||
{
|
||||
"date": "2025-11-06",
|
||||
"price": 497.1
|
||||
},
|
||||
{
|
||||
"date": "2025-11-07",
|
||||
"price": 496.82
|
||||
}
|
||||
],
|
||||
"GOOGL": [
|
||||
{
|
||||
"date": "2025-11-03",
|
||||
"price": 283.72
|
||||
},
|
||||
{
|
||||
"date": "2025-11-04",
|
||||
"price": 277.54
|
||||
},
|
||||
{
|
||||
"date": "2025-11-05",
|
||||
"price": 284.31
|
||||
},
|
||||
{
|
||||
"date": "2025-11-06",
|
||||
"price": 284.75
|
||||
},
|
||||
{
|
||||
"date": "2025-11-07",
|
||||
"price": 278.83
|
||||
}
|
||||
],
|
||||
"NVDA": [
|
||||
{
|
||||
"date": "2025-11-03",
|
||||
"price": 206.88
|
||||
},
|
||||
{
|
||||
"date": "2025-11-04",
|
||||
"price": 198.69
|
||||
},
|
||||
{
|
||||
"date": "2025-11-05",
|
||||
"price": 195.21
|
||||
},
|
||||
{
|
||||
"date": "2025-11-06",
|
||||
"price": 188.08
|
||||
},
|
||||
{
|
||||
"date": "2025-11-07",
|
||||
"price": 188.15
|
||||
}
|
||||
],
|
||||
"TSLA": [
|
||||
{
|
||||
"date": "2025-11-03",
|
||||
"price": 468.37
|
||||
},
|
||||
{
|
||||
"date": "2025-11-04",
|
||||
"price": 444.26
|
||||
},
|
||||
{
|
||||
"date": "2025-11-05",
|
||||
"price": 462.07
|
||||
},
|
||||
{
|
||||
"date": "2025-11-06",
|
||||
"price": 445.91
|
||||
},
|
||||
{
|
||||
"date": "2025-11-07",
|
||||
"price": 429.52
|
||||
}
|
||||
],
|
||||
"META": [
|
||||
{
|
||||
"date": "2025-11-03",
|
||||
"price": 637.71
|
||||
},
|
||||
{
|
||||
"date": "2025-11-04",
|
||||
"price": 627.32
|
||||
},
|
||||
{
|
||||
"date": "2025-11-05",
|
||||
"price": 635.95
|
||||
},
|
||||
{
|
||||
"date": "2025-11-06",
|
||||
"price": 618.94
|
||||
},
|
||||
{
|
||||
"date": "2025-11-07",
|
||||
"price": 621.71
|
||||
}
|
||||
],
|
||||
"AMZN": [
|
||||
{
|
||||
"date": "2025-11-03",
|
||||
"price": 254.0
|
||||
},
|
||||
{
|
||||
"date": "2025-11-04",
|
||||
"price": 249.32
|
||||
},
|
||||
{
|
||||
"date": "2025-11-05",
|
||||
"price": 250.2
|
||||
},
|
||||
{
|
||||
"date": "2025-11-06",
|
||||
"price": 243.04
|
||||
},
|
||||
{
|
||||
"date": "2025-11-07",
|
||||
"price": 244.41
|
||||
}
|
||||
]
|
||||
},
|
||||
"portfolio_state": {
|
||||
"cash": 25395.10000000001,
|
||||
"positions": {
|
||||
"MSFT": {
|
||||
"long": 60,
|
||||
"short": 0,
|
||||
"long_cost_basis": 514.2845833333333,
|
||||
"short_cost_basis": 0.0
|
||||
},
|
||||
"GOOGL": {
|
||||
"long": 50,
|
||||
"short": 0,
|
||||
"long_cost_basis": 279.556,
|
||||
"short_cost_basis": 0.0
|
||||
},
|
||||
"META": {
|
||||
"long": 20,
|
||||
"short": 0,
|
||||
"long_cost_basis": 644.155,
|
||||
"short_cost_basis": 0.0
|
||||
},
|
||||
"AMZN": {
|
||||
"long": 40,
|
||||
"short": 0,
|
||||
"long_cost_basis": 247.5725,
|
||||
"short_cost_basis": 0.0
|
||||
},
|
||||
"NVDA": {
|
||||
"long": 20,
|
||||
"short": 0,
|
||||
"long_cost_basis": 203.0,
|
||||
"short_cost_basis": 0.0
|
||||
},
|
||||
"TSLA": {
|
||||
"long": 0,
|
||||
"short": 15,
|
||||
"long_cost_basis": 0.0,
|
||||
"short_cost_basis": 454.46
|
||||
},
|
||||
"AAPL": {
|
||||
"long": 30,
|
||||
"short": 0,
|
||||
"long_cost_basis": 267.89,
|
||||
"short_cost_basis": 0.0
|
||||
}
|
||||
},
|
||||
"margin_used": 1704.225
|
||||
},
|
||||
"all_trades": [
|
||||
{
|
||||
"id": "t_20251103_MSFT_0",
|
||||
"ts": 1762156800000,
|
||||
"trading_date": "2025-11-03",
|
||||
"side": "LONG",
|
||||
"ticker": "MSFT",
|
||||
"qty": 15,
|
||||
"price": 519.8
|
||||
},
|
||||
{
|
||||
"id": "t_20251103_GOOGL_1",
|
||||
"ts": 1762156800000,
|
||||
"trading_date": "2025-11-03",
|
||||
"side": "LONG",
|
||||
"ticker": "GOOGL",
|
||||
"qty": 20,
|
||||
"price": 282.18
|
||||
},
|
||||
{
|
||||
"id": "t_20251103_META_2",
|
||||
"ts": 1762156800000,
|
||||
"trading_date": "2025-11-03",
|
||||
"side": "LONG",
|
||||
"ticker": "META",
|
||||
"qty": 10,
|
||||
"price": 656.0
|
||||
},
|
||||
{
|
||||
"id": "t_20251103_AMZN_3",
|
||||
"ts": 1762156800000,
|
||||
"trading_date": "2025-11-03",
|
||||
"side": "LONG",
|
||||
"ticker": "AMZN",
|
||||
"qty": 15,
|
||||
"price": 255.36
|
||||
},
|
||||
{
|
||||
"id": "t_20251104_MSFT_0",
|
||||
"ts": 1762243200000,
|
||||
"trading_date": "2025-11-04",
|
||||
"side": "LONG",
|
||||
"ticker": "MSFT",
|
||||
"qty": 25,
|
||||
"price": 511.76
|
||||
},
|
||||
{
|
||||
"id": "t_20251104_GOOGL_1",
|
||||
"ts": 1762243200000,
|
||||
"trading_date": "2025-11-04",
|
||||
"side": "LONG",
|
||||
"ticker": "GOOGL",
|
||||
"qty": 15,
|
||||
"price": 276.75
|
||||
},
|
||||
{
|
||||
"id": "t_20251104_NVDA_2",
|
||||
"ts": 1762243200000,
|
||||
"trading_date": "2025-11-04",
|
||||
"side": "LONG",
|
||||
"ticker": "NVDA",
|
||||
"qty": 20,
|
||||
"price": 203.0
|
||||
},
|
||||
{
|
||||
"id": "t_20251104_TSLA_3",
|
||||
"ts": 1762243200000,
|
||||
"trading_date": "2025-11-04",
|
||||
"side": "SHORT",
|
||||
"ticker": "TSLA",
|
||||
"qty": 15,
|
||||
"price": 454.46
|
||||
},
|
||||
{
|
||||
"id": "t_20251105_MSFT_0",
|
||||
"ts": 1762329600000,
|
||||
"trading_date": "2025-11-05",
|
||||
"side": "LONG",
|
||||
"ticker": "MSFT",
|
||||
"qty": 20,
|
||||
"price": 513.3
|
||||
},
|
||||
{
|
||||
"id": "t_20251105_GOOGL_1",
|
||||
"ts": 1762329600000,
|
||||
"trading_date": "2025-11-05",
|
||||
"side": "LONG",
|
||||
"ticker": "GOOGL",
|
||||
"qty": 15,
|
||||
"price": 278.87
|
||||
},
|
||||
{
|
||||
"id": "t_20251105_META_2",
|
||||
"ts": 1762329600000,
|
||||
"trading_date": "2025-11-05",
|
||||
"side": "LONG",
|
||||
"ticker": "META",
|
||||
"qty": 10,
|
||||
"price": 632.31
|
||||
},
|
||||
{
|
||||
"id": "t_20251106_AAPL_0",
|
||||
"ts": 1762416000000,
|
||||
"trading_date": "2025-11-06",
|
||||
"side": "LONG",
|
||||
"ticker": "AAPL",
|
||||
"qty": 30,
|
||||
"price": 267.89
|
||||
},
|
||||
{
|
||||
"id": "t_20251107_AMZN_0",
|
||||
"ts": 1762502400000,
|
||||
"trading_date": "2025-11-07",
|
||||
"side": "LONG",
|
||||
"ticker": "AMZN",
|
||||
"qty": 25,
|
||||
"price": 242.9
|
||||
},
|
||||
{
|
||||
"id": "t_20251107_TSLA_1",
|
||||
"ts": 1762502400000,
|
||||
"trading_date": "2025-11-07",
|
||||
"side": "SHORT",
|
||||
"ticker": "TSLA",
|
||||
"qty": -5,
|
||||
"price": 437.92
|
||||
}
|
||||
],
|
||||
"daily_position_history": {},
|
||||
"last_update_date": "2025-11-07"
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
[
|
||||
{
|
||||
"ticker": "MSFT",
|
||||
"quantity": 60,
|
||||
"currentPrice": 496.82,
|
||||
"marketValue": 29809.2,
|
||||
"weight": 0.3028
|
||||
},
|
||||
{
|
||||
"ticker": "CASH",
|
||||
"quantity": 1,
|
||||
"currentPrice": 25395.1,
|
||||
"marketValue": 25395.1,
|
||||
"weight": 0.258
|
||||
},
|
||||
{
|
||||
"ticker": "GOOGL",
|
||||
"quantity": 50,
|
||||
"currentPrice": 278.83,
|
||||
"marketValue": 13941.5,
|
||||
"weight": 0.1416
|
||||
},
|
||||
{
|
||||
"ticker": "META",
|
||||
"quantity": 20,
|
||||
"currentPrice": 621.71,
|
||||
"marketValue": 12434.2,
|
||||
"weight": 0.1263
|
||||
},
|
||||
{
|
||||
"ticker": "AMZN",
|
||||
"quantity": 40,
|
||||
"currentPrice": 244.41,
|
||||
"marketValue": 9776.4,
|
||||
"weight": 0.0993
|
||||
},
|
||||
{
|
||||
"ticker": "AAPL",
|
||||
"quantity": 30,
|
||||
"currentPrice": 268.47,
|
||||
"marketValue": 8054.1,
|
||||
"weight": 0.0818
|
||||
},
|
||||
{
|
||||
"ticker": "TSLA",
|
||||
"quantity": -15,
|
||||
"currentPrice": 429.52,
|
||||
"marketValue": -6442.8,
|
||||
"weight": 0.0655
|
||||
},
|
||||
{
|
||||
"ticker": "NVDA",
|
||||
"quantity": 20,
|
||||
"currentPrice": 188.15,
|
||||
"marketValue": 3763.0,
|
||||
"weight": 0.0382
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"totalAssetValue": 98434.93,
|
||||
"totalReturn": -1.57,
|
||||
"cashPosition": 25395.1,
|
||||
"tickerWeights": {},
|
||||
"totalTrades": 14,
|
||||
"winRate": 0.0,
|
||||
"bullBear": {
|
||||
"bull": {
|
||||
"n": 0,
|
||||
"win": 0
|
||||
},
|
||||
"bear": {
|
||||
"n": 0,
|
||||
"win": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
{
|
||||
"totalAssetValue": 98434.93,
|
||||
"totalReturn": -1.57,
|
||||
"cashPosition": 25395.1,
|
||||
"tickerWeights": {
|
||||
"MSFT": 0.3028,
|
||||
"GOOGL": 0.1416,
|
||||
"META": 0.1263,
|
||||
"AMZN": 0.0993,
|
||||
"NVDA": 0.0382,
|
||||
"TSLA": -0.0655,
|
||||
"AAPL": 0.0818
|
||||
},
|
||||
"totalTrades": 14,
|
||||
"pnlPct": -1.57,
|
||||
"balance": 98434.93,
|
||||
"equity": [
|
||||
{
|
||||
"t": 1762070400000,
|
||||
"v": 100000.0
|
||||
},
|
||||
{
|
||||
"t": 1762156800000,
|
||||
"v": 99785.98
|
||||
},
|
||||
{
|
||||
"t": 1762243200000,
|
||||
"v": 99590.68
|
||||
},
|
||||
{
|
||||
"t": 1762329600000,
|
||||
"v": 99298.78
|
||||
},
|
||||
{
|
||||
"t": 1762416000000,
|
||||
"v": 98425.78
|
||||
},
|
||||
{
|
||||
"t": 1762502400000,
|
||||
"v": 98434.93
|
||||
}
|
||||
],
|
||||
"baseline": [
|
||||
{
|
||||
"t": 1762070400000,
|
||||
"v": 100000.0
|
||||
},
|
||||
{
|
||||
"t": 1762156800000,
|
||||
"v": 99760.66
|
||||
},
|
||||
{
|
||||
"t": 1762243200000,
|
||||
"v": 97620.18
|
||||
},
|
||||
{
|
||||
"t": 1762329600000,
|
||||
"v": 98327.37
|
||||
},
|
||||
{
|
||||
"t": 1762416000000,
|
||||
"v": 96286.86
|
||||
},
|
||||
{
|
||||
"t": 1762502400000,
|
||||
"v": 95539.06
|
||||
}
|
||||
],
|
||||
"baseline_vw": [
|
||||
{
|
||||
"t": 1762070400000,
|
||||
"v": 100000.0
|
||||
},
|
||||
{
|
||||
"t": 1762156800000,
|
||||
"v": 99716.91
|
||||
},
|
||||
{
|
||||
"t": 1762243200000,
|
||||
"v": 97721.94
|
||||
},
|
||||
{
|
||||
"t": 1762329600000,
|
||||
"v": 98028.19
|
||||
},
|
||||
{
|
||||
"t": 1762416000000,
|
||||
"v": 96206.83
|
||||
},
|
||||
{
|
||||
"t": 1762502400000,
|
||||
"v": 95565.33
|
||||
}
|
||||
],
|
||||
"momentum": [
|
||||
{
|
||||
"t": 1762070400000,
|
||||
"v": 100000.0
|
||||
},
|
||||
{
|
||||
"t": 1762156800000,
|
||||
"v": 99835.69
|
||||
},
|
||||
{
|
||||
"t": 1762243200000,
|
||||
"v": 99054.53
|
||||
},
|
||||
{
|
||||
"t": 1762329600000,
|
||||
"v": 99406.81
|
||||
},
|
||||
{
|
||||
"t": 1762416000000,
|
||||
"v": 98768.07
|
||||
},
|
||||
{
|
||||
"t": 1762502400000,
|
||||
"v": 97890.54
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": "t_20251107_AMZN_0",
|
||||
"timestamp": 1762502400000,
|
||||
"trading_date": "2025-11-07",
|
||||
"side": "LONG",
|
||||
"ticker": "AMZN",
|
||||
"qty": 25,
|
||||
"price": 242.9
|
||||
},
|
||||
{
|
||||
"id": "t_20251107_TSLA_1",
|
||||
"timestamp": 1762502400000,
|
||||
"trading_date": "2025-11-07",
|
||||
"side": "SHORT",
|
||||
"ticker": "TSLA",
|
||||
"qty": -5,
|
||||
"price": 437.92
|
||||
},
|
||||
{
|
||||
"id": "t_20251106_AAPL_0",
|
||||
"timestamp": 1762416000000,
|
||||
"trading_date": "2025-11-06",
|
||||
"side": "LONG",
|
||||
"ticker": "AAPL",
|
||||
"qty": 30,
|
||||
"price": 267.89
|
||||
},
|
||||
{
|
||||
"id": "t_20251105_MSFT_0",
|
||||
"timestamp": 1762329600000,
|
||||
"trading_date": "2025-11-05",
|
||||
"side": "LONG",
|
||||
"ticker": "MSFT",
|
||||
"qty": 20,
|
||||
"price": 513.3
|
||||
},
|
||||
{
|
||||
"id": "t_20251105_GOOGL_1",
|
||||
"timestamp": 1762329600000,
|
||||
"trading_date": "2025-11-05",
|
||||
"side": "LONG",
|
||||
"ticker": "GOOGL",
|
||||
"qty": 15,
|
||||
"price": 278.87
|
||||
},
|
||||
{
|
||||
"id": "t_20251105_META_2",
|
||||
"timestamp": 1762329600000,
|
||||
"trading_date": "2025-11-05",
|
||||
"side": "LONG",
|
||||
"ticker": "META",
|
||||
"qty": 10,
|
||||
"price": 632.31
|
||||
},
|
||||
{
|
||||
"id": "t_20251104_MSFT_0",
|
||||
"timestamp": 1762243200000,
|
||||
"trading_date": "2025-11-04",
|
||||
"side": "LONG",
|
||||
"ticker": "MSFT",
|
||||
"qty": 25,
|
||||
"price": 511.76
|
||||
},
|
||||
{
|
||||
"id": "t_20251104_GOOGL_1",
|
||||
"timestamp": 1762243200000,
|
||||
"trading_date": "2025-11-04",
|
||||
"side": "LONG",
|
||||
"ticker": "GOOGL",
|
||||
"qty": 15,
|
||||
"price": 276.75
|
||||
},
|
||||
{
|
||||
"id": "t_20251104_NVDA_2",
|
||||
"timestamp": 1762243200000,
|
||||
"trading_date": "2025-11-04",
|
||||
"side": "LONG",
|
||||
"ticker": "NVDA",
|
||||
"qty": 20,
|
||||
"price": 203.0
|
||||
},
|
||||
{
|
||||
"id": "t_20251104_TSLA_3",
|
||||
"timestamp": 1762243200000,
|
||||
"trading_date": "2025-11-04",
|
||||
"side": "SHORT",
|
||||
"ticker": "TSLA",
|
||||
"qty": 15,
|
||||
"price": 454.46
|
||||
},
|
||||
{
|
||||
"id": "t_20251103_MSFT_0",
|
||||
"timestamp": 1762156800000,
|
||||
"trading_date": "2025-11-03",
|
||||
"side": "LONG",
|
||||
"ticker": "MSFT",
|
||||
"qty": 15,
|
||||
"price": 519.8
|
||||
},
|
||||
{
|
||||
"id": "t_20251103_GOOGL_1",
|
||||
"timestamp": 1762156800000,
|
||||
"trading_date": "2025-11-03",
|
||||
"side": "LONG",
|
||||
"ticker": "GOOGL",
|
||||
"qty": 20,
|
||||
"price": 282.18
|
||||
},
|
||||
{
|
||||
"id": "t_20251103_META_2",
|
||||
"timestamp": 1762156800000,
|
||||
"trading_date": "2025-11-03",
|
||||
"side": "LONG",
|
||||
"ticker": "META",
|
||||
"qty": 10,
|
||||
"price": 656.0
|
||||
},
|
||||
{
|
||||
"id": "t_20251103_AMZN_3",
|
||||
"timestamp": 1762156800000,
|
||||
"trading_date": "2025-11-03",
|
||||
"side": "LONG",
|
||||
"ticker": "AMZN",
|
||||
"qty": 15,
|
||||
"price": 255.36
|
||||
}
|
||||
]
|
||||
@@ -51,8 +51,8 @@ in use.
|
||||
| Surface | Location | Replacement | ETA |
|
||||
|---------|----------|-------------|-----|
|
||||
| Legacy analyst agents | `backend.agents.analyst.*` | `EvoAgent` | After EvoAgent smoke tests pass |
|
||||
| Mixed workspace_id semantics | `/api/workspaces/{id}/agents/...` | Explicit `run_id` vs `workspace_id` routes | TBD |
|
||||
| Root-level runtime directories | `live/`, `backtest/`, `production/` | `runs/<run_id>/` | Already deprecated, safe to ignore |
|
||||
| Mixed workspace_id semantics | `/api/workspaces/{id}/agents/...` | ✅ `/api/runs/{run_id}/agents/...` routes added | Completed |
|
||||
| Root-level runtime directories | `live/`, `backtest/`, `production/` | `runs/<run_id>/` | ✅ Removed, backed up to runs/_legacy/ |
|
||||
|
||||
**Status**: Do not add new code using these surfaces. Migrate existing usage
|
||||
when touching related code.
|
||||
|
||||
@@ -130,12 +130,12 @@ export function fetchRuntimeLogs() {
|
||||
}
|
||||
|
||||
function buildRunScopedAgentPath(runId, agentId, suffix = '') {
|
||||
return `/workspaces/${encodeURIComponent(runId)}/agents/${encodeURIComponent(agentId)}${suffix}`;
|
||||
return `/runs/${encodeURIComponent(runId)}/agents/${encodeURIComponent(agentId)}${suffix}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runtime-read agent routes still use the `/workspaces/...` prefix on the
|
||||
* backend, but the leading identifier on this surface is the active `run_id`.
|
||||
* Runtime agent routes use `/runs/{run_id}/agents/...`.
|
||||
* Legacy `/workspaces/...` routes are deprecated but remain for backward compatibility.
|
||||
*/
|
||||
export function fetchAgentProfile(runId, agentId) {
|
||||
return safeFetch(CONTROL_API_BASE, buildRunScopedAgentPath(runId, agentId, '/profile'));
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
[]
|
||||
@@ -1,134 +0,0 @@
|
||||
[
|
||||
{
|
||||
"agentId": "portfolio_manager",
|
||||
"name": "投资经理",
|
||||
"role": "投资经理",
|
||||
"avatar": "pm",
|
||||
"rank": null,
|
||||
"winRate": null,
|
||||
"bull": {
|
||||
"n": 0,
|
||||
"win": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"bear": {
|
||||
"n": 0,
|
||||
"win": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"logs": [],
|
||||
"signals": [],
|
||||
"modelName": "deepseek-v3.2",
|
||||
"modelProvider": "DASHSCOPE"
|
||||
},
|
||||
{
|
||||
"agentId": "risk_manager",
|
||||
"name": "风控经理",
|
||||
"role": "风控经理",
|
||||
"avatar": "risk",
|
||||
"rank": null,
|
||||
"winRate": null,
|
||||
"bull": {
|
||||
"n": 0,
|
||||
"win": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"bear": {
|
||||
"n": 0,
|
||||
"win": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"logs": [],
|
||||
"signals": [],
|
||||
"modelName": "deepseek-v3.2",
|
||||
"modelProvider": "DASHSCOPE"
|
||||
},
|
||||
{
|
||||
"agentId": "sentiment_analyst",
|
||||
"name": "情绪分析师",
|
||||
"role": "情绪分析师",
|
||||
"avatar": "sentiment",
|
||||
"rank": 0,
|
||||
"winRate": null,
|
||||
"bull": {
|
||||
"n": 0,
|
||||
"win": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"bear": {
|
||||
"n": 0,
|
||||
"win": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"logs": [],
|
||||
"signals": [],
|
||||
"modelName": "deepseek-v3.2",
|
||||
"modelProvider": "DASHSCOPE"
|
||||
},
|
||||
{
|
||||
"agentId": "technical_analyst",
|
||||
"name": "技术分析师",
|
||||
"role": "技术分析师",
|
||||
"avatar": "technical",
|
||||
"rank": 0,
|
||||
"winRate": null,
|
||||
"bull": {
|
||||
"n": 0,
|
||||
"win": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"bear": {
|
||||
"n": 0,
|
||||
"win": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"logs": [],
|
||||
"signals": [],
|
||||
"modelName": "deepseek-v3.2",
|
||||
"modelProvider": "DASHSCOPE"
|
||||
},
|
||||
{
|
||||
"agentId": "fundamentals_analyst",
|
||||
"name": "基本面分析师",
|
||||
"role": "基本面分析师",
|
||||
"avatar": "fundamentals",
|
||||
"rank": 0,
|
||||
"winRate": null,
|
||||
"bull": {
|
||||
"n": 0,
|
||||
"win": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"bear": {
|
||||
"n": 0,
|
||||
"win": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"logs": [],
|
||||
"signals": [],
|
||||
"modelName": "deepseek-v3.2",
|
||||
"modelProvider": "DASHSCOPE"
|
||||
},
|
||||
{
|
||||
"agentId": "valuation_analyst",
|
||||
"name": "估值分析师",
|
||||
"role": "估值分析师",
|
||||
"avatar": "valuation",
|
||||
"rank": 0,
|
||||
"winRate": null,
|
||||
"bull": {
|
||||
"n": 0,
|
||||
"win": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"bear": {
|
||||
"n": 0,
|
||||
"win": 0,
|
||||
"unknown": 0
|
||||
},
|
||||
"logs": [],
|
||||
"signals": [],
|
||||
"modelName": "deepseek-v3.2",
|
||||
"modelProvider": "DASHSCOPE"
|
||||
}
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"totalAssetValue": 100000.0,
|
||||
"totalReturn": 0.0,
|
||||
"cashPosition": 100000.0,
|
||||
"tickerWeights": {},
|
||||
"totalTrades": 0,
|
||||
"winRate": 0.0,
|
||||
"bullBear": {
|
||||
"bull": {
|
||||
"n": 0,
|
||||
"win": 0
|
||||
},
|
||||
"bear": {
|
||||
"n": 0,
|
||||
"win": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"totalAssetValue": 100000.0,
|
||||
"totalReturn": 0.0,
|
||||
"cashPosition": 100000.0,
|
||||
"tickerWeights": {},
|
||||
"totalTrades": 0,
|
||||
"pnlPct": 0.0,
|
||||
"balance": 100000.0,
|
||||
"equity": [],
|
||||
"baseline": [],
|
||||
"baseline_vw": [],
|
||||
"momentum": []
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
[]
|
||||
Reference in New Issue
Block a user