feat: add Neosantara provider - #6165
Conversation
Neosantara is an OpenAI-compatible LLM gateway. Both catalog endpoints used by the sync are public and need no API key: - https://api.neosantara.xyz/v1/models - https://api.neosantara.xyz/v1/public/pricing The sync module registers with the existing provider-sync workflow, so the catalog is refreshed by the usual periodic automation. Scope of the generated catalog: - text models with at least 100k context that advertise function calling - image generation models that have a canonical models/ entry - deprecated models are excluded Pricing is published in USD per million tokens, applying the customer-facing discount and converting IDR list prices with the exchange rate the pricing endpoint reports. Per-image pricing is left unpublished because models.dev has no field for it. Reasoning options are reviewed per model: each effort list is the lab/peer set intersected with what the public request schema accepts, and models that expose no caller control author an empty set. Provider files stay override-only via base_model, so limits, modalities and benchmarks are inherited from the canonical lab entries.
Single-colour vector mark with a square viewBox and no fixed dimensions, so it inherits the surrounding text colour when inlined.
The embedded raster carried ~13% transparent padding on every side and was inset a further 25 units inside a 500x500 viewBox, so the mark covered only about 66% of the canvas and rendered smaller than neighbouring logos. Crop to the opaque bounds, centre the mark on a square canvas that it fills edge to edge, and drop the root width/height (the site strips them when it inlines the file). Also reduces the file from 102 KB to 59 KB.
Action items
|
This host is OpenAI-compatible and normalizes reasoning onto a single caller-facing field, reasoning_effort, accepting none|minimal|low|medium|high|xhigh. Upstream-native shapes are handled behind that field and never appear in a caller's request, so documenting a thinking budget or a vendor toggle described the wrong surface. Every reasoning model now authors an effort list taken from its lab entry and same-surface peers, intersected with the values this host accepts, so `max` is dropped and `minimal` appears only where the lab or peers list it. Two models whose lab and peers document no graded level keep a toggle, since on or off is the caller's only choice there. Drops all budget_tokens controls and the empty control sets, and removes the wire comments that pointed at a native surface callers never reach.
The per-model table meant every new reasoning model needed a code change. The values it held were already in the repository, so read them at sync time instead: the underlying lab entry wins, otherwise the set its same-surface peers agree on, intersected with the values this host accepts. Preferring the lab over a relay also corrects two entries the table had copied from the wrong source. deepseek-v4-flash now follows DeepSeek's own low|high rather than a relay's full enum, and laguna-xs-2.1 is a toggle like Poolside's own entry instead of inheriting its sibling's graded levels. New models now inherit controls with no change here.
Action items
|
…g options The first pass at deriving controls had four flaws. It looked for a lab entry at one fixed path, so nested layouts such as providers/poolside/models/poolside were missed; it ignored lab entries that declare no control, so an always-on reasoner borrowed a minority peer's levels; it tallied only peers that already had an effort list, letting one relay outvote thirty toggles; and it fell back to a bare toggle, which claims an on/off field this host does not expose. Controls are now projected onto this host's single field the way Eden AI does it: an empty lab set stays empty, a lab toggle becomes reasoning_effort = none and joins the effort list, and the lab always outranks a relay. kimi-k2-thinking is always-on again, kimi-k2.5 and k2.6 no longer claim graded levels their lab does not document, deepseek-v4-pro gains the none it needs to turn thinking off, and the Laguna pair follows Poolside instead of a sibling.
|
Thanks — the reasoning items were right, and fixing them exposed real bugs in how I was deriving controls. Addressed in Root cause. This host is OpenAI-compatible and normalizes reasoning onto a single caller-facing field,
The four derivation bugs you named — all real, all fixed. Lab lookup now matches by filename anywhere under Concretely: One item I'd push back on: The lab marks the underlying models as reasoners, but this host does not serve them that way, which is what a provider override is for. Happy to revisit if you'd rather the provider file stay silent and inherit. The logo is still on me and I'll follow up with a vector mark. |
Action items
|
Reading the gateway's public schemas end to end corrected one thing and explained several others. No model may author a toggle here. `reasoning.enabled` on its own is inert: reasoning is switched on by `reasoning_effort` being anything other than `none`, so a toggle would name a control a caller cannot use. Models whose lab documents only a binary on/off therefore author the one effort value that changes behaviour. provider.toml now carries the wire format the way the established relays do: the accepted effort values, how reasoning is switched off, that a non-reasoning model rejects the field outright, and that published costs are the raw price after the catalog discount with IDR converted at the rate the pricing endpoint reports.
Action items
|
…mment Six models whose lab documents only an on/off were published as effort ["none"], which told a caller how to switch reasoning off but named no value that switches it on. They are toggles now, each carrying a leading comment for the mapping this host uses: reasoning_effort = "none" is off, any other accepted value leaves it on, and reasoning.enabled alone does nothing. Follows the same shape and header convention as the LLM Gateway sync, which fronts many labs behind one reasoning_effort field too.
The vector mark still carried six hex fills and strokes, so it rendered the same shade on both themes. Every fill and stroke is currentColor now, which is what the site inlines against, while the existing opacity values keep the depth of the original artwork.
|
All four addressed in Logo — now a real vector: 9 paths, no raster, square Binary reasoning published as Same shape and header convention as DeepSeek V4 losing the top tier —
Keeping Longer term. The recurring theme across your reviews is that this entry infers the host's reasoning surface from lab and peer files. The hosts that avoid that publish it themselves — |
Action items
|
|
@rekram1-node hei, the github action reviewer just making me going through to the loop. He always asking, but I'm unable to answer with the tag (eg. @bot). The sync and reasoning field is gateway design, so if the reviewer point to the upstream that the existing models.dev provider have, is not valid issues, and he always ask, how tf i will reply to that feedback. Please give me some advice :) |
There was a problem hiding this comment.
this svg.... Would prefer b64 embed instead of full vector
Capability flags are read only from each catalog entry's `capabilities` array, never inherited from lab metadata: this gateway enforces its own list and answers HTTP 400 for `reasoning_effort` on a model without `reasoning`, so a model the lab ships as a reasoner is published here with reasoning = false until the catalog advertises it. `tool_call` follows `function_calling` for the same reason. A capability the gateway adds later is therefore picked up by the next sync with no code change. Reasoning models gain `interleaved.field = "reasoning_content"`. Verified against the live API through the AI SDK with streaming: `reasoning_effort = none` returns zero reasoning tokens, any graded level streams `reasoning_content` deltas, and `max` is rejected with the enum `none|minimal|low|medium|high|xhigh`. Reasoning-control indexing: the provider tree is resolved from the module rather than the working directory, an unreadable tree throws instead of silently publishing "no caller control" for every reasoning model, and a lab entry whose controls this host cannot express is left unrecorded so peer consensus still applies. Model TOMLs regenerated with `sync-models.ts neosantara`; re-running it reports no further changes. Also lets the remaining logo paths inherit the theme color.
|
about opus 7, 8... this is neosantara side wrong statement about the model id, sorry... will be fixed and will auto sync if we made changes on the gateway |
Action items
|
Treat missing lab and peer reasoning controls as unresolved instead of publishing an affirmative always-on control set. Such models are now skipped and reported until a reviewed control mapping exists; an empty list remains reserved for explicitly always-on models. Neosantara's DeepSeek route only consumes effort as an enable flag and does not forward graded effort upstream, so publish those routes as a binary toggle rather than claiming high/max distinctions the gateway cannot honor. Correct the toggle header to record that omission defaults to off. Add authoritative FX provenance headers to every model whose IDR source pricing is converted to USD, including the rate, source, and pricing snapshot date. Regenerate provider TOMLs from the public catalog. The gateway capability list remains authoritative for reasoning support: models that omit reasoning from GET /v1/models retain reasoning=false, regardless of provider-agnostic lab metadata.
Action items
|
Replace the uniform host-enum reasoning surface with per-model effort lists that follow each model's real controls (first-party lab entry + same-surface OpenRouter/Vercel peers), never a full-enum dump: - Poolside Laguna, InclusionAI Ling, Moonshot Kimi K2.5/K2.6 -> toggle - StepFun Step-3.5 -> low/high; Meta Muse -> minimal..xhigh - GPT-5.4/5.5 -> none/low/medium/high/xhigh (no minimal); GPT-5.6 adds max - Claude -> none/low/medium/high; Kimi K3 -> none/low/high/max - GLM-4.7 / *V and other forced/always-on models -> [] (no caller control) Distinguish an explicit empty reasoning_efforts (always-on, []) from a missing one: a reasoning model whose surface the catalog does not report is now skipped and reported, not stamped always-on. Toggle entries carry the leading wire comment. Rewrite provider.toml for the /v1/catalog source and the []/toggle/effort mapping. Regenerated TOMLs (6 toggle, 4 always-on, 24 effort); re-sync is idempotent and bun validate passes.
Action items
|
…lab entry Per-family effort ladders could never match AGENTS.md's per-model baseline (gpt-5-nano != gpt-5.4, claude-opus-4-6 != 4-7, gemini-3.6 has minimal), so the sync now reads each model's first-party lab entry from the canonical tree and uses its effort values, intersected with the host enum. The catalog reasoning_efforts supplies only the host mechanism: [] = always-on, ["none"] = toggle, else graded (with `none` kept only when the lab supports off and the host exposes it). Fixes the flagged ladders: gpt-5-nano -> minimal/low/medium/high; gpt-5.4 -> none/low/medium/high/xhigh; claude-opus-4-6 -> low/medium/high/max (4-7 adds xhigh); gemini-3.6-flash -> minimal/low/medium/high; deepseek-v4-flash -> none/low/high/max (pro none/high/max); step-3.5 -> low/high. No full-enum dumps or invented levels.
Action items
|
… copy it verbatim The /v1/catalog endpoint now resolves each model's real reasoning_effort surface host-side from that model's models.dev lab entry (with documented host exceptions: DeepSeek collapses low/medium->high so it accepts high/max; z.ai GLM-4.7 and vision variants force thinking on; Anthropic is served via the OpenAI-compatible reasoning_effort field so off = none; dedicated -reasoning variants are always-on). The sync therefore drops the tree-derivation/intersection and copies the catalog's reasoning_efforts verbatim (llmgateway convention): [] -> always-on, ["none"] -> toggle, else effort, intersected with the host enum defensively. This removes the invented levels the reviewer flagged: deepseek-v4-flash is none/high/max (no invented low), gpt-5-nano is minimal/low/medium/high, dated snapshots resolve to the canonical entry (deepseek-v4-flash-0731 -> none/high/max), and every listed reasoning model except the two neosantara-internal garda ids now carries an accurate surface.
Action items
|
…d clean overrides - Align Claude reasoning models with lab baselines by removing unsupported 'none' effort - Restore 'low' reasoning effort for DeepSeek Flash variants - Remove redundant structured_output overrides from base_model files - Remove unused fs import in sync module - Apply catalog discounts to reflect effective billed rates on Gemini, Kimi, and MiniMax
Action items
|
…asoning - Author toggle + graded effort for Claude Sonnet 5 matching lab and OpenRouter baselines - Include required wire comment naming reasoning_effort = none for toggle models - Support has_toggle in sync provider and test suite for reproducible generation
Action items
|
Action items
|
…lities for lab-aligned models - Delegate base_model and override_name resolution directly to backend /v1/catalog - Remove hardcoded model aliases and override maps from sync module - Restore reasoning effort surfaces for gpt-5.4-mini, gemini-3.1-flash-lite, glm-4.7, claude-4.5-opus, claude-4.5-sonnet, gpt-oss-20b, and gpt-oss-120b - Point deepseek-v4-flash and deepseek-v4-pro to their canonical dated base models (0731 and 0813)
Action items
|
…t lab baselines - Expose toggle reasoning controls for Claude 4.5 Sonnet, GLM-4.7, GLM-4.7-Flash, GLM-4.6V-Flash, MiniMax M3, and Nemotron Super 49B - Drop redundant and incorrect vision/attachment overrides on DeepSeek V4 Flash/Pro, GLM-4.7, and GLM-4.5-Flash to inherit text-only lab metadata - Restore reasoning capabilities and effort ladders on MiniMax M3 and Muse Glimmer 30B
Action items
|
…ant structured_output overrides - Confirm catalog base_model exists in local models/ tree before accepting it, falling back safely to id resolution - Drop false structured_output = false overrides across 15 models to inherit structured output support from lab base models - Add unit test coverage for invalid base_model handling and safe fallback
Action items
|
… align graded reasoning - Add deploymentModalities to strip image/pdf/video inputs when vision is false, preventing multimodal inheritance alongside attachment = false - Treat vision and tools as optional, removing redundant [modalities] overrides on multimodal lab models - Align reasoning controls on claude-4.5-opus, claude-opus-4-6, and claude-sonnet-4-6 to effort with none, eliminating invented toggle hybrids
Action items
|
…aude reasoning controls - Inherit lab multimodal inputs and drop attachment = false on vision-capable models (claude-opus-4-6, claude-sonnet-4-6, claude-fable-5, glm-5.3-flash, gpt-5.4, gpt-5.5, gpt-5.4-mini, kimi-k2.6, kimi-k3) - Align Claude reasoning controls on claude-opus-4-6, claude-sonnet-4-6, and claude-4.5-opus to toggle + graded effort without none, accompanied by wire comment - Add dedicated headers documenting always-on thinking on minimax-m2.7 and citing Meta model card/peer baselines for muse-glimmer-30b - Regenerate model TOMLs ensuring translator and catalog remain 100% idempotent
Action items
|
…rower deployment modalities
… and update provider comments
|
No actionable findings. |
Neosantara is an OpenAI-compatible LLM gateway (a multi-model relay) from Indonesia. This PR adds the provider, logo, model TOMLs, and a sync module.
Data source
The sync consumes a single public endpoint —
GET https://api.neosantara.xyz/v1/catalog— which returns the catalog in the models.dev / LLM Gateway response shape (family,architecture,providers[]withreasoning_efforts, and USD‑per‑tokenpricing). No API key is required.The gateway performs the host‑specific work upstream (USD pricing incl. IDR conversion and promotional discounts, capability flags, and the mapped reasoning surface), so the models.dev sync is a thin, override‑only translation of that shape.
Scope / filters
A model is synced when it:
models/entry (verified locally before falling back to id), andAnything the public filter accepts but we can't map yet (no canonical
models/entry) is reported viaskippedNoticeinstead of dropped silently. 49 models currently sync (48 token‑priced + 1 image).Reasoning options (per-model)
Neosantara normalizes reasoning onto a single OpenAI-compatible
reasoning_effortfield, where off is expressed asreasoning_effort = "none". PerAGENTS.md:reasoning_effortsreasoning_options[][]— always-on, no caller control (e.g.minimax-m2.7Dahl relay)["none"][{ type = "toggle" }](binary on/off, with leading wire comment)none)[{ type = "effort", values = … }]— only effort, no toggle hybrid, no wire commentLevels are intersected with the host ladder
none·minimal·low·medium·high·xhigh·max.Host surface vs the lab surface
Because Neosantara proxies each upstream provider, a model's effort surface equals its lab surface on models.dev, except:
low/mediumintohigh; the wire acceptshigh/maxonly, so the lab'slowis dropped →deepseek-v4-*=none/high/max.reasoning_effortfield (not Anthropic's nativethinking/budget_tokens), so off is expressed asreasoning_effort = "none". Claude entries therefore author onlyeffortwith["none", ...graded](e.g.claude-opus-4-6=none/low/medium/high/max), with no toggle hybrid.-reasoningvariants (e.g.grok-4.1-fast-reasoning) are always-on[].[]matching lab and peers.Modalities and attachments
Host deployments match what Neosantara actually serves:
[modalities] input = ["text", "image"]to prevent over-claiming lab base-model PDF/video capabilities while keepingattachment = true.attachment = falseis authored alongside text-only input modalities.Pricing
USD per million tokens, converted from the endpoint's per‑token USD strings.
input = 0 / output = 0.Notes
interleaved.field = "reasoning_content"on reasoning models (the gateway streams reasoning there).max_outputis intentionally not published — the gateway's runtime cap is not the model's true output limit.0 created, 0 updated, 0 removed, 49 unchanged), andbun validatepasses.