Spaces:
Running
Running
Update openspace/skill_engine/store.py
Browse files- openspace/skill_engine/store.py +114 -6
openspace/skill_engine/store.py
CHANGED
|
@@ -795,10 +795,8 @@ class SkillStore:
|
|
| 795 |
).fetchall()
|
| 796 |
else:
|
| 797 |
rows = conn.execute("SELECT * FROM skill_records").fetchall()
|
| 798 |
-
|
| 799 |
-
for
|
| 800 |
-
rec = self._to_record(conn, row)
|
| 801 |
-
result[rec.skill_id] = rec
|
| 802 |
logger.info(f"Loaded {len(result)} skill records (active_only={active_only})")
|
| 803 |
return result
|
| 804 |
|
|
@@ -841,7 +839,7 @@ class SkillStore:
|
|
| 841 |
"ORDER BY lineage_generation ASC",
|
| 842 |
(name,),
|
| 843 |
).fetchall()
|
| 844 |
-
return
|
| 845 |
|
| 846 |
@_db_retry()
|
| 847 |
def load_by_category(
|
|
@@ -864,7 +862,7 @@ class SkillStore:
|
|
| 864 |
"SELECT * FROM skill_records WHERE category=?",
|
| 865 |
(category.value,),
|
| 866 |
).fetchall()
|
| 867 |
-
return
|
| 868 |
|
| 869 |
@_db_retry()
|
| 870 |
def load_analyses(
|
|
@@ -1437,6 +1435,116 @@ class SkillStore:
|
|
| 1437 |
return analysis_id
|
| 1438 |
|
| 1439 |
# Deserialization
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1440 |
def _to_record(
|
| 1441 |
self, conn: sqlite3.Connection, row: sqlite3.Row
|
| 1442 |
) -> SkillRecord:
|
|
|
|
| 795 |
).fetchall()
|
| 796 |
else:
|
| 797 |
rows = conn.execute("SELECT * FROM skill_records").fetchall()
|
| 798 |
+
records = self._to_records_bulk(conn, rows)
|
| 799 |
+
result: Dict[str, SkillRecord] = {rec.skill_id: rec for rec in records}
|
|
|
|
|
|
|
| 800 |
logger.info(f"Loaded {len(result)} skill records (active_only={active_only})")
|
| 801 |
return result
|
| 802 |
|
|
|
|
| 839 |
"ORDER BY lineage_generation ASC",
|
| 840 |
(name,),
|
| 841 |
).fetchall()
|
| 842 |
+
return self._to_records_bulk(conn, rows)
|
| 843 |
|
| 844 |
@_db_retry()
|
| 845 |
def load_by_category(
|
|
|
|
| 862 |
"SELECT * FROM skill_records WHERE category=?",
|
| 863 |
(category.value,),
|
| 864 |
).fetchall()
|
| 865 |
+
return self._to_records_bulk(conn, rows)
|
| 866 |
|
| 867 |
@_db_retry()
|
| 868 |
def load_analyses(
|
|
|
|
| 1435 |
return analysis_id
|
| 1436 |
|
| 1437 |
# Deserialization
|
| 1438 |
+
def _to_records_bulk(
|
| 1439 |
+
self, conn: sqlite3.Connection, rows: List[sqlite3.Row]
|
| 1440 |
+
) -> List[SkillRecord]:
|
| 1441 |
+
"""Bulk deserialize skill_records rows → List[SkillRecord]."""
|
| 1442 |
+
if not rows:
|
| 1443 |
+
return []
|
| 1444 |
+
|
| 1445 |
+
sids = [r["skill_id"] for r in rows]
|
| 1446 |
+
parents_map = {sid: [] for sid in sids}
|
| 1447 |
+
deps_map = {sid: [] for sid in sids}
|
| 1448 |
+
tags_map = {sid: [] for sid in sids}
|
| 1449 |
+
analyses_map = {sid: [] for sid in sids}
|
| 1450 |
+
|
| 1451 |
+
def chunks(lst, n):
|
| 1452 |
+
for i in range(0, len(lst), n):
|
| 1453 |
+
yield lst[i:i + n]
|
| 1454 |
+
|
| 1455 |
+
for chunk in chunks(sids, 900):
|
| 1456 |
+
placeholders = ",".join(["?"] * len(chunk))
|
| 1457 |
+
|
| 1458 |
+
# Parents
|
| 1459 |
+
p_rows = conn.execute(
|
| 1460 |
+
f"SELECT skill_id, parent_skill_id FROM skill_lineage_parents WHERE skill_id IN ({placeholders})",
|
| 1461 |
+
chunk,
|
| 1462 |
+
).fetchall()
|
| 1463 |
+
for pr in p_rows:
|
| 1464 |
+
parents_map[pr["skill_id"]].append(pr["parent_skill_id"])
|
| 1465 |
+
|
| 1466 |
+
# Tool deps
|
| 1467 |
+
d_rows = conn.execute(
|
| 1468 |
+
f"SELECT skill_id, tool_key, critical FROM skill_tool_deps WHERE skill_id IN ({placeholders})",
|
| 1469 |
+
chunk,
|
| 1470 |
+
).fetchall()
|
| 1471 |
+
for dr in d_rows:
|
| 1472 |
+
deps_map[dr["skill_id"]].append(dr)
|
| 1473 |
+
|
| 1474 |
+
# Tags
|
| 1475 |
+
t_rows = conn.execute(
|
| 1476 |
+
f"SELECT skill_id, tag FROM skill_tags WHERE skill_id IN ({placeholders})",
|
| 1477 |
+
chunk,
|
| 1478 |
+
).fetchall()
|
| 1479 |
+
for tr in t_rows:
|
| 1480 |
+
tags_map[tr["skill_id"]].append(tr["tag"])
|
| 1481 |
+
|
| 1482 |
+
# Analyses
|
| 1483 |
+
a_rows = conn.execute(
|
| 1484 |
+
f"""
|
| 1485 |
+
SELECT ea.*, sj.skill_id as sj_skill_id
|
| 1486 |
+
FROM execution_analyses ea
|
| 1487 |
+
JOIN skill_judgments sj ON ea.id = sj.analysis_id
|
| 1488 |
+
WHERE sj.skill_id IN ({placeholders})
|
| 1489 |
+
ORDER BY ea.timestamp DESC
|
| 1490 |
+
""",
|
| 1491 |
+
chunk,
|
| 1492 |
+
).fetchall()
|
| 1493 |
+
for ar in a_rows:
|
| 1494 |
+
sid = ar["sj_skill_id"]
|
| 1495 |
+
if len(analyses_map[sid]) < SkillRecord.MAX_RECENT:
|
| 1496 |
+
analyses_map[sid].append(self._to_analysis(conn, ar))
|
| 1497 |
+
|
| 1498 |
+
result = []
|
| 1499 |
+
for row in rows:
|
| 1500 |
+
sid = row["skill_id"]
|
| 1501 |
+
|
| 1502 |
+
raw_snapshot = row["lineage_content_snapshot"] or "{}"
|
| 1503 |
+
snapshot: Dict[str, str] = json.loads(raw_snapshot)
|
| 1504 |
+
|
| 1505 |
+
lineage = SkillLineage(
|
| 1506 |
+
origin=SkillOrigin(row["lineage_origin"]),
|
| 1507 |
+
generation=row["lineage_generation"],
|
| 1508 |
+
parent_skill_ids=parents_map[sid],
|
| 1509 |
+
source_task_id=row["lineage_source_task_id"],
|
| 1510 |
+
change_summary=row["lineage_change_summary"],
|
| 1511 |
+
content_diff=row["lineage_content_diff"],
|
| 1512 |
+
content_snapshot=snapshot,
|
| 1513 |
+
created_at=datetime.fromisoformat(row["lineage_created_at"]),
|
| 1514 |
+
created_by=row["lineage_created_by"],
|
| 1515 |
+
)
|
| 1516 |
+
|
| 1517 |
+
deps = deps_map[sid]
|
| 1518 |
+
record = SkillRecord(
|
| 1519 |
+
skill_id=sid,
|
| 1520 |
+
name=row["name"],
|
| 1521 |
+
description=row["description"],
|
| 1522 |
+
path=row["path"],
|
| 1523 |
+
is_active=bool(row["is_active"]),
|
| 1524 |
+
category=SkillCategory(row["category"]),
|
| 1525 |
+
tags=tags_map[sid],
|
| 1526 |
+
visibility=(
|
| 1527 |
+
SkillVisibility(row["visibility"])
|
| 1528 |
+
if row["visibility"] else SkillVisibility.PRIVATE
|
| 1529 |
+
),
|
| 1530 |
+
creator_id=row["creator_id"] or "",
|
| 1531 |
+
lineage=lineage,
|
| 1532 |
+
tool_dependencies=[r["tool_key"] for r in deps],
|
| 1533 |
+
critical_tools=[
|
| 1534 |
+
r["tool_key"] for r in deps if r["critical"]
|
| 1535 |
+
],
|
| 1536 |
+
total_selections=row["total_selections"],
|
| 1537 |
+
total_applied=row["total_applied"],
|
| 1538 |
+
total_completions=row["total_completions"],
|
| 1539 |
+
total_fallbacks=row["total_fallbacks"],
|
| 1540 |
+
recent_analyses=analyses_map[sid],
|
| 1541 |
+
first_seen=datetime.fromisoformat(row["first_seen"]),
|
| 1542 |
+
last_updated=datetime.fromisoformat(row["last_updated"]),
|
| 1543 |
+
)
|
| 1544 |
+
result.append(record)
|
| 1545 |
+
|
| 1546 |
+
return result
|
| 1547 |
+
|
| 1548 |
def _to_record(
|
| 1549 |
self, conn: sqlite3.Connection, row: sqlite3.Row
|
| 1550 |
) -> SkillRecord:
|