monoize 1.6.1 → 1.6.2-linux-arm64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/monoize ADDED
Binary file
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "monoize",
3
- "version": "1.6.1",
4
- "description": "A protocol-normalizing AI gateway with provider routing, fail-forward, transforms, and billing.",
3
+ "version": "1.6.2-linux-arm64",
4
+ "description": "A protocol-normalizing AI gateway with provider routing, fail-forward, transforms, and billing. Native binary for linux arm64.",
5
5
  "license": "MIT",
6
- "type": "module",
7
- "bin": {
8
- "monoize": "bin/monoize.js"
9
- },
6
+ "os": [
7
+ "linux"
8
+ ],
9
+ "cpu": [
10
+ "arm64"
11
+ ],
10
12
  "engines": {
11
13
  "node": ">=18"
12
14
  },
@@ -18,15 +20,6 @@
18
20
  "url": "git+https://github.com/Ikaleio/monoize.git"
19
21
  },
20
22
  "homepage": "https://github.com/Ikaleio/monoize#readme",
21
- "bugs": "https://github.com/Ikaleio/monoize/issues",
22
- "optionalDependencies": {
23
- "monoize-linux-x64": "npm:monoize@1.6.1-linux-x64",
24
- "monoize-linux-arm64": "npm:monoize@1.6.1-linux-arm64",
25
- "monoize-darwin-x64": "npm:monoize@1.6.1-darwin-x64",
26
- "monoize-darwin-arm64": "npm:monoize@1.6.1-darwin-arm64",
27
- "monoize-win32-x64": "npm:monoize@1.6.1-win32-x64",
28
- "monoize-win32-arm64": "npm:monoize@1.6.1-win32-arm64"
29
- },
30
23
  "publishConfig": {
31
24
  "access": "public"
32
25
  }
