diff --git a/src/torq/api/routes.py b/src/torq/api/routes.py index 1a775c5..beab88e 100644 --- a/src/torq/api/routes.py +++ b/src/torq/api/routes.py @@ -5,7 +5,7 @@ from datetime import datetime, timezone from fastapi import APIRouter, HTTPException, Request from fastapi.responses import FileResponse, StreamingResponse -from pydantic import BaseModel +from pydantic import BaseModel, Field from torq.config import settings from torq.db import models @@ -19,13 +19,13 @@ class MachineIn(BaseModel): - id: str - model: str - location: str + id: str = Field(max_length=100) + model: str = Field(max_length=200) + location: str = Field(max_length=200) class FaultIn(BaseModel): - fault_code: str + fault_code: str = Field(min_length=1, max_length=50) machine: str = "" context: str = "" translate: bool = True diff --git a/src/torq/config.py b/src/torq/config.py index 0abbc6d..fa7cc73 100644 --- a/src/torq/config.py +++ b/src/torq/config.py @@ -29,17 +29,12 @@ def _ensure_llm_key(self): if not self.llm_api_key: self.llm_api_key = os.environ.get("OPENAI_API_KEY", "") if not self.llm_api_key: - print("=" * 60) - print(" FATAL: No LLM API key found.") - print() - print(" Set LLM_API_KEY or OPENAI_API_KEY in your .env file") - print(" or export it as an environment variable.") - print() - print(" Example:") - print(" LLM_API_KEY=sk-... # DeepSeek (default)") - print(" OPENAI_API_KEY=sk-... # OpenAI-compatible") - print("=" * 60) - sys.exit(1) + import logging + logging.warning( + "No LLM API key found. " + "Set LLM_API_KEY or OPENAI_API_KEY in your .env file. " + "LLM-dependent features (diagnosis, translation) will fall back." + ) return self # Vector DB (Qdrant) diff --git a/src/torq/dispatch/routing.py b/src/torq/dispatch/routing.py index 822406c..3c5fa00 100644 --- a/src/torq/dispatch/routing.py +++ b/src/torq/dispatch/routing.py @@ -11,7 +11,10 @@ def _load_roster(shifts_file: Path | None = None) -> list[dict]: shifts_file = shifts_file or settings.shifts_file if not shifts_file.exists(): return [] - return json.loads(shifts_file.read_text(encoding="utf-8")) + try: + return json.loads(shifts_file.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError): + return [] def choose_technician(wo: WorkOrder, roster: list[dict] | None = None) -> dict | None: diff --git a/web/src/ErrorBoundary.jsx b/web/src/ErrorBoundary.jsx new file mode 100644 index 0000000..68ce9df --- /dev/null +++ b/web/src/ErrorBoundary.jsx @@ -0,0 +1,25 @@ +import { Component } from "react"; + +export default class ErrorBoundary extends Component { + constructor(props) { + super(props); + this.state = { error: null }; + } + + static getDerivedStateFromError(error) { + return { error }; + } + + render() { + if (this.state.error) { + return ( +
{this.state.error.message}
+
+ {t("dashboard.subtitle")}
+ {errored &&