Discover and synchronize OpenAI-compatible Pi provider models #32

Closed
opened 2026-08-24 21:04:31 +01:00 by vnprc-agent · 0 comments

Let an operator add an OpenAI-compatible Pi provider from its URL and bearer token, then keep its advertised model catalog synchronized without hand-written model files or inference from Nexus.

Primary goals:

  • Discover complete providers — query the authenticated /models endpoint, translate useful advertised metadata into Pi model entries, and import every reported model by default.
  • Keep catalogs current — add a refresh operation that reports metadata changes, adds new models, and safely removes models the provider no longer advertises.
  • Make the common path short — default a new credential to the provider ID and target every inventory-declared libvirt dev VM, while preserving explicit target and credential overrides.
  • Reuse credentials safely — authenticate discovery with the credential's encrypted deployment-default token when available, without exposing plaintext through arguments, environment variables, logs, or persistent files.
  • Document the boundary — make clear that Nexus performs authenticated metadata reads only; inference, commits, pushes, rebuilds, deployment, and provider-side token lifecycle remain outside this command.

Current state

scripts/pi-provider accepts models through --model or --models-file. The operator must separately call the provider, translate its response into Pi's model vocabulary, preserve a temporary file long enough for the command to consume it, and repeat that work whenever the provider adds or retires models. docs/credentials.md documents the manual interface and currently promises that the tool never verifies a remote request.

The existing lifecycle already owns repository resolution, inventory classification, credential-to-provider lookup, transaction staging, rollback, recovery, derived-output validation, and Age ciphertext. This feature should extend those mechanisms rather than create a second writer or a provider-specific helper.

Operator interface

Add discovery commands with this intended shape; the development plan may adjust spelling if the final Go CLI has a more coherent equivalent:

pi-provider [--dry-run] discover-add <provider> --url <https-url> [--credential <credential>] [--target <dev-vm>[,<dev-vm>...]] [--api <adapter>] [--prompt-token]
pi-provider [--dry-run] discover-refresh <provider> [--prompt-token]

discover-add defaults the credential ID to the provider ID, the adapter as described below, and the target set to all machines for which inventory reports type == "dev" and runtime == "libvirt". Repeated/comma-separated --target remains the advanced least-privilege override, and the existing retarget command remains supported. Do not add a model-selection prompt: every valid reported model enters the catalog and Pi's model selector remains the place where an operator chooses one.

The URL is the provider's HTTPS API base, normally ending in /v1; discovery performs GET <base>/models. A malformed URL, failed authentication, non-success response, invalid JSON, missing/non-array data, empty model set, duplicate/empty model IDs, or a response too large for a documented defensive limit must fail before repository mutation. Diagnostics may include HTTP status and a bounded provider error message, but never authorization material.

Keep the existing manual add path for providers whose discovery endpoint is absent or insufficient.

Metadata translation

Treat /models as OpenAI-compatible but allow useful provider extensions. Translate only an explicit allowlist into Pi model fields; do not copy the response wholesale and do not close the existing pass-through schema used by manual --models-file input.

At minimum:

  • id becomes the required Pi model id.
  • context_window, context_length, or max_model_len may supply contextWindow, with documented precedence when more than one appears.
  • max_output_tokens or max_completion_tokens may supply maxTokens, again with documented precedence.
  • Explicit image-input capability may add image beside text in Pi's input list; otherwise remain text-only.
  • Explicit reasoning-trace/thinking capability may set Pi's reasoning flag. Provider metadata wins; any name-based fallback must be conservative, documented, and reported as an inference rather than a provider fact.
  • If chat completions are explicitly supported, default to openai-completions. If only Responses is explicitly supported, use Pi's Responses adapter. A minimal standard response with no capability declaration remains usable with the documented conservative defaults.

Omit unknown limits rather than inventing provider facts, allowing Pi's own defaults to apply, and warn that the metadata was incomplete. Validate translated positive integer limits and supported Pi field shapes before staging them.

Operational metadata such as route health, reliability, evidence timestamps, and deprecation notices should inform output but should not be persisted into models.json unless it is part of Pi's model schema. Warn without blocking when a provider reports an unhealthy/unknown route, unverified tool use, deprecation, or a maximum output of 1,024 tokens or fewer.

Refresh and removal safety

