155 lines
5.3 KiB
Python
155 lines
5.3 KiB
Python
from dataclasses import dataclass, field
|
|||
|
|
from datetime import datetime
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
from gfa_core.mas.memory.base import SearchResult
|
||
|
|
from gfa_core.mas.memory.case import AnalysisCase, CaseMemory
|
||
|
|
from gfa_core.mas.memory.knowledge import KnowledgeEntry, KnowledgeMemory
|
||
|
|
from gfa_core.mas.memory.session import SessionData, SessionMemory
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass
|
||
|
|
class MemoryStats:
|
||
|
|
session_count: int = 0
|
||
|
|
case_count: int = 0
|
||
|
|
knowledge_count: int = 0
|
||
|
|
last_updated: datetime = field(default_factory=datetime.now)
|
||
|
|
|
||
|
|
|
||
|
|
class MemoryManager:
|
||
|
|
def __init__(
|
||
|
|
self,
|
||
|
|
session_memory: SessionMemory | None = None,
|
||
|
|
case_memory: CaseMemory | None = None,
|
||
|
|
knowledge_memory: KnowledgeMemory | None = None,
|
||
|
|
):
|
||
|
|
self.session = session_memory or SessionMemory()
|
||
|
|
self.case = case_memory or CaseMemory()
|
||
|
|
self.knowledge = knowledge_memory or KnowledgeMemory()
|
||
|
|
|
||
|
|
async def create_session(self, session_id: str, initial_state: dict[str, Any] | None = None) -> SessionData:
|
||
|
|
session = SessionData(
|
||
|
|
session_id=session_id,
|
||
|
|
state=initial_state or {},
|
||
|
|
)
|
||
|
|
await self.session.store(session_id, session)
|
||
|
|
return session
|
||
|
|
|
||
|
|
async def get_session(self, session_id: str) -> SessionData | None:
|
||
|
|
return await self.session.retrieve(session_id)
|
||
|
|
|
||
|
|
async def add_message_to_session(self, session_id: str, role: str, content: str) -> None:
|
||
|
|
await self.session.add_message(session_id, {"role": role, "content": content})
|
||
|
|
|
||
|
|
async def update_session_state(self, session_id: str, state: dict[str, Any]) -> None:
|
||
|
|
await self.session.update_state(session_id, state)
|
||
|
|
|
||
|
|
async def create_case(
|
||
|
|
self,
|
||
|
|
case_id: str,
|
||
|
|
user_id: str | None = None,
|
||
|
|
gene_family: str | None = None,
|
||
|
|
species: str | None = None,
|
||
|
|
workflow: list[dict] | None = None,
|
||
|
|
) -> AnalysisCase:
|
||
|
|
case = AnalysisCase(
|
||
|
|
case_id=case_id,
|
||
|
|
user_id=user_id,
|
||
|
|
gene_family=gene_family,
|
||
|
|
species=species,
|
||
|
|
workflow=workflow or [],
|
||
|
|
)
|
||
|
|
await self.case.store(case_id, case)
|
||
|
|
return case
|
||
|
|
|
||
|
|
async def get_case(self, case_id: str) -> AnalysisCase | None:
|
||
|
|
return await self.case.retrieve(case_id)
|
||
|
|
|
||
|
|
async def update_case_results(self, case_id: str, results: dict[str, Any]) -> None:
|
||
|
|
case = await self.case.retrieve(case_id)
|
||
|
|
if case:
|
||
|
|
case.results.update(results)
|
||
|
|
await self.case.store(case_id, case)
|
||
|
|
|
||
|
|
async def complete_case(self, case_id: str) -> None:
|
||
|
|
await self.case.update_status(case_id, "completed")
|
||
|
|
|
||
|
|
async def add_case_feedback(self, case_id: str, feedback: dict[str, Any]) -> None:
|
||
|
|
await self.case.add_feedback(case_id, feedback)
|
||
|
|
|
||
|
|
async def add_knowledge(
|
||
|
|
self,
|
||
|
|
entry_id: str,
|
||
|
|
content: str,
|
||
|
|
embedding: list[float] | None = None,
|
||
|
|
source: str | None = None,
|
||
|
|
category: str | None = None,
|
||
|
|
tags: list[str] | None = None,
|
||
|
|
) -> None:
|
||
|
|
await self.knowledge.add_entry(
|
||
|
|
entry_id=entry_id,
|
||
|
|
content=content,
|
||
|
|
embedding=embedding,
|
||
|
|
source=source,
|
||
|
|
category=category,
|
||
|
|
tags=tags,
|
||
|
|
)
|
||
|
|
|
||
|
|
async def search_knowledge(
|
||
|
|
self,
|
||
|
|
query_embedding: list[float],
|
||
|
|
top_k: int = 10,
|
||
|
|
) -> list[SearchResult]:
|
||
|
|
return await self.knowledge.search(query_embedding, top_k)
|
||
|
|
|
||
|
|
async def search_knowledge_by_tags(self, tags: list[str], top_k: int = 10) -> list[SearchResult]:
|
||
|
|
return await self.knowledge.search_by_tags(tags, top_k)
|
||
|
|
|
||
|
|
async def extract_case_to_knowledge(self, case_id: str) -> KnowledgeEntry | None:
|
||
|
|
case = await self.case.retrieve(case_id)
|
||
|
|
if not case or case.status != "completed":
|
||
|
|
return None
|
||
|
|
|
||
|
|
entry_id = f"case_{case_id}"
|
||
|
|
content = self._extract_knowledge_content(case)
|
||
|
|
|
||
|
|
entry = KnowledgeEntry(
|
||
|
|
entry_id=entry_id,
|
||
|
|
content=content,
|
||
|
|
source=f"case:{case_id}",
|
||
|
|
category="workflow_template",
|
||
|
|
tags=[case.gene_family, case.species] if case.gene_family and case.species else [],
|
||
|
|
metadata={
|
||
|
|
"gene_family": case.gene_family,
|
||
|
|
"species": case.species,
|
||
|
|
"workflow_steps": len(case.workflow),
|
||
|
|
},
|
||
|
|
)
|
||
|
|
|
||
|
|
await self.knowledge.store(entry_id, entry)
|
||
|
|
return entry
|
||
|
|
|
||
|
|
def _extract_knowledge_content(self, case: AnalysisCase) -> str:
|
||
|
|
parts = []
|
||
|
|
if case.gene_family:
|
||
|
|
parts.append(f"Gene Family: {case.gene_family}")
|
||
|
|
if case.species:
|
||
|
|
parts.append(f"Species: {case.species}")
|
||
|
|
if case.workflow:
|
||
|
|
parts.append("Workflow:")
|
||
|
|
for i, step in enumerate(case.workflow, 1):
|
||
|
|
parts.append(f" {i}. {step.get('tool', 'unknown')}: {step.get('description', '')}")
|
||
|
|
return "\n".join(parts)
|
||
|
|
|
||
|
|
async def get_stats(self) -> MemoryStats:
|
||
|
|
return MemoryStats(
|
||
|
|
session_count=len(self.session._sessions),
|
||
|
|
case_count=len(await self.case.list_by_status("completed")),
|
||
|
|
knowledge_count=len(self.knowledge._entries),
|
||
|
|
)
|
||
|
|
|
||
|
|
async def clear_all(self) -> None:
|
||
|
|
await self.session.clear()
|
||
|
|
await self.case.clear()
|
||
|
|
await self.knowledge.clear()
|