Skip to content

feat(kenari): add Kenari as a first-class provider#793

Open
doedja wants to merge 3 commits into
Zoo-Code-Org:mainfrom
doedja:feat/kenari-provider
Open

feat(kenari): add Kenari as a first-class provider#793
doedja wants to merge 3 commits into
Zoo-Code-Org:mainfrom
doedja:feat/kenari-provider

Conversation

@doedja

@doedja doedja commented Jul 3, 2026

Copy link
Copy Markdown

Closes #792

What

Adds Kenari as a first-class dynamic provider. Kenari is an Indonesian OpenAI-compatible AI gateway billed in Rupiah (IDR): one kn- API key covers Claude, GPT, DeepSeek, GLM, Kimi and more. It already works through the generic OpenAI Compatible provider; this gives it a named entry with the live model list auto-populated.

How

Mirrors the Opencode Go provider (#319) file-by-file, adapted to current main:

  • src/api/providers/kenari.ts: handler on RouterProvider (streaming text, reasoning_content, tool-call partials, usage with cache-read tokens).
  • src/api/providers/fetchers/kenari.ts: dynamic model list from the public https://kenari.id/v1/models (no key needed; the key is forwarded when present). Maps context_length and modalities.input (image support). Pricing is intentionally not mapped: Kenari returns IDR (micro_idr_per_1m_tokens), and the ModelInfo price fields are USD, so a converted or raw value would be wrong; cost stays undefined instead.
  • packages/types: kenari.ts (default glm-5-2, 1,048,576 context fallback matching the live default model), settings schema, discriminated union, modelIdKeys, MODELS_BY_PROVIDER, SECRET_STATE_KEYS.
  • Registration: buildApiHandler, modelCache, webviewMessageHandler (fetched unconditionally like the other public-endpoint routers), dynamicProviderExtras.
  • Webview: Kenari.tsx settings component (key field + get-key CTA + ModelPicker), ApiOptions, PROVIDERS constant, providerModelConfig, useSelectedModel, validate.ts, and the kenariApiKey/getKenariApiKey strings across all 18 locales.
  • Tests: handler spec (streaming, request body, completePrompt, error wrapping), fetcher spec (mapping, auth header, error/invalid-entry fallbacks), settings component spec, plus kenari cases in the ClineProvider / webviewMessageHandler / validate suites.

Deliberately skipped from #319: the Anthropic-wire split machinery. Kenari serves every model over OpenAI /chat/completions, so the default "openai" protocol is correct with no extra code.

Testing

  • packages/types suite, src shared+providers sweep (1603 passed / 1 skipped), webview settings+hooks+utils sweep (592 passed), kenari-focused suites 216/216.
  • check-types clean in all three packages; eslint --max-warnings=0 and prettier clean on every touched file.
  • Live verification: https://kenari.id/v1/models public (24 models with context_length and modalities); chat completions verified with a real kn- key against https://kenari.id/v1. Docs: https://kenari.id/docs, OpenAPI: https://kenari.id/openapi.json.

Disclosure: I run kenari.id. Happy to provide a test key for verification.

Summary by CodeRabbit

  • New Features
    • Added Kenari as a supported provider end-to-end, including UI settings, model discovery/selection, routing, and chat handling.
    • Introduced Kenari API key configuration and added the new secret storage key (kenariApiKey).
    • Enabled Kenari streaming/non-streaming responses with text, reasoning, tool-call partials, and usage.
    • Added Kenari settings localization across multiple languages.
  • Bug Fixes
    • Improved Kenari configuration validation and model-id fallback behavior.
  • Tests
    • Expanded Kenari test coverage for model fetching/caching, message streaming formatting, and UI interactions.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d9ae7f58-a85c-4999-adda-7c613da6d9d4

📥 Commits

Reviewing files that changed from the base of the PR and between 959b674 and 69e8925.

📒 Files selected for processing (3)
  • src/api/providers/__tests__/kenari.spec.ts
  • src/api/providers/fetchers/__tests__/kenari.spec.ts
  • src/api/providers/kenari.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/api/providers/fetchers/tests/kenari.spec.ts
  • src/api/providers/kenari.ts
  • src/api/providers/tests/kenari.spec.ts

📝 Walkthrough

Walkthrough

This PR adds Kenari as a first-class provider across shared types, backend model fetching and chat handling, webview router-model aggregation, settings UI wiring, validation, and localized settings strings.

Changes

Kenari Provider Integration

Layer / File(s) Summary
Shared types and defaults
packages/types/src/global-settings.ts, packages/types/src/provider-settings.ts, packages/types/src/providers/index.ts, packages/types/src/providers/kenari.ts, src/shared/api.ts
Adds kenari to shared provider metadata, schema validation, secret keys, model-id mappings, default provider lookup, and shared API extras.
Backend fetcher and handler
src/api/index.ts, src/api/providers/kenari.ts, src/api/providers/index.ts, src/api/providers/fetchers/kenari.ts, src/api/providers/fetchers/modelCache.ts, src/api/__tests__/index.spec.ts, src/api/providers/__tests__/kenari.spec.ts, src/api/providers/fetchers/__tests__/kenari.spec.ts, src/api/providers/fetchers/__tests__/modelCache.spec.ts
Implements Kenari model fetching and chat handling, wires both into backend provider dispatch, and adds tests for fetch, stream, and completion behavior.
Webview router-model aggregation
src/core/webview/webviewMessageHandler.ts, src/core/webview/__tests__/ClineProvider.spec.ts, src/core/webview/__tests__/webviewMessageHandler.spec.ts
Adds Kenari as a public model-fetch candidate, initializes its router-model bucket, flushes cached models on new API keys, and updates test expectations.
Settings UI, validation, and localization
webview-ui/src/components/settings/ApiOptions.tsx, webview-ui/src/components/settings/constants.ts, webview-ui/src/components/settings/providers/Kenari.tsx, webview-ui/src/components/settings/providers/index.ts, webview-ui/src/components/settings/ModelPicker.tsx, webview-ui/src/components/settings/utils/providerModelConfig.ts, webview-ui/src/components/ui/hooks/useSelectedModel.ts, webview-ui/src/components/settings/__tests__/ApiOptions.spec.tsx, webview-ui/src/components/settings/providers/__tests__/Kenari.spec.tsx, webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts, webview-ui/src/utils/validate.ts, webview-ui/src/utils/__tests__/validate.spec.ts, webview-ui/src/i18n/locales/*/settings.json
Adds the Kenari settings component, registers it in the provider UI, extends model selection and validation wiring, and adds localized settings strings and tests.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • Zoo-Code-Org/Zoo-Code#319: Extends the same dynamic-provider plumbing with a new provider branch across shared types, model fetching, and webview wiring.
  • Zoo-Code-Org/Zoo-Code#344: Adds another provider to the same shared type, default-model, and cache-dispatch surfaces used here.
  • Zoo-Code-Org/Zoo-Code#437: Modifies requestRouterModels in the same way to treat a router provider’s /models endpoint as public and fetch it unconditionally.

Suggested labels: awaiting-review

Suggested reviewers: navedmerchant, JamesRobert20, hannesrudolph, edelauna

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding Kenari as a first-class provider.
Description check ✅ Passed The PR description includes the issue link, what/how, and testing details, matching the template well.
Linked Issues check ✅ Passed The changes satisfy #792 by adding Kenari handler, public model fetcher, settings UI, validation, and tests with the required base URL and auth flow.
Out of Scope Changes check ✅ Passed The added files and locale/test updates all support the Kenari provider feature and do not introduce obvious unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/api/providers/fetchers/kenari.ts (1)

7-7: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Base URL string duplicated with the API handler.

KENARI_BASE_URL here and the hardcoded "https://kenari.id/v1" literal in src/api/providers/kenari.ts (constructor baseURL) are two independent sources of truth. If Kenari's endpoint changes, only one may get updated.

♻️ Proposed fix: export and reuse the constant
-const KENARI_BASE_URL = "https://kenari.id/v1"
+export const KENARI_BASE_URL = "https://kenari.id/v1"

Then in src/api/providers/kenari.ts:

-			baseURL: "https://kenari.id/v1",
+			baseURL: KENARI_BASE_URL,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/providers/fetchers/kenari.ts` at line 7, The Kenari base URL is
defined in two places, creating duplicate sources of truth between
KENARI_BASE_URL in the fetcher module and the hardcoded baseURL in the Kenari
provider constructor. Export and reuse the shared KENARI_BASE_URL constant from
the fetcher module inside the Kenari provider so both paths always use the same
endpoint, and keep the provider constructor aligned with that shared symbol.
src/core/webview/webviewMessageHandler.ts (1)

1156-1172: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated "public provider candidate" wiring (same as opencode-go block above).

Lines 1156-1172 mirror lines 1139-1154 almost verbatim (comment text included). Consider extracting a small helper (e.g. pushPublicProviderCandidate(candidates, key, resolvedKey, flushCondition)) since this pattern will likely repeat for future public dynamic providers.

♻️ Sketch of a shared helper
+const pushPublicKeyedCandidate = async (
+	candidates: { key: RouterName; options: GetModelsOptions }[],
+	routerKey: RouterName,
+	apiKey: string | undefined,
+	explicitKeyProvided: boolean,
+) => {
+	if (explicitKeyProvided) {
+		await flushModels({ provider: routerKey, apiKey } as GetModelsOptions, true)
+	}
+	candidates.push({ key: routerKey, options: { provider: routerKey, apiKey } as GetModelsOptions })
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/webview/webviewMessageHandler.ts` around lines 1156 - 1172, The
Kenari candidate setup is duplicating the same “public provider candidate”
wiring already used in the opencode-go block, so extract the shared logic into a
small helper (for example around the candidate push/flush flow in
`webviewMessageHandler.ts`). Move the repeated key resolution, optional
`flushModels` call, and `candidates.push` behavior into that helper, then reuse
it for both the `kenari` and `opencode-go` paths so future public dynamic
providers can follow the same pattern without copy-paste.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/api/providers/fetchers/kenari.ts`:
- Line 7: The Kenari base URL is defined in two places, creating duplicate
sources of truth between KENARI_BASE_URL in the fetcher module and the hardcoded
baseURL in the Kenari provider constructor. Export and reuse the shared
KENARI_BASE_URL constant from the fetcher module inside the Kenari provider so
both paths always use the same endpoint, and keep the provider constructor
aligned with that shared symbol.

In `@src/core/webview/webviewMessageHandler.ts`:
- Around line 1156-1172: The Kenari candidate setup is duplicating the same
“public provider candidate” wiring already used in the opencode-go block, so
extract the shared logic into a small helper (for example around the candidate
push/flush flow in `webviewMessageHandler.ts`). Move the repeated key
resolution, optional `flushModels` call, and `candidates.push` behavior into
that helper, then reuse it for both the `kenari` and `opencode-go` paths so
future public dynamic providers can follow the same pattern without copy-paste.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3570c048-727a-4a48-a722-5c0b66d0506e

📥 Commits

Reviewing files that changed from the base of the PR and between 1c728e7 and eb9af0e.

📒 Files selected for processing (43)
  • packages/types/src/global-settings.ts
  • packages/types/src/provider-settings.ts
  • packages/types/src/providers/index.ts
  • packages/types/src/providers/kenari.ts
  • src/api/index.ts
  • src/api/providers/__tests__/kenari.spec.ts
  • src/api/providers/fetchers/__tests__/kenari.spec.ts
  • src/api/providers/fetchers/kenari.ts
  • src/api/providers/fetchers/modelCache.ts
  • src/api/providers/index.ts
  • src/api/providers/kenari.ts
  • src/core/webview/__tests__/ClineProvider.spec.ts
  • src/core/webview/__tests__/webviewMessageHandler.spec.ts
  • src/core/webview/webviewMessageHandler.ts
  • src/shared/api.ts
  • webview-ui/src/components/settings/ApiOptions.tsx
  • webview-ui/src/components/settings/ModelPicker.tsx
  • webview-ui/src/components/settings/constants.ts
  • webview-ui/src/components/settings/providers/Kenari.tsx
  • webview-ui/src/components/settings/providers/__tests__/Kenari.spec.tsx
  • webview-ui/src/components/settings/providers/index.ts
  • webview-ui/src/components/settings/utils/providerModelConfig.ts
  • webview-ui/src/components/ui/hooks/useSelectedModel.ts
  • webview-ui/src/i18n/locales/ca/settings.json
  • webview-ui/src/i18n/locales/de/settings.json
  • webview-ui/src/i18n/locales/en/settings.json
  • webview-ui/src/i18n/locales/es/settings.json
  • webview-ui/src/i18n/locales/fr/settings.json
  • webview-ui/src/i18n/locales/hi/settings.json
  • webview-ui/src/i18n/locales/id/settings.json
  • webview-ui/src/i18n/locales/it/settings.json
  • webview-ui/src/i18n/locales/ja/settings.json
  • webview-ui/src/i18n/locales/ko/settings.json
  • webview-ui/src/i18n/locales/nl/settings.json
  • webview-ui/src/i18n/locales/pl/settings.json
  • webview-ui/src/i18n/locales/pt-BR/settings.json
  • webview-ui/src/i18n/locales/ru/settings.json
  • webview-ui/src/i18n/locales/tr/settings.json
  • webview-ui/src/i18n/locales/vi/settings.json
  • webview-ui/src/i18n/locales/zh-CN/settings.json
  • webview-ui/src/i18n/locales/zh-TW/settings.json
  • webview-ui/src/utils/__tests__/validate.spec.ts
  • webview-ui/src/utils/validate.ts

@doedja doedja force-pushed the feat/kenari-provider branch from eb9af0e to 90a25b9 Compare July 3, 2026 09:11
@doedja

doedja commented Jul 3, 2026

Copy link
Copy Markdown
Author

The platform-unit-test failure was in SettingsView.change-detection.spec.tsx ("resets cached provider state when a new import timestamp arrives", expected baseten / got deepseek), a file this PR does not touch; it passes locally on this branch (4/4) and the same test failed once on main's own CI when it landed in #726. Pushed a same-content commit to retrigger CI since I lack rerun permissions.

@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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.

Provider request: Kenari (kenari.id), Indonesian OpenAI-compatible gateway

1 participant