diff --git a/backend/app/services/narrative/narrative_translator.py b/backend/app/services/narrative/narrative_translator.py index 607c563d..085aaaaf 100644 --- a/backend/app/services/narrative/narrative_translator.py +++ b/backend/app/services/narrative/narrative_translator.py @@ -37,10 +37,18 @@ def _format_world_locations(world: dict) -> str: locs = list(world.get("locations", {}).values())[:5] if not locs: return "(none)" - return "\n ".join( - f"{_escape_braces(l.get('name', ''))} — {_escape_braces(l.get('description', ''))}" - for l in locs - ) + lines = [] + for l in locs: + name = _escape_braces(l.get("name", "")) + desc = _escape_braces(l.get("description", "")) + line = f"{name} — {desc}" + # Cinematic schema: if atmosphere is present, surface it to the LLM as + # a mood anchor for any scene set here. + atmosphere = l.get("atmosphere") + if atmosphere: + line += f" [atmosphere: {_escape_braces(atmosphere)}]" + lines.append(line) + return "\n ".join(lines) def read_actions_for_round( diff --git a/backend/tests/test_narrative_translator.py b/backend/tests/test_narrative_translator.py index ea361bd9..0a9d91bd 100644 --- a/backend/tests/test_narrative_translator.py +++ b/backend/tests/test_narrative_translator.py @@ -142,3 +142,33 @@ def test_generate_prose_includes_world_context(): assert "Magic is forbidden" in prompt assert "A stranger arrived" in prompt assert "The Tower" in prompt + + +def test_location_atmosphere_surfaces_in_prompt(): + """Cinematic location schema — atmosphere should reach the LLM.""" + actions = [{"agent_name": "Alice", "action_type": "CREATE_POST", "action_args": {}}] + characters = [ + {"id": "1", "name": "Alice", "status": "alive", + "emotional_state": {"current": {"anger": 0, "fear": 0, "joy": 0, + "sadness": 0, "trust": 0.5, "surprise": 0}}}, + ] + world = { + "rules": [], + "locations": { + "tower": { + "id": "tower", + "name": "The Iron Tower", + "description": "dark spire", + "atmosphere": "oppressive silence, dust in shafts of cold light", + } + }, + "event_log": [], + } + + with patch("app.services.narrative.narrative_translator.call_llm") as mock_llm: + mock_llm.return_value = "prose" + generate_prose(actions, characters, tone="noir", previous_beats=[], world=world) + + prompt = mock_llm.call_args[0][0] + assert "atmosphere" in prompt.lower() + assert "oppressive silence" in prompt diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 4cefb536..05ee36d5 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -6,6 +6,8 @@ import SimulationRunView from '../views/SimulationRunView.vue' import ReportView from '../views/ReportView.vue' import InteractionView from '../views/InteractionView.vue' import StoryTimelineView from '../views/StoryTimelineView.vue' +import GodModeView from '../views/GodModeView.vue' +import WorldBuilderView from '../views/WorldBuilderView.vue' const routes = [ { @@ -48,6 +50,18 @@ const routes = [ name: 'Story', component: StoryTimelineView, props: true + }, + { + path: '/godmode/:simulationId', + name: 'GodMode', + component: GodModeView, + props: true + }, + { + path: '/world/:simulationId', + name: 'World', + component: WorldBuilderView, + props: true } ] diff --git a/frontend/src/views/GodModeView.vue b/frontend/src/views/GodModeView.vue new file mode 100644 index 00000000..abb9a5b3 --- /dev/null +++ b/frontend/src/views/GodModeView.vue @@ -0,0 +1,225 @@ + + + + + diff --git a/frontend/src/views/StoryTimelineView.vue b/frontend/src/views/StoryTimelineView.vue index 8f080711..413f33d8 100644 --- a/frontend/src/views/StoryTimelineView.vue +++ b/frontend/src/views/StoryTimelineView.vue @@ -1,5 +1,11 @@