Skip to content

feat(kap-server): add provider write endpoints and models.dev/registry import#2110

Open
liruifengv wants to merge 7 commits into
mainfrom
feat/kap-provider-write-endpoints
Open

feat(kap-server): add provider write endpoints and models.dev/registry import#2110
liruifengv wants to merge 7 commits into
mainfrom
feat/kap-provider-write-endpoints

Conversation

@liruifengv

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue — this is the server half of the desktop app's custom-provider feature (client PR follows in the app repo).

Problem

The kap-server API surface could read providers and models but had no way to write them: no create/edit/delete endpoints, no access to the models.dev directory, and no custom-registry import — so GUI clients (the desktop app) could not offer provider management without hand-editing config.toml.

What changed

Write endpoints (packages/kap-server/src/routes/modelCatalog.ts):

  • POST /providers — manual create (wire type + credentials + model list), 40921 on id conflict.
  • PUT /providers/{id} — replace-style edit: new_id rename (providers key, aliases, default_provider and a pointing default_model all migrate), tri-state api_key (absent = keep, "" = clear, otherwise replace), model aliases rebuilt with form-unknown fields (betaApi, reasoningKey, overrides, …) merged through.
  • DELETE /providers/{id} — removes the provider and its aliases (bare 204); OAuth-managed providers are rejected with 40003 everywhere.
  • GET /providers/{id} — additionally reveals the stored api_key for edit prefill (loopback + bearer only; the list route stays redacted).
  • GET /catalog/providers[{id}] — pruned models.dev directory (catalogUpstream.ts): 10-minute in-memory cache with stale fallback, built-in snapshot via tsdown define (same mechanism as the CLI), shared in-flight fetch, and server-resolved import eligibility (wire_type / needs_base_url / rejected).
  • POST /providers:import_catalog / :import_registry — collection actions next to :refresh (find-my-way cannot register a static sibling of the in-segment :action parameter). Imports reuse the core's own normalization (resolveCatalogImport, catalogProviderModels, fetchCustomRegistry, remove-then-apply), write the registry source blob so scheduled refreshes rediscover it, and never move the global default_provider/default_model pointers.

Write-path disk consistency (the subtle part — see also the companion fix):

  • The providers/models TOML transforms rebuild entries but overlay entry fields onto the old on-disk raw, so field-level clears must assign explicit undefined; import refreshes swap aliases in two passes (drop, then re-add) so stale on-disk fields cannot survive.
  • Re-importing with an omitted api_key keeps the stored credential (registry imports inherit the previous source key for the same URL).
  • Multi-step write sequences (inspect → build → replace × N) serialize through a process-local chain.

Companion fix (first commit): modelsToToml merged { ...oldRaw, ...converted }, which resurrected any key the new record carried with an explicit undefined — field deletes through config.replace never reached the disk and came back on restart.

Tests: 45 new kap-server endpoint tests (incl. on-disk persistence assertions), one direct modelsToToml unit test, apiSurface snapshot updated. pnpm vitest run green for kap-server (766) and agent-core-v2 (3972).

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran `gen-changesets` skill, or this PR needs no changeset.
  • Ran `gen-docs` skill, or this PR needs no doc update.

@changeset-bot

changeset-bot Bot commented Jul 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f292a5b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

…oToml

The models TOML transform merged each entry as { ...oldRaw, ...converted },
so a field the new record carried with an explicit undefined — deleted from
'converted' by setDefined — was put right back from the old on-disk raw.
Field-level clears issued through config.replace (e.g. the provider write
routes dropping base_url, display_name, capabilities) only took effect in
memory and resurrected on the next boot. Merge via setDefined onto the old
raw directly, matching providerEntryToToml.
@liruifengv
liruifengv force-pushed the feat/kap-provider-write-endpoints branch from a16bcce to fe7a4ef Compare July 23, 2026 12:56
@pkg-pr-new

pkg-pr-new Bot commented Jul 23, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@b26a1fa
npx https://pkg.pr.new/@moonshot-ai/kimi-code@b26a1fa

commit: b26a1fa

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a16bccea96

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/kap-server/src/catalogUpstream.ts Outdated
Comment thread packages/kap-server/src/routes/modelCatalog.ts
…y import

Add the provider management write surface plus server-proxied catalogs so
API clients (the desktop app first) can manage providers without editing
config.toml by hand:

- POST/PUT/DELETE /providers: manual create, replace-style edit (new_id
  rename with pointer migration, tri-state api_key, form-unknown fields
  merged through), and delete with real alias removal.
- GET /providers/{id} additionally reveals the stored api_key for edit
  prefill on the loopback+bearer transport; the list stays redacted.
- GET /catalog/providers[{id}]: pruned models.dev directory with import
  eligibility resolved server-side (10-min cache, stale fallback, built-in
  snapshot, shared in-flight fetch).
- POST /providers:import_catalog and :import_registry (collection actions
  next to :refresh): catalog/registry imports with TUI-aligned
  remove-then-apply refresh semantics, OAuth-managed guards, and the
  registry source blob that scheduled refreshes rediscover.

Write-path consistency: field-level clears assign explicit undefined (the
TOML transforms only drop keys that way), import refreshes swap aliases in
two passes so stale on-disk fields cannot ride along, re-imports keep the
stored credential when api_key is omitted, and multi-step writes serialize
through a process-local chain. Global default_provider/default_model are
never modified by provider writes.
@liruifengv
liruifengv force-pushed the feat/kap-provider-write-endpoints branch from fe7a4ef to 2f4c9c9 Compare July 23, 2026 13:05
…key collisions on replace

- Cache the built-in snapshot on upstream failure, so an offline install no
  longer pays the full upstream timeout on every catalog call.
- Reject a PUT whose rebuilt alias keys collide with an alias owned by
  another provider (foreign-prefix keys are global), checked before any
  write so a collision never lands half the edit.
@liruifengv

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b3147a79b8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/agent-core-v2/src/app/kosongConfig/configSection.ts Outdated
liruifengv and others added 4 commits July 23, 2026 21:29
…one is configured

A fresh setup has no global default model, so the first provider added
through the write endpoints left GET /auth permanently unready and new
sessions without a selected model. Seed default_model from the created
provider's default (or first) model on POST /providers, :import_catalog
and :import_registry when — and only when — no default is configured;
an existing pointer is never moved, not even a dangling one.
…gine

kap-server's provider write endpoints imported @moonshot-ai/kosong and
@moonshot-ai/kimi-code-oauth directly for the models.dev browse/import
and custom-registry import. Move that capability behind a new App-scope
IModelsDevImportService so the server only talks to agent-core-v2:

- app/kosongConfig/modelsDev.ts mirrors the third-party api.json schema
  and hosts the pure translation (wire inference, endpoint resolution,
  model pruning, thinking/gateway overrides) ported from kosong's
  catalog.ts; kosong's own type surface stays limited to built-in
  vocabulary
- modelsDevUpstream.ts owns the directory fetch, 10-minute cache, and
  built-in snapshot fallback; modelsDevImportService.ts owns the import
  orchestration (tri-state api_key, two-pass alias swap, OAuth-managed
  rejection, serialized writes) with coded modelsDev.* errors
- kap-server routes shrink to wire mapping (zod schemas + numeric error
  code mapping) and drop the kosong/oauth dependencies

The /api/v1 surface is byte-identical: the apiSurface snapshot and all
existing modelCatalog tests pass unchanged.
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.

2 participants