AbteeXAILabs commited on
Commit
ff7e812
·
verified ·
1 Parent(s): c37299b

Publish expanded LumynaX product platform package

Browse files
MANIFEST.in ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ include README.md LICENSE requirements.txt SMOKE_TESTS.md PYPI_RELEASE.md quickstart.py product_manifest.json product_blueprint.md architecture.md gateway_contract.md
2
+ recursive-include marama_route *.py py.typed
3
+ recursive-include marama_route/configs *
4
+ recursive-include marama_route/examples *
5
+ recursive-include marama_route/integrations *
6
+ recursive-include marama_route/schemas *
PYPI_RELEASE.md ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # PyPI Release: LumynaX MaramaRoute
2
+
3
+ Package: `lumynax-marama-route`
4
+ Version: `0.4.0`
5
+
6
+ ## Local Build
7
+
8
+ ```bash
9
+ python -m build
10
+ python -m twine check dist/*
11
+ python quickstart.py
12
+ marama-route serve --smoke
13
+ ```
14
+
15
+ ## Publish
16
+
17
+ ```bash
18
+ python -m twine upload dist/*
19
+ ```
20
+
21
+ Required local credentials: `TWINE_USERNAME=__token__` and `TWINE_PASSWORD=<pypi-token>`.
22
+ The GitHub workflow can publish with PyPI trusted publishing when the PyPI project
23
+ is configured to trust `Aimaghsoodi/TinyLuminaX`.
24
+
25
+ ## Runtime Surface
26
+
27
+ - CLI: `marama-route`
28
+ - Local UI/API: `marama-route serve --port 8787 --open`
29
+ - OpenAI-compatible API: `GET /v1/models`, `POST /v1/route`, `POST /v1/chat/completions`
30
+ - Default mode: route-only, with live OpenAI-compatible backend proxying configured in `configs/gateway.local.json`
README.md CHANGED
@@ -21,10 +21,17 @@ language:
21
  # LumynaX MaramaRoute
22
  <!-- abteex-marama-route-card:v3 -->
23
 
24
- Standalone release package for `AbteeXAILab/marama-route`, bundled with 82 registry entries.
25
 
26
  ## Install
27
 
 
 
 
 
 
 
 
28
  ```bash
29
  hf download AbteeXAILab/marama-route --local-dir marama-route --repo-type model
30
  cd marama-route
@@ -35,6 +42,9 @@ python quickstart.py
35
  ## Included Runtime Commands
36
 
37
  ```bash
 
 
 
38
  python -m marama_route.cli route --registry configs/lumynax_model_registry.json --request examples/request.code-restricted.json
39
  python -m marama_route.cli models --registry configs/lumynax_model_registry.json
40
  python -m marama_route.cli chat-dry-run --registry configs/lumynax_model_registry.json --request examples/request.openai-chat-code.json
@@ -77,6 +87,16 @@ Every request is evaluated through ordered gates:
77
 
78
  ## Quickstart
79
 
 
 
 
 
 
 
 
 
 
 
80
  Build or refresh the registry from the latest HF card report:
81
 
82
  ```bash
@@ -151,6 +171,16 @@ py -3 -m tinyluminax.products.marama_route.cli serve --smoke
151
 
152
  The gateway exposes `GET /health`, `GET /v1/models`, `POST /v1/route`, and `POST /v1/chat/completions`. By default it runs in `route_only` mode and returns an OpenAI-shaped routed response without retaining prompts. Set `products/lumynax-marama-route/configs/gateway.local.json` to `mode: live` and map model ids to OpenAI-compatible backends to proxy real generation.
153
 
 
 
 
 
 
 
 
 
 
 
154
  Smoke-check the UI routes without opening a browser:
155
 
156
  ```bash
 
21
  # LumynaX MaramaRoute
22
  <!-- abteex-marama-route-card:v3 -->
23
 
24
+ Standalone release package for PyPI package `lumynax-marama-route` and Hugging Face repo `AbteeXAILab/marama-route`, bundled with 82 registry entries.
25
 
26
  ## Install
27
 
28
+ ```bash
29
+ pip install lumynax-marama-route
30
+ marama-route serve --smoke
31
+ ```
32
+
33
+ HF mirror install:
34
+
35
  ```bash
36
  hf download AbteeXAILab/marama-route --local-dir marama-route --repo-type model
37
  cd marama-route
 
42
  ## Included Runtime Commands
43
 
44
  ```bash
45
+ marama-route serve --smoke
46
+ marama-route opencode-config --registry configs/lumynax_model_registry.json
47
+ marama-route ui --port 8787 --open
48
  python -m marama_route.cli route --registry configs/lumynax_model_registry.json --request examples/request.code-restricted.json
49
  python -m marama_route.cli models --registry configs/lumynax_model_registry.json
50
  python -m marama_route.cli chat-dry-run --registry configs/lumynax_model_registry.json --request examples/request.openai-chat-code.json
 
87
 
88
  ## Quickstart
89
 
90
+ Install the standalone package:
91
+
92
+ ```bash
93
+ pip install lumynax-marama-route
94
+ marama-route serve --smoke
95
+ marama-route serve --port 8787 --open
96
+ ```
97
+
98
+ The same package is mirrored on Hugging Face at `AbteeXAILab/marama-route` and can be installed from a downloaded snapshot with `pip install -e .`.
99
+
100
  Build or refresh the registry from the latest HF card report:
101
 
102
  ```bash
 
171
 
172
  The gateway exposes `GET /health`, `GET /v1/models`, `POST /v1/route`, and `POST /v1/chat/completions`. By default it runs in `route_only` mode and returns an OpenAI-shaped routed response without retaining prompts. Set `products/lumynax-marama-route/configs/gateway.local.json` to `mode: live` and map model ids to OpenAI-compatible backends to proxy real generation.
173
 
174
+ ## PyPI Release Readiness
175
+
176
+ The generated standalone package includes `pyproject.toml`, `MANIFEST.in`, `SMOKE_TESTS.md`, `PYPI_RELEASE.md`, package data, console script entry points, and wheel/sdist validation support. The release gate is:
177
+
178
+ ```bash
179
+ py -3 scripts/build_product_release_packages.py
180
+ py -3 scripts/verify_product_installs.py --product marama-route
181
+ py -3 scripts/publish_products_to_pypi.py --product marama-route --dry-run
182
+ ```
183
+
184
  Smoke-check the UI routes without opening a browser:
185
 
186
  ```bash
configs/gateway.local.json CHANGED
@@ -1,13 +1,13 @@
1
- {
2
- "mode": "route_only",
3
- "prompt_retention": "not_stored_by_default",
4
- "default_timeout_seconds": 120,
5
- "backends": {
6
- "example-local-openai-compatible": {
7
- "type": "openai_compatible",
8
- "base_url": "http://127.0.0.1:8000/v1",
9
- "api_key_env": "",
10
- "model": "local-model-id"
11
- }
12
- }
13
- }
 
1
+ {
2
+ "mode": "route_only",
3
+ "prompt_retention": "not_stored_by_default",
4
+ "default_timeout_seconds": 120,
5
+ "backends": {
6
+ "example-local-openai-compatible": {
7
+ "type": "openai_compatible",
8
+ "base_url": "http://127.0.0.1:8000/v1",
9
+ "api_key_env": "",
10
+ "model": "local-model-id"
11
+ }
12
+ }
13
+ }
configs/lumynax_model_registry.json CHANGED
@@ -2,7 +2,7 @@
2
  "registry_id": "lumynax-marama-route-registry-v0",
3
  "publisher": "AbteeX AI Labs",
4
  "source_report": "docs/releases/hf-model-card-v4-refresh-2026-05-11.json",
