Files
Labvoice/src/mcp_server/models.py
T
2026-08-30 20:44:00 +02:00

180 lines
3.9 KiB
Python

"""Tool input/output schemas and compact result types."""
from enum import StrEnum
from typing import Literal
from pydantic import BaseModel, Field
class AllocationPolicy(StrEnum):
FIFO = "fifo"
NEAREST_EXPIRY = "nearest_expiry"
SPECIFIC = "specific"
class Consumable(BaseModel):
resource_key: str
quantity: float | None = None
unit: str
allocation: AllocationPolicy = AllocationPolicy.FIFO
container_id: int | None = None
optional: bool = False
class ProtocolAnnotation(BaseModel):
consumables: list[Consumable] = Field(default_factory=list)
prompt_quantity: bool = False
class StepInfo(BaseModel):
id: int
position: int | None = None
body: str
finished: bool = False
class ContainerStock(BaseModel):
container_id: int
amount: str
available: float
unit: str
class NextStep(BaseModel):
experiment_id: int
step: StepInfo
consumables: list[Consumable] = Field(default_factory=list)
stock: list[ContainerStock] = Field(default_factory=list)
class ItemSummary(BaseModel):
id: int
title: str
resource_key: str | None = None
class CommentInfo(BaseModel):
id: int
body: str
created_at: str | None = None
class ExperimentSummary(BaseModel):
id: int
title: str
status: str | None = None
class ExperimentContext(BaseModel):
experiment_id: int
title: str
status: str | None = None
unfinished_steps: list[StepInfo] = Field(default_factory=list)
linked_resources: list[ItemSummary] = Field(default_factory=list)
recent_comments: list[CommentInfo] = Field(default_factory=list)
class AppState(BaseModel):
experiment_id: int
title: str
next_step: NextStep | None = None
unfinished_steps: list[int] = Field(default_factory=list)
class HandoffContext(BaseModel):
"""Minimal id-only context the app forwards to the edge LLM."""
experiment_id: int
user_id: int
step_id: int | None = None
device_id: str | None = None
location_id: str | None = None
class TemplateSummary(BaseModel):
id: int
title: str
short_description: str
tags: list[str] = Field(default_factory=list)
class CreateExperimentResult(BaseModel):
ok: bool = True
experiment_id: int
title: str
first_step: StepInfo | None = None
class ConsumedRecord(BaseModel):
resource_key: str
container_id: int
amount: str
remaining: str
class CompleteStepResult(BaseModel):
ok: bool = True
status: Literal["completed", "protocol_complete", "reverted", "partial_failure"] = "completed"
experiment_id: int
step: StepInfo
consumed: list[ConsumedRecord] = Field(default_factory=list)
comment_id: int | None = None
next_step: StepInfo | None = None
class ObservationResult(BaseModel):
ok: bool = True
experiment_id: int
comment_id: int | None = None
step: StepInfo | None = None
class InventoryResult(BaseModel):
ok: bool = True
container_id: int
resource_key: str | None = None
amount: str
remaining: str
class ContainerAllocation(BaseModel):
container_id: int
take: float
unit: str
remaining: str
class MappingEntry(BaseModel):
resource_key: str
item_id: int
title: str | None = None
unit: str | None = None
class MappingProposal(BaseModel):
resource_key: str
item_id: int
title: str | None = None
reason: str | None = None
score: float | None = None
class ScanResult(BaseModel):
containers_seen: int = 0
markers_found: int = 0
proposals: list[MappingProposal] = Field(default_factory=list)
class ValidationIssue(BaseModel):
severity: Literal["error", "warning"]
message: str
step_id: int | None = None
resource_key: str | None = None
class ValidationReport(BaseModel):
entity_type: str
entity_id: int
ok: bool
issues: list[ValidationIssue] = Field(default_factory=list)