Skip to content

feat(neosantara): add Neosantara integration as OpenAI-compatible pro… - #20641

Closed
ErRickow wants to merge 8 commits into
BerriAI:mainfrom
neosantara-xyz:feat/neosantara
Closed

feat(neosantara): add Neosantara integration as OpenAI-compatible pro…#20641
ErRickow wants to merge 8 commits into
BerriAI:mainfrom
neosantara-xyz:feat/neosantara

Conversation

@ErRickow

@ErRickow ErRickow commented Feb 7, 2026

Copy link
Copy Markdown
Contributor

Neosantara is an LLM Gateaway from Indonesian that provides OpenAI Compatible and Anthropic Compatibe endpoints


Maybe u need verify this changes at litellm/main.py If this doesn't correct let me know and will be make a changes again

Relevant issues

None

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.
  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Type

🆕 New Feature
📖 Documentation
✅ Test

Changes

  • Registered Neosantara in [litellm/llms/openai_like/providers.json].

  • Added NEOSANTARA to LlmProviders enum in [litellm/types/utils.py].

  • Added neosantara to LITELLM_CHAT_PROVIDERS, openai_compatible_providers, and openai_compatible_endpoints in [litellm/constants.py].

  • Implemented auto-detection logic in get_llm_provider_logic.py to recognize the provider automatically when api_base="https://api.neosantara.xyz/v1" is used.

  • Synchronized model_prices_and_context_window.json with Neosantara Models

  • Created comprehensive documentation in docs/my-website/docs/providers/neosantara.md:

    • Python SDK usage (Streaming & Non-streaming).
    • Embedding usage.
    • LiteLLM Proxy Server configuration.
  • Added Neosantara to the documentation sidebar.
    Testing

  • Created tests/test_litellm/test_neosantara.py with 6 tests:

    • Provider info mapping verification.
    • Chat completion formatting.
    • Embedding formatting (verifying vector data parsing).
    • Automatic provider detection via api_base.
    • Bridge tests for litellm.responses() and litellm.anthropic_messages().

@vercel

vercel Bot commented Feb 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Mar 4, 2026 5:50am

Request Review

@CLAassistant

CLAassistant commented Feb 7, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@greptile-apps

greptile-apps Bot commented Feb 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR integrates Neosantara as an OpenAI-compatible provider across the LiteLLM codebase — registering it in the provider JSON, enum, constants, model pricing, documentation, and tests.

  • Critical regression in litellm/main.py: The embedding function change adds overly broad catch-all conditions (custom_llm_provider in litellm.openai_compatible_providers and JSONProviderRegistry.exists(...)) that will intercept existing providers (hosted_vllm, llamafile, lm_studio, vercel_ai_gateway) before their dedicated embedding handlers can execute, routing them through the generic OpenAI path with wrong API bases/keys.
  • Auto-detection in get_llm_provider_logic.py hardcodes neosantara detection before the openai_compatible_endpoints loop instead of following the established pattern used by all other providers — the endpoint should be added to openai_compatible_endpoints in constants.py and detected inside the loop.
  • provider_endpoints_support.json claims neosantara supports nearly every endpoint type (assistants, fine_tuning, realtime, vector_stores, RAG, audio, etc.) — highly inflated compared to similar small providers like Groq or SambaNova.
  • LITELLM_EMBEDDING_PROVIDERS_SUPPORTING_INPUT_ARRAY_OF_TOKENS addition lacks evidence that neosantara's embedding endpoint actually accepts pre-tokenized input arrays.
  • Registration in __init__.py, types/utils.py, providers.json, and documentation follow the correct existing patterns.

Confidence Score: 1/5

  • This PR introduces a critical regression in the embedding function that will break multiple existing providers.
  • The catch-all condition added to main.py's embedding function will intercept hosted_vllm, llamafile, lm_studio, and vercel_ai_gateway before their dedicated handlers, routing them to the generic OpenAI embedding path with incorrect API bases and keys. This is a breaking change for existing users of those providers.
  • litellm/main.py (critical regression in embedding routing), provider_endpoints_support.json (inflated endpoint claims), litellm/litellm_core_utils/get_llm_provider_logic.py (non-standard auto-detection pattern)

Important Files Changed