5
- "model_count": 98,
6
  "models": [
7
  {
8
  "model_id": "lumynax-coder-qwen25-05b-instruct-gguf",
@@ -3246,624 +3246,6 @@
3246
  "public_status": "public and non-gated",
3247
  "validation_status": "scaffold_verified"
3248
  }
3249
- },
3250
- {
3251
- "model_id": "lumynax-frontier-coder-qwen3-480b-a35b-gguf",
3252
- "repo_id": "AbteeXAILab/lumynax-frontier-coder-qwen3-480b-a35b-gguf",
3253
- "title": "LumynaX Frontier Coder Qwen3 480B A35B GGUF",
3254
- "family": "coder",
3255
- "runtime": "llama_cpp",
3256
- "modalities": [
3257
- "text"
3258
- ],
3259
- "context_tokens": 262144,
3260
- "jurisdiction": "NZ",
3261
- "residency": [
3262
- "NZ"
3263
- ],
3264
- "license_id": "apache-2.0",
3265
- "quantization": "Q4_K_M GGUF",
3266
- "primary_artifact": "Q4_K_M/Qwen3-Coder-480B-A35B-Instruct-Q4_K_M-00001-of-00006.gguf",
3267
- "active_params_b": 35,
3268
- "total_params_b": 480,
3269
- "quality_rank": 1,
3270
- "cost_rank": 3,
3271
- "sovereignty_tier": 2,
3272
- "supports_tools": true,
3273
- "supports_json": true,
3274
- "tags": [
3275
- "coder",
3276
- "frontier",
3277
- "moe",
3278
- "gguf"
3279
- ],
3280
- "metadata": {
3281
- "upstream_repo": "Qwen/Qwen3-Coder-480B-A35B-Instruct",
3282
- "release_version": "v0.1.0",
3283
- "package_state": "weights_mirrored",
3284
- "public_status": "public and non-gated",
3285
- "validation_status": "scaffold_verified"
3286
- }
3287
- },
3288
- {
3289
- "model_id": "lumynax-frontier-coder-deepseek-v25-1210-gguf",
3290
- "repo_id": "AbteeXAILab/lumynax-frontier-coder-deepseek-v25-1210-gguf",
3291
- "title": "LumynaX Frontier Coder DeepSeek V2.5-1210 GGUF",
3292
- "family": "coder",
3293
- "runtime": "llama_cpp",
3294
- "modalities": [
3295
- "text"
3296
- ],
3297
- "context_tokens": 163840,
3298
- "jurisdiction": "NZ",
3299
- "residency": [
3300
- "NZ"
3301
- ],
3302
- "license_id": "other",
3303
- "quantization": "Q4_K_M GGUF",
3304
- "primary_artifact": "DeepSeek-V2.5-1210-Q4_K_M/DeepSeek-V2.5-1210-Q4_K_M-00001-of-00004.gguf",
3305
- "active_params_b": 21,
3306
- "total_params_b": 236,
3307
- "quality_rank": 1,
3308
- "cost_rank": 3,
3309
- "sovereignty_tier": 2,
3310
- "supports_tools": true,
3311
- "supports_json": true,
3312
- "tags": [
3313
- "coder",
3314
- "frontier",
3315
- "moe",
3316
- "gguf"
3317
- ],
3318
- "metadata": {
3319
- "upstream_repo": "deepseek-ai/DeepSeek-V2.5-1210",
3320
- "release_version": "v0.1.0",
3321
- "package_state": "weights_mirrored",
3322
- "public_status": "public and non-gated",
3323
- "validation_status": "scaffold_verified"
3324
- }
3325
- },
3326
- {
3327
- "model_id": "lumynax-coder-codellama-70b-instruct-gguf",
3328
- "repo_id": "AbteeXAILab/lumynax-coder-codellama-70b-instruct-gguf",
3329
- "title": "LumynaX Coder CodeLlama 70B Instruct GGUF",
3330
- "family": "codellama",
3331
- "runtime": "llama_cpp",
3332
- "modalities": [
3333
- "text"
3334
- ],
3335
- "context_tokens": 16384,
3336
- "jurisdiction": "NZ",
3337
- "residency": [
3338
- "NZ"
3339
- ],
3340
- "license_id": "other",
3341
- "quantization": "Q4_K_M GGUF",
3342
- "primary_artifact": "codellama-70b-instruct.Q4_K_M.gguf",
3343
- "active_params_b": null,
3344
- "total_params_b": 70,
3345
- "quality_rank": 2,
3346
- "cost_rank": 3,
3347
- "sovereignty_tier": 3,
3348
- "supports_tools": true,
3349
- "supports_json": true,
3350
- "tags": [
3351
- "coder",
3352
- "frontier",
3353
- "moe",
3354
- "gguf"
3355
- ],
3356
- "metadata": {
3357
- "upstream_repo": "codellama/CodeLlama-70b-Instruct-hf",
3358
- "release_version": "v0.1.0",
3359
- "package_state": "weights_mirrored",
3360
- "public_status": "public and non-gated",
3361
- "validation_status": "scaffold_verified"
3362
- }
3363
- },
3364
- {
3365
- "model_id": "lumynax-coder-deepseek-coder-33b-gguf",
3366
- "repo_id": "AbteeXAILab/lumynax-coder-deepseek-coder-33b-gguf",
3367
- "title": "LumynaX Coder DeepSeek-Coder 33B GGUF",
3368
- "family": "deepseek",
3369
- "runtime": "llama_cpp",
3370
- "modalities": [
3371
- "text"
3372
- ],
3373
- "context_tokens": 16384,
3374
- "jurisdiction": "NZ",
3375
- "residency": [
3376
- "NZ"
3377
- ],
3378
- "license_id": "other",
3379
- "quantization": "Q4_K_M GGUF",
3380
- "primary_artifact": "deepseek-coder-33b-instruct.Q4_K_M.gguf",
3381
- "active_params_b": null,
3382
- "total_params_b": 33,
3383
- "quality_rank": 2,
3384
- "cost_rank": 3,
3385
- "sovereignty_tier": 3,
3386
- "supports_tools": true,
3387
- "supports_json": true,
3388
- "tags": [
3389
- "coder",
3390
- "frontier",
3391
- "moe",
3392
- "gguf"
3393
- ],
3394
- "metadata": {
3395
- "upstream_repo": "deepseek-ai/deepseek-coder-33b-instruct",
3396
- "release_version": "v0.1.0",
3397
- "package_state": "weights_mirrored",
3398
- "public_status": "public and non-gated",
3399
- "validation_status": "scaffold_verified"
3400
- }
3401
- },
3402
- {
3403
- "model_id": "lumynax-coder-qwen25-coder-32b-gguf",
3404
- "repo_id": "AbteeXAILab/lumynax-coder-qwen25-coder-32b-gguf",
3405
- "title": "LumynaX Coder Qwen2.5-Coder 32B Instruct GGUF",
3406
- "family": "qwen25",
3407
- "runtime": "llama_cpp",
3408
- "modalities": [
3409
- "text"
3410
- ],
3411
- "context_tokens": 131072,
3412
- "jurisdiction": "NZ",
3413
- "residency": [
3414
- "NZ"
3415
- ],
3416
- "license_id": "apache-2.0",
3417
- "quantization": "Q4_K_M GGUF",
3418
- "primary_artifact": "Qwen2.5-Coder-32B-Instruct-Q4_K_M.gguf",
3419
- "active_params_b": null,
3420
- "total_params_b": 32,
3421
- "quality_rank": 2,
3422
- "cost_rank": 3,
3423
- "sovereignty_tier": 3,
3424
- "supports_tools": true,
3425
- "supports_json": true,
3426
- "tags": [
3427
- "coder",
3428
- "frontier",
3429
- "moe",
3430
- "gguf"
3431
- ],
3432
- "metadata": {
3433
- "upstream_repo": "Qwen/Qwen2.5-Coder-32B-Instruct",
3434
- "release_version": "v0.1.0",
3435
- "package_state": "weights_mirrored",
3436
- "public_status": "public and non-gated",
3437
- "validation_status": "scaffold_verified"
3438
- }
3439
- },
3440
- {
3441
- "model_id": "lumynax-coder-starcoder2-15b-gguf",
3442
- "repo_id": "AbteeXAILab/lumynax-coder-starcoder2-15b-gguf",
3443
- "title": "LumynaX Coder StarCoder2 15B Instruct GGUF",
3444
- "family": "starcoder2",
3445
- "runtime": "llama_cpp",
3446
- "modalities": [
3447
- "text"
3448
- ],
3449
- "context_tokens": 16384,
3450
- "jurisdiction": "NZ",
3451
- "residency": [
3452
- "NZ"
3453
- ],
3454
- "license_id": "apache-2.0",
3455
- "quantization": "Q4_K_M GGUF",
3456
- "primary_artifact": "starcoder2-15b-instruct-v0.1-Q4_K_M.gguf",
3457
- "active_params_b": null,
3458
- "total_params_b": 15,
3459
- "quality_rank": 2,
3460
- "cost_rank": 3,
3461
- "sovereignty_tier": 3,
3462
- "supports_tools": true,
3463
- "supports_json": true,
3464
- "tags": [
3465
- "coder",
3466
- "coding",
3467
- "moe",
3468
- "gguf"
3469
- ],
3470
- "metadata": {
3471
- "upstream_repo": "bigcode/starcoder2-15b-instruct-v0.1",
3472
- "release_version": "v0.1.0",
3473
- "package_state": "weights_mirrored",
3474
- "public_status": "public and non-gated",
3475
- "validation_status": "scaffold_verified"
3476
- }
3477
- },
3478
- {
3479
- "model_id": "lumynax-coder-yi-coder-9b-gguf",
3480
- "repo_id": "AbteeXAILab/lumynax-coder-yi-coder-9b-gguf",
3481
- "title": "LumynaX Coder Yi-Coder 9B Chat GGUF",
3482
- "family": "yi",
3483
- "runtime": "llama_cpp",
3484
- "modalities": [
3485
- "text"
3486
- ],
3487
- "context_tokens": 131072,
3488
- "jurisdiction": "NZ",
3489
- "residency": [
3490
- "NZ"
3491
- ],
3492
- "license_id": "other",
3493
- "quantization": "Q4_K_M GGUF",
3494
- "primary_artifact": "Yi-Coder-9B-Chat-Q4_K_M.gguf",
3495
- "active_params_b": null,
3496
- "total_params_b": 9,
3497
- "quality_rank": 2,
3498
- "cost_rank": 3,
3499
- "sovereignty_tier": 3,
3500
- "supports_tools": true,
3501
- "supports_json": true,
3502
- "tags": [
3503
- "coder",
3504
- "coding",
3505
- "moe",
3506
- "gguf"
3507
- ],
3508
- "metadata": {
3509
- "upstream_repo": "01-ai/Yi-Coder-9B-Chat",
3510
- "release_version": "v0.1.0",
3511
- "package_state": "weights_mirrored",
3512
- "public_status": "public and non-gated",
3513
- "validation_status": "scaffold_verified"
3514
- }
3515
- },
3516
- {
3517
- "model_id": "lumynax-coder-codeqwen15-7b-chat-gguf",
3518
- "repo_id": "AbteeXAILab/lumynax-coder-codeqwen15-7b-chat-gguf",
3519
- "title": "LumynaX Coder CodeQwen1.5 7B Chat GGUF",
3520
- "family": "codeqwen15",
3521
- "runtime": "llama_cpp",
3522
- "modalities": [
3523
- "text"
3524
- ],
3525
- "context_tokens": 65536,
3526
- "jurisdiction": "NZ",
3527
- "residency": [
3528
- "NZ"
3529
- ],
3530
- "license_id": "apache-2.0",
3531
- "quantization": "Q4_K_M GGUF",
3532
- "primary_artifact": "codeqwen-1_5-7b-chat-q4_k_m.gguf",
3533
- "active_params_b": null,
3534
- "total_params_b": 7,
3535
- "quality_rank": 2,
3536
- "cost_rank": 3,
3537
- "sovereignty_tier": 3,
3538
- "supports_tools": true,
3539
- "supports_json": true,
3540
- "tags": [
3541
- "coder",
3542
- "coding",
3543
- "moe",
3544
- "gguf"
3545
- ],
3546
- "metadata": {
3547
- "upstream_repo": "Qwen/CodeQwen1.5-7B-Chat",
3548
- "release_version": "v0.1.0",
3549
- "package_state": "weights_mirrored",
3550
- "public_status": "public and non-gated",
3551
- "validation_status": "scaffold_verified"
3552
- }
3553
- },
3554
- {
3555
- "model_id": "lumynax-reasoning-deepseek-prover-v2-671b-gguf",
3556
- "repo_id": "AbteeXAILab/lumynax-reasoning-deepseek-prover-v2-671b-gguf",
3557
- "title": "LumynaX Reasoning DeepSeek-Prover V2 671B GGUF",
3558
- "family": "deepseek",
3559
- "runtime": "llama_cpp",
3560
- "modalities": [
3561
- "text"
3562
- ],
3563
- "context_tokens": 163840,
3564
- "jurisdiction": "NZ",
3565
- "residency": [
3566
- "NZ"
3567
- ],
3568
- "license_id": "other",
3569
- "quantization": "Q4_K_M GGUF (sharded)",
3570
- "primary_artifact": "Q4_K_M/DeepSeek-Prover-V2-671B-Q4_K_M-00001-of-00009.gguf",
3571
- "active_params_b": 37,
3572
- "total_params_b": 671,
3573
- "quality_rank": 1,
3574
- "cost_rank": 5,
3575
- "sovereignty_tier": 2,
3576
- "supports_tools": true,
3577
- "supports_json": true,
3578
- "tags": [
3579
- "reasoning",
3580
- "math",
3581
- "proof",
3582
- "deepseek",
3583
- "moe",
3584
- "gguf",
3585
- "frontier"
3586
- ],
3587
- "metadata": {
3588
- "upstream_repo": "deepseek-ai/DeepSeek-Prover-V2-671B",
3589
- "release_version": "v0.1.0",
3590
- "package_state": "weights_mirrored",
3591
- "public_status": "public and non-gated",
3592
- "validation_status": "scaffold_verified"
3593
- }
3594
- },
3595
- {
3596
- "model_id": "lumynax-reasoning-deepseek-r1-distill-llama-70b-gguf",
3597
- "repo_id": "AbteeXAILab/lumynax-reasoning-deepseek-r1-distill-llama-70b-gguf",
3598
- "title": "LumynaX Reasoning DeepSeek-R1 Distill Llama 70B GGUF",
3599
- "family": "deepseek",
3600
- "runtime": "llama_cpp",
3601
- "modalities": [
3602
- "text"
3603
- ],
3604
- "context_tokens": 131072,
3605
- "jurisdiction": "NZ",
3606
- "residency": [
3607
- "NZ"
3608
- ],
3609
- "license_id": "llama3.3",
3610
- "quantization": "Q4_K_M GGUF",
3611
- "primary_artifact": "DeepSeek-R1-Distill-Llama-70B-Q4_K_M.gguf",
3612
- "active_params_b": null,
3613
- "total_params_b": 70,
3614
- "quality_rank": 1,
3615
- "cost_rank": 4,
3616
- "sovereignty_tier": 3,
3617
- "supports_tools": true,
3618
- "supports_json": true,
3619
- "tags": [
3620
- "reasoning",
3621
- "r1-distill",
3622
- "deepseek",
3623
- "llama",
3624
- "gguf",
3625
- "chain-of-thought"
3626
- ],
3627
- "metadata": {
3628
- "upstream_repo": "deepseek-ai/DeepSeek-R1-Distill-Llama-70B",
3629
- "release_version": "v0.1.0",
3630
- "package_state": "weights_mirrored",
3631
- "public_status": "public and non-gated",
3632
- "validation_status": "scaffold_verified"
3633
- }
3634
- },
3635
- {
3636
- "model_id": "lumynax-longctx-prolong-512k-instruct",
3637
- "repo_id": "AbteeXAILab/lumynax-longctx-prolong-512k-instruct",
3638
- "title": "LumynaX Long-Context ProLong-512K Instruct",
3639
- "family": "llama",
3640
- "runtime": "transformers",
3641
- "modalities": [
3642
- "text"
3643
- ],
3644
- "context_tokens": 524288,
3645
- "jurisdiction": "NZ",
3646
- "residency": [
3647
- "NZ",
3648
- "AU",
3649
- "global"
3650
- ],
3651
- "license_id": "llama3",
3652
- "quantization": "bf16 safetensors (sharded)",
3653
- "primary_artifact": "model.safetensors.index.json",
3654
- "active_params_b": null,
3655
- "total_params_b": 8,
3656
- "quality_rank": 2,
3657
- "cost_rank": 3,
3658
- "sovereignty_tier": 3,
3659
- "supports_tools": false,
3660
- "supports_json": true,
3661
- "tags": [
3662
- "long-context",
3663
- "512k",
3664
- "prolong",
3665
- "reasoning"
3666
- ],
3667
- "metadata": {
3668
- "upstream_repo": "princeton-nlp/Llama-3-8B-ProLong-512k-Instruct",
3669
- "release_version": "v0.1.0",
3670
- "package_state": "weights_mirrored",
3671
- "public_status": "public and non-gated",
3672
- "validation_status": "scaffold_verified"
3673
- }
3674
- },
3675
- {
3676
- "model_id": "lumynax-frontier-phi-35-moe-instruct-gguf",
3677
- "repo_id": "AbteeXAILab/lumynax-frontier-phi-35-moe-instruct-gguf",
3678
- "title": "LumynaX Frontier Phi-3.5 MoE Instruct GGUF",
3679
- "family": "phi",
3680
- "runtime": "llama_cpp",
3681
- "modalities": [
3682
- "text"
3683
- ],
3684
- "context_tokens": 131072,
3685
- "jurisdiction": "NZ",
3686
- "residency": [
3687
- "NZ"
3688
- ],
3689
- "license_id": "mit",
3690
- "quantization": "Q4_K_M GGUF",
3691
- "primary_artifact": "Phi-3.5-MoE-instruct-Q4_K_M.gguf",
3692
- "active_params_b": 6.6,
3693
- "total_params_b": 42,
3694
- "quality_rank": 1,
3695
- "cost_rank": 2,
3696
- "sovereignty_tier": 3,
3697
- "supports_tools": true,
3698
- "supports_json": true,
3699
- "tags": [
3700
- "frontier",
3701
- "moe",
3702
- "phi",
3703
- "microsoft",
3704
- "gguf"
3705
- ],
3706
- "metadata": {
3707
- "upstream_repo": "microsoft/Phi-3.5-MoE-instruct",
3708
- "release_version": "v0.1.0",
3709
- "package_state": "weights_mirrored",
3710
- "public_status": "public and non-gated",
3711
- "validation_status": "scaffold_verified"
3712
- }
3713
- },
3714
- {
3715
- "model_id": "lumynax-longctx-yi-9b-200k",
3716
- "repo_id": "AbteeXAILab/lumynax-longctx-yi-9b-200k",
3717
- "title": "LumynaX Long-Context Yi-9B 200K",
3718
- "family": "yi",
3719
- "runtime": "transformers",
3720
- "modalities": [
3721
- "text"
3722
- ],
3723
- "context_tokens": 204800,
3724
- "jurisdiction": "NZ",
3725
- "residency": [
3726
- "NZ",
3727
- "AU",
3728
- "global"
3729
- ],
3730
- "license_id": "apache-2.0",
3731
- "quantization": "bf16 safetensors (sharded)",
3732
- "primary_artifact": "model.safetensors.index.json",
3733
- "active_params_b": null,
3734
- "total_params_b": 9,
3735
- "quality_rank": 2,
3736
- "cost_rank": 3,
3737
- "sovereignty_tier": 3,
3738
- "supports_tools": false,
3739
- "supports_json": true,
3740
- "tags": [
3741
- "long-context",
3742
- "200k",
3743
- "yi"
3744
- ],
3745
- "metadata": {
3746
- "upstream_repo": "01-ai/Yi-9B-200K",
3747
- "release_version": "v0.1.0",
3748
- "package_state": "weights_mirrored",
3749
- "public_status": "public and non-gated",
3750
- "validation_status": "scaffold_verified"
3751
- }
3752
- },
3753
- {
3754
- "model_id": "lumynax-longctx-glm4-9b-chat-1m-gguf",
3755
- "repo_id": "AbteeXAILab/lumynax-longctx-glm4-9b-chat-1m-gguf",
3756
- "title": "LumynaX Long-Context GLM-4-9B-Chat-1M GGUF",
3757
- "family": "glm",
3758
- "runtime": "llama_cpp",
3759
- "modalities": [
3760
- "text"
3761
- ],
3762
- "context_tokens": 1048576,
3763
- "jurisdiction": "NZ",
3764
- "residency": [
3765
- "NZ"
3766
- ],
3767
- "license_id": "apache-2.0",
3768
- "quantization": "Q4_K_M GGUF",
3769
- "primary_artifact": "glm-4-9b-chat-1m-Q4_K_M.gguf",
3770
- "active_params_b": null,
3771
- "total_params_b": 9,
3772
- "quality_rank": 2,
3773
- "cost_rank": 2,
3774
- "sovereignty_tier": 3,
3775
- "supports_tools": true,
3776
- "supports_json": true,
3777
- "tags": [
3778
- "long-context",
3779
- "1m",
3780
- "glm",
3781
- "gguf"
3782
- ],
3783
- "metadata": {
3784
- "upstream_repo": "THUDM/glm-4-9b-chat-1m",
3785
- "release_version": "v0.1.0",
3786
- "package_state": "weights_mirrored",
3787
- "public_status": "public and non-gated",
3788
- "validation_status": "scaffold_verified"
3789
- }
3790
- },
3791
- {
3792
- "model_id": "lumynax-longctx-qwen25-7b-1m-gguf",
3793
- "repo_id": "AbteeXAILab/lumynax-longctx-qwen25-7b-1m-gguf",
3794
- "title": "LumynaX Long-Context Qwen2.5 7B Instruct 1M GGUF",
3795
- "family": "qwen",
3796
- "runtime": "llama_cpp",
3797
- "modalities": [
3798
- "text"
3799
- ],
3800
- "context_tokens": 1048576,
3801
- "jurisdiction": "NZ",
3802
- "residency": [
3803
- "NZ"
3804
- ],
3805
- "license_id": "apache-2.0",
3806
- "quantization": "Q4_K_M GGUF",
3807
- "primary_artifact": "Qwen2.5-7B-Instruct-1M-Q4_K_M.gguf",
3808
- "active_params_b": null,
3809
- "total_params_b": 7,
3810
- "quality_rank": 2,
3811
- "cost_rank": 2,
3812
- "sovereignty_tier": 3,
3813
- "supports_tools": true,
3814
- "supports_json": true,
3815
- "tags": [
3816
- "long-context",
3817
- "1m",
3818
- "qwen",
3819
- "gguf"
3820
- ],
3821
- "metadata": {
3822
- "upstream_repo": "Qwen/Qwen2.5-7B-Instruct-1M",
3823
- "release_version": "v0.1.0",
3824
- "package_state": "weights_mirrored",
3825
- "public_status": "public and non-gated",
3826
- "validation_status": "scaffold_verified"
3827
- }
3828
- },
3829
- {
3830
- "model_id": "lumynax-moe-olmoe-1b-7b-0924-instruct-gguf",
3831
- "repo_id": "AbteeXAILab/lumynax-moe-olmoe-1b-7b-0924-instruct-gguf",
3832
- "title": "LumynaX MoE OLMoE 1B/7B 0924 Instruct GGUF",
3833
- "family": "olmo",
3834
- "runtime": "llama_cpp",
3835
- "modalities": [
3836
- "text"
3837
- ],
3838
- "context_tokens": 4096,
3839
- "jurisdiction": "NZ",
3840
- "residency": [
3841
- "NZ"
3842
- ],
3843
- "license_id": "apache-2.0",
3844
- "quantization": "Q4_K_M GGUF",
3845
- "primary_artifact": "OLMoE-1B-7B-0924-Instruct-Q4_K_M.gguf",
3846
- "active_params_b": 1,
3847
- "total_params_b": 7,
3848
- "quality_rank": 2,
3849
- "cost_rank": 1,
3850
- "sovereignty_tier": 3,
3851
- "supports_tools": true,
3852
- "supports_json": true,
3853
- "tags": [
3854
- "moe",
3855
- "olmoe",
3856
- "fully-open",
3857
- "gguf",
3858
- "allenai"
3859
- ],
3860
- "metadata": {
3861
- "upstream_repo": "allenai/OLMoE-1B-7B-0924-Instruct",
3862
- "release_version": "v0.1.0",
3863
- "package_state": "weights_mirrored",
3864
- "public_status": "public and non-gated",
3865
- "validation_status": "scaffold_verified"
3866
- }
3867
  }
