diff --git a/docs/OPEN_TASKS_PLAN.md b/docs/OPEN_TASKS_PLAN.md deleted file mode 100644 index 6398af8..0000000 --- a/docs/OPEN_TASKS_PLAN.md +++ /dev/null @@ -1,155 +0,0 @@ -# Plan: Open Tasks (Ollama Integration in InMemorySessionService) - -This document plans the two remaining backend open tasks: wiring the two-call Ollama pattern into **createSession** and **submitTurn** in [InMemorySessionService](src/main/java/de/neitzel/roleplay/business/InMemorySessionService.java). - ---- - -## Overview - -| Task | Method | Goal | -|------|--------|------| -| 1 | `createSession` | Produce opening narrative and initial state via Ollama (narrative + state extraction). | -| 2 | `submitTurn` | Produce turn narrative and state update via Ollama (narrative + state update), then merge into session. | - -Both follow the same pattern: build **TurnContext** (JSON) → **Call 1** (narrative) → **Call 2** (state update with narrative in user message) → map **StateUpdateResponse** and narrative into API responses and session state. - ---- - -## Prerequisites and Dependencies - -- **[OllamaClient](src/main/java/de/neitzel/roleplay/fascade/OllamaClient.java)** – already provides `generateNarrative(model, systemPrompt, userContent)` and `generateStateUpdate(model, systemPrompt, userContent)`. -- **Common types** ([common](src/main/java/de/neitzel/roleplay/common/)) – `TurnContext`, `StateUpdateResponse`, `SituationSnapshot`, `CharacterSet`, `CharacterSnapshot`, `UserAction`, `Recommendation`, `SituationUpdate`, `CharacterUpdate`, `OpenThreadsChanges`, `CharacterResponse`, `Suggestion` (with `SuggestionType`, `RiskLevel`). -- **API models** (`de.neitzel.roleplay.fascade.model`) – generated from OpenAPI: `SessionResponse`, `TurnResponse`, `SituationState`, `CharacterState`, `Suggestion`, `CharacterResponseItem`, etc. -- **Prompts** – defined in [ROLEPLAY_CONCEPT.md](docs/ROLEPLAY_CONCEPT.md) (§4.3 for init, §4.4 for turn). Should be extracted to constants or a small prompt provider (e.g. in `business` or `common`) so they stay consistent and maintainable. - ---- - -## Task 1: createSession – Opening scene and initial state - -### Flow - -1. Create `SessionResponse` with sessionId, model, language, safetyLevel, turnNumber (0) – **unchanged**. -2. Build initial **situation** and **characters** from `request.getScenario()` (reuse existing `buildSituationFromScenario` / `buildCharactersFromScenario`). -3. Build **TurnContext** for session init: - - **currentSituation**: Map from `SituationState` (API) to `SituationSnapshot` (common). Initial values: setting, currentScene from scenario; timeline/openThreads/externalPressures/worldStateFlags can be empty or minimal. - - **characters**: Map from `CharacterState` list to `CharacterSet` (userCharacter + aiCharacters). Map each to `CharacterSnapshot` (id, name, role, personalityTraits, speakingStyle, goals from scenario; currentMood/status/knowledge/relationships/recentActionsSummary can be null or default). - - **userAction**: `null` (no user action at init). - - **recommendation**: `null`. - - **recentHistorySummary**: `null` or `""`. -4. Serialise `TurnContext` to JSON (snake_case) via Jackson `ObjectMapper`. -5. **Call 1** – `ollamaClient.generateNarrative(model, INIT_SYSTEM_PROMPT, contextJson)`. -6. **Call 2** – `ollamaClient.generateStateUpdate(model, STATE_EXTRACT_SYSTEM_PROMPT, contextJson + "\n\nNarrative that was just generated:\n" + narrative)`. -7. Set **session** from Call 1 + Call 2: - - `session.setNarrative(narrative)` (from Call 1). - - Apply **StateUpdateResponse** to session: - - **updated_situation** → merge into `session.getSituation()` (or build new `SituationState`): currentScene, append newTimelineEntries to timeline, apply openThreadsChanges (add/remove from openThreads), set worldStateFlags. - - **updated_characters** → merge into `session.getCharacters()` by characterId: update currentMood, append knowledgeGained to knowledge, apply relationshipChanges to relationships; keep id/name/role/isUserCharacter from existing list. - - **suggestions** → map common `Suggestion` list to API `Suggestion` list and `session.setSuggestions(...)`. -8. Store session and return. - -### Design choices - -- **Context builder**: Introduce a small helper (or service) to build `TurnContext` from scenario + optional session state, and to serialise it. Keeps `InMemorySessionService` focused on orchestration. -- **Situation/character mapping**: Reuse or introduce mappers between API models (`SituationState`, `CharacterState`) and common types (`SituationSnapshot`, `CharacterSnapshot`) so both createSession and submitTurn can share the same logic. -- **Error handling**: If Ollama fails (network, parse), decide: throw and fail session creation, or fall back to placeholder narrative and log. Prefer throwing and letting the resource layer return 5xx, with a clear message. - ---- - -## Task 2: submitTurn – Turn narrative and state update - -### Flow - -1. Load session; if missing return `Optional.empty()` – **unchanged**. -2. Increment turn number – **unchanged**. -3. Build **TurnContext** for this turn: - - **currentSituation**: From `session.getSituation()` → `SituationSnapshot` (setting, currentScene, timeline, openThreads, externalPressures, worldStateFlags). - - **characters**: From `session.getCharacters()` → `CharacterSet` (userCharacter + aiCharacters as `CharacterSnapshot` list). - - **userAction**: From `turnRequest.getUserAction()` → map API `UserActionRequest` to common `UserAction` (type, content, selectedSuggestionId). - - **recommendation**: From `turnRequest.getRecommendation()` → map to common `Recommendation` (desiredTone, preferredDirection, focusCharacters), or null. - - **recentHistorySummary**: For now empty string or a short placeholder; later can be derived from stored turn history if added. -4. Serialise `TurnContext` to JSON. -5. **Call 1** – `ollamaClient.generateNarrative(model, TURN_NARRATIVE_SYSTEM_PROMPT, contextJson)`. -6. **Call 2** – `ollamaClient.generateStateUpdate(model, STATE_EXTRACT_SYSTEM_PROMPT, contextJson + "\n\nNarrative that was just generated:\n" + narrative)`. -7. Build **TurnResponse**: - - turnNumber, narrative (from Call 1). - - characterResponses, updatedSituation, updatedCharacters, suggestions from `StateUpdateResponse` (mapped to API model types). -8. **Merge** state update into session (same as createSession): apply updated_situation to `session.getSituation()`, updated_characters to `session.getCharacters()`, replace `session.setSuggestions(...)`, set `session.setNarrative(narrative)`, `session.setTurnNumber(nextTurn)`. -9. Save session and return `Optional.of(turnResponse)`. - -### Design choices - -- **Shared merge logic**: Extract “apply StateUpdateResponse to SessionResponse” into a private method (or small helper) used by both createSession and submitTurn. -- **TurnResponse mapping**: Map `StateUpdateResponse` (common) to `TurnResponse` (API): responses → characterResponses, updatedSituation, updatedCharacters, suggestions. API uses same structure; ensure enum/value names match (e.g. speech/action/reaction, risk_level). - ---- - -## Shared implementation elements - -### 1. Prompts - -- **INIT_SYSTEM_PROMPT** (opening narrative) – from concept §4.3 Call 1. -- **STATE_EXTRACT_SYSTEM_PROMPT** (JSON extraction) – from concept §4.3 Call 2 (same for init and turn). -- **TURN_NARRATIVE_SYSTEM_PROMPT** (turn continuation) – from concept §4.4 Call 1. - -Store as constants in a single class (e.g. `OllamaPrompts` in `business`) or in a configurable provider. - -### 2. Context building - -- **Session init**: `TurnContext` from scenario-derived situation + characters; no userAction/recommendation. -- **Turn**: `TurnContext` from current session situation + characters + userAction + recommendation. - -Both need **API → common** mapping for situation and characters when building the context. A dedicated mapper or builder class keeps the service clean. - -### 3. State merge (StateUpdateResponse → SessionResponse) - -- **Situation**: Apply `SituationUpdate`: set currentScene if present; append newTimelineEntries to timeline; apply openThreadsChanges (add/remove from openThreads); replace or merge worldStateFlags. -- **Characters**: For each `CharacterUpdate` find character by characterId; update currentMood; append knowledgeGained to knowledge; merge relationshipChanges into relationships. -- **Suggestions**: Replace session suggestions with the new list (mapped from common to API). - -### 4. API ↔ common mapping - -- **Common** uses snake_case (Jackson `@JsonNaming`); **API** models are OpenAPI-generated (camelCase). When mapping StateUpdateResponse (common) into SessionResponse / TurnResponse (API), copy fields and convert enums where needed (e.g. ResponseType, SuggestionType, RiskLevel). -- **TurnContext** is always built from common types and serialised to JSON as-is for Ollama. - ---- - -## Suggested order of implementation - -1. **Prompts** – Add `OllamaPrompts` (or similar) with the three system prompt strings from the concept doc. -2. **Mappers** – Add mapping from API `SituationState`/`CharacterState` (and scenario) to common `SituationSnapshot`/`CharacterSet`/`CharacterSnapshot`; and from common `StateUpdateResponse`/`Suggestion`/etc. to API types for SessionResponse/TurnResponse. Optionally a dedicated “context builder” that takes scenario or session + turnRequest and returns `TurnContext`. -3. **State merge** – Implement “apply StateUpdateResponse to SessionResponse” (situation, characters, suggestions) in one place. -4. **createSession** – Inject `OllamaClient` and `ObjectMapper`; build context from scenario; call two-step Ollama; merge state; set narrative/situation/characters/suggestions on session. -5. **submitTurn** – Build context from session + turnRequest; call two-step Ollama; build TurnResponse; merge state into session; return TurnResponse. -6. **Tests** – Update or add unit tests: mock `OllamaClient` in `InMemorySessionServiceTest` to verify two-call pattern and state merge; optionally integration test with a real or stubbed Ollama. - ---- - -## Risks and mitigations - -| Risk | Mitigation | -|------|------------| -| Ollama slow/unavailable | Timeouts already set in rest-client config; fail fast and return 502/503 from resource layer. | -| Model returns invalid JSON (Call 2) | Already throws `OllamaParseException`; map to 502 or 503 in a mapper if not already. | -| Large context / token limit | Keep recentHistorySummary short; later add condensing of older turns. | -| Enum mismatches (API vs common) | Use same enum names in OpenAPI as in common (e.g. speech, action, reaction); document mapping in one place. | - ---- - -## Files to touch (summary) - -| File / area | Change | -|------------|--------| -| New: e.g. `business/OllamaPrompts.java` | System prompt constants for init narrative, turn narrative, state extraction. | -| New or existing: mappers / context builder | Build `TurnContext` from scenario or session + turnRequest; map StateUpdateResponse → SessionResponse / TurnResponse. | -| [InMemorySessionService](src/main/java/de/neitzel/roleplay/business/InMemorySessionService.java) | Inject `OllamaClient`, `ObjectMapper`; implement createSession and submitTurn with two-call pattern and state merge; remove TODOs and placeholder narratives. | -| [InMemorySessionServiceTest](src/test/java/de/neitzel/roleplay/business/InMemorySessionServiceTest.java) | Add or adjust tests with mocked OllamaClient to verify createSession and submitTurn call Ollama and merge state. | -| Optional: exception mapper | Map `OllamaParseException` (and network errors) to a suitable HTTP response if not already. | - ---- - -## Done criteria - -- **createSession**: Opening narrative and initial situation/characters/suggestions come from Ollama (Call 1 + Call 2); no placeholder text. -- **submitTurn**: Turn narrative and updated situation/characters/suggestions come from Ollama; session state is updated; TurnResponse contains narrative and structured state (characterResponses, updatedSituation, updatedCharacters, suggestions). -- Unit tests cover the two-call flow with mocked OllamaClient and assert on state merge. -- No remaining TODOs in InMemorySessionService for Ollama integration.