One module per API area (base, files, activity, skills, conversations, notifications, forms, search, plans, projects, services, misc, diff, cron, agents) on a shared base.Schema; __init__.py re-exports every model so handlers keep writing schemas.<Name>. OpenAPI output is byte-identical. PlanSummary lives in conversations.py to keep the module graph a DAG. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
264 lines
9.1 KiB
Python
264 lines
9.1 KiB
Python
"""Small mutation / misc envelopes (ui state, models, accounts, workers,
|
|
spawn/upload results, meta) and the deploy status banner."""
|
|
from __future__ import annotations
|
|
|
|
from typing import Literal, Optional
|
|
|
|
from .base import Schema
|
|
from .conversations import ConvMeta
|
|
|
|
__all__ = [
|
|
"UiStateValue",
|
|
"OkResponse",
|
|
"ModelInfo",
|
|
"ModelsResponse",
|
|
"PendingLogin",
|
|
"AccountStatus",
|
|
"LoginCodeResult",
|
|
"Account",
|
|
"AccountsResponse",
|
|
"Worker",
|
|
"WorkersResponse",
|
|
"OpenRouterModel",
|
|
"OpenRouterModelsResponse",
|
|
"SpawnResult",
|
|
"InterruptResult",
|
|
"UploadedFile",
|
|
"UploadResult",
|
|
"ConversationMetaResponse",
|
|
"MetaUpdateResult",
|
|
"ArchiveOldResult",
|
|
"DeployStep",
|
|
"DeployStatus",
|
|
]
|
|
|
|
|
|
# ── small mutation / misc envelopes ───────────────────────────────────────────
|
|
class UiStateValue(Schema):
|
|
value: Optional[str] = None
|
|
|
|
|
|
class OkResponse(Schema):
|
|
ok: bool
|
|
|
|
|
|
class ModelInfo(Schema):
|
|
"""One pickable model (a model tag in the composer)."""
|
|
id: str # the exact `--model` argument
|
|
displayName: str
|
|
family: str # opus | sonnet | haiku | fable | qwen
|
|
version: str # "4.8", "5", … (within the family)
|
|
alias: Optional[str] = None # CLI shorthand, only on a family's newest
|
|
latest: bool = False
|
|
# Which agent harness runs sessions on this model: "claude" (the claude
|
|
# CLI) or "pi" (the pi.dev runner). Picking the chip picks the harness.
|
|
harness: Optional[str] = None
|
|
# pi models only: "openrouter" (hosted) or "evox2" (local LM Studio).
|
|
provider: Optional[str] = None
|
|
# Optional short chip label ("qwen 3.6 local"); the frontend derives one
|
|
# from the id when absent.
|
|
label: Optional[str] = None
|
|
# Context-window size in tokens (`max_input_tokens` from the Anthropic
|
|
# Models API, with a static per-family fallback). Drives the conversation
|
|
# page's context-fill gizmo.
|
|
contextWindow: Optional[int] = None
|
|
|
|
|
|
class ModelsResponse(Schema):
|
|
models: list[ModelInfo]
|
|
default: str # model a run uses when nothing is picked
|
|
|
|
|
|
class PendingLogin(Schema):
|
|
"""A headless ``claude auth login`` waiting for the code its sign-in page
|
|
shows (see sidecar/claude_cli.py)."""
|
|
loginId: str
|
|
account: str
|
|
url: str # the OAuth sign-in URL to open
|
|
startedAt: int # epoch seconds
|
|
expiresAt: int # the CLI is killed after this
|
|
|
|
|
|
class AccountStatus(Schema):
|
|
"""An account's live login, from ``claude auth status`` under its config
|
|
dir (via the sidecar). ``configured`` False ⇒ the sidecar doesn't declare
|
|
the account (or couldn't be reached — then ``error`` says so)."""
|
|
configured: bool
|
|
loggedIn: bool
|
|
email: Optional[str] = None
|
|
orgName: Optional[str] = None
|
|
subscriptionType: Optional[str] = None
|
|
authMethod: Optional[str] = None
|
|
error: Optional[str] = None
|
|
# A `claude auth login` started from Settings and still waiting for its
|
|
# code (None when none is pending).
|
|
pendingLogin: Optional[PendingLogin] = None
|
|
|
|
|
|
class LoginCodeResult(Schema):
|
|
"""The account's fresh login after a code/logout (``claude auth status``)."""
|
|
id: str
|
|
loggedIn: bool
|
|
email: Optional[str] = None
|
|
orgName: Optional[str] = None
|
|
subscriptionType: Optional[str] = None
|
|
authMethod: Optional[str] = None
|
|
message: Optional[str] = None
|
|
|
|
|
|
|
|
class Account(Schema):
|
|
"""A Claude account profile a session can run on (backend/accounts.py —
|
|
hard-coded; Settings only owns its plan/price presentation)."""
|
|
id: str # "personal" | "work"
|
|
label: str
|
|
description: str
|
|
color: str # a TAG_COLORS key
|
|
projects: list[str] # ~/projects slugs routed to this account
|
|
excludeFromTotals: bool # left out of aggregated totals by default
|
|
default: bool
|
|
status: Optional[AccountStatus] = None # only with ?health=1
|
|
|
|
|
|
class AccountsResponse(Schema):
|
|
accounts: list[Account]
|
|
default: str
|
|
|
|
|
|
class Worker(Schema):
|
|
"""A paired remote runner (backend/workers.py): the same sidecar on
|
|
another machine, driven over the tailnet. No token here — it never leaves
|
|
the backend."""
|
|
id: str
|
|
name: str
|
|
host: str
|
|
port: int
|
|
url: str
|
|
account: Optional[str] = None # the hub account its login is
|
|
email: Optional[str] = None # its `claude` login
|
|
orgName: Optional[str] = None
|
|
hostname: Optional[str] = None
|
|
platform: Optional[str] = None # "darwin" | "linux"
|
|
machine: Optional[str] = None
|
|
version: Optional[str] = None
|
|
cwd: Optional[str] = None # where a new run starts there
|
|
permissionMode: Optional[str] = None
|
|
pairedAt: Optional[str] = None
|
|
# The account's new runs go here whenever it is online. Off: only runs the
|
|
# composer pins to this worker (the lab stays the default runner).
|
|
autoRoute: bool = False
|
|
status: str # online | offline | unauthorized | unknown
|
|
lastSeen: Optional[str] = None
|
|
lastError: Optional[str] = None
|
|
running: int = 0 # live runs it launched
|
|
mirrored: int = 0 # transcripts mirrored since boot
|
|
syncing: bool = False
|
|
|
|
|
|
class WorkersResponse(Schema):
|
|
workers: list[Worker]
|
|
|
|
|
|
|
|
class OpenRouterModel(Schema):
|
|
"""One OpenRouter model a pi.dev session can be spawned on.
|
|
|
|
The catalogue behind it is APPE's (models.dev, synced daily) — see
|
|
`backend/openrouter.py`. Prices are USD per **million** tokens.
|
|
"""
|
|
id: str # the `--model` argument, e.g. "openai/gpt-oss-20b"
|
|
name: str # display name ("GPT OSS 20B")
|
|
vendor: str # the id's first segment ("openai")
|
|
description: str = ""
|
|
inputCost: float
|
|
outputCost: float
|
|
# Cache-read price. None = the model has no prompt cache (a cache read is
|
|
# then billed as a fresh read), which is a different fact from a free cache.
|
|
cacheCost: Optional[float] = None
|
|
contextWindow: Optional[int] = None
|
|
# Parameters in billions, mined from the id/name by APPE. None for models
|
|
# that publish no size (most closed ones).
|
|
modelSize: Optional[float] = None
|
|
tags: list[str] = [] # vision | reasoning | tools | opensource | …
|
|
tier: str = "" # small | medium | big (blended-price heuristic)
|
|
license: str = ""
|
|
speedTps: Optional[float] = None # median output tokens/sec
|
|
|
|
|
|
class OpenRouterModelsResponse(Schema):
|
|
models: list[OpenRouterModel] # cheapest first (blended 3:1 in:out)
|
|
source: str # the APPE URL the catalogue came from
|
|
generatedAt: str = "" # when APPE generated it (ISO 8601)
|
|
|
|
|
|
class SpawnResult(Schema):
|
|
sessionId: str
|
|
pid: Optional[int] = None
|
|
# "inbox" when a follow-up was delivered into the still-running session
|
|
# through its inbox socket instead of restarting it on the new prompt.
|
|
delivered: Optional[str] = None
|
|
|
|
|
|
class InterruptResult(Schema):
|
|
sessionId: str
|
|
ok: bool
|
|
|
|
|
|
class UploadedFile(Schema):
|
|
name: str
|
|
size: int
|
|
contentType: str
|
|
# Repo-relative path the composer injects into the prompt (read by the session).
|
|
repoPath: str
|
|
# Backend serve route the thread viewer renders the file from.
|
|
url: str
|
|
|
|
|
|
class UploadResult(Schema):
|
|
files: list[UploadedFile]
|
|
|
|
|
|
class ConversationMetaResponse(Schema):
|
|
meta: dict[str, ConvMeta]
|
|
|
|
|
|
class MetaUpdateResult(Schema):
|
|
id: str
|
|
meta: ConvMeta
|
|
|
|
|
|
class ArchiveOldResult(Schema):
|
|
archived: int # how many conversations were newly archived
|
|
cutoff: str # UTC start-of-yesterday used as the age threshold
|
|
|
|
|
|
# ── deploy status ────────────────────────────────────────────────────────────
|
|
class DeployStep(Schema):
|
|
"""One phase of the blue-green deploy, with its rendered dot state."""
|
|
n: int
|
|
label: str
|
|
# pending (grey) · active (loading) · done (green) · failed (red)
|
|
state: Literal["pending", "active", "done", "failed"]
|
|
|
|
|
|
class DeployStatus(Schema):
|
|
"""Snapshot of the in-flight ai-agent deploy (``deploy.sh``), or ``null``.
|
|
|
|
Written by the host-side deploy script into the data dir and polled by the
|
|
backend, which pings a ``deploy`` SSE event on change. Drives the sticky
|
|
in-app deploy banner (worktree label + a dot-per-phase timeline)."""
|
|
# Worktree / branch label the deploy was kicked off from.
|
|
label: Optional[str] = None
|
|
status: Literal["running", "done", "failed"]
|
|
phaseNum: int
|
|
totalPhases: int
|
|
# Human description of the current phase.
|
|
phase: Optional[str] = None
|
|
steps: list[DeployStep]
|
|
startedAt: Optional[int] = None
|
|
updatedAt: Optional[int] = None
|
|
# The conversation that triggered the deploy, for the expanded panel's link.
|
|
sessionId: Optional[str] = None
|
|
convUrl: Optional[str] = None
|
|
convTitle: Optional[str] = None
|