DEMONstrating AI in Kotlin — a teaching repo of small, self-contained examples for building with LLM APIs.
Every example is a standalone fun main() you can read in isolation and run from your IDE.
No framework, no app to assemble — just the API, one concept at a time.
# 1. Copy the template and fill in your real keys
cp src/main/resources/credentials.properties.example src/main/resources/credentials.properties
# then edit src/main/resources/credentials.properties
# 2. Compile
./gradlew compileKotlin
# 3. Run any example from the IDE run gutter (⚠️ working directory = project root)Keys are not env vars: every provider reads the same src/main/resources/credentials.properties
(git-ignored — never commit it), one property per provider (anthropic.api.key, openai.api.key,
google.api.key, mistral.api.key). Ollama needs no key (local server). Advanced: run with
-Dprofile=local (or APP_PROFILE=local) to overlay credentials.properties.local on top, if you
ever want to switch between multiple credential sets without editing the base file.
Exception: AWS Bedrock. There is no aws.properties — it has two auth variants instead, both
via real environment variables rather than a properties file. SigV4 (_01_starter_sigv4):
set AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY (+ AWS_SESSION_TOKEN if temporary) and
AWS_REGION — the AWS SDK's default provider chain picks them up on its own, and if you've ever
run aws configure/an SSO login, ~/.aws/credentials/~/.aws/config already satisfy it with no
env vars needed at all. API keys (_01b_starter_apikey, Bedrock's newer bearer-token auth):
set AWS_BEARER_TOKEN_BEDROCK to a key generated from the Bedrock console or
aws iam create-service-specific-credential.
src/main/kotlin/demo/api/…
| # | Topic | # | Topic |
|---|---|---|---|
_01 |
Starter — first call | _13 |
🔎 RAG over Sherlock Holmes (BM25, no embeddings) |
_02 |
Multi-turn chat | _14 |
🧠 Extended thinking (budget_tokens) |
_03 |
System prompts | _15 |
🧠 Adaptive thinking + effort |
_04 |
⚡ Prompt caching | _16 |
👁️ Vision |
_05 |
🔧 Tool definition | _17 |
👁️ Vision from an asset file |
_06 |
Temperature | _18 |
🔌 MCP client — tools over stdio |
_07 |
📡 Streaming | _19 |
🔌 MCP client — remote Streamable HTTP |
_08 |
Structured output via prefill | _20 |
🔌 MCP resources |
_09 |
Structured outputs (native) | _21 |
🔌 MCP prompts |
_10 |
✍️ Prompt engineering | _22 |
📄 OCR — images |
_11 _12 |
🔧 Tool parameters (typed / raw JSON) | _23 |
📄 OCR — PDF |
_24 |
🛡️ Moderation (Claude-as-classifier prompt) |
| Provider | Examples |
|---|---|
| OpenAI | _01 starter · _02 chat · _03 system prompt · _04 🔧 function calling · _05 📡 streaming · _06 structured outputs · _07 JSON mode · _08 👁️ vision · _09 👁️ vision from a URL · _10 🛡️ moderation · _11 🎛️ parameters (temperature, topP, seed, n, stop, logprobs) · _12 🎙️ speech-to-text + follow-up question · _13 🔁 Responses API (stateful previousResponseId, vs _02's stateless history) |
| Google Gemini | _01 starter · _02 chat · _03 system prompt · _04 🛡️ moderation (safety ratings, not a dedicated endpoint) |
| Mistral | _01 starter · _02 structured outputs · _03 function calling · _04 📄 OCR (dedicated endpoint) · _05 📄 OCR — PDF, chained into chat for targeted extraction · _06 🧑💻 Codestral FIM · _07 👁️ vision · _08 🔎 Embeddings + semantic RAG over Sherlock Holmes · _09 🛡️ moderation (dedicated endpoint) |
| Ollama 🏠 | _01 starter · _02 chat, both via the OpenAI-compatible API and the native HTTP API · _03 🔧 tool calling (native /api/chat) · _04 structured outputs (native format + JSON Schema) · _05 🔎 local embeddings + semantic RAG over Sherlock Holmes · _06 👁️ vision (native images field) · _07 📊 local perf introspection (load_duration, tokens/s, /api/ps) · _08 📦 model management by code (/api/pull, /api/show, /api/delete) — see doc/ollama.md for the two API surfaces and local model picks |
| DeepSeek | _01 starter — OpenAI-compatible (openai-java + different baseUrl, no /v1 segment), model deepseek-v4-flash · _02 🧠 thinking mode (dual-mode toggle, reasoning_content) · _03 🔧 function calling · _04 ⚡ prompt caching (automatic, prompt_cache_hit_tokens/prompt_cache_miss_tokens) |
| AWS Bedrock | Claude via Bedrock's Converse API · _01 starter (SigV4 auth) · _01b starter (API key / bearer token auth) · _02 multi-turn chat · _03 🔀 multi-vendor router — cheap Nova Micro triage escalates to Claude Haiku only when a question needs it, same client/API for both vendors |
mcp/python/server.py — a dependency-free, pure-stdlib Python stdio server exposing all three
MCP primitives: tools (roll_dice, slugify), resources (the Sherlock stories) and a
prompt (haiku). Shell helpers (list_tools.sh, call_tools.sh, …) let you poke at it without
Kotlin — see mcp/python/README.md.
Each provider has a common/models.kt with a Models.DEFAULT — change it in one place and every
example of that provider follows.
Note
The Anthropic default is claude-haiku-4-5 on purpose: several examples demonstrate
older mechanics. Assistant prefill (_08), budget_tokens thinking (_14) and temperature
(_06) are rejected by the newest models — their modern replacements are _09 and _15.
Switching the default to Opus 4.6+ / Sonnet 5 will make those three fail by design.
doc/ollama.md— how Ollama runs models locally, its two API surfaces (native vs. OpenAI-compatible), and a rundown of common local model families with what's actually used in this repo's examples.doc/moderation.md— moderation compared across all four providers (Anthropic, OpenAI, Google, Mistral): dedicated endpoint vs. safety metadata vs. prompt-based classification, category-by-category.
assets/books/ holds 12 public-domain Sherlock Holmes stories (Project Gutenberg). They feed two
contrasting retrieval strategies: chunk-and-rank RAG (_13) versus whole-document reads through MCP
resources (_20). assets/images/ and assets/pdf/ feed the vision and OCR examples.