feat(rag): Phase 1 — rewrite build_card_text to natural-language format (#137)

* feat(rag): Phase 1 — rewrite build_card_text to natural-language format

* feat(rag): Phase 2 — Forge-enriched ChromaDB RAG store (2a–2e)

Phase 2a: Forge lazy import guard (_try_import_forge_lookup)
Phase 2b: _build_document appends Forge trigger/replacement/static/keyword tags
Phase 2c: _build_metadata adds has_trigger, has_replacement, has_static, trigger_modes fields
Phase 2d: query_cards extended with require_trigger, require_replacement, require_static params + ChromaDB where clause support
Phase 2e: get_rag_stats reports forge_enriched and forge_coverage; _write_build_meta tracks coverage; build loop counts enriched cards

* feat(rag): Phase 3 — add mtgrules second ChromaDB collection

Adds build_rules_index() and query_rules() operating on a separate
'mtgrules' ChromaDB collection. Chunks MagicCompRules.txt by rule-number
boundaries, embeds each chunk, and exposes semantic search. Completely
independent from the mtg_oracle card collection.

* feat(coach): Phase 4 — behavior-aware RAG + rules context in coach_service

Phase 4a: run_coaching_session uses dual RAG query
  - Primary: commander synergy (n=10)
  - Secondary: require_replacement=True for graveyard/protection cards (n=5)
  - Deduplicates by name before merging
Phase 4b: query_rules() fetches 3 Commander rules chunks to ground the prompt
  - Passed as rules_context kwarg to build_user_prompt()

* feat(rag): Phase 5 — add /api/rag/build-rules and /api/rag/query-rules endpoints
This commit is contained in:
KoalaTrapLord authored and GitHub committed 2026-03-29 02:11:38 -04:00
1 parent ac536999eb
commit aefd0cace6
4 files changed
+387 -190

No files matched your search

+50
View File
@@ -119,3 +119,53 @@ async def rag_status_detail():
except Exception as exc:
log.exception("RAG status check failed")
return {"status": "error", "error": str(exc)}
# ── Phase 5: Rules RAG endpoints ───────────────────────────────────────────
class RulesBuildRequest(BaseModel):
rules_path: str = Field(
...,
description="Absolute or repo-relative path to MagicCompRules.txt",
)
chunk_size: int = Field(400, ge=100, le=2000, description="Approx characters per chunk")
@router.post("/api/rag/build-rules")
async def rag_build_rules(body: RulesBuildRequest):
"""
Build the mtgrules ChromaDB collection from the MTG Comprehensive Rules text file.
Download MagicCompRules.txt from https://magic.wizards.com/en/rules and pass the
local path as `rules_path`. This is a one-time or yearly operation.
"""
try:
from services.rag_store import build_rules_index
stats = build_rules_index(body.rules_path, chunk_size=body.chunk_size)
return {"status": "ok", **stats}
except FileNotFoundError as exc:
raise HTTPException(status_code=404, detail=str(exc))
except Exception as exc:
log.exception("Rules RAG build failed")
raise HTTPException(status_code=500, detail=str(exc))
@router.get("/api/rag/query-rules")
async def rag_query_rules(
q: str,
n: int = 5,
):
"""
Semantic search over the MTG Comprehensive Rules (mtgrules collection).
Args:
q: Natural-language rules question, e.g. "Does Teferi stop split second?"
n: Number of rule chunks to return (default 5, max 20).
Returns list of {rule_id, text, distance}.
"""
try:
from services.rag_store import query_rules
return {"results": query_rules(q, n_results=min(n, 20))}
except Exception as exc:
log.exception("Rules RAG query failed")
raise HTTPException(status_code=500, detail=str(exc))