Technology summary
Controller map
Every controller inherits fromControllerBase (no views). Routes use attribute routing.
Public API controllers
Admin API controllers
All admin controllers are under/api/admin/ and use IAdminSupabaseClient for data access with service role key.
ArenaController — the core
TheArenaController (Controllers/Api/ArenaController.cs) is the heart of DualMind. It handles all chat operations.
Dependencies injected
Single chat flow (POST /api/arena/chat)
- Validate prompt is not empty
- Select model: if
request.Modelis"auto"or null, call_modelSelector.GetRandomModelAsync(); otherwise use the specified model - Call
ExecuteWithFallbackAsync(model, prompt, system, maxTokens, temperature) - Build
ChatResponsewith output content, model info, usage stats, response time - Log message via
_messageLogger.LogMessageAsync() - If
request.ThreadIdis set, persist to thread via_threadMessagesService.LogSingleAsync() - Return response
Dual chat flow (POST /api/arena/dualchat)
- Validate prompt
- Determine selection mode:
- Manual: Both
model1andmodel2specified by client - Topper:
_leaderboardModelSelector.GetTopperAndRandomModelAsync()— top-rated model vs random - Random (default):
_modelSelector.GetTwoRandomModelsAsync()
- Manual: Both
- Execute both models in parallel via
Task.WhenAll(task1, task2) - Build two
ChatResponseobjects - Log both messages and the comparison
- Compute arena metrics (winner by length, winner by tokens, verdict)
- Return
{ agent1, agent2, comparisonId, arena: { comparison, models } }
Fallback logic (ExecuteWithFallbackAsync)
Service layer
All business logic is inCore/Services/. Services are registered as Scoped except ModelSelector which is Singleton.
Data access layer
DualMind does not use Entity Framework. It makes direct HTTP calls to the Supabase PostgREST API.ISupabaseService (user-facing)
Used by public controllers. Configured with service role key in HttpClient default headers.
IAdminSupabaseClient (admin-facing)
Used by admin controllers. Provides generic CRUD operations:
Middleware pipeline
Configured inProgram.cs, executed in order:
- Exception handler — catches unhandled exceptions, returns
ProblemDetailsJSON - Request logging — logs correlation ID, method, path, duration, status code
- CORS —
AllowAllpolicy (any origin, method, header) - HTTPS redirection
- Authentication — JWT Bearer validation
- Authorization —
[Authorize]attribute enforcement - Controller routing —
app.MapControllers()
Error response format
All errors follow a consistent shape:INVALID_REQUEST, API_ERROR, STREAM_ERROR, THREADS_ERROR, THREAD_CREATE_ERROR, MODELS_ERROR, NOT_FOUND, UNAUTHORIZED, FORBIDDEN.