mini-apigw 0.0.6__tar.gz

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.
Files changed (41) hide show
  1. mini_apigw-0.0.6/LICENSE.md +24 -0
  2. mini_apigw-0.0.6/PKG-INFO +313 -0
  3. mini_apigw-0.0.6/README.md +284 -0
  4. mini_apigw-0.0.6/gateway/__init__.py +3 -0
  5. mini_apigw-0.0.6/gateway/accounting.py +252 -0
  6. mini_apigw-0.0.6/gateway/api/__init__.py +1 -0
  7. mini_apigw-0.0.6/gateway/api/admin.py +113 -0
  8. mini_apigw-0.0.6/gateway/api/v1.py +359 -0
  9. mini_apigw-0.0.6/gateway/app.py +60 -0
  10. mini_apigw-0.0.6/gateway/auth.py +55 -0
  11. mini_apigw-0.0.6/gateway/backends/__init__.py +27 -0
  12. mini_apigw-0.0.6/gateway/backends/anthropic.py +66 -0
  13. mini_apigw-0.0.6/gateway/backends/base.py +69 -0
  14. mini_apigw-0.0.6/gateway/backends/ollama.py +558 -0
  15. mini_apigw-0.0.6/gateway/backends/openai.py +65 -0
  16. mini_apigw-0.0.6/gateway/cli.py +279 -0
  17. mini_apigw-0.0.6/gateway/config.py +571 -0
  18. mini_apigw-0.0.6/gateway/ipacl.py +25 -0
  19. mini_apigw-0.0.6/gateway/log.py +51 -0
  20. mini_apigw-0.0.6/gateway/main.py +8 -0
  21. mini_apigw-0.0.6/gateway/middleware/__init__.py +0 -0
  22. mini_apigw-0.0.6/gateway/middleware/trace.py +48 -0
  23. mini_apigw-0.0.6/gateway/policies.py +33 -0
  24. mini_apigw-0.0.6/gateway/routing.py +146 -0
  25. mini_apigw-0.0.6/gateway/runtime.py +223 -0
  26. mini_apigw-0.0.6/gateway/scheduling.py +131 -0
  27. mini_apigw-0.0.6/gateway/signals.py +34 -0
  28. mini_apigw-0.0.6/gateway/sse.py +28 -0
  29. mini_apigw-0.0.6/gateway/trace.py +186 -0
  30. mini_apigw-0.0.6/mini_apigw.egg-info/PKG-INFO +313 -0
  31. mini_apigw-0.0.6/mini_apigw.egg-info/SOURCES.txt +39 -0
  32. mini_apigw-0.0.6/mini_apigw.egg-info/dependency_links.txt +1 -0
  33. mini_apigw-0.0.6/mini_apigw.egg-info/entry_points.txt +2 -0
  34. mini_apigw-0.0.6/mini_apigw.egg-info/requires.txt +9 -0
  35. mini_apigw-0.0.6/mini_apigw.egg-info/top_level.txt +1 -0
  36. mini_apigw-0.0.6/pyproject.toml +64 -0
  37. mini_apigw-0.0.6/setup.cfg +4 -0
  38. mini_apigw-0.0.6/tests/test_config.py +101 -0
  39. mini_apigw-0.0.6/tests/test_policies.py +21 -0
  40. mini_apigw-0.0.6/tests/test_routing.py +11 -0
  41. mini_apigw-0.0.6/tests/test_scheduling.py +51 -0
