Skip to content

docs(api): clarify max_output_length and is_ready semantics - #935

Open
lloydmak99 wants to merge 2 commits into
mainfrom
skale-feedback-2026-08-18
Open

docs(api): clarify max_output_length and is_ready semantics#935
lloydmak99 wants to merge 2 commits into
mainfrom
skale-feedback-2026-08-18

Conversation

@lloydmak99

Copy link
Copy Markdown
Contributor

Doc-comment-only change to the OpenAPI descriptions for max_output_length, top_provider.max_completion_tokens, and is_ready. No behaviour change — the diff contains nothing but /// lines.

The SKALE team reported both fields as misleading during their OpenClaw integration. Both reports were valid.

max_output_length

The name promises an enforced cap; nothing enforces it. crates/api/src/routes/completions.rs forwards request.max_tokens to the provider unchanged and never clamps it against this column.

Live on 2026-08-18, the gateway published max_output_length: 8192 for deepseek-ai/DeepSeek-V4-Flash while the model's own endpoint reported max_model_len: 1048576 and advertised no output cap. A sweep of all 20 direct completions endpoints found that none advertises an output cap — every one reports only max_model_len, which bounds prompt and completion together. A separate audit of cvm-compose-files confirmed no serving configuration imposes a fixed per-response output cap on any model.

Worth noting the contrast with the neighbouring field: apply_backend_model_metadata overwrites a stored value whenever a backend advertises a positive one. Backends advertise max_model_len, which feeds advertised_context_length(), but nothing that feeds advertised_max_output_length(). Of the 15 NEAR-hosted models, 13 have a backend reporting max_model_len and all 13 published context_length values match it exactly. context_length self-heals from the deployment; max_output_length has nothing to heal it from.

The comments now state that the field is advisory, that max_tokens is forwarded unchanged, and that the binding limit is the context window.

is_ready

