ProviderEndpoint
The ONE reading of a provider's base URL — every door this gateway knocks on is this root plus its own path. There were three readings and they disagreed (greenhouse evidence/1069 §C1). {@see ProviderReach} and {@see ProviderWindow} trimmed a trailing `/v1`; {@see LlmService} did not. So with `http://…:11438/v1` — the spelling every OpenAI guide shows — the catalogue question asked `/v1/models` and answered «1 model(s) served», while the turn asked `/v1/v1/chat/completions` and died on a 404. Two judges of the same endpoint, one saying yes and the other failing: the panel said a thing the turn could not do. BOTH SPELLINGS SERVE (greenhouse decisions/0536). A base URL with or without its `/v1` names the same server: refusing one of them would make a person unlearn the guide they copied from, and the only thing it would protect is a provider whose chat lives under `/v1/v1/…`, which none does.
ProviderEndpoint::root()
public static function root(string $baseUrl): stringThe root every path is appended to: trimmed, without a trailing slash and without a trailing `/v1`. Only a WHOLE trailing `/v1` segment is dropped: `…/api/v1` keeps `/api`, `…/v1beta` stays as it is.
Parameters
| Name | Type | Description |
|---|---|---|
| $baseUrl | string |
ProviderEndpoint::shown()
public static function shown(string $url): stringAn endpoint as a person may read it — on the panel, in the ledger: without the credentials a base URL can carry (`http://user:secret@host`), and without a query or fragment (where a key sometimes travels). A string that is not a URL is returned as it came.
Parameters
| Name | Type | Description |
|---|---|---|
| $url | string |