Skip to content

Build1 publisher3 min readPublished

Mohdel 1.0 computes per-call cost from a price catalog you maintain yourself

The MIT-licensed Node gateway wraps thirteen cloud providers and local servers in one call shape, and the dollar figure it returns for each call comes out of a catalog your own team transcribes from provider pricing pages.

The Engineer · Build desk

Illustration accompanying Mohdel 1.0 computes per-call cost from a price catalog you maintain yourself

What happened

  • mohdel 1.0.0 shipped this week as an MIT-licensed self-hosted LLM gateway and SDK for Node, and its author says it runs the inference layer of a document analysis product with hundreds of thousands of users.
  • It covers thirteen named cloud providers plus a local provider for OpenAI-compatible servers, all behind one call where changing provider means changing one string.
  • There are two deployment paths, an in-process import and a cross-process gate reached over a unix socket, and moving between them is configuration rather than code.
  • Prices come from a user-owned catalog that a coding agent transcribes into a candidate file, which a check command validates and an apply command lands only after printing a full diff.
  • Setting OTEL_EXPORTER_OTLP_ENDPOINT emits spans following the GenAI semantic conventions, with mohdel.cost and mohdel.time_to_first_token_ms added on top.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • decision A Node team that wanted cost accounting across providers previously chose between a Python sidecar to operate, a SaaS router inside the request path, or a pile of provider SDKs; that choice now has a fourth entry, and the question becomes who keeps the prices current.
  • cost Owning the catalog means owning a transcription job with no upstream feed. Every provider price change leaves your cost numbers quietly wrong until a person reads the pricing page again.
  • constraint With no default endpoint and no per-call override on the local provider, a misconfigured local call fails instead of spending money at a cloud vendor. It also means every local target has to be configured explicitly before it can be used.
  • capability Because the app-to-gate wire is documented NDJSON with validated reference clients, a Lua or OCaml service can use the same gateway, cost fields and telemetry without a JavaScript runtime in its request path.

The two code samples in the release post can be checked against each other. The result example reports 42 input tokens, 128 output tokens and a cost of 0.002046 USD [5]. The catalog entry for the same model lists inputPrice 3 and outputPrice 15 [13]. Read those as dollars per million tokens: 42 x 3 / 1,000,000 is 0.000126, and 128 x 15 / 1,000,000 is 0.00192. The sum is 0.002046 [22]. The post never names the unit.

That same shape documents outputTokens as visible output only, excluding thinking, with thinkingTokens as its own field [5]. The catalog example carries no price for thinking tokens, and nothing in the post says whether cost adds them at the output rate [23]. On a reasoning model, that gap decides whether result.cost is a number you can bill a customer from.

The adapter layer absorbs the differences that normally leak into application code: max_tokens versus max_output_tokens, a top-level system field versus instructions, and five different usage shapes [6].

Catalog upkeep is what that USD figure costs. No provider exposes prices as data; according to the release post the source of truth is a marketing page with a table and a footnote about cached reads [15]. So the cost field is downstream of somebody's HTML. The handling is procedural: the coding agent writes a candidate file and never the catalog itself, check validates schema, types and required fields, and apply prints a full diff and waits [16]. Entries record the URL the numbers came from and the date they were read, and the brief tells the agent to leave a field out rather than guess it [17]. OpenRouter needs none of this, because it publishes per-token prices in its own model list, and setup offers to add every free model in one keystroke [18].

I would want the cross-process path in anything long-running. A Rust supervisor called thin-gate owns a pool of session subprocesses, the app talks to it over a unix socket, and the provider SDKs run outside the app process [10]. An adapter that hangs, leaks or panics takes down one session; the supervisor respawns it and the caller gets a recoverable error [10]. The in-process path has no supervisor, and the post recommends it for scripts, CLI tools, tests and single-process services [9]. Running the gate also gets you sessions alive and respawned, calls by provider and status, a call-duration histogram, and cooldown, quota and policy rejections, with per-model and per-provider rate limits living in the catalog [20].

The operating history is the strongest thing in the post and the hardest to use. It is the author's own account of one deployment, and the post carries no measurement and no feature-by-feature comparison with LiteLLM [24]. For that history to tell you anything about your own service, the providers you call have to sit among the thirteen it names [3], and your call types have to be the ones routed through the single call shape, which the post lists as tool calls, streaming, vision and speech to text [7].

What to watch

  • Whether any provider beyond OpenRouter starts publishing per-token prices as data, which would remove the transcription step entirely.
  • Whether the NDJSON wire and its conformance fixtures get versioned, so the Lua, Gleam, Rust and OCaml clients survive future releases.
  • Whether deployments outside the author's own product report on the gate path under load.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories