Skip to content

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

Open
doedja wants to merge 4 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 4 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!

@github-actions github-actions Bot added the awaiting-review PR changes are ready and waiting for maintainer re-review label Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting-review PR changes are ready and waiting for maintainer re-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant