import os from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from sqlalchemy.orm import Session as DBSession from app.core import settings_store from app.core.auth import require_settings_password from app.core.memory_kb import local_knowledge from app.db.database import get_db router = APIRouter(prefix="/api/settings", tags=["settings"], dependencies=[Depends(require_settings_password)]) class SettingEntry(BaseModel): key: str is_set: bool # 秘匿キーはマスク済みの値、それ以外は現在値そのもの。未設定なら None display_value: str | None # 入力欄をパスワード扱いにするか(APIキー等は True) secret: bool overridden: bool class SaveSettingsRequest(BaseModel): values: dict[str, str] @router.get("", response_model=list[SettingEntry]) def list_settings(db: DBSession = Depends(get_db)): return settings_store.list_masked(db) @router.put("", response_model=list[SettingEntry]) async def save_settings(req: SaveSettingsRequest, db: DBSession = Depends(get_db)): before_embedding_model = os.environ.get("LOCAL_KB_EMBEDDING_MODEL") try: for key, value in req.values.items(): if not value: continue # 空欄は「変更なし」として無視する settings_store.save(db, key, value) except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) from e _reembed_if_model_changed(before_embedding_model) return settings_store.list_masked(db) @router.delete("/{key}", response_model=list[SettingEntry]) async def clear_setting(key: str, db: DBSession = Depends(get_db)): before_embedding_model = os.environ.get("LOCAL_KB_EMBEDDING_MODEL") try: settings_store.clear(db, key) except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) from e _reembed_if_model_changed(before_embedding_model) return settings_store.list_masked(db) def _reembed_if_model_changed(before: str | None) -> None: """埋め込みモデルが変わったら、ローカルKBの再埋め込みをバックグラウンドで走らせる。 モデルが違うと次元数も意味空間も変わり、古いベクトルは比較しても無意味なので、 設定を変えた時点で全件やり直す(完了を待たずに応答は返す)。 """ if os.environ.get("LOCAL_KB_EMBEDDING_MODEL") == before: return local_knowledge.start_reembed_all()