From 7d26a51ffe21c4391f946578758e33ab0eb8764d Mon Sep 17 00:00:00 2001 From: Nur Ad-Duja Date: Sat, 4 Jul 2026 08:10:50 +0700 Subject: [PATCH 1/2] feat(provider): add Kenari as OpenAI-compatible JSON provider Kenari (https://kenari.id) is an OpenAI-compatible LLM gateway with IDR-denominated billing, serving models from DeepSeek, GLM, Kimi, Qwen, Claude, GPT and others at https://kenari.id/v1. Follows the JSON-based provider pattern (same shape as neosantara): - providers.json entry (KENARI_API_KEY / KENARI_API_BASE) - LlmProviders.KENARI enum value - provider_endpoints_support entries - registry/config/prefix-detection/URL tests Kenari accepts max_completion_tokens natively, so no param mapping is needed. Verified live: litellm.completion on kenari/deepseek-v4-flash, streaming and non-streaming. --- litellm/llms/openai_like/providers.json | 6 ++ .../provider_endpoints_support_backup.json | 17 +++++ litellm/types/utils.py | 1 + provider_endpoints_support.json | 17 +++++ tests/test_litellm/llms/kenari/test_kenari.py | 66 +++++++++++++++++++ 5 files changed, 107 insertions(+) create mode 100644 tests/test_litellm/llms/kenari/test_kenari.py diff --git a/litellm/llms/openai_like/providers.json b/litellm/llms/openai_like/providers.json index 164100d4194..7aa17b7d323 100644 --- a/litellm/llms/openai_like/providers.json +++ b/litellm/llms/openai_like/providers.json @@ -132,6 +132,12 @@ }, "supported_endpoints": ["/v1/chat/completions", "/v1/responses"] }, + "kenari": { + "base_url": "https://kenari.id/v1", + "api_key_env": "KENARI_API_KEY", + "api_base_env": "KENARI_API_BASE", + "supported_endpoints": ["/v1/chat/completions"] + }, "tensormesh": { "base_url": "https://serverless.tensormesh.ai/v1", "api_key_env": "TENSORMESH_INFERENCE_API_KEY", diff --git a/litellm/provider_endpoints_support_backup.json b/litellm/provider_endpoints_support_backup.json index dd7712aabca..683909da8a1 100644 --- a/litellm/provider_endpoints_support_backup.json +++ b/litellm/provider_endpoints_support_backup.json @@ -1252,6 +1252,23 @@ "rerank": false } }, + "kenari": { + "display_name": "Kenari (`kenari`)", + "url": "https://docs.litellm.ai/docs/providers/kenari", + "endpoints": { + "chat_completions": true, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": false + } + }, "lambda_ai": { "display_name": "Lambda AI (`lambda_ai`)", "url": "https://docs.litellm.ai/docs/providers/lambda_ai", diff --git a/litellm/types/utils.py b/litellm/types/utils.py index 18cf9461648..52ab3f2b447 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -3699,6 +3699,7 @@ class LlmProviders(str, Enum): POE = "poe" CHUTES = "chutes" NEOSANTARA = "neosantara" + KENARI = "kenari" PARASAIL = "parasail" XIAOMI_MIMO = "xiaomi_mimo" TENSORMESH = "tensormesh" diff --git a/provider_endpoints_support.json b/provider_endpoints_support.json index 65db63dc045..59b86071467 100644 --- a/provider_endpoints_support.json +++ b/provider_endpoints_support.json @@ -1367,6 +1367,23 @@ "rerank": false } }, + "kenari": { + "display_name": "Kenari (`kenari`)", + "url": "https://docs.litellm.ai/docs/providers/kenari", + "endpoints": { + "chat_completions": true, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": false + } + }, "lambda_ai": { "display_name": "Lambda AI (`lambda_ai`)", "url": "https://docs.litellm.ai/docs/providers/lambda_ai", diff --git a/tests/test_litellm/llms/kenari/test_kenari.py b/tests/test_litellm/llms/kenari/test_kenari.py new file mode 100644 index 00000000000..3bd9a585b9b --- /dev/null +++ b/tests/test_litellm/llms/kenari/test_kenari.py @@ -0,0 +1,66 @@ +import os +from unittest.mock import patch + +KENARI_API_BASE = "https://kenari.id/v1" + + +def test_kenari_json_registry(): + import litellm + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + assert litellm.LlmProviders.KENARI.value == "kenari" + assert litellm.LlmProviders("kenari") == litellm.LlmProviders.KENARI + assert JSONProviderRegistry.exists("kenari") + config = JSONProviderRegistry.get("kenari") + assert config is not None + assert config.base_url == KENARI_API_BASE + assert config.api_key_env == "KENARI_API_KEY" + assert config.api_base_env == "KENARI_API_BASE" + assert config.supported_endpoints == ["/v1/chat/completions"] + + +def test_kenari_dynamic_config_env_vars(): + from litellm.llms.openai_like.dynamic_config import create_config_class + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + config = create_config_class(JSONProviderRegistry.get("kenari"))() + + with patch.dict( + os.environ, + { + "KENARI_API_KEY": "test-key", + "KENARI_API_BASE": "https://custom.kenari.example/v1", + }, + ): + api_base, api_key = config._get_openai_compatible_provider_info(None, None) + + assert api_base == "https://custom.kenari.example/v1" + assert api_key == "test-key" + + +def test_kenari_provider_detection_by_prefix(): + from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + + model, provider, _, api_base = get_llm_provider("kenari/deepseek-v4-pro") + + assert model == "deepseek-v4-pro" + assert provider == "kenari" + assert api_base == KENARI_API_BASE + + +def test_kenari_chat_complete_url(): + from litellm.llms.openai_like.dynamic_config import create_config_class + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + config = create_config_class(JSONProviderRegistry.get("kenari"))() + + assert ( + config.get_complete_url( + api_base=None, + api_key=None, + model="deepseek-v4-pro", + optional_params={}, + litellm_params={}, + ) + == "https://kenari.id/v1/chat/completions" + ) From 94259f9c5ac52b827a960ddc2356176728b737d3 Mon Sep 17 00:00:00 2001 From: Nur Ad-Duja Date: Sat, 4 Jul 2026 08:57:00 +0700 Subject: [PATCH 2/2] chore: trigger CI re-run