Files
Agentic-OS/backend/app/schemas.py
nearxos 0375b20bb4 Enhance MCP development features and introduce skills management
- 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.
2026-06-14 22:27:24 +03:00

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