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
|
…iformly Neosantara is a relay that normalizes every model's reasoning onto one OpenAI-compatible reasoning_effort field and maps each upstream native shape (Anthropic budgets, DeepSeek/GLM toggles, OpenAI effort) behind it. Its catalog advertises only whether a model reasons, not per-model effort levels, so stop deriving per-model control sets from lab/peer files and advertise the host's accepted enum for every reasoning-capable model. This removes the lab/peer indexing, the DeepSeek toggle special-case, and the always-on [] path. No reasoning model publishes [] anymore; DeepSeek is treated like every other relayed model since the gateway maps effort. Each generated reasoning TOML now carries a leading note explaining that the effort list is Neosantara's mapped request surface, not the upstream provider's API, so reviewers understand the divergence. Verified live via the AI SDK (streaming): the host accepts none|minimal|low|medium|high|xhigh and rejects max (HTTP 400). Future-proof: an optional catalog reasoning_efforts array is consumed automatically (filtered to the host enum) when Neosantara starts publishing per-model levels; until then the full enum is used. Capability flags remain catalog-driven, so a model the lab ships as a reasoner stays reasoning=false until the neosantara catalog advertises it.
Action items
|
…easoning Rework the sync to consume Neosantara's single models.dev / LLM Gateway shaped catalog (/v1/catalog) instead of merging /v1/models + /v1/public/ pricing. The gateway now performs the host-specific work (USD-per-token pricing, capability flags, and the mapped reasoning surface), so the sync is a thin, override-only translation. Reasoning options are per-model, derived from each model's real control surface reported by the catalog and mapped with the LLM Gateway convention: [] = always-on (no caller control), ["none"] = on/off toggle, otherwise graded effort. No more uniform full-enum dump. Efforts are intersected with the host ladder, which now runs up to `max` (GLM-5.x and DeepSeek V4 use it). Toggle entries carry the required leading wire comment. Kept: the 100k+ context / function-calling / image-model filters and the base-model aliasing. Dropped: the two-endpoint merge, IDR conversion, discount handling, and FX header (all now upstream). Regenerated the provider TOMLs; re-running the sync is idempotent and `bun validate` passes.
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
|
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 any promotional discount, 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 (by id, or a small alias map for renamed generations), 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
reasoning_effortfield and reports each model's real control surface in the catalog. The sync maps it with the LLM Gateway convention:reasoning_effortsreasoning_options[][]— always‑on, no caller control["none"][{ type = "toggle" }](with the required leading wire comment)[{ type = "effort", values = … }]Levels are intersected with the host ladder
none·minimal·low·medium·high·xhigh·max. Examples:glm-4.5-flash= toggle;glm-4.7-flash/*V= always‑on (forced thinking);deepseek-v4-*=none/high/max;glm-5.3-flash=low/high/max(forced);gpt-5.x=none…xhigh;gemini-3.x=none/low/medium/high. Current split: 29 effort, 4 always‑on, 1 toggle. These were derived from the gateway's provider adapters and, for the GLM free models, confirmed with live requests.Pricing
USD per million tokens, converted from the endpoint's per‑token USD strings. IDR conversion and promotional discounts are handled upstream by the gateway, so no currency logic lives in the sync. Free models keep
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.bun validatepasses.