LLM
LLM is an abstract facade every provider adapter
(Monad\Clarity\Services\LLMAdapters\{OpenAI,Anthropic,DeepSeek,Gemini}) implements: one
complete() method translating a provider-agnostic request to that provider's wire
format and back. No global registry — construct the adapter you want directly, with its own
credentials and an HttpClient:
use Monad\Clarity\Services\HttpClient;
use Monad\Clarity\Services\LLMAdapters\Anthropic;
use Monad\Clarity\Services\LLM\LLMRequest;
$adapter = new Anthropic(apiKey: $apiKey, httpClient: new HttpClient());
$response = $adapter->complete(new LLMRequest(
model: 'claude-opus-4',
messages: [['role' => 'user', 'content' => 'Say hello in five words.']],
systemInstruction: 'You are terse.',
));
$response->content; // the model's text reply
LLMRequest fields
new LLMRequest(
string $model,
array $messages, // [['role' => 'user'|'assistant', 'content' => string], ...]
?string $systemInstruction = null, // a top-level field, never a message — providers disagree on system-role messages
float $temperature = 1.0, // 0.0–2.0
int $maxOutputTokens = 1024,
int $timeoutSeconds = 30,
?array $responseSchema = null, // a JSON Schema the output must conform to; null = plain text
)
Validated at construction — a malformed request throws before any network round trip.
LLMResponse fields
$response->provider; // e.g. 'anthropic'
$response->model;
$response->content; // string, or array if $responseSchema was set and honoured
$response->usage; // ['inputTokens' => int, 'outputTokens' => int]
$response->providerRequestId; // ?string
$response->raw; // the provider's full decoded response body — an escape hatch
What's deliberately not here
No agents, tool orchestration, vector databases, memory, prompt pipelines, or automatic cross-provider retries — a thin, provider-agnostic request/response contract, not an agentic framework.
Next steps
- HttpClient — what every adapter sends requests over.