@@ -0,0 +1,24 @@
1
+ Copyright 2025, Thomas Spielauer
2
+
3
+ Redistribution and use in source and binary forms, with or without modification,
4
+ are permitted provided that the following conditions are met:
5
+
6
+ * Redistributions of source code must retain this list of conditions and the
7
+ following disclaimer.
8
+ * Redistributions in binary form must reproduce this list of conditions and the
9
+ following disclaimer in the documentation and/or other materials provided with
10
+ the distribution.
11
+ * Neither the name of the copyright holder nor the names of its contributors may
12
+ be used to endorse or promote products derived from this software without
13
+ specific prior written permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
19
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,313 @@
1
+ Metadata-Version: 2.4
2
+ Name: mini-apigw
3
+ Version: 0.0.6
4
+ Summary: Minimal OpenAI-compatible edge gateway with multi-backend routing
5
+ Author-email: Thomas Spielauer <pypipackages01@tspi.at>
6
+ License-Expression: BSD-3-Clause
7
+ Project-URL: Homepage, https://www.github.com/tspspi/mini-apigw
8
+ Keywords: API-gateway,gateway,API,LLM
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Topic :: Internet :: WWW/HTTP
16
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE.md
20
+ Requires-Dist: fastapi>=0.110
21
+ Requires-Dist: uvicorn>=0.27
22
+ Requires-Dist: requests>=2.31
23
+ Requires-Dist: openai>=1.0
24
+ Requires-Dist: ollama>=0.3
25
+ Requires-Dist: psycopg2>=2.9.10
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest>=7.0; extra == "dev"
28
+ Dynamic: license-file
29
+
30
+ # mini-apigw — Minimal OpenAI‑compatible API Gateway
31
+
32
+ __WORK IN PROGRESS_
33
+
34
+ mini-apigw is a small edge gateway that presents an OpenAI‑compatible API surface and routes requests
35
+ to multiple LLM and image generation backends. It is designed for __simplicity__ and ease of control: you
36
+ configure backends and apps in JSON, set policies and cost limits per app, and the gateway handles
37
+ routing, scheduling, usage accounting, optional persistence, trace logging, and admin endpoints.
38
+
39
+ The main reason I developed this gateway was to handle multiple access to shared resources - I personally
40
+ utilize machines that run LLMs (ollama, vllm) as well as SDXL based image generation and other software. They
41
+ compete for GPU resources and since those tools are usually not developed to work together nicely and
42
+ arbitrate GPU usage the gateway offers serialization of requests inside _sequence groups_. All backends
43
+ in the same sequence group execute requests of all backends in the same group strictly in sequence so the
44
+ local backends can handle loading and unloading of competing backends.
45
+
46
+ Backends included out of the box:
47
+
48
+ * OpenAI
49
+ * Ollama
50
+
51
+ In development:
52
+
53
+ * Anthropic
54
+ * Fooocus
55
+ * vLLM
56
+
57
+ The gateway exposes the familiar `/v1` endpoints (`/chat/completions`, `/completions`,
58
+ `/embeddings`, `/images/generations`, and `/models`) and normalizes responses where
59
+ needed. This allows one to use the ```openai``` client library for any backend. Note that the gateway
60
+ does not implement the full OpenAI API - it just passes through the above mentioned
61
+ endpoints. In addition it uses local API keys. Those API keys are used to select the application
62
+ from ```apps.json``` configuration file.
63
+
64
+ ## Installation
65
+
66
+ From PyPI (recommended):
67
+
68
+ ```bash
69
+ pip install mini-apigw
70
+ ```
71
+
72
+ From source (editable):
73
+
74
+ ```bash
75
+ pip install -e .
76
+ ```
77
+
78
+ ## Configuration
79
+
80
+ The gateway reads three JSON files from a configuration directory. By default this
81
+ is `./config` (at the moment, this will be fixed soon, this will be a breaking change!),
82
+ or override with the environment variable `MINIAPIGW_CONFIG_DIR` or the CLI flag `--config-dir`.
83
+
84
+ Required files:
85
+
86
+ - `daemon.json`: service, logging, admin, timeouts, and optional Postgres settings
87
+ - `backends.json`: model providers, aliases, costs, and capabilities
88
+ - `apps.json`: application definitions, API keys, allow/deny policies, cost limits, and tracing
89
+
90
+ Example `daemon.json` (minimal, no persistent accounting log):
91
+
92
+ ```json
93
+ {
94
+ "listen": { "host_v4": "0.0.0.0", "port": 8080 },
95
+ "admin": { "bind": ["127.0.0.1:8081", "[::1]:8081"], "stats_networks": ["127.0.0.1/32", "::1/128"] },
96
+ "logging": { "level": "INFO", "redact_prompts": true, "access_log": true },
97
+ "reload": { "enable_sighup": true },
98
+ "timeouts": { "default_connect_s": 60, "default_read_s": 600 },
99
+ "database": null
100
+ }
101
+ ```
102
+
103
+ Example `backends.json` (mixed OpenAI + Ollama):
104
+
105
+ ```json
106
+ {
107
+ "aliases": { "llama3.2": "llama3.2:latest" },
108
+ "sequence_groups": { "local_gpu_01": { "description": "Serialized work for local GPU tasks" } },
109
+ "backends": [
110
+ {
111
+ "type": "openai",
112
+ "name": "openai-primary",
113
+ "base_url": "https://api.openai.com/v1",
114
+ "api_key": "<openai_key>",
115
+ "concurrency": 4,
116
+ "supports": { "chat": ["gpt-4o-mini"], "embeddings": ["text-embedding-3-small"], "images": ["gpt-image-1", "dall-e-3"] },
117
+ "cost": { "currency": "usd", "unit": "1k_tokens", "models": { "gpt-4o-mini": {"prompt": 0.002, "completion": 0.004} } }
118
+ },
119
+ {
120
+ "type": "ollama",
121
+ "name": "ollama-local",
122
+ "base_url": "http://127.0.0.1:11434",
123
+ "sequence_group": "local_gpu_01",
124
+ "concurrency": 1,
125
+ "supports": { "chat": ["llama3.2:latest", "gpt-oss:120b"], "completions": ["llama3.2:latest"], "embeddings": ["nomic-embed-text"] },
126
+ "cost": {
127
+ "models": {
128
+ "llama3.2:latest": {"prompt": 0.0, "completion": 0.0},
129
+ "gpt-oss:120b": {"prompt": 0.001, "completion": 0.001 }
130
+ }
131
+ }
132
+ }
133
+ ]
134
+ }
135
+ ```
136
+
137
+ Example `apps.json` (one app, you just declare them one after each other), the API keys are threatened
138
+ transparent, you can use any string as Bearer token, it just has to be unique; the app ID is used in filemanes
139
+ so you might not want to use special characters:
140
+
141
+ ```json
142
+ {
143
+ "apps": [
144
+ {
145
+ "app_id": "demo",
146
+ "name": "Demo application",
147
+ "api_keys": [
148
+ "sk-example-key"
149
+ ],
150
+ "policy": {
151
+ "allow": [ "gpt-4o-mini", "llama3.2" ],
152
+ "deny": []
153
+ },
154
+ "cost_limit": {
155
+ "period": "day",
156
+ "limit": 10.0
157
+ },
158
+ "trace": {
159
+ "file": "/var/log/llmgw/demo.jsonl",
160
+ "image_dir": "/var/log/llmgw/images/demo",
161
+ "include_prompts": true,
162
+ "include_response": true,
163
+ "include_keys": true
164
+ }
165
+ }
166
+ ]
167
+ }
168
+ ```
169
+
170
+ Notes:
171
+
172
+ - Backends declare capabilities via `supports` (globs allowed) and optional `aliases`.
173
+ Use `concurrency` and `sequence_group` to tune throughput/serialization. Costs
174
+ under `cost` are used to estimate per‑app spend. This may of course deviate from real
175
+ platform billing. It's just there to provide an estimate (and may be reworked later on).
176
+ Also billing is currently not tracked for images.
177
+ - Apps bind one or more API keys to an `app_id`, use `policy.allow`/`policy.deny` to
178
+ restrict models, and `cost_limit` to enforce soft limits. Per‑app traces can be persisted
179
+ as JSONL with optional image capture.
180
+ - The admin interface binds on the same port (due to limitations in FastAPI code and simplicity).
181
+ It is restricted to localhost by default, beware of limitations of this kind of filtering
182
+ when running local jails; additional networks can be allow‑listed with CIDRs in `admin.stats_networks`.
183
+
184
+ ## Running
185
+
186
+ The package installs a console entry point `mini-apigw`.
187
+
188
+ Foreground server_
189
+
190
+ ```bash
191
+ mini-apigw start --config-dir ./config --foreground --reload
192
+ ```
193
+
194
+ Daemonize with defaults from `daemon.json` (port/host or Unix socket):
195
+
196
+ ```bash
197
+ mini-apigw start --config-dir ./config
198
+ ```
199
+
200
+ Override listener explicitly:
201
+
202
+ ```bash
203
+ mini-apigw start --config-dir ./config --host 0.0.0.0 --port 8080
204
+ # or use a Unix domain socket
205
+ mini-apigw start --config-dir ./config --unix-socket /var/llmgw/llmgw.sock
206
+ ```
207
+
208
+ Admin helpers (call built‑in admin endpoints):
209
+
210
+ ```bash
211
+ mini-apigw reload --config-dir ./config
212
+ mini-apigw stop --config-dir ./config
213
+ mini-apigw token --bytes 32 # generate a random API key
214
+ ```
215
+
216
+ ## OpenAI‑Compatible API
217
+
218
+ All endpoints live under `/v1` and require `Authorization: Bearer <api_key>`.
219
+
220
+ List models (combines declared and auto‑discovered where available):
221
+
222
+ ```bash
223
+ curl -s -H "Authorization: Bearer sk-demo-key" http://127.0.0.1:8080/v1/models | jq .
224
+ ```
225
+
226
+ Chat completions (JSON response; set `stream: true` for SSE):
227
+
228
+ ```bash
229
+ curl -s \
230
+ -H "Authorization: Bearer sk-demo-key" \
231
+ -H "Content-Type: application/json" \
232
+ -d '{
233
+ "model": "gpt-4o-mini",
234
+ "messages": [
235
+ {"role": "system", "content": "You are helpful."},
236
+ {"role": "user", "content": "Say hi"}
237
+ ],
238
+ "stream": false
239
+ }' \
240
+ http://127.0.0.1:8080/v1/chat/completions | jq .
241
+ ```
242
+
243
+ Embeddings:
244
+
245
+ ```bash
246
+ curl -s \
247
+ -H "Authorization: Bearer sk-demo-key" \
248
+ -H "Content-Type: application/json" \
249
+ -d '{"model": "text-embedding-3-small", "input": "hello"}' \
250
+ http://127.0.0.1:8080/v1/embeddings | jq .
251
+ ```
252
+
253
+ Image generation (if supported by a configured backend):
254
+
255
+ ```bash
256
+ curl -s \
257
+ -H "Authorization: Bearer sk-demo-key" \
258
+ -H "Content-Type: application/json" \
259
+ -d '{"model": "gpt-image-1", "prompt": "a lighthouse at dusk"}' \
260
+ http://127.0.0.1:8080/v1/images/generations | jq .
261
+ ```
262
+
263
+ ## Accounting and Persistence
264
+
265
+ mini-apigw estimates request cost using backend‑specific `cost` rates and tracked token
266
+ usage (this is work in progress), and aggregates totals per app. By default, accounting
267
+ is in‑memory. To persist usage to Postgres for reporting or bootstrapping daily totals,
268
+ set the `database` section in `daemon.json` and apply the schema in `sql/schema.sql`.
269
+
270
+ Notes:
271
+
272
+ - Postgres is optional. If configured, the gateway uses `psycopg`/`psycopg2` to
273
+ insert request rows asynchronously and reconstructs the current‑day state on startup.
274
+ - Cost limits (`apps[].cost_limit`) are enforced against the running totals; requests
275
+ beyond the limit are rejected with `403`.
276
+
277
+ ## Tracing
278
+
279
+ Per‑app tracing can write JSONL events and capture images from generation responses.
280
+ Enable under `apps[].trace` with `file` and/or `image_dir`. You can include prompts,
281
+ responses (non‑streaming), and masked API keys with `include_prompts`, `include_response`,
282
+ and `include_keys`.
283
+
284
+ Trace files are append‑only JSONL; image files are written under `imagedir` with content‑based
285
+ extensions for base64 payloads, or a `.txt` containing the URL for URL‑based images.
286
+
287
+ ## Admin and Stats
288
+
289
+ Admin endpoints are local‑only by default (IPv4/IPv6 loopback) and can be extended
290
+ with `admin.stats_networks` CIDR allow‑lists.
291
+
292
+ - `POST /admin/reload` — reload configuration files atomically
293
+ - `POST /admin/shutdown` — request a graceful stop
294
+ - `GET /stats/live` — in‑flight/queue stats per backend and sequence group
295
+ - `GET /stats/usage?app_id=<id>` — current usage snapshot (optionally filtered)
296
+
297
+ The `mini-apigw reload` and `stop` CLI commands call these endpoints using the admin
298
+ bind defined in `daemon.json`.
299
+
300
+ Note that at this moment this means any local user can control shutdown and reload! This
301
+ is __work in progress__
302
+
303
+ ## Deployment Notes
304
+
305
+ The CLI embeds Uvicorn and supports IPv4/IPv6 hosts or a Unix domain socket. For production, consider:
306
+
307
+ - running behind a reverse proxy (TLS termination, headers)
308
+ - setting `logging.redact_prompts` to `true` unless needed
309
+ - using `sequence_group` and `concurrency` to match GPU/CPU constraints
310
+
311
+ ## License
312
+
313
+ See `LICENSE.md` for the full text.
@@ -0,0 +1,284 @@
1
+ # mini-apigw — Minimal OpenAI‑compatible API Gateway
2
+
3
+ __WORK IN PROGRESS_
4
+
5
+ mini-apigw is a small edge gateway that presents an OpenAI‑compatible API surface and routes requests
6
+ to multiple LLM and image generation backends. It is designed for __simplicity__ and ease of control: you
7
+ configure backends and apps in JSON, set policies and cost limits per app, and the gateway handles
8
+ routing, scheduling, usage accounting, optional persistence, trace logging, and admin endpoints.
9
+
10
+ The main reason I developed this gateway was to handle multiple access to shared resources - I personally
11
+ utilize machines that run LLMs (ollama, vllm) as well as SDXL based image generation and other software. They
12
+ compete for GPU resources and since those tools are usually not developed to work together nicely and
13
+ arbitrate GPU usage the gateway offers serialization of requests inside _sequence groups_. All backends
14
+ in the same sequence group execute requests of all backends in the same group strictly in sequence so the
15
+ local backends can handle loading and unloading of competing backends.
16
+
17
+ Backends included out of the box:
18
+
19
+ * OpenAI
20
+ * Ollama
21
+
22
+ In development:
23
+
24
+ * Anthropic
25
+ * Fooocus
26
+ * vLLM
27
+
28
+ The gateway exposes the familiar `/v1` endpoints (`/chat/completions`, `/completions`,
29
+ `/embeddings`, `/images/generations`, and `/models`) and normalizes responses where
30
+ needed. This allows one to use the ```openai``` client library for any backend. Note that the gateway
31
+ does not implement the full OpenAI API - it just passes through the above mentioned
32
+ endpoints. In addition it uses local API keys. Those API keys are used to select the application
33
+ from ```apps.json``` configuration file.
34
+
35
+ ## Installation
36
+
37
+ From PyPI (recommended):
38
+
39
+ ```bash
40
+ pip install mini-apigw
41
+ ```
42
+
43
+ From source (editable):
44
+
45
+ ```bash
46
+ pip install -e .
47
+ ```
48
+
49
+ ## Configuration
50
+
51
+ The gateway reads three JSON files from a configuration directory. By default this
52
+ is `./config` (at the moment, this will be fixed soon, this will be a breaking change!),
53
+ or override with the environment variable `MINIAPIGW_CONFIG_DIR` or the CLI flag `--config-dir`.
54
+
55
+ Required files:
56
+
57
+ - `daemon.json`: service, logging, admin, timeouts, and optional Postgres settings
58
+ - `backends.json`: model providers, aliases, costs, and capabilities
59
+ - `apps.json`: application definitions, API keys, allow/deny policies, cost limits, and tracing
60
+
61
+ Example `daemon.json` (minimal, no persistent accounting log):
62
+
63
+ ```json
64
+ {
65
+ "listen": { "host_v4": "0.0.0.0", "port": 8080 },
66
+ "admin": { "bind": ["127.0.0.1:8081", "[::1]:8081"], "stats_networks": ["127.0.0.1/32", "::1/128"] },
67
+ "logging": { "level": "INFO", "redact_prompts": true, "access_log": true },
68
+ "reload": { "enable_sighup": true },
69
+ "timeouts": { "default_connect_s": 60, "default_read_s": 600 },
70
+ "database": null
71
+ }
72
+ ```
73
+
74
+ Example `backends.json` (mixed OpenAI + Ollama):
75
+
76
+ ```json
77
+ {
78
+ "aliases": { "llama3.2": "llama3.2:latest" },
79
+ "sequence_groups": { "local_gpu_01": { "description": "Serialized work for local GPU tasks" } },
80
+ "backends": [
81
+ {
82
+ "type": "openai",
83
+ "name": "openai-primary",
84
+ "base_url": "https://api.openai.com/v1",
85
+ "api_key": "<openai_key>",
86
+ "concurrency": 4,
87
+ "supports": { "chat": ["gpt-4o-mini"], "embeddings": ["text-embedding-3-small"], "images": ["gpt-image-1", "dall-e-3"] },
88
+ "cost": { "currency": "usd", "unit": "1k_tokens", "models": { "gpt-4o-mini": {"prompt": 0.002, "completion": 0.004} } }
89
+ },
90
+ {
91
+ "type": "ollama",
92
+ "name": "ollama-local",
93
+ "base_url": "http://127.0.0.1:11434",
94
+ "sequence_group": "local_gpu_01",
95
+ "concurrency": 1,
96
+ "supports": { "chat": ["llama3.2:latest", "gpt-oss:120b"], "completions": ["llama3.2:latest"], "embeddings": ["nomic-embed-text"] },
97
+ "cost": {
98
+ "models": {
99
+ "llama3.2:latest": {"prompt": 0.0, "completion": 0.0},
100
+ "gpt-oss:120b": {"prompt": 0.001, "completion": 0.001 }
101
+ }
102
+ }
103
+ }
104
+ ]
105
+ }
106
+ ```
107
+
108
+ Example `apps.json` (one app, you just declare them one after each other), the API keys are threatened
109
+ transparent, you can use any string as Bearer token, it just has to be unique; the app ID is used in filemanes
110
+ so you might not want to use special characters:
111
+
112
+ ```json
113
+ {
114
+ "apps": [
115
+ {
116
+ "app_id": "demo",
117
+ "name": "Demo application",
118
+ "api_keys": [
119
+ "sk-example-key"
120
+ ],
121
+ "policy": {
122
+ "allow": [ "gpt-4o-mini", "llama3.2" ],
123
+ "deny": []
124
+ },
125
+ "cost_limit": {
126
+ "period": "day",
127
+ "limit": 10.0
128
+ },
129
+ "trace": {
130
+ "file": "/var/log/llmgw/demo.jsonl",
131
+ "image_dir": "/var/log/llmgw/images/demo",
132
+ "include_prompts": true,
133
+ "include_response": true,
134
+ "include_keys": true
135
+ }
136
+ }
137
+ ]
138
+ }
139
+ ```
140
+
141
+ Notes:
142
+
143
+ - Backends declare capabilities via `supports` (globs allowed) and optional `aliases`.
144
+ Use `concurrency` and `sequence_group` to tune throughput/serialization. Costs
145
+ under `cost` are used to estimate per‑app spend. This may of course deviate from real
146
+ platform billing. It's just there to provide an estimate (and may be reworked later on).
147
+ Also billing is currently not tracked for images.
148
+ - Apps bind one or more API keys to an `app_id`, use `policy.allow`/`policy.deny` to
149
+ restrict models, and `cost_limit` to enforce soft limits. Per‑app traces can be persisted
150
+ as JSONL with optional image capture.
151
+ - The admin interface binds on the same port (due to limitations in FastAPI code and simplicity).
152
+ It is restricted to localhost by default, beware of limitations of this kind of filtering
153
+ when running local jails; additional networks can be allow‑listed with CIDRs in `admin.stats_networks`.
154
+
155
+ ## Running
156
+
157
+ The package installs a console entry point `mini-apigw`.
158
+
159
+ Foreground server_
160
+
161
+ ```bash
162
+ mini-apigw start --config-dir ./config --foreground --reload
163
+ ```
164
+
165
+ Daemonize with defaults from `daemon.json` (port/host or Unix socket):
166
+
167
+ ```bash
168
+ mini-apigw start --config-dir ./config
169
+ ```
170
+
171
+ Override listener explicitly:
172
+
173
+ ```bash
174
+ mini-apigw start --config-dir ./config --host 0.0.0.0 --port 8080
175
+ # or use a Unix domain socket
176
+ mini-apigw start --config-dir ./config --unix-socket /var/llmgw/llmgw.sock
177
+ ```
178
+
179
+ Admin helpers (call built‑in admin endpoints):
180
+
181
+ ```bash
182
+ mini-apigw reload --config-dir ./config
183
+ mini-apigw stop --config-dir ./config
184
+ mini-apigw token --bytes 32 # generate a random API key
185
+ ```
186
+
187
+ ## OpenAI‑Compatible API
188
+
189
+ All endpoints live under `/v1` and require `Authorization: Bearer <api_key>`.
190
+
191
+ List models (combines declared and auto‑discovered where available):
192
+
193
+ ```bash
194
+ curl -s -H "Authorization: Bearer sk-demo-key" http://127.0.0.1:8080/v1/models | jq .
195
+ ```
196
+
197
+ Chat completions (JSON response; set `stream: true` for SSE):
198
+
199
+ ```bash
200
+ curl -s \
201
+ -H "Authorization: Bearer sk-demo-key" \
202
+ -H "Content-Type: application/json" \
203
+ -d '{
204
+ "model": "gpt-4o-mini",
205
+ "messages": [
206
+ {"role": "system", "content": "You are helpful."},
207
+ {"role": "user", "content": "Say hi"}
208
+ ],
209
+ "stream": false
210
+ }' \
211
+ http://127.0.0.1:8080/v1/chat/completions | jq .
212
+ ```
213
+
214
+ Embeddings:
215
+
216
+ ```bash
217
+ curl -s \
218
+ -H "Authorization: Bearer sk-demo-key" \
219
+ -H "Content-Type: application/json" \
220
+ -d '{"model": "text-embedding-3-small", "input": "hello"}' \
221
+ http://127.0.0.1:8080/v1/embeddings | jq .
222
+ ```
223
+
224
+ Image generation (if supported by a configured backend):
225
+
226
+ ```bash
227
+ curl -s \
228
+ -H "Authorization: Bearer sk-demo-key" \
229
+ -H "Content-Type: application/json" \
230
+ -d '{"model": "gpt-image-1", "prompt": "a lighthouse at dusk"}' \
231
+ http://127.0.0.1:8080/v1/images/generations | jq .
232
+ ```
233
+
234
+ ## Accounting and Persistence
235
+
236
+ mini-apigw estimates request cost using backend‑specific `cost` rates and tracked token
237
+ usage (this is work in progress), and aggregates totals per app. By default, accounting
238
+ is in‑memory. To persist usage to Postgres for reporting or bootstrapping daily totals,
239
+ set the `database` section in `daemon.json` and apply the schema in `sql/schema.sql`.
240
+
241
+ Notes:
242
+
243
+ - Postgres is optional. If configured, the gateway uses `psycopg`/`psycopg2` to
244
+ insert request rows asynchronously and reconstructs the current‑day state on startup.
245
+ - Cost limits (`apps[].cost_limit`) are enforced against the running totals; requests
246
+ beyond the limit are rejected with `403`.
247
+
248
+ ## Tracing
249
+
250
+ Per‑app tracing can write JSONL events and capture images from generation responses.
251
+ Enable under `apps[].trace` with `file` and/or `image_dir`. You can include prompts,
252
+ responses (non‑streaming), and masked API keys with `include_prompts`, `include_response`,
253
+ and `include_keys`.
254
+
255
+ Trace files are append‑only JSONL; image files are written under `imagedir` with content‑based
256
+ extensions for base64 payloads, or a `.txt` containing the URL for URL‑based images.
257
+
258
+ ## Admin and Stats
259
+
260
+ Admin endpoints are local‑only by default (IPv4/IPv6 loopback) and can be extended
261
+ with `admin.stats_networks` CIDR allow‑lists.
262
+
263
+ - `POST /admin/reload` — reload configuration files atomically
264
+ - `POST /admin/shutdown` — request a graceful stop
265
+ - `GET /stats/live` — in‑flight/queue stats per backend and sequence group
266
+ - `GET /stats/usage?app_id=<id>` — current usage snapshot (optionally filtered)
267
+
268
+ The `mini-apigw reload` and `stop` CLI commands call these endpoints using the admin
269
+ bind defined in `daemon.json`.
270
+
271
+ Note that at this moment this means any local user can control shutdown and reload! This
272
+ is __work in progress__
273
+
274
+ ## Deployment Notes
275
+
276
+ The CLI embeds Uvicorn and supports IPv4/IPv6 hosts or a Unix domain socket. For production, consider:
277
+
278
+ - running behind a reverse proxy (TLS termination, headers)
279
+ - setting `logging.redact_prompts` to `true` unless needed
280
+ - using `sequence_group` and `concurrency` to match GPU/CPU constraints
281
+
282
+ ## License
283
+
284
+ See `LICENSE.md` for the full text.
@@ -0,0 +1,3 @@
1
+ __all__ = ["__version__"]
2
+
3
+ __version__ = "0.1.0"