ADR-018: Multi-Provider Model Discovery
- Status
-
Accepted
- Date
-
2025-12
- Authors
-
Netresearch DTT GmbH
Context
Different LLM providers expose different model listing APIs. OpenAI offers
GET /v1/models, Ollama uses GET /api/tags, Anthropic has no public
listing endpoint, and Gemini uses a different URL structure entirely. The
setup wizard needs a unified way to discover available models regardless of
provider.
Problem statement
- Heterogeneous APIs: No standard protocol for model listing.
- Authentication variance: Bearer tokens, API key headers, URL parameters.
- Response format divergence: Each provider returns different JSON structures.
- Offline providers: Some providers (Anthropic, Azure) lack public model list APIs.
- Endpoint normalization: Users enter URLs with/without trailing slashes, versions, schemes.
Decision
Abstract model discovery behind Model with two operations:
ModelDiscoveryInterface contract
interface ModelDiscoveryInterface
{
/** @return array{success: bool, message: string} */
public function testConnection(DetectedProvider $provider, string $apiKey): array;
/** @return array<DiscoveredModel> */
public function discover(DetectedProvider $provider, string $apiKey): array;
}
Copied!
The Model implementation dispatches per adapter type:
Provider-specific dispatch
public function discover(DetectedProvider $provider, string $apiKey): array
{
return match ($provider->adapterType) {
'openai' => $this->discoverOpenAI($endpoint, $apiKey),
'anthropic' => $this->discoverAnthropic($endpoint, $apiKey),
'gemini' => $this->discoverGemini($endpoint, $apiKey),
'ollama' => $this->discoverOllama($endpoint),
'mistral' => $this->discoverMistral($endpoint, $apiKey),
'groq' => $this->discoverGroq($endpoint, $apiKey),
'openrouter' => $this->discoverOpenRouter($endpoint, $apiKey),
default => $this->getDefaultModels($provider->adapterType),
};
}
Copied!
Key design elements:
- API-driven discovery for providers with listing endpoints (OpenAI, Ollama, Mistral, Groq, OpenRouter, Gemini).
- Static fallback catalogs for providers without listing endpoints (Anthropic, Azure, unknown). Maintained with current model information.
- Provider detection via
Providerusing URL pattern matching with confidence scores (1.0 for exact match, 0.3 for unknown).Detector - Normalized DTOs:
Discoveredunifies model metadata across providers (modelId, name, capabilities, contextLength, costs, recommended flag).Model - Authentication dispatch: Per-provider header format (
Authorization: Bearer,x-api-key,x-goog-api-key, none for Ollama).
Provider detection patterns
Provider matches endpoint URLs against known patterns:
| Pattern | Adapter Type | Confidence |
|---|---|---|
| api.openai.com | openai | 1.0 |
| api.anthropic.com | anthropic | 1.0 |
| generativelanguage.googleapis.com | gemini | 1.0 |
| \*.openai.azure.com | azure_openai | 1.0 |
| localhost:11434 | ollama | 1.0 |
| \*/v1/chat/completions (path match) | openai | 0.6 |
| Unknown endpoint | openai (fallback) | 0.3 |
Consequences
Positive:
- ●● Unified model discovery across seven provider types.
- ● Static catalogs ensure discovery works even without API access.
- ● Confidence scoring lets the UI warn about uncertain detections.
- ◐ PSR HTTP interfaces allow testing with mock HTTP clients.
- ◐ Endpoint normalization handles common user input variations.
Negative:
- ◑ Static catalogs require periodic updates as providers release new models.
- ◑ API-based discovery may expose all models, including deprecated ones.
- ✕ Rate limiting on model listing endpoints not handled.
Net Score: +5.0 (Strong positive)
Files changed
Added:
Classes/Service/ Setup Wizard/ Model Discovery Interface. php Classes/Service/ Setup Wizard/ Model Discovery. php Classes/Service/ Setup Wizard/ Provider Detector. php Classes/Service/ Setup Wizard/ DTO/ Detected Provider. php Classes/Service/ Setup Wizard/ DTO/ Discovered Model. php