A .NET MAUI + Blazor desktop application that orchestrates multi-participant AI discussions across multiple LLM providers. Create conversation panels where ChatGPT, Claude, Gemini, and DeepSeek debate topics with customizable personalities — and inject your own messages to steer the conversation in real time.
- Overview
- Features
- Architecture
- Getting Started
- Configuration
- How It Works
- Project Structure
- Services
- Theming
- Data Persistence
- JavaScript Interop
- Supported Providers
- Security
LLM Think Tank lets you pit multiple AI models against each other in structured conversations. Each participant has a customizable personality, and the app manages turn-taking, history sharing, and conversation persistence automatically. You can run multiple conversations in parallel using tabs, watch the discussion unfold, and jump in with your own messages at any time.
Built with:
- .NET 10 (MAUI + Blazor WebView)
- Targets: Windows, Android, iOS, macOS (Catalyst)
- No external UI framework beyond Bootstrap (included in wwwroot)
- 4 providers: OpenAI (ChatGPT), Anthropic (Claude), Google (Gemini), DeepSeek
- Each participant calls its provider's API with full conversation history
- Configurable model and max tokens per provider
- Run multiple independent conversations simultaneously
- Each tab has its own topic, participants, and message history
- Typing indicator (animated dots) on tabs with active conversations
- Right-click context menu for rename and close
- Conversations persist across app restarts
- Markdown-based personality definitions per participant
- 4 built-in default templates (one per provider)
- Create unlimited custom personalities
- AI-powered personality generation — describe what you want and the LLM writes it
- Per-template auth override (use different API keys or models per participant)
- Click the message input to pause the conversation
- Type a message and press Enter or click Send
- Your message is injected into the shared history for the next round
- Conversation automatically resumes after sending
- Use any configured provider to generate a discussion topic
- Dropdown to select which AI generates the topic
- 18 themes: dark, light, spring, summer, autumn, winter, matrix, ice, sunset, neon, dracula, solarized, midnight, aurora, ember, ocean, forest, mono
- Customizable control height (28-60px), gutter spacing (0-30px), and border radius (0-24px)
- All visual properties driven by CSS variables, updated via JS interop
- Real-time online/offline status indicators per provider
- Built-in connectivity test sends a test message to verify API access
- Debounced polling (10-second intervals)
- Each participant generates a markdown perspective file per conversation
- Tracks personality, latest response, and conversation context
- Viewable in the status panel's Context tab
- Raw API response logging (redacted for security)
- Status events for conversation milestones
- Three-tab status panel: Perspective, Context, Diagnostics
+----------------------------------------------------------+
| MAUI Shell (MainPage.xaml) |
| +----------------------------------------------------+ |
| | Blazor WebView | |
| | +----------+ +------------------------------+ | |
| | | NavMenu | | Page Content | | |
| | | (top) | | Home | Chat | Settings | | |
| | +----------+ +------------------------------+ | |
| +----------------------------------------------------+ |
+----------------------------------------------------------+
| | |
+----+-----+ +--------+--------+ +----+------+
| Settings | | ThinkTank | |Appearance |
| Service | | Service | | Service |
+----+-----+ +--------+--------+ +-----------+
| |
+----+-----+ +--------+----------------------------+
|Settings | | HTTP Clients |
| .json | | OpenAI | Claude | Gemini | DeepSeek|
+----------+ +-----------------------------------------+
All services are registered as singletons in MauiProgram.cs for shared global state.
- .NET 10 SDK with MAUI workload
- API keys for at least one provider (OpenAI, Anthropic, Google AI, or DeepSeek)
# Restore dependencies
dotnet restore
# Run on Windows
dotnet build -t:Run -f net10.0-windows10.0.19041.0
# Or use Visual Studio / VS Code with MAUI extension- Navigate to Settings > Providers
- Enter your API key(s) in the JSON editor for each provider
- Click model chips to set your preferred model
- Go to Conversations (Think Tank)
- Enter a topic, select participants, and click Start
Each provider's configuration is stored as a JSON object:
{
"type": "bearer",
"apiKey": "sk-your-key-here",
"model": "gpt-4o",
"maxTokens": 2048
}| Field | Description |
|---|---|
type |
Auth type: "bearer" (OpenAI/DeepSeek), "anthropic" (Claude), "google" (Gemini) |
apiKey |
Your API key for the provider |
model |
Model identifier to use for API calls |
maxTokens |
Maximum output tokens per response (default: 2048) |
Known Models (selectable via chip buttons in Settings):
| Provider | Models |
|---|---|
| OpenAI | gpt-4, gpt-5, gpt-5-mini, gpt-5-nano |
| Claude | claude-sonnet-4, claude-sonnet-4-6 |
| Gemini | gemini-2.5-flash, gemini-2.5-pro, gemini-2.0-flash, gemini-2.0-flash-lite |
| DeepSeek | deepseek-chat, deepseek-reasoner |
Personalities are markdown templates that define how each AI participant behaves in conversation. The personality markdown is sent as the system prompt to the provider's API.
- Default templates: ChatGPT, Claude, Gemini, DeepSeek (built-in, cannot be deleted)
- Custom templates: Create via Settings > Personalities > "+ Add"
- Generate: Click "Generate" to have the AI write a personality for you
- Auth Override: Each template can optionally override the provider's default auth config (different API key, model, or maxTokens)
All visual customization is in Settings > Appearance:
| Setting | Range | Default | Description |
|---|---|---|---|
| Theme | 18 options | dark | Color scheme for the entire app |
| Control Height | 28-60px | 40px | Height of buttons, inputs, and interactive elements |
| Gutter | 0-30px | 10px | Spacing between UI elements |
| Border Radius | 0-24px | 10px | Roundness of corners |
User enters topic + selects participants + clicks Start
|
v
+---------+
| Round N |<------------------------------+
+----+-----+ |
| |
v |
+-----------+ yes +----------+ |
|User typing?+--------->| Wait... | |
+-----+-----+ +----+-----+ |
no | | (user sends |
| | or clears) |
|<---------------------+ |
v |
For each participant: |
+-- Set as current speaker (typing dots) |
+-- Call provider API with shared history |
+-- Add response to messages |
+-- Save to chat.json + perspective.md |
+-- Wait 400ms |
| |
v |
+-------------+ no |
|Stop pressed? +--------------------------->/
+------+------+
yes |
v
Conversation ends
- Focus the message input field at the bottom of the chat
- The conversation pauses (overlay appears: "Conversation Paused")
- Type your message and press Enter or click Send
- Your message appears in the chat as "You" with a user avatar
- The message is added to shared history for the next round
- The conversation resumes automatically
After the first round completes, the app generates a conversation title in the background:
- Each participant is asked to suggest a concise title (max 5 words)
- The first participant then picks the best title from all suggestions
- The tab title updates automatically
ThinkTank/
+-- Components/
| +-- Layout/
| | +-- MainLayout.razor # App shell (NavMenu + content)
| | +-- MainLayout.razor.css
| | +-- NavMenu.razor # Top nav bar (Home, Conversations, Settings)
| | +-- NavMenu.razor.css
| +-- Pages/
| | +-- Home.razor # Landing page
| | +-- Chat.razor # Main think tank UI (~930 lines)
| | +-- Settings.razor # Provider auth + personality editor (~640 lines)
| | +-- SettingsAppearance.razor # Theme & visual controls
| | +-- NotFound.razor
| +-- Shared/
| | +-- ConfirmationDialog.razor # Reusable confirmation modal
| +-- Routes.razor
| +-- _Imports.razor
+-- Services/
| +-- ThinkTankService.cs # Core AI orchestration (~565 lines)
| +-- SettingsService.cs # Persistence layer (~360 lines)
| +-- AppearanceService.cs # Theme management
| +-- ChatConversationsService.cs # Tab/conversation management
| +-- ChatLogService.cs # Logging + chat storage
| +-- ProviderAuthConfig.cs # Auth config record
| +-- HumanNameService.cs # Random name generation
| +-- NameGeneratorService.cs # AI-powered name generation
+-- wwwroot/
| +-- app.css # Global styles + all 18 theme definitions
| +-- theme.js # JS interop for theme/control sizing
| +-- index.html # MAUI WebView host
| +-- lib/bootstrap/ # Bootstrap CSS
+-- Resources/ # Icons, fonts, images, splash screens
+-- App.xaml / App.xaml.cs # MAUI app entry
+-- MainPage.xaml # BlazorWebView host
+-- MauiProgram.cs # DI registration
+-- ThinkTank.csproj # Project config (.NET 10, MAUI)
The core orchestration engine. Routes API calls to the correct provider based on participant configuration.
Key responsibilities:
- Build conversation history in each provider's native message format
- Call provider APIs with personality prompts, topic context, and shared history
- Trim history to the last 8 turns (
MaxContextTurns) to stay within context limits - Sanitize model output (strip self-referencing prefixes like "[ChatGPT]:")
- Emit diagnostics events for API response logging
- Support per-participant auth overrides (different API keys, models, or token limits)
Provider endpoints:
| Provider | Endpoint |
|---|---|
| OpenAI | https://api.openai.com/v1/chat/completions |
| Claude | https://api.anthropic.com/v1/messages |
| Gemini | https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent |
| DeepSeek | https://api.deepseek.com/chat/completions |
Handles all persistence to %LOCALAPPDATA%\MindAttic\ThinkTank\Settings.json.
Manages:
- Provider auth configs (API keys, models, max tokens)
- Participant templates (personalities)
- Conversation state (tabs, messages, participants)
- Appearance settings (theme, control height, gutter, border radius)
Migrations:
- Auto-creates default templates on first launch
- Marks legacy templates as defaults when upgrading
- Injects
maxTokensinto existing auth configs that don't have it
Manages the 18-theme system and visual customization. Changes are applied via CSS variable updates through JS interop and persisted via SettingsService.
Manages the conversation tab system. Uses ObservableCollection<ChatConversation> for reactive UI updates. Handles tab creation, switching, closing, and persistence.
ChatLogService: In-memory log of chat events with a Changed event for UI updates.
ChatStorage (static): File-based persistence per conversation:
chat.json— append-only event log (starts, turns, user messages){participantId}.md— per-participant perspective markdown files
The app uses CSS custom properties (variables) defined per theme in wwwroot/app.css. Each theme is a html[data-theme="..."] selector block that overrides the root variables.
Each provider has a dedicated color used throughout the UI for avatars, message bubbles, borders, and accents:
| Provider | Color Variable | Default Hex | Avatar |
|---|---|---|---|
| ChatGPT | --openai |
#10a37f |
⬡ |
| Claude | --claude |
#cc785c |
◈ |
| Gemini | --gemini |
#4285f4 |
✦ |
| DeepSeek | --deepseek |
#a855f7 |
◉ |
| Variable | Purpose |
|---|---|
--bg |
Main background color |
--surface |
Card/panel background color |
--border |
Border color |
--text |
Primary text color |
--muted |
Secondary/dimmed text color |
--control-height |
Interactive element height (buttons, inputs) |
--gutter |
Element spacing |
--radius |
Border radius |
--title-gradient |
Nav brand text gradient |
| Theme | Style |
|---|---|
| dark | Default dark mode |
| light | Light mode with subtle backgrounds |
| spring | Green, pink, blue, purple accents |
| summer | Cyan, gold, sky, purple accents |
| autumn | Orange, pink, gold, purple — warm tones |
| winter | Green, blue hues — cool and crisp |
| matrix | Green monochrome terminal aesthetic |
| ice | Cyan and blue frosty tones |
| sunset | Pink, gold, blue, purple gradient feel |
| neon | Bright neon accents on dark background |
| dracula | Classic Dracula color scheme |
| solarized | Ethan Schoonover's Solarized palette |
| midnight | Dark with bright accent colors |
| aurora | Cyan, green, purple, pink — northern lights |
| ember | Warm orange and red tones |
| ocean | Deep blue oceanic tones |
| forest | Natural green forest palette |
| mono | Grayscale monochrome |
All data is stored in the local application data directory:
%LOCALAPPDATA%\MindAttic\ThinkTank\
+-- Settings.json # All app settings
+-- Personalities/ # Personality markdown files
| +-- {templateId}.md
+-- Conversations/
+-- {chatId}/
+-- chat.json # Append-only event log
+-- {participantId}.md # Perspective files
{
"ProviderAuth": {
"openai": "{ \"type\": \"bearer\", \"apiKey\": \"...\", \"model\": \"gpt-4o\", \"maxTokens\": 2048 }",
"claude": "{ \"type\": \"anthropic\", \"apiKey\": \"...\", \"model\": \"claude-sonnet-4-6\", \"maxTokens\": 2048 }",
"gemini": "{ \"type\": \"google\", \"apiKey\": \"...\", \"model\": \"gemini-2.5-flash\", \"maxTokens\": 2048 }",
"deepseek": "{ \"type\": \"bearer\", \"apiKey\": \"...\", \"model\": \"deepseek-chat\", \"maxTokens\": 2048 }"
},
"Templates": [ ... ],
"Conversations": [ ... ],
"AppearanceTheme": "dark",
"ControlHeight": 40,
"Gutter": 10,
"BorderRadius": 10
}- Conversation tabs and participants from
Settings.json - Message history from
Conversations/<chatId>/chat.json - Status events from
Settings.json - All appearance settings and provider configs
| Type | Description |
|---|---|
chat-start |
Conversation initialization with chatId and topic |
turn |
AI participant response with participantId, providerId, round, text |
user |
User-injected message with round and text |
The app uses JS interop for DOM operations that Blazor can't handle natively:
| Function | Location | Purpose |
|---|---|---|
setTheme(mode) |
theme.js | Sets html[data-theme] attribute |
setControlHeight(px) |
theme.js | Updates --control-height CSS variable |
setGutter(px) |
theme.js | Updates --gutter CSS variable |
setBorderRadius(px) |
theme.js | Updates --radius CSS variable |
isNearBottom(el, threshold) |
index.html | Checks if element is scrolled near bottom |
scrollToBottom(el) |
index.html | Scrolls element to bottom |
blurActive() |
index.html | Blurs the currently focused element |
- Auth: Bearer token
- API: Chat Completions (
/v1/chat/completions) - Default model: gpt-4o
- Message format: OpenAI chat messages (system/user/assistant roles)
- Auth:
x-api-keyheader +anthropic-versionheader - API: Messages (
/v1/messages) - Default model: claude-sonnet-4-6
- Message format: Anthropic messages with separate system prompt
- Auth: API key in URL query parameter
- API: GenerateContent (
/v1beta/models/{model}:generateContent) - Default model: gemini-2.0-flash-lite
- Message format: Google AI content parts with system instruction
- Auth: Bearer token
- API: Chat Completions (
/chat/completions) — OpenAI-compatible - Default model: deepseek-chat
- Message format: OpenAI-compatible chat messages
- Do not commit API keys. Provider auth JSON is stored in Local AppData and should remain local.
- API responses in the Diagnostics panel are redacted (content replaced with "...") to prevent accidental exposure.
- All HTTP communication uses HTTPS.