- Added configuration options for requiring human approval before applying LLM-generated MCP patches. - Updated Docker setup to include skills directory. - Integrated skills management into the backend, allowing for procedural guides and skill matching. - Refactored database initialization to apply Alembic migrations. - Enhanced task approval process to handle MCP patch applications with optional approval. - Introduced new schemas for skills and updated existing APIs to support skills functionality. This commit lays the groundwork for improved agent capabilities and better management of MCP development processes.
347 lines
7.9 KiB
Python
347 lines
7.9 KiB
Python
"""Pydantic request/response schemas."""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Any, Literal
|
|
|
|
from pydantic import BaseModel, ConfigDict, EmailStr
|
|
|
|
from app.models.enums import ApprovalStatus, DeviceType, Role, TaskStatus
|
|
|
|
|
|
# ---- Auth / users -----------------------------------------------------
|
|
class Token(BaseModel):
|
|
access_token: str
|
|
token_type: str = "bearer"
|
|
role: Role
|
|
email: str
|
|
|
|
|
|
class UserCreate(BaseModel):
|
|
email: EmailStr
|
|
password: str
|
|
full_name: str | None = None
|
|
role: Role = Role.readonly
|
|
|
|
|
|
class UserUpdate(BaseModel):
|
|
full_name: str | None = None
|
|
role: Role | None = None
|
|
is_active: bool | None = None
|
|
password: str | None = None
|
|
|
|
|
|
class UserOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: int
|
|
email: str
|
|
full_name: str | None
|
|
role: Role
|
|
is_active: bool
|
|
created_at: datetime
|
|
|
|
|
|
# ---- Inventory (vessels + onboard devices) ------------------------------
|
|
class CatalogEntryOut(BaseModel):
|
|
key: str
|
|
label: str
|
|
description: str
|
|
device_type: str
|
|
mcp_server: str | None
|
|
default_port: int
|
|
default_username: str | None
|
|
has_default_secret: bool = False
|
|
secret_kind: str = "password" # password | api_key
|
|
env_defaults_configured: bool = False
|
|
asterisk_container: str | None = None # GeneseasX docker container name (e.g. voip)
|
|
category: str
|
|
|
|
|
|
class VesselDeviceSelection(BaseModel):
|
|
"""One catalog slot when creating/updating a vessel."""
|
|
catalog_key: str
|
|
enabled: bool = True
|
|
port: int | None = None # falls back to catalog default_port
|
|
address: str | None = None # falls back to vessel public_ip
|
|
username: str | None = None
|
|
secret: str | None = None
|
|
notes: str | None = None
|
|
|
|
|
|
class VesselCreate(BaseModel):
|
|
name: str
|
|
site: str | None = None
|
|
public_ip: str | None = None
|
|
notes: str | None = None
|
|
devices: list[VesselDeviceSelection] = []
|
|
|
|
|
|
class VesselUpdate(BaseModel):
|
|
name: str | None = None
|
|
site: str | None = None
|
|
public_ip: str | None = None
|
|
notes: str | None = None
|
|
devices: list[VesselDeviceSelection] | None = None
|
|
|
|
|
|
class DeviceOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: int
|
|
name: str
|
|
catalog_key: str | None
|
|
device_type: DeviceType
|
|
address: str
|
|
port: int | None
|
|
mcp_server: str | None
|
|
username: str | None
|
|
has_secret: bool = False
|
|
notes: str | None
|
|
vessel_id: int | None
|
|
created_at: datetime
|
|
|
|
|
|
class VesselOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: int
|
|
name: str
|
|
site: str | None
|
|
public_ip: str | None
|
|
notes: str | None
|
|
created_at: datetime
|
|
|
|
|
|
class VesselDetail(VesselOut):
|
|
devices: list[DeviceOut] = []
|
|
|
|
|
|
class DeviceUpdate(BaseModel):
|
|
port: int | None = None
|
|
address: str | None = None
|
|
username: str | None = None
|
|
secret: str | None = None
|
|
notes: str | None = None
|
|
|
|
|
|
# ---- Tasks ------------------------------------------------------------
|
|
class TaskCreate(BaseModel):
|
|
title: str
|
|
issue: str
|
|
device_id: int | None = None
|
|
vessel_id: int | None = None
|
|
details: dict[str, Any] | None = None
|
|
|
|
|
|
class TaskPreviewIn(BaseModel):
|
|
title: str = ""
|
|
issue: str
|
|
vessel_id: int
|
|
|
|
|
|
class TaskPreviewDevice(BaseModel):
|
|
catalog_key: str
|
|
name: str
|
|
label: str
|
|
checks: list[str] = []
|
|
|
|
|
|
class TaskPreviewOut(BaseModel):
|
|
vessel_id: int
|
|
vessel_name: str | None = None
|
|
devices: list[TaskPreviewDevice]
|
|
rule_id: str | None = None
|
|
rule_name: str | None = None
|
|
severity: str | None = None
|
|
hints: list[str] = []
|
|
warning: str | None = None
|
|
|
|
|
|
class TaskContinue(BaseModel):
|
|
follow_up: str
|
|
|
|
|
|
class TaskEscalateLlm(BaseModel):
|
|
tier: Literal["economy", "premium"]
|
|
|
|
|
|
class TaskEventOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: int
|
|
kind: str
|
|
message: str | None
|
|
payload: dict[str, Any] | None
|
|
created_at: datetime
|
|
|
|
|
|
class TaskOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: int
|
|
title: str
|
|
issue: str
|
|
details: dict[str, Any] | None
|
|
status: TaskStatus
|
|
device_id: int | None
|
|
vessel_id: int | None
|
|
created_by_id: int | None
|
|
report: dict[str, Any] | None
|
|
error: str | None
|
|
memory_id: str | None
|
|
obsidian_path: str | None
|
|
vessel_name: str | None = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class TaskOverviewOut(BaseModel):
|
|
id: int
|
|
title: str
|
|
issue: str
|
|
status: TaskStatus
|
|
vessel_id: int | None = None
|
|
vessel_name: str | None = None
|
|
summary: str | None = None
|
|
key_finding: str | None = None
|
|
devices_checked: list[str] = []
|
|
resolved: bool | None = None
|
|
memory_id: str | None = None
|
|
obsidian_path: str | None = None
|
|
obsidian_push_error: str | None = None
|
|
run_cost_usd: float | None = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class TaskDetail(TaskOut):
|
|
events: list[TaskEventOut] = []
|
|
|
|
|
|
class ApprovalOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: int
|
|
task_id: int
|
|
tool_name: str
|
|
tool_args: dict[str, Any] | None
|
|
risk: str | None
|
|
status: ApprovalStatus
|
|
created_at: datetime
|
|
|
|
|
|
class ApprovalDecision(BaseModel):
|
|
approve: bool
|
|
|
|
|
|
# ---- MCP --------------------------------------------------------------
|
|
class McpToolOut(BaseModel):
|
|
name: str
|
|
description: str | None = None
|
|
|
|
|
|
class McpServerOut(BaseModel):
|
|
name: str
|
|
status: str # loaded | error | cloning | stopped
|
|
tools: list[McpToolOut] = []
|
|
error: str | None = None
|
|
source: str | None = None # local | git
|
|
|
|
|
|
class McpCommandResult(BaseModel):
|
|
ok: bool
|
|
action: str | None = None
|
|
server: str | None = None
|
|
error: str | None = None
|
|
|
|
|
|
class McpDevelopRequest(BaseModel):
|
|
issue: str
|
|
tool: str | None = None
|
|
task_id: int | None = None
|
|
push: bool | None = None
|
|
|
|
|
|
class McpDevelopResult(BaseModel):
|
|
ok: bool
|
|
task_id: int | None = None
|
|
summary: str | None = None
|
|
error: str | None = None
|
|
tools: list[str] | None = None
|
|
push: dict | None = None
|
|
|
|
|
|
# ---- Balance ----------------------------------------------------------
|
|
class BalanceOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
provider: str
|
|
total_credits: float | None
|
|
total_usage: float | None
|
|
remaining: float | None
|
|
currency: str
|
|
error: str | None
|
|
created_at: datetime
|
|
|
|
|
|
# ---- LLM routing ------------------------------------------------------
|
|
class LlmRoutingConfigIn(BaseModel):
|
|
local_model_id: str | None = None
|
|
economy_gemini_id: str | None = None
|
|
economy_deepseek_id: str | None = None
|
|
economy_openrouter_id: str | None = None
|
|
premium_gemini_id: str | None = None
|
|
premium_deepseek_id: str | None = None
|
|
premium_openrouter_id: str | None = None
|
|
cloud_provider_order: list[str] | None = None
|
|
auto_escalate: bool | None = None
|
|
max_tier: str | None = None
|
|
|
|
|
|
class ProviderModelsOut(BaseModel):
|
|
providers: dict[str, Any]
|
|
routing_order: str
|
|
|
|
|
|
class LlmRoutingConfigOut(BaseModel):
|
|
config: dict[str, Any]
|
|
catalog: dict[str, Any]
|
|
available_backends: dict[str, bool]
|
|
strategy: str
|
|
|
|
|
|
# ---- Troubleshooting rules --------------------------------------------
|
|
class TroubleshootingRulesUpdate(BaseModel):
|
|
yaml: str
|
|
|
|
|
|
class TroubleshootingRulesOut(BaseModel):
|
|
yaml: str
|
|
parsed: dict[str, Any]
|
|
path_hint: str
|
|
|
|
|
|
# ---- Agent skills -----------------------------------------------------
|
|
class SkillSummaryOut(BaseModel):
|
|
id: str
|
|
name: str
|
|
description: str
|
|
priority: int
|
|
rule_ids: list[str]
|
|
match: dict[str, Any]
|
|
path_hint: str
|
|
|
|
|
|
class SkillsListOut(BaseModel):
|
|
skills: list[SkillSummaryOut]
|
|
path_hint: str
|
|
|
|
|
|
class SkillOut(BaseModel):
|
|
id: str
|
|
name: str
|
|
description: str
|
|
priority: int
|
|
rule_ids: list[str]
|
|
match: dict[str, Any]
|
|
markdown: str
|
|
path_hint: str
|
|
|
|
|
|
class SkillUpdate(BaseModel):
|
|
markdown: str
|