Filename Overview
litellm/main.py Adds overly broad catch-all conditions to the embedding function that will break existing providers (hosted_vllm, llamafile, lm_studio, vercel_ai_gateway) by intercepting them before their dedicated handlers.
litellm/litellm_core_utils/get_llm_provider_logic.py Adds hardcoded Neosantara auto-detection before the existing openai_compatible_endpoints loop, duplicating boilerplate instead of following the established pattern.
litellm/constants.py Correctly adds neosantara to LITELLM_CHAT_PROVIDERS and openai_compatible_providers, but questionably adds it to LITELLM_EMBEDDING_PROVIDERS_SUPPORTING_INPUT_ARRAY_OF_TOKENS without evidence of support.
litellm/init.py Adds model sets and registration logic for neosantara following existing patterns correctly.
litellm/types/utils.py Adds NEOSANTARA to LlmProviders enum — straightforward, no issues.
litellm/llms/openai_like/providers.json Registers neosantara with correct base URL and API key env var. Removes trailing newline (minor).
model_prices_and_context_window.json Adds ~35 neosantara model entries with pricing/context info. Has unrelated formatting changes to vertex_ai entries.
provider_endpoints_support.json Claims support for nearly every endpoint type including assistants, fine_tuning, realtime, vector_stores — highly inflated compared to similar providers.
tests/test_litellm/test_neosantara.py Tests cover provider detection, completion, embedding, and bridge APIs. Uses mock patching at openai SDK level. Missing trailing newline.
docs/my-website/docs/providers/neosantara.md Documentation follows the standard provider page format with usage examples and model listing.
docs/my-website/sidebars.js Adds neosantara to sidebar in alphabetical order — no issues.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["embedding() called"] --> B{"custom_llm_provider == 'openai' OR\n'together_ai' OR 'nvidia_nim' OR\n'litellm_proxy' OR\n**in openai_compatible_providers** OR\n**JSONProviderRegistry.exists()**?"}
    B -- "Yes (NEW catch-all)" --> C["Generic OpenAI embedding handler\napi_base defaults to api.openai.com"]
    B -- "No" --> D{"custom_llm_provider == 'hosted_vllm'?"}
    D -- "Yes" --> E["hosted_vllm handler\nHOSTED_VLLM_API_BASE"]
    D -- "No" --> F{"custom_llm_provider == 'llamafile'\nOR 'lm_studio'?"}
    F -- "Yes" --> G["openai_like handler\nOPENAI_LIKE_API_BASE"]
    F -- "No" --> H{"custom_llm_provider ==\n'vercel_ai_gateway'?"}
    H -- "Yes" --> I["vercel handler\nVERCEL_AI_GATEWAY_API_BASE"]

    style B fill:#ff6b6b,color:#000
    style C fill:#ff6b6b,color:#000
    style E fill:#cccccc,color:#000
    style G fill:#cccccc,color:#000
    style I fill:#cccccc,color:#000
Loading

Last reviewed commit: 583f645

@greptile-apps greptile-apps 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.

11 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