3868
  ]
3869
  }
 
2
  "registry_id": "lumynax-marama-route-registry-v0",
3
  "publisher": "AbteeX AI Labs",
4
  "source_report": "docs/releases/hf-model-card-v4-refresh-2026-05-11.json",
5
+ "model_count": 82,
6
  "models": [
7
  {
8
  "model_id": "lumynax-coder-qwen25-05b-instruct-gguf",
 
3246
  "public_status": "public and non-gated",
3247
  "validation_status": "scaffold_verified"
3248
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3249
  }
3250
  ]
3251
  }
marama_route/__init__.py CHANGED
@@ -1,40 +1,40 @@
1
- from __future__ import annotations
2
-
3
- from .gateway import (
4
- build_chat_route_response,
5
- build_models_response,
6
- route_chat_payload,
7
- routing_request_from_chat_payload,
8
- )
9
- from .platform import (
10
- build_opencode_provider_config,
11
- build_registry_analytics,
12
- catalog_models,
13
- compare_models,
14
- route_scenario_matrix,
15
- )
16
- from .registry import ModelEndpoint, RoutingRequest, load_model_registry
17
- from .router import RouteDecision, SovereignModelRouter
18
- from .server import handle_gateway_request, load_gateway_config, smoke_gateway
19
- from .ui import smoke_ui as smoke_ui
20
-
21
- __all__ = [
22
- "ModelEndpoint",
23
- "RouteDecision",
24
- "RoutingRequest",
25
- "SovereignModelRouter",
26
- "build_chat_route_response",
27
- "build_models_response",
28
- "build_opencode_provider_config",
29
- "build_registry_analytics",
30
- "catalog_models",
31
- "compare_models",
32
- "handle_gateway_request",
33
- "load_gateway_config",
34
- "load_model_registry",
35
- "route_chat_payload",
36
- "route_scenario_matrix",
37
- "routing_request_from_chat_payload",
38
- "smoke_gateway",
39
- "smoke_ui",
40
- ]
 
