Skip to content
docsv0.42.1

ProviderReach

WHETHER A MODEL ANSWERS, AND WHICH ONES THE PROVIDER OFFERS — asked, never assumed. ── WHY THIS EXISTS ────────────────────────────────────────────────────────────────────────────── Nothing in this framework could say «there is no reachable model». Every surface that showed one READ IT FROM CONFIG: the Desktop's footer printed `Model: qwen3.8-27b` whether or not anything was listening, because it resolved `agent.model` with a hardcoded fallback — and its endpoint fallback named a lab machine, a host that stopped resolving when that machine moved to a private network. Measured 2026-09-09: under its name on that network `:11438/v1/models` answers 200, and under its old name it answers 000 (greenhouse decisions/0266). So a surface asserted a model it had never asked. That is the mistake this house calls its most expensive — asking the TEXT what only EXECUTION answers — committed by the one line a person reads to find out what they are talking to. ── WHAT IT ANSWERS, INCLUDING WHAT IT CANNOT ──────────────────────────────────────────────────── `GET /v1/models` is the portable door — the same one {@see ProviderWindow} already knocks on for the context window, so this asks the question the house was already asking and reads a different part of the answer. There is no second transport and no second timeout. Three facts come back, and the third is the one nobody thinks to want: · did anything answer — `reached()` · which models it offers — `models()` · does it offer the one we DECLARE — `offersDeclared()` A provider that answers with a catalogue that does not contain the configured model is not a working house: every turn will fail at the provider, and the failure will look like a bug in the turn. Config says one thing, the server serves another, and until now nothing compared them. ── THE PROMISE, THE SAME ONE ITS SIBLING MAKES ────────────────────────────────────────────────── An unanswered question, never an exception. Unreachable, non-JSON, JSON without the field, an empty catalogue — each resolves as «not reached» or «none offered». The caller asked whether it could talk to a model, not for a new way to die. WHERE THE INPUTS CAME FROM IS NOT THIS CLASS'S BUSINESS. It measures a provider; whether the base URL arrived from `config/app.php`, from the environment, or from a package's default is something only the caller knows, and it is exactly what a person needs in order to fix an unreachable one — so the caller reports it beside this answer rather than this class guessing at it.

ProviderReach::__construct()

public function __construct(string $baseUrl, string $declared = '', ?callable $fetch = null):

Parameters

Parameters of __construct()
NameTypeDescription
$baseUrlstringwhere the provider lives — the same root {@see LlmService} is given, with or without a trailing `/v1`
$declaredstringthe model this house says it talks to, or '' when nothing was declared
$fetch(null | callable(string): ?string)the network seam; the default reads over HTTP/HTTPS with a short ceiling

ProviderReach::reached()

public function reached(): bool

Whether anything answered the catalogue door with a readable JSON object. A base URL that is empty never reaches: there is nothing to knock on, and inventing a default here is how a package ends up naming somebody else's machine.

ProviderReach::models()

public function models(): array

Every model id the provider offers, in the order it offered them. Ids only — a chip a person picks from needs a name it can send back, and the rest of an entry (`aliases`, `tags`, `meta`) is the provider's own vocabulary, which a caller that wants it can ask for itself. Empty when nothing answered, and empty when a provider answered with no catalogue: those are different facts, and {@see reached()} is what tells them apart.

ProviderReach::offersDeclared()

public function offersDeclared(): ?bool

Whether the provider offers the model this house DECLARED — `null` when there is nothing to compare. `null` is not `false`, and the difference is the whole point: nothing declared, or nothing reached, means the question was never answerable, while `false` means it WAS answered and the answer is that this house is configured to talk to a model its provider does not serve.