diff --git a/api/app/domain/services/adventure_service.py b/api/app/domain/services/adventure_service.py index 0610be5..409397f 100644 --- a/api/app/domain/services/adventure_service.py +++ b/api/app/domain/services/adventure_service.py @@ -144,6 +144,7 @@ class AdventureService: self, adventure_id: uuid.UUID, entry_id: uuid.UUID, + user_id: uuid.UUID, ) -> None: """Full entry generation pipeline. Called from the worker queue. @@ -157,6 +158,7 @@ class AdventureService: assert adventure is not None, f"Adventure {adventure_id} not found" all_entries = await self.entry_repo.list_for_adventure(adventure_id) + all_decisions = [await self.decision_repo.get_for_entry_and_user(entry_id=uuid.UUID(e.id), user_id=user_id) for e in all_entries] current_entry = next(e for e in all_entries if e.id == str(entry_id)) is_first_entry = current_entry.entry_index == 0 is_final_entry = current_entry.entry_index + 1 == adventure.max_entry_count @@ -184,6 +186,7 @@ class AdventureService: vibes=adventure.vibes, protagonist=adventure.protagonist, prior_entries=prior_entries, + prior_decisions=all_decisions, ) raw_text, usage_dict = await self.anthropic_client.complete( @@ -192,7 +195,7 @@ class AdventureService: max_tokens=2048, ) - story_text, choices_parsed, gm_notes, story_so_far = parse_entry_response(raw_text) + story_text, choices_parsed, gm_notes = parse_entry_response(raw_text) await self.entry_repo.update_content( entry_id=entry_id, diff --git a/api/app/outbound/anthropic/adventure_prompts.py b/api/app/outbound/anthropic/adventure_prompts.py index a314b7c..2595b22 100644 --- a/api/app/outbound/anthropic/adventure_prompts.py +++ b/api/app/outbound/anthropic/adventure_prompts.py @@ -96,6 +96,7 @@ def build_conversation_messages( vibes: list[str], protagonist: list[str], prior_entries: list[tuple[AdventureEntry, list[AdventureEntryPossibleChoice], str | None]], + prior_decisions: list[AdventureEntryPossibleChoice | None], ) -> list[dict]: """Build the full messages array for an Anthropic API call. @@ -110,8 +111,22 @@ def build_conversation_messages( messages.append( {"role": "assistant", "content": reconstruct_assistant_message(entry, choices)} ) - if chosen_label is not None: - messages.append({"role": "user", "content": chosen_label}) + + # Find the player's decision for this entry + choice_ids = [c.id for c in choices] + decision_for_entry = next( + (d for d in prior_decisions if d and d.choice_id in choice_ids), + None + ) + + # If a decision exists, append the player's chosen option + if decision_for_entry: + chosen_option = next( + (c for c in choices if c.id == decision_for_entry.choice_id), + None + ) + if chosen_option: + messages.append({"role": "user", "content": chosen_option.label}) return messages diff --git a/api/app/routers/api/adventures.py b/api/app/routers/api/adventures.py index 95364b8..65e179e 100644 --- a/api/app/routers/api/adventures.py +++ b/api/app/routers/api/adventures.py @@ -120,10 +120,10 @@ def _make_service(db: AsyncSession) -> AdventureService: async def _run_entry_pipeline_task( - adventure_id: uuid.UUID, entry_id: uuid.UUID + adventure_id: uuid.UUID, entry_id: uuid.UUID, user_id: uuid.UUID ) -> None: async with AsyncSessionLocal() as db: - await _make_service(db).run_entry_pipeline(adventure_id, entry_id) + await _make_service(db).run_entry_pipeline(adventure_id, entry_id, user_id) # --------------------------------------------------------------------------- @@ -289,7 +289,7 @@ async def create_adventure( ) await worker.enqueue( partial( - _run_entry_pipeline_task, uuid.UUID(adventure.id), uuid.UUID(first_entry.id) + _run_entry_pipeline_task, uuid.UUID(adventure.id), uuid.UUID(first_entry.id), user_id ) ) return _to_adventure_response(adventure) @@ -375,6 +375,7 @@ async def record_decision( _run_entry_pipeline_task, uuid.UUID(next_entry.adventure_id), uuid.UUID(next_entry.id), + user_id, ) ) return DecisionResponse( diff --git a/api/app/routers/bff/adventure.py b/api/app/routers/bff/adventure.py index 7ec56f6..dcc1963 100644 --- a/api/app/routers/bff/adventure.py +++ b/api/app/routers/bff/adventure.py @@ -28,6 +28,7 @@ class AdventureChoiceItem(BaseModel): class AdventureEntryItem(BaseModel): id: str adventure_id: str + possible_choices: list[AdventureChoiceItem] | None generated_from_choice_id: str | None status: str entry_index: int @@ -81,6 +82,7 @@ async def get_adventure( entries = await PostgresAdventureEntryRepository(db).list_for_adventure(adv_id) + choices_repo = PostgresAdventureEntryChoiceRepository(db) translation_repo = PostgresAdventureEntryTranslationRepository(db) audio_repo = PostgresAdventureEntryAudioRepository(db) @@ -93,10 +95,15 @@ async def get_adventure( target_language=adventure.source_language, ) audio = await audio_repo.get_for_entry(entry_id=eid, component_type="story_text") + choices = await choices_repo.list_for_entry(eid) entry_items.append( AdventureEntryItem( id=entry.id, adventure_id=entry.adventure_id, + possible_choices=[ + AdventureChoiceItem(id=c.id, index=c.index, label=c.label, text=c.text) + for c in choices ] + if choices else None, generated_from_choice_id=entry.generated_from_choice_id, status=entry.status, entry_index=entry.entry_index,