docs: add narrative layer design spec and implementation plan
- docs/superpowers/specs/2026-03-26-narrative-layer-design.md - docs/superpowers/plans/2026-03-26-narrative-layer-foundation.md Spec extends MiroFish into a creative storytelling platform via a new narrative layer over the existing OASIS simulation engine. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
fa0f6519b1
commit
c5cc599053
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,532 @@
|
|||
# MiroFish Narrative Layer — Design Specification
|
||||
|
||||
**Date:** 2026-03-26
|
||||
**Approach:** A — Narrative Layer on top of existing OASIS simulation
|
||||
**Target:** Creative simulation engine (novel continuation, world-building, interactive fiction)
|
||||
**Scale:** Small community (10–50 users), basic auth, moderate concurrency
|
||||
|
||||
---
|
||||
|
||||
## 1. Problem Statement
|
||||
|
||||
MiroFish is a multi-agent swarm intelligence engine built for prediction — it simulates social media interactions (Twitter/Reddit) to forecast outcomes. The simulation infrastructure (OASIS engine, Zep graph memory, LLM-driven agents) is powerful but locked into a social-media metaphor.
|
||||
|
||||
**Goal:** Extend MiroFish into a creative storytelling platform where users can upload fiction, define worlds, and watch AI agents generate emergent narratives — without replacing the proven OASIS simulation backbone.
|
||||
|
||||
**Key insight:** Social media actions map naturally to narrative actions. A "post" is speech. A "like" is agreement. A "repost" is rumor-spreading. By adding a translation layer, we reinterpret simulation output as story prose.
|
||||
|
||||
---
|
||||
|
||||
## 2. Architecture
|
||||
|
||||
### 2.1 System Diagram
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ Frontend (Vue 3) │
|
||||
│ ┌────────────┐ ┌──────────────┐ ┌────────────────┐ │
|
||||
│ │ Story │ │ Character │ │ God Mode │ │
|
||||
│ │ Timeline │ │ Workshop │ │ Control Panel │ │
|
||||
│ └────────────┘ └──────────────┘ └────────────────┘ │
|
||||
│ ┌────────────┐ ┌──────────────┐ ┌────────────────┐ │
|
||||
│ │ World │ │ Branching │ │ Export │ │
|
||||
│ │ Map View │ │ Timeline │ │ Studio │ │
|
||||
│ └────────────┘ └──────────────┘ └────────────────┘ │
|
||||
├──────────────────────────────────────────────────────┤
|
||||
│ Narrative Engine (NEW) │
|
||||
│ ┌──────────────────────────────────────────────┐ │
|
||||
│ │ Narrative Translator │ │
|
||||
│ │ - Action → Story Event mapping │ │
|
||||
│ │ - Emotional state tracking │ │
|
||||
│ │ - Plot arc detection │ │
|
||||
│ └──────────────────────────────────────────────┘ │
|
||||
│ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │
|
||||
│ │ Character │ │ World State │ │ Timeline │ │
|
||||
│ │ Engine │ │ Manager │ │ Brancher │ │
|
||||
│ └─────────────┘ └──────────────┘ └─────────────┘ │
|
||||
├──────────────────────────────────────────────────────┤
|
||||
│ Existing MiroFish Backend │
|
||||
│ Graph Builder → OASIS Simulation → Report Agent │
|
||||
│ Zep Memory → Profile Generator → LLM Client │
|
||||
└──────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 New Backend Services
|
||||
|
||||
All new services live in `backend/app/services/narrative/` to keep the existing codebase untouched.
|
||||
|
||||
| Service | File | Purpose |
|
||||
|---|---|---|
|
||||
| Narrative Translator | `narrative_translator.py` | Converts OASIS actions into story prose via LLM |
|
||||
| Character Engine | `character_engine.py` | Manages extended character profiles (backstory, emotions, arcs) |
|
||||
| World State Manager | `world_state.py` | Tracks locations, factions, resources, world rules |
|
||||
| Timeline Brancher | `timeline_brancher.py` | Snapshots and forks simulation state |
|
||||
| Seed Enhancer | `seed_enhancer.py` | Processes EPUB/DOCX/templates into MiroFish seeds |
|
||||
| Export Studio | `export_studio.py` | Generates EPUB, screenplay, chapter formats |
|
||||
|
||||
### 2.3 New API Routes
|
||||
|
||||
New route blueprint: `narrative_bp = Blueprint('narrative', __name__)` in `backend/app/api/narrative.py`. Register in `backend/app/__init__.py` via `app.register_blueprint(narrative_bp, url_prefix='/api/narrative')`. Import added to `backend/app/api/__init__.py`.
|
||||
|
||||
| Method | Endpoint | Purpose |
|
||||
|---|---|---|
|
||||
| POST | `/api/narrative/translate` | Translate a simulation round into story prose |
|
||||
| GET | `/api/narrative/story/{sim_id}` | Get full story for a simulation |
|
||||
| GET | `/api/narrative/story/{sim_id}/round/{round}` | Get story for a specific round |
|
||||
| POST | `/api/narrative/characters` | Create/update extended character profiles |
|
||||
| GET | `/api/narrative/characters/{sim_id}` | Get all characters with emotional states |
|
||||
| GET | `/api/narrative/characters/{sim_id}/{char_id}` | Get single character detail |
|
||||
| POST | `/api/narrative/world` | Create/update world state |
|
||||
| GET | `/api/narrative/world/{sim_id}` | Get current world state |
|
||||
| POST | `/api/narrative/godmode/inject` | Inject an event into a running simulation |
|
||||
| POST | `/api/narrative/godmode/modify-character` | Modify character state mid-simulation |
|
||||
| POST | `/api/narrative/godmode/change-rules` | Change world rules mid-simulation |
|
||||
| POST | `/api/narrative/branch/{sim_id}/{round}` | Fork timeline at a specific round |
|
||||
| GET | `/api/narrative/branches/{sim_id}` | List all branches for a simulation |
|
||||
| POST | `/api/narrative/export/{sim_id}` | Export story in specified format |
|
||||
| GET | `/api/narrative/export/{sim_id}/status` | Check export generation status |
|
||||
|
||||
### 2.4 New Frontend Views
|
||||
|
||||
| View | File | Purpose |
|
||||
|---|---|---|
|
||||
| Story Timeline | `views/StoryTimelineView.vue` | Read the generated narrative chapter by chapter |
|
||||
| Character Workshop | `views/CharacterWorkshopView.vue` | Create, edit, and browse characters |
|
||||
| World Builder | `views/WorldBuilderView.vue` | Define locations, factions, rules |
|
||||
| God Mode | `views/GodModeView.vue` | Control panel for mid-simulation intervention |
|
||||
| Branch Explorer | `views/BranchExplorerView.vue` | Visual timeline tree, compare branches |
|
||||
| Export Studio | `views/ExportStudioView.vue` | Choose format, preview, download |
|
||||
|
||||
### 2.6 Frontend Routes
|
||||
|
||||
| Route | View | Description |
|
||||
|---|---|---|
|
||||
| `/story/:simId` | StoryTimelineView | Read generated narrative |
|
||||
| `/characters/:simId` | CharacterWorkshopView | Create and browse characters |
|
||||
| `/world/:simId` | WorldBuilderView | Define world settings |
|
||||
| `/godmode/:simId` | GodModeView | Mid-simulation controls |
|
||||
| `/branches/:simId` | BranchExplorerView | Timeline tree and comparison |
|
||||
| `/export/:simId` | ExportStudioView | Format and download stories |
|
||||
|
||||
Routes are added to `frontend/src/router/index.js` following the existing pattern of `/:simulationId` parameterized routes.
|
||||
|
||||
### 2.5 New Frontend Components
|
||||
|
||||
| Component | Purpose |
|
||||
|---|---|
|
||||
| `StoryBeat.vue` | Single story event/paragraph with character attribution |
|
||||
| `CharacterCard.vue` | Character portrait with stats, emotions, relationships |
|
||||
| `RelationshipGraph.vue` | Visual graph of character relationships (D3.js) |
|
||||
| `EmotionTracker.vue` | Per-character emotional state over time (sparkline) |
|
||||
| `WorldMap.vue` | Visual map of locations and character positions |
|
||||
| `TimelineTree.vue` | Branching timeline visualization |
|
||||
| `GodModeToolbar.vue` | Floating toolbar for injecting events |
|
||||
| `ExportPreview.vue` | Preview formatted output before download |
|
||||
|
||||
---
|
||||
|
||||
## 3. Narrative Translator — Core Logic
|
||||
|
||||
### 3.0 Integration Point — How the Narrative Layer Connects to OASIS
|
||||
|
||||
The existing OASIS simulation runs as a subprocess that writes actions to `actions.jsonl` (one JSON line per agent action per round). The existing `_monitor_simulation` method in `SimulationRunner` already polls this file every 2 seconds, tracking file position to read only new lines.
|
||||
|
||||
**Narrative translation is asynchronous and on-demand.** The Narrative Translator does NOT hook into the simulation loop. Instead:
|
||||
|
||||
1. **During simulation:** The existing `_monitor_simulation` continues reading `actions.jsonl` as-is. No changes to the simulation runner.
|
||||
2. **After each round completes:** The frontend polls a new endpoint `GET /api/narrative/story/{sim_id}/latest` which triggers translation of any untranslated rounds.
|
||||
3. **Translation reads `actions.jsonl`** directly, maintaining its own file offset in `narrative/translator_state.json`. It reads actions for the next untranslated round, generates prose, and stores the result.
|
||||
4. **Batch translation** is also available via `POST /api/narrative/translate` for retroactively translating a completed simulation.
|
||||
|
||||
**Why on-demand (not in-loop):** LLM prose generation takes 2-5 seconds per scene. Injecting this into the simulation monitor would slow round processing by 10-20x. By decoupling translation, the simulation runs at full speed and narrative is generated as users view it.
|
||||
|
||||
**IPC for God Mode:** The existing `SimulationIPCClient` supports file-based IPC with `INTERVIEW`, `BATCH_INTERVIEW`, and `CLOSE_ENV` commands. God Mode leverages `INTERVIEW` to inject custom prompts into running agents (see Section 6). New IPC command types (`INJECT_EVENT`, `MODIFY_AGENT_PROMPT`) will be added to `SimulationIPCServer` and the OASIS runner scripts.
|
||||
|
||||
### 3.1 Action Mapping
|
||||
|
||||
| OASIS Action | Narrative Interpretation |
|
||||
|---|---|
|
||||
| `CREATE_POST` | Character speaks, declares, announces |
|
||||
| `LIKE_POST` | Character agrees, supports, nods |
|
||||
| `REPOST` | Character spreads rumor, amplifies, gossips |
|
||||
| `QUOTE_POST` | Character responds, debates, challenges |
|
||||
| `FOLLOW` | Character allies with, shows loyalty to |
|
||||
| `DO_NOTHING` | Character reflects, observes, waits *(inferred — see note below)* |
|
||||
| `CREATE_COMMENT` | Character engages in dialogue |
|
||||
| `DISLIKE_POST` | Character opposes, confronts, disapproves |
|
||||
| `LIKE_COMMENT` | Character validates someone's response |
|
||||
| `DISLIKE_COMMENT` | Character dismisses, mocks |
|
||||
| `SEARCH_POSTS` | Character investigates, seeks information |
|
||||
| `SEARCH_USER` | Character seeks out a specific person |
|
||||
| `MUTE` | Character avoids, ignores, shuns |
|
||||
|
||||
**Note on `DO_NOTHING`:** This action may not appear in `actions.jsonl` since the logger only records actions agents take. The translator infers inaction: any agent present in the simulation but absent from a round's action log is treated as "observing/waiting." This is narratively valuable.
|
||||
|
||||
### 3.2 Translation Pipeline
|
||||
|
||||
For each simulation round:
|
||||
|
||||
1. **Collect actions** — read `actions.jsonl` from the translator's saved file offset, gather all actions until the next `round_end` event
|
||||
2. **Infer inaction** — compare active agents against the full character roster; absent agents are marked as "observing"
|
||||
3. **Enrich with context** — pull character emotional states, relationships, world state from `narrative/` files
|
||||
4. **Group into scenes** — cluster related actions (same thread / interacting agents)
|
||||
5. **Generate prose** — LLM call per scene with action data + context → narrative paragraph
|
||||
6. **Detect plot events** — classify the round as rising action / climax / falling action / resolution
|
||||
7. **Update states** — update character emotions and relationships based on what happened
|
||||
8. **Store** — persist story beat to `narrative/story_beats.json`, update translator file offset in `narrative/translator_state.json`
|
||||
|
||||
### 3.3 Resilience
|
||||
|
||||
- **LLM retry:** 3 retries with exponential backoff (1s, 2s, 4s) using the existing `retry` utility in `backend/app/utils/retry.py`
|
||||
- **Partial failure:** If a scene fails after retries, store a placeholder beat with raw action data. User can retry individual beats via API.
|
||||
- **Graceful degradation:** If the LLM is completely unavailable, fall back to template-based summary: `"{agent_name} {action_verb} {target}"`
|
||||
- **Cost estimate:** ~3-5 LLM calls per round (one per scene). A 20-round simulation ≈ 60-100 calls ≈ 30k-50k tokens ≈ $0.15-$0.25 with GPT-4o-mini.
|
||||
|
||||
### 3.4 LLM Prompt Structure
|
||||
|
||||
The translator uses a structured prompt:
|
||||
|
||||
```
|
||||
You are a narrative writer. Convert these simulation events into a story passage.
|
||||
|
||||
World: {world_description}
|
||||
Setting: {current_location_context}
|
||||
Characters involved: {character_summaries_with_emotions}
|
||||
|
||||
Events this round:
|
||||
{structured_action_list}
|
||||
|
||||
Previous story context (last 2 beats):
|
||||
{previous_prose}
|
||||
|
||||
Write a story passage (2-4 paragraphs) that:
|
||||
- Uses third-person past tense
|
||||
- Shows character emotions through action and dialogue
|
||||
- Maintains consistency with the world rules
|
||||
- Advances the narrative naturally from the previous context
|
||||
|
||||
Tone: {user_defined_tone — e.g., "dark fantasy", "lighthearted comedy", "political thriller"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Character Engine
|
||||
|
||||
### 4.1 Extended Character Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "char_abc123",
|
||||
"name": "Elena Voss",
|
||||
"oasis_agent_id": "agent_xyz",
|
||||
|
||||
"backstory": "Former diplomat turned rogue negotiator...",
|
||||
"motivations": ["power", "redemption"],
|
||||
"personality_traits": ["cunning", "loyal_to_few", "distrustful"],
|
||||
"speech_style": "Formal, precise, with occasional dark humor",
|
||||
|
||||
"emotional_state": {
|
||||
"current": {"anger": 0.3, "fear": 0.1, "joy": 0.0, "sadness": 0.4, "trust": 0.2},
|
||||
"history": [{"round": 1, "state": {...}}, ...]
|
||||
},
|
||||
|
||||
"relationships": {
|
||||
"char_def456": {"type": "rival", "intensity": 0.8, "history": ["betrayal in round 3"]},
|
||||
"char_ghi789": {"type": "ally", "intensity": 0.6, "history": ["formed alliance in round 1"]}
|
||||
},
|
||||
|
||||
"arc": {
|
||||
"archetype": "fall_from_grace",
|
||||
"stage": "descent",
|
||||
"key_moments": [{"round": 3, "event": "Betrayed longtime ally"}]
|
||||
},
|
||||
|
||||
"location": "The Iron Tower",
|
||||
"faction": "The Council"
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 Emotional State Model
|
||||
|
||||
Six basic emotions tracked as 0.0–1.0 floats: `anger`, `fear`, `joy`, `sadness`, `trust`, `surprise`.
|
||||
|
||||
Updated each round based on:
|
||||
- Actions taken by the character
|
||||
- Actions taken *toward* the character
|
||||
- World events
|
||||
- Relationship changes
|
||||
|
||||
**v1: Display-only.** Emotional states are derived from actions and displayed in the UI but do NOT feed back into OASIS agent prompts. OASIS profiles are generated once at simulation setup time by `OasisProfileGenerator` and are not modified mid-simulation.
|
||||
|
||||
**v2 (future):** Use the new `INJECT_CONTEXT` IPC command (see Section 6.2) to prepend emotional context to agent prompts each round. E.g., `"You are currently feeling angry and distrustful after the betrayal."` This creates a feedback loop where emotions influence behavior, but adds an IPC call per agent per round.
|
||||
|
||||
### 4.3 Character Arc Detection
|
||||
|
||||
After each round, the Character Engine evaluates arc progression using pattern matching:
|
||||
|
||||
| Arc Type | Pattern |
|
||||
|---|---|
|
||||
| Hero's Journey | Comfort → Crisis → Growth → Triumph |
|
||||
| Fall from Grace | Status → Temptation → Descent → Consequences |
|
||||
| Redemption | Flaw → Suffering → Realization → Atonement |
|
||||
| Coming of Age | Innocence → Challenge → Learning → Maturity |
|
||||
| Tragedy | Hope → Hubris → Downfall → Loss |
|
||||
|
||||
**Detection mechanism:** Rule-based classification using emotional state deltas:
|
||||
- Trust drops > 0.3 in one round → "crisis" stage
|
||||
- Joy sustained > 0.6 for 3+ rounds → "comfort" or "triumph" stage
|
||||
- Anger + fear both > 0.5 → "descent" stage
|
||||
- Transition from high-negative to high-positive emotions → "redemption"
|
||||
|
||||
If rule-based detection is ambiguous, a single LLM classification call is made with the character's action history summary (~200 tokens). Detected arcs are stored in `characters.json` and surfaced in the UI and export.
|
||||
|
||||
---
|
||||
|
||||
## 5. World State Manager
|
||||
|
||||
### 5.1 World State Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"sim_id": "sim_abc123",
|
||||
"name": "The Shattered Kingdoms",
|
||||
"genre": "dark_fantasy",
|
||||
"tone": "grim, political",
|
||||
"era": "medieval",
|
||||
|
||||
"rules": [
|
||||
"Magic exists but is feared and regulated",
|
||||
"The monarchy has fallen; power is contested by three factions",
|
||||
"Winter is approaching and resources are scarce"
|
||||
],
|
||||
|
||||
"locations": {
|
||||
"iron_tower": {"name": "The Iron Tower", "type": "fortress", "faction": "council", "characters": ["char_abc"]},
|
||||
"market_district": {"name": "Market District", "type": "city", "faction": "neutral", "characters": []}
|
||||
},
|
||||
|
||||
"factions": {
|
||||
"council": {"name": "The Council", "power": 0.6, "members": ["char_abc"], "goals": ["restore order"]},
|
||||
"rebels": {"name": "The Free Folk", "power": 0.3, "members": ["char_def"], "goals": ["overthrow council"]}
|
||||
},
|
||||
|
||||
"resources": {
|
||||
"food": {"abundance": 0.3, "controlled_by": "council"},
|
||||
"weapons": {"abundance": 0.5, "controlled_by": "rebels"}
|
||||
},
|
||||
|
||||
"timeline_position": {"round": 5, "branch": "main"}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 World State Updates
|
||||
|
||||
Each simulation round, the World State Manager:
|
||||
1. Processes narrative events for world-level changes (faction power shifts, resource changes)
|
||||
2. Updates character locations based on their actions
|
||||
3. Checks world rules for constraint violations
|
||||
4. Triggers world events (e.g., "winter arrives" at round 10)
|
||||
|
||||
---
|
||||
|
||||
## 6. God Mode
|
||||
|
||||
### 6.1 Intervention Types
|
||||
|
||||
| Intervention | Effect |
|
||||
|---|---|
|
||||
| **Inject Event** | Add a world event ("earthquake strikes", "a stranger arrives") that agents must react to |
|
||||
| **Modify Character** | Change a character's emotional state, motivation, or relationships |
|
||||
| **Change Rules** | Add or remove world rules ("magic is now forbidden") |
|
||||
| **Force Action** | Make a specific character take a specific action next round |
|
||||
| **Kill Character** | Remove an agent from the simulation permanently |
|
||||
| **Resurrect Character** | Re-introduce a removed character |
|
||||
| **Time Skip** | Jump forward N rounds with summarized events |
|
||||
| **Rewind** | Go back to a previous round (creates a branch) |
|
||||
|
||||
### 6.2 Implementation — IPC Dispatch Mechanism
|
||||
|
||||
God Mode leverages the existing **file-based IPC** system (`SimulationIPCClient` / `SimulationIPCServer`). The IPC currently supports `INTERVIEW`, `BATCH_INTERVIEW`, and `CLOSE_ENV` commands.
|
||||
|
||||
**Mapping God Mode interventions to IPC:**
|
||||
|
||||
| Intervention | IPC Mechanism |
|
||||
|---|---|
|
||||
| **Inject Event** | `BATCH_INTERVIEW` — send all agents a prompt describing the event, forcing them to react in their next action. E.g., `"An earthquake just struck. How do you respond?"` |
|
||||
| **Modify Character** | `INTERVIEW` — send the target agent a prompt that reframes their emotional/motivational state. E.g., `"You have just learned that your ally betrayed you. Your trust is shattered."` The Character Engine also updates its local emotional state JSON. |
|
||||
| **Change Rules** | `BATCH_INTERVIEW` — inform all agents of the rule change. E.g., `"Magic has been outlawed. Anyone caught using it faces death."` Also update `world_state.json`. |
|
||||
| **Force Action** | `INTERVIEW` — send the target agent a directive prompt. E.g., `"You decide to confront Marcus in front of the council. Describe your accusation."` The response is logged as the agent's action. |
|
||||
| **Kill Character** | Stop sending actions for this agent in subsequent rounds. Mark as "dead" in `characters.json`. OASIS agent continues to exist but receives no prompts. |
|
||||
| **Resurrect Character** | Reverse of kill — resume prompting the agent with a resurrection context prompt. |
|
||||
| **Time Skip** | Set simulation to run N rounds without narrative translation, then batch-translate with a "summary" prompt instead of detailed prose. |
|
||||
| **Rewind** | Creates a new simulation from scratch using the same config but with a modified starting prompt that includes "the story so far up to round N." See Section 7 for details. |
|
||||
|
||||
**New IPC command type needed:** `INJECT_CONTEXT` — a variant of `BATCH_INTERVIEW` that prepends context to all agents' next-round system prompts rather than triggering an immediate interview response. This requires a small addition to the OASIS runner scripts (`run_twitter_simulation.py`, `run_reddit_simulation.py`).
|
||||
|
||||
Each intervention is logged to `narrative/god_mode_log.json` for story coherence and auditability.
|
||||
|
||||
---
|
||||
|
||||
## 7. Timeline Branching
|
||||
|
||||
### 7.1 Branch Model
|
||||
|
||||
```json
|
||||
{
|
||||
"sim_id": "sim_abc123",
|
||||
"branches": {
|
||||
"main": {"parent": null, "fork_round": 0, "status": "running", "rounds_completed": 10},
|
||||
"what_if_elena_dies": {"parent": "main", "fork_round": 5, "status": "running", "rounds_completed": 3},
|
||||
"peaceful_ending": {"parent": "main", "fork_round": 7, "status": "paused", "rounds_completed": 1}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 Branching Mechanics — Replay-Based Approach
|
||||
|
||||
**OASIS does not support checkpointing.** The simulation database is deleted on startup and there is no state serialization for mid-simulation resumption. Therefore, branching uses a **replay + diverge** strategy:
|
||||
|
||||
1. User selects a round N to branch from
|
||||
2. System creates a new simulation directory under `uploads/simulations/{sim_id}/branches/{branch_name}/`
|
||||
3. System copies the original `simulation_config.json` and `profiles.json`
|
||||
4. System generates a **"story so far" summary** — an LLM-generated condensation of all narrative beats from rounds 1 to N
|
||||
5. This summary is injected into every agent's starting prompt as context: `"The following events have already occurred: {summary}. You are now at this point in the story."`
|
||||
6. A new OASIS subprocess launches with modified agent prompts that include this prior context
|
||||
7. User can optionally inject a God Mode event to differentiate the branch (e.g., "In this timeline, Elena survives the betrayal")
|
||||
8. Both branches run as independent OASIS processes — user can switch between them in the UI
|
||||
|
||||
**Trade-offs of replay approach:**
|
||||
- **Pro:** No OASIS modifications needed. Uses existing subprocess launch mechanism.
|
||||
- **Con:** Agents don't have exact memory of previous rounds — they have an LLM-summarized version. This means branches may drift from the original timeline's "feel." For creative fiction, this is acceptable (stories branch naturally).
|
||||
- **Con:** Starting a branch requires an LLM call for summary generation (~5-10 seconds). Not a major bottleneck.
|
||||
- **Performance:** Branch launch takes the same time as a fresh simulation start (~10-30 seconds), regardless of which round you branch from.
|
||||
|
||||
Storage: Each branch gets its own subdirectory under `uploads/simulations/{sim_id}/branches/{branch_name}/` with its own `simulation_config.json`, `actions.jsonl`, and `narrative/` folder.
|
||||
|
||||
---
|
||||
|
||||
## 8. Enhanced Input Pipeline
|
||||
|
||||
### 8.1 New Seed Formats
|
||||
|
||||
| Format | Processing |
|
||||
|---|---|
|
||||
| **EPUB** | Extract text → identify characters (NER) → extract settings → generate world state |
|
||||
| **DOCX** | Same as EPUB but with python-docx parsing |
|
||||
| **YAML template** | Structured world definition — characters, locations, rules — no LLM needed |
|
||||
| **Image** | Send to multimodal LLM → extract character description / setting description |
|
||||
| **URL** | Scrape page content → process as text seed |
|
||||
|
||||
### 8.2 YAML World Template Format
|
||||
|
||||
```yaml
|
||||
world:
|
||||
name: "The Shattered Kingdoms"
|
||||
genre: "dark_fantasy"
|
||||
tone: "grim, political"
|
||||
rules:
|
||||
- "Magic exists but is feared"
|
||||
- "Winter is approaching"
|
||||
|
||||
characters:
|
||||
- name: "Elena Voss"
|
||||
role: "protagonist"
|
||||
backstory: "Former diplomat..."
|
||||
motivations: ["power", "redemption"]
|
||||
personality: ["cunning", "loyal"]
|
||||
|
||||
- name: "Marcus Iron"
|
||||
role: "antagonist"
|
||||
backstory: "Military commander..."
|
||||
motivations: ["control", "legacy"]
|
||||
|
||||
locations:
|
||||
- name: "The Iron Tower"
|
||||
type: "fortress"
|
||||
description: "A dark spire..."
|
||||
|
||||
factions:
|
||||
- name: "The Council"
|
||||
goals: ["restore order"]
|
||||
members: ["Elena Voss"]
|
||||
|
||||
simulation:
|
||||
max_rounds: 20
|
||||
tone: "dark_fantasy"
|
||||
platform: "twitter" # underlying OASIS platform
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Export Studio
|
||||
|
||||
### 9.1 Output Formats
|
||||
|
||||
| Format | Structure |
|
||||
|---|---|
|
||||
| **Chapters** | Story beats grouped into chapters (every 3-5 rounds), with chapter titles |
|
||||
| **Screenplay** | Character names in caps, dialogue, (parenthetical action), scene headings |
|
||||
| **EPUB** | Full e-book with cover, table of contents, chapters, character appendix |
|
||||
| **Character Dossiers** | Per-character PDF with backstory, arc summary, key moments, relationships |
|
||||
| **Raw Timeline** | JSON export of all events for programmatic use |
|
||||
|
||||
### 9.2 Export Pipeline
|
||||
|
||||
1. User selects simulation + branch + format
|
||||
2. Backend collects all story beats for the selected scope
|
||||
3. LLM pass for cohesion — smooth transitions between beats, add chapter breaks
|
||||
4. Format-specific rendering (EPUB via `ebooklib`, screenplay via template)
|
||||
5. Return download URL
|
||||
|
||||
**Async handling:** Export runs in a background thread (consistent with existing `SimulationRunner._monitor_simulation` pattern). The `POST /api/narrative/export/{sim_id}` endpoint returns immediately with an export job ID. Frontend polls `GET /api/narrative/export/{sim_id}/status` until complete. Note: server restarts will lose in-progress exports — acceptable for the target scale.
|
||||
|
||||
---
|
||||
|
||||
## 10. Data Model Changes
|
||||
|
||||
### 10.1 New Files per Simulation
|
||||
|
||||
```
|
||||
uploads/simulations/{sim_id}/
|
||||
├── state.json # existing
|
||||
├── profiles.json # existing
|
||||
├── entities.json # existing
|
||||
├── simulation_config.json # existing
|
||||
├── narrative/ # NEW
|
||||
│ ├── world_state.json # world definition
|
||||
│ ├── characters.json # extended character profiles
|
||||
│ ├── story_beats.json # generated narrative per round
|
||||
├── branches/ # Timeline branches (see Section 7)
|
||||
│ ├── what_if_branch/
|
||||
│ │ ├── simulation_config.json
|
||||
│ │ ├── actions.jsonl
|
||||
│ │ └── narrative/
|
||||
│ │ ├── story_beats.json
|
||||
│ │ └── characters.json
|
||||
│ └── exports/
|
||||
│ ├── story_chapters.epub
|
||||
│ └── screenplay.txt
|
||||
```
|
||||
|
||||
### 10.2 Database Additions
|
||||
|
||||
No new database — continues using file-based storage consistent with existing MiroFish patterns. For the 10-50 user scale, this is sufficient. If we need to scale later, we migrate to SQLite or PostgreSQL.
|
||||
|
||||
---
|
||||
|
||||
## 11. Authentication & Multi-User
|
||||
|
||||
**Deferred to a separate spec.** The existing MiroFish codebase has zero authentication — no middleware, no user model, no token checks. Adding JWT auth is a cross-cutting concern that touches every existing endpoint and deserves its own design. For v1 of the Narrative Layer, we operate without auth (same as existing MiroFish). Multi-user isolation is handled by simulation IDs — each simulation is self-contained in its own directory.
|
||||
|
||||
**When auth is added (separate spec):** Simple JWT tokens, user model in SQLite, middleware on all `/api/` routes, project isolation by user directory, optional "public" flag for sharing.
|
||||
|
||||
---
|
||||
|
||||
## 12. Non-Goals (Explicitly Out of Scope)
|
||||
|
||||
- Real-time collaborative editing (too complex for v1)
|
||||
- Voice/audio output of stories
|
||||
- Image generation for scenes (future enhancement)
|
||||
- Mobile app
|
||||
- Horizontal scaling / microservices
|
||||
- Payment / billing
|
||||
Loading…
Reference in New Issue