POST /api/arena/dualchat
Send a prompt to two AI models simultaneously for blind comparison. Supports random, topper, and manual selection modes.
POST
Dual Chat (Arena Mode) Stable
Send a prompt to two AI models simultaneously. Models respond in parallel, and responses are returned anonymized for blind comparison.Authentication Required
JWT Claims Extraction (Lines 338-350):Request Body
string
required
User message sent to both modelsValidation (Line 200):
string
Model selection strategyValues:
"random": Two different random active models (Lines 246-249)"topper": Top-performing model + random model (Lines 237-242)- Default if manual models provided:
"manual"(Line 233)
string
First model name (manual selection)Validation (Lines 218-229):Required: Only if
model2 also providedstring
Second model name (manual selection)Required: Only if
model1 also providedstring
System prompt applied to both modelsDefault: Implementation-defined and not guaranteed (provider-specific)
integer
Maximum tokens per model responseApplies: To both models independently
number
Sampling temperature (0.0-2.0) for both modelsDefault: Not specified by API contract (provider-defined)
string
Session tracking identifierAuto-generation (Line 197):
string
Thread UUID to associate comparison with conversationValidation (Lines 354-360):
Response
boolean
Always
true on success (Line 392)object
First model response (same structure as single chat)
object
Second model response (same structure as agent1)
string
UUID identifying this comparison (Line 198, 395)Used for: Voting via
/api/arena/model-voteobject
string
ISO8601 UTC timestamp (Line 415)
integer
Total request duration (Lines 265, 416)Note: Due to parallel execution, approximately equal to slowest model time
Side Effects
Database Mutations (Lines 333-360):-
message_logs table (Lines 333-334):
- Two separate log entries (one per model)
-
users table UPSERT (Lines 336-350):
- Executes on every authenticated request
- Idempotent operation
-
comparisons table (Line 352):
- Stores comparison data for voting/leaderboard
- Links to both models
-
thread_messages table (conditional, Lines 354-360):
- Only if
threadIdprovided and valid GUID - Links message to comparison via
comparisonId
- Only if
Behavior
Parallel Execution (Lines 254-257):- Each model has independent 45s timeout
- Each model has independent fallback chain
- One model failure doesn’t block the other
- Measures total elapsed time
- Due to parallel execution:
max(model1_time, model2_time) + overhead
Topper Mode Implementation (Lines 239-241):
- Queries
model_votestable for highest win rate - Pairs top model with random model
- Ensures diverse comparison
Error Conditions
Nested Exception Handling (Lines 421-443):
Edge Cases
- Same model selected twice: Not prevented by code, allowed in random selection
- Topper mode with insufficient vote data: Behavior not enforced by server contract (assumed fallback to random selection)
- Invalid threadId GUID: Silently skipped, no error (Line 356 guard)
- Model fallback changes model names:
finalModel1andfinalModel2may differ from requested models - Null usage stats: Handled with null-coalescing (Lines 366-367)
Comparison ID Usage
Generated at request start (Line 198):- Logging comparison to
comparisonstable (Line 352) - Linking to thread message in
thread_messagestable (Line 358) - Returned in response for voting (Line 395)
- Voting endpoint requires this ID:
POST /api/arena/model-vote
Rate Limits
No explicit rate limiting in controller. Provider-level limits apply:- Groq free tier: 30 req/min, 14,400 tokens/min
- Dual chat consumes 2× tokens (both models)
- Effective limit: ~15 dual-chat requests/min on free tier