Skip to content

[AISAGE-707] feat(ai-gateway): support public custom LLMs - #8294

Open
chrarnoldus wants to merge 2 commits into
mainfrom
kilo/public-custom-llms
Open

chrarnoldus wants to merge 2 commits into
mainfrom
kilo/public-custom-llms

Conversation

@chrarnoldus

@chrarnoldus chrarnoldus commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Kilo admins can now publish a custom LLM to every user, signed in or not. This builds on #7780, which made custom LLM routing fail closed, and #7778, which made custom LLMs free. It replaces #7291.

Definition schema

  • public: new field, public: { inference_providers: string[] }.
    • It cannot be combined with organization_ids or group_ids.
    • Each provider must be a lowercase slug, and at least one is required.
  • description: required when public is set, optional otherwise. Private custom LLMs fall back to the existing disclaimer.
  • organization_ids: optional, but still required unless public is set.

Routing and policy

  • Access: getProvider serves a public custom LLM to every caller, including anonymous ones.
  • Policy is not skipped: public custom LLMs do not set bypassAccessCheck, so organization and group policy apply as for any model. They are never exempt from model policy, because no organization or group grant applies to them.
  • Provider allow lists: allow lists are enforced against public.inference_providers, through the new getModelInferenceProviderRestriction.
  • Request-level check: a request whose provider.only / provider.ignore excludes every declared provider gets a 403 (checkInferenceProvidersAllowed).

Reusing Kilo-exclusive ids

  • A new custom LLM id may now reuse a Kilo-exclusive id ([AISAGE-707] refactor(ai-gateway): resolve custom LLMs by catalog lookup and fail closed #7780 rejected this); the custom LLM then replaces that model.
    • Snapshot and catalog: the exclusive model is hidden from both.
    • Exclusive rules skipped: rate limits, unavailable status, provider pinning, request rewriting and exclusive pricing do not apply to the custom LLM.
  • Catalog outage: lookupCustomLlm reports unknown for Kilo-exclusive ids a custom LLM could claim (no : or ~) while the custom LLM catalog cannot be read and nothing is cached. The request gets a 503 instead of falling back to the exclusive model.
  • Policy never inherits the exclusive model's providers: a public custom LLM is pinned to its declared providers. A private one, or an unknown lookup, is pinned to none, so policy denies it for anyone without a grant.

Catalog and snapshot

  • Gateway catalog: public custom LLMs appear with isFree, isCustomLlm and inference_provider_ids. OpenRouter and Kilo-exclusive entries with the same id are hidden.
  • Per-organization lists: listAvailableCustomLlms only lists private custom LLMs.
  • Snapshot: the provider sync lists each public custom LLM under every declared provider. A provider that OpenRouter does not know is created in the snapshot.
  • Pickers: isCustomLlm keeps public custom LLMs out of the same places as private ones: auto-routing pools, the router and default model pickers, and Organization Auto targets.

Admin UI

  • Confirmation dialog: appears when a save makes a definition public, or when a new definition (created or copied) replaces a Kilo-exclusive model. It recommends local testing first.
  • List badges: each custom LLM has a "Public" or "Private" badge, and a "Replaces Kilo-exclusive" badge where it shadows one.
    • list returns the Kilo-exclusive ids for this.
  • The help text explains the public fields.
  • The editor also checks the cross-field rules before saving.

Verification

  • I did not test the admin UI or the gateway by hand.
  • Typecheck passes for @kilocode/db and @kilocode/web-shared. apps/web typecheck runs out of memory in the sandbox, so CI is the first full check.
  • oxlint and oxfmt pass on changed files.
  • The affected Jest suites (about 7,300 tests) pass against a local Postgres.

Visual Changes

The admin Custom LLMs page has a new Access column with badges, updated help text, and a confirmation dialog for public or Kilo-exclusive-replacing saves. I took no screenshots.

Reviewer Notes

  • Snapshot timing: a public custom LLM only appears in the models-by-provider snapshot after the next provider sync. Allow-list enforcement reads inference_providers from the definition, so it does not wait for the sync.
  • provider field: when organization policy narrows providers, provider.only is forwarded to the custom upstream in the request body, as organization privacy settings already are. Use remove_from_body if an upstream rejects the field.

@chrarnoldus chrarnoldus self-assigned this Oct 20, 2026
@chrarnoldus chrarnoldus changed the title feat(ai-gateway): support public custom LLMs [AISAGE-707] feat(ai-gateway): support public custom LLMs Oct 20, 2026
Comment thread packages/web-shared/src/lib/model-allow.server.ts Outdated
Comment thread apps/web/src/app/admin/custom-llms/CustomLlmsContent.tsx Outdated
@kilo-code-bot

kilo-code-bot Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

Since the last review the PR branch was rebased onto newer main (dd3b24022 vs 6224eac74); the PR's own files are byte-identical, the three prior findings remain fixed in current code, and the rebased combination with new main ai-gateway changes introduces no new issue in the PR's lines.

Files Reviewed (25 files)
  • apps/web/src/app/admin/custom-llms/CustomLlmsContent.tsx
  • apps/web/src/lib/providers/openrouter/public-custom-llm-snapshot.test.ts
  • apps/web/src/lib/providers/openrouter/public-custom-llm-snapshot.ts
  • apps/web/src/lib/providers/openrouter/sync-providers.ts
  • apps/web/src/routers/admin/custom-llm-router.test.ts
  • apps/web/src/routers/admin/custom-llm-router.ts
  • apps/web/src/tests/openrouter-models.test.ts
  • packages/db/src/schema-types.ts
  • packages/web-shared/src/lib/ai-gateway/custom-llm/access.ts
  • packages/web-shared/src/lib/ai-gateway/custom-llm/custom-llm-catalog.test.ts
  • packages/web-shared/src/lib/ai-gateway/custom-llm/custom-llm-catalog.ts
  • packages/web-shared/src/lib/ai-gateway/custom-llm/listAvailableCustomLlms.ts
  • packages/web-shared/src/lib/ai-gateway/handlers/llm-proxy.test.ts
  • packages/web-shared/src/lib/ai-gateway/handlers/llm-proxy.ts
  • packages/web-shared/src/lib/ai-gateway/llm-proxy-helpers.ts
  • packages/web-shared/src/lib/ai-gateway/processUsage.ts
  • packages/web-shared/src/lib/ai-gateway/providers/apply-provider-specific-logic.ts
  • packages/web-shared/src/lib/ai-gateway/providers/get-provider.public-custom-llm.test.ts
  • packages/web-shared/src/lib/ai-gateway/providers/get-provider.ts
  • packages/web-shared/src/lib/ai-gateway/providers/openrouter/index.ts
  • packages/web-shared/src/lib/model-allow.server.test.ts
  • packages/web-shared/src/lib/model-allow.server.ts
  • packages/web-shared/src/lib/organizations/effective-model-access.server.test.ts
  • packages/web-shared/src/lib/organizations/group-policies/model-access/model-access.server.ts
  • packages/web-shared/src/tests/helpers/custom-llm.helper.ts
Previous Review Summaries (2 snapshots, latest commit 6224eac)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 6224eac)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The incremental commit 6224eac74 resolves all three prior findings — fail-closed lookups now skip ids a custom LLM cannot claim, policy pins private/unknown custom LLMs to no providers instead of inheriting the shadowed exclusive model's providers, and the admin UI warns and badges saves that replace a Kilo-exclusive model — with matching tests, and no new issues were found in the changed code.

