# Chatoyant [![npm version](https://img.shields.io/npm/v/chatoyant.svg?style=flat-square)](https://www.npmjs.com/package/chatoyant) [![CI](https://img.shields.io/github/actions/workflow/status/Anonyfox/chatoyant/ci.yml?branch=main&label=CI&style=flat-square)](https://github.com/Anonyfox/chatoyant/actions/workflows/ci.yml) [![OCaml](https://img.shields.io/badge/OCaml-5.3%2B-ec6813?style=flat-square&logo=ocaml)](https://ocaml.org/) [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue?style=flat-square&logo=typescript)](https://www.typescriptlang.org/) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](https://opensource.org/licenses/MIT)
Chatoyant Logo
OCaml-first SDK for LLM providers with an Eio native runtime and a zero-runtime-dependency JavaScript package generated by Melange. Chatoyant provides typed provider clients, structured outputs, tool calling, streaming, token/cost accounting, and root-only npm exports. _chatoyant_ /shuh-TOY-uhnt/ - having a changeable lustre. ## Highlights - Native OCaml API built around Eio, result-returning calls, `.mli` contracts, and typed tool definitions. - Melange-generated npm package with one root import path, ESM and CommonJS entrypoints, colocated `.d.ts` and `.d.cts` declarations, and no runtime npm dependencies. - Unified `Chat` session API, one-shot text/data/stream shortcuts, tool calling, streaming accumulation, JSON roundtrip, and token/cost accounting. - Raw provider clients for OpenAI, Anthropic, xAI, Meta (Muse Spark), OpenRouter, and local OpenAI-compatible servers. - Standalone Draft 2020-12 JSON Schema parser/validator with OpenAI strict projection and typed OCaml codec generation. - Production-derived JS usage tests, OCaml native tests, TypeScript declaration checks, package metadata checks, and the official JSON Schema suite against the bundled npm package. ## Package Size Next To Other JS LLM Libraries Measured on 2026-10-27 with Node v20.19.2 and npm 11.21.1. Each case uses fresh production installs of registry latest versions. Install sizes count node_modules file bytes (1 MB = 1024 KB); bundles use a one-request script, esbuild 0.25 (minified ESM for Node 20), and gzip level 9. | SDK | Versions | Installed packages | node_modules | Minified bundle | Gzip | | --- | --- | ---: | ---: | ---: | ---: | | chatoyant | chatoyant@0.16.0 | 1 | 0.6 MB | 215.2 KB | 54.1 KB | | openai | openai@7.45.0 | 1 | 22.1 MB | 376.5 KB | 83.1 KB | | @anthropic-ai/sdk | @anthropic-ai/sdk@1.3.0 | 8 | 16.1 MB | 384.6 KB | 91.1 KB | | ai + @ai-sdk/openai | ai@7.0.154, @ai-sdk/openai@4.0.103 | 12 | 21.9 MB | 885.3 KB | 198.6 KB | | langchain + @langchain/openai | langchain@1.5.20, @langchain/core@1.3.0, @langchain/openai@1.7.1 | 25 | 76.5 MB | 1495.3 KB | 349.0 KB | Chatoyant has the smallest install and bundles in this sample; openai ties its installed package count. The openai and Anthropic cases each cover one provider; chatoyant includes all six provider adapters plus JSON Schema validation and token/cost tables. One root import still pulls all adapters into the bundle. See [Smallest JavaScript LLM SDK: measured sizes](https://anonyfox.github.io/chatoyant/chatoyant/js_llm_sdk_size.html) for the method, source scripts, measured JSON and a typechecked example. Rerun with `node scripts/measure-sdk-size.mjs`. ## Provider Routing | Provider | Env var | Detection | | --- | --- | --- | | OpenAI | `OPENAI_API_KEY` | `gpt-*`, `o1-*`, `o3-*`, `o4-*`, `chatgpt-*` | | Anthropic | `ANTHROPIC_API_KEY` | `claude-*` | | xAI | `XAI_API_KEY` | `grok-*` | | Meta | `META_API_KEY` (or `MODEL_API_KEY`) | `muse-*` | | OpenRouter | `OPENROUTER_API_KEY` | Slash notation such as `openai/gpt-4o` | | Local | `LOCAL_BASE_URL` | Explicit `provider: "local"` or local fallback | Model presets are available for quick intent-based calls: `fast`, `cheap`, `balanced`, `best`, and `reasoning`. | Preset | OpenAI | Anthropic | xAI | Meta | | --- | --- | --- | --- | --- | | fast / cheap | gpt-6-luna | claude-haiku-5-5 | grok-4.20-0309-non-reasoning | muse-spark-1.3 | | balanced | gpt-6.1-sol | claude-sonnet-5-5 | grok-4.3 | muse-spark-1.3 | | best / reasoning | gpt-6-astra | claude-fable-5-1 | grok-4.7 | muse-spark-1.3 | Also recognized: gpt-6.1-terra (text/image input, 1,050,000 context tokens, 128,000 max output tokens), gpt-6-sol, gpt-5.6-cyber, gpt-5.5-cyber, gpt-5.3-codex, chat-latest, gpt-5-search-api, claude-mythos-5-1, claude-opus-5-5, claude-opus-5, grok-4.6, grok-build-0.1, muse-spark-1.3-contributor, muse-wonder-1.0, grok-imagine-image-2.0, grok-imagine-image-quality, grok-imagine-video-1.5, and grok-imagine-video-1.5-lite. OpenAI prices use the short-context tier (up to 272K input tokens); higher long-context rates are not modelled. GPT-5.6 Sol uses OpenAI's promotional price, at least through 2026-11-21. Haiku 5.5 prompts above 100K tokens and Grok Build prompts at or above 200K tokens have higher rates not modelled here. Retired on first-party APIs (ids still resolve for pricing/detection): claude-opus-4-1, claude-opus-4, claude-sonnet-4, claude-3-5-haiku/claude-3-*; xAI no longer lists grok-4-1-fast-*, grok-code-fast-1, grok-3*, grok-4-0709. ## OCaml Quick Start ```ocaml open Chatoyant let () = Eio_main.run @@ fun env -> let ai = Chatoyant.openai ~model:"gpt-6-luna" env in match Chatoyant.gen_text ai "Say hello in three words." with | Ok text -> print_endline text | Error err -> prerr_endline (Chatoyant.Error.provider err) ``` Typed tools are ordinary modules. Comments become schema descriptions, `option` means optional, and the generated `tool` value plugs into a chat. ```ocaml module%tool Calculate = struct type operation = | Add | Divide type request = { operation : operation; (** Operation to apply. *) values : float list [@min_items 1]; (** Numbers to combine in order. *) } type answer = { result : float } (** Combine numbers with a typed arithmetic operation. *) let run : request -> (answer, string) result = fun { operation; values } -> match operation, values with | _, [] -> Error "at least one value is required" | Add, values -> Ok { result = List.fold_left ( +. ) 0. values } | Divide, first :: rest -> List.fold_left (fun acc value -> Result.bind acc (fun n -> if value = 0. then Error "division by zero" else Ok (n /. value))) (Ok first) rest |> Result.map (fun result -> { result }) end let () = Eio_main.run @@ fun env -> let ai = Chatoyant.openai ~model:"gpt-6-luna" ~tools:[ Calculate.tool ] env in ignore (Chatoyant.gen_text ai "Divide 83 by 3.") ``` For the full native guide, see [OCAML.md](OCAML.md). ## JavaScript Quick Start ```ts import { Chat, Schema, createTool, genData, genStream, genText } from "chatoyant"; const text = await genText("What is 2+2?", { model: "fast", system: "Return short answers.", }); class Person extends Schema { name = Schema.String({ description: "Person name" }); age = Schema.Integer({ minimum: 0 }); } const person = await genData("Extract: Alice is 30 years old", Person); for await (const chunk of genStream("Write a haiku about typed APIs.")) { process.stdout.write(chunk); } const lookup = createTool({ name: "lookup", description: "Lookup data", parameters: { q: Schema.String({ minLength: 1 }) }, async execute({ args }) { return { found: args.q }; }, }); const chat = new Chat({ model: "gpt-4o" }); chat.system("Use tools when useful.").user("Find needle").addTool(lookup); console.log(await chat.generate()); ``` All JavaScript imports come from the package root: ```ts import { Chat, OpenAI, OpenRouter, Tokens, JsonSchema, genText } from "chatoyant"; ``` Former subpath imports from the TypeScript package intentionally move to this root surface. See [JAVASCRIPT.md](JAVASCRIPT.md). ## Build And Test From the repository root: ```bash make build # OCaml + Melange build, then the esbuild npm bundle make test # native tests, JS parity tests, tsc, and the JSON Schema suite make check # the full release gate make help # list all targets ``` `make check` builds and tests the native OCaml package first, emits Melange ESM, bundles the npm package with esbuild, runs Node's native tests against `dist/index.js`, checks `dist/index.d.ts` with `tsc`, runs the pinned official JSON Schema suite through the bundled package, lints opam metadata, checks local documentation links, verifies package metadata, and dry-packs the npm artifact. See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, the project layout, and the release process, and [CHANGELOG.md](CHANGELOG.md) for release history. ## Support If this package helps your project, consider sponsoring its maintenance: [GitHub Sponsors](https://github.com/sponsors/Anonyfox). [Anonyfox](https://anonyfox.com) | [MIT License](LICENSE)