Skip to content

Repository files navigation

😈 demoniak

DEMONstrating AI in Kotlin — a teaching repo of small, self-contained examples for building with LLM APIs.

Kotlin JDK Gradle Anthropic OpenAI Google Mistral Ollama DeepSeek AWS Bedrock MCP

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.

🚀 Quick start

# 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.

📚 The examples

src/main/kotlin/demo/api/…

🤖 Anthropic — the full tutorial

# 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)

🌐 Other providers

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 server

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.

🎛️ Switching models

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.

📄 Deeper dives

  • 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.

📖 Shared corpus

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.