mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-11-06 13:12:27 +01:00
Squash Odysseus development history
This commit is contained in:
+78
-14
@@ -6,6 +6,7 @@ from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Request, Response
|
||||
from core.middleware import require_admin
|
||||
from services.memory import MemoryStoreUnreadable
|
||||
from src.auth_helpers import get_current_user
|
||||
from src.settings import load_settings, save_settings, load_features, save_features
|
||||
|
||||
@@ -76,8 +77,21 @@ def setup_backup_routes(memory_manager, preset_manager, skills_manager) -> APIRo
|
||||
|
||||
# ── Memories ──
|
||||
if "memories" in body and isinstance(body["memories"], list):
|
||||
existing = memory_manager.load_all()
|
||||
existing_texts = {e.get("text", "").strip().lower() for e in existing}
|
||||
# Strict load: importing on top of an unreadable store would write
|
||||
# only the incoming rows back and drop everything already saved.
|
||||
try:
|
||||
existing = memory_manager.load_all_for_update()
|
||||
except MemoryStoreUnreadable as e:
|
||||
logger.error("Refusing to import memories: %s", e)
|
||||
raise HTTPException(
|
||||
503, "Memory store is temporarily unreadable — nothing was imported."
|
||||
)
|
||||
# Dedup against THIS user's own memories only. Using every tenant's
|
||||
# rows (load_all) meant a memory whose text matched any other
|
||||
# user's was silently skipped, so the importing user lost their own
|
||||
# data. The full store is still saved back below.
|
||||
existing_texts = {e.get("text", "").strip().lower()
|
||||
for e in existing if e.get("owner") == user}
|
||||
added = 0
|
||||
for mem in body["memories"]:
|
||||
if not isinstance(mem, dict) or not mem.get("text"):
|
||||
@@ -96,24 +110,74 @@ def setup_backup_routes(memory_manager, preset_manager, skills_manager) -> APIRo
|
||||
# ── Skills ──
|
||||
if "skills" in body and isinstance(body["skills"], list):
|
||||
existing = skills_manager.load_all()
|
||||
existing_ids = {s.get("id") for s in existing}
|
||||
existing_titles = {s.get("title", "").strip().lower() for s in existing}
|
||||
# Dedup against THIS user's own skills only. Using every tenant's
|
||||
# rows (load_all) meant a skill whose id/name/title matched any
|
||||
# other user's was silently skipped, so the importing user lost
|
||||
# their own data — same cross-tenant bug fixed for memories above.
|
||||
# The full store is still saved back below.
|
||||
own = [s for s in existing if s.get("owner") == user]
|
||||
existing_names = {s.get("name") for s in own if s.get("name")}
|
||||
existing_ids = {s.get("id") for s in own if s.get("id")}
|
||||
existing_titles = {
|
||||
(s.get("title") or s.get("description") or "").strip().lower()
|
||||
for s in own
|
||||
}
|
||||
added = 0
|
||||
for skill in body["skills"]:
|
||||
if not isinstance(skill, dict) or not skill.get("title"):
|
||||
if not isinstance(skill, dict):
|
||||
continue
|
||||
# Skip if same id or same title already exists
|
||||
if skill.get("id") in existing_ids:
|
||||
title = (
|
||||
skill.get("title") or skill.get("description")
|
||||
or skill.get("name") or ""
|
||||
).strip()
|
||||
if not title:
|
||||
continue
|
||||
if skill["title"].strip().lower() in existing_titles:
|
||||
sid = skill.get("id") or skill.get("name")
|
||||
if sid and sid in existing_ids:
|
||||
continue
|
||||
if user and not skill.get("owner"):
|
||||
skill["owner"] = user
|
||||
existing.append(skill)
|
||||
existing_ids.add(skill.get("id"))
|
||||
existing_titles.add(skill["title"].strip().lower())
|
||||
nm = skill.get("name")
|
||||
if nm and nm in existing_names:
|
||||
continue
|
||||
if title.lower() in existing_titles:
|
||||
continue
|
||||
owner = skill.get("owner")
|
||||
if user and not owner:
|
||||
owner = user
|
||||
# Skills live on disk as SKILL.md files; the old JSON-era
|
||||
# skills_manager.save() no longer exists. Write each new skill
|
||||
# via add_skill (source="user" skips auto-dedup — this is an
|
||||
# explicit backup restore).
|
||||
result = skills_manager.add_skill(
|
||||
title=title,
|
||||
name=skill.get("name"),
|
||||
description=skill.get("description"),
|
||||
problem=skill.get("problem", ""),
|
||||
solution=skill.get("solution", ""),
|
||||
steps=skill.get("steps"),
|
||||
tags=skill.get("tags"),
|
||||
source="user",
|
||||
teacher_model=skill.get("teacher_model"),
|
||||
confidence=skill.get("confidence", 0.8),
|
||||
owner=owner,
|
||||
category=skill.get("category", "general"),
|
||||
when_to_use=skill.get("when_to_use"),
|
||||
procedure=skill.get("procedure"),
|
||||
pitfalls=skill.get("pitfalls"),
|
||||
verification=skill.get("verification"),
|
||||
platforms=skill.get("platforms"),
|
||||
requires_toolsets=skill.get("requires_toolsets"),
|
||||
fallback_for_toolsets=skill.get("fallback_for_toolsets"),
|
||||
status=skill.get("status", "draft"),
|
||||
version=skill.get("version", "1.0.0"),
|
||||
)
|
||||
if result.get("_deduped"):
|
||||
continue
|
||||
if result.get("name"):
|
||||
existing_names.add(result["name"])
|
||||
if result.get("id"):
|
||||
existing_ids.add(result["id"])
|
||||
existing_titles.add(title.lower())
|
||||
added += 1
|
||||
skills_manager.save(existing)
|
||||
imported.append(f"{added} skills")
|
||||
|
||||
# ── Presets ──
|
||||
|
||||
Reference in New Issue
Block a user