- Enhance InMemorySessionService to utilize the two-call Ollama pattern for session creation and turn submissions, generating narratives and state updates based on provided scenarios. - Introduce OllamaContextBuilder to construct turn contexts for both session initialization and turn continuation. - Add OllamaPrompts class to define system prompts for narrative generation and state extraction. - Implement StateUpdateMapper to handle merging state updates into session responses. - Create unit tests for InMemorySessionService to validate Ollama interactions and ensure correct session state management.
12 KiB
12 KiB
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.
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 – already provides
generateNarrative(model, systemPrompt, userContent)andgenerateStateUpdate(model, systemPrompt, userContent). - Common types (common) –
TurnContext,StateUpdateResponse,SituationSnapshot,CharacterSet,CharacterSnapshot,UserAction,Recommendation,SituationUpdate,CharacterUpdate,OpenThreadsChanges,CharacterResponse,Suggestion(withSuggestionType,RiskLevel). - API models (
de.neitzel.roleplay.fascade.model) – generated from OpenAPI:SessionResponse,TurnResponse,SituationState,CharacterState,Suggestion,CharacterResponseItem, etc. - Prompts – defined in ROLEPLAY_CONCEPT.md (§4.3 for init, §4.4 for turn). Should be extracted to constants or a small prompt provider (e.g. in
businessorcommon) so they stay consistent and maintainable.
Task 1: createSession – Opening scene and initial state
Flow
- Create
SessionResponsewith sessionId, model, language, safetyLevel, turnNumber (0) – unchanged. - Build initial situation and characters from
request.getScenario()(reuse existingbuildSituationFromScenario/buildCharactersFromScenario). - Build TurnContext for session init:
- currentSituation: Map from
SituationState(API) toSituationSnapshot(common). Initial values: setting, currentScene from scenario; timeline/openThreads/externalPressures/worldStateFlags can be empty or minimal. - characters: Map from
CharacterStatelist toCharacterSet(userCharacter + aiCharacters). Map each toCharacterSnapshot(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:
nullor"".
- currentSituation: Map from
- Serialise
TurnContextto JSON (snake_case) via JacksonObjectMapper. - Call 1 –
ollamaClient.generateNarrative(model, INIT_SYSTEM_PROMPT, contextJson). - Call 2 –
ollamaClient.generateStateUpdate(model, STATE_EXTRACT_SYSTEM_PROMPT, contextJson + "\n\nNarrative that was just generated:\n" + narrative). - 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 newSituationState): 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
Suggestionlist to APISuggestionlist andsession.setSuggestions(...).
- updated_situation → merge into
- Store session and return.
Design choices
- Context builder: Introduce a small helper (or service) to build
TurnContextfrom scenario + optional session state, and to serialise it. KeepsInMemorySessionServicefocused 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
- Load session; if missing return
Optional.empty()– unchanged. - Increment turn number – unchanged.
- Build TurnContext for this turn:
- currentSituation: From
session.getSituation()→SituationSnapshot(setting, currentScene, timeline, openThreads, externalPressures, worldStateFlags). - characters: From
session.getCharacters()→CharacterSet(userCharacter + aiCharacters asCharacterSnapshotlist). - userAction: From
turnRequest.getUserAction()→ map APIUserActionRequestto commonUserAction(type, content, selectedSuggestionId). - recommendation: From
turnRequest.getRecommendation()→ map to commonRecommendation(desiredTone, preferredDirection, focusCharacters), or null. - recentHistorySummary: For now empty string or a short placeholder; later can be derived from stored turn history if added.
- currentSituation: From
- Serialise
TurnContextto JSON. - Call 1 –
ollamaClient.generateNarrative(model, TURN_NARRATIVE_SYSTEM_PROMPT, contextJson). - Call 2 –
ollamaClient.generateStateUpdate(model, STATE_EXTRACT_SYSTEM_PROMPT, contextJson + "\n\nNarrative that was just generated:\n" + narrative). - Build TurnResponse:
- turnNumber, narrative (from Call 1).
- characterResponses, updatedSituation, updatedCharacters, suggestions from
StateUpdateResponse(mapped to API model types).
- Merge state update into session (same as createSession): apply updated_situation to
session.getSituation(), updated_characters tosession.getCharacters(), replacesession.setSuggestions(...), setsession.setNarrative(narrative),session.setTurnNumber(nextTurn). - 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) toTurnResponse(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:
TurnContextfrom scenario-derived situation + characters; no userAction/recommendation. - Turn:
TurnContextfrom 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
CharacterUpdatefind 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
- Prompts – Add
OllamaPrompts(or similar) with the three system prompt strings from the concept doc. - Mappers – Add mapping from API
SituationState/CharacterState(and scenario) to commonSituationSnapshot/CharacterSet/CharacterSnapshot; and from commonStateUpdateResponse/Suggestion/etc. to API types for SessionResponse/TurnResponse. Optionally a dedicated “context builder” that takes scenario or session + turnRequest and returnsTurnContext. - State merge – Implement “apply StateUpdateResponse to SessionResponse” (situation, characters, suggestions) in one place.
- createSession – Inject
OllamaClientandObjectMapper; build context from scenario; call two-step Ollama; merge state; set narrative/situation/characters/suggestions on session. - submitTurn – Build context from session + turnRequest; call two-step Ollama; build TurnResponse; merge state into session; return TurnResponse.
- Tests – Update or add unit tests: mock
OllamaClientinInMemorySessionServiceTestto 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 | Inject OllamaClient, ObjectMapper; implement createSession and submitTurn with two-call pattern and state merge; remove TODOs and placeholder narratives. |
| InMemorySessionServiceTest | 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.