package/README.md DELETED
@@ -1,324 +0,0 @@
1
- <div align="center">
2
-
3
- <img src="frontend/public/monoize.svg" width="96" alt="Monoize logo">
4
-
5
- # Monoize
6
-
7
- **AI APIs look alike. Their contracts differ.**
8
-
9
- Monoize is a Rust gateway for OpenAI Responses, Chat Completions, Anthropic Messages, Gemini, embeddings, and image APIs. It converts protocol semantics. It routes one logical model across multiple upstream channels. It handles failures between clients and upstreams.
10
-
11
- [English](README.md) · [简体中文](README.zh-CN.md)
12
- </div>
13
-
14
- ## The problem
15
-
16
- An AI API gateway does more than map JSON fields.
17
-
18
- Responses, Chat Completions, and Messages use different data models for conversation history, reasoning, tools, usage, errors, and streaming. A converter can return HTTP 200 and still corrupt the conversation. It can drop encrypted reasoning, attach a delta to the wrong content block, duplicate a stream event, or turn a tool result into assistant text.
19
-
20
- Routing also requires a state machine. A gateway must retry a failed channel. It must move to the next provider. It must stop retrying after it sends the first response byte to the client. If a gateway switches upstreams after that point, it splices two different generations into one stream.
21
-
22
- Clients and upstreams also differ in boundary behavior. Claude Code, OpenRouter-compatible clients, Codex WebSocket clients, DeepSeek tool loops, image providers, and provider SSE implementations make different assumptions.
23
-
24
- Inline images add latency. Upload time and upstream image preprocessing increase time to first token. When every retry carries the same base64 payload, this cost multiplies.
25
- ## Where common converters fail
26
-
27
- Format support does not equal protocol correctness. These public examples were checked on 2026-08-10:
28
-
29
- - OpenAI uses `encrypted_content` to preserve reasoning across stateless multi-turn requests. In New API commit [`823e263`](https://github.com/QuantumNous/new-api/commit/823e26304a396854ace30b52b98ec497c2dd9c36), the Responses output DTO [cannot represent that field](https://github.com/QuantumNous/new-api/blob/823e26304a396854ace30b52b98ec497c2dd9c36/relaykit/dto/openai_response.go#L327-L339). The Responses-to-Chat converter [reads only reasoning text](https://github.com/QuantumNous/new-api/blob/823e26304a396854ace30b52b98ec497c2dd9c36/relaykit/relayconvert/internal/oai_responses/to_oai_chat_resp.go#L212-L229). The conversion drops encrypted reasoning. See the [OpenAI reasoning guide](https://developers.openai.com/api/docs/guides/reasoning#preserve-reasoning-without-stored-responses).
30
- - LiteLLM issue [#32357](https://github.com/BerriAI/litellm/issues/32357) reports an Anthropic adapter that emits `message_start` twice and sends `thinking_delta` inside a text block. Anthropic SDKs discard that reasoning because the event violates block lifecycle rules.
31
- - New API issue [#5480](https://github.com/QuantumNous/new-api/issues/5480) documents streaming relay paths that retain complete generated text in memory to count tokens. Proxy memory grows with output length and concurrency.
32
-
33
- Monoize addresses these issues in its protocol model, stream state machines, routing rules, and resource bounds.
34
- ## What Monoize does
35
-
36
- ### Semantic protocol conversion
37
-
38
- Monoize decodes each supported protocol into URP v2. URP v2 is a flat, typed representation. It separates text, reasoning summaries, raw reasoning, encrypted reasoning, tool calls, tool results, images, files, refusals, usage, and control boundaries into distinct nodes.
39
-
40
- The selected upstream adapter encodes these nodes into the target protocol. The response follows the same path in reverse.
41
-
42
- This design provides these properties:
43
-
44
- - The Responses, Chat Completions, and Messages matrix is tested in streaming and non-streaming modes.
45
- - Encrypted reasoning remains separate from visible reasoning. Optional `mz2` envelopes preserve opaque reasoning across incompatible replay formats.
46
- - Tool-call IDs, parallel calls, multipart tool results, and assistant history keep their roles.
47
- - Responses output items and Messages content blocks maintain balanced lifecycle events.
48
- - Unknown fields within the same protocol family pass through. Monoize strips unsafe nested fields at cross-family boundaries to avoid invalid requests.
49
-
50
- See the [protocol test matrix](spec/urp-v2-flat-protocol-test-matrix.spec.md) for test cases.
51
-
52
- ### Retry before commit
53
-
54
- A logical model can match several ordered Providers. Each Provider contains weighted Channels.
55
-
56
- Monoize evaluates routes in a bounded waterfall:
57
-
58
- 1. Select the first matching Provider.
59
- 2. Select an eligible Channel by weight and affinity.
60
- 3. Retry retryable failures within configured budgets.
61
- 4. When the current route is exhausted, advance to the next route.
62
- 5. Stop fallback after sending the first response byte.
63
-
64
- Network errors, timeouts, `429`, and selected `5xx` responses advance the waterfall. Client errors such as `400`, `401`, `403`, and `422` stop the waterfall. Circuit breakers, passive health checks, active probes, cooldowns, and model affinity exclude unhealthy channels from the path.
65
-
66
- Monoize never switches providers in the middle of a visible stream. Transition rules are defined in the [routing specification](spec/monoize-upstream-routing.spec.md).
67
- ### Boundary transforms
68
-
69
- Core adapters handle standard protocol conversion. Ordered transforms handle behavior specific to a client, provider, model, or API key.
70
-
71
- Examples include:
72
-
73
- - OpenRouter structured reasoning and trailing usage chunks.
74
- - DeepSeek reasoning replay during tool loops.
75
- - Anthropic thinking blocks and signatures.
76
- - Codex Responses WebSocket sessions and `/v1/responses/compact`.
77
- - Converting data-URL images to provider-native image sources.
78
- - Splitting SSE frames for clients with small line buffers.
79
- - Cleaning up orphaned tool calls and repairing consecutive identical roles.
80
- - Role mapping between `system` and `developer`.
81
- - Prompt-cache breakpoints for system prompts, tools, and OpenAI schemas.
82
- - Removing provider-specific headers, adding model suffixes, and mapping token budgets.
83
-
84
- Transforms can run at Provider, global, or API-key scope. Model globs select matching rules. See the [transform specification](spec/urp-transform-system.spec.md).
85
- ### Request image compression
86
-
87
- `compress_user_message_images` is an opt-in request transform. It resizes and recompresses inline user images before routing them upstream. Supported output formats include JPEG, PNG, WebP, and JPEG XL.
88
-
89
- The transform preserves the image node and provider-specific detail hints. It skips unsupported formats and remote URLs. Input bytes, decoded pixels, concurrent encodes, cache entries, and cache bytes have explicit bounds.
90
-
91
- The transform reduces request size and image-related TTFT. Cached results avoid duplicate encoding during retries and repeated requests.
92
- ### Low forwarding overhead
93
-
94
- Monoize reduces proxy overhead:
95
-
96
- - Rust and Tokio handle asynchronous I/O without an interpreter on the request path.
97
- - The default stream path decodes and encodes incrementally through bounded channels.
98
- - Usage estimation updates counters as deltas arrive, without buffering the complete response text.
99
- - Rate-limit keys, health state, affinity, API-key caches, request capture, WebSocket history, and image transforms have explicit memory bounds.
100
- - A release build embeds the React dashboard. One process serves the API, the dashboard, and Prometheus metrics.
101
-
102
- Some response transforms intentionally use buffered streaming. Replicate also uses that path. The default bridge remains incremental.
103
-
104
- This comparison concerns proxy-side CPU, memory, and latency. It does not claim to make an upstream model generate tokens faster. See [stream usage accounting](src/handlers/usage.rs) and [runtime resource bounds](spec/runtime-resource-bounds.spec.md).
105
- ## Supported surface
106
-
107
- ### Downstream endpoints
108
-
109
- | Method | Endpoint | Contract |
110
- | --- | --- | --- |
111
- | `GET` | `/v1/models` | OpenAI-compatible model list |
112
- | `POST` | `/v1/responses` | OpenAI Responses, streaming or non-streaming |
113
- | `GET` | `/v1/responses` | OpenAI Responses WebSocket transport |
114
- | `POST` | `/v1/responses/compact` | Responses compaction |
115
- | `POST` | `/v1/chat/completions` | OpenAI Chat Completions |
116
- | `POST` | `/v1/messages` | Anthropic Messages |
117
- | `POST` | `/v1/embeddings` | Embeddings |
118
- | `POST` | `/v1/images/generations` | Image generation |
119
- | `POST` | `/v1/images/edits` | Multipart image edits |
120
-
121
- Every forwarding endpoint also has an `/api/v1/...` alias.
122
-
123
- ### Upstream channel types
124
-
125
- | Type | Native upstream contract |
126
- | --- | --- |
127
- | `responses` | OpenAI Responses-compatible |
128
- | `chat_completion` | OpenAI Chat Completions-compatible |
129
- | `messages` | Anthropic Messages-compatible |
130
- | `gemini` | Google Gemini native |
131
- | `openai_image` | OpenAI-compatible image API |
132
- | `replicate` | Replicate predictions |
133
-
134
- Providers define routing order, retry budgets, and health policy. Channels hold the actual upstream type, base URL, credential, model mapping, weight, and timeout.
135
-
136
- ## Request path
137
-
138
- ```text
139
- Client protocol
140
-
141
-
142
- Decode to typed URP v2
143
-
144
-
145
- Provider waterfall ──► weighted Channel ──► circuit breaker / affinity
146
- │ │
147
- │ retry or fail forward
148
- │ before the first byte
149
-
150
- Provider, global, and API-key transforms
151
-
152
-
153
- Upstream protocol encoding
154
-
155
-
156
- Upstream stream ──► URP v2 events ──► downstream protocol events
157
- ```
158
-
159
- ## Quick start
160
-
161
- Run Monoize once with Bun:
162
-
163
- ```bash
164
- bunx monoize
165
- ```
166
-
167
- Or install it globally:
168
-
169
- ```bash
170
- bun add --global monoize
171
- monoize
172
- ```
173
-
174
- The same package works with npm and pnpm:
175
-
176
- ```bash
177
- npx monoize
178
- # or: pnpm dlx monoize
179
- # global: npm install --global monoize
180
- # global: pnpm add --global monoize
181
- ```
182
-
183
- The package manager installs only the native binary for the current operating system and CPU. The npm package supports GNU-libc and musl-based Linux distributions, macOS, and Windows on x86-64 and ARM64. Linux packages use static musl executables and do not depend on the host libc or `libstdc++`.
184
-
185
- To build from source, install a stable Rust toolchain and [Bun](https://bun.sh/). A release build compiles the frontend and embeds it in the executable.
186
-
187
- ```bash
188
- cargo build --release
189
- ./target/release/monoize
190
- ```
191
-
192
- Open `http://localhost:8080`. The first registered account becomes `super_admin`, even when public registration is disabled. Then:
193
-
194
- 1. Create a Provider.
195
- 2. Add at least one Channel with its upstream URL and credential.
196
- 3. Map a logical model to the Channel.
197
- 4. Create an API key.
198
-
199
- ### Docker
200
-
201
- The published image supports Linux x86-64 and ARM64. Run it with a persistent SQLite volume:
202
-
203
- ```bash
204
- docker run -d \
205
- --name monoize \
206
- --restart unless-stopped \
207
- -p 8080:8080 \
208
- -v monoize-data:/app/data \
209
- ghcr.io/ikaleio/monoize:latest
210
- ```
211
-
212
- To use PostgreSQL or a non-default SQLite location, set `MONOIZE_DATABASE_DSN` with `-e`.
213
-
214
- Call the logical model through any supported downstream protocol:
215
-
216
- ```bash
217
- curl http://localhost:8080/v1/responses \
218
- -H 'Authorization: Bearer sk-your-monoize-key' \
219
- -H 'Content-Type: application/json' \
220
- -d '{
221
- "model": "your-logical-model",
222
- "input": "Explain why stream fallback must stop after the first byte.",
223
- "stream": true
224
- }'
225
- ```
226
-
227
- ## Configuration
228
-
229
- Runtime bootstrap uses environment variables. The database stores Providers, Channels, models, routing policy, transforms, users, and API keys. The dashboard manages them.
230
-
231
- | Variable | Default | Purpose |
232
- | --- | --- | --- |
233
- | `MONOIZE_LISTEN` | `0.0.0.0:8080` | HTTP listen address |
234
- | `MONOIZE_DATABASE_DSN` | `sqlite://./data/monoize.db` | SQLite or PostgreSQL DSN |
235
- | `DATABASE_URL` | unset | Fallback DSN when `MONOIZE_DATABASE_DSN` is unset |
236
- | `MONOIZE_METRICS_PATH` | `/metrics` | Prometheus metrics path |
237
- | `MONOIZE_HTTP_BODY_MAX_BYTES` | `52428800` | Forwarding request-body limit |
238
- | `MONOIZE_TRUSTED_PROXY_CIDRS` | `127.0.0.0/8,::1/128` | Trusted reverse-proxy networks; an explicitly empty value disables trust |
239
- | `MONOIZE_UPSTREAM_PROXY_URL` | unset | Node-local outbound HTTP(S) proxy for upstream calls; channels may override per channel via `proxy_url` |
240
- | `MONOIZE_CAP_API_ENDPOINT` | unset | Optional external Cap site endpoint including the site-key path; unset uses Monoize's built-in Cap service |
241
- | `MONOIZE_CAP_SECRET_KEY` | unset | Secret for the external Cap site; configure it together with `MONOIZE_CAP_API_ENDPOINT` |
242
-
243
- Dashboard login and registration use Monoize's built-in Cap proof-of-work service by default and require no extra configuration. Administrators can disable human verification under system settings, which removes bot and credential-stuffing protection from these endpoints. To use [Cap Standalone](https://capjs.js.org/guide/) instead, create one site key, set both variables above, and allow the dashboard origin in Cap's CORS configuration. Monoize then verifies each token through the site's `/siteverify` endpoint.
244
-
245
- Monoize supports SQLite and PostgreSQL. One Monoize application process is the supported writer for its business tables.
246
-
247
- ### Primary/replica deployment
248
-
249
- Monoize can run as one writable primary plus read-only replicas. All nodes share one PostgreSQL database (`spec/primary-replica-deployment.spec.md`). Replicas serve `/v1/**` traffic only. They do not serve the dashboard. Replicas ship request logs and billing deltas to the primary over an authenticated internal API. Balance checks subtract locally unshipped charges to keep overspend bounded. Failover is manual: to promote a replica, switch its role and restart it.
250
-
251
- | Variable | Default | Purpose |
252
- | --- | --- | --- |
253
- | `MONOIZE_NODE_ROLE` | `primary` | `primary` or `replica` |
254
- | `MONOIZE_PRIMARY_INTERNAL_URL` | required on replicas | Base URL of the primary for metering shipment |
255
- | `MONOIZE_REPLICA_TOKEN` | unset | Shared secret: required on replicas; on a primary it enables the ingest endpoint |
256
- | `MONOIZE_REPLICA_ID` | auto-generated and persisted | Fixed replica identity (UUID v4). When unset, an identity is generated once and persisted as `replica-identity` inside the metering spool directory, so the ID survives restarts |
257
- | `MONOIZE_CONFIG_POLL_INTERVAL_SECONDS` | `5` | Replica config-epoch poll interval |
258
- | `MONOIZE_METERING_SHIP_INTERVAL_SECONDS` | `10` | Replica metering shipment interval |
259
- | `MONOIZE_METERING_SHIP_BATCH_MAX_ENTRIES` | `500` | Per-batch entry cap (hard cap 2000) |
260
- | `MONOIZE_REPLICA_METERING_SPOOL_DIR` | `./data/replica-metering-spool` | Durable delta spool directory |
261
-
262
- ## Operations
263
-
264
- The embedded dashboard manages:
265
-
266
- - Providers, Channels, health, priority, model mapping, and pricing multipliers.
267
- - API keys, quotas, model restrictions, IP allowlists, transforms, and sub-accounts.
268
- - Users, balances, nano-dollar billing, and an append-only ledger.
269
- - Request logs with TTFB, duration, token usage, cost, errors, and tried routes.
270
- - Model metadata and pricing imported from [Models.dev](https://models.dev).
271
- - Prometheus metrics and live operational views.
272
-
273
- Request capture is opt-in and bounded. Credentials and prompt bodies are not part of normal observability logs.
274
-
275
- ## Limits and non-goals
276
-
277
- - Monoize forwards tool definitions and tool calls. It does not execute tools locally.
278
- - Monoize does not provide OpenAI Files, vector stores, or local retrieval.
279
- - Responses object storage and later object retrieval are not implemented.
280
- - Fallback ends after downstream bytes begin. Mid-stream provider switching is intentionally forbidden.
281
- - Cross-family conversion preserves representable semantics. Provider-specific nested fields that have no safe target representation are intentionally removed.
282
- - Image compression is opt-in. It does not fetch arbitrary remote image URLs unless the separate URL-resolution transform is configured.
283
-
284
- ## Release artifacts
285
-
286
- A GitHub Release whose tag equals `v` plus the Cargo package version triggers the [release workflow](.github/workflows/release.yml). The workflow builds native x86-64 and ARM64 binaries for Linux, macOS, and Windows.
287
-
288
- Linux and macOS assets use `tar.gz`. Windows assets use `zip`. Every archive includes both READMEs and the license. Every archive has a separate SHA-256 file. The workflow uploads nothing until all six builds and all checksum checks succeed.
289
-
290
- A manual workflow run executes the same six-platform preflight. It does not change a GitHub Release. The exact asset contract is defined in the [release artifact specification](spec/release-artifacts.spec.md).
291
-
292
- The workflow also builds seven npm tarballs: one TypeScript-derived launcher and six platform packages. A normal Bun, npm, or pnpm installation selects one platform package through `os` and `cpu` metadata. The npm publication job authenticates through npm Trusted Publishing and GitHub Actions OIDC; it does not use a long-lived npm token. The exact npm contract is defined in the [npm CLI distribution specification](spec/npm-cli-distribution.spec.md).
293
-
294
- ## Development and verification
295
-
296
- Run the backend tests:
297
-
298
- ```bash
299
- cargo test
300
- ```
301
-
302
- Run the frontend checks:
303
-
304
- ```bash
305
- cd frontend
306
- bun install
307
- bun run lint
308
- bun run build
309
- ```
310
-
311
- Run the live three-protocol suite against a configured instance:
312
-
313
- ```bash
314
- cd sdk-tests
315
- bun run live-protocol-suite.ts <baseURL> <apiKey> <model>
316
- ```
317
-
318
- The suite checks non-streaming text, streaming text, tool loops, and streaming tool loops through Chat Completions, Responses, and Messages.
319
-
320
- Observable behavior is specified under [`spec/`](spec/). Code and specifications change together.
321
-
322
- ## License
323
-
324
- Monoize is licensed under the [MIT License](LICENSE).
package/bin/monoize.js DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- import{spawn as w}from"node:child_process";import{existsSync as v,realpathSync as b}from"node:fs";import{createRequire as y}from"node:module";import s from"node:path";import{fileURLToPath as S}from"node:url";var h=[{rustTarget:"x86_64-unknown-linux-musl",packageAlias:"monoize-linux-x64",versionSuffix:"linux-x64",platform:"linux",architecture:"x64",executable:"monoize"},{rustTarget:"aarch64-unknown-linux-musl",packageAlias:"monoize-linux-arm64",versionSuffix:"linux-arm64",platform:"linux",architecture:"arm64",executable:"monoize"},{rustTarget:"x86_64-apple-darwin",packageAlias:"monoize-darwin-x64",versionSuffix:"darwin-x64",platform:"darwin",architecture:"x64",executable:"monoize"},{rustTarget:"aarch64-apple-darwin",packageAlias:"monoize-darwin-arm64",versionSuffix:"darwin-arm64",platform:"darwin",architecture:"arm64",executable:"monoize"},{rustTarget:"x86_64-pc-windows-msvc",packageAlias:"monoize-win32-x64",versionSuffix:"win32-x64",platform:"win32",architecture:"x64",executable:"monoize.exe"},{rustTarget:"aarch64-pc-windows-msvc",packageAlias:"monoize-win32-arm64",versionSuffix:"win32-arm64",platform:"win32",architecture:"arm64",executable:"monoize.exe"}];function u(e,n){return h.find((r)=>r.platform===e&&r.architecture===n)}var m=b(S(import.meta.url)),T=y(m).resolve;function k(e=process.env,n=process.argv[1]??"",r=m){let o=e.npm_config_user_agent??"",t=e.npm_execpath??"";if(/\bpnpm\//.test(o)||t.includes("pnpm")||r.includes(`${s.sep}.pnpm${s.sep}`))return"pnpm";if(/\bbun\//.test(o)||t.includes("bun")||n.includes(".bun/install/global")||n.includes(".bun\\install\\global"))return"bun";return"npm"}function N(e){switch(e){case"bun":return"bun install -g monoize@latest";case"pnpm":return"pnpm add -g monoize@latest";default:return"npm install -g monoize@latest"}}function g(e=process.platform,n=process.arch){let r=u(e,n);if(!r)throw Error(`unsupported platform: ${e} (${n})`);return r}function d(e,n=T,r=v){let o;try{o=n(`${e.packageAlias}/package.json`)}catch{throw Error(`missing optional dependency ${e.packageAlias}`)}let t=s.join(s.dirname(o),"bin",e.executable);if(!r(t))throw Error(`missing optional dependency ${e.packageAlias}: expected ${e.executable}`);return t}async function x(e,n){let r=w(e,[...n],{cwd:process.cwd(),env:process.env,stdio:"inherit"}),o=["SIGINT","SIGTERM","SIGHUP"],t=new Map;for(let a of o){let i=()=>{if(!r.killed)try{r.kill(a)}catch{}};t.set(a,i),process.on(a,i)}let l=()=>{for(let[a,i]of t)process.off(a,i)};return await new Promise((a,i)=>{r.once("error",(c)=>{l(),i(c)}),r.once("exit",(c,p)=>{if(l(),p)a({type:"signal",signal:p});else a({type:"code",exitCode:c??1})})})}function f(e){let n=e instanceof Error?e.message:String(e);if(!n.startsWith("missing optional dependency"))return`monoize: ${n}`;let r=N(k());return`monoize: ${n}. Reinstall with: ${r}`}try{let e=g(),n=d(e),r=await x(n,process.argv.slice(2));if(r.type==="signal")process.kill(process.pid,r.signal);else process.exit(r.exitCode)}catch(e){console.error(f(e)),process.exit(1)}