zukhriddinai's picture
Deploy LabOps Guardian API
a608353 verified
Raw
History Blame Contribute Delete
6.59 kB
"""Pydantic models for LabOps Guardian.
Every stored fact carries a `source_type` + `confidence` (truth-state system) so the
agent never presents a camera-guess or hypothesis as a confirmed fact.
"""
from __future__ import annotations
from typing import Any, Literal, Optional
from pydantic import BaseModel, Field
SourceType = Literal[
"observed_by_sensor",
"user_reported",
"sop_grounded",
"calculated",
"camera_inferred",
"pending_confirmation",
"human_confirmed",
"stale",
]
Confidence = Literal["high", "medium", "low"]
Severity = Literal["low", "medium", "high", "critical"]
# ── Core stored objects ───────────────────────────────────────────────────────
class NormalRange(BaseModel):
min: float
max: float
unit: str = "C"
class Equipment(BaseModel):
id: str
name: str
kind: str
current_temperature: Optional[str] = None
status: str = "ok" # ok | alarm | error | idle
normal_range: Optional[NormalRange] = None
source_type: SourceType = "observed_by_sensor"
confidence: Confidence = "high"
updated_at: Optional[str] = None
class Sample(BaseModel):
sample_id: str
name: str
location: str
storage_temperature: str
max_room_temp_minutes: int = 20
room_temp_started_at: Optional[str] = None
room_temp_deadline: Optional[str] = None
source_type: SourceType = "user_reported"
confidence: Confidence = "medium"
updated_at: Optional[str] = None
class Incident(BaseModel):
incident_id: str
type: str # temperature_excursion | centrifuge_error | ...
equipment_id: str
severity: Severity = "high"
status: Literal["open", "investigating", "resolved"] = "open"
current_value: Optional[str] = None
threshold: Optional[str] = None
observations: list[str] = Field(default_factory=list)
tickets: list[str] = Field(default_factory=list)
created_at: str
updated_at: Optional[str] = None
class Ticket(BaseModel):
ticket_id: str
incident_id: str
summary: Optional[str] = None
severity: str
assigned_to: str
status: str = "open"
notes: Optional[str] = None
created_at: str
class PriorEvent(BaseModel):
id: str
equipment_id: str
issue_type: str
timestamp: str
summary: Optional[str] = None
recorded_cause: Optional[str] = None
resolution: Optional[str] = None
duration_hours: Optional[float] = None
source_type: SourceType = "human_confirmed"
confidence: Confidence = "high"
class InventoryItem(BaseModel):
item_name: str
location: str
bin: Optional[str] = None
record_count: Optional[int] = None
camera_inferred_count: Optional[int] = None
stock_level: str = "ok"
confidence: Confidence = "medium"
source_type: SourceType = "camera_inferred"
timestamp: Optional[str] = None
class Reminder(BaseModel):
id: str
label: str
due_at: str
sample_id: Optional[str] = None
kind: Literal["warning", "escalation", "manual"] = "manual"
status: Literal["open", "done", "cancelled"] = "open"
created_at: Optional[str] = None
class Event(BaseModel):
id: str
type: str
payload: dict[str, Any] = Field(default_factory=dict)
source_type: SourceType = "user_reported"
confidence: Confidence = "medium"
timestamp: Optional[str] = None
class Message(BaseModel):
id: str
recipient_role: str
message: str
status: Literal["draft", "sent"] = "draft"
source_type: SourceType = "pending_confirmation"
timestamp: Optional[str] = None
class ExperimentRun(BaseModel):
id: str
title: str
started_at: str
sample_ids: list[str] = Field(default_factory=list)
notes: list[str] = Field(default_factory=list)
status: str = "in_progress"
# ── Request bodies ────────────────────────────────────────────────────────────
class EventRequest(BaseModel):
type: str
equipment_id: Optional[str] = None
value: Optional[float] = None
unit: str = "C"
payload: dict[str, Any] = Field(default_factory=dict)
source_type: SourceType = "observed_by_sensor"
confidence: Confidence = "high"
timestamp: Optional[str] = None
class RetrieveSopRequest(BaseModel):
issue_type: Optional[str] = None
equipment_id: Optional[str] = None
query: Optional[str] = None # fallback free-text search
sample_id: Optional[str] = None
class CreateTicketRequest(BaseModel):
incident_id: str
summary: Optional[str] = None
severity: str = "high"
assigned_to: str = "Facilities"
notes: Optional[str] = None
class RecallHistoryRequest(BaseModel):
equipment_id: str
issue_type: Optional[str] = None
class CreateIncidentRequest(BaseModel):
type: str
equipment_id: str
severity: Severity = "high"
current_value: Optional[str] = None
threshold: Optional[str] = None
class AddObservationRequest(BaseModel):
observation: str
class MoveSampleRequest(BaseModel):
from_location: Optional[str] = None
to_location: str
from_temperature: Optional[str] = None
allowed_room_temp_minutes: int = 20
class ValidateCalculationRequest(BaseModel):
calculation_type: str = "percent_volume_volume"
target_percent: Optional[float] = None
final_volume_ml: Optional[float] = None
user_answer_ul: Optional[float] = None
stock_percent: Optional[float] = None
user_answer_g: Optional[float] = None
class FindInventoryRequest(BaseModel):
item_name: str
class CreateReminderRequest(BaseModel):
# Either the explicit label+due_at form, or the Rasa duration_minutes+message form
label: Optional[str] = None
due_at: Optional[str] = None
sample_id: Optional[str] = None
duration_minutes: Optional[int] = None
message: Optional[str] = None
class LogActivityEventRequest(BaseModel):
person_name: str
event_type: str = "lab_activity"
sample_id: Optional[str] = None
description: Optional[str] = None
source_type: SourceType = "user_reported"
confidence: Confidence = "medium"
class SendEmergencyMessageRequest(BaseModel):
recipient_role: str
message: str
confirmed: bool = False
class GenerateHandoffRequest(BaseModel):
incident_id: Optional[str] = None
shift: Optional[str] = None