Przemyslaw Chachaj
Add habit engine with MCP tools
a98fb2d
Raw
History Blame Contribute Delete
4.53 kB
import json
import time
from pathlib import Path
from typing import Dict, Any, Optional
# Prosty storage w pliku JSON (działa w Space tak długo, jak instancja żyje)
STORAGE_PATH = Path(__file__).resolve().parent.parent / "storage" / "habits.json"
STORAGE_PATH.parent.mkdir(parents=True, exist_ok=True)
# Opcjonalne: zaawansowany pipeline z reels-agent (transkrypcja, summary, Nebius)
try:
from . import transcriber, summarizer # type: ignore
except Exception:
transcriber = None
summarizer = None
def _load_db() -> Dict[str, Any]:
if not STORAGE_PATH.exists():
return {}
try:
with STORAGE_PATH.open("r", encoding="utf-8") as f:
return json.load(f)
except Exception:
return {}
def _save_db(db: Dict[str, Any]) -> None:
with STORAGE_PATH.open("w", encoding="utf-8") as f:
json.dump(db, f, ensure_ascii=False, indent=2)
def _build_initial_step(link: str, category: str) -> str:
"""
Tu próbujemy użyć starego pipeline'u (reels-agent):
- transkrypcja
- summary → konkretna akcja
Jeśli te funkcje nie istnieją / wywalą się → fallback na prosty tekst.
"""
# Fallback – zawsze działa
fallback = "Obejrzyj materiał i zapisz 1 konkretną rzecz, którą dziś wdrożysz."
# Jeśli nie ma modułów – wracamy fallback
if transcriber is None or summarizer is None:
return fallback
try:
# PRZYKŁADOWA KONWENCJA – dopasujesz w razie czego u siebie:
# transcriber.transcribe_link(link) -> str (pełna transkrypcja)
# summarizer.summarize_to_action(text, category) -> str (krótka akcja)
if hasattr(transcriber, "transcribe_link") and hasattr(summarizer, "summarize_to_action"):
text = transcriber.transcribe_link(link) # type: ignore[attr-defined]
action = summarizer.summarize_to_action(text, category) # type: ignore[attr-defined]
if isinstance(action, str) and action.strip():
return action.strip()
except Exception:
# Nie zabijamy Space’a – po prostu wracamy prostą wersję
return fallback
return fallback
def create_contract(uid: str, link: str, category: str) -> Dict[str, Any]:
"""
Tworzy / nadpisuje kontrakt nawyku:
- uid: ID usera (np. szef_1_career)
- link: IG/YT/TikTok
- category: 'career' / 'health' / 'relationships' / ...
Zwraca dict gotowy do wysłania jako JSON przez MCP.
"""
db = _load_db()
initial_step = _build_initial_step(link, category)
contract = {
"uid": uid,
"link": link,
"category": category,
"created_at": int(time.time()),
"streak": 0,
"last_done_at": None,
"next_step": initial_step,
"history": [], # lista wykonanych kroków
}
db[uid] = contract
_save_db(db)
return contract
def get_contract(uid: str) -> Optional[Dict[str, Any]]:
db = _load_db()
return db.get(uid)
def complete_step(uid: str, note: Optional[str] = None) -> Dict[str, Any]:
"""
User zrobił zadanie → zwiększamy streak, dopisujemy do historii
i generujemy kolejny krok (na razie prosty).
"""
db = _load_db()
if uid not in db:
raise ValueError(f"Contract with uid={uid} not found")
contract = db[uid]
now = int(time.time())
# aktualizacja streak
contract["streak"] = int(contract.get("streak") or 0) + 1
contract["last_done_at"] = now
# dopisanie historii
contract.setdefault("history", [])
contract["history"].append(
{
"ts": now,
"done_step": contract.get("next_step"),
"note": note,
}
)
# nowy krok - tutaj możesz później wpiąć bardziej zaawansowany plan_generator
link = contract.get("link", "")
category = contract.get("category", "general")
contract["next_step"] = _build_initial_step(link, category)
db[uid] = contract
_save_db(db)
return contract
def get_next_step(uid: str) -> Dict[str, Any]:
"""
Zwraca aktualny kontrakt i next_step dla danego uid.
"""
contract = get_contract(uid)
if not contract:
raise ValueError(f"Contract with uid={uid} not found")
return {
"uid": contract["uid"],
"link": contract["link"],
"category": contract["category"],
"streak": contract.get("streak", 0),
"next_step": contract.get("next_step"),
"last_done_at": contract.get("last_done_at"),
}