discover-refresh resolves the provider's credential and compares the translated result with the currently configured model list. It reports three stable, sorted groups: added models, removed models, and retained models whose translated metadata changed. New models and metadata changes are staged automatically.

Absence from /models is not enough by itself to remove configured state. When the first response would remove any model, fetch a second complete snapshot. Abort without mutation if either response is empty/invalid or the normalized snapshots disagree. If they agree, display the removal diff and require explicit confirmation before staging it. A declined or interrupted confirmation changes nothing. This is a safety check against inconsistent discovery responses, not an inference-health probe.

Preserve model order deterministically so an unchanged provider response produces no file diff. Refresh changes provider metadata only; it never changes credential targets, named tokens, or the selected deployment default.

Credential and network boundary

When discover-add creates a credential, read the bearer once through the existing hidden-input path. Use it to authenticate discovery and, only after discovery and all preflight checks succeed, encrypt it as the default named token.

When discover-add uses an existing credential, or when discover-refresh resolves one, authenticate with only that credential's non-null defaultToken. Decrypt its ciphertext on demand with the configured Nexus Age identity and stream the plaintext directly into the HTTP client's private configuration/input path. Discovery against an existing credential must never add, rotate, rewrite, or change the selection of a named token.

Plaintext must never enter child arguments, inherited environment, logs, diagnostics, shell tracing, command history, or persistent storage. Temporary non-secret data stays in the existing private tmpfs staging area. Zero or release plaintext buffers as far as the implementation language permits.

--prompt-token forces hidden interactive input for either discovery command. A missing default, missing ciphertext, or failed decryption should fall back to the same hidden prompt with a clear explanation; a supplied fallback token authenticates only this metadata operation and does not alter an existing credential. Authentication failure must not silently try other named tokens. Discovery may use a bearer only for metadata endpoints and must never call chat completions, Responses, embeddings, audio, image, or other inference endpoints.

--dry-run may perform the authenticated read and show the normalized plan, but must not mutate either repository or ciphertext. Its help and output must state that it performs network access and may decrypt or prompt for a bearer despite being non-mutating.

Documentation, plan, and validation

Update command help and docs/credentials.md so a cold operator can perform initial discovery, understand all-dev-VM targeting and its blast radius, refresh safely, interpret warnings, recover from empty/inconsistent discovery, and know where the command stops. Use only synthetic URLs, inventories, responses, identities, and tokens in public documentation and tests.

Before implementation, write and review an R3 public development plan covering the authenticated metadata boundary, on-demand Age decryption, transaction/recovery integration, interface contracts, agent gates, acceptance witnesses, and rollback. Link this issue from the plan and every implementation PR.

Exercise the production command through local fake HTTPS/HTTP-client fixtures, including:

  • rich extended metadata and a minimal standard model response;
  • all-dev-VM defaults plus explicit target override;
  • automatic adapter and capability translation;
  • incomplete metadata and warning-only health/reliability conditions;
  • empty, malformed, oversized, duplicate-ID, authentication-failure, and bounded-error responses;
  • add with a new credential, add with an existing shared credential, and proof that existing token state is unchanged;
  • refresh using the encrypted default token and both commands using --prompt-token;
  • proof that bearer plaintext is absent from argv, environment, logs, journals, generated files, and persistent temporary files;
  • new models, changed metadata, unchanged responses, and deterministic ordering;
  • removal with agreeing snapshots and confirmation, disagreeing snapshots, refusal, EOF, and interrupted execution;
  • transaction rollback and recover at the new mutation boundaries;
  • a sabotage fixture proving the remote client cannot reach an inference route.

Retain the existing black-box lifecycle suite as a compatibility witness. Tests must use fixture inputs with the production generator/client seam rather than test-only branches in production code.

Sequencing and scope

This work is independent of allod/nexus#28. Implement it against the current pi-provider; if allod/nexus#28 becomes active before the feature lands, coordinate the branches and preserve the discovery witnesses through the port. Do not make this feature wait for a speculative rewrite.

This issue covers the public Nexus command, documentation, synthetic tests, and any narrowly required public contract additions. It does not contain or configure a real provider, bearer token, deployment inventory, machine identity, private repository path, or ciphertext. It does not commit generated deployment artifacts, rebuild machines, verify real inference, select preferred models, monitor ongoing model health, or revoke provider-side credentials. Those are separate private integration and operator steps after the public implementation is reviewed and merged.

