Spaces:
Running
Running
| """μ½ν μΈ λ‘λ β content/ YAML λ©λͺ¨λ¦¬ λ‘λ (MVP ꡬν). | |
| μμ κ³μΈ΅(services)μ μ΄ μΈν°νμ΄μ€λ§ μ¬μ©νλ€. 2λ¨κ³μμ GraphRAG(kb_query νμ±) | |
| ꡬνμΌλ‘ κ΅μ²΄ν΄λ services μ½λκ° λ°λμ§ μμμΌ νλ€ (ablation μ건). | |
| μ€ν¬μΌλ¬ μ€μ½ν: μΉ΄λμλ μ μ μμ(order)κ° μλ€. λͺ¨λ μ‘°νλ | |
| `order(card) <= order(νμ¬ μΉ΄λ)` νν°λ₯Ό κ°μ νλ€ (beat_id β€ νμ¬). | |
| """ | |
| from __future__ import annotations | |
| import hashlib | |
| from pathlib import Path | |
| from engine.schemas.beatcard import BeatCard, RCard | |
| from engine.schemas.character import CharacterSheet | |
| from engine.schemas.chapter import Chapter | |
| from engine.schemas.validate import check_links, load_content | |
| class ContentError(RuntimeError): | |
| pass | |
| class ContentRepository: | |
| def __init__(self, root: str | Path = "content"): | |
| self.root = Path(root) | |
| episodes, r_cards, chapter, characters, errors = load_content(self.root) | |
| link_errors, _ = check_links(episodes, chapter, r_cards) | |
| errors += link_errors | |
| if errors: | |
| raise ContentError("κ²μ¦ μ€ν¨ μ½ν μΈ λ μ μ¬νμ§ μλλ€:\n" + "\n".join(errors)) | |
| if chapter is None: | |
| raise ContentError("μ±ν° λ©ν μμ") | |
| self.chapter: Chapter = chapter | |
| self.r_cards: dict[str, RCard] = r_cards | |
| self.characters: dict[str, CharacterSheet] = { | |
| c.id: c for c in (characters.characters if characters else []) | |
| } | |
| self._cards: dict[str, BeatCard] = {} | |
| self._order: dict[str, int] = {} | |
| self.episode_entry: dict[str, str] = {} | |
| i = 0 | |
| for ep in episodes: | |
| self.episode_entry[ep.episode.id] = ep.cards[0].id | |
| for card in ep.cards: | |
| self._cards[card.id] = card | |
| self._order[card.id] = i | |
| i += 1 | |
| # μ½ν μΈ λ²μ μ€ν¬ν β μΈμ 볡μ μ λΆμΌμΉ κ²μ¬μ© (μΉ΄λ ID/μμ κΈ°μ€. | |
| # λ³Έλ¬Έλ§ λ°λ κ°±μ μ κ°μ§ λͺ»νμ§λ§ 볡μμ΄ κΉ¨μ§μ§λ μλλ€) | |
| ordered_ids = sorted(self._order, key=self._order.get) | |
| self.content_version: str = hashlib.sha256( | |
| "|".join([self.chapter.id, *ordered_ids]).encode()).hexdigest()[:16] | |
| # ββ μ‘°ν (μ€ν¬μΌλ¬ νν° κ°μ ) ββββββββββββββββββββββββββββ | |
| def get_card(self, card_id: str, current_id: str | None = None) -> BeatCard: | |
| """μΉ΄λ μ‘°ν. current_idλ₯Ό μ£Όλ©΄ beat_id β€ νμ¬ νν°λ₯Ό κ°μ νλ€.""" | |
| card = self._cards.get(card_id) | |
| if card is None: | |
| raise ContentError(f"μΉ΄λ μμ: {card_id}") | |
| if current_id is not None and self.order(card_id) > self.order(current_id): | |
| raise ContentError(f"μ€ν¬μΌλ¬ νν°: {card_id}λ νμ¬({current_id}) μ΄ν μΉ΄λ") | |
| return card | |
| def order(self, card_id: str) -> int: | |
| return self._order[card_id] | |
| def total_cards(self) -> int: | |
| return len(self._cards) | |
| def has_card(self, card_id: str) -> bool: | |
| return card_id in self._cards | |
| def cards_upto(self, current_id: str) -> list[BeatCard]: | |
| """νμ¬ μμ κΉμ§μ μΉ΄λ μ λΆ (컨ν μ€νΈ μ£Όμ μ©).""" | |
| cut = self.order(current_id) | |
| return [c for cid, c in self._cards.items() if self._order[cid] <= cut] | |
| def resolve_ref(self, target: str) -> str | None: | |
| """μΉ΄λ ID λλ μνΌμλ μ°Έμ‘°(E02)λ₯Ό μ€μ μΉ΄λ IDλ‘. μ±ν° λ°μ΄λ©΄ None(μΆκ΅¬).""" | |
| if target in self._cards: | |
| return target | |
| if target in self.episode_entry: | |
| return self.episode_entry[target] | |
| return None # E07 λ± β μ±ν° μΆκ΅¬ | |