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

+25 -17
View File
@@ -3,35 +3,43 @@ services/card_text.py
=====================
Shared utility for building card text representations.
Both services/rag_store.py and coach/embeddings.py need to produce
identical text strings for embedding so that vector similarity is
consistent across both systems. This module provides a single
canonical implementation.
Both services/rag_store.py and coach/embeddings.py need to produce
identical text strings for embedding so that vector similarity is
consistent across both systems. This module provides a single
canonical implementation.
Format: Name | Mana Cost | Type | Oracle Text
(fields that are empty/missing are omitted)
Format: Natural-language sentences.
(fields that are empty/missing are omitted)
"""
from __future__ import annotations
def build_card_text(card: dict) -> str:
"""Build the canonical text representation of a card for embedding.
"""Build canonical natural-language card text for embedding.
Produces '{Name} is a {Type}. It costs {ManaCost}. {OracleText}'
Fields that are empty/None are omitted.
Args:
card: Dict with keys 'name', 'mana_cost', 'type_line', 'oracle_text'.
Missing keys are treated as empty strings.
Returns:
Pipe-separated string like 'Sol Ring | {1} | Artifact | {T}: Add {C}{C}.'
Natural language string suitable for nomic-embed-text or GTE models.
"""
parts = [card.get("name", "") or ""]
mana_cost = card.get("mana_cost", "") or ""
if mana_cost:
parts.append(mana_cost)
type_line = card.get("type_line", "") or ""
if type_line:
parts.append(type_line)
oracle = card.get("oracle_text", "") or ""
name = (card.get("name", "") or "").strip()
mana = (card.get("mana_cost", "") or "").strip()
types = (card.get("type_line", "") or "").strip()
oracle = (card.get("oracle_text", "") or "").strip()
parts: list[str] = []
if name:
base = f"{name} is a {types}." if types else f"{name}."
parts.append(base)
if mana:
parts.append(f"It costs {mana}.")
if oracle:
parts.append(oracle)
return " | ".join(parts)
return " ".join(parts) if parts else name