monoize 1.10.0-linux-x64 → 1.10.0

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/README.md ADDED
@@ -0,0 +1,257 @@
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 AI APIs. It converts semantics between OpenAI Responses, Chat Completions, and Anthropic Messages. It routes one logical model across multiple upstream Channels. It serves the management dashboard from the same process.
10
+
11
+ [English](README.md) · [简体中文](README.zh-CN.md)
12
+
13
+ </div>
14
+
15
+ <div align="center">
16
+ <img src="docs/public/images/en/dashboard.webp" width="880" alt="Monoize dashboard preview">
17
+ </div>
18
+
19
+ ## Why Monoize
20
+
21
+ An AI API gateway does more than map JSON fields.
22
+
23
+ Responses, Chat Completions, and Messages use different data models for conversation history, reasoning, tools, usage, and streaming. A field-level converter can return HTTP 200 and still corrupt the conversation:
24
+
25
+ 1. **Lost reasoning context.** Responses carries reasoning state across stateless requests in `encrypted_content`. A converter that cannot represent this field drops it silently in multi-turn conversations.
26
+ 2. **Broken stream lifecycle.** Each protocol defines its own open and close rules for content blocks. A reasoning delta inside a text block, or a duplicated start event, makes downstream SDKs discard data.
27
+ 3. **Spliced streams on failover.** A gateway must retry failed upstreams. After it sends the first response byte, switching upstreams splices two different generations into one stream.
28
+
29
+ Monoize addresses these problems with a typed protocol model, stream state machines, and a bounded routing waterfall.
30
+
31
+ ## Core design
32
+
33
+ ### 1. URP v2 protocol model
34
+
35
+ 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. The upstream adapter encodes these nodes into the target protocol. The response follows the same path in reverse.
36
+
37
+ - Encrypted reasoning remains separate from visible reasoning. Optional `mz2` envelopes preserve opaque reasoning across incompatible replay formats.
38
+ - Tool-call IDs, parallel calls, multipart tool results, and assistant history keep their roles.
39
+ - Responses output items and Messages content blocks keep balanced lifecycle events.
40
+ - Unknown fields within one protocol family pass through. Cross-family conversion strips nested fields the target cannot represent.
41
+
42
+ ### 2. Retry before the first byte
43
+
44
+ A logical model can match several ordered Providers. Each Provider contains weighted Channels.
45
+
46
+ 1. Select the first matching Provider.
47
+ 2. Select a healthy Channel by weight and Channel affinity.
48
+ 3. Retry retryable failures within configured budgets.
49
+ 4. When the current route is exhausted, advance to the next route.
50
+ 5. Stop fallback after sending the first response byte.
51
+
52
+ Network errors, timeouts, `429`, and selected `5xx` responses advance the waterfall. `400`, `401`, `403`, and `422` stop it. Circuit breakers, passive health checks, active probes, and cooldowns exclude unhealthy Channels from the path. Monoize never switches Providers in the middle of a visible stream. See the [routing specification](spec/monoize-upstream-routing.spec.md).
53
+
54
+ ### 3. Low forwarding overhead
55
+
56
+ - Rust and Tokio handle asynchronous I/O without an interpreter on the request path.
57
+ - The default stream path decodes and encodes incrementally through bounded channels.
58
+ - Usage counters update as deltas arrive, without buffering the complete response text.
59
+
60
+ Some response transforms rebuild the full response and use a buffered synthetic stream. Replicate also uses that path. The default bridge remains incremental. This comparison concerns proxy-side CPU, memory, and latency. It does not claim to make an upstream model generate tokens faster.
61
+
62
+ ## Capabilities
63
+
64
+ **Protocol conversion.** Streaming and non-streaming conversion among Responses, Chat Completions, and Messages. Gemini, OpenAI image APIs, and Replicate connect as upstreams.
65
+
66
+ **Routing.** Ordered Provider fallback, weighted Channels, circuit breakers and active probes, Channel affinity, and per-API-key model redirects.
67
+
68
+ **Boundary transforms**, attached at global, Provider, or API-key scope and matched by model glob:
69
+
70
+ - OpenRouter structured reasoning and trailing usage chunks.
71
+ - DeepSeek reasoning replay during tool loops.
72
+ - Anthropic thinking blocks and signatures.
73
+ - Codex Responses WebSocket sessions and `/v1/responses/compact`.
74
+ - Prompt-cache breakpoints for system prompts, tools, and history.
75
+ - `compress_user_message_images`: recompress inline user images to JPEG, PNG, WebP, or JPEG XL to reduce TTFT.
76
+ - Custom JavaScript transforms that rewrite requests and responses at runtime.
77
+ - SSE frame splitting, orphaned tool-call cleanup, consecutive-role merging, and `system`/`developer` role mapping.
78
+
79
+ **Operations.**
80
+
81
+ - Embedded React dashboard: Providers, Channels, model mapping, pricing, users, API keys, and sub-accounts.
82
+ - Nano-dollar billing, multipliers, and an append-only ledger. Price sync from [models.dev](https://models.dev), [OpenRouter](https://openrouter.ai), and new-api.
83
+ - Request logs with TTFB, duration, tokens, cost, errors, and tried routes.
84
+ - Request Capture: per-request event timelines, opt-in and bounded.
85
+ - Built-in Cap proof-of-work human verification with no external Captcha service.
86
+ - Prometheus `/metrics`.
87
+
88
+ ## Request path
89
+
90
+ ```text
91
+ Client protocol (Responses / Chat Completions / Messages)
92
+
93
+
94
+ Decode to URP v2
95
+
96
+
97
+ Provider waterfall ──► weighted Channel ──► circuit breaker / affinity
98
+ │ │
99
+ │ retry or advance before the first byte
100
+
101
+ Transforms (global / Provider / API key)
102
+
103
+
104
+ Encode to upstream protocol
105
+
106
+
107
+ Upstream stream ──► URP v2 events ──► downstream protocol events
108
+ ```
109
+
110
+ ## Quick start
111
+
112
+ ### npm / Bun
113
+
114
+ ```bash
115
+ bunx monoize
116
+ # or: npx monoize
117
+ ```
118
+
119
+ Global install:
120
+
121
+ ```bash
122
+ bun add --global monoize
123
+ monoize
124
+ ```
125
+
126
+ The package manager installs only the native binary for the current OS and CPU. Supported targets: Linux x86-64 and ARM64 (glibc and musl), Windows x86-64.
127
+
128
+ ### Docker
129
+
130
+ ```bash
131
+ docker run -d \
132
+ --name monoize \
133
+ --restart unless-stopped \
134
+ -p 8080:8080 \
135
+ -v monoize-data:/app/data \
136
+ ghcr.io/ikaleio/monoize:latest
137
+ ```
138
+
139
+ `docker-compose.yml`:
140
+
141
+ ```yaml
142
+ services:
143
+ monoize:
144
+ image: ghcr.io/ikaleio/monoize:latest
145
+ restart: unless-stopped
146
+ ports:
147
+ - "8080:8080"
148
+ volumes:
149
+ - ./data:/app/data
150
+ # For PostgreSQL:
151
+ # environment:
152
+ # - MONOIZE_DATABASE_DSN=postgres://user:pass@host/monoize
153
+ ```
154
+
155
+ ### Build from source
156
+
157
+ Requires a stable Rust toolchain and [Bun](https://bun.sh/). A release build compiles the frontend and embeds it in the executable.
158
+
159
+ ```bash
160
+ cargo build --release
161
+ ./target/release/monoize
162
+ ```
163
+
164
+ ### First configuration
165
+
166
+ Open `http://localhost:8080`. The first registered account becomes `super_admin`, even when public registration is disabled.
167
+
168
+ 1. Create a Provider.
169
+ 2. Add at least one Channel with its upstream URL and credential.
170
+ 3. Map a logical model to the Channel.
171
+ 4. Create an API key.
172
+
173
+ ```bash
174
+ curl http://localhost:8080/v1/chat/completions \
175
+ -H 'Authorization: Bearer sk-your-monoize-key' \
176
+ -H 'Content-Type: application/json' \
177
+ -d '{
178
+ "model": "your-logical-model",
179
+ "messages": [{"role": "user", "content": "Hello"}],
180
+ "stream": true
181
+ }'
182
+ ```
183
+
184
+ ## Supported surface
185
+
186
+ ### Downstream endpoints
187
+
188
+ | Method | Endpoint | Contract |
189
+ | --- | --- | --- |
190
+ | `GET` | `/v1/models` | OpenAI-compatible model list |
191
+ | `POST` | `/v1/responses` | OpenAI Responses, streaming or non-streaming |
192
+ | `GET` | `/v1/responses` | OpenAI Responses WebSocket transport |
193
+ | `POST` | `/v1/responses/compact` | Responses compaction |
194
+ | `POST` | `/v1/chat/completions` | OpenAI Chat Completions |
195
+ | `POST` | `/v1/messages` | Anthropic Messages |
196
+ | `POST` | `/v1/embeddings` | Embeddings |
197
+ | `POST` | `/v1/images/generations` | Image generation |
198
+ | `POST` | `/v1/images/edits` | Multipart image edits |
199
+
200
+ Every forwarding endpoint also has an `/api/v1/...` alias.
201
+
202
+ ### Upstream Channel types
203
+
204
+ | Type | Native upstream contract |
205
+ | --- | --- |
206
+ | `responses` | OpenAI Responses-compatible |
207
+ | `chat_completion` | OpenAI Chat Completions-compatible |
208
+ | `messages` | Anthropic Messages-compatible |
209
+ | `gemini` | Google Gemini native |
210
+ | `openai_image` | OpenAI-compatible image API |
211
+ | `replicate` | Replicate predictions |
212
+
213
+ ## Configuration
214
+
215
+ Runtime bootstrap uses environment variables. The database stores Providers, Channels, models, routing, transforms, users, and API keys. The dashboard manages them.
216
+
217
+ | Variable | Default | Purpose |
218
+ | --- | --- | --- |
219
+ | `MONOIZE_LISTEN` | `0.0.0.0:8080` | HTTP listen address |
220
+ | `MONOIZE_DATABASE_DSN` | `sqlite://./data/monoize.db` | SQLite or PostgreSQL DSN |
221
+ | `MONOIZE_METRICS_PATH` | `/metrics` | Prometheus metrics path |
222
+ | `MONOIZE_HTTP_BODY_MAX_BYTES` | `52428800` | Forwarding request-body limit |
223
+ | `MONOIZE_TRUSTED_PROXY_CIDRS` | `127.0.0.0/8,::1/128` | Trusted reverse-proxy networks; an explicitly empty value disables trust |
224
+ | `MONOIZE_UPSTREAM_PROXY_URL` | unset | Node-local outbound HTTP(S) proxy; Channels may override via `proxy_url` |
225
+ | `MONOIZE_CAP_API_ENDPOINT` | unset | External Cap site endpoint; unset uses the built-in Cap service |
226
+ | `MONOIZE_CAP_SECRET_KEY` | unset | Secret for the external Cap site; set together with the endpoint |
227
+
228
+ ### Primary/replica deployment
229
+
230
+ Monoize can run as one writable primary plus read-only replicas that share one PostgreSQL database. Replicas serve `/v1/**` traffic only and 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 bound overspend. Failover is manual: switch the role and restart. See the [primary/replica specification](spec/primary-replica-deployment.spec.md).
231
+
232
+ | Variable | Default | Purpose |
233
+ | --- | --- | --- |
234
+ | `MONOIZE_NODE_ROLE` | `primary` | `primary` or `replica` |
235
+ | `MONOIZE_PRIMARY_INTERNAL_URL` | required on replicas | Internal base URL of the primary |
236
+ | `MONOIZE_REPLICA_TOKEN` | unset | Shared secret; required on replicas, enables the ingest endpoint on the primary |
237
+ | `MONOIZE_REPLICA_ID` | generated and persisted | Fixed replica identity (UUID v4) |
238
+ | `MONOIZE_CONFIG_POLL_INTERVAL_SECONDS` | `5` | Replica config poll interval |
239
+ | `MONOIZE_METERING_SHIP_INTERVAL_SECONDS` | `10` | Replica metering shipment interval |
240
+ | `MONOIZE_REPLICA_METERING_SPOOL_DIR` | `./data/replica-metering-spool` | Durable metering spool directory |
241
+
242
+ ## Limits and non-goals
243
+
244
+ - Monoize forwards tool definitions and tool calls. It does not execute tools locally.
245
+ - No OpenAI Files, vector stores, or local retrieval.
246
+ - No Responses object storage or later retrieval by ID.
247
+ - Fallback ends after downstream bytes begin. Mid-stream Provider switching is forbidden.
248
+ - Cross-family conversion preserves representable semantics. Provider-specific nested fields without a safe target representation are removed.
249
+ - Image compression is opt-in. Remote image URLs are not fetched unless the separate URL-resolution transform is configured.
250
+
251
+ ## Specifications and documentation
252
+
253
+ Observable behavior is specified under [`spec/`](spec/). Code and specifications change together. Full documentation lives under [`docs/`](docs/).
254
+
255
+ ## License
256
+
257
+ Monoize is licensed under the [MIT License](LICENSE).
package/bin/monoize.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import{spawn as v}from"node:child_process";import{existsSync as y,realpathSync as b}from"node:fs";import{createRequire as w}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-pc-windows-msvc",packageAlias:"monoize-win32-x64",versionSuffix:"win32-x64",platform:"win32",architecture:"x64",executable:"monoize.exe"}];function u(e,t){return h.find((n)=>n.platform===e&&n.architecture===t)}var g=b(S(import.meta.url)),T=w(g).resolve;function k(e=process.env,t=process.argv[1]??"",n=g){let o=e.npm_config_user_agent??"",r=e.npm_execpath??"";if(/\bpnpm\//.test(o)||r.includes("pnpm")||n.includes(`${s.sep}.pnpm${s.sep}`))return"pnpm";if(/\bbun\//.test(o)||r.includes("bun")||t.includes(".bun/install/global")||t.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 m(e=process.platform,t=process.arch){let n=u(e,t);if(!n)throw Error(`unsupported platform: ${e} (${t})`);return n}function d(e,t=T,n=y){let o;try{o=t(`${e.packageAlias}/package.json`)}catch{throw Error(`missing optional dependency ${e.packageAlias}`)}let r=s.join(s.dirname(o),"bin",e.executable);if(!n(r))throw Error(`missing optional dependency ${e.packageAlias}: expected ${e.executable}`);return r}async function x(e,t){let n=v(e,[...t],{cwd:process.cwd(),env:process.env,stdio:"inherit"}),o=["SIGINT","SIGTERM","SIGHUP"],r=new Map;for(let a of o){let i=()=>{if(!n.killed)try{n.kill(a)}catch{}};r.set(a,i),process.on(a,i)}let l=()=>{for(let[a,i]of r)process.off(a,i)};return await new Promise((a,i)=>{n.once("error",(c)=>{l(),i(c)}),n.once("exit",(c,p)=>{if(l(),p)a({type:"signal",signal:p});else a({type:"code",exitCode:c??1})})})}function f(e){let t=e instanceof Error?e.message:String(e);if(!t.startsWith("missing optional dependency"))return`monoize: ${t}`;let n=N(k());return`monoize: ${t}. Reinstall with: ${n}`}try{let e=m(),t=d(e),n=await x(t,process.argv.slice(2));if(n.type==="signal")process.kill(process.pid,n.signal);else process.exit(n.exitCode)}catch(e){console.error(f(e)),process.exit(1)}
package/package.json CHANGED
@@ -1,14 +1,12 @@
1
1
  {
2
2
  "name": "monoize",
3
- "version": "1.10.0-linux-x64",
4
- "description": "A protocol-normalizing AI gateway with provider routing, fail-forward, transforms, and billing. Native binary for linux x64.",
3
+ "version": "1.10.0",
4
+ "description": "A protocol-normalizing AI gateway with provider routing, fail-forward, transforms, and billing.",
5
5
  "license": "MIT",
6
- "os": [
7
- "linux"
8
- ],
9
- "cpu": [
10
- "x64"
11
- ],
6
+ "type": "module",
7
+ "bin": {
8
+ "monoize": "bin/monoize.js"
9
+ },
12
10
  "engines": {
13
11
  "node": ">=18"
14
12
  },
@@ -20,6 +18,12 @@
20
18
  "url": "git+https://github.com/Ikaleio/monoize.git"
21
19
  },
22
20
  "homepage": "https://github.com/Ikaleio/monoize#readme",
21
+ "bugs": "https://github.com/Ikaleio/monoize/issues",
22
+ "optionalDependencies": {
23
+ "monoize-linux-x64": "npm:monoize@1.10.0-linux-x64",
24
+ "monoize-linux-arm64": "npm:monoize@1.10.0-linux-arm64",
25
+ "monoize-win32-x64": "npm:monoize@1.10.0-win32-x64"
26
+ },
23
27
  "publishConfig": {
24
28
  "access": "public"
25
29
  }
package/bin/monoize DELETED
@@ -1,4 +0,0 @@
1
- [diffend] Oversized file quarantined before diffing.
2
- name: package/bin/monoize
3
- size: 60633792 bytes
4
- sha256: 41747f9993a79382af7084126221936d44f3a18a87c09fa45683e8e1922b67c3