Let an operator add an OpenAI-compatible Pi provider from its URL and bearer token, then keep its advertised model catalog synchronized without hand-written model files or inference from Nexus. Primary goals: - **Discover complete providers** — query the authenticated `/models` endpoint, translate useful advertised metadata into Pi model entries, and import every reported model by default. - **Keep catalogs current** — add a refresh operation that reports metadata changes, adds new models, and safely removes models the provider no longer advertises. - **Make the common path short** — default a new credential to the provider ID and target every inventory-declared libvirt dev VM, while preserving explicit target and credential overrides. - **Reuse credentials safely** — authenticate discovery with the credential's encrypted deployment-default token when available, without exposing plaintext through arguments, environment variables, logs, or persistent files. - **Document the boundary** — make clear that Nexus performs authenticated metadata reads only; inference, commits, pushes, rebuilds, deployment, and provider-side token lifecycle remain outside this command. ### Current state `scripts/pi-provider` accepts models through `--model` or `--models-file`. The operator must separately call the provider, translate its response into Pi's model vocabulary, preserve a temporary file long enough for the command to consume it, and repeat that work whenever the provider adds or retires models. `docs/credentials.md` documents the manual interface and currently promises that the tool never verifies a remote request. The existing lifecycle already owns repository resolution, inventory classification, credential-to-provider lookup, transaction staging, rollback, recovery, derived-output validation, and Age ciphertext. This feature should extend those mechanisms rather than create a second writer or a provider-specific helper. ### Operator interface Add discovery commands with this intended shape; the development plan may adjust spelling if the final Go CLI has a more coherent equivalent: ```text pi-provider [--dry-run] discover-add <provider> --url <https-url> [--credential <credential>] [--target <dev-vm>[,<dev-vm>...]] [--api <adapter>] [--prompt-token] pi-provider [--dry-run] discover-refresh <provider> [--prompt-token] ``` `discover-add` defaults the credential ID to the provider ID, the adapter as described below, and the target set to all machines for which inventory reports `type == "dev"` and `runtime == "libvirt"`. Repeated/comma-separated `--target` remains the advanced least-privilege override, and the existing `retarget` command remains supported. Do not add a model-selection prompt: every valid reported model enters the catalog and Pi's model selector remains the place where an operator chooses one. The URL is the provider's HTTPS API base, normally ending in `/v1`; discovery performs `GET <base>/models`. A malformed URL, failed authentication, non-success response, invalid JSON, missing/non-array `data`, empty model set, duplicate/empty model IDs, or a response too large for a documented defensive limit must fail before repository mutation. Diagnostics may include HTTP status and a bounded provider error message, but never authorization material. Keep the existing manual `add` path for providers whose discovery endpoint is absent or insufficient. ### Metadata translation Treat `/models` as OpenAI-compatible but allow useful provider extensions. Translate only an explicit allowlist into Pi model fields; do not copy the response wholesale and do not close the existing pass-through schema used by manual `--models-file` input. At minimum: - `id` becomes the required Pi model `id`. - `context_window`, `context_length`, or `max_model_len` may supply `contextWindow`, with documented precedence when more than one appears. - `max_output_tokens` or `max_completion_tokens` may supply `maxTokens`, again with documented precedence. - Explicit image-input capability may add `image` beside `text` in Pi's `input` list; otherwise remain text-only. - Explicit reasoning-trace/thinking capability may set Pi's `reasoning` flag. Provider metadata wins; any name-based fallback must be conservative, documented, and reported as an inference rather than a provider fact. - If chat completions are explicitly supported, default to `openai-completions`. If only Responses is explicitly supported, use Pi's Responses adapter. A minimal standard response with no capability declaration remains usable with the documented conservative defaults. Omit unknown limits rather than inventing provider facts, allowing Pi's own defaults to apply, and warn that the metadata was incomplete. Validate translated positive integer limits and supported Pi field shapes before staging them. Operational metadata such as route health, reliability, evidence timestamps, and deprecation notices should inform output but should not be persisted into `models.json` unless it is part of Pi's model schema. Warn without blocking when a provider reports an unhealthy/unknown route, unverified tool use, deprecation, or a maximum output of 1,024 tokens or fewer. ### Refresh and removal safety `discover-refresh` resolves the provider's credential and compares the translated result with the currently configured model list. It reports three stable, sorted groups: added models, removed models, and retained models whose translated metadata changed. New models and metadata changes are staged automatically. Absence from `/models` is not enough by itself to remove configured state. When the first response would remove any model, fetch a second complete snapshot. Abort without mutation if either response is empty/invalid or the normalized snapshots disagree. If they agree, display the removal diff and require explicit confirmation before staging it. A declined or interrupted confirmation changes nothing. This is a safety check against inconsistent discovery responses, not an inference-health probe. Preserve model order deterministically so an unchanged provider response produces no file diff. Refresh changes provider metadata only; it never changes credential targets, named tokens, or the selected deployment default. ### Credential and network boundary When `discover-add` creates a credential, read the bearer once through the existing hidden-input path. Use it to authenticate discovery and, only after discovery and all preflight checks succeed, encrypt it as the `default` named token. When `discover-add` uses an existing credential, or when `discover-refresh` resolves one, authenticate with only that credential's non-null `defaultToken`. Decrypt its ciphertext on demand with the configured Nexus Age identity and stream the plaintext directly into the HTTP client's private configuration/input path. Discovery against an existing credential must never add, rotate, rewrite, or change the selection of a named token. Plaintext must never enter child arguments, inherited environment, logs, diagnostics, shell tracing, command history, or persistent storage. Temporary non-secret data stays in the existing private tmpfs staging area. Zero or release plaintext buffers as far as the implementation language permits. `--prompt-token` forces hidden interactive input for either discovery command. A missing default, missing ciphertext, or failed decryption should fall back to the same hidden prompt with a clear explanation; a supplied fallback token authenticates only this metadata operation and does not alter an existing credential. Authentication failure must not silently try other named tokens. Discovery may use a bearer only for metadata endpoints and must never call chat completions, Responses, embeddings, audio, image, or other inference endpoints. `--dry-run` may perform the authenticated read and show the normalized plan, but must not mutate either repository or ciphertext. Its help and output must state that it performs network access and may decrypt or prompt for a bearer despite being non-mutating. ### Documentation, plan, and validation Update command help and `docs/credentials.md` so a cold operator can perform initial discovery, understand all-dev-VM targeting and its blast radius, refresh safely, interpret warnings, recover from empty/inconsistent discovery, and know where the command stops. Use only synthetic URLs, inventories, responses, identities, and tokens in public documentation and tests. Before implementation, write and review an R3 public development plan covering the authenticated metadata boundary, on-demand Age decryption, transaction/recovery integration, interface contracts, agent gates, acceptance witnesses, and rollback. Link this issue from the plan and every implementation PR. Exercise the production command through local fake HTTPS/HTTP-client fixtures, including: - rich extended metadata and a minimal standard model response; - all-dev-VM defaults plus explicit target override; - automatic adapter and capability translation; - incomplete metadata and warning-only health/reliability conditions; - empty, malformed, oversized, duplicate-ID, authentication-failure, and bounded-error responses; - add with a new credential, add with an existing shared credential, and proof that existing token state is unchanged; - refresh using the encrypted default token and both commands using `--prompt-token`; - proof that bearer plaintext is absent from argv, environment, logs, journals, generated files, and persistent temporary files; - new models, changed metadata, unchanged responses, and deterministic ordering; - removal with agreeing snapshots and confirmation, disagreeing snapshots, refusal, EOF, and interrupted execution; - transaction rollback and `recover` at the new mutation boundaries; - a sabotage fixture proving the remote client cannot reach an inference route. Retain the existing black-box lifecycle suite as a compatibility witness. Tests must use fixture inputs with the production generator/client seam rather than test-only branches in production code. ### Sequencing and scope This work is independent of allod/nexus#28. Implement it against the current `pi-provider`; if allod/nexus#28 becomes active before the feature lands, coordinate the branches and preserve the discovery witnesses through the port. Do not make this feature wait for a speculative rewrite. This issue covers the public Nexus command, documentation, synthetic tests, and any narrowly required public contract additions. It does not contain or configure a real provider, bearer token, deployment inventory, machine identity, private repository path, or ciphertext. It does not commit generated deployment artifacts, rebuild machines, verify real inference, select preferred models, monitor ongoing model health, or revoke provider-side credentials. Those are separate private integration and operator steps after the public implementation is reviewed and merged.
vnprc closed this issue 2026-08-25 02:43:40 +01:00
Sign in to join this conversation.
No description provided.