Replace repository with DuckLM runtime
This commit is contained in:
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import aiosqlite
|
||||
from pydantic import BaseModel
|
||||
|
||||
from duck_core.tasks.store import utc_now
|
||||
|
||||
|
||||
class ExperienceRecord(BaseModel):
|
||||
id: int | None = None
|
||||
task_id: str
|
||||
skill_id: str | None = None
|
||||
summary: str
|
||||
result: str
|
||||
what_worked: list[str] = []
|
||||
what_failed: list[str] = []
|
||||
reusable_lesson: str | None = None
|
||||
suggested_skill_patch: str | None = None
|
||||
confidence: float | None = None
|
||||
created_at: str
|
||||
|
||||
|
||||
class ExperienceRecorder:
|
||||
def __init__(self, db_path: str):
|
||||
self.db_path = Path(db_path)
|
||||
|
||||
async def init(self) -> None:
|
||||
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
await db.execute(
|
||||
"""
|
||||
create table if not exists experience_records (
|
||||
id integer primary key autoincrement,
|
||||
task_id text not null,
|
||||
skill_id text,
|
||||
summary text not null,
|
||||
result text not null,
|
||||
what_worked_json text,
|
||||
what_failed_json text,
|
||||
reusable_lesson text,
|
||||
suggested_skill_patch text,
|
||||
confidence real,
|
||||
created_at text not null
|
||||
)
|
||||
"""
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
async def record(
|
||||
self,
|
||||
task_id: str,
|
||||
summary: str,
|
||||
result: str,
|
||||
skill_id: str | None = None,
|
||||
what_worked: list[str] | None = None,
|
||||
what_failed: list[str] | None = None,
|
||||
reusable_lesson: str | None = None,
|
||||
suggested_skill_patch: str | None = None,
|
||||
confidence: float | None = None,
|
||||
) -> ExperienceRecord:
|
||||
await self.init()
|
||||
now = utc_now()
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute(
|
||||
"""
|
||||
insert into experience_records(
|
||||
task_id, skill_id, summary, result, what_worked_json,
|
||||
what_failed_json, reusable_lesson, suggested_skill_patch,
|
||||
confidence, created_at
|
||||
) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
task_id,
|
||||
skill_id,
|
||||
summary,
|
||||
result,
|
||||
json.dumps(what_worked or []),
|
||||
json.dumps(what_failed or []),
|
||||
reusable_lesson,
|
||||
suggested_skill_patch,
|
||||
confidence,
|
||||
now,
|
||||
),
|
||||
)
|
||||
await db.commit()
|
||||
row_id = cursor.lastrowid
|
||||
if suggested_skill_patch and skill_id:
|
||||
self.write_skill_update_proposal(task_id, skill_id, suggested_skill_patch)
|
||||
return ExperienceRecord(
|
||||
id=row_id,
|
||||
task_id=task_id,
|
||||
skill_id=skill_id,
|
||||
summary=summary,
|
||||
result=result,
|
||||
what_worked=what_worked or [],
|
||||
what_failed=what_failed or [],
|
||||
reusable_lesson=reusable_lesson,
|
||||
suggested_skill_patch=suggested_skill_patch,
|
||||
confidence=confidence,
|
||||
created_at=now,
|
||||
)
|
||||
|
||||
async def list_records(self) -> list[ExperienceRecord]:
|
||||
await self.init()
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
db.row_factory = aiosqlite.Row
|
||||
cursor = await db.execute(
|
||||
"select * from experience_records order by created_at desc"
|
||||
)
|
||||
rows = await cursor.fetchall()
|
||||
return [self._row_to_record(row) for row in rows]
|
||||
|
||||
async def get_record(self, record_id: int) -> ExperienceRecord | None:
|
||||
await self.init()
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
db.row_factory = aiosqlite.Row
|
||||
cursor = await db.execute(
|
||||
"select * from experience_records where id = ?", (record_id,)
|
||||
)
|
||||
row = await cursor.fetchone()
|
||||
return self._row_to_record(row) if row else None
|
||||
|
||||
def write_skill_update_proposal(self, task_id: str, skill_id: str, patch: str) -> Path:
|
||||
directory = Path("skills/_proposals")
|
||||
directory.mkdir(parents=True, exist_ok=True)
|
||||
path = directory / f"{utc_now().replace(':', '').replace('+', '_')}_{skill_id}.patch.md"
|
||||
path.write_text(
|
||||
"\n".join(
|
||||
[
|
||||
"# Skill update proposal",
|
||||
"",
|
||||
f"Skill: {skill_id}",
|
||||
"",
|
||||
"## Reason",
|
||||
"",
|
||||
"Reflection suggested a reusable skill improvement.",
|
||||
"",
|
||||
"## Proposed changes",
|
||||
"",
|
||||
patch,
|
||||
"",
|
||||
"## Evidence",
|
||||
"",
|
||||
f"Task id: {task_id}",
|
||||
"",
|
||||
"## Risk",
|
||||
"",
|
||||
"Low.",
|
||||
"",
|
||||
"## Requires human approval",
|
||||
"",
|
||||
"Yes.",
|
||||
]
|
||||
)
|
||||
)
|
||||
return path
|
||||
|
||||
def _row_to_record(self, row: aiosqlite.Row) -> ExperienceRecord:
|
||||
return ExperienceRecord(
|
||||
id=row["id"],
|
||||
task_id=row["task_id"],
|
||||
skill_id=row["skill_id"],
|
||||
summary=row["summary"],
|
||||
result=row["result"],
|
||||
what_worked=json.loads(row["what_worked_json"] or "[]"),
|
||||
what_failed=json.loads(row["what_failed_json"] or "[]"),
|
||||
reusable_lesson=row["reusable_lesson"],
|
||||
suggested_skill_patch=row["suggested_skill_patch"],
|
||||
confidence=row["confidence"],
|
||||
created_at=row["created_at"],
|
||||
)
|
||||
Reference in New Issue
Block a user