Comment thread model_prices_and_context_window.json Outdated
Comment on lines +34903 to +34906
"neosantara/nusantara-base": {
"max_input_tokens": 64000,
"max_output_tokens": 2048,
"input_cost_per_token": 0.00000001875,

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.

Duplicate model cost keys

This commit introduces duplicated Neosantara entries in model_prices_and_context_window.json (e.g., neosantara/nusantara-base, neosantara/archipelago-70b, etc. appear multiple times). In JSON, duplicate keys are undefined/implementation-dependent; whichever occurrence is last wins, which can silently change max token limits/costs and provider detection via add_known_models().

Fix: de-duplicate so each model key appears exactly once, and validate that the resulting map is deterministic.

Comment thread provider_endpoints_support.json Outdated
Comment on lines +1470 to +1477
"neosantara": {
"display_name": "Neosantara (`neosantara`)",
"url": "https://docs.litellm.ai/docs/providers/neosantara",
"endpoints": {
"chat_completions": true,
"messages": true,
"responses": true,
"embeddings": true

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.

Missing endpoint flags keys

The new neosantara entry’s endpoints object only includes a subset of keys (chat_completions/messages/responses/embeddings), while neighboring providers include the full flag set (e.g., image_generations, audio_transcriptions, moderations, batches, rerank, a2a, interactions, etc.). Any code that assumes these keys exist (rather than using .get(..., False)) will raise KeyError/fail when iterating providers.

Fix: include the full set of endpoint boolean keys with explicit true/false values, matching the schema used for other providers.

Comment thread tests/test_litellm/test_neosantara.py Outdated
Comment on lines +85 to +109
def test_neosantara_responses_api_bridge():
"""
Test that Neosantara works with litellm.responses() API bridge.
"""
os.environ["NEOSANTARA_API_KEY"] = "sk-1234"

with pytest.MonkeyPatch().context() as m:
def mock_send(self, request, **kwargs):
return httpx.Response(
200,
content='{"choices": [{"message": {"content": "Hello from responses API"}, "finish_reason": "stop", "index": 0}], "usage": {"prompt_tokens": 10, "completion_tokens": 10, "total_tokens": 20}, "object": "chat.completion", "id": "resp-123", "created": 123456789}'.encode(),
request=request
)

m.setattr("httpx.Client.send", mock_send)

response = litellm.responses(
model="neosantara/claude-3-haiku",
input="hi",
api_key="sk-1234"
)

assert response.model == "neosantara/claude-3-haiku"
assert response.output[0].content[0].text == "Hello from responses API"

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.

Bridge tests mock wrong schema

test_neosantara_responses_api_bridge() mocks an OpenAI chat.completion payload and then asserts on litellm.responses() output shape. responses() expects a Responses API object (with output blocks), and anthropic_messages() expects an Anthropic messages-shaped response. If the bridge code doesn’t transform chat.completion into those shapes, these tests will fail (or pass for the wrong reasons if the bridge returns raw dicts).

Fix: mock the actual wire-format each bridge expects/produces (Responses API + Anthropic Messages), or assert on the exact transformation behavior implemented in the bridge layer.

@greptile-apps

greptile-apps Bot commented Feb 7, 2026

Copy link
Copy Markdown
Contributor
Additional Comments (1)

litellm/llms/openai_like/providers.json
Broken providers.json structure

providers.json is no longer valid/consistent with the expected top-level mapping of provider name → config. The sarvam entry appears to be nested under the preceding object (indentation/brace placement) and neosantara is placed alongside it, which will make JSONProviderRegistry/provider config loading fail or silently ignore providers depending on parser/loader behavior. This is on the critical path for resolving OpenAI-like providers.

Fix: ensure sarvam and neosantara are top-level keys (siblings of gmi, etc.) and the file ends with the correct closing braces.

(Also verify the file parses as JSON after the edit.)

@jquinter

jquinter commented Feb 7, 2026

Copy link
Copy Markdown
Contributor

@greptile-apps can you re-review this?

@greptile-apps

greptile-apps Bot commented Feb 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Overview

Greptile Summary

This PR adds Neosantara as an OpenAI-compatible provider by registering a JSON provider config (litellm/llms/openai_like/providers.json), extending the provider enum, adding the provider to OpenAI-compatible provider/endpoint lists, and adding docs + tests.

Provider routing is primarily handled via get_llm_provider() using either an explicit neosantara/<model> prefix (via JSONProviderRegistry) or via api_base auto-detection (substring match against openai_compatible_endpoints). The main functional risk in this PR is that the api_base auto-detection path is currently inconsistent and will not reliably map https://api.neosantara.xyz/v1 to neosantara.

Tests and metadata files were added/updated to reflect supported endpoints and model pricing/context windows, but the new tests’ HTTP mocking and bridge-schema assertions do not appear aligned with LiteLLM’s actual request path and bridge output shapes.

Confidence Score: 2/5

  • This PR is not safe to merge yet due to a provider auto-detection bug and brittle/incorrect tests.
  • Auto-detection via api_base appears broken because the endpoint substring list and the endpoint equality checks are misaligned, which will break documented usage (get_llm_provider(..., api_base=...)). Additionally, tests rely on monkeypatching httpx send methods that may not intercept LiteLLM’s real HTTP path and include bridge-schema assertions that likely don’t match actual bridge behavior, risking CI failures or false confidence.
  • litellm/litellm_core_utils/get_llm_provider_logic.py, tests/test_litellm/test_neosantara.py

Important Files Changed

Filename Overview
docs/my-website/docs/providers/neosantara.md Adds Neosantara provider documentation with SDK and proxy examples; no functional code changes.
docs/my-website/sidebars.js Adds Neosantara doc to providers sidebar; ensure build tooling validates JS syntax in CI.
litellm/init.py No Neosantara-specific logic changes observed; relies on existing provider loading and model registration paths.
litellm/constants.py Registers 'neosantara' in openai-compatible lists and embedding providers; includes a likely duplicate entry ('baseten') pre-existing, and adds neosantara endpoint substring matching.
litellm/litellm_core_utils/get_llm_provider_logic.py Adds api_base auto-detection for https://api.neosantara.xyz/v1 -> neosantara; bug: the endpoint constant string does not match the substring list entry, so auto-detection will not fire.
litellm/llms/openai_like/providers.json Adds JSON-configured provider entry for neosantara with base_url and env var; used by JSONProviderRegistry.
litellm/main.py No Neosantara-specific logic found in scanned sections; large file, but provider routing relies on get_llm_provider logic.
litellm/types/utils.py Adds NEOSANTARA to LlmProviders enum; integrates into provider_list / validation paths.
model_prices_and_context_window.json Adds Neosantara model pricing/context entries; prior duplicate-key issue was noted in review threads and should remain de-duplicated and deterministic.
provider_endpoints_support.json Adds neosantara provider endpoint support flags; prior review notes missing full endpoint key set—ensure schema-complete endpoints map.
tests/test_litellm/test_neosantara.py Adds tests for provider mapping, request formatting, and bridges; tests assume httpx.Client.send patching is sufficient and include bridge-shape assertions that likely don't match actual bridge outputs.

Sequence Diagram

sequenceDiagram
  participant U as Caller
  participant GL as get_llm_provider()
  participant JPR as JSONProviderRegistry
  participant OH as OpenAI-like handler
  participant NX as Neosantara API

  U->>GL: "get_llm_provider(model=neosantara/..., api_base=None)"
  GL->>JPR: "exists('neosantara')"
  JPR-->>GL: "true"
  GL-->>U: "provider=neosantara, api_base=https://api.neosantara.xyz/v1"

  U->>GL: "get_llm_provider(model=claude-3-haiku, api_base=https://api.neosantara.xyz/v1)"
  GL-->>U: "expected provider=neosantara"
  Note over GL,U: "In this PR, substring list vs. endpoint comparison mismatch can prevent auto-detect"

  U->>OH: "completion(model=neosantara/claude-3-haiku)"
  OH->>NX: "POST /v1/chat/completions"
  NX-->>OH: "chat.completion JSON"
  OH-->>U: "ModelResponse"

Loading

@greptile-apps greptile-apps 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.

11 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 304 to 311
elif endpoint == "https://api.inference.wandb.ai/v1":
custom_llm_provider = "wandb"
dynamic_api_key = get_secret_str("WANDB_API_KEY")
elif endpoint == "https://api.neosantara.xyz/v1":
custom_llm_provider = "neosantara"
dynamic_api_key = get_secret_str("NEOSANTARA_API_KEY")

if api_base is not None and not isinstance(api_base, str):

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.

Neosantara auto-detect never triggers

get_llm_provider() checks for endpoint in litellm.openai_compatible_endpoints: if endpoint in api_base, but the Neosantara entry in openai_compatible_endpoints is "https://api.neosantara.xyz/v1" while this branch compares against endpoint == "https://api.neosantara.xyz/v1" (note the extra https:// prefix in the list and also that other entries are often host/path substrings). As written, endpoint in api_base will not match when users pass api_base="https://api.neosantara.xyz/v1" unless the list and the elif endpoint == ... constant are aligned; this makes the documented auto-detection path fail (and your test_neosantara_auto_detection_api_base will fail in real execution).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

any suggestions how to fix?

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.

Looking at the code, the issue is that the auto-detection happens inside the loop that checks if endpoint in api_base, but your elif check uses exact equality == instead of the substring check.

The fix is simple - change your condition to match the pattern used by other providers:

Suggested change
elif endpoint == "https://api.inference.wandb.ai/v1":
custom_llm_provider = "wandb"
dynamic_api_key = get_secret_str("WANDB_API_KEY")
elif endpoint == "https://api.neosantara.xyz/v1":
custom_llm_provider = "neosantara"
dynamic_api_key = get_secret_str("NEOSANTARA_API_KEY")
if api_base is not None and not isinstance(api_base, str):
elif "api.neosantara.xyz" in endpoint:
custom_llm_provider = "neosantara"
dynamic_api_key = get_secret_str("NEOSANTARA_API_KEY")

This way:

  1. The outer loop condition if endpoint in api_base will match when api_base="https://api.neosantara.xyz/v1"
  2. Your inner check "api.neosantara.xyz" in endpoint will then correctly identify it as Neosantara

This pattern matches how other providers like Fireworks and Together AI handle detection in the same file.

Comment thread tests/test_litellm/test_neosantara.py Outdated
Comment on lines +24 to +53
# We mock the actual call to avoid network requests
with pytest.MonkeyPatch().context() as m:
def mock_send(self, request, **kwargs):
# Verify request URL and headers
assert "api.neosantara.xyz" in str(request.url)
assert request.headers["Authorization"] == "Bearer sk-1234"

# Verify request body is OpenAI format
body = json.loads(request.read())
assert "messages" in body
assert body["model"] == "claude-3-haiku"

return httpx.Response(
200,
content='{"choices": [{"message": {"content": "Hello world"}, "finish_reason": "stop", "index": 0}], "usage": {"prompt_tokens": 10, "completion_tokens": 10, "total_tokens": 20}, "object": "chat.completion"}'.encode(),
request=request
)

m.setattr("httpx.Client.send", mock_send)

response = litellm.completion(
model="neosantara/claude-3-haiku",
messages=[{"role": "user", "content": "hi"}],
api_key="sk-1234"
)

assert response.choices[0].message.content == "Hello world"
assert response._hidden_params["custom_llm_provider"] == "neosantara"
assert response._hidden_params["api_base"] == "https://api.neosantara.xyz/v1"

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.

Monkeypatch may not intercept HTTP

These tests patch httpx.Client.send / httpx.AsyncClient.send, but LiteLLM often routes requests through its own HTTPHandler / AsyncHTTPHandler (or an AsyncClient instance), so patching the class method may not affect the actual call path. If the patched method isn’t used, the test can attempt a real network request (and/or fail for the wrong reason). Consider patching the concrete request function LiteLLM uses (e.g., the module-level client/handler used in OpenAI-like providers) so the tests deterministically intercept outbound traffic.

Also appears in: tests/test_litellm/test_neosantara.py:61-85, :105-131, :139-171.

Comment thread tests/test_litellm/test_neosantara.py Outdated
Comment on lines +99 to +130
def test_neosantara_responses_api_bridge():
"""
Test that Neosantara works with litellm.responses() API bridge.
"""
os.environ["NEOSANTARA_API_KEY"] = "sk-1234"

with pytest.MonkeyPatch().context() as m:
def mock_send(self, request, **kwargs):
# Verify the bridge transformed 'input' into OpenAI 'messages'
body = json.loads(request.read())
assert "messages" in body
assert body["messages"][0]["content"] == "hi from responses"
assert "/v1/chat/completions" in str(request.url)

return httpx.Response(
200,
content='{"choices": [{"message": {"content": "Hello from bridge"}, "finish_reason": "stop", "index": 0}], "usage": {"prompt_tokens": 10, "completion_tokens": 10, "total_tokens": 20}, "object": "chat.completion", "id": "chatcmpl-123", "created": 1677610602}'.encode(),
request=request
)

m.setattr("httpx.Client.send", mock_send)

response = litellm.responses(
model="neosantara/claude-3-haiku",
input="hi from responses",
api_key="sk-1234"
)

# Verify result is transformed to Responses API schema
assert response.model == "neosantara/claude-3-haiku"
assert response.output[0].content[0].text == "Hello from bridge"
assert hasattr(response, "created_at")

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.

Bridge response schema mismatch

test_neosantara_responses_api_bridge() mocks a chat.completion-shaped response ({"choices": ...}) but then asserts litellm.responses() returns a Responses API object with output[0].content[0].text and created_at. Unless the bridge layer actually transforms chat.completions into Responses API output blocks, this will fail (or pass only if responses() is returning an unexpected raw/compat object). The mock payload/assertions need to match the real wire format and transformation implemented by the bridge.

Also appears in: tests/test_litellm/test_neosantara.py:133-170 for anthropic_messages().

@ErRickow

ErRickow commented Feb 7, 2026

Copy link
Copy Markdown
Contributor Author

@greptile-apps re review the changes

@greptile-apps

greptile-apps Bot commented Feb 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Overview

Greptile Summary

Added Neosantara as a new OpenAI-compatible LLM provider for the Indonesian market. Integration includes 42 models (chat and embeddings), provider auto-detection via api_base, comprehensive test coverage, and complete documentation.

Key changes:

  • Registered provider in providers.json with correct base URL, API key environment variable, and parameter mappings
  • Added provider to all required constants lists and enums across the codebase
  • Implemented auto-detection logic to identify Neosantara when api_base contains api.neosantara.xyz/v1
  • Synchronized 42 model definitions with pricing and token limits in model_prices_and_context_window.json
  • Created 6 comprehensive tests covering provider info, auto-detection, completions, embeddings, and bridge APIs
  • Added complete provider documentation with SDK and proxy usage examples

Previous review concerns addressed:

  • Duplicate model cost keys have been de-duplicated
  • Provider endpoint flags now include the complete set of boolean keys
  • Bridge tests properly mock and verify transformations
  • Auto-detection logic correctly uses substring matching pattern

Confidence Score: 4/5

  • This PR is safe to merge with minor style improvements recommended
  • The integration is well-structured and follows established patterns for adding OpenAI-compatible providers. All previous review concerns have been addressed. The only issue is a redundant check in main.py that doesn't affect functionality. Test coverage is comprehensive with 6 tests covering all integration points.
  • litellm/main.py has a minor redundancy but doesn't require blocking changes

Important Files Changed

Filename Overview
litellm/llms/openai_like/providers.json Added neosantara provider configuration with correct base_url, api_key_env, and param_mappings
litellm/constants.py Registered neosantara in all required provider lists and endpoints with correct configurations
litellm/litellm_core_utils/get_llm_provider_logic.py Added auto-detection logic for neosantara when api_base contains api.neosantara.xyz/v1
litellm/main.py Added neosantara to embedding condition (redundant with openai_compatible_providers check), also added broader compatibility checks
tests/test_litellm/test_neosantara.py Comprehensive tests covering provider detection, completions, embeddings, and bridge APIs with proper mocking

Sequence Diagram

sequenceDiagram
    participant User
    participant LiteLLM as litellm.completion()
    participant GetProvider as get_llm_provider()
    participant Registry as JSONProviderRegistry
    participant OpenAI as OpenAILikeAPI
    participant Neosantara as api.neosantara.xyz

    User->>LiteLLM: completion(model="neosantara/claude-3-haiku")
    LiteLLM->>GetProvider: get_llm_provider("neosantara/claude-3-haiku")
    GetProvider->>GetProvider: Parse prefix "neosantara/"
    GetProvider->>Registry: Check providers.json for "neosantara"
    Registry-->>GetProvider: Found: base_url, api_key_env, param_mappings
    GetProvider-->>LiteLLM: provider="neosantara", api_base="https://api.neosantara.xyz/v1"
    
    alt Auto-detection via api_base
        User->>LiteLLM: completion(model="claude-3-haiku", api_base="https://api.neosantara.xyz/v1")
        LiteLLM->>GetProvider: get_llm_provider(model, api_base)
        GetProvider->>GetProvider: Loop through openai_compatible_endpoints
        GetProvider->>GetProvider: if "api.neosantara.xyz/v1" in api_base
        GetProvider-->>LiteLLM: provider="neosantara", dynamic_api_key
    end
    
    LiteLLM->>OpenAI: Route to OpenAI-compatible handler
    OpenAI->>OpenAI: Transform params (max_completion_tokens → max_tokens)
    OpenAI->>Neosantara: POST /v1/chat/completions
    Note over Neosantara: OpenAI-compatible endpoint
    Neosantara-->>OpenAI: Chat completion response
    OpenAI-->>LiteLLM: Parse and transform response
    LiteLLM-->>User: ModelResponse object
Loading

@greptile-apps greptile-apps 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.

5 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment thread litellm/main.py Outdated
or custom_llm_provider == "together_ai"
or custom_llm_provider == "nvidia_nim"
or custom_llm_provider == "litellm_proxy"
or custom_llm_provider == "neosantara"

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.

redundant - neosantara is already in openai_compatible_providers (checked on line 4760)

@ErRickow

ErRickow commented Feb 7, 2026

Copy link
Copy Markdown
Contributor Author

@jquinter hi, i was resolving the commented code review there

@ErRickow

ErRickow commented Feb 7, 2026

Copy link
Copy Markdown
Contributor Author

@greptile-apps re review the pr

@greptile-apps

greptile-apps Bot commented Feb 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Overview

Greptile Summary

This PR adds Neosantara, an Indonesian LLM gateway, as a new OpenAI-compatible provider to LiteLLM. The implementation follows established patterns and includes all necessary components.

Key changes:

  • Registered Neosantara in providers.json with base URL, API key configuration, and parameter mappings (max_completion_tokensmax_tokens)
  • Added provider enum, constants, and endpoint registrations across core files
  • Implemented auto-detection logic that recognizes api.neosantara.xyz/v1 in api_base parameter
  • Added 21 Neosantara models to pricing/token limits configuration
  • Included comprehensive test coverage (6 tests) for provider detection, chat completions, embeddings, and API bridges
  • Provided complete documentation with Python SDK and proxy server examples

Previous issues resolved:
All issues from prior review threads have been addressed in subsequent commits:

  • Duplicate model entries removed from model_prices_and_context_window.json
  • Complete endpoint flag set added to provider_endpoints_support.json with all required keys
  • Auto-detection logic properly aligned with endpoint list pattern
  • Main.py correctly uses generic openai_compatible_providers check without redundancy

The implementation is clean, follows LiteLLM conventions, and integrates seamlessly with existing OpenAI-compatible provider infrastructure.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The PR follows established patterns for adding new OpenAI-compatible providers, includes comprehensive tests, complete documentation, and addresses all previously identified issues (duplicates, missing endpoint flags, test mocking). The implementation is straightforward and aligns with the codebase architecture.
  • No files require special attention

Important Files Changed

Filename Overview
litellm/llms/openai_like/providers.json Adds Neosantara provider config with base URL, API key env var, and parameter mappings
litellm/constants.py Registers neosantara in provider lists, endpoints, and embedding models
litellm/litellm_core_utils/get_llm_provider_logic.py Adds auto-detection logic for Neosantara based on api_base URL
model_prices_and_context_window.json Adds 21 Neosantara models with pricing and token limits (duplicates resolved)
provider_endpoints_support.json Adds complete Neosantara endpoint flags with all required keys
tests/test_litellm/test_neosantara.py Comprehensive test suite with provider detection, chat, embeddings, and bridge tests

Sequence Diagram

sequenceDiagram
    participant User
    participant LiteLLM
    participant get_llm_provider
    participant JSONProviderRegistry
    participant OpenAILike
    participant Neosantara

    User->>LiteLLM: completion(model="neosantara/claude-3-haiku")
    LiteLLM->>get_llm_provider: get_llm_provider(model)
    
    alt Model has neosantara/ prefix
        get_llm_provider->>JSONProviderRegistry: Check providers.json
        JSONProviderRegistry-->>get_llm_provider: base_url, api_key_env, param_mappings
        get_llm_provider-->>LiteLLM: provider="neosantara", api_base="https://api.neosantara.xyz/v1"
    else api_base contains "api.neosantara.xyz/v1"
        get_llm_provider->>get_llm_provider: Auto-detect from openai_compatible_endpoints
        get_llm_provider-->>LiteLLM: provider="neosantara", dynamic_api_key from NEOSANTARA_API_KEY
    end

    LiteLLM->>OpenAILike: Route to OpenAI-compatible handler
    OpenAILike->>OpenAILike: Apply param_mappings (max_completion_tokens → max_tokens)
    OpenAILike->>Neosantara: POST https://api.neosantara.xyz/v1/chat/completions
    Neosantara-->>OpenAILike: OpenAI-compatible response
    OpenAILike-->>LiteLLM: Formatted response
    LiteLLM-->>User: ModelResponse
Loading

@greptile-apps greptile-apps 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.

6 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@ErRickow

Copy link
Copy Markdown
Contributor Author

@jquinter any response? should i make new pull or should i follow existing provider pattern?

@giulio-leone

Copy link
Copy Markdown
Contributor

Automated patch bundle from top-50 unresolved backlog scan.\nGenerated due limited direct branch-write access; please apply/cherry-pick the proposed minimal edits below.\n\n## PR #20641feat/neosantara

Unresolved threads summary

  • greptile-apps, model_prices_and_context_window.json:34906, Duplicate model cost keys: This commit introduces duplicated Neosantara entries in model_prices_and_context_window.json (e.g., neosantara/nusantara-base, neosantara/ar….
  • greptile-apps, provider_endpoints_support.json:1477, Missing endpoint flags keys: The new neosantara entry’s endpoints object only includes a subset of keys (chat_completions/messages/responses/embeddings), while neighbo….
  • greptile-apps, tests/test_litellm/test_neosantara.py:109, Bridge tests mock wrong schema.
  • greptile-apps, litellm/litellm_core_utils/get_llm_provider_logic.py:311, Looking at the code, the issue is that the auto-detection happens inside the loop that checks if endpoint in api_base, but your elif check uses exact equality == inste….
  • greptile-apps, tests/test_litellm/test_neosantara.py:53, Monkeypatch may not intercept HTTP.
  • greptile-apps, tests/test_litellm/test_neosantara.py:130, Bridge response schema mismatch.
  • greptile-apps, litellm/main.py:4758, redundant - neosantara is already in openai_compatible_providers (checked on line 4760).

Concrete patch proposal

  • model_prices_and_context_window.json: collapse duplicated branches into one canonical implementation.

  • provider_endpoints_support.json: add the full endpoint-flag object used by neighboring providers.

  • tests/test_litellm/test_neosantara.py: align bridge test fixtures with the provider's real response schema; patch the actual HTTP client symbol used by production code.

  • litellm/litellm_core_utils/get_llm_provider_logic.py: move provider auto-detection outside the loop and replace equality check with endpoint inclusion check.

  • litellm/main.py: remove duplicate provider-registration branch and keep one source of truth.

@ErRickow

ErRickow commented Mar 2, 2026

Copy link
Copy Markdown
Contributor Author

@giulio-leone Its will be great if i'm create the dir for Neosantara in llms/? much easier maintain. and add the anthropic compatible endpoint as well on there?

I just fixed the integration but not yet push to the repo. I just realized my integration doesn't make on llms/ and wow this is make me confused when first time write the integration. Because one integration on the openai_like its working on other helper, responses api, even with Anthropic Ecosystems.

Though? before i push the changes i think

@ErRickow

ErRickow commented Mar 4, 2026

Copy link
Copy Markdown
Contributor Author

@giulio-leone you can tests now, 7 tests passed

Comment thread litellm/main.py
Comment on lines +4759 to +4760
or custom_llm_provider in litellm.openai_compatible_providers
or JSONProviderRegistry.exists(custom_llm_provider)

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.

Catch-all breaks existing embedding providers

These two new conditions (custom_llm_provider in litellm.openai_compatible_providers and JSONProviderRegistry.exists(custom_llm_provider)) are far broader than what's needed for neosantara. They will now intercept every openai-compatible provider and route it through the generic OpenAI embedding path — but several of those providers have their own dedicated elif blocks further down that will now never be reached:

  • hosted_vllm (line 4832) — has specific HOSTED_VLLM_API_BASE/HOSTED_VLLM_API_KEY env var handling
  • llamafile / lm_studio (line 4856) — uses openai_like_embedding handler
  • vercel_ai_gateway (line 4964) — has specific VERCEL_AI_GATEWAY_API_BASE/VERCEL_AI_GATEWAY_API_KEY and VERCEL_OIDC_TOKEN handling

With this change, these providers will fall into the generic OpenAI handler, which defaults api_base to https://api.openai.com/v1 and api_key to OPENAI_API_KEY — completely wrong for them.

To only add neosantara embedding support without breaking existing providers, the condition should be narrowed. For example:

Suggested change
or custom_llm_provider in litellm.openai_compatible_providers
or JSONProviderRegistry.exists(custom_llm_provider)
or custom_llm_provider in litellm.openai_compatible_providers
or JSONProviderRegistry.exists(custom_llm_provider)

Should instead be something like:

            or custom_llm_provider == "neosantara"

Or, better yet, follow the existing pattern and add neosantara to the list at the top of this elif chain.

Context Used: Rule from dashboard - What: Avoid writing provider-specific code outside of the llms/ directory.

Why: This practice ensur... (source)

Comment on lines +198 to +211
if "api.neosantara.xyz/v1" in api_base:
custom_llm_provider = "neosantara"
dynamic_api_key = get_secret_str("NEOSANTARA_API_KEY")
if api_base is not None and not isinstance(api_base, str):
raise Exception(
"api base needs to be a string. api_base={}".format(api_base)
)
if dynamic_api_key is not None and not isinstance(dynamic_api_key, str):
raise Exception(
"dynamic_api_key needs to be a string. dynamic_api_key={}".format(
dynamic_api_key
)
)
return model, custom_llm_provider, dynamic_api_key, api_base

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.

Auto-detection bypasses established pattern

This hardcoded block is placed before the openai_compatible_endpoints loop, duplicating the validation/return logic that already exists inside that loop. Every other provider (Perplexity, Groq, Cerebras, etc.) adds its endpoint to openai_compatible_endpoints in constants.py and is then detected inside the loop with an elif clause.

Neosantara should follow the same pattern:

  1. Add "api.neosantara.xyz/v1" to openai_compatible_endpoints in constants.py
  2. Add an elif "api.neosantara.xyz" in endpoint: clause inside the existing loop (around line 319)

This removes 14 lines of duplicated boilerplate and keeps the codebase consistent.

Context Used: Rule from dashboard - What: Avoid writing provider-specific code outside of the llms/ directory.

Why: This practice ensur... (source)

Comment thread litellm/constants.py
"azure",
"hosted_vllm",
"nebius",
"neosantara",

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.

Unsubstantiated token-array support claim

Adding neosantara to LITELLM_EMBEDDING_PROVIDERS_SUPPORTING_INPUT_ARRAY_OF_TOKENS means LiteLLM will allow callers to pass pre-tokenized integer arrays as embedding input. Only providers whose embedding endpoints actually accept token arrays (like OpenAI, Azure) should be in this list. There's no documentation or evidence that Neosantara's nusa-embedding-0001 endpoint supports this — if it doesn't, this will cause silent failures or incorrect behavior at the provider level.

Comment on lines +1470 to +1500
"neosantara": {
"display_name": "Neosantara (`neosantara`)",
"url": "https://docs.litellm.ai/docs/providers/neosantara",
"endpoints": {
"chat_completions": true,
"messages": true,
"responses": true,
"embeddings": true,
"image_generations": true,
"audio_transcriptions": true,
"audio_speech": true,
"moderations": true,
"batches": true,
"rerank": false,
"container": true,
"compact": true,
"a2a": true,
"interactions": true,
"vector_store_files": true,
"vector_stores_create": true,
"vector_stores_search": true,
"assistants": true,
"container_files": true,
"fine_tuning": true,
"image_variations": true,
"rag_ingest": true,
"rag_query": true,
"realtime": true,
"text_completion": true
}
},

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.

Inflated endpoint support claims

Neosantara's endpoint flags claim support for assistants, fine_tuning, realtime, vector_stores_*, rag_*, image_generations, audio_transcriptions, audio_speech, moderations, batches, container, container_files, and image_variations. For comparison, similar small OpenAI-compatible providers like Groq, Together AI, and SambaNova only claim chat_completions, messages, responses, and a few metadata flags.

Unless Neosantara actually supports all of these endpoints, these should be set to false to prevent LiteLLM from advertising or routing unsupported operations to this provider.

@ErRickow

ErRickow commented Mar 4, 2026

Copy link
Copy Markdown
Contributor Author

i think i need re-pull request and try to understand the repo first:)

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.

4 participants