https://github.com/ebullient/quarkus-soloplay
Generated by code.quarkus.io
https://github.com/ebullient/quarkus-soloplay
Last synced: about 1 month ago
JSON representation
Generated by code.quarkus.io
- Host: GitHub
- URL: https://github.com/ebullient/quarkus-soloplay
- Owner: ebullient
- License: apache-2.0
- Created: 2025-12-08T16:17:34.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2026-03-06T03:22:16.000Z (4 months ago)
- Last Synced: 2026-03-29T16:56:54.629Z (3 months ago)
- Language: Java
- Size: 743 KB
- Stars: 1
- Watchers: 0
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# Soloplay
An AI-powered solo tabletop RPG assistant,
built as a conference demo for progressive disclosure of LLM and RAG integration concepts.
Built with **Quarkus**, **LangChain4j**, **Ollama** (local LLM), and **Neo4j** (graph database + vector storage).
---
## What This Demonstrates
This application shows four levels of LLM integration, each building on the last:
### Level 1 — Plain Chat (`/chat`)
A single `@RegisterAiService` interface with an inline system prompt. No memory, no tools —
the simplest possible LangChain4j integration.
**Key source:**
- [`ChatAssistant`](src/main/java/dev/ebullient/soloplay/ai/ChatAssistant.java) — the AI service interface
- [`ChatResource`](src/main/java/dev/ebullient/soloplay/api/ChatResource.java) — REST endpoint at `/api/chat`
### Level 2 — Guided Character Creation (`/play/{gameId}`)
Session memory via `@MemoryId`, structured JSON output with guardrails, and a multi-turn
conversation that extracts character details across several stages.
**Key source:**
- [`ActorCreationAssistant`](src/main/java/dev/ebullient/soloplay/play/ActorCreationAssistant.java) — AI service with session memory and structured extraction
- [`ActorCreationEngine`](src/main/java/dev/ebullient/soloplay/play/ActorCreationEngine.java) — orchestrates the multi-stage creation flow
### Level 3 — Lore Queries with RAG (`/lore`)
Retrieval-augmented generation: vector similarity search against ingested campaign documents,
with AI tools for cross-reference resolution.
The document ingestion pipeline (`/ingest`) prepares the knowledge base: upload markdown
with YAML frontmatter → chunk → embed with nomic-embed-text → store in Neo4j vector index.
**Key source:**
- [`LoreAssistant`](src/main/java/dev/ebullient/soloplay/ai/LoreAssistant.java) — RAG-enabled AI service
- [`LoreRetriever`](src/main/java/dev/ebullient/soloplay/ai/LoreRetriever.java) — `RetrievalAugmentor` supplier for Neo4j vector search
- [`LoreTools`](src/main/java/dev/ebullient/soloplay/ai/LoreTools.java) — AI tools: document retrieval and semantic search
- [`IngestService`](src/main/java/dev/ebullient/soloplay/IngestService.java) — document parsing, chunking, and embedding pipeline
### Level 4 — Full Gameplay (`/play/{gameId}`)
Full orchestration via an `AgentOrchestrator` that coordinates five specialized agents per turn.
Only the narration agent maintains session memory and has tool access; the other four are stateless
and run concurrently via `CompletableFuture`. A WebSocket transport streams results for interactive play.
The five agents:
| Agent | Purpose | Stateful? |
| ----- | ------- | --------- |
| [`NarrationAgent`](src/main/java/dev/ebullient/soloplay/play/agents/NarrationAgent.java) | Storytelling — produces the scene narration | Yes (session memory + tools) |
| [`DiceAgent`](src/main/java/dev/ebullient/soloplay/play/agents/DiceAgent.java) | Decides if/what dice roll is required | No |
| [`SuggestionAgent`](src/main/java/dev/ebullient/soloplay/play/agents/SuggestionAgent.java) | Generates 2–3 concrete next-action choices | No |
| [`CheckpointAgent`](src/main/java/dev/ebullient/soloplay/play/agents/CheckpointAgent.java) | Identifies milestone moments and segment completion | No |
| [`RecapAgent`](src/main/java/dev/ebullient/soloplay/play/agents/RecapAgent.java) | Produces a 1–2 sentence turn summary | No |
**Key source:**
- [`AgentOrchestrator`](src/main/java/dev/ebullient/soloplay/play/agents/AgentOrchestrator.java) — coordinates agents, assembles the composite `GamePlayResponse`
- [`GamePlayEngine`](src/main/java/dev/ebullient/soloplay/play/GamePlayEngine.java) — routes turns through scene start, recap, action, and roll resolution
- [`GameTools`](src/main/java/dev/ebullient/soloplay/play/GameTools.java) — AI tools: story recap, actor lookup, location lookup
- [`PlayWebSocket`](src/main/java/dev/ebullient/soloplay/play/PlayWebSocket.java) — WebSocket at `/ws/play/{gameId}` for interactive play
---
## Application Routes
| Route | Description |
| ----- | ----------- |
| `/` | Landing page — links to all sections |
| `/chat` | Level 1: Plain chat with the LLM |
| `/lore` | Level 3: Lore queries with RAG |
| `/ingest` | Document upload and ingestion management |
| `/game` | Game list; create new games |
| `/play/{gameId}` | Levels 2 + 4: Character creation → active gameplay |
| `/inspect/{gameId}` | View game state and turn history |
| `/party/{gameId}` | Manage party members |
---
## Running the Demo
### Prerequisites
**Ollama** (local LLM):
```shell
ollama serve
ollama pull llama3.2 # default chat model
ollama pull nomic-embed-text # embeddings
```
The default model (`llama3.2`) works for levels 1 and 2. For tool calling at levels 3 and 4,
a larger model is recommended. Override via `.env` in the project root:
```shell
QUARKUS_LANGCHAIN4J_OLLAMA_CHAT_MODEL_MODEL_NAME=qwen3:14b
```
**Neo4j** (graph database + vector store):
```shell
docker compose up -d neo4j
```
### Start the application
```shell
./mvnw quarkus:dev
```
Open [http://localhost:8080](http://localhost:8080).
---
## Frontend: Renarde + Vanilla JS
The UI is server-rendered with [Renarde](https://docs.quarkiverse.io/quarkus-renarde/dev/index.html)
(Qute templates, type-safe routing, flash scope) and driven entirely by vanilla JavaScript — no
frontend framework, no build step.
**Renarde controllers** handle page routing, form submissions, and redirects:
- [`Game`](src/main/java/dev/ebullient/soloplay/web/Game.java) — CRUD forms with flash-scope feedback
- [`Play`](src/main/java/dev/ebullient/soloplay/web/Play.java) — serves the play page with a `data-game-id` attribute for JS to pick up
- [`Lore`](src/main/java/dev/ebullient/soloplay/web/Lore.java) — multipart file upload for document ingestion (CSRF-protected with `{#authenticityToken/}`)
**Vanilla JS modules** add client-side interactivity:
| Module | What it does |
| ------ | ------------ |
| [`play-interface.js`](src/main/resources/META-INF/resources/play-interface.js) | WebSocket streaming chat — reconnection, message buffering, choice buttons |
| [`chat-interface.js`](src/main/resources/META-INF/resources/chat-interface.js) | Simple fetch-based chat for `/chat` and `/lore` |
| [`game-index.js`](src/main/resources/META-INF/resources/game-index.js) | Game list — info/delete modals via REST calls |
| [`inspect.js`](src/main/resources/META-INF/resources/inspect.js) | Developer tool: browse game-state JSON from `/api/game` endpoints |
The REST API under `/api/` ([`GameResource`](src/main/java/dev/ebullient/soloplay/api/GameResource.java),
[`ChatResource`](src/main/java/dev/ebullient/soloplay/api/ChatResource.java),
[`LoreResource`](src/main/java/dev/ebullient/soloplay/api/LoreResource.java)) provides
the JSON and HTML-fragment responses consumed by these scripts.
---
## Presentations
* [Devnexus 2026 (Erin Schnabel and Jennifer Reif)](https://speakerdeck.com/jmhreif/supercharge-your-applications-with-java-graphs-and-a-touch-of-ai)
## Contributing
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for architecture details, API reference,
build commands, and contributor guidelines.