1
+ from __future__ import annotations
2
+
3
+ from .gateway import (
4
+ build_chat_route_response,
5
+ build_models_response,
6
+ route_chat_payload,
7
+ routing_request_from_chat_payload,
8
+ )
9
+ from .platform import (
10
+ build_opencode_provider_config,
11
+ build_registry_analytics,
12
+ catalog_models,
13
+ compare_models,
14
+ route_scenario_matrix,
15
+ )
16
+ from .registry import ModelEndpoint, RoutingRequest, load_model_registry
17
+ from .router import RouteDecision, SovereignModelRouter
18
+ from .server import handle_gateway_request, load_gateway_config, smoke_gateway
19
+ from .ui import smoke_ui as smoke_ui
20
+
21
+ __all__ = [
22
+ "ModelEndpoint",
23
+ "RouteDecision",
24
+ "RoutingRequest",
25
+ "SovereignModelRouter",
26
+ "build_chat_route_response",
27
+ "build_models_response",
28
+ "build_opencode_provider_config",
29
+ "build_registry_analytics",
30
+ "catalog_models",
31
+ "compare_models",
32
+ "handle_gateway_request",
33
+ "load_gateway_config",
34
+ "load_model_registry",
35
+ "route_chat_payload",
36
+ "route_scenario_matrix",
37
+ "routing_request_from_chat_payload",
38
+ "smoke_gateway",
39
+ "smoke_ui",
40
+ ]
marama_route/_ui_server.py CHANGED
@@ -1,121 +1,121 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import socket
5
- import webbrowser
6
- from collections.abc import Callable
7
- from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
8
- from typing import Any
9
- from urllib.parse import urlparse
10
-
11
- ApiHandler = Callable[[str, str, dict[str, Any] | None], tuple[int, dict[str, Any]]]
12
-
13
-
14
- def find_available_port(host: str, preferred_port: int, *, attempts: int = 50) -> int:
15
- start = preferred_port if preferred_port > 0 else 0
16
- if start == 0:
17
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as probe:
18
- probe.bind((host, 0))
19
- return int(probe.getsockname()[1])
20
-
21
- for port in range(start, start + attempts):
22
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as probe:
23
- probe.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
24
- try:
25
- probe.bind((host, port))
26
- except OSError:
27
- continue
28
- return port
29
- raise OSError(f"No available port found from {preferred_port} to {preferred_port + attempts - 1}")
30
-
31
-
32
- def serve_dashboard(
33
- *,
34
- product_name: str,
35
- html: str,
36
- api_handler: ApiHandler,
37
- host: str,
38
- port: int,
39
- open_browser: bool = False,
40
- api_path_prefixes: tuple[str, ...] = ("/api/",),
41
- api_exact_paths: tuple[str, ...] = (),
42
- ) -> int:
43
- actual_port = find_available_port(host, port)
44
- exact_paths = set(api_exact_paths)
45
-
46
- def is_api_path(path: str) -> bool:
47
- return path in exact_paths or any(path.startswith(prefix) for prefix in api_path_prefixes)
48
-
49
- class Handler(BaseHTTPRequestHandler):
50
- server_version = "AbteeXProductUI/0.1"
51
-
52
- def do_GET(self) -> None: # noqa: N802 - stdlib handler method name
53
- path = urlparse(self.path).path
54
- if path == "/":
55
- self._send_text(200, html, "text/html; charset=utf-8")
56
- return
57
- if is_api_path(path):
58
- self._send_api("GET", path, None)
59
- return
60
- self._send_json(404, {"ok": False, "error": "not_found"})
61
-
62
- def do_POST(self) -> None: # noqa: N802 - stdlib handler method name
63
- path = urlparse(self.path).path
64
- if not is_api_path(path):
65
- self._send_json(404, {"ok": False, "error": "not_found"})
66
- return
67
- try:
68
- length = int(self.headers.get("Content-Length", "0"))
69
- raw = self.rfile.read(length).decode("utf-8") if length else "{}"
70
- payload = json.loads(raw)
71
- if not isinstance(payload, dict):
72
- raise ValueError("JSON body must be an object")
73
- self._send_api("POST", path, payload)
74
- except Exception as exc: # defensive API boundary
75
- self._send_json(400, {"ok": False, "error": str(exc)})
76
-
77
- def log_message(self, format: str, *args: Any) -> None: # noqa: A002
78
- return
79
-
80
- def _send_api(
81
- self,
82
- method: str,
83
- path: str,
84
- payload: dict[str, Any] | None,
85
- ) -> None:
86
- try:
87
- status, response = api_handler(method, path, payload)
88
- except Exception as exc: # defensive API boundary
89
- status, response = 500, {"ok": False, "error": str(exc)}
90
- self._send_json(status, response)
91
-
92
- def _send_json(self, status: int, payload: dict[str, Any]) -> None:
93
- body = json.dumps(payload, indent=2, sort_keys=True).encode("utf-8")
94
- self.send_response(status)
95
- self.send_header("Content-Type", "application/json; charset=utf-8")
96
- self.send_header("Content-Length", str(len(body)))
97
- self.send_header("Cache-Control", "no-store")
98
- self.end_headers()
99
- self.wfile.write(body)
100
-
101
- def _send_text(self, status: int, body: str, content_type: str) -> None:
102
- encoded = body.encode("utf-8")
103
- self.send_response(status)
104
- self.send_header("Content-Type", content_type)
105
- self.send_header("Content-Length", str(len(encoded)))
106
- self.send_header("Cache-Control", "no-store")
107
- self.end_headers()
108
- self.wfile.write(encoded)
109
-
110
- server = ThreadingHTTPServer((host, actual_port), Handler)
111
- url = f"http://{host}:{actual_port}/"
112
- print(f"{product_name} UI listening on {url}")
113
- if open_browser:
114
- webbrowser.open(url)
115
- try:
116
- server.serve_forever()
117
- except KeyboardInterrupt:
118
- print(f"{product_name} UI stopped")
119
- finally:
120
- server.server_close()
121
- return 0
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import socket
5
+ import webbrowser
6
+ from collections.abc import Callable
7
+ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
8
+ from typing import Any
9
+ from urllib.parse import urlparse
10
+
11
+ ApiHandler = Callable[[str, str, dict[str, Any] | None], tuple[int, dict[str, Any]]]
12
+
13
+
14
+ def find_available_port(host: str, preferred_port: int, *, attempts: int = 50) -> int:
15
+ start = preferred_port if preferred_port > 0 else 0
16
+ if start == 0:
17
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as probe:
18
+ probe.bind((host, 0))
19
+ return int(probe.getsockname()[1])
20
+
21
+ for port in range(start, start + attempts):
22
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as probe:
23
+ probe.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
24
+ try:
25
+ probe.bind((host, port))
26
+ except OSError:
27
+ continue
28
+ return port
29
+ raise OSError(f"No available port found from {preferred_port} to {preferred_port + attempts - 1}")
30
+
31
+
32
+ def serve_dashboard(
33
+ *,
34
+ product_name: str,
35
+ html: str,
36
+ api_handler: ApiHandler,
37
+ host: str,
38
+ port: int,
39
+ open_browser: bool = False,
40
+ api_path_prefixes: tuple[str, ...] = ("/api/",),
41
+ api_exact_paths: tuple[str, ...] = (),
42
+ ) -> int:
43
+ actual_port = find_available_port(host, port)
44
+ exact_paths = set(api_exact_paths)
45
+
46
+ def is_api_path(path: str) -> bool:
47
+ return path in exact_paths or any(path.startswith(prefix) for prefix in api_path_prefixes)
48
+
49
+ class Handler(BaseHTTPRequestHandler):
50
+ server_version = "AbteeXProductUI/0.1"
51
+
52
+ def do_GET(self) -> None: # noqa: N802 - stdlib handler method name
53
+ path = urlparse(self.path).path
54
+ if path == "/":
55
+ self._send_text(200, html, "text/html; charset=utf-8")
56
+ return
57
+ if is_api_path(path):
58
+ self._send_api("GET", path, None)
59
+ return
60
+ self._send_json(404, {"ok": False, "error": "not_found"})
61
+
62
+ def do_POST(self) -> None: # noqa: N802 - stdlib handler method name
63
+ path = urlparse(self.path).path
64
+ if not is_api_path(path):
65
+ self._send_json(404, {"ok": False, "error": "not_found"})
66
+ return
67
+ try:
68
+ length = int(self.headers.get("Content-Length", "0"))
69
+ raw = self.rfile.read(length).decode("utf-8") if length else "{}"
70
+ payload = json.loads(raw)
71
+ if not isinstance(payload, dict):
72
+ raise ValueError("JSON body must be an object")
73
+ self._send_api("POST", path, payload)
74
+ except Exception as exc: # defensive API boundary
75
+ self._send_json(400, {"ok": False, "error": str(exc)})
76
+
77
+ def log_message(self, format: str, *args: Any) -> None: # noqa: A002
78
+ return
79
+
80
+ def _send_api(
81
+ self,
82
+ method: str,
83
+ path: str,
84
+ payload: dict[str, Any] | None,
85
+ ) -> None:
86
+ try:
87
+ status, response = api_handler(method, path, payload)
88
+ except Exception as exc: # defensive API boundary
89
+ status, response = 500, {"ok": False, "error": str(exc)}
90
+ self._send_json(status, response)
91
+
92
+ def _send_json(self, status: int, payload: dict[str, Any]) -> None:
93
+ body = json.dumps(payload, indent=2, sort_keys=True).encode("utf-8")
94
+ self.send_response(status)
95
+ self.send_header("Content-Type", "application/json; charset=utf-8")
96
+ self.send_header("Content-Length", str(len(body)))
97
+ self.send_header("Cache-Control", "no-store")
98
+ self.end_headers()
99
+ self.wfile.write(body)
100
+
101
+ def _send_text(self, status: int, body: str, content_type: str) -> None:
102
+ encoded = body.encode("utf-8")
103
+ self.send_response(status)
104
+ self.send_header("Content-Type", content_type)
105
+ self.send_header("Content-Length", str(len(encoded)))
106
+ self.send_header("Cache-Control", "no-store")
107
+ self.end_headers()
108
+ self.wfile.write(encoded)
109
+
110
+ server = ThreadingHTTPServer((host, actual_port), Handler)
111
+ url = f"http://{host}:{actual_port}/"
112
+ print(f"{product_name} UI listening on {url}")
113
+ if open_browser:
114
+ webbrowser.open(url)
115
+ try:
116
+ server.serve_forever()
117
+ except KeyboardInterrupt:
118
+ print(f"{product_name} UI stopped")
119
+ finally:
120
+ server.server_close()
121
+ return 0
marama_route/cli.py CHANGED
@@ -1,233 +1,233 @@
1
- from __future__ import annotations
2
-
3
- import argparse
4
- import json
5
- from collections.abc import Sequence
6
- from pathlib import Path
7
- from typing import Any
8
-
9
- from .gateway import build_models_response, route_chat_payload
10
- from .platform import (
11
- build_opencode_provider_config,
12
- build_registry_analytics,
13
- catalog_models,
14
- compare_models,
15
- route_scenario_matrix,
16
- )
17
- from .registry import RoutingRequest, load_model_registry
18
- from .router import SovereignModelRouter
19
-
20
-
21
- def _load_json_mapping(path: Path) -> dict[str, Any]:
22
- payload = json.loads(path.read_text(encoding="utf-8-sig"))
23
- if not isinstance(payload, dict):
24
- raise ValueError(f"Expected mapping in {path}")
25
- return payload
26
-
27
-
28
- def _route(args: argparse.Namespace) -> int:
29
- models = load_model_registry(args.registry)
30
- payload = _load_json_mapping(args.request)
31
- decision = SovereignModelRouter(models).route(RoutingRequest.from_payload(payload))
32
- print(json.dumps(decision.to_dict(), indent=2, sort_keys=True))
33
- return 0 if decision.selected_model is not None else 2
34
-
35
-
36
- def _models(args: argparse.Namespace) -> int:
37
- models = load_model_registry(args.registry)
38
- print(json.dumps(build_models_response(models), indent=2, sort_keys=True))
39
- return 0
40
-
41
-
42
- def _chat_dry_run(args: argparse.Namespace) -> int:
43
- models = load_model_registry(args.registry)
44
- payload = _load_json_mapping(args.request)
45
- result = route_chat_payload(payload, models)
46
- print(json.dumps(result, indent=2, sort_keys=True))
47
- selected = result["route_decision"]["selected_model"]
48
- return 0 if selected is not None else 2
49
-
50
-
51
- def _catalog(args: argparse.Namespace) -> int:
52
- models = load_model_registry(args.registry)
53
- result = catalog_models(
54
- models,
55
- {
56
- "search": args.search,
57
- "task_type": args.task,
58
- "runtime": args.runtime,
59
- "modality": args.modality,
60
- "jurisdiction": args.jurisdiction,
61
- "min_context_tokens": args.min_context_tokens,
62
- "requires_json": args.requires_json,
63
- "requires_tools": args.requires_tools,
64
- "requires_local": args.requires_local,
65
- "limit": args.limit,
66
- },
67
- )
68
- print(json.dumps(result, indent=2, sort_keys=True))
69
- return 0
70
-
71
-
72
- def _compare(args: argparse.Namespace) -> int:
73
- models = load_model_registry(args.registry)
74
- model_ids = [item.strip() for value in args.model for item in value.split(",") if item.strip()]
75
- request = _load_json_mapping(args.request) if args.request else None
76
- result = compare_models(models, model_ids, request)
77
- print(json.dumps(result, indent=2, sort_keys=True))
78
- return 0 if result["ok"] else 2
79
-
80
-
81
- def _matrix(args: argparse.Namespace) -> int:
82
- models = load_model_registry(args.registry)
83
- result = route_scenario_matrix(models)
84
- print(json.dumps(result, indent=2, sort_keys=True))
85
- return 0 if result["ok"] or args.allow_blocked_exit_zero else 2
86
-
87
-
88
- def _analytics(args: argparse.Namespace) -> int:
89
- print(json.dumps(build_registry_analytics(load_model_registry(args.registry)), indent=2, sort_keys=True))
90
- return 0
91
-
92
-
93
- def _opencode_config(args: argparse.Namespace) -> int:
94
- models = load_model_registry(args.registry)
95
- result = build_opencode_provider_config(models, base_url=args.base_url)
96
- print(json.dumps(result, indent=2, sort_keys=True))
97
- return 0
98
-
99
-
100
- def _ui(args: argparse.Namespace) -> int:
101
- from .ui import run_ui
102
-
103
- return run_ui(
104
- registry_path=args.registry,
105
- host=args.host,
106
- port=args.port,
107
- open_browser=args.open,
108
- smoke=args.smoke,
109
- )
110
-
111
-
112
- def _serve(args: argparse.Namespace) -> int:
113
- from .server import serve_gateway
114
-
115
- return serve_gateway(
116
- registry_path=args.registry,
117
- config_path=args.config,
118
- host=args.host,
119
- port=args.port,
120
- open_browser=args.open,
121
- smoke=args.smoke,
122
- )
123
-
124
-
125
- def build_parser() -> argparse.ArgumentParser:
126
- parser = argparse.ArgumentParser(
127
- prog="lumynax-marama-route",
128
- description="Route requests across LumynaX sovereign model releases.",
129
- )
130
- subparsers = parser.add_subparsers(dest="command")
131
- route = subparsers.add_parser("route", help="Select a LumynaX model for a request.")
132
- route.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.")
133
- route.add_argument("--request", type=Path, required=True, help="Routing request JSON.")
134
- route.set_defaults(handler=_route)
135
-
136
- models = subparsers.add_parser(
137
- "models",
138
- help="Emit an OpenAI-compatible /v1/models response.",
139
- )
140
- models.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.")
141
- models.set_defaults(handler=_models)
142
-
143
- chat = subparsers.add_parser(
144
- "chat-dry-run",
145
- help="Route an OpenAI-compatible chat request without invoking a backend.",
146
- )
147
- chat.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.")
148
- chat.add_argument("--request", type=Path, required=True, help="OpenAI chat request JSON.")
149
- chat.set_defaults(handler=_chat_dry_run)
150
-
151
- catalog = subparsers.add_parser(
152
- "catalog",
153
- help="Search and filter the MaramaRoute model catalog.",
154
- )
155
- catalog.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.")
156
- catalog.add_argument("--search", default="")
157
- catalog.add_argument("--task", default="")
158
- catalog.add_argument("--runtime", default="")
159
- catalog.add_argument("--modality", default="")
160
- catalog.add_argument("--jurisdiction", default="NZ")
161
- catalog.add_argument("--min-context-tokens", type=int, default=0)
162
- catalog.add_argument("--requires-json", action=argparse.BooleanOptionalAction, default=False)
163
- catalog.add_argument("--requires-tools", action=argparse.BooleanOptionalAction, default=False)
164
- catalog.add_argument("--requires-local", action=argparse.BooleanOptionalAction, default=False)
165
- catalog.add_argument("--limit", type=int, default=25)
166
- catalog.set_defaults(handler=_catalog)
167
-
168
- compare = subparsers.add_parser(
169
- "compare",
170
- help="Compare routed fit for selected model ids.",
171
- )
172
- compare.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.")
173
- compare.add_argument("--model", action="append", required=True, help="Model id, repeatable or comma-separated.")
174
- compare.add_argument("--request", type=Path, default=None, help="Optional routing request JSON.")
175
- compare.set_defaults(handler=_compare)
176
-
177
- matrix = subparsers.add_parser(
178
- "matrix",
179
- help="Run the built-in sovereign routing scenario matrix.",
180
- )
181
- matrix.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.")
182
- matrix.add_argument("--allow-blocked-exit-zero", action=argparse.BooleanOptionalAction, default=False)
183
- matrix.set_defaults(handler=_matrix)
184
-
185
- analytics = subparsers.add_parser("analytics", help="Summarise registry coverage.")
186
- analytics.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.")
187
- analytics.set_defaults(handler=_analytics)
188
-
189
- opencode = subparsers.add_parser(
190
- "opencode-config",
191
- help="Emit an OpenCode-compatible MaramaRoute provider config.",
192
- )
193
- opencode.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.")
194
- opencode.add_argument("--base-url", default="http://127.0.0.1:8787/v1")
195
- opencode.set_defaults(handler=_opencode_config)
196
-
197
- ui = subparsers.add_parser(
198
- "ui",
199
- help="Launch the local MaramaRoute browser platform.",
200
- )
201
- ui.add_argument("--registry", type=Path, default=None, help="MaramaRoute model registry JSON.")
202
- ui.add_argument("--host", type=str, default="127.0.0.1")
203
- ui.add_argument("--port", type=int, default=8787)
204
- ui.add_argument("--open", action=argparse.BooleanOptionalAction, default=False)
205
- ui.add_argument("--smoke", action=argparse.BooleanOptionalAction, default=False)
206
- ui.set_defaults(handler=_ui)
207
-
208
- serve = subparsers.add_parser(
209
- "serve",
210
- help="Run the local OpenAI-compatible MaramaRoute gateway and browser console.",
211
- )
212
- serve.add_argument("--registry", type=Path, default=None, help="MaramaRoute model registry JSON.")
213
- serve.add_argument("--config", type=Path, default=None, help="Gateway backend config JSON.")
214
- serve.add_argument("--host", type=str, default="127.0.0.1")
215
- serve.add_argument("--port", type=int, default=8787)
216
- serve.add_argument("--open", action=argparse.BooleanOptionalAction, default=False)
217
- serve.add_argument("--smoke", action=argparse.BooleanOptionalAction, default=False)
218
- serve.set_defaults(handler=_serve)
219
- return parser
220
-
221
-
222
- def main(argv: Sequence[str] | None = None) -> int:
223
- parser = build_parser()
224
- args = parser.parse_args(argv)
225
- handler = getattr(args, "handler", None)
226
- if handler is None:
227
- parser.print_help()
228
- return 0
229
- return int(handler(args))
230
-
231
-
232
- if __name__ == "__main__":
233
- raise SystemExit(main())
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ from collections.abc import Sequence
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from .gateway import build_models_response, route_chat_payload
10
+ from .platform import (
11
+ build_opencode_provider_config,
12
+ build_registry_analytics,
13
+ catalog_models,
14
+ compare_models,
15
+ route_scenario_matrix,
16
+ )
17
+ from .registry import RoutingRequest, load_model_registry
18
+ from .router import SovereignModelRouter
19
+
20
+
21
+ def _load_json_mapping(path: Path) -> dict[str, Any]:
22
+ payload = json.loads(path.read_text(encoding="utf-8-sig"))
23
+ if not isinstance(payload, dict):
24
+ raise ValueError(f"Expected mapping in {path}")
25
+ return payload
26
+
27
+
28
+ def _route(args: argparse.Namespace) -> int:
29
+ models = load_model_registry(args.registry)
30
+ payload = _load_json_mapping(args.request)
31
+ decision = SovereignModelRouter(models).route(RoutingRequest.from_payload(payload))
32
+ print(json.dumps(decision.to_dict(), indent=2, sort_keys=True))
33
+ return 0 if decision.selected_model is not None else 2
34
+
35
+
36
+ def _models(args: argparse.Namespace) -> int:
37
+ models = load_model_registry(args.registry)
38
+ print(json.dumps(build_models_response(models), indent=2, sort_keys=True))
39
+ return 0
40
+
41
+
42
+ def _chat_dry_run(args: argparse.Namespace) -> int:
43
+ models = load_model_registry(args.registry)
44
+ payload = _load_json_mapping(args.request)
45
+ result = route_chat_payload(payload, models)
46
+ print(json.dumps(result, indent=2, sort_keys=True))
47
+ selected = result["route_decision"]["selected_model"]
48
+ return 0 if selected is not None else 2
49
+
50
+
51
+ def _catalog(args: argparse.Namespace) -> int:
52
+ models = load_model_registry(args.registry)
53
+ result = catalog_models(
54
+ models,
55
+ {
56
+ "search": args.search,
57
+ "task_type": args.task,
58
+ "runtime": args.runtime,
59
+ "modality": args.modality,
60
+ "jurisdiction": args.jurisdiction,
61
+ "min_context_tokens": args.min_context_tokens,
62
+ "requires_json": args.requires_json,
63
+ "requires_tools": args.requires_tools,
64
+ "requires_local": args.requires_local,
65
+ "limit": args.limit,
66
+ },
67
+ )
68
+ print(json.dumps(result, indent=2, sort_keys=True))
69
+ return 0
70
+
71
+
72
+ def _compare(args: argparse.Namespace) -> int:
73
+ models = load_model_registry(args.registry)
74
+ model_ids = [item.strip() for value in args.model for item in value.split(",") if item.strip()]
75
+ request = _load_json_mapping(args.request) if args.request else None
76
+ result = compare_models(models, model_ids, request)
77
+ print(json.dumps(result, indent=2, sort_keys=True))
78
+ return 0 if result["ok"] else 2
79
+
80
+
81
+ def _matrix(args: argparse.Namespace) -> int:
82
+ models = load_model_registry(args.registry)
83
+ result = route_scenario_matrix(models)
84
+ print(json.dumps(result, indent=2, sort_keys=True))
85
+ return 0 if result["ok"] or args.allow_blocked_exit_zero else 2
86
+
87
+
88
+ def _analytics(args: argparse.Namespace) -> int:
89
+ print(json.dumps(build_registry_analytics(load_model_registry(args.registry)), indent=2, sort_keys=True))
90
+ return 0
91
+
92
+
93
+ def _opencode_config(args: argparse.Namespace) -> int:
94
+ models = load_model_registry(args.registry)
95
+ result = build_opencode_provider_config(models, base_url=args.base_url)
96
+ print(json.dumps(result, indent=2, sort_keys=True))
97
+ return 0
98
+
99
+
100
+ def _ui(args: argparse.Namespace) -> int:
101
+ from .ui import run_ui
102
+
103
+ return run_ui(
104
+ registry_path=args.registry,
105
+ host=args.host,
106
+ port=args.port,
107
+ open_browser=args.open,
108
+ smoke=args.smoke,
109
+ )
110
+
111
+
112
+ def _serve(args: argparse.Namespace) -> int:
113
+ from .server import serve_gateway
114
+
115
+ return serve_gateway(
116
+ registry_path=args.registry,
117
+ config_path=args.config,
118
+ host=args.host,
119
+ port=args.port,
120
+ open_browser=args.open,
121
+ smoke=args.smoke,
122
+ )
123
+
124
+
125
+ def build_parser() -> argparse.ArgumentParser:
126
+ parser = argparse.ArgumentParser(
127
+ prog="lumynax-marama-route",
128
+ description="Route requests across LumynaX sovereign model releases.",
129
+ )
130
+ subparsers = parser.add_subparsers(dest="command")
131
+ route = subparsers.add_parser("route", help="Select a LumynaX model for a request.")
132
+ route.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.")
133
+ route.add_argument("--request", type=Path, required=True, help="Routing request JSON.")
134
+ route.set_defaults(handler=_route)
135
+
136
+ models = subparsers.add_parser(
137
+ "models",
138
+ help="Emit an OpenAI-compatible /v1/models response.",
139
+ )
140
+ models.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.")
141
+ models.set_defaults(handler=_models)
142
+
143
+ chat = subparsers.add_parser(
144
+ "chat-dry-run",
145
+ help="Route an OpenAI-compatible chat request without invoking a backend.",
146
+ )
147
+ chat.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.")
148
+ chat.add_argument("--request", type=Path, required=True, help="OpenAI chat request JSON.")
149
+ chat.set_defaults(handler=_chat_dry_run)
150
+
151
+ catalog = subparsers.add_parser(
152
+ "catalog",
153
+ help="Search and filter the MaramaRoute model catalog.",
154
+ )
155
+ catalog.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.")
156
+ catalog.add_argument("--search", default="")
157
+ catalog.add_argument("--task", default="")
158
+ catalog.add_argument("--runtime", default="")
159
+ catalog.add_argument("--modality", default="")
160
+ catalog.add_argument("--jurisdiction", default="NZ")
161
+ catalog.add_argument("--min-context-tokens", type=int, default=0)
162
+ catalog.add_argument("--requires-json", action=argparse.BooleanOptionalAction, default=False)
163
+ catalog.add_argument("--requires-tools", action=argparse.BooleanOptionalAction, default=False)
164
+ catalog.add_argument("--requires-local", action=argparse.BooleanOptionalAction, default=False)
165
+ catalog.add_argument("--limit", type=int, default=25)
166
+ catalog.set_defaults(handler=_catalog)
167
+
168
+ compare = subparsers.add_parser(
169
+ "compare",
170
+ help="Compare routed fit for selected model ids.",
171
+ )
172
+ compare.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.")
173
+ compare.add_argument("--model", action="append", required=True, help="Model id, repeatable or comma-separated.")
174
+ compare.add_argument("--request", type=Path, default=None, help="Optional routing request JSON.")
175
+ compare.set_defaults(handler=_compare)
176
+
177
+ matrix = subparsers.add_parser(
178
+ "matrix",
179
+ help="Run the built-in sovereign routing scenario matrix.",
180
+ )
181
+ matrix.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.")
182
+ matrix.add_argument("--allow-blocked-exit-zero", action=argparse.BooleanOptionalAction, default=False)
183
+ matrix.set_defaults(handler=_matrix)
184
+
185
+ analytics = subparsers.add_parser("analytics", help="Summarise registry coverage.")
186
+ analytics.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.")
187
+ analytics.set_defaults(handler=_analytics)
188
+
189
+ opencode = subparsers.add_parser(
190
+ "opencode-config",
191
+ help="Emit an OpenCode-compatible MaramaRoute provider config.",
192
+ )
193
+ opencode.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.")
194
+ opencode.add_argument("--base-url", default="http://127.0.0.1:8787/v1")
195
+ opencode.set_defaults(handler=_opencode_config)
196
+
197
+ ui = subparsers.add_parser(
198
+ "ui",
199
+ help="Launch the local MaramaRoute browser platform.",
200
+ )
201
+ ui.add_argument("--registry", type=Path, default=None, help="MaramaRoute model registry JSON.")
202
+ ui.add_argument("--host", type=str, default="127.0.0.1")
203
+ ui.add_argument("--port", type=int, default=8787)
204
+ ui.add_argument("--open", action=argparse.BooleanOptionalAction, default=False)
205
+ ui.add_argument("--smoke", action=argparse.BooleanOptionalAction, default=False)
206
+ ui.set_defaults(handler=_ui)
207
+
208
+ serve = subparsers.add_parser(
209
+ "serve",
210
+ help="Run the local OpenAI-compatible MaramaRoute gateway and browser console.",
211
+ )
212
+ serve.add_argument("--registry", type=Path, default=None, help="MaramaRoute model registry JSON.")
213
+ serve.add_argument("--config", type=Path, default=None, help="Gateway backend config JSON.")
214
+ serve.add_argument("--host", type=str, default="127.0.0.1")
215
+ serve.add_argument("--port", type=int, default=8787)
216
+ serve.add_argument("--open", action=argparse.BooleanOptionalAction, default=False)
217
+ serve.add_argument("--smoke", action=argparse.BooleanOptionalAction, default=False)
218
+ serve.set_defaults(handler=_serve)
219
+ return parser
220
+
221
+
222
+ def main(argv: Sequence[str] | None = None) -> int:
223
+ parser = build_parser()
224
+ args = parser.parse_args(argv)
225
+ handler = getattr(args, "handler", None)
226
+ if handler is None:
227
+ parser.print_help()
228
+ return 0
229
+ return int(handler(args))
230
+
231
+
232
+ if __name__ == "__main__":
233
+ raise SystemExit(main())
marama_route/configs/gateway.local.json CHANGED
@@ -1,13 +1,13 @@
1
- {
2
- "mode": "route_only",
3
- "prompt_retention": "not_stored_by_default",
4
- "default_timeout_seconds": 120,
5
- "backends": {
6
- "example-local-openai-compatible": {
7
- "type": "openai_compatible",
8
- "base_url": "http://127.0.0.1:8000/v1",
9
- "api_key_env": "",
10
- "model": "local-model-id"
11
- }
12
- }
13
- }
 