The comments already described the OpenRouter mechanics but not the consequence for clients. Live data shows every proxied third-party model (openai/*, anthropic/*, google/*, qwen/qwen3.7-max) reporting false while being fully serviceable, and several models omitting the field entirely — so a client treating it as a boolean availability gate gets three different wrong answers.

The comments now state explicitly that it does not indicate NEAR AI Cloud availability and that presence in /v1/models is the availability signal.

Verification

cargo fmt --check and cargo check --all-targets both pass. Tri-state PATCH semantics text is unchanged.

Not addressed here

Two follow-ups surfaced during the audit and are deliberately left out of this doc-only PR:

  • The stored values themselves need correcting — four rows are provably wrong (openai/privacy-filter and openai/whisper-large-v3 publish an output limit larger than their context; Qwen/Qwen3-Embedding-0.6B and black-forest-labs/FLUX.2-klein-4B carry token limits on non-generative models). 8192 and 16384 cover 31 of 48 rows.
  • max_output_length cannot be cleared through PATCH /v1/admin/models: the update uses max_output_length = COALESCE($21, max_output_length), whereas is_ready, deprecation_date, and openrouter_slug three lines below use CASE WHEN $clear THEN NULL ELSE COALESCE(...) END, and the request field is a plain Option<i32> rather than Nullable<T>. Correcting the data needs either direct SQL or a small tri-state change.

🤖 Generated with Claude Code

The SKALE team reported both fields as misleading during their OpenClaw
integration, and both reports were valid.

max_output_length (and its top_provider.max_completion_tokens mirror) is
admin-curated catalog metadata published for OpenRouter-schema
compatibility. Nothing enforces it: the request path forwards max_tokens
to the provider unchanged. The binding limit is the model's context
window, shared between prompt and completion.

is_ready is OpenRouter's catalog publication flag, stored and exposed
verbatim. It says nothing about NEAR AI Cloud availability — every
proxied third-party model reports false while being fully serviceable,
and the field is omitted entirely when unset.

Doc comments only; no behaviour change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lloydmak99
lloydmak99 deployed to Cloud API test env August 18, 2026 22:03 — with GitHub Actions Active
@github-actions

Copy link
Copy Markdown

OpenCodeReview: Review complete: 0 finding(s) across 1 selected item(s).

@claude

claude Bot commented Aug 18, 2026

Copy link
Copy Markdown

Review — docs-only change to model metadata semantics

Verified the claims in the diff against the code. The core assertions hold:

  • No clamping: crates/api/src/routes/completions.rs:634 and :1265 forward request.max_tokens straight into the provider request; nothing in crates/services/src/completions/ compares it against max_output_length / max_completion_tokens. The "forwards unchanged and does not clamp" wording is accurate.
  • is_ready is not an availability gate: it is a plain pass-through column (crates/database/src/repositories/model.rs:459) and is never consulted on any serving path. Listing on /v1/models is gated by m.is_active = true (crates/database/src/repositories/model.rs:90), not by is_ready.
  • All five OpenAPI-exposed structs carrying these fields (ModelInfo, TopProvider, ModelMetadata, UpdateModelApiRequest, ModelHistoryEntry) were updated — no partially-updated schema left behind.

No critical issues. Notes below; none block merge, but (1) is a factual error in the new text.

1. "curated per model by NEAR AI Cloud administrators" is not the whole story — a backend can override it

The published value is not purely admin-curated. On every model-list cache refresh, a positive backend-advertised value overwrites the stored one — crates/services/src/models/mod.rs:43:

if let Some(max_output_length) = metadata.max_output_length.filter(|value| *value > 0) {
    model.max_output_length = Some(max_output_length);
}

That feeds from advertised_max_output_length() (crates/inference_providers/src/models.rs:815), which reads both max_output_length and top_provider.max_completion_tokens, and is merged in crates/inference_providers/src/attested/nearai/mod.rs:245-256. The path is live and covered by tests (get_models_with_pricing_uses_backend_output_when_db_output_missing, crates/services/src/models/mod.rs:416).

The PR description says "nothing that feeds advertised_max_output_length()" — that is true of the current fleet, not of the code. A doc comment describing provenance should describe the mechanism, since the moment a backend starts advertising an output cap the comment becomes wrong with no code change. Suggest: "curated per model by NEAR AI Cloud administrators, and overridden by a positive backend-advertised value when one is reported."

2. UpdateModelApiRequest.max_output_length — PATCH semantics still undocumented, and the field cannot be cleared

The old text ("Maximum number of output tokens…") was replaced with read-path prose, so this request field now documents what the value means on read but nothing about what a PATCH does. That matters here because it differs from its neighbours: is_ready three fields down carries an explicit tri-state block, while max_output_length uses plain COALESCE:

-- crates/database/src/repositories/model.rs:454
max_output_length = COALESCE($21, max_output_length),
-- vs. crates/database/src/repositories/model.rs:458
is_ready = CASE WHEN $27 THEN NULL ELSE COALESCE($25, is_ready) END,

Combined with the plain Option<i32> type, null and omitted are indistinguishable — an admin sending "maxOutputLength": null to clear a wrong value gets a silent no-op. The PR body already identifies this as a follow-up; since this PR is editing exactly this doc comment, one line here would close the gap in the OpenAPI spec now: "PATCH: omitted or null leaves the value unchanged; this field cannot currently be cleared."

3. Stale copy of the removed sentence in the service layer

crates/services/src/models/ports.rs:68 still reads /// Maximum number of output tokens the model can produce in a single response. — the exact claim this PR is removing from the API layer. Internal-only (not in the OpenAPI output), so cosmetic, but it is the doc the next person reading the service sees.

Minor

"presence in /v1/models is the availability signal" is a strong absolute. Listing reflects the is_active catalog flag, not live provider health — a model can be listed while its backend is unreachable. "Listing in /v1/models is the availability signal, not is_ready" would say the useful thing without over-promising.

Note: I did not run cargo fmt --check / cargo check locally (sandbox restrictions); CI covers both, and rustfmt does not reflow doc comments by default so the new lines are inert to it.

✅ Approved — comment-only, no behaviour change, claims verified. Items 1 and 2 are worth a quick follow-up commit.

@ironloopai

ironloopai Bot commented Aug 18, 2026

Copy link
Copy Markdown

🧭 IronLoop Run · Review

This comment updates in place as the Run moves through its stages.

🟩 Final result · Completed

🟨 Queued🟦 Working🟦 Posting results🟩 Completed

Automatic trigger · attempt 1 of 3 · completed in 32s

IronLoop completed the review and posted it to GitHub.

🔗 Result

Open submitted review →

Run details

Run: ddf11799-c46e-44a6-b979-3cc60942d7ae
Base: main at 95b3c8c
Head: skale-feedback-2026-08-18 at 9d13e98
Created: 2026-08-18 22:08 UTC
Updated: 2026-08-18 22:08 UTC

@ironloopai ironloopai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 IronLoop review

🟢 No actionable findings

No additional findings. The substantive documentation concerns applicable to this change were already raised in the captured review discussion.

Validation

  • Changed-area inspection — Inspected the complete documentation-only change and traced the described fields through model listing, backend metadata merging, request forwarding, and admin update paths.
  • CI checks — Captured unit, integration, end-to-end, lint, release-build, audit, and advisory checks passed.
Review details
  • Run: ddf11799-c46e-44a6-b979-3cc60942d7ae
  • Workflow: Review
  • Attempts: 1

Addresses four points from the PR #935 review.

The provenance claim was wrong. The comments said the value is "curated
per model by NEAR AI Cloud administrators", which describes the current
fleet rather than the mechanism: apply_backend_model_metadata overwrites
the stored value whenever a backend advertises a positive one, on every
model-list cache refresh. No NEAR backend advertises an output value
today, which is why stored values survive — but the comment would have
gone silently wrong the moment one did. Now states the override.

UpdateModelApiRequest.max_output_length documented what the value means
on read but nothing about what a PATCH does, and it differs from its
neighbours: plain COALESCE against is_ready's tri-state CASE WHEN, with
a plain Option<i32> rather than Nullable<T>. An admin sending null to
clear a wrong value gets a silent no-op. Now says so.

services/models/ports.rs still carried the exact sentence this branch
removed from the API layer.

"Presence in /v1/models is the availability signal" over-promised:
listing is gated by is_active, not live provider health.

Doc comments only; no behaviour change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lloydmak99

Copy link
Copy Markdown
Contributor Author

Addressed all four review points in bc422c3 — doc comments only, no behaviour change.

1. Provenance was wrong — good catch. The comment said "curated per model by NEAR AI Cloud administrators", which describes the current fleet rather than the mechanism. apply_backend_model_metadata (crates/services/src/models/mod.rs:43) overwrites the stored value whenever a backend advertises a positive one, on every model-list cache refresh. No NEAR backend advertises an output value today — which is exactly why the wrong wording looked right — but it would have gone silently stale the moment one did. All five structs now state the override.

2. PATCH semantics added. The request field now documents that an omitted value or null leaves it unchanged and that it cannot currently be cleared. Worth restating why that matters: an operator trying to fix one of the bad catalog values by sending "maxOutputLength": null gets a silent no-op, so correcting the data needs direct SQL or the tri-state change.

3. Stale duplicate removed. crates/services/src/models/ports.rs:68 now reads consistently with the API layer.

4. Absolute softened. "Presence in /v1/models is the availability signal" → listing rather than is_ready is the signal to use, with an explicit note that listing does not guarantee live provider health, since it is gated by is_active.

Verified locally: cargo fmt --check and cargo check --all-targets both pass, and git diff main contains only /// and // lines.

One clarification on the PR description, since it is the same distinction as point 1: "nothing that feeds advertised_max_output_length()" was a statement about the current fleet, confirmed by sweeping all 20 direct completions endpoints on 2026-08-18 — every one reports only max_model_len. The code path is live regardless, which is why the comments now describe it.

🤖 Generated with Claude Code

@lloydmak99
lloydmak99 deployed to Cloud API test env August 20, 2026 20:21 — with GitHub Actions Active
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant