Most AI demos start with a prompt.
That is useful, but it is not quite an app yet.
Apps need memory. They need state. They need validation. They need some way to turn a model response into something the rest of the application can understand.
A choose-your-own-adventure game is a fun way to show that pattern.
The user picks a genre, names a character, reads a generated scene, chooses what to do next, and the app keeps the story moving. Behind the scenes, the model is not just producing prose. It is returning structured JSON that the Flask app can store, validate, and send back to the player.
Code:
What the example builds
The project is a Python Flask API for a short text adventure game.
The player can:
- start a new game
- pick a genre
- name their character
- read a generated scene
- choose option 1, 2, or 3
- continue the story for a few turns
- end with a
won,lost, orescapedstatus
The app tracks:
game_idgenreplayer_name- story history
- current scene
- choices
- location
- health
- inventory
- turn count
- game status
That turns the LLM into one part of an application, not the whole application.
The API shape
The app exposes:
POST /game/startPOST /game/<id>/chooseGET /game/<id>GET /gamesGET /health
To start a game:
curl -X POST http://localhost:5000/game/start \
-H "Content-Type: application/json" \
-d '{
"genre": "fantasy",
"player_name": "Aria"
}' | python3 -m json.tool
The response includes the generated opening scene, three choices, and game state:
{
"game_id": "game-1750280400",
"scene": "Aria awakens beneath the twisted boughs of the Eldergrove...",
"choices": [
"Approach the crumbling tower through the mist",
"Follow the luminescent mushroom path deeper into the grove",
"Turn toward the howl and prepare to face whatever hunts in the dark"
],
"state": {
"location": "Eldergrove",
"health": 100,
"inventory": [],
"turn": 1
},
"status": "ongoing"
}
Then the player chooses what to do next:
curl -X POST http://localhost:5000/game/game-1750280400/choose \
-H "Content-Type: application/json" \
-d '{"choice": 1}' | python3 -m json.tool
The app sends the story history and the selected choice back to Telnyx AI Inference, then returns the next scene.
How it uses Telnyx AI Inference
The app sends chat-completion requests to:
POST /v2/ai/chat/completions
The default model is:
AI_MODEL=moonshotai/Kimi-K2.6
The system prompt asks the model to act as a game master and return JSON only:
{
"scene": "the scene description",
"choices": ["choice 1", "choice 2", "choice 3"],
"state": {
"location": "...",
"health": 100,
"inventory": ["..."],
"turn": 1
},
"status": "ongoing"
}
That structured response is the important part.
The model can be creative, but the app still receives something predictable:
- prose goes into
scene - actions go into
choices - state goes into
state - game progress goes into
status
This is the same pattern you would use for less playful applications too: training simulations, onboarding flows, troubleshooting assistants, interactive tutorials, or guided decision trees.
Why this is a useful AI app pattern
A game is a friendly demo, but the architecture is serious.
The app owns the durable state. The model generates the next step. The response is structured enough that normal backend code can decide what happens next.
That separation matters.
If the model only returned free-form text, the app would have to guess what changed. Did the player take damage? Did they pick up an item? Did the game end?
By asking for JSON, the app gets a clean contract:
- update the inventory
- update health
- show the next choices
- stop if the status is no longer
ongoing
That is how you move from "LLM as a text box" to "LLM as part of an application."
Running the example
Clone the repo:
git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/adventure-game-python
Create your environment file:
cp .env.example .env
Add your Telnyx API key:
TELNYX_API_KEY=your_telnyx_api_key
AI_MODEL=moonshotai/Kimi-K2.6
HOST=127.0.0.1
PORT=5000
Install dependencies and start the app:
pip install -r requirements.txt
python app.py
Check health:
curl http://localhost:5000/health
Start a game:
curl -X POST http://localhost:5000/game/start \
-H "Content-Type: application/json" \
-d '{"genre":"cyberpunk","player_name":"Void"}' | python3 -m json.tool
Choose the next action:
curl -X POST http://localhost:5000/game/game-<id>/choose \
-H "Content-Type: application/json" \
-d '{"choice":1}' | python3 -m json.tool
Try different genres:
curl -X POST http://localhost:5000/game/start \
-H "Content-Type: application/json" \
-d '{"genre":"mystery","player_name":"Mira"}'
curl -X POST http://localhost:5000/game/start \
-H "Content-Type: application/json" \
-d '{"genre":"post-apocalyptic","player_name":"Scout"}'
Supported genres are:
fantasysci-fimysteryhorrorcyberpunkpost-apocalyptic
Where this can go next
The sample stores games in memory so the mechanics stay easy to follow.
A production version could add:
- Redis or Postgres for persisted game sessions
- authentication and per-user saved games
- streaming responses so scenes appear as they are generated
- a web UI with buttons for choices
- image generation for scenes
- multiplayer branches
- moderation rules for family-friendly genres
- analytics for which choices players pick
- longer campaigns with checkpoints
But the core workflow stays the same:
- keep state in your app
- send relevant context to the model
- request structured output
- validate and store the response
- let the user take the next action
That pattern is useful far beyond games.