1
+ {
2
+ "mode": "route_only",
3
+ "prompt_retention": "not_stored_by_default",
4
+ "default_timeout_seconds": 120,
5
+ "backends": {
6
+ "example-local-openai-compatible": {
7
+ "type": "openai_compatible",
8
+ "base_url": "http://127.0.0.1:8000/v1",
9
+ "api_key_env": "",
10
+ "model": "local-model-id"
11
+ }
12
+ }
13
+ }
marama_route/py.typed ADDED
File without changes
marama_route/server.py CHANGED
@@ -1,312 +1,312 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import os
5
- import tempfile
6
- import urllib.error
7
- import urllib.request
8
- from pathlib import Path
9
- from typing import Any
10
-
11
- try: # repo package
12
- from tinyluminax.products._ui_server import serve_dashboard
13
- except ModuleNotFoundError: # standalone HF package
14
- from ._ui_server import serve_dashboard
15
-
16
- from .gateway import route_chat_payload
17
- from .platform import build_models_api, route_or_chat_payload, route_receipt
18
- from .registry import load_model_registry
19
- from .ui import (
20
- PRODUCT_NAME,
21
- build_dashboard_state,
22
- build_expanded_dashboard_html,
23
- default_openai_chat_request_path,
24
- default_registry_path,
25
- handle_api_request,
26
- load_json_mapping,
27
- )
28
-
29
- PACKAGE_ROOT = Path(__file__).resolve().parent
30
- PACKAGE_PARENT = PACKAGE_ROOT.parent
31
-
32
- DEFAULT_GATEWAY_CONFIG: dict[str, Any] = {
33
- "mode": "route_only",
34
- "prompt_retention": "not_stored_by_default",
35
- "default_timeout_seconds": 120,
36
- "backends": {},
37
- }
38
-
39
-
40
- def default_gateway_config_path() -> Path:
41
- candidates = [
42
- Path.cwd() / "products" / "lumynax-marama-route" / "configs" / "gateway.local.json",
43
- Path.cwd() / "configs" / "gateway.local.json",
44
- PACKAGE_ROOT / "configs" / "gateway.local.json",
45
- PACKAGE_PARENT / "configs" / "gateway.local.json",
46
- ]
47
- for path in candidates:
48
- if path.exists():
49
- return path
50
- return candidates[0]
51
-
52
-
53
- def default_route_request_path() -> Path:
54
- candidates = [
55
- Path.cwd() / "products" / "lumynax-marama-route" / "examples" / "request.code-restricted.json",
56
- Path.cwd() / "examples" / "request.code-restricted.json",
57
- PACKAGE_ROOT / "examples" / "request.code-restricted.json",
58
- PACKAGE_PARENT / "examples" / "request.code-restricted.json",
59
- ]
60
- for path in candidates:
61
- if path.exists():
62
- return path
63
- return candidates[0]
64
-
65
-
66
- def load_gateway_config(path: Path | None = None) -> dict[str, Any]:
67
- config = dict(DEFAULT_GATEWAY_CONFIG)
68
- config["backends"] = dict(DEFAULT_GATEWAY_CONFIG["backends"])
69
- resolved = path or default_gateway_config_path()
70
- if resolved.exists():
71
- payload = json.loads(resolved.read_text(encoding="utf-8-sig"))
72
- if not isinstance(payload, dict):
73
- raise ValueError(f"Expected gateway config object in {resolved}")
74
- config.update(payload)
75
- config["backends"] = dict(payload.get("backends") or {})
76
- config["config_path"] = str(resolved)
77
- return config
78
-
79
-
80
- def handle_gateway_request(
81
- method: str,
82
- path: str,
83
- payload: dict[str, Any] | None,
84
- registry_path: Path,
85
- config_path: Path | None = None,
86
- ) -> tuple[int, dict[str, Any]]:
87
- models = load_model_registry(registry_path)
88
- config = load_gateway_config(config_path)
89
-
90
- if path.startswith("/api/"):
91
- return handle_api_request(method, path, payload, registry_path)
92
- if method == "GET" and path in {"/health", "/v1/health"}:
93
- return 200, {
94
- "ok": True,
95
- "product": PRODUCT_NAME,
96
- "mode": config["mode"],
97
- "model_count": len(models),
98
- "configured_backends": len(config.get("backends") or {}),
99
- "prompt_retention": config.get("prompt_retention", "not_stored_by_default"),
100
- }
101
- if method == "GET" and path == "/v1/models":
102
- return 200, build_models_api(models)
103
- if method == "POST" and path == "/v1/route" and payload is not None:
104
- result = route_or_chat_payload(payload, models)
105
- return (200 if result["ok"] else 422), result
106
- if method == "POST" and path == "/v1/chat/completions" and payload is not None:
107
- return chat_completion_gateway(payload, models, config)
108
- return 404, {"ok": False, "error": "not_found"}
109
-
110
-
111
- def chat_completion_gateway(
112
- payload: dict[str, Any],
113
- models: tuple[Any, ...],
114
- config: dict[str, Any],
115
- ) -> tuple[int, dict[str, Any]]:
116
- route_result = route_chat_payload(payload, models)
117
- decision = route_result["route_decision"]
118
- selected = decision.get("selected_model")
119
- if not isinstance(selected, dict):
120
- return 422, {"ok": False, "error": "no_eligible_model", **route_result}
121
-
122
- receipt = route_receipt(payload, route_result)
123
- dry_run = bool(
124
- payload.get("dry_run")
125
- or payload.get("marama_route_dry_run")
126
- or config.get("mode", "route_only") == "route_only"
127
- )
128
- if dry_run:
129
- response = dict(route_result["chat_completion_response"])
130
- response["marama_route"] = dict(response["marama_route"])
131
- response["marama_route"].update(
132
- {
133
- "backend_mode": "route_only",
134
- "receipt": receipt,
135
- "prompt_retention": config.get("prompt_retention", "not_stored_by_default"),
136
- },
137
- )
138
- return 200, response
139
-
140
- backend = _backend_for_model(selected["model_id"], config)
141
- if backend is None:
142
- return 424, {
143
- "ok": False,
144
- "error": "backend_not_configured",
145
- "message": "Routing succeeded, but no live backend is configured for the selected model.",
146
- "selected_model": selected["model_id"],
147
- "required_config": {
148
- "mode": "live",
149
- "backends": {
150
- selected["model_id"]: {
151
- "type": "openai_compatible",
152
- "base_url": "http://127.0.0.1:8000/v1",
153
- "api_key_env": "OPTIONAL_ENV_NAME",
154
- },
155
- },
156
- },
157
- "receipt": receipt,
158
- **route_result,
159
- }
160
- return _proxy_openai_chat_completion(payload, selected, backend, config, route_result, receipt)
161
-
162
-
163
- def smoke_gateway(
164
- *,
165
- registry_path: Path | None = None,
166
- config_path: Path | None = None,
167
- ) -> dict[str, Any]:
168
- resolved_registry = registry_path or default_registry_path()
169
- resolved_config = config_path or _temporary_route_only_config()
170
- route_payload = load_json_mapping(default_route_request_path())
171
- chat_payload = load_json_mapping(default_openai_chat_request_path())
172
- chat_payload["dry_run"] = True
173
-
174
- health_status, health = handle_gateway_request("GET", "/health", None, resolved_registry, resolved_config)
175
- models_status, models = handle_gateway_request("GET", "/v1/models", None, resolved_registry, resolved_config)
176
- route_status, route = handle_gateway_request("POST", "/v1/route", route_payload, resolved_registry, resolved_config)
177
- chat_status, chat = handle_gateway_request(
178
- "POST",
179
- "/v1/chat/completions",
180
- chat_payload,
181
- resolved_registry,
182
- resolved_config,
183
- )
184
-
185
- if health_status != 200 or models_status != 200 or route_status != 200 or chat_status != 200:
186
- raise RuntimeError("MaramaRoute gateway smoke failed")
187
- if chat.get("object") != "chat.completion" or chat["marama_route"]["selected_model"] is None:
188
- raise RuntimeError("MaramaRoute gateway did not return a routed chat response")
189
- return {
190
- "ok": True,
191
- "product": PRODUCT_NAME,
192
- "mode": health["mode"],
193
- "model_count": health["model_count"],
194
- "route_selected_model": route["route_decision"]["selected_model"]["model_id"],
195
- "chat_selected_model": chat["marama_route"]["selected_model"]["model_id"],
196
- "configured_backends": health["configured_backends"],
197
- }
198
-
199
-
200
- def serve_gateway(
201
- *,
202
- registry_path: Path | None = None,
203
- config_path: Path | None = None,
204
- host: str = "127.0.0.1",
205
- port: int = 8787,
206
- open_browser: bool = False,
207
- smoke: bool = False,
208
- ) -> int:
209
- resolved_registry = registry_path or default_registry_path()
210
- if smoke:
211
- print(json.dumps(smoke_gateway(registry_path=resolved_registry, config_path=config_path), indent=2, sort_keys=True))
212
- return 0
213
-
214
- html = build_expanded_dashboard_html(build_dashboard_state(resolved_registry))
215
- return serve_dashboard(
216
- product_name=f"{PRODUCT_NAME} Gateway",
217
- html=html,
218
- api_handler=lambda method, path, request_payload: handle_gateway_request(
219
- method,
220
- path,
221
- request_payload,
222
- resolved_registry,
223
- config_path,
224
- ),
225
- host=host,
226
- port=port,
227
- open_browser=open_browser,
228
- api_path_prefixes=("/api/", "/v1/"),
229
- api_exact_paths=("/health",),
230
- )
231
-
232
-
233
- def _backend_for_model(model_id: str, config: dict[str, Any]) -> dict[str, Any] | None:
234
- backends = config.get("backends")
235
- if not isinstance(backends, dict):
236
- return None
237
- backend = backends.get(model_id) or backends.get("*")
238
- return dict(backend) if isinstance(backend, dict) else None
239
-
240
-
241
- def _proxy_openai_chat_completion(
242
- payload: dict[str, Any],
243
- selected: dict[str, Any],
244
- backend: dict[str, Any],
245
- config: dict[str, Any],
246
- route_result: dict[str, Any],
247
- receipt: dict[str, Any],
248
- ) -> tuple[int, dict[str, Any]]:
249
- if str(backend.get("type") or "openai_compatible") != "openai_compatible":
250
- return 424, {"ok": False, "error": "unsupported_backend_type", "backend": backend}
251
-
252
- base_url = str(backend.get("base_url") or "").rstrip("/")
253
- if not base_url:
254
- return 424, {"ok": False, "error": "backend_base_url_missing", "selected_model": selected["model_id"]}
255
- endpoint = f"{base_url}/chat/completions"
256
- upstream_payload = dict(payload)
257
- upstream_payload["model"] = str(backend.get("model") or selected["model_id"])
258
- for key in ("route", "routing", "dry_run", "marama_route_dry_run"):
259
- upstream_payload.pop(key, None)
260
-
261
- headers = {"Content-Type": "application/json"}
262
- api_key_env = str(backend.get("api_key_env") or "")
263
- if api_key_env and os.getenv(api_key_env):
264
- headers["Authorization"] = f"Bearer {os.environ[api_key_env]}"
265
- headers.update({str(key): str(value) for key, value in dict(backend.get("headers") or {}).items()})
266
-
267
- timeout = float(backend.get("timeout_seconds") or config.get("default_timeout_seconds") or 120)
268
- request = urllib.request.Request(
269
- endpoint,
270
- data=json.dumps(upstream_payload).encode("utf-8"),
271
- headers=headers,
272
- method="POST",
273
- )
274
- try:
275
- with urllib.request.urlopen(request, timeout=timeout) as response: # noqa: S310 - operator-configured local/remote backend
276
- body = response.read().decode("utf-8")
277
- payload_out = json.loads(body)
278
- if not isinstance(payload_out, dict):
279
- raise ValueError("upstream response was not a JSON object")
280
- payload_out["marama_route"] = {
281
- "dry_run": False,
282
- "selected_model": selected,
283
- "fallback_models": route_result["route_decision"]["fallback_models"],
284
- "rejected_count": len(route_result["route_decision"]["rejected"]),
285
- "receipt": receipt,
286
- "backend_base_url": base_url,
287
- "prompt_retention": config.get("prompt_retention", "not_stored_by_default"),
288
- }
289
- return int(response.status), payload_out
290
- except urllib.error.HTTPError as exc:
291
- return exc.code, {
292
- "ok": False,
293
- "error": "backend_http_error",
294
- "status": exc.code,
295
- "body": exc.read().decode("utf-8", errors="replace"),
296
- "receipt": receipt,
297
- **route_result,
298
- }
299
- except Exception as exc:
300
- return 502, {
301
- "ok": False,
302
- "error": "backend_unavailable",
303
- "message": str(exc),
304
- "receipt": receipt,
305
- **route_result,
306
- }
307
-
308
-
309
- def _temporary_route_only_config() -> Path:
310
- path = Path(tempfile.gettempdir()) / "marama-route-smoke.gateway.json"
311
- path.write_text(json.dumps(DEFAULT_GATEWAY_CONFIG, indent=2, sort_keys=True), encoding="utf-8")
312
- return path
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ import tempfile
6
+ import urllib.error
7
+ import urllib.request
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ try: # repo package
12
+ from tinyluminax.products._ui_server import serve_dashboard
13
+ except ModuleNotFoundError: # standalone HF package
14
+ from ._ui_server import serve_dashboard
15
+
16
+ from .gateway import route_chat_payload
17
+ from .platform import build_models_api, route_or_chat_payload, route_receipt
18
+ from .registry import load_model_registry
19
+ from .ui import (
20
+ PRODUCT_NAME,
21
+ build_dashboard_state,
22
+ build_expanded_dashboard_html,
23
+ default_openai_chat_request_path,
24
+ default_registry_path,
25
+ handle_api_request,
26
+ load_json_mapping,
27
+ )
28
+
29
+ PACKAGE_ROOT = Path(__file__).resolve().parent
30
+ PACKAGE_PARENT = PACKAGE_ROOT.parent
31
+
32
+ DEFAULT_GATEWAY_CONFIG: dict[str, Any] = {
33
+ "mode": "route_only",
34
+ "prompt_retention": "not_stored_by_default",
35
+ "default_timeout_seconds": 120,
36
+ "backends": {},
37
+ }
38
+
39
+
40
+ def default_gateway_config_path() -> Path:
41
+ candidates = [
42
+ Path.cwd() / "products" / "lumynax-marama-route" / "configs" / "gateway.local.json",
43
+ Path.cwd() / "configs" / "gateway.local.json",
44
+ PACKAGE_ROOT / "configs" / "gateway.local.json",
45
+ PACKAGE_PARENT / "configs" / "gateway.local.json",
46
+ ]
47
+ for path in candidates:
48
+ if path.exists():
49
+ return path
50
+ return candidates[0]
51
+
52
+
53
+ def default_route_request_path() -> Path:
54
+ candidates = [
55
+ Path.cwd() / "products" / "lumynax-marama-route" / "examples" / "request.code-restricted.json",
56
+ Path.cwd() / "examples" / "request.code-restricted.json",
57
+ PACKAGE_ROOT / "examples" / "request.code-restricted.json",
58
+ PACKAGE_PARENT / "examples" / "request.code-restricted.json",
59
+ ]
60
+ for path in candidates:
61
+ if path.exists():
62
+ return path
63
+ return candidates[0]
64
+
65
+
66
+ def load_gateway_config(path: Path | None = None) -> dict[str, Any]:
67
+ config = dict(DEFAULT_GATEWAY_CONFIG)
68
+ config["backends"] = dict(DEFAULT_GATEWAY_CONFIG["backends"])
69
+ resolved = path or default_gateway_config_path()
70
+ if resolved.exists():
71
+ payload = json.loads(resolved.read_text(encoding="utf-8-sig"))
72
+ if not isinstance(payload, dict):
73
+ raise ValueError(f"Expected gateway config object in {resolved}")
74
+ config.update(payload)
75
+ config["backends"] = dict(payload.get("backends") or {})
76
+ config["config_path"] = str(resolved)
77
+ return config
78
+
79
+
80
+ def handle_gateway_request(
81
+ method: str,
82
+ path: str,
83
+ payload: dict[str, Any] | None,
84
+ registry_path: Path,
85
+ config_path: Path | None = None,
86
+ ) -> tuple[int, dict[str, Any]]:
87
+ models = load_model_registry(registry_path)
88
+ config = load_gateway_config(config_path)
89
+
90
+ if path.startswith("/api/"):
91
+ return handle_api_request(method, path, payload, registry_path)
92
+ if method == "GET" and path in {"/health", "/v1/health"}:
93
+ return 200, {
94
+ "ok": True,
95
+ "product": PRODUCT_NAME,
96
+ "mode": config["mode"],
97
+ "model_count": len(models),
98
+ "configured_backends": len(config.get("backends") or {}),
99
+ "prompt_retention": config.get("prompt_retention", "not_stored_by_default"),
100
+ }
101
+ if method == "GET" and path == "/v1/models":
102
+ return 200, build_models_api(models)
103
+ if method == "POST" and path == "/v1/route" and payload is not None:
104
+ result = route_or_chat_payload(payload, models)
105
+ return (200 if result["ok"] else 422), result
106
+ if method == "POST" and path == "/v1/chat/completions" and payload is not None:
107
+ return chat_completion_gateway(payload, models, config)
108
+ return 404, {"ok": False, "error": "not_found"}
109
+
110
+
111
+ def chat_completion_gateway(
112
+ payload: dict[str, Any],
113
+ models: tuple[Any, ...],
114
+ config: dict[str, Any],
115
+ ) -> tuple[int, dict[str, Any]]:
116
+ route_result = route_chat_payload(payload, models)
117
+ decision = route_result["route_decision"]
118
+ selected = decision.get("selected_model")
119
+ if not isinstance(selected, dict):
120
+ return 422, {"ok": False, "error": "no_eligible_model", **route_result}
121
+
122
+ receipt = route_receipt(payload, route_result)
123
+ dry_run = bool(
124
+ payload.get("dry_run")
125
+ or payload.get("marama_route_dry_run")
126
+ or config.get("mode", "route_only") == "route_only"
127
+ )
128
+ if dry_run:
129
+ response = dict(route_result["chat_completion_response"])
130
+ response["marama_route"] = dict(response["marama_route"])
131
+ response["marama_route"].update(
132
+ {
133
+ "backend_mode": "route_only",
134
+ "receipt": receipt,
135
+ "prompt_retention": config.get("prompt_retention", "not_stored_by_default"),
136
+ },
137
+ )
138
+ return 200, response
139
+
140
+ backend = _backend_for_model(selected["model_id"], config)
141
+ if backend is None:
142
+ return 424, {
143
+ "ok": False,
144
+ "error": "backend_not_configured",
145
+ "message": "Routing succeeded, but no live backend is configured for the selected model.",
146
+ "selected_model": selected["model_id"],
147
+ "required_config": {
148
+ "mode": "live",
149
+ "backends": {
150
+ selected["model_id"]: {
151
+ "type": "openai_compatible",
152
+ "base_url": "http://127.0.0.1:8000/v1",
153
+ "api_key_env": "OPTIONAL_ENV_NAME",
154
+ },
155
+ },
156
+ },
157
+ "receipt": receipt,
158
+ **route_result,
159
+ }
160
+ return _proxy_openai_chat_completion(payload, selected, backend, config, route_result, receipt)
161
+
162
+
163
+ def smoke_gateway(
164
+ *,
165
+ registry_path: Path | None = None,
166
+ config_path: Path | None = None,
167
+ ) -> dict[str, Any]:
168
+ resolved_registry = registry_path or default_registry_path()
169
+ resolved_config = config_path or _temporary_route_only_config()
170
+ route_payload = load_json_mapping(default_route_request_path())
171
+ chat_payload = load_json_mapping(default_openai_chat_request_path())
172
+ chat_payload["dry_run"] = True
173
+
174
+ health_status, health = handle_gateway_request("GET", "/health", None, resolved_registry, resolved_config)
175
+ models_status, models = handle_gateway_request("GET", "/v1/models", None, resolved_registry, resolved_config)
176
+ route_status, route = handle_gateway_request("POST", "/v1/route", route_payload, resolved_registry, resolved_config)
177
+ chat_status, chat = handle_gateway_request(
178
+ "POST",
179
+ "/v1/chat/completions",
180
+ chat_payload,
181
+ resolved_registry,
182
+ resolved_config,
183
+ )
184
+
185
+ if health_status != 200 or models_status != 200 or route_status != 200 or chat_status != 200:
186
+ raise RuntimeError("MaramaRoute gateway smoke failed")
187
+ if chat.get("object") != "chat.completion" or chat["marama_route"]["selected_model"] is None:
188
+ raise RuntimeError("MaramaRoute gateway did not return a routed chat response")
189
+ return {
190
+ "ok": True,
191
+ "product": PRODUCT_NAME,
192
+ "mode": health["mode"],
193
+ "model_count": health["model_count"],
194
+ "route_selected_model": route["route_decision"]["selected_model"]["model_id"],
195
+ "chat_selected_model": chat["marama_route"]["selected_model"]["model_id"],
196
+ "configured_backends": health["configured_backends"],
197
+ }
198
+
199
+
200
+ def serve_gateway(
201
+ *,
202
+ registry_path: Path | None = None,
203
+ config_path: Path | None = None,
204
+ host: str = "127.0.0.1",
205
+ port: int = 8787,
206
+ open_browser: bool = False,
207
+ smoke: bool = False,
208
+ ) -> int:
209
+ resolved_registry = registry_path or default_registry_path()
210
+ if smoke:
211
+ print(json.dumps(smoke_gateway(registry_path=resolved_registry, config_path=config_path), indent=2, sort_keys=True))
212
+ return 0
213
+
214
+ html = build_expanded_dashboard_html(build_dashboard_state(resolved_registry))
215
+ return serve_dashboard(
216
+ product_name=f"{PRODUCT_NAME} Gateway",
217
+ html=html,
218
+ api_handler=lambda method, path, request_payload: handle_gateway_request(
219
+ method,
220
+ path,
221
+ request_payload,
222
+ resolved_registry,
223
+ config_path,
224
+ ),
225
+ host=host,
226
+ port=port,
227
+ open_browser=open_browser,
228
+ api_path_prefixes=("/api/", "/v1/"),
229
+ api_exact_paths=("/health",),
230
+ )
231
+
232
+
233
+ def _backend_for_model(model_id: str, config: dict[str, Any]) -> dict[str, Any] | None:
234
+ backends = config.get("backends")
235
+ if not isinstance(backends, dict):
236
+ return None
237
+ backend = backends.get(model_id) or backends.get("*")
238
+ return dict(backend) if isinstance(backend, dict) else None
239
+
240
+
241
+ def _proxy_openai_chat_completion(
242
+ payload: dict[str, Any],
243
+ selected: dict[str, Any],
244
+ backend: dict[str, Any],
245
+ config: dict[str, Any],
246
+ route_result: dict[str, Any],
247
+ receipt: dict[str, Any],
248
+ ) -> tuple[int, dict[str, Any]]:
249
+ if str(backend.get("type") or "openai_compatible") != "openai_compatible":
250
+ return 424, {"ok": False, "error": "unsupported_backend_type", "backend": backend}
251
+
252
+ base_url = str(backend.get("base_url") or "").rstrip("/")
253
+ if not base_url:
254
+ return 424, {"ok": False, "error": "backend_base_url_missing", "selected_model": selected["model_id"]}
255
+ endpoint = f"{base_url}/chat/completions"
256
+ upstream_payload = dict(payload)
257
+ upstream_payload["model"] = str(backend.get("model") or selected["model_id"])
258
+ for key in ("route", "routing", "dry_run", "marama_route_dry_run"):
259
+ upstream_payload.pop(key, None)
260
+
261
+ headers = {"Content-Type": "application/json"}
262
+ api_key_env = str(backend.get("api_key_env") or "")
263
+ if api_key_env and os.getenv(api_key_env):
264
+ headers["Authorization"] = f"Bearer {os.environ[api_key_env]}"
265
+ headers.update({str(key): str(value) for key, value in dict(backend.get("headers") or {}).items()})
266
+
267
+ timeout = float(backend.get("timeout_seconds") or config.get("default_timeout_seconds") or 120)
268
+ request = urllib.request.Request(
269
+ endpoint,
270
+ data=json.dumps(upstream_payload).encode("utf-8"),
271
+ headers=headers,
272
+ method="POST",
273
+ )
274
+ try:
275
+ with urllib.request.urlopen(request, timeout=timeout) as response: # noqa: S310 - operator-configured local/remote backend
276
+ body = response.read().decode("utf-8")
277
+ payload_out = json.loads(body)
278
+ if not isinstance(payload_out, dict):
279
+ raise ValueError("upstream response was not a JSON object")
280
+ payload_out["marama_route"] = {
281
+ "dry_run": False,
282
+ "selected_model": selected,
283
+ "fallback_models": route_result["route_decision"]["fallback_models"],
284
+ "rejected_count": len(route_result["route_decision"]["rejected"]),
285
+ "receipt": receipt,
286
+ "backend_base_url": base_url,
287
+ "prompt_retention": config.get("prompt_retention", "not_stored_by_default"),
288
+ }
289
+ return int(response.status), payload_out
290
+ except urllib.error.HTTPError as exc:
291
+ return exc.code, {
292
+ "ok": False,
293
+ "error": "backend_http_error",
294
+ "status": exc.code,
295
+ "body": exc.read().decode("utf-8", errors="replace"),
296
+ "receipt": receipt,
297
+ **route_result,
298
+ }
299
+ except Exception as exc:
300
+ return 502, {
301
+ "ok": False,
302
+ "error": "backend_unavailable",
303
+ "message": str(exc),
304
+ "receipt": receipt,
305
+ **route_result,
306
+ }
307
+
308
+
309
+ def _temporary_route_only_config() -> Path:
310
+ path = Path(tempfile.gettempdir()) / "marama-route-smoke.gateway.json"
311
+ path.write_text(json.dumps(DEFAULT_GATEWAY_CONFIG, indent=2, sort_keys=True), encoding="utf-8")
312
+ return path
product_manifest.json CHANGED
@@ -1,54 +1,68 @@
1
- {
2
- "product_name": "LumynaX MaramaRoute",
3
- "slug": "lumynax-marama-route",
4
  "publisher": "AbteeX AI Labs",
5
  "family": "LumynaX sovereign products",
6
  "stage": "local_runtime",
7
- "positioning": "Sovereign model router for LumynaX releases",
8
- "target_region": "NZ",
9
- "compatibility_goal": "OpenAI/OpenRouter-style model routing for LumynaX releases",
10
- "primary_modules": [
11
- "registry_compiler",
12
- "sovereign_router",
13
- "gateway_adapter",
14
- "catalog_search",
15
- "model_comparison",
16
- "scenario_matrix",
17
- "provider_config_export",
18
- "policy_packs",
19
- "telemetry_ledger",
20
- "opencode_provider_config",
21
- "openai_compatible_gateway_service",
22
- "backend_proxy_config"
23
- ],
24
- "runtime_entrypoints": [
25
- "python -m tinyluminax.products.marama_route.cli route",
26
- "python -m tinyluminax.products.marama_route.cli models",
27
- "python -m tinyluminax.products.marama_route.cli chat-dry-run",
28
- "python -m tinyluminax.products.marama_route.cli catalog",
29
- "python -m tinyluminax.products.marama_route.cli matrix",
30
- "python -m tinyluminax.products.marama_route.cli opencode-config",
 
 
 
 
 
 
 
 
 
 
 
 
31
  "python -m tinyluminax.products.marama_route.cli ui",
32
- "python -m tinyluminax.products.marama_route.cli serve"
33
- ],
34
- "api_surfaces": [
35
- "GET /health",
36
- "GET /v1/health",
37
- "GET /v1/models",
38
- "POST /v1/route",
39
- "POST /v1/chat/completions",
40
- "POST /v1/embeddings",
41
- "GET /api/health",
42
- "POST /api/route",
43
- "POST /api/catalog",
44
- "POST /api/compare",
45
- "POST /api/matrix",
46
- "POST /api/opencode-config"
47
  ],
48
- "brand_system": {
49
- "paper": "#fffefa",
50
- "ink": "#0a0a0b",
51
- "accent": "#e08a2c",
52
- "muted": "#726b62"
53
- }
54
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "product_name": "LumynaX MaramaRoute",
3
+ "slug": "lumynax-marama-route",
4
  "publisher": "AbteeX AI Labs",
5
  "family": "LumynaX sovereign products",
6
  "stage": "local_runtime",
7
+ "package": {
8
+ "pypi_name": "lumynax-marama-route",
9
+ "python_import": "marama_route",
10
+ "console_script": "marama-route",
11
+ "version": "0.4.0",
12
+ "release_artifacts": [
13
+ "wheel",
14
+ "sdist",
15
+ "huggingface_model_repo",
16
+ "github_source"
17
+ ]
18
+ },
19
+ "positioning": "Sovereign model router for LumynaX releases",
20
+ "target_region": "NZ",
21
+ "compatibility_goal": "OpenAI/OpenRouter-style model routing for LumynaX releases",
22
+ "primary_modules": [
23
+ "registry_compiler",
24
+ "sovereign_router",
25
+ "gateway_adapter",
26
+ "catalog_search",
27
+ "model_comparison",
28
+ "scenario_matrix",
29
+ "provider_config_export",
30
+ "policy_packs",
31
+ "telemetry_ledger",
32
+ "opencode_provider_config",
33
+ "openai_compatible_gateway_service",
34
+ "backend_proxy_config"
35
+ ],
36
+ "runtime_entrypoints": [
37
+ "python -m tinyluminax.products.marama_route.cli route",
38
+ "python -m tinyluminax.products.marama_route.cli models",
39
+ "python -m tinyluminax.products.marama_route.cli chat-dry-run",
40
+ "python -m tinyluminax.products.marama_route.cli catalog",
41
+ "python -m tinyluminax.products.marama_route.cli matrix",
42
+ "python -m tinyluminax.products.marama_route.cli opencode-config",
43
  "python -m tinyluminax.products.marama_route.cli ui",
44
+ "python -m tinyluminax.products.marama_route.cli serve",
45
+ "marama-route serve",
46
+ "marama-route serve --smoke"
 
 
 
 
 
 
 
 
 
 
 
 
47
  ],
48
+ "api_surfaces": [
49
+ "GET /health",
50
+ "GET /v1/health",
51
+ "GET /v1/models",
52
+ "POST /v1/route",
53
+ "POST /v1/chat/completions",
54
+ "POST /v1/embeddings",
55
+ "GET /api/health",
56
+ "POST /api/route",
57
+ "POST /api/catalog",
58
+ "POST /api/compare",
59
+ "POST /api/matrix",
60
+ "POST /api/opencode-config"
61
+ ],
62
+ "brand_system": {
63
+ "paper": "#fffefa",
64
+ "ink": "#0a0a0b",
65
+ "accent": "#e08a2c",
66
+ "muted": "#726b62"
67
+ }
68
+ }
pyproject.toml CHANGED
@@ -4,18 +4,40 @@ build-backend = "setuptools.build_meta"
4
 