Files Reviewed (7 files)
  • apps/web/src/app/admin/custom-llms/CustomLlmsContent.tsx
  • apps/web/src/routers/admin/custom-llm-router.ts
  • apps/web/src/routers/admin/custom-llm-router.test.ts
  • packages/web-shared/src/lib/ai-gateway/custom-llm/custom-llm-catalog.ts
  • packages/web-shared/src/lib/ai-gateway/custom-llm/custom-llm-catalog.test.ts
  • packages/web-shared/src/lib/model-allow.server.ts
  • packages/web-shared/src/lib/model-allow.server.test.ts

Previous review (commit aea7028)

Status: 3 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
packages/web-shared/src/lib/ai-gateway/custom-llm/custom-llm-catalog.ts 154 Fail-closed unknown also covers Kilo-exclusive ids containing :/~, which a custom LLM can never claim, so a catalog read failure returns 503 for built-in models that cannot be shadowed.
packages/web-shared/src/lib/model-allow.server.ts 67 A private custom LLM reusing a Kilo-exclusive id falls back to the exclusive model's provider set, bypassing requireModelInCurrentSnapshot and letting policy report the model as allowed while routing returns 404.

SUGGESTION

File Line Issue
apps/web/src/app/admin/custom-llms/CustomLlmsContent.tsx 330 The confirmation only fires on the public transition; saving a private definition that claims a Kilo-exclusive id silently hides that model for every user.
Files Reviewed (25 files)
  • apps/web/src/app/admin/custom-llms/CustomLlmsContent.tsx - 1 issue
  • apps/web/src/lib/providers/openrouter/public-custom-llm-snapshot.test.ts
  • apps/web/src/lib/providers/openrouter/public-custom-llm-snapshot.ts
  • apps/web/src/lib/providers/openrouter/sync-providers.ts
  • apps/web/src/routers/admin/custom-llm-router.test.ts
  • apps/web/src/routers/admin/custom-llm-router.ts
  • apps/web/src/tests/openrouter-models.test.ts
  • packages/db/src/schema-types.ts
  • packages/web-shared/src/lib/ai-gateway/custom-llm/access.ts
  • packages/web-shared/src/lib/ai-gateway/custom-llm/custom-llm-catalog.test.ts
  • packages/web-shared/src/lib/ai-gateway/custom-llm/custom-llm-catalog.ts - 1 issue
  • packages/web-shared/src/lib/ai-gateway/custom-llm/listAvailableCustomLlms.ts
  • packages/web-shared/src/lib/ai-gateway/handlers/llm-proxy.test.ts
  • packages/web-shared/src/lib/ai-gateway/handlers/llm-proxy.ts
  • packages/web-shared/src/lib/ai-gateway/llm-proxy-helpers.ts
  • packages/web-shared/src/lib/ai-gateway/processUsage.ts
  • packages/web-shared/src/lib/ai-gateway/providers/apply-provider-specific-logic.ts
  • packages/web-shared/src/lib/ai-gateway/providers/get-provider.public-custom-llm.test.ts
  • packages/web-shared/src/lib/ai-gateway/providers/get-provider.ts
  • packages/web-shared/src/lib/ai-gateway/providers/openrouter/index.ts
  • packages/web-shared/src/lib/model-allow.server.test.ts
  • packages/web-shared/src/lib/model-allow.server.ts - 1 issue
  • packages/web-shared/src/lib/organizations/effective-model-access.server.test.ts
  • packages/web-shared/src/lib/organizations/group-policies/model-access/model-access.server.ts
  • packages/web-shared/src/tests/helpers/custom-llm.helper.ts

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4.1-flash-1017 · Input: 34.8K · Output: 6.7K · Cached: 331.1K

Review guidance: REVIEW.md from base branch main

@chrarnoldus
chrarnoldus force-pushed the kilo/public-custom-llms branch from 6224eac to dd3b240 Compare October 20, 2026 16:32

This branch has not been deployed

No deployments
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