[Haik]: ckpt, modes subapp is done and satisfactory for now, altho future touch ups may be done

This commit is contained in:
haikdc
2026-04-05 11:38:46 -07:00
parent dbdd6e8cb7
commit 80233bbce3
4 changed files with 67 additions and 69 deletions
@@ -3,10 +3,16 @@
Separated from models.py to keep schema classes small and data separate.
"""
from backend.apps.modes.models import Mode
from backend.config.paths import OUTPUTS_WORKSPACE_DIR as OUTPUTS_WORKSPACE, SKILLS_WORKSPACE_DIR as SKILLS_WORKSPACE
from backend.apps.modes.Mode import Mode
from typing import List
from backend.config.paths import DB_ROOT
import os
BUILTIN_MODES: list[Mode] = [
# NOTE: When the skills and outputs subapps are implemented, we will need to update these paths to import from the subapps.
SKILLS_WORKSPACE: str = os.path.join(DB_ROOT, "skills")
OUTPUTS_WORKSPACE: str = os.path.join(DB_ROOT, "outputs")
BUILTIN_MODES: List[Mode] = [
Mode(
id="agent",
name="Agent",
+16
View File
@@ -0,0 +1,16 @@
from pydantic import BaseModel, Field
from typing import Optional
from uuid import uuid4
class Mode(BaseModel):
id: str = Field(default_factory=lambda: uuid4().hex)
name: str
description: str = ""
system_prompt: Optional[str] = None
tools: Optional[list[str]] = None
default_next_mode: Optional[str] = None
is_builtin: bool = False
icon: str = "smart_toy"
color: str = "#818cf8"
default_folder: Optional[str] = None
-38
View File
@@ -1,38 +0,0 @@
from pydantic import BaseModel, Field
from typing import Optional
from uuid import uuid4
class Mode(BaseModel):
id: str = Field(default_factory=lambda: uuid4().hex)
name: str
description: str = ""
system_prompt: Optional[str] = None
tools: Optional[list[str]] = None
default_next_mode: Optional[str] = None
is_builtin: bool = False
icon: str = "smart_toy"
color: str = "#818cf8"
default_folder: Optional[str] = None
class ModeCreate(BaseModel):
name: str
description: str = ""
system_prompt: Optional[str] = None
tools: Optional[list[str]] = None
default_next_mode: Optional[str] = None
icon: str = "smart_toy"
color: str = "#818cf8"
default_folder: Optional[str] = None
class ModeUpdate(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
system_prompt: Optional[str] = None
tools: Optional[list[str]] = None
default_next_mode: Optional[str] = None
icon: Optional[str] = None
color: Optional[str] = None
default_folder: Optional[str] = None
+42 -28
View File
@@ -1,50 +1,54 @@
import json
import os
import logging
from contextlib import asynccontextmanager
from fastapi import HTTPException
from pydantic import BaseModel
from typing import Optional
from backend.config.Apps import SubApp
from backend.apps.common.json_store import JsonStore
from backend.apps.modes.models import Mode, ModeCreate, ModeUpdate
from backend.apps.modes.builtin import BUILTIN_MODES
from backend.core.db.PydanticStore import PydanticStore
from backend.apps.modes.Mode import Mode
from backend.apps.modes.BUILTIN_MODES import BUILTIN_MODES
from backend.config.paths import DB_ROOT
logger = logging.getLogger(__name__)
from backend.config.paths import MODES_DIR as DATA_DIR
MODE_STORE: PydanticStore[Mode] = PydanticStore[Mode](
model_cls=Mode,
data_dir=os.path.join(DB_ROOT, "modes"),
id_field="id",
dump_mode="json",
not_found_detail="Mode not found",
)
@asynccontextmanager
async def modes_lifespan():
os.makedirs(DATA_DIR, exist_ok=True)
for builtin in BUILTIN_MODES:
path = os.path.join(DATA_DIR, f"{builtin.id}.json")
if not os.path.exists(path):
_save(builtin)
MODE_STORE.save(builtin)
yield
modes = SubApp("modes", modes_lifespan)
_store = JsonStore(Mode, DATA_DIR, not_found_detail="Mode not found")
_load_all = _store.load_all
_save = _store.save
_load = _store.load
load_mode = _store.load_or_none
@modes.router.get("/list")
async def list_modes():
builtin_defaults = {m.id: m.model_dump() for m in BUILTIN_MODES}
return {"modes": [m.model_dump() for m in _load_all()], "builtin_defaults": builtin_defaults}
return {"modes": [m.model_dump() for m in MODE_STORE.load_all()], "builtin_defaults": builtin_defaults}
@modes.router.get("/{mode_id}")
async def get_mode(mode_id: str):
return _load(mode_id).model_dump()
return MODE_STORE.load(mode_id).model_dump()
class ModeCreate(BaseModel):
name: str
description: str = ""
system_prompt: Optional[str] = None
tools: Optional[list[str]] = None
default_next_mode: Optional[str] = None
icon: str = "smart_toy"
color: str = "#818cf8"
default_folder: Optional[str] = None
@modes.router.post("/create")
async def create_mode(body: ModeCreate):
mode = Mode(
@@ -58,16 +62,26 @@ async def create_mode(body: ModeCreate):
default_folder=body.default_folder,
is_builtin=False,
)
_save(mode)
MODE_STORE.save(mode)
return {"ok": True, "mode": mode.model_dump()}
class ModeUpdate(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
system_prompt: Optional[str] = None
tools: Optional[list[str]] = None
default_next_mode: Optional[str] = None
icon: Optional[str] = None
color: Optional[str] = None
default_folder: Optional[str] = None
@modes.router.put("/{mode_id}")
async def update_mode(mode_id: str, body: ModeUpdate):
mode = _load(mode_id)
mode = MODE_STORE.load(mode_id)
for k, v in body.model_dump(exclude_unset=True).items():
setattr(mode, k, v)
_save(mode)
MODE_STORE.save(mode)
return {"ok": True, "mode": mode.model_dump()}
@@ -77,14 +91,14 @@ async def reset_mode(mode_id: str):
builtin = next((m for m in BUILTIN_MODES if m.id == mode_id), None)
if not builtin:
raise HTTPException(status_code=400, detail="Only built-in modes can be reset")
_save(builtin)
MODE_STORE.save(builtin)
return {"ok": True, "mode": builtin.model_dump()}
@modes.router.delete("/{mode_id}")
async def delete_mode(mode_id: str):
mode = _load(mode_id)
mode = MODE_STORE.load(mode_id)
if mode.is_builtin:
raise HTTPException(status_code=403, detail="Cannot delete built-in modes")
_store.delete(mode_id)
MODE_STORE.delete(mode_id)
return {"ok": True}