5
  [project]
6
  name = "lumynax-marama-route"
7
- version = "0.3.0"
8
  description = "LumynaX MaramaRoute: sovereign OpenAI-compatible model router for LumynaX releases."
9
  readme = "README.md"
10
  requires-python = ">=3.11"
11
  license = "Apache-2.0"
12
  authors = [{ name = "AbteeX AI Labs" }]
13
- keywords = ["lumynax", "marama-route", "model-router", "sovereignty", "openrouter-alternative", "openai-compatible"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  [project.urls]
16
  Homepage = "https://lumynax.com"
17
- Repository = "https://huggingface.co/AbteeXAILab/marama-route"
 
18
  Abteex = "https://abteex.com"
 
19
 
20
  [project.scripts]
21
  marama-route = "marama_route.cli:main"
@@ -25,5 +47,8 @@ where = ["."]
25
  include = ["marama_route*"]
26
  namespaces = true
27
 
 
 
 
28
  [tool.setuptools.package-data]
29
- marama_route = ["configs/*", "examples/*", "integrations/*", "schemas/*"]
 
4
 
5
  [project]
6
  name = "lumynax-marama-route"
7
+ version = "0.4.0"
8
  description = "LumynaX MaramaRoute: sovereign OpenAI-compatible model router for LumynaX releases."
9
  readme = "README.md"
10
  requires-python = ">=3.11"
11
  license = "Apache-2.0"
12
  authors = [{ name = "AbteeX AI Labs" }]
13
+ maintainers = [{ name = "AbteeX AI Labs" }]
14
+ keywords = ["lumynax", "marama-route", "model-router", "sovereignty", "openrouter-alternative", "openai-compatible", "new-zealand"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Environment :: Console",
18
+ "Environment :: Web Environment",
19
+ "Intended Audience :: Developers",
20
+ "Intended Audience :: Information Technology",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Topic :: Internet :: WWW/HTTP :: HTTP Servers",
26
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
27
+ "Topic :: Software Development :: Libraries :: Python Modules",
28
+ "Typing :: Typed",
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ release = ["build>=1.2", "twine>=5.1"]
33
+ dev = ["build>=1.2", "ruff>=0.6", "twine>=5.1"]
34
 
35
  [project.urls]
36
  Homepage = "https://lumynax.com"
37
+ Repository = "https://github.com/Aimaghsoodi/TinyLuminaX"
38
+ "Hugging Face" = "https://huggingface.co/AbteeXAILab/marama-route"
39
  Abteex = "https://abteex.com"
40
+ Issues = "https://github.com/Aimaghsoodi/TinyLuminaX/issues"
41
 
42
  [project.scripts]
43
  marama-route = "marama_route.cli:main"
 
47
  include = ["marama_route*"]
48
  namespaces = true
49
 
50
+ [tool.setuptools]
51
+ include-package-data = true
52
+
53
  [tool.setuptools.package-data]
54
+ marama_route = ["py.typed", "configs/*", "examples/*", "integrations/*", "schemas/*"]