feat(sync): 显式导入 Vault 前预览旧版人设

This commit is contained in:
2026-09-09 07:03:29 +08:00
parent e2dbddb979
commit 3ef4766da4
7 changed files with 146 additions and 3 deletions
+6
View File
@@ -1624,6 +1624,12 @@ def get_global_persona():
return load_persona()
@router.get("/settings/persona/legacy", tags=["Settings"])
def get_legacy_persona_preview():
from app.services.persona_settings import legacy_persona_preview
return legacy_persona_preview()
@router.put("/settings/persona", response_model=PersonaSettings, tags=["Settings"])
def put_global_persona(request: PersonaSettings):
return save_persona(request)
+15
View File
@@ -42,6 +42,21 @@ def load_persona():
return PersonaSettings.model_validate_json(row[0]) if row else PersonaSettings()
def legacy_persona_preview():
"""Explicit read-only import source; no automatic Vault ownership inference."""
from app.errors import ApiError
from app.services.desktop_notes import call
if not _desktop():
raise ApiError(404, 'RESOURCE_NOT_FOUND', '此入口仅用于桌面人设导入。')
call('persona.get', id='default') # Revalidate the authenticated Vault at Host.
with closing(connection()) as conn:
row = conn.execute("SELECT data FROM global_persona WHERE id=1").fetchone()
if not row:
return {'available': False, 'persona': None}
source = PersonaSettings.model_validate_json(row[0])
return {'available': True, 'persona': source.model_dump(exclude={'revision'})}
def save_persona(settings):
if _desktop():
from uuid import uuid4
+30
View File
@@ -89,3 +89,33 @@ def test_desktop_missing_persona_does_not_import_unowned_global_data(monkeypatch
monkeypatch.setattr(persona_settings, '_desktop', lambda: True)
monkeypatch.setattr(desktop_notes, 'call', lambda *args, **kwargs: None)
assert load_persona() == PersonaSettings()
def test_legacy_preview_requires_host_scope_and_never_mutates_source(monkeypatch):
from app.services import persona_settings, desktop_notes
original = save_persona(PersonaSettings(system_prompt='legacy preview', version=0))
monkeypatch.setattr(persona_settings, '_desktop', lambda: True)
calls = []
def allowed(method, **params):
calls.append((method, params))
return None
monkeypatch.setattr(desktop_notes, 'call', allowed)
preview = persona_settings.legacy_persona_preview()
assert preview['available'] is True
assert preview['persona']['system_prompt'] == 'legacy preview'
assert 'revision' not in preview['persona']
assert calls == [('persona.get', {'id': 'default'})]
def denied(*args, **kwargs):
raise ApiError(409, 'VAULT_PERMISSION_CHANGED', 'controlled')
monkeypatch.setattr(desktop_notes, 'call', denied)
with pytest.raises(ApiError):
persona_settings.legacy_persona_preview()
monkeypatch.setattr(persona_settings, '_desktop', lambda: False)
assert load_persona() == original
def test_legacy_preview_reports_no_source_without_creating_persona(monkeypatch):
from app.services import persona_settings, desktop_notes
monkeypatch.setattr(persona_settings, '_desktop', lambda: True)
monkeypatch.setattr(desktop_notes, 'call', lambda *args, **kwargs: None)
assert persona_settings.legacy_persona_preview() == {'available': False, 'persona': None}