710 lines
22 KiB
Python
710 lines
22 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Agent API Routes
|
|
|
|
Provides REST API endpoints for agent management within workspaces.
|
|
"""
|
|
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 import AgentFactory, get_registry
|
|
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/workspaces/{workspace_id}/agents", tags=["agents"])
|
|
|
|
|
|
# Request/Response Models
|
|
class CreateAgentRequest(BaseModel):
|
|
"""Request to create a new agent."""
|
|
agent_id: str = Field(..., description="Unique agent identifier")
|
|
agent_type: str = Field(..., description="Type of agent (e.g., technical_analyst)")
|
|
name: Optional[str] = Field(None, description="Display name")
|
|
description: Optional[str] = Field(None, description="Agent description")
|
|
clone_from: Optional[str] = Field(None, description="Agent ID to clone from")
|
|
llm_model_config: Optional[Dict[str, Any]] = Field(None, description="LLM model configuration")
|
|
|
|
|
|
class UpdateAgentRequest(BaseModel):
|
|
"""Request to update an agent."""
|
|
name: Optional[str] = None
|
|
description: Optional[str] = None
|
|
enabled_skills: Optional[List[str]] = None
|
|
disabled_skills: Optional[List[str]] = None
|
|
|
|
|
|
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 AgentResponse(BaseModel):
|
|
"""Agent information response."""
|
|
agent_id: str
|
|
agent_type: str
|
|
workspace_id: str
|
|
config_path: str
|
|
agent_dir: str
|
|
status: str = "inactive"
|
|
|
|
|
|
class AgentFileResponse(BaseModel):
|
|
"""Agent file content response."""
|
|
filename: str
|
|
content: str
|
|
|
|
|
|
class AgentProfileResponse(BaseModel):
|
|
agent_id: str
|
|
workspace_id: str
|
|
profile: Dict[str, Any]
|
|
|
|
|
|
class AgentSkillsResponse(BaseModel):
|
|
agent_id: str
|
|
workspace_id: str
|
|
skills: List[Dict[str, Any]]
|
|
|
|
|
|
class SkillDetailResponse(BaseModel):
|
|
agent_id: str
|
|
workspace_id: str
|
|
skill: Dict[str, Any]
|
|
|
|
|
|
# Dependencies
|
|
def get_agent_factory():
|
|
"""Get AgentFactory instance."""
|
|
return AgentFactory()
|
|
|
|
|
|
def get_workspace_manager():
|
|
"""Get run-scoped workspace manager instance."""
|
|
return RunWorkspaceManager()
|
|
|
|
|
|
def get_skills_manager():
|
|
"""Get SkillsManager instance."""
|
|
return SkillsManager()
|
|
|
|
|
|
# Routes
|
|
@router.post("", response_model=AgentResponse)
|
|
async def create_agent(
|
|
workspace_id: str,
|
|
request: CreateAgentRequest,
|
|
factory: AgentFactory = Depends(get_agent_factory),
|
|
registry = Depends(get_registry),
|
|
):
|
|
"""
|
|
Create a new agent in a workspace.
|
|
|
|
Args:
|
|
workspace_id: Workspace identifier
|
|
request: Agent creation parameters
|
|
|
|
Returns:
|
|
Created agent information
|
|
"""
|
|
# Check workspace exists
|
|
if not factory.workspaces_root.exists():
|
|
raise HTTPException(status_code=404, detail="Workspaces root not found")
|
|
|
|
workspace_dir = factory.workspaces_root / workspace_id
|
|
if not workspace_dir.exists():
|
|
raise HTTPException(status_code=404, detail=f"Workspace '{workspace_id}' not found")
|
|
|
|
try:
|
|
# Create agent
|
|
agent = factory.create_agent(
|
|
agent_id=request.agent_id,
|
|
agent_type=request.agent_type,
|
|
workspace_id=workspace_id,
|
|
clone_from=request.clone_from,
|
|
)
|
|
|
|
# Register in registry
|
|
registry.register(
|
|
agent_id=request.agent_id,
|
|
agent_type=request.agent_type,
|
|
workspace_id=workspace_id,
|
|
config_path=str(agent.config_path),
|
|
agent_dir=str(agent.agent_dir),
|
|
status="inactive",
|
|
)
|
|
|
|
return AgentResponse(
|
|
agent_id=agent.agent_id,
|
|
agent_type=agent.agent_type,
|
|
workspace_id=agent.workspace_id,
|
|
config_path=str(agent.config_path),
|
|
agent_dir=str(agent.agent_dir),
|
|
status="inactive",
|
|
)
|
|
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
|
|
|
|
@router.get("", response_model=List[AgentResponse])
|
|
async def list_agents(
|
|
workspace_id: str,
|
|
factory: AgentFactory = Depends(get_agent_factory),
|
|
):
|
|
"""
|
|
List all agents in a workspace.
|
|
|
|
Args:
|
|
workspace_id: Workspace identifier
|
|
|
|
Returns:
|
|
List of agents
|
|
"""
|
|
try:
|
|
agents_data = factory.list_agents(workspace_id=workspace_id)
|
|
return [
|
|
AgentResponse(
|
|
agent_id=agent["agent_id"],
|
|
agent_type=agent["agent_type"],
|
|
workspace_id=workspace_id,
|
|
config_path=agent["config_path"],
|
|
agent_dir=str(Path(agent["config_path"]).parent),
|
|
status="inactive",
|
|
)
|
|
for agent in agents_data
|
|
]
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=404, detail=str(e))
|
|
|
|
|
|
@router.get("/{agent_id}", response_model=AgentResponse)
|
|
async def get_agent(
|
|
workspace_id: str,
|
|
agent_id: str,
|
|
registry = Depends(get_registry),
|
|
):
|
|
"""
|
|
Get agent details.
|
|
|
|
Args:
|
|
workspace_id: Workspace identifier
|
|
agent_id: Agent identifier
|
|
|
|
Returns:
|
|
Agent information
|
|
"""
|
|
agent_info = registry.get(agent_id)
|
|
|
|
if not agent_info or agent_info.workspace_id != workspace_id:
|
|
raise HTTPException(status_code=404, detail=f"Agent '{agent_id}' not found")
|
|
|
|
return AgentResponse(
|
|
agent_id=agent_info.agent_id,
|
|
agent_type=agent_info.agent_type,
|
|
workspace_id=agent_info.workspace_id,
|
|
config_path=agent_info.config_path,
|
|
agent_dir=agent_info.agent_dir,
|
|
status=agent_info.status,
|
|
)
|
|
|
|
|
|
@router.get("/{agent_id}/profile", response_model=AgentProfileResponse)
|
|
async def get_agent_profile(
|
|
workspace_id: str,
|
|
agent_id: str,
|
|
skills_manager: SkillsManager = Depends(get_skills_manager),
|
|
):
|
|
asset_dir = skills_manager.get_agent_asset_dir(workspace_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, workspace_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=workspace_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,
|
|
workspace_id=workspace_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(
|
|
workspace_id: str,
|
|
agent_id: str,
|
|
skills_manager: SkillsManager = Depends(get_skills_manager),
|
|
):
|
|
agent_asset_dir = skills_manager.get_agent_asset_dir(workspace_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=workspace_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(workspace_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, workspace_id=workspace_id, skills=payload)
|
|
|
|
|
|
@router.get("/{agent_id}/skills/{skill_name}", response_model=SkillDetailResponse)
|
|
async def get_agent_skill_detail(
|
|
workspace_id: str,
|
|
agent_id: str,
|
|
skill_name: str,
|
|
skills_manager: SkillsManager = Depends(get_skills_manager),
|
|
):
|
|
try:
|
|
detail = skills_manager.load_agent_skill_document(
|
|
config_name=workspace_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, workspace_id=workspace_id, skill=detail)
|
|
|
|
|
|
@router.delete("/{agent_id}")
|
|
async def delete_agent(
|
|
workspace_id: str,
|
|
agent_id: str,
|
|
factory: AgentFactory = Depends(get_agent_factory),
|
|
registry = Depends(get_registry),
|
|
):
|
|
"""
|
|
Delete an agent.
|
|
|
|
Args:
|
|
workspace_id: Workspace identifier
|
|
agent_id: Agent identifier
|
|
|
|
Returns:
|
|
Success message
|
|
"""
|
|
# Check agent exists in registry
|
|
agent_info = registry.get(agent_id)
|
|
if not agent_info or agent_info.workspace_id != workspace_id:
|
|
raise HTTPException(status_code=404, detail=f"Agent '{agent_id}' not found")
|
|
|
|
# Delete from factory
|
|
success = factory.delete_agent(agent_id, workspace_id)
|
|
if not success:
|
|
raise HTTPException(status_code=404, detail=f"Agent '{agent_id}' not found")
|
|
|
|
# Unregister
|
|
registry.unregister(agent_id)
|
|
|
|
return {"message": f"Agent '{agent_id}' deleted successfully"}
|
|
|
|
|
|
@router.patch("/{agent_id}", response_model=AgentResponse)
|
|
async def update_agent(
|
|
workspace_id: str,
|
|
agent_id: str,
|
|
request: UpdateAgentRequest,
|
|
registry = Depends(get_registry),
|
|
):
|
|
"""
|
|
Update agent configuration.
|
|
|
|
Args:
|
|
workspace_id: Workspace identifier
|
|
agent_id: Agent identifier
|
|
request: Update parameters
|
|
|
|
Returns:
|
|
Updated agent information
|
|
"""
|
|
agent_info = registry.get(agent_id)
|
|
if not agent_info or agent_info.workspace_id != workspace_id:
|
|
raise HTTPException(status_code=404, detail=f"Agent '{agent_id}' not found")
|
|
|
|
# Update metadata in registry
|
|
metadata_updates = {}
|
|
if request.name:
|
|
metadata_updates["name"] = request.name
|
|
if request.description:
|
|
metadata_updates["description"] = request.description
|
|
|
|
if metadata_updates:
|
|
registry.update_metadata(agent_id, metadata_updates)
|
|
|
|
# Update skills if provided
|
|
if request.enabled_skills or request.disabled_skills:
|
|
skills_manager = SkillsManager()
|
|
skills_manager.update_agent_skill_overrides(
|
|
config_name=workspace_id,
|
|
agent_id=agent_id,
|
|
enable=request.enabled_skills or [],
|
|
disable=request.disabled_skills or [],
|
|
)
|
|
|
|
# Get updated info
|
|
agent_info = registry.get(agent_id)
|
|
return AgentResponse(
|
|
agent_id=agent_info.agent_id,
|
|
agent_type=agent_info.agent_type,
|
|
workspace_id=agent_info.workspace_id,
|
|
config_path=agent_info.config_path,
|
|
agent_dir=agent_info.agent_dir,
|
|
status=agent_info.status,
|
|
)
|
|
|
|
|
|
@router.post("/{agent_id}/skills/{skill_name}/enable")
|
|
async def enable_skill(
|
|
workspace_id: str,
|
|
agent_id: str,
|
|
skill_name: str,
|
|
registry = Depends(get_registry),
|
|
):
|
|
"""
|
|
Enable a skill for an agent.
|
|
|
|
Args:
|
|
workspace_id: Workspace identifier
|
|
agent_id: Agent identifier
|
|
skill_name: Skill name to enable
|
|
|
|
Returns:
|
|
Success message
|
|
"""
|
|
agent_info = registry.get(agent_id)
|
|
if not agent_info or agent_info.workspace_id != workspace_id:
|
|
raise HTTPException(status_code=404, detail=f"Agent '{agent_id}' not found")
|
|
|
|
skills_manager = SkillsManager()
|
|
result = skills_manager.update_agent_skill_overrides(
|
|
config_name=workspace_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(
|
|
workspace_id: str,
|
|
agent_id: str,
|
|
skill_name: str,
|
|
registry = Depends(get_registry),
|
|
):
|
|
"""
|
|
Disable a skill for an agent.
|
|
|
|
Args:
|
|
workspace_id: Workspace identifier
|
|
agent_id: Agent identifier
|
|
skill_name: Skill name to disable
|
|
|
|
Returns:
|
|
Success message
|
|
"""
|
|
agent_info = registry.get(agent_id)
|
|
if not agent_info or agent_info.workspace_id != workspace_id:
|
|
raise HTTPException(status_code=404, detail=f"Agent '{agent_id}' not found")
|
|
|
|
skills_manager = SkillsManager()
|
|
result = skills_manager.update_agent_skill_overrides(
|
|
config_name=workspace_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(
|
|
workspace_id: str,
|
|
agent_id: str,
|
|
request: InstallExternalSkillRequest,
|
|
registry=Depends(get_registry),
|
|
):
|
|
"""Install an external skill into one agent's local skills."""
|
|
agent_info = registry.get(agent_id)
|
|
if not agent_info or agent_info.workspace_id != workspace_id:
|
|
raise HTTPException(status_code=404, detail=f"Agent '{agent_id}' not found")
|
|
|
|
skills_manager = SkillsManager()
|
|
try:
|
|
result = skills_manager.install_external_skill_for_agent(
|
|
config_name=workspace_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(
|
|
workspace_id: str,
|
|
agent_id: str,
|
|
request: LocalSkillRequest,
|
|
registry=Depends(get_registry),
|
|
):
|
|
agent_info = registry.get(agent_id)
|
|
if not agent_info or agent_info.workspace_id != workspace_id:
|
|
raise HTTPException(status_code=404, detail=f"Agent '{agent_id}' not found")
|
|
|
|
skills_manager = SkillsManager()
|
|
try:
|
|
skills_manager.create_agent_local_skill(
|
|
config_name=workspace_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(
|
|
workspace_id: str,
|
|
agent_id: str,
|
|
skill_name: str,
|
|
request: LocalSkillContentRequest,
|
|
registry=Depends(get_registry),
|
|
):
|
|
agent_info = registry.get(agent_id)
|
|
if not agent_info or agent_info.workspace_id != workspace_id:
|
|
raise HTTPException(status_code=404, detail=f"Agent '{agent_id}' not found")
|
|
|
|
skills_manager = SkillsManager()
|
|
try:
|
|
skills_manager.update_agent_local_skill(
|
|
config_name=workspace_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(
|
|
workspace_id: str,
|
|
agent_id: str,
|
|
skill_name: str,
|
|
registry=Depends(get_registry),
|
|
):
|
|
agent_info = registry.get(agent_id)
|
|
if not agent_info or agent_info.workspace_id != workspace_id:
|
|
raise HTTPException(status_code=404, detail=f"Agent '{agent_id}' not found")
|
|
|
|
skills_manager = SkillsManager()
|
|
try:
|
|
skills_manager.delete_agent_local_skill(
|
|
config_name=workspace_id,
|
|
agent_id=agent_id,
|
|
skill_name=skill_name,
|
|
)
|
|
skills_manager.forget_agent_skill_overrides(
|
|
config_name=workspace_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(
|
|
workspace_id: str,
|
|
agent_id: str,
|
|
file: UploadFile = File(...),
|
|
name: Optional[str] = Form(None),
|
|
activate: bool = Form(True),
|
|
registry=Depends(get_registry),
|
|
):
|
|
"""Upload a zip skill package from frontend and install for one agent."""
|
|
agent_info = registry.get(agent_id)
|
|
if not agent_info or agent_info.workspace_id != workspace_id:
|
|
raise HTTPException(status_code=404, detail=f"Agent '{agent_id}' not found")
|
|
|
|
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)
|
|
|
|
skills_manager = SkillsManager()
|
|
result = skills_manager.install_external_skill_for_agent(
|
|
config_name=workspace_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(
|
|
workspace_id: str,
|
|
agent_id: str,
|
|
filename: str,
|
|
workspace_manager: RunWorkspaceManager = Depends(get_workspace_manager),
|
|
):
|
|
"""
|
|
Read an agent's workspace file.
|
|
|
|
Args:
|
|
workspace_id: Workspace 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=workspace_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(
|
|
workspace_id: str,
|
|
agent_id: str,
|
|
filename: str,
|
|
content: str = Body(..., media_type="text/plain"),
|
|
workspace_manager: RunWorkspaceManager = Depends(get_workspace_manager),
|
|
):
|
|
"""
|
|
Update an agent's workspace file.
|
|
|
|
Args:
|
|
workspace_id: Workspace 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=workspace_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))
|