{"_id":"@company786/meridian-blue-sdk","_rev":"9-5700632f36d1ecbe2eab43b28e426fe9","name":"@company786/meridian-blue-sdk","dist-tags":{"latest":"1.0.7"},"versions":{"0.1.0":{"name":"@company786/meridian-blue-sdk","version":"0.1.0","keywords":["meridian-blue","ai","llm","sdk","openai","groq","mistral"],"author":{"name":"Meridian Blue"},"license":"MIT","_id":"@company786/meridian-blue-sdk@0.1.0","maintainers":[{"name":"company786","email":"company.abidullah786@gmail.com"}],"dist":{"shasum":"d887414629fe06daf02ed1ba932317ba4e7e3b68","tarball":"https://registry.npmjs.org/@company786/meridian-blue-sdk/-/meridian-blue-sdk-0.1.0.tgz","fileCount":26,"integrity":"sha512-cpyN1yGAfkTndABK1/UWyVsbhSJm9Ci8DHzagxe/zDrfTNVbMzkbuoPsAASIAsDhbjlq/ALt/A0aBl+HqxG92A==","signatures":[{"sig":"MEUCIQDJuUUq7DHUpo9jbfkwVEVrA4jdFjUX2cF/i/VYmLtmWQIgDPVTvb3MoeGjqeRzMizdoaIixtRQK4Gktz0/+5MGAPA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":29084},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"d88f0a40fbc8e479c76936f8795734ae78820e22","scripts":{"dev":"tsc --watch","build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"company786","email":"company.abidullah786@gmail.com"},"_npmVersion":"11.8.0","description":"Official TypeScript/JavaScript SDK for the Meridian-Blue API","directories":{},"_nodeVersion":"24.13.1","dependencies":{"axios":"^1.7.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/meridian-blue-sdk_0.1.0_1775564070415_0.4573020759007982","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@company786/meridian-blue-sdk","version":"0.2.0","keywords":["meridian-blue","ai","llm","sdk","openai","groq","mistral"],"author":{"name":"Meridian Blue"},"license":"MIT","_id":"@company786/meridian-blue-sdk@0.2.0","maintainers":[{"name":"company786","email":"company.abidullah786@gmail.com"}],"dist":{"shasum":"df94397c705ceaa539ca52bdb30ab287c624257c","tarball":"https://registry.npmjs.org/@company786/meridian-blue-sdk/-/meridian-blue-sdk-0.2.0.tgz","fileCount":26,"integrity":"sha512-yGV2p61+2Fsaz5WtDn2dv13/w7UYTmcJmp6WwI3po1gBVs0irKSyx06DWDrNWeU+5sSh55NosHEDwGg+g5Svcg==","signatures":[{"sig":"MEQCICUQxOoO3+kNoPody8ouVnmTLzI3MPMisxJ0bYm29d9nAiBcjhHS8m22kiULvR0QqujBALq5qfpJH5HVAnw3CWDRMQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":29015},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"d88f0a40fbc8e479c76936f8795734ae78820e22","scripts":{"dev":"tsc --watch","build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"company786","email":"company.abidullah786@gmail.com"},"_npmVersion":"11.8.0","description":"Official TypeScript/JavaScript SDK for the Meridian-Blue API","directories":{},"_nodeVersion":"24.13.1","dependencies":{"axios":"^1.7.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/meridian-blue-sdk_0.2.0_1775648497905_0.799924874625966","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@company786/meridian-blue-sdk","version":"0.3.0","keywords":["meridian-blue","ai","llm","sdk","openai","groq","mistral"],"author":{"name":"Meridian Blue"},"license":"MIT","_id":"@company786/meridian-blue-sdk@0.3.0","maintainers":[{"name":"company786","email":"company.abidullah786@gmail.com"}],"dist":{"shasum":"915c592c7f2cbfbaf03cbfa69f520dfcb994bb70","tarball":"https://registry.npmjs.org/@company786/meridian-blue-sdk/-/meridian-blue-sdk-0.3.0.tgz","fileCount":26,"integrity":"sha512-Se0CsbWxPSkWYJiGswfpV7ODTyv+BUrfHrIRVMevpsu8gLlbx5hqLm9RTi2sJmsvTDBQB/PuDVbS+1/8f/g1sw==","signatures":[{"sig":"MEUCIFdZhXeCU6vj9RcOdboE3HVlJwQiNpFWZaRt7LsYW8u7AiEAgLBuYLYaXWuLnlshz0OVimWvoD59cI3zTIdNh236Kh4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":23628},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"fb3cc1b08a643dd273e92214633780fa78e9a871","scripts":{"dev":"tsc --watch","build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"company786","email":"company.abidullah786@gmail.com"},"_npmVersion":"11.8.0","description":"Official TypeScript/JavaScript SDK for the Meridian-Blue API","directories":{},"_nodeVersion":"24.13.1","dependencies":{"axios":"^1.7.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/meridian-blue-sdk_0.3.0_1775820521142_0.8302075202712613","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@company786/meridian-blue-sdk","version":"1.0.0","keywords":["meridian-blue","ai","llm","sdk","openai","groq","mistral"],"author":{"name":"Meridian Blue"},"license":"MIT","_id":"@company786/meridian-blue-sdk@1.0.0","maintainers":[{"name":"company786","email":"company.abidullah786@gmail.com"}],"dist":{"shasum":"9292685b4716b050943a012d3bed57ac5af78f81","tarball":"https://registry.npmjs.org/@company786/meridian-blue-sdk/-/meridian-blue-sdk-1.0.0.tgz","fileCount":38,"integrity":"sha512-rimwKg2/s+Yw5qj1CghRmdw8X5YxA6fq3ggFuCZCic+66+qArj1cLFJ+RjPcErLYVuq5VyCAn46TUhCijYt86g==","signatures":[{"sig":"MEUCIQD8NUKBuOZBSAN5rHUKKB45YRUzx+bq5/XvmMwAbVcmNgIgZX8oNhPRLrMKVpRLn14y7+AiOyjO6w3dZA6joe3Eh08=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":47550},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"4d0d54205848f1b1c7dc0dc9af8d882411e53fca","scripts":{"dev":"tsc --watch","build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"company786","email":"company.abidullah786@gmail.com"},"_npmVersion":"11.8.0","description":"Official TypeScript/JavaScript SDK for the Meridian-Blue API","directories":{},"_nodeVersion":"24.13.1","dependencies":{"axios":"^1.7.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/meridian-blue-sdk_1.0.0_1775821207235_0.19925649952557878","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@company786/meridian-blue-sdk","version":"1.0.2","keywords":["meridian-blue","ai","llm","sdk","openai","groq","mistral"],"author":{"name":"Meridian Blue"},"license":"MIT","_id":"@company786/meridian-blue-sdk@1.0.2","maintainers":[{"name":"company786","email":"company.abidullah786@gmail.com"}],"dist":{"shasum":"4da551c592aa2277055ee8d9e75c934460e8bc86","tarball":"https://registry.npmjs.org/@company786/meridian-blue-sdk/-/meridian-blue-sdk-1.0.2.tgz","fileCount":38,"integrity":"sha512-tttq+/RfMOyUmS0LKpArrujI5GWSQJjqfPm9N74jk2PV0SC3YmuCcnXj6JxxCnLQrrEOBbZYnHZoUr/9MTgxKg==","signatures":[{"sig":"MEQCID8fpFS6yTIfqE8ubTERVgbi2eh/yG3gNpQGtbxS85DJAiABZbYUG9j+jDmVaiz89vSWc+hp+7UePg2yB/GTywm4Vg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":66247},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"8ef535f36535661acec537edcb2a36dfcf604ee6","scripts":{"dev":"tsc --watch","build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"company786","email":"company.abidullah786@gmail.com"},"_npmVersion":"11.8.0","description":"Official TypeScript/JavaScript SDK for the Meridian-Blue API","directories":{},"_nodeVersion":"24.13.1","dependencies":{"axios":"^1.7.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/meridian-blue-sdk_1.0.2_1776686041713_0.7114111361310307","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"@company786/meridian-blue-sdk","version":"1.0.4","keywords":["meridian-blue","ai","llm","sdk","openai","groq","mistral"],"author":{"name":"Meridian Blue"},"license":"MIT","_id":"@company786/meridian-blue-sdk@1.0.4","maintainers":[{"name":"company786","email":"company.abidullah786@gmail.com"}],"dist":{"shasum":"47e67258efe1cee334b857beb47f93e17b4ab09b","tarball":"https://registry.npmjs.org/@company786/meridian-blue-sdk/-/meridian-blue-sdk-1.0.4.tgz","fileCount":38,"integrity":"sha512-ZmkSiR+NJj12FqW6IqrLd/mWoqXSN7z1EU8OePXpSA0yy/KARwg5oMbd/nhUmLC7s2W8iZIfAWrILp8sC6uFBg==","signatures":[{"sig":"MEUCIQCcZmwpEwG53PdWkB0DIVo6G3G9DRiMjPpGYYBAyt5RKQIgcGilyvn3egvUEbQGWnKEhWo/WvxHSHcSZKog1i67Jhs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":75129},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"99fa7745ae2deaa39bcdc6e418e7036af7967aa3","scripts":{"dev":"tsc --watch","build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"company786","email":"company.abidullah786@gmail.com"},"_npmVersion":"11.8.0","description":"Official TypeScript/JavaScript SDK for the Meridian-Blue API","directories":{},"_nodeVersion":"24.13.1","dependencies":{"axios":"^1.7.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/meridian-blue-sdk_1.0.4_1778651441149_0.5983483753749019","host":"s3://npm-registry-packages-npm-production"}},"1.0.5":{"name":"@company786/meridian-blue-sdk","version":"1.0.5","keywords":["meridian-blue","ai","llm","sdk","openai","groq","mistral"],"author":{"name":"Meridian Blue"},"license":"MIT","_id":"@company786/meridian-blue-sdk@1.0.5","maintainers":[{"name":"company786","email":"company.abidullah786@gmail.com"}],"dist":{"shasum":"80329e7f59a7a518b5eb8cf93c8c5299d8af3509","tarball":"https://registry.npmjs.org/@company786/meridian-blue-sdk/-/meridian-blue-sdk-1.0.5.tgz","fileCount":38,"integrity":"sha512-TRcZgvajXC+2GrBiosaFcV1APr+IkN7Novg/xo2YzvYYJio7sMBP0moebIEh2ct7qiROyjKdcQK6Hg+K8pUb0A==","signatures":[{"sig":"MEUCIGIFDsUvr/4kR189B+wyjhfPJjwRJQ7pgOzFLMieHllnAiEAgySNPgQ5GjrpVjleB5CS44rbHXNxxmi4+qHBVDzPLX4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":76460},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"99fa7745ae2deaa39bcdc6e418e7036af7967aa3","scripts":{"dev":"tsc --watch","build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"company786","email":"company.abidullah786@gmail.com"},"_npmVersion":"11.8.0","description":"Official TypeScript/JavaScript SDK for the Meridian-Blue API","directories":{},"_nodeVersion":"24.13.1","dependencies":{"axios":"^1.7.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/meridian-blue-sdk_1.0.5_1778664930390_0.3196501747521201","host":"s3://npm-registry-packages-npm-production"}},"1.0.6":{"name":"@company786/meridian-blue-sdk","version":"1.0.6","keywords":["meridian-blue","ai","llm","sdk","openai","groq","mistral"],"author":{"name":"Meridian Blue"},"license":"MIT","_id":"@company786/meridian-blue-sdk@1.0.6","maintainers":[{"name":"company786","email":"company.abidullah786@gmail.com"}],"dist":{"shasum":"3db58ac2a7cd7edae2d18ed9879bff69e7246a65","tarball":"https://registry.npmjs.org/@company786/meridian-blue-sdk/-/meridian-blue-sdk-1.0.6.tgz","fileCount":38,"integrity":"sha512-MRpFOpVs1M1opZMg0JMxYUIqha34iwC+xl2nP3Q4MiJRaghKBstOyfo4IIdTh122QLoaX0TAajPHvxgZQoPvDA==","signatures":[{"sig":"MEQCIEcOj4OL32B+kGg1oKvSkCYnTZGZhECPkqKsEUdRdCDMAiAvypXbfpwajYnICQsOcVKbX/R1XndJOqzsgzceCAM6yA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":76486},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"99fa7745ae2deaa39bcdc6e418e7036af7967aa3","scripts":{"dev":"tsc --watch","build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"company786","email":"company.abidullah786@gmail.com"},"_npmVersion":"11.8.0","description":"Official TypeScript/JavaScript SDK for the Meridian-Blue API","directories":{},"_nodeVersion":"24.13.1","dependencies":{"axios":"^1.7.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.0"},"_npmOperationalInternal":{"tmp":"tmp/meridian-blue-sdk_1.0.6_1778674745687_0.3886361573219168","host":"s3://npm-registry-packages-npm-production"}},"1.0.7":{"name":"@company786/meridian-blue-sdk","version":"1.0.7","description":"Official TypeScript/JavaScript SDK for the Meridian-Blue API","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"scripts":{"build":"tsc","dev":"tsc --watch","prepublishOnly":"npm run build"},"keywords":["meridian-blue","ai","llm","sdk","openai","groq","mistral"],"author":{"name":"Meridian Blue"},"license":"MIT","dependencies":{"axios":"^1.7.0"},"devDependencies":{"typescript":"^5.4.0"},"engines":{"node":">=18.0.0"},"gitHead":"c1a78037a79d1726ad32747ddb5bd56271df3423","_id":"@company786/meridian-blue-sdk@1.0.7","_nodeVersion":"24.13.1","_npmVersion":"11.8.0","dist":{"integrity":"sha512-knbmUu89CF9Cj5zQ5W8buPjzicxxxFdZBzw2ePKMfXIHVhjO7Yx8C2rIfQWAVWOuytI99O2vYSM//lV84uS0xg==","shasum":"e09b1a09bf692444468f9f2afb11aef823315301","tarball":"https://registry.npmjs.org/@company786/meridian-blue-sdk/-/meridian-blue-sdk-1.0.7.tgz","fileCount":38,"unpackedSize":76444,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIHwdiwS4eKAoaZ77iuSPhZUiQTq2+V669tt3g2mej2X9AiEAva6t38SOjOB6BJmwbm+7yzNKv4eLVt8EdUS4nUAkgJI="}]},"_npmUser":{"name":"company786","email":"company.abidullah786@gmail.com"},"directories":{},"maintainers":[{"name":"company786","email":"company.abidullah786@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/meridian-blue-sdk_1.0.7_1778675054474_0.8801359956944492"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-07T12:14:30.328Z","modified":"2026-05-13T12:24:14.729Z","0.1.0":"2026-04-07T12:14:30.609Z","0.2.0":"2026-04-08T11:41:38.048Z","0.3.0":"2026-04-10T11:28:41.284Z","1.0.0":"2026-04-10T11:40:07.372Z","1.0.2":"2026-04-20T11:54:01.853Z","1.0.4":"2026-05-13T05:50:41.316Z","1.0.5":"2026-05-13T09:35:30.531Z","1.0.6":"2026-05-13T12:19:05.858Z","1.0.7":"2026-05-13T12:24:14.642Z"},"author":{"name":"Meridian Blue"},"license":"MIT","keywords":["meridian-blue","ai","llm","sdk","openai","groq","mistral"],"description":"Official TypeScript/JavaScript SDK for the Meridian-Blue API","maintainers":[{"name":"company786","email":"company.abidullah786@gmail.com"}],"readme":"# Meridian Blue SDK\n\nOfficial TypeScript / JavaScript client for the Meridian Blue proxy API.\n\nMeridian Blue is an OpenAI-compatible gateway that routes each request across\nmultiple upstream providers (OpenAI, Anthropic, Groq, Gemini, Mistral,\nOpenRouter, Cloudflare Workers AI, and others), falls back automatically when\na provider fails, and layers EU AI Act compliance (risk classification,\npurpose limitation, audit trail) on top.\n\nThis package is a thin, typed wrapper around the HTTP API. Anything the\nSDK documents is implemented on the server; everything not documented here\nis not yet supported.\n\n---\n\n## Table of Contents\n\n- [Install](#install)\n- [Quick start](#quick-start)\n- [Authentication](#authentication)\n- [Chat completions](#chat-completions)\n  - [Single model](#single-model)\n  - [Model fallback chain (`models[]`)](#model-fallback-chain-models)\n  - [Multimodal content](#multimodal-content)\n  - [Automatic truncation](#automatic-truncation)\n  - [EU AI Act compliance fields](#eu-ai-act-compliance-fields)\n  - [Streaming](#streaming)\n- [Embeddings](#embeddings)\n- [Image generation](#image-generation)\n- [Audio — text-to-speech](#audio--text-to-speech)\n- [Audio — transcription](#audio--transcription)\n- [Response shape](#response-shape)\n- [Response headers](#response-headers)\n- [Error handling](#error-handling)\n- [Examples in other languages](#examples-in-other-languages)\n- [Publishing a new version](#publishing-a-new-version)\n- [Testing against the live server](#testing-against-the-live-server)\n\n---\n\n## Install\n\n```bash\nnpm install @company786/meridian-blue-sdk\n# or\npnpm add @company786/meridian-blue-sdk\n# or\nyarn add @company786/meridian-blue-sdk\n```\n\nRequirements: Node.js 18+ and an ESM-capable toolchain. The package is\ndistributed as ESM only (`\"type\": \"module\"`).\n\n---\n\n## Quick start\n\n```ts\nimport { MeridianBlue } from \"@company786/meridian-blue-sdk\";\n\nconst client = new MeridianBlue({\n  apiKey: process.env.MERIDIAN_BLUE_API_KEY!,\n});\n\nconst response = await client.chat.completions({\n  model: \"claude-opus-4-7\",\n  messages: [{ role: \"user\", content: \"Explain JWTs in one sentence.\" }],\n});\n\nconsole.log(response.choices[0].message.content);\nconsole.log(`Cost: ${response.billing.cost} credits`);\nconsole.log(`Balance: ${response.billing.balanceAfter}`);\n```\n\n---\n\n## Authentication\n\nCreate an API key in the dashboard (`/dashboard/api-keys`) and pass it to\nthe constructor. Keys are prefixed with `kp_live_`.\n\n```ts\nconst client = new MeridianBlue({\n  apiKey: \"kp_live_...\",\n  baseUrl: \"https://api.meridianblue.ai\", // optional — this is the default\n  timeout: 30_000,                        // optional — default 30s\n});\n```\n\nConfiguration options:\n\n| Field     | Type   | Default                       | Description                   |\n| --------- | ------ | ----------------------------- | ----------------------------- |\n| `apiKey`  | string | —                             | Required. Your `kp_live_` key |\n| `baseUrl` | string | `https://api.meridianblue.ai`  | Override for self-hosted      |\n| `timeout` | number | `30000`                       | HTTP timeout in ms            |\n\n---\n\n## Chat completions\n\n`POST /api/v1/chat/completions` — OpenAI-compatible, with Meridian extensions.\n\n### Full parameter reference\n\n| Field              | Type          | Required | Server behavior |\n| ------------------ | ------------- | -------- | --------------- |\n| `model`            | string        | one of   | Single model id. Used by the router, stripped before upstream call. |\n| `models`           | string[]      | one of   | Ordered fallback chain. Index 0 tried first. Stripped before upstream call. |\n| `messages`         | ChatMessage[] | yes      | Standard OpenAI messages array. Supports multimodal content. |\n| `temperature`      | number        | no       | Forwarded to upstream. |\n| `max_tokens`       | number        | no       | Forwarded to upstream. |\n| `stream`           | boolean       | no       | Enables Server-Sent Events streaming. See [Streaming](#streaming). |\n| `auto_truncate`    | boolean       | no       | If true, oversized prompts are trimmed server-side. Stripped before upstream call. |\n| `purpose`          | string        | no\\*     | Declared purpose for EU AI Act risk classification. Stripped. |\n| `user_consent_id`  | string        | no\\*     | Consent token. Required by server when purpose is high-risk. Stripped. |\n| `end_user_id`      | string        | no       | Stable ID for the downstream user. Hashed + audited. Stripped. |\n| `deployer_context` | string        | no       | Deployment hint (e.g. `internal_rag`). Feeds the risk classifier. Stripped. |\n| any other field    | —             | no       | Forwarded to upstream (e.g. `top_p`, `presence_penalty`, `tools`). |\n\n\\* Required only when the server's policy layer classifies the request as\nhigh-risk. Low-risk requests can omit them.\n\n*\"Stripped\"* means the server consumes the field internally and removes\nit before forwarding to the upstream provider, so providers that reject\nunknown top-level fields (Groq, most OpenAI-compat vendors) do not 400.\n\nExactly one of `model` or `models` must be provided.\n\n### Single model\n\n```ts\nconst response = await client.chat.completions({\n  model: \"claude-opus-4-7\",\n  messages: [\n    { role: \"system\", content: \"You are a concise assistant.\" },\n    { role: \"user\", content: \"What is 2+2?\" },\n  ],\n  temperature: 0.2,\n  max_tokens: 100,\n});\n```\n\n### Model fallback chain (`models[]`)\n\nThe proxy tries each model in order. If every provider for index 0 fails,\nit advances to index 1, and so on. The proxy **never** substitutes a model\nthat isn't in this list.\n\n```ts\nconst response = await client.chat.completions({\n  models: [\n    \"openai/gpt-4o\",\n    \"anthropic/claude-3-5-sonnet\",\n    \"groq/llama-3.3-70b\",\n  ],\n  messages: [{ role: \"user\", content: \"Summarise the Rome Statute.\" }],\n});\n\nif (response.billing.isFallback) {\n  console.log(`Served from fallback model: ${response.model}`);\n}\n```\n\nNote: the free-tier daily quota applies to the *entire chain*. If a user\nis out of free requests, every entry in `models` must be paid-eligible\nand the user must have enough credit, or the request is rejected.\n\n### Multimodal content\n\n`messages[].content` may be either a plain string or an array of content\nparts. Supported part types:\n\n| Type          | Shape                                                    | Notes |\n| ------------- | -------------------------------------------------------- | ----- |\n| `text`        | `{ type: \"text\", text }`                                 | |\n| `image_url`   | `{ type: \"image_url\", image_url: { url, detail? } }`     | URL or `data:image/...;base64,...` |\n| `input_audio` | `{ type: \"input_audio\", input_audio: { data, format } }` | Base64 audio, format one of `wav`/`mp3`/`flac`/`webm`/`ogg` |\n| `file`        | `{ type: \"file\", file: { url, mime_type } }`             | PDFs, documents |\n| `video`       | `{ type: \"video\", video: { url, mime_type } }`           | Routed through native Gemini when required |\n\n```ts\nconst response = await client.chat.completions({\n  model: \"openai/gpt-4o\",\n  messages: [\n    {\n      role: \"user\",\n      content: [\n        { type: \"text\", text: \"What is in this image?\" },\n        {\n          type: \"image_url\",\n          image_url: { url: \"https://example.com/cat.jpg\", detail: \"high\" },\n        },\n      ],\n    },\n  ],\n});\n```\n\nMultimodal requests (image/video) count against a separate per-user daily\ncap (5/day on the free tier) on top of the regular request counter.\n\n### Automatic truncation\n\nIf a prompt exceeds the selected model's context window, the default is\nto reject it. Setting `auto_truncate: true` makes the server apply a\nmiddle-out truncation strategy and reports what was removed.\n\n```ts\nconst response = await client.chat.completions({\n  model: \"claude-opus-4-7\",\n  messages: veryLongChatHistory,\n  auto_truncate: true,\n});\n\nif (response.truncation?.applied) {\n  console.log(\n    `Truncated ${response.truncation.messages_removed} messages ` +\n    `(${response.truncation.original_tokens} → ${response.truncation.final_tokens} tokens)`,\n  );\n}\n```\n\n### EU AI Act compliance fields\n\nThe server classifies every request against risk categories drawn from\nthe EU AI Act. For high-risk categories (medical advice, legal advice,\nbiometrics, social scoring, etc.) the server requires declared purpose\nand consent.\n\n```ts\nconst response = await client.chat.completions({\n  model: \"openai/gpt-4o\",\n  messages: [\n    { role: \"user\", content: \"Given these symptoms, what could be wrong?\" },\n  ],\n  purpose: \"medical_advice_triage\",\n  user_consent_id: \"consent_9f3b...\",\n  end_user_id: \"user_42\",\n  deployer_context: \"telehealth_pre_triage_chatbot\",\n});\n\nconsole.log(response.risk_classification.level);        // \"high\"\nconsole.log(response.risk_classification.triggered_rules);\nconsole.log(response.user_notice?.text);                // user-facing disclosure\nconsole.log(response.explainability.human_readable);\n```\n\nPurpose / consent are only enforced for high-risk categories — omitting\nthem on a low-risk chat request is fine.\n\n### Streaming\n\n`stream: true` produces a Server-Sent Events response (`Content-Type:\ntext/event-stream`). The typed `client.chat.completions()` method does not\nparse SSE; use raw fetch or the underlying axios instance against\n`/api/v1/chat/completions`:\n\n```ts\nconst res = await fetch(\"https://api.meridianblue.ai/api/v1/chat/completions\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.MERIDIAN_BLUE_API_KEY}`,\n    \"Content-Type\": \"application/json\",\n    Accept: \"text/event-stream\",\n  },\n  body: JSON.stringify({\n    model: \"claude-opus-4-7\",\n    messages: [{ role: \"user\", content: \"Stream a haiku about JWTs.\" }],\n    stream: true,\n  }),\n});\n\nconst reader = res.body!.getReader();\nconst decoder = new TextDecoder();\nwhile (true) {\n  const { done, value } = await reader.read();\n  if (done) break;\n  process.stdout.write(decoder.decode(value));\n}\n```\n\nStreaming responses still include billing and compliance metadata; they\nare emitted as trailing `data:` events after the final token.\n\n---\n\n## Embeddings\n\n`POST /api/v1/embeddings`\n\n```ts\nconst response = await client.embeddings.create({\n  model: \"openai/text-embedding-3-small\",\n  input: [\"hello world\", \"another sentence\"],\n});\n\nconsole.log(response.data[0].embedding);   // number[]\nconsole.log(response._meridian.provider);  // upstream provider used\n```\n\n| Field             | Type                | Required | Notes |\n| ----------------- | ------------------- | -------- | ----- |\n| `model`           | string              | yes      | Embedding model id |\n| `input`           | string \\| string[]  | yes      | Text(s) to embed |\n| `encoding_format` | string              | no       | e.g. `\"float\"`, `\"base64\"` |\n| `dimensions`      | number              | no       | Only for models that accept it |\n\n---\n\n## Image generation\n\n`POST /api/v1/images/generations`\n\n```ts\nconst response = await client.images.generate({\n  model: \"openai/dall-e-3\",\n  prompt: \"a neon-lit cyberpunk street at night, photorealistic\",\n  size: \"1024x1024\",\n  quality: \"hd\",\n  response_format: \"url\",\n});\n\nconsole.log(response.data[0].url);\n```\n\n| Field             | Type   | Required | Notes |\n| ----------------- | ------ | -------- | ----- |\n| `model`           | string | yes      | |\n| `prompt`          | string | yes      | |\n| `n`               | number | no       | Number of images |\n| `size`            | string | no       | e.g. `\"1024x1024\"`, `\"1792x1024\"` |\n| `quality`         | string | no       | `\"standard\"` or `\"hd\"` |\n| `style`           | string | no       | `\"vivid\"` or `\"natural\"` |\n| `response_format` | string | no       | `\"url\"` or `\"b64_json\"` |\n\n---\n\n## Audio — text-to-speech\n\n`POST /api/v1/audio/speech` — returns raw binary audio.\n\n```ts\nimport { writeFileSync } from \"node:fs\";\n\nconst speech = await client.audio.speech({\n  model: \"openai/tts-1\",\n  input: \"Good morning. The system is nominal.\",\n  voice: \"nova\",\n  response_format: \"mp3\",\n});\n\nwriteFileSync(\"out.mp3\", Buffer.from(speech.data));\nconsole.log(speech.provider, speech.latencyMs);\n```\n\n| Field             | Type   | Required | Notes |\n| ----------------- | ------ | -------- | ----- |\n| `model`           | string | yes      | |\n| `input`           | string | yes      | Text to synthesize |\n| `voice`           | string | no       | e.g. `alloy`, `echo`, `fable`, `onyx`, `nova`, `shimmer` |\n| `response_format` | string | no       | `mp3`, `opus`, `aac`, `flac`, `wav` |\n| `speed`           | number | no       | 0.25 – 4.0 |\n\n---\n\n## Audio — transcription\n\n`POST /api/v1/audio/transcriptions`\n\n```ts\nimport { readFileSync } from \"node:fs\";\n\nconst base64 = readFileSync(\"recording.mp3\").toString(\"base64\");\n\nconst transcription = await client.audio.transcribe({\n  model: \"openai/whisper-1\",\n  file: base64,\n  language: \"en\",\n});\n\nconsole.log(transcription.text);\n```\n\n| Field             | Type   | Required | Notes |\n| ----------------- | ------ | -------- | ----- |\n| `model`           | string | yes      | |\n| `file`            | string | yes      | **Base64-encoded** audio content |\n| `language`        | string | no       | ISO 639-1 code |\n| `response_format` | string | no       | `json`, `text`, `srt`, `verbose_json`, `vtt` |\n| `temperature`     | number | no       | |\n\n---\n\n## Response shape\n\nEvery chat completion response includes both the standard OpenAI fields\nand the Meridian extensions:\n\n```ts\n{\n  // Standard OpenAI fields\n  id: \"chatcmpl-...\",\n  object: \"chat.completion\",\n  created: 1734567890,\n  model: \"claude-opus-4-7\",\n  choices: [{ index: 0, message: { role: \"assistant\", content: \"...\" }, finish_reason: \"stop\" }],\n  usage: { prompt_tokens: 23, completion_tokens: 41, total_tokens: 64 },\n\n  // Meridian extensions\n  billing: {\n    cost: 0.031,\n    balanceAfter: 9974.203,\n    isFallback: false,\n    latencyMs: 842,\n  },\n  risk_classification: {\n    level: \"minimal\",           // \"minimal\" | \"limited\" | \"high\" | \"prohibited\"\n    reason: \"general_query\",\n    triggered_rules: [],\n    requires_human_review: false,\n    auto_restricted: false,\n    classifier_version: \"1.0.0\",\n    confidence: 0.98,\n  },\n  explainability: {\n    model_selection_reason: \"Lowest-latency available provider meeting policy\",\n    risk_reasoning: \"No high-risk keywords detected.\",\n    human_readable: \"Routed to claude-opus-4-7 via OpenAI.\",\n  },\n  user_notice: { /* only present for high-risk requests */ },\n  truncation: { /* only present when auto_truncate applied */ },\n}\n```\n\n---\n\n## Response headers\n\nThe proxy sets several informational headers on every response. Read them\nvia the axios instance if you need them programmatically.\n\n| Header                        | Description |\n| ----------------------------- | ----------- |\n| `X-Meridian-Provider`         | Upstream provider that served the request |\n| `X-Meridian-Model`            | Provider's native model id |\n| `X-Meridian-Latency-Ms`       | Round-trip latency |\n| `X-Meridian-Fallback`         | `\"true\"` if a fallback was used |\n| `X-Meridian-Fallback-Count`   | Number of failed providers before success |\n| `X-Meridian-Fallback-Chain`   | Comma-separated provider(status) list |\n| `X-Meridian-Risk-Level`       | Risk classification level |\n| `X-Meridian-Policy-Warnings`  | Set when deployer policy warnings fire |\n| `X-Meridian-Override`         | Set when a manual routing override was applied |\n| `X-Meridian-Review`           | `\"advisory_logged\"` or `\"blocking\"` |\n| `X-Meridian-Cache`            | `\"hit\"` or `\"miss\"` for cacheable requests |\n| `X-Meridian-Cache-Bypass`     | Reason the cache was skipped |\n| `Retry-After`                 | Seconds to wait (429 responses only) |\n\n---\n\n## Error handling\n\nThe SDK maps HTTP errors to typed exceptions. Use `instanceof` checks.\n\n```ts\nimport {\n  MeridianBlue,\n  AuthenticationError,\n  InsufficientBalanceError,\n  PermissionError,\n  NotFoundError,\n  RateLimitError,\n  APIError,\n  MeridianBlueError,\n} from \"@company786/meridian-blue-sdk\";\n\ntry {\n  await client.chat.completions({ /* ... */ });\n} catch (err) {\n  if (err instanceof RateLimitError) {\n    console.log(`Rate limited. Retry after ${err.retryAfterSeconds}s.`);\n  } else if (err instanceof InsufficientBalanceError) {\n    console.log(`Balance: ${err.currentBalance}, debt limit: ${err.debtLimit}`);\n  } else if (err instanceof AuthenticationError) {\n    console.log(\"API key invalid or missing.\");\n  } else if (err instanceof PermissionError) {\n    console.log(\"API key has been revoked.\");\n  } else if (err instanceof NotFoundError) {\n    console.log(\"Model not found or not enabled for your account.\");\n  } else if (err instanceof APIError) {\n    console.log(\"All upstream providers failed.\");\n  } else if (err instanceof MeridianBlueError) {\n    console.log(`Network / unknown error: ${err.message}`);\n  }\n}\n```\n\n| Class                        | HTTP status | Trigger |\n| ---------------------------- | ----------- | ------- |\n| `AuthenticationError`        | 401         | Missing or malformed API key |\n| `InsufficientBalanceError`   | 402         | Balance below the debt cap and model is paid |\n| `PermissionError`            | 403         | API key revoked, or free quota exhausted |\n| `NotFoundError`              | 404         | Unknown model, or model not enabled for tenant |\n| `RateLimitError`             | 429         | Per-key rate limit exceeded (carries `retryAfterSeconds`) |\n| `APIError`                   | 500 / 502   | All providers in the fallback chain failed |\n| `MeridianBlueError`          | 0           | Network error / no HTTP response |\n\n---\n\n## Examples in other languages\n\nThe Meridian Blue server speaks standard HTTP/JSON, so any language with\nan HTTP client can use it. The SDK is provided for TS/JS; other languages\nuse raw HTTP.\n\n### Python (with `requests`)\n\n```python\nimport os\nimport requests\n\nBASE_URL = \"https://api.meridianblue.ai\"\nAPI_KEY = os.environ[\"MERIDIAN_BLUE_API_KEY\"]\n\nheaders = {\n    \"Authorization\": f\"Bearer {API_KEY}\",\n    \"Content-Type\": \"application/json\",\n}\n\n# Single model\nresp = requests.post(\n    f\"{BASE_URL}/api/v1/chat/completions\",\n    headers=headers,\n    json={\n        \"model\": \"claude-opus-4-7\",\n        \"messages\": [{\"role\": \"user\", \"content\": \"Hello\"}],\n    },\n    timeout=30,\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(data[\"choices\"][0][\"message\"][\"content\"])\nprint(\"Cost:\", data[\"billing\"][\"cost\"], \"credits\")\n\n# Fallback chain + compliance\nresp = requests.post(\n    f\"{BASE_URL}/api/v1/chat/completions\",\n    headers=headers,\n    json={\n        \"models\": [\n            \"openai/gpt-4o\",\n            \"anthropic/claude-3-5-sonnet\",\n            \"groq/llama-3.3-70b\",\n        ],\n        \"messages\": [{\"role\": \"user\", \"content\": \"Summarise the Rome Statute.\"}],\n        \"auto_truncate\": True,\n        \"purpose\": \"legal_research\",\n    },\n    timeout=60,\n)\ndata = resp.json()\nprint(\"Served by:\", data[\"model\"], \"fallback?\", data[\"billing\"][\"isFallback\"])\n```\n\n### Python (with the OpenAI SDK)\n\nMeridian Blue is OpenAI-compatible — you can point the official OpenAI\nPython SDK at it. Meridian extensions (`purpose`, `models`, etc.) go\nthrough as `extra_body`; responses still contain the extensions but the\nOpenAI SDK just ignores unknown fields.\n\n```python\nfrom openai import OpenAI\n\nclient = OpenAI(\n    api_key=os.environ[\"MERIDIAN_BLUE_API_KEY\"],\n    base_url=\"https://api.meridianblue.ai/api/v1\",\n)\n\nresp = client.chat.completions.create(\n    model=\"claude-opus-4-7\",\n    messages=[{\"role\": \"user\", \"content\": \"Hello\"}],\n    extra_body={\n        \"models\": [\"claude-opus-4-7\", \"groq/llama-3.3-70b\"],\n        \"auto_truncate\": True,\n    },\n)\nprint(resp.choices[0].message.content)\n# Meridian fields are present on the raw response:\nprint(resp.model_extra.get(\"billing\"))\n```\n\n### cURL\n\n```bash\n# Single model\ncurl -sS https://api.meridianblue.ai/api/v1/chat/completions \\\n  -H \"Authorization: Bearer $MERIDIAN_BLUE_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"model\": \"claude-opus-4-7\",\n    \"messages\": [{\"role\": \"user\", \"content\": \"Hello\"}]\n  }'\n\n# Fallback chain with compliance fields\ncurl -sS https://api.meridianblue.ai/api/v1/chat/completions \\\n  -H \"Authorization: Bearer $MERIDIAN_BLUE_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"models\": [\n      \"openai/gpt-4o\",\n      \"anthropic/claude-3-5-sonnet\",\n      \"groq/llama-3.3-70b\"\n    ],\n    \"messages\": [{\"role\": \"user\", \"content\": \"Summarise DORA.\"}],\n    \"purpose\": \"regulatory_research\",\n    \"deployer_context\": \"internal_compliance_tool\",\n    \"auto_truncate\": true\n  }'\n\n# Streaming\ncurl -N https://api.meridianblue.ai/api/v1/chat/completions \\\n  -H \"Authorization: Bearer $MERIDIAN_BLUE_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Accept: text/event-stream\" \\\n  -d '{\n    \"model\": \"claude-opus-4-7\",\n    \"messages\": [{\"role\": \"user\", \"content\": \"Stream a haiku.\"}],\n    \"stream\": true\n  }'\n\n# Embeddings\ncurl -sS https://api.meridianblue.ai/api/v1/embeddings \\\n  -H \"Authorization: Bearer $MERIDIAN_BLUE_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"model\": \"openai/text-embedding-3-small\",\n    \"input\": [\"hello world\"]\n  }'\n\n# Image generation\ncurl -sS https://api.meridianblue.ai/api/v1/images/generations \\\n  -H \"Authorization: Bearer $MERIDIAN_BLUE_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"model\": \"openai/dall-e-3\",\n    \"prompt\": \"a neon cyberpunk street\",\n    \"size\": \"1024x1024\"\n  }'\n```\n\n### Go (`net/http`)\n\n```go\npackage main\n\nimport (\n    \"bytes\"\n    \"encoding/json\"\n    \"io\"\n    \"net/http\"\n    \"os\"\n)\n\nfunc main() {\n    body, _ := json.Marshal(map[string]any{\n        \"model\":    \"claude-opus-4-7\",\n        \"messages\": []map[string]string{{\"role\": \"user\", \"content\": \"Hello\"}},\n    })\n    req, _ := http.NewRequest(\"POST\",\n        \"https://api.meridianblue.ai/api/v1/chat/completions\",\n        bytes.NewReader(body))\n    req.Header.Set(\"Authorization\", \"Bearer \"+os.Getenv(\"MERIDIAN_BLUE_API_KEY\"))\n    req.Header.Set(\"Content-Type\", \"application/json\")\n\n    resp, _ := http.DefaultClient.Do(req)\n    defer resp.Body.Close()\n    out, _ := io.ReadAll(resp.Body)\n    os.Stdout.Write(out)\n}\n```\n\n### Java (`HttpClient`)\n\n```java\nvar client = java.net.http.HttpClient.newHttpClient();\nvar body = \"\"\"\n  {\"model\":\"claude-opus-4-7\",\n   \"messages\":[{\"role\":\"user\",\"content\":\"Hello\"}]}\n\"\"\";\nvar req = java.net.http.HttpRequest.newBuilder()\n    .uri(java.net.URI.create(\"https://api.meridianblue.ai/api/v1/chat/completions\"))\n    .header(\"Authorization\", \"Bearer \" + System.getenv(\"MERIDIAN_BLUE_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .POST(java.net.http.HttpRequest.BodyPublishers.ofString(body))\n    .build();\nvar resp = client.send(req, java.net.http.HttpResponse.BodyHandlers.ofString());\nSystem.out.println(resp.body());\n```\n\n---\n\n## Publishing a new version\n\nThis package is published to npm under the scoped name\n`@company786/meridian-blue-sdk`. Scoped packages default to private on\nnpm — the first publish must use `--access public`.\n\n### One-time setup\n\n```bash\nnpm login                         # log in with an account that has\n                                  # publish rights on the @company786 org\nnpm whoami                        # sanity check\n```\n\n### Release flow\n\n```bash\n# 1. Make sure the working tree is clean and the build passes.\nnpm run build\n\n# 2. Bump the version (choose one).\nnpm version patch                 # 1.0.0 -> 1.0.1\nnpm version minor                 # 1.0.0 -> 1.1.0\nnpm version major                 # 1.0.0 -> 2.0.0\n\n# 3. Publish. `prepublishOnly` re-runs the build.\nnpm publish --access public\n\n# 4. Push the tag that `npm version` created.\ngit push origin main --tags\n```\n\nNotes:\n- The `files` field in `package.json` restricts the tarball to `dist/`,\n  so source `.ts` files are not shipped.\n- CI should run `npm run build` before publishing to catch type errors.\n- For a pre-release candidate, use `npm version prerelease --preid=rc` and\n  publish with `npm publish --tag next`.\n\n### Verifying a release\n\n```bash\n# See what would be published without actually publishing.\nnpm pack --dry-run\n\n# After publishing, install into a scratch project to verify.\nmkdir /tmp/mb-check && cd /tmp/mb-check\nnpm init -y\nnpm install @company786/meridian-blue-sdk\nnode --input-type=module -e \"\n  import('@company786/meridian-blue-sdk').then(m => console.log(Object.keys(m)));\n\"\n```\n\n---\n\n## Testing against the live server\n\nThe default `baseUrl` (`https://api.meridianblue.ai`) points at the\nproduction deployment. A minimal smoke test:\n\n```bash\nexport MERIDIAN_BLUE_API_KEY=kp_live_...\nnode --input-type=module -e \"\n  import { MeridianBlue } from '@company786/meridian-blue-sdk';\n  const c = new MeridianBlue({ apiKey: process.env.MERIDIAN_BLUE_API_KEY });\n  const r = await c.chat.completions({\n    model: 'claude-opus-4-7',\n    messages: [{ role: 'user', content: 'Say hi.' }],\n  });\n  console.log(r.choices[0].message.content);\n  console.log('balance:', r.billing.balanceAfter);\n\"\n```\n\nIf you get `AuthenticationError`, the key is wrong or has been revoked.\nIf you get `InsufficientBalanceError`, top up credits in the dashboard.\nIf you get `NotFoundError`, the model id is not enabled for your account —\ncheck the dashboard model catalogue.\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}