harshatheg commited on
Commit
c87a0aa
·
0 Parent(s):

Release v1

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .dockerignore +9 -0
  2. .gitattributes +39 -0
  3. .gitignore +7 -0
  4. API.md +118 -0
  5. Dockerfile +21 -0
  6. LICENSE +202 -0
  7. MODEL_CARD.md +30 -0
  8. NOTICE +16 -0
  9. README.md +132 -0
  10. adapter/adapter_config.json +43 -0
  11. adapter/adapter_model.safetensors +3 -0
  12. assets/base_files.sha256.json +32 -0
  13. assets/codebook255.json +1532 -0
  14. assets/examples/bounding-box.png +3 -0
  15. assets/examples/intent-classification.png +0 -0
  16. assets/examples/visual-coordinates.png +3 -0
  17. assets/runtime_manifest.json +37 -0
  18. config.json +122 -0
  19. demo/README.md +49 -0
  20. demo/app.py +150 -0
  21. demo/assets/person.jpg +3 -0
  22. demo/client.py +166 -0
  23. demo/presets.py +23 -0
  24. demo/requirements.txt +3 -0
  25. docs/ARCHITECTURE.md +34 -0
  26. docs/DATA_AND_LICENSES.md +50 -0
  27. docs/DOWNLOADS.md +60 -0
  28. docs/HUB_METRICS.md +11 -0
  29. download.py +181 -0
  30. examples/atomic_request.json +1 -0
  31. examples/numeric_request.json +7 -0
  32. gpc1_server/__init__.py +3 -0
  33. gpc1_server/api.py +315 -0
  34. gpc1_server/assets.py +38 -0
  35. gpc1_server/bootstrap.py +23 -0
  36. gpc1_server/compiler.py +205 -0
  37. gpc1_server/errors.py +16 -0
  38. gpc1_server/native_onepass.py +122 -0
  39. gpc1_server/runtime.py +246 -0
  40. gpc1_server/schemas.py +50 -0
  41. gpc1_server/vision_cache.py +98 -0
  42. model/LICENSE +202 -0
  43. model/chat_template.jinja +154 -0
  44. model/config.json +115 -0
  45. model/generation_config.json +13 -0
  46. model/merges.txt +0 -0
  47. model/model-00001-of-00016.safetensors +3 -0
  48. model/model-00002-of-00016.safetensors +3 -0
  49. model/model-00003-of-00016.safetensors +3 -0
  50. model/model-00004-of-00016.safetensors +3 -0
.dockerignore ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ **/__pycache__
2
+ **/*.py[cod]
3
+ **/.pytest_cache
4
+ **/.cache
5
+ .git
6
+ .env
7
+ .env.*
8
+ base
9
+ model
.gitattributes ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ assets/examples/visual-coordinates.png filter=lfs diff=lfs merge=lfs -text
37
+ assets/examples/bounding-box.png filter=lfs diff=lfs merge=lfs -text
38
+ demo/assets/person.jpg filter=lfs diff=lfs merge=lfs -text
39
+ model/tokenizer.json filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.py[cod]
3
+ .pytest_cache/
4
+ .cache/
5
+ .env
6
+ .env.*
7
+ base/
API.md ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # GPC-1 API
2
+
3
+ The NVIDIA server exposes `GET /v1/models` and `POST /v1/chat/completions`. Requests use OpenAI-style `messages` and `response_format`; responses contain `choices` and a `gpc1` result object. This is a structured-prediction interface, not a complete Chat Completions implementation.
4
+
5
+ ## Requests and responses
6
+
7
+ Use `model: "gpc-1"` and a strict JSON schema:
8
+
9
+ ```json
10
+ {"type":"json_schema","json_schema":{"name":"result","strict":true,"schema":{}}}
11
+ ```
12
+
13
+ Replace `schema` with an explicit object schema. Require every property and set `additionalProperties: false`. Supported types are `object`, `array`, `string`, `number`, `integer`, `boolean`, and `null`. References, unions, recursion, and patterns are unsupported.
14
+
15
+ Put mode-specific options in `gpc1`. OpenAI Python clients can pass them through `extra_body={"gpc1": ...}`.
16
+
17
+ - `choices[0].message.content`: JSON-encoded selected values.
18
+ - `gpc1`: probability distributions, numeric summaries, and execution accounting.
19
+ - `usage`: `null`; token usage is not reported.
20
+ - Errors: `{"error":{"message": "...", "type": "...", "param": null, "code": "..."}}`.
21
+
22
+ ## Interpreting scores
23
+
24
+ Probabilities describe the model's distribution over the choices, numeric grid, or complete records supplied in the request. Changing that output space can change the scores. A probability of 0.9 is not a measured 90% accuracy rate; choose decision thresholds using representative examples from your application.
25
+
26
+ ## Atomic categorical
27
+
28
+ Set `mode: "atomic"`, `field_key`, and an ordered `choices` array of 2–255 unique JSON values. The schema must contain exactly that field with a matching `enum`.
29
+
30
+ The response includes `value`, `selected_index`, and `probabilities` in choice order.
31
+
32
+ [Complete categorical request](examples/atomic_request.json)
33
+
34
+ ## Numeric101
35
+
36
+ Set `mode: "numeric101"` and `numeric_fields`. Each field requires `key`, `description`, `minimum`, `maximum`, and `unit`. Optional `aliases` and `reference` clarify meaning. Schema properties must use `type: "number"` with matching bounds.
37
+
38
+ Each field uses `value(i) = minimum + (maximum - minimum) × i / 100`, for `i = 0…100`.
39
+
40
+ | Response field | Meaning |
41
+ | --- | --- |
42
+ | `choices[0].message.content` | Most likely grid value for each key |
43
+ | `gpc1.fields[key].map_value` | Most likely grid value |
44
+ | `gpc1.fields[key].expected_value` | Probability-weighted mean in the declared units |
45
+ | `gpc1.fields[key].probabilities` | Probabilities in ascending grid order |
46
+ | `gpc1.fields[key].normalized_grid` | Grid positions from 0 to 1 |
47
+
48
+ The mean may fall between grid positions. Numeric fields are separate marginals, not a full joint distribution.
49
+
50
+ [Complete numeric request](examples/numeric_request.json)
51
+
52
+ ## Finite joint
53
+
54
+ Set `mode: "finite_joint"` and `allowed_records` containing unique complete JSON records that satisfy the schema. The response includes the selected `value`, candidate `probabilities`, and `log_scores`.
55
+
56
+ The schema accepts 2–255 records, subject to stricter runtime memory and row limits. The default runtime limit is 32 records. Probabilities are conditional on the supplied records; this mode does not generate arbitrary JSON.
57
+
58
+ ## Images
59
+
60
+ Include one `image_url` content part containing a base64 PNG, JPEG, or WebP data URI:
61
+
62
+ ```json
63
+ {"type":"image_url","image_url":{"url":"data:image/jpeg;base64,..."}}
64
+ ```
65
+
66
+ Images are supported only in `numeric101`. HTTP(S) and file URLs are rejected. Describe the target, coordinate origin, axes, and units explicitly. For boxes, request `x_min`, `y_min`, `x_max`, and `y_max`; multiply normalized horizontal values by image width and vertical values by image height. Check coordinate ordering before drawing.
67
+
68
+ The visual README examples use `expected_value`, not the MAP values in message content.
69
+
70
+ ## Deployment
71
+
72
+ Set `GPC1_MODEL_PATH` to the packaged `model/` directory and `GPC1_API_KEY` to a secret. Send `Authorization: Bearer YOUR_KEY` to the model endpoints. Startup verifies the packaged base and adapter; serving does not fetch models or images from URLs.
73
+
74
+ Run one server worker per model on a GPU. Model requests execute one at a time; additional requests wait in a bounded queue. Use separate replicas to serve parallel traffic. `GET /healthz` returns `{"status":"ok"}`. `GET /readyz` returns `{"status":"ready"}` after the model backend is selected, or HTTP 503 with `{"status":"not_ready"}` before then. These two health endpoints do not require the bearer key and do not return model metadata.
75
+
76
+ Build the container from the downloaded package, then mount its model weights read-only. Set `GPC1_API_KEY` in your environment before launching:
77
+
78
+ ```bash
79
+ docker build -t gpc-1 .
80
+ docker run --gpus all --rm -p 127.0.0.1:8000:8000 \
81
+ -e GPC1_API_KEY -e GPC1_MODEL_PATH=/models/gpc-1 \
82
+ -v "$PWD/model:/models/gpc-1:ro" gpc-1
83
+ ```
84
+
85
+ For internet-facing deployment, put an HTTPS proxy in front of the server. Keep the bearer key server-side and configure request-size limits at the proxy too.
86
+
87
+ ### Context window
88
+
89
+ The release server admits up to **256K tokens (262,144)** per compiled input, matching the backbone's configured context capacity. This includes prompt formatting, schema, choices, numeric field definitions, and image tokens, not just the supplied text. Inputs are never silently truncated.
90
+
91
+ This is the configured ceiling, not a completed 256K GPU validation or a guarantee that every request fits on one GPU. Memory and latency depend on input length, images, and candidate count. Set `GPC1_MAX_INPUT_TOKENS` lower for your hardware. The hosted ZeroGPU demo uses a separate 8,192-token limit.
92
+
93
+ Finite-joint requests also have an aggregate padded-token budget, defaulting to twice the input ceiling (524,288 tokens). Larger candidate sets may reach that budget before the per-input ceiling. Request-body and output-memory limits remain independent.
94
+
95
+ | Setting | Default |
96
+ | --- | --- |
97
+ | `GPC1_MAX_INPUT_TOKENS` | 262,144; configurable from 1 to 262,144 |
98
+ | `GPC1_MAX_CONCURRENCY` | 1, required; use replicas for parallel traffic |
99
+ | `GPC1_MAX_PENDING_REQUESTS` | 4 waiting requests; one additional request may be active |
100
+ | `GPC1_MAX_QUEUE_WAIT_SECONDS` | 2 |
101
+ | `GPC1_MAX_BODY_BYTES` | 16 MiB |
102
+ | `GPC1_MAX_IMAGE_BYTES` | 3 MiB |
103
+ | `GPC1_MAX_IMAGE_PIXELS` | 16,777,216 |
104
+ | `GPC1_IMAGE_CACHE_MB` | 64; one image's vision features, or 0 to disable |
105
+ | `GPC1_MAX_JOINT_ROWS` | 32 |
106
+ | `GPC1_MAX_PADDED_TOKENS` | `max(131072, 2 × GPC1_MAX_INPUT_TOKENS)`; 524,288 by default |
107
+ | `GPC1_MAX_LOGIT_VECTORS` | 512 |
108
+ | `GPC1_MAX_LOGITS_BYTES` | 512 MiB |
109
+
110
+ Over-limit inputs are rejected, never truncated. POST requests require `Content-Length`. A full queue or an expired queue wait returns HTTP 429 with code `server_busy`; body and image size limits return 413. Set `GPC1_ADMIT_VISION_NUMERIC=disabled` or `GPC1_ADMIT_FINITE_JOINT=disabled` to disable those modes.
111
+
112
+ Repeated requests for the same processed image can reuse its vision features without changing the prediction math. A new image still requires encoding. The cache stays in memory, holds at most one image's features, and can be disabled with `GPC1_IMAGE_CACHE_MB=0`.
113
+
114
+ ## Unsupported
115
+
116
+ Streaming, tools, audio, remote image fetching, atomic image classification, image-conditioned finite-joint output, and unrestricted JSON generation. Omit `temperature` and `max_completion_tokens`; they are rejected rather than ignored.
117
+
118
+ [Setup](README.md#get-started) · [Downloads and CI](docs/DOWNLOADS.md) · [Architecture](docs/ARCHITECTURE.md) · [Model card](MODEL_CARD.md)
Dockerfile ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM nvidia/cuda:13.0.1-cudnn-runtime-ubuntu24.04
2
+
3
+ ENV PYTHONDONTWRITEBYTECODE=1 \
4
+ PYTHONUNBUFFERED=1 \
5
+ HF_HUB_OFFLINE=1 \
6
+ TRANSFORMERS_OFFLINE=1 \
7
+ GPC1_MAX_CONCURRENCY=1
8
+ WORKDIR /srv/gpc1
9
+ RUN apt-get update && apt-get install -y --no-install-recommends python3.12 python3-pip \
10
+ && rm -rf /var/lib/apt/lists/*
11
+ COPY requirements.txt ./
12
+ RUN python3.12 -m pip install --no-cache-dir --break-system-packages \
13
+ --index-url https://download.pytorch.org/whl/cu130 torch==2.9.1 torchvision==0.24.1 \
14
+ && python3.12 -m pip install --no-cache-dir --break-system-packages -r requirements.txt
15
+ COPY gpc1_server ./gpc1_server
16
+ COPY assets ./assets
17
+ COPY adapter ./adapter
18
+ RUN useradd --create-home --uid 10001 gpc1 && chown -R gpc1:gpc1 /srv/gpc1
19
+ USER gpc1
20
+ EXPOSE 8000
21
+ CMD ["python3.12", "-m", "uvicorn", "gpc1_server.api:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1", "--no-access-log"]
LICENSE ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2026 Alibaba Cloud
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
MODEL_CARD.md ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # GPC-1 model card
2
+
3
+ GPC-1 is a structured prediction model based on Qwen3.5-35B-A3B. It supports categorical choices, bounded numeric estimates, image-conditioned coordinates, and complete records selected from a caller-defined set.
4
+
5
+ ## Model details
6
+
7
+ | Property | Value |
8
+ | --- | --- |
9
+ | API model ID | `gpc-1` |
10
+ | Backbone family | `Qwen/Qwen3.5-35B-A3B` |
11
+ | Architecture | Multimodal mixture of experts |
12
+ | Distribution | GPC-1 backbone and matching adapter |
13
+ | Runtime | NVIDIA, BF16, Transformers / PyTorch |
14
+ | Categorical support | 2–255 caller-defined choices |
15
+ | Numeric support | 101 positions per declared range |
16
+ | Joint output | Caller-enumerated complete records |
17
+ | Server input ceiling | 256K tokens (262,144), including compiled request overhead |
18
+ | License | Apache-2.0, with applicable third-party notices |
19
+
20
+ The release package contains GPC-1's backbone weights and matching adapter. The runtime loads and verifies both. See [setup](README.md#get-started), [download options](docs/DOWNLOADS.md), and [context configuration](API.md#context-window).
21
+
22
+ ## Intended use
23
+
24
+ Document and message classification, bounded numerical estimates, image-coordinate annotation, and structured workflow decisions. Define labels, units, and reference frames explicitly in each request.
25
+
26
+ Validate application-specific accuracy before using predictions in medical, legal, financial, or safety-critical decisions. The [API guide](API.md) defines score interpretation and supported output structures.
27
+
28
+ ## License
29
+
30
+ GPC-1 inference code and the Qwen base use Apache-2.0. Preserve applicable upstream notices. Example media retain their own licenses. See [LICENSE](LICENSE), [NOTICE](NOTICE), and [attribution](docs/DATA_AND_LICENSES.md).
NOTICE ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GPC-1
2
+
3
+ This distribution includes original GPC-1 inference software licensed under
4
+ the Apache License, Version 2.0.
5
+
6
+ GPC-1 is designed for and may be distributed with an adapter for
7
+ Qwen/Qwen3.5-35B-A3B at revision
8
+ 59d61f3ce65a6d9863b86d2e96597125219dc754. The upstream Qwen model is
9
+ licensed under the Apache License, Version 2.0. Preserve all applicable
10
+ upstream copyright, patent, trademark, and attribution notices.
11
+
12
+ Qwen model source: https://huggingface.co/Qwen/Qwen3.5-35B-A3B
13
+ Pinned license: https://huggingface.co/Qwen/Qwen3.5-35B-A3B/blob/59d61f3ce65a6d9863b86d2e96597125219dc754/LICENSE
14
+
15
+ Third-party attributions and example-media licenses are documented in
16
+ docs/DATA_AND_LICENSES.md.
README.md ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ base_model: Qwen/Qwen3.5-35B-A3B
4
+ library_name: transformers
5
+ base_model_relation: adapter
6
+ inference: false
7
+ tags:
8
+ - classification
9
+ - structured-prediction
10
+ - multimodal
11
+ - peft
12
+ - lora
13
+ ---
14
+
15
+ # GPC-1
16
+
17
+ Introducing **GPC-1**, a general-purpose classifier based on **Qwen3.5-35B-A3B**, post-trained for categorical decisions, bounded numerical estimates, and image-conditioned coordinates.
18
+
19
+ Define your labels, numeric ranges, or allowed JSON records through one API. Build document routing, scoring, visual annotation, and structured workflows around outputs your application can use directly. Multiple numeric fields are evaluated in one model pass.
20
+
21
+ [Demo](https://huggingface.co/spaces/harshatheg/GPC-1-Demo) · [Get started](#get-started) · [Download options](docs/DOWNLOADS.md) · [API guide](API.md) · [Local demo](demo/README.md) · [Model card](MODEL_CARD.md)
22
+
23
+ ## Key features
24
+
25
+ - **Categorical classification:** supply your own labels and receive a selected class with probabilities over the available choices.
26
+ - **Continuous range selections:** specify bounds and a field description to obtain a numerical estimate.
27
+ - **Image-conditioned numerical outputs:** ask for coordinates using the image and a natural-language description of the target.
28
+ - **Dependent structured choices:** score complete allowed JSON records together to keep related categorical decisions consistent.
29
+ - **OpenAI-style serving:** use familiar messages and JSON schemas on NVIDIA hardware.
30
+
31
+ ## Continuous range selections
32
+
33
+ Define the range, units, and reference frame for each numeric field. The same interface can represent a position, angle, distance, or score.
34
+
35
+ For example, `0` to `1` can describe a horizontal image coordinate, while `0` to `360` can describe an angle in degrees.
36
+
37
+ ```json
38
+ {
39
+ "key": "x_coord",
40
+ "description": "Horizontal position of the requested point; 0 is the left edge and 1 is the right edge",
41
+ "minimum": 0,
42
+ "maximum": 1,
43
+ "unit": "image-width fraction"
44
+ }
45
+ ```
46
+
47
+ Each field uses 101 evenly spaced positions. The API returns the most likely position and a probability-weighted mean that can fall between positions. These are finite-resolution estimates. [Output semantics](API.md#numeric101).
48
+
49
+ ## Examples
50
+
51
+ ### Bounding boxes
52
+
53
+ Four normalized coordinates define two corners: `x_min`, `y_min`, `x_max`, and `y_max`, with the origin at the image’s top-left. [Image request format](API.md#images).
54
+
55
+ ![Bounding box predicted by GPC-1 for the runner in red](assets/examples/bounding-box.png)
56
+
57
+ ### Human pose and keypoints
58
+
59
+ Describe a landmark and request its coordinates, such as `right_hip_x` and `right_hip_y`. Each coordinate is a separate numeric estimate; a complete skeleton is not enforced.
60
+
61
+ ![Human pose example with a tennis player](assets/examples/visual-coordinates.png)
62
+
63
+ ### Intent classification
64
+
65
+ Define intent labels in the request and receive a selected label with probabilities over those labels.
66
+
67
+ ![Intent-classification example](assets/examples/intent-classification.png)
68
+
69
+ ### Numerical request
70
+
71
+ > “A tank holds 200 liters. It is 35% full. Return the current volume of liquid in liters.”
72
+
73
+ Define `volume` over `0–200`, with units `liters`. GPC-1 returns `70` as the most likely value. The same numeric interface can express a rating, distance, fraction, or image coordinate; the prompt supplies the interpretation.
74
+
75
+ ### Dependent decisions
76
+
77
+ Instead of independently choosing a department and an action, provide the complete records your workflow permits. GPC-1 scores those records together, so an invalid combination cannot appear in the returned support. Joint scoring is bounded by the records you enumerate, not arbitrary JSON generation.
78
+
79
+ See the [API guide](API.md) for request payloads and response fields.
80
+
81
+ ## Get started
82
+
83
+ Run GPC-1 on compatible NVIDIA hardware with enough memory for the BF16 model. The complete release includes the GPC-1 backbone, its matching adapter, and serving code. The server loads both weight components automatically.
84
+
85
+ ```bash
86
+ python3 -m pip install "huggingface-hub>=1.7,<2"
87
+ hf download harshatheg/GPC-1 --local-dir gpc-1
88
+ python gpc-1/download.py verify gpc-1
89
+ cd gpc-1
90
+ python3 -m pip install --index-url https://download.pytorch.org/whl/cu130 torch==2.9.1 torchvision==0.24.1
91
+ python3 -m pip install -r requirements.txt
92
+ export GPC1_MODEL_PATH="$PWD/model"
93
+ export GPC1_API_KEY="$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))')"
94
+ python3 -m uvicorn gpc1_server.api:app \
95
+ --host 127.0.0.1 --port 8000 --no-access-log
96
+ ```
97
+
98
+ Keep the server running. In another terminal, open `gpc-1`, set `GPC1_API_KEY` to the same key used by the server, and send an OpenAI-style request to `/v1/chat/completions`:
99
+
100
+ ```bash
101
+ curl http://127.0.0.1:8000/v1/chat/completions \
102
+ -H "Authorization: Bearer $GPC1_API_KEY" \
103
+ -H "Content-Type: application/json" \
104
+ --data @examples/atomic_request.json
105
+ ```
106
+
107
+ The API uses familiar `messages` and `response_format` fields, with a `gpc1` extension for the prediction mode. See [serving and examples](API.md) for numeric ranges, image inputs, joint records, and deployment configuration. The [download guide](docs/DOWNLOADS.md) covers pinned revisions, Python, CI, and offline deployment.
108
+
109
+ ## At a glance
110
+
111
+ | Model detail | GPC-1 |
112
+ | --- | --- |
113
+ | Backbone family | Qwen3.5-35B-A3B |
114
+ | Packaged weights | GPC-1 backbone + matching adapter |
115
+ | Architecture | Multimodal mixture of experts |
116
+ | Categorical choices | 2–255 per request |
117
+ | Numeric outputs | 101-point support over a range you define |
118
+ | Image interface | Image-conditioned numeric fields |
119
+ | Dependent outputs | Enumerated joint records |
120
+ | Server input ceiling | 256K tokens (262,144), including compiled schema and image tokens |
121
+ | Serving | NVIDIA / PyTorch, OpenAI-style HTTP API |
122
+ | License | Apache-2.0 |
123
+
124
+ See [context and deployment](API.md#context-window) for hardware requirements and the hosted demo's separate limits.
125
+
126
+ ## Under the hood
127
+
128
+ GPC-1 uses schema-bound scoring to return typed predictions from its multimodal backbone and matching adapter. Numeric fields share one model pass; dependent categorical decisions are scored as complete allowed records.
129
+
130
+ [Architecture](docs/ARCHITECTURE.md) · [Model card](MODEL_CARD.md) · [API documentation](API.md) · [Local demo](demo/README.md) · [License](LICENSE) · [Notices](NOTICE)
131
+
132
+ GPC-1 inference code is Apache-2.0. The Qwen base retains its upstream license and notices; [third-party attribution](docs/DATA_AND_LICENSES.md) is included with the package.
adapter/adapter_config.json ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "alora_invocation_tokens": null,
3
+ "alpha_pattern": {},
4
+ "arrow_config": null,
5
+ "auto_mapping": null,
6
+ "base_model_name_or_path": "model",
7
+ "bias": "none",
8
+ "corda_config": null,
9
+ "ensure_weight_tying": false,
10
+ "eva_config": null,
11
+ "exclude_modules": null,
12
+ "fan_in_fan_out": false,
13
+ "inference_mode": true,
14
+ "init_lora_weights": true,
15
+ "kasa_config": null,
16
+ "layer_replication": null,
17
+ "layers_pattern": null,
18
+ "layers_to_transform": null,
19
+ "loftq_config": {},
20
+ "lora_alpha": 16,
21
+ "lora_bias": false,
22
+ "lora_dropout": 0.0,
23
+ "lora_ga_config": null,
24
+ "megatron_config": null,
25
+ "megatron_core": "megatron.core",
26
+ "modules_to_save": null,
27
+ "monteclora_config": null,
28
+ "peft_type": "LORA",
29
+ "peft_version": "0.21.0",
30
+ "qalora_group_size": 16,
31
+ "r": 16,
32
+ "rank_pattern": {},
33
+ "revision": null,
34
+ "target_modules": ".*language_model.*\\.(q_proj|k_proj|v_proj|o_proj|in_proj_qkv|out_proj)$",
35
+ "target_parameters": null,
36
+ "task_type": "CAUSAL_LM",
37
+ "trainable_token_indices": null,
38
+ "use_bdlora": null,
39
+ "use_dora": false,
40
+ "use_qalora": false,
41
+ "use_rslora": false,
42
+ "velora_config": null
43
+ }
adapter/adapter_model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2b2521cff2a98afdd42e3051c8c6de60ad928b1268244f2c13b4193059c6e1f4
3
+ size 45250376
assets/base_files.sha256.json ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "files": {
3
+ "chat_template.jinja": "a4aee8afcf2e0711942cf848899be66016f8d14a889ff9ede07bca099c28f715",
4
+ "config.json": "5e4d7f74fec2f360eb9cfbfcd6ec0c4c76e684d3a11caaed259d9fd9bfbc7944",
5
+ "generation_config.json": "4f25002776b741773666203dcea8f54619f177ace3ae483d311102092a4658e0",
6
+ "merges.txt": "a9d356d7bdf1ef4949e3e748e95b8e10ad9d4e2e838eddc38a0a7b6b94d1db8d",
7
+ "model-00001-of-00016.safetensors": "43a908b3d324d59b66c73a33e5c182809eb2df7da474b2fd2cbdabb16baeee38",
8
+ "model-00002-of-00016.safetensors": "4de54e39c2ecc6f3fb5e79e752f84b65c96c316d0d848c9b55d36a062df69e70",
9
+ "model-00003-of-00016.safetensors": "847472ab2e3e10da0baea3993890ce393cc1b364f53674a00a92ac6ed856ec0d",
10
+ "model-00004-of-00016.safetensors": "81b2b46905d62b66c6a0d2360050c266ea9416f913708e9a1447b28465f458f0",
11
+ "model-00005-of-00016.safetensors": "3a13dfef35c76070c92e7a303640f90b11795dd1ecc6e501d0e00d8310b0e3a2",
12
+ "model-00006-of-00016.safetensors": "baeb2742c637cb517e6410c1b7dc53ea69312078a1f0b708d43d7a62dffa22a9",
13
+ "model-00007-of-00016.safetensors": "5cc9bcd2e0a8e17f1319f8d808fd6595d9944f2a0e01e9e7cb8d550b9646e413",
14
+ "model-00008-of-00016.safetensors": "f294ac742f1f1762b8f05963812b33dd3bff1701b0ec887fe46717c7d66a638e",
15
+ "model-00009-of-00016.safetensors": "2198de19b6c6eda23f6b72f787f861ee4495ff9f29551f3b40cef72f1afbd96d",
16
+ "model-00010-of-00016.safetensors": "3fecc834fc976ae9205039b01c53049f18ca5b68d973f0977bfe90335faf0918",
17
+ "model-00011-of-00016.safetensors": "4e253c10600a5368df08bdd92e97c69c4b3b59fc4e7d8326a5f33b0888358779",
18
+ "model-00012-of-00016.safetensors": "420af5489aecba4d174c875520e3fcf5180ead42c9c6d8bc903ce8cb910f996c",
19
+ "model-00013-of-00016.safetensors": "a2b5627ba8a9b4564a795388ee90c757abb0f386aa6bef813ff3b6c52a9a85f7",
20
+ "model-00014-of-00016.safetensors": "e52489ba1847e0b1d89a445fa4f3b26dd1f2e957c70b35657e6333ea9be8f12a",
21
+ "model-00015-of-00016.safetensors": "7ef51deb1d96fe34a37d8bec88b4f7151c0ac762b802fe446c990ed4580bbb67",
22
+ "model-00016-of-00016.safetensors": "4df5299dd7e8033e6821336584fd5b8830c116cdea7385e64e3b0c2b14609ea8",
23
+ "model.safetensors.index.json": "f8448aa8b2fbf3519723b0c2965fc94ed64cafdd463381636a1df351952b3f1a",
24
+ "preprocessor_config.json": "27225450ac9c6529872ee1924fcb0962ff5634834f817040f444118116f4e516",
25
+ "tokenizer.json": "5f9e4d4901a92b997e463c1f46055088b6cca5ca61a6522d1b9f64c4bb81cb42",
26
+ "tokenizer_config.json": "316230d6a809701f4db5ea8f8fc862bc3a6f3229c937c174e674ff3ca0a64ac8",
27
+ "video_preprocessor_config.json": "7768af27c1fafa9cc9011c1dc20067e03f8915e03b63504550e11d5066986d13",
28
+ "vocab.json": "ce99b4cb2983d118806ce0a8b777a35b093e2000a503ebde25853284c9dfa003"
29
+ },
30
+ "model_id": "harshatheg/GPC-1",
31
+ "revision": "base-278124e95f96d21b"
32
+ }
assets/codebook255.json ADDED
@@ -0,0 +1,1532 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "code": "A",
4
+ "position": 0,
5
+ "token_id": 32,
6
+ "utf8_hex": "41"
7
+ },
8
+ {
9
+ "code": "B",
10
+ "position": 1,
11
+ "token_id": 33,
12
+ "utf8_hex": "42"
13
+ },
14
+ {
15
+ "code": "C",
16
+ "position": 2,
17
+ "token_id": 34,
18
+ "utf8_hex": "43"
19
+ },
20
+ {
21
+ "code": "D",
22
+ "position": 3,
23
+ "token_id": 35,
24
+ "utf8_hex": "44"
25
+ },
26
+ {
27
+ "code": "E",
28
+ "position": 4,
29
+ "token_id": 36,
30
+ "utf8_hex": "45"
31
+ },
32
+ {
33
+ "code": "F",
34
+ "position": 5,
35
+ "token_id": 37,
36
+ "utf8_hex": "46"
37
+ },
38
+ {
39
+ "code": "G",
40
+ "position": 6,
41
+ "token_id": 38,
42
+ "utf8_hex": "47"
43
+ },
44
+ {
45
+ "code": "H",
46
+ "position": 7,
47
+ "token_id": 39,
48
+ "utf8_hex": "48"
49
+ },
50
+ {
51
+ "code": "I",
52
+ "position": 8,
53
+ "token_id": 40,
54
+ "utf8_hex": "49"
55
+ },
56
+ {
57
+ "code": "J",
58
+ "position": 9,
59
+ "token_id": 41,
60
+ "utf8_hex": "4a"
61
+ },
62
+ {
63
+ "code": "K",
64
+ "position": 10,
65
+ "token_id": 42,
66
+ "utf8_hex": "4b"
67
+ },
68
+ {
69
+ "code": "L",
70
+ "position": 11,
71
+ "token_id": 43,
72
+ "utf8_hex": "4c"
73
+ },
74
+ {
75
+ "code": "M",
76
+ "position": 12,
77
+ "token_id": 44,
78
+ "utf8_hex": "4d"
79
+ },
80
+ {
81
+ "code": "N",
82
+ "position": 13,
83
+ "token_id": 45,
84
+ "utf8_hex": "4e"
85
+ },
86
+ {
87
+ "code": "O",
88
+ "position": 14,
89
+ "token_id": 46,
90
+ "utf8_hex": "4f"
91
+ },
92
+ {
93
+ "code": "P",
94
+ "position": 15,
95
+ "token_id": 47,
96
+ "utf8_hex": "50"
97
+ },
98
+ {
99
+ "code": "Q",
100
+ "position": 16,
101
+ "token_id": 48,
102
+ "utf8_hex": "51"
103
+ },
104
+ {
105
+ "code": "R",
106
+ "position": 17,
107
+ "token_id": 49,
108
+ "utf8_hex": "52"
109
+ },
110
+ {
111
+ "code": "S",
112
+ "position": 18,
113
+ "token_id": 50,
114
+ "utf8_hex": "53"
115
+ },
116
+ {
117
+ "code": "T",
118
+ "position": 19,
119
+ "token_id": 51,
120
+ "utf8_hex": "54"
121
+ },
122
+ {
123
+ "code": "U",
124
+ "position": 20,
125
+ "token_id": 52,
126
+ "utf8_hex": "55"
127
+ },
128
+ {
129
+ "code": "V",
130
+ "position": 21,
131
+ "token_id": 53,
132
+ "utf8_hex": "56"
133
+ },
134
+ {
135
+ "code": "W",
136
+ "position": 22,
137
+ "token_id": 54,
138
+ "utf8_hex": "57"
139
+ },
140
+ {
141
+ "code": "X",
142
+ "position": 23,
143
+ "token_id": 55,
144
+ "utf8_hex": "58"
145
+ },
146
+ {
147
+ "code": "Y",
148
+ "position": 24,
149
+ "token_id": 56,
150
+ "utf8_hex": "59"
151
+ },
152
+ {
153
+ "code": "Z",
154
+ "position": 25,
155
+ "token_id": 57,
156
+ "utf8_hex": "5a"
157
+ },
158
+ {
159
+ "code": "AA",
160
+ "position": 26,
161
+ "token_id": 5840,
162
+ "utf8_hex": "4141"
163
+ },
164
+ {
165
+ "code": "AB",
166
+ "position": 27,
167
+ "token_id": 1803,
168
+ "utf8_hex": "4142"
169
+ },
170
+ {
171
+ "code": "AC",
172
+ "position": 28,
173
+ "token_id": 1646,
174
+ "utf8_hex": "4143"
175
+ },
176
+ {
177
+ "code": "AD",
178
+ "position": 29,
179
+ "token_id": 1745,
180
+ "utf8_hex": "4144"
181
+ },
182
+ {
183
+ "code": "AE",
184
+ "position": 30,
185
+ "token_id": 13276,
186
+ "utf8_hex": "4145"
187
+ },
188
+ {
189
+ "code": "AF",
190
+ "position": 31,
191
+ "token_id": 8018,
192
+ "utf8_hex": "4146"
193
+ },
194
+ {
195
+ "code": "AG",
196
+ "position": 32,
197
+ "token_id": 1825,
198
+ "utf8_hex": "4147"
199
+ },
200
+ {
201
+ "code": "AH",
202
+ "position": 33,
203
+ "token_id": 28946,
204
+ "utf8_hex": "4148"
205
+ },
206
+ {
207
+ "code": "AI",
208
+ "position": 34,
209
+ "token_id": 15015,
210
+ "utf8_hex": "4149"
211
+ },
212
+ {
213
+ "code": "AJ",
214
+ "position": 35,
215
+ "token_id": 29595,
216
+ "utf8_hex": "414a"
217
+ },
218
+ {
219
+ "code": "AK",
220
+ "position": 36,
221
+ "token_id": 11568,
222
+ "utf8_hex": "414b"
223
+ },
224
+ {
225
+ "code": "AL",
226
+ "position": 37,
227
+ "token_id": 939,
228
+ "utf8_hex": "414c"
229
+ },
230
+ {
231
+ "code": "AM",
232
+ "position": 38,
233
+ "token_id": 1354,
234
+ "utf8_hex": "414d"
235
+ },
236
+ {
237
+ "code": "AN",
238
+ "position": 39,
239
+ "token_id": 1058,
240
+ "utf8_hex": "414e"
241
+ },
242
+ {
243
+ "code": "AO",
244
+ "position": 40,
245
+ "token_id": 18183,
246
+ "utf8_hex": "414f"
247
+ },
248
+ {
249
+ "code": "AP",
250
+ "position": 41,
251
+ "token_id": 2456,
252
+ "utf8_hex": "4150"
253
+ },
254
+ {
255
+ "code": "AQ",
256
+ "position": 42,
257
+ "token_id": 88898,
258
+ "utf8_hex": "4151"
259
+ },
260
+ {
261
+ "code": "AR",
262
+ "position": 43,
263
+ "token_id": 905,
264
+ "utf8_hex": "4152"
265
+ },
266
+ {
267
+ "code": "AS",
268
+ "position": 44,
269
+ "token_id": 1846,
270
+ "utf8_hex": "4153"
271
+ },
272
+ {
273
+ "code": "AT",
274
+ "position": 45,
275
+ "token_id": 802,
276
+ "utf8_hex": "4154"
277
+ },
278
+ {
279
+ "code": "AU",
280
+ "position": 46,
281
+ "token_id": 33869,
282
+ "utf8_hex": "4155"
283
+ },
284
+ {
285
+ "code": "AV",
286
+ "position": 47,
287
+ "token_id": 7839,
288
+ "utf8_hex": "4156"
289
+ },
290
+ {
291
+ "code": "AW",
292
+ "position": 48,
293
+ "token_id": 14006,
294
+ "utf8_hex": "4157"
295
+ },
296
+ {
297
+ "code": "AX",
298
+ "position": 49,
299
+ "token_id": 2860,
300
+ "utf8_hex": "4158"
301
+ },
302
+ {
303
+ "code": "AY",
304
+ "position": 50,
305
+ "token_id": 2926,
306
+ "utf8_hex": "4159"
307
+ },
308
+ {
309
+ "code": "AZ",
310
+ "position": 51,
311
+ "token_id": 22828,
312
+ "utf8_hex": "415a"
313
+ },
314
+ {
315
+ "code": "BA",
316
+ "position": 52,
317
+ "token_id": 6844,
318
+ "utf8_hex": "4241"
319
+ },
320
+ {
321
+ "code": "BB",
322
+ "position": 53,
323
+ "token_id": 9798,
324
+ "utf8_hex": "4242"
325
+ },
326
+ {
327
+ "code": "BC",
328
+ "position": 54,
329
+ "token_id": 4738,
330
+ "utf8_hex": "4243"
331
+ },
332
+ {
333
+ "code": "BD",
334
+ "position": 55,
335
+ "token_id": 9265,
336
+ "utf8_hex": "4244"
337
+ },
338
+ {
339
+ "code": "BE",
340
+ "position": 56,
341
+ "token_id": 11261,
342
+ "utf8_hex": "4245"
343
+ },
344
+ {
345
+ "code": "BF",
346
+ "position": 57,
347
+ "token_id": 19278,
348
+ "utf8_hex": "4246"
349
+ },
350
+ {
351
+ "code": "BG",
352
+ "position": 58,
353
+ "token_id": 36513,
354
+ "utf8_hex": "4247"
355
+ },
356
+ {
357
+ "code": "BH",
358
+ "position": 59,
359
+ "token_id": 93801,
360
+ "utf8_hex": "4248"
361
+ },
362
+ {
363
+ "code": "BI",
364
+ "position": 60,
365
+ "token_id": 8335,
366
+ "utf8_hex": "4249"
367
+ },
368
+ {
369
+ "code": "BJ",
370
+ "position": 61,
371
+ "token_id": 14544,
372
+ "utf8_hex": "424a"
373
+ },
374
+ {
375
+ "code": "BK",
376
+ "position": 62,
377
+ "token_id": 85266,
378
+ "utf8_hex": "424b"
379
+ },
380
+ {
381
+ "code": "BL",
382
+ "position": 63,
383
+ "token_id": 9110,
384
+ "utf8_hex": "424c"
385
+ },
386
+ {
387
+ "code": "BM",
388
+ "position": 64,
389
+ "token_id": 28000,
390
+ "utf8_hex": "424d"
391
+ },
392
+ {
393
+ "code": "BN",
394
+ "position": 65,
395
+ "token_id": 15137,
396
+ "utf8_hex": "424e"
397
+ },
398
+ {
399
+ "code": "BO",
400
+ "position": 66,
401
+ "token_id": 4525,
402
+ "utf8_hex": "424f"
403
+ },
404
+ {
405
+ "code": "BP",
406
+ "position": 67,
407
+ "token_id": 25261,
408
+ "utf8_hex": "4250"
409
+ },
410
+ {
411
+ "code": "BR",
412
+ "position": 68,
413
+ "token_id": 12717,
414
+ "utf8_hex": "4252"
415
+ },
416
+ {
417
+ "code": "BS",
418
+ "position": 69,
419
+ "token_id": 7116,
420
+ "utf8_hex": "4253"
421
+ },
422
+ {
423
+ "code": "BT",
424
+ "position": 70,
425
+ "token_id": 17078,
426
+ "utf8_hex": "4254"
427
+ },
428
+ {
429
+ "code": "BU",
430
+ "position": 71,
431
+ "token_id": 14497,
432
+ "utf8_hex": "4255"
433
+ },
434
+ {
435
+ "code": "BV",
436
+ "position": 72,
437
+ "token_id": 57339,
438
+ "utf8_hex": "4256"
439
+ },
440
+ {
441
+ "code": "BW",
442
+ "position": 73,
443
+ "token_id": 74909,
444
+ "utf8_hex": "4257"
445
+ },
446
+ {
447
+ "code": "BX",
448
+ "position": 74,
449
+ "token_id": 52072,
450
+ "utf8_hex": "4258"
451
+ },
452
+ {
453
+ "code": "BY",
454
+ "position": 75,
455
+ "token_id": 19305,
456
+ "utf8_hex": "4259"
457
+ },
458
+ {
459
+ "code": "CA",
460
+ "position": 76,
461
+ "token_id": 4887,
462
+ "utf8_hex": "4341"
463
+ },
464
+ {
465
+ "code": "CB",
466
+ "position": 77,
467
+ "token_id": 12607,
468
+ "utf8_hex": "4342"
469
+ },
470
+ {
471
+ "code": "CC",
472
+ "position": 78,
473
+ "token_id": 3580,
474
+ "utf8_hex": "4343"
475
+ },
476
+ {
477
+ "code": "CD",
478
+ "position": 79,
479
+ "token_id": 6281,
480
+ "utf8_hex": "4344"
481
+ },
482
+ {
483
+ "code": "CE",
484
+ "position": 80,
485
+ "token_id": 2036,
486
+ "utf8_hex": "4345"
487
+ },
488
+ {
489
+ "code": "CF",
490
+ "position": 81,
491
+ "token_id": 9362,
492
+ "utf8_hex": "4346"
493
+ },
494
+ {
495
+ "code": "CG",
496
+ "position": 82,
497
+ "token_id": 8533,
498
+ "utf8_hex": "4347"
499
+ },
500
+ {
501
+ "code": "CH",
502
+ "position": 83,
503
+ "token_id": 2080,
504
+ "utf8_hex": "4348"
505
+ },
506
+ {
507
+ "code": "CI",
508
+ "position": 84,
509
+ "token_id": 10911,
510
+ "utf8_hex": "4349"
511
+ },
512
+ {
513
+ "code": "CK",
514
+ "position": 85,
515
+ "token_id": 2925,
516
+ "utf8_hex": "434b"
517
+ },
518
+ {
519
+ "code": "CL",
520
+ "position": 86,
521
+ "token_id": 3040,
522
+ "utf8_hex": "434c"
523
+ },
524
+ {
525
+ "code": "CM",
526
+ "position": 87,
527
+ "token_id": 9690,
528
+ "utf8_hex": "434d"
529
+ },
530
+ {
531
+ "code": "CN",
532
+ "position": 88,
533
+ "token_id": 27731,
534
+ "utf8_hex": "434e"
535
+ },
536
+ {
537
+ "code": "CO",
538
+ "position": 89,
539
+ "token_id": 8023,
540
+ "utf8_hex": "434f"
541
+ },
542
+ {
543
+ "code": "CP",
544
+ "position": 90,
545
+ "token_id": 6901,
546
+ "utf8_hex": "4350"
547
+ },
548
+ {
549
+ "code": "CR",
550
+ "position": 91,
551
+ "token_id": 8702,
552
+ "utf8_hex": "4352"
553
+ },
554
+ {
555
+ "code": "CS",
556
+ "position": 92,
557
+ "token_id": 6211,
558
+ "utf8_hex": "4353"
559
+ },
560
+ {
561
+ "code": "CT",
562
+ "position": 93,
563
+ "token_id": 1123,
564
+ "utf8_hex": "4354"
565
+ },
566
+ {
567
+ "code": "CU",
568
+ "position": 94,
569
+ "token_id": 16307,
570
+ "utf8_hex": "4355"
571
+ },
572
+ {
573
+ "code": "CV",
574
+ "position": 95,
575
+ "token_id": 18990,
576
+ "utf8_hex": "4356"
577
+ },
578
+ {
579
+ "code": "CW",
580
+ "position": 96,
581
+ "token_id": 64045,
582
+ "utf8_hex": "4357"
583
+ },
584
+ {
585
+ "code": "CX",
586
+ "position": 97,
587
+ "token_id": 63037,
588
+ "utf8_hex": "4358"
589
+ },
590
+ {
591
+ "code": "CY",
592
+ "position": 98,
593
+ "token_id": 33380,
594
+ "utf8_hex": "4359"
595
+ },
596
+ {
597
+ "code": "DA",
598
+ "position": 99,
599
+ "token_id": 6151,
600
+ "utf8_hex": "4441"
601
+ },
602
+ {
603
+ "code": "DB",
604
+ "position": 100,
605
+ "token_id": 3392,
606
+ "utf8_hex": "4442"
607
+ },
608
+ {
609
+ "code": "DC",
610
+ "position": 101,
611
+ "token_id": 5449,
612
+ "utf8_hex": "4443"
613
+ },
614
+ {
615
+ "code": "DD",
616
+ "position": 102,
617
+ "token_id": 3967,
618
+ "utf8_hex": "4444"
619
+ },
620
+ {
621
+ "code": "DE",
622
+ "position": 103,
623
+ "token_id": 1113,
624
+ "utf8_hex": "4445"
625
+ },
626
+ {
627
+ "code": "DF",
628
+ "position": 104,
629
+ "token_id": 5095,
630
+ "utf8_hex": "4446"
631
+ },
632
+ {
633
+ "code": "DG",
634
+ "position": 105,
635
+ "token_id": 51923,
636
+ "utf8_hex": "4447"
637
+ },
638
+ {
639
+ "code": "DH",
640
+ "position": 106,
641
+ "token_id": 49600,
642
+ "utf8_hex": "4448"
643
+ },
644
+ {
645
+ "code": "DI",
646
+ "position": 107,
647
+ "token_id": 17099,
648
+ "utf8_hex": "4449"
649
+ },
650
+ {
651
+ "code": "DJ",
652
+ "position": 108,
653
+ "token_id": 51483,
654
+ "utf8_hex": "444a"
655
+ },
656
+ {
657
+ "code": "DK",
658
+ "position": 109,
659
+ "token_id": 17756,
660
+ "utf8_hex": "444b"
661
+ },
662
+ {
663
+ "code": "DL",
664
+ "position": 110,
665
+ "token_id": 16037,
666
+ "utf8_hex": "444c"
667
+ },
668
+ {
669
+ "code": "DM",
670
+ "position": 111,
671
+ "token_id": 8135,
672
+ "utf8_hex": "444d"
673
+ },
674
+ {
675
+ "code": "DN",
676
+ "position": 112,
677
+ "token_id": 30237,
678
+ "utf8_hex": "444e"
679
+ },
680
+ {
681
+ "code": "DO",
682
+ "position": 113,
683
+ "token_id": 5683,
684
+ "utf8_hex": "444f"
685
+ },
686
+ {
687
+ "code": "DP",
688
+ "position": 114,
689
+ "token_id": 9992,
690
+ "utf8_hex": "4450"
691
+ },
692
+ {
693
+ "code": "DR",
694
+ "position": 115,
695
+ "token_id": 7444,
696
+ "utf8_hex": "4452"
697
+ },
698
+ {
699
+ "code": "DS",
700
+ "position": 116,
701
+ "token_id": 5751,
702
+ "utf8_hex": "4453"
703
+ },
704
+ {
705
+ "code": "DT",
706
+ "position": 117,
707
+ "token_id": 10284,
708
+ "utf8_hex": "4454"
709
+ },
710
+ {
711
+ "code": "DU",
712
+ "position": 118,
713
+ "token_id": 20887,
714
+ "utf8_hex": "4455"
715
+ },
716
+ {
717
+ "code": "DV",
718
+ "position": 119,
719
+ "token_id": 59884,
720
+ "utf8_hex": "4456"
721
+ },
722
+ {
723
+ "code": "DW",
724
+ "position": 120,
725
+ "token_id": 52396,
726
+ "utf8_hex": "4457"
727
+ },
728
+ {
729
+ "code": "DX",
730
+ "position": 121,
731
+ "token_id": 16103,
732
+ "utf8_hex": "4458"
733
+ },
734
+ {
735
+ "code": "DY",
736
+ "position": 122,
737
+ "token_id": 67547,
738
+ "utf8_hex": "4459"
739
+ },
740
+ {
741
+ "code": "EA",
742
+ "position": 123,
743
+ "token_id": 18535,
744
+ "utf8_hex": "4541"
745
+ },
746
+ {
747
+ "code": "EB",
748
+ "position": 124,
749
+ "token_id": 8006,
750
+ "utf8_hex": "4542"
751
+ },
752
+ {
753
+ "code": "EC",
754
+ "position": 125,
755
+ "token_id": 7263,
756
+ "utf8_hex": "4543"
757
+ },
758
+ {
759
+ "code": "ED",
760
+ "position": 126,
761
+ "token_id": 1425,
762
+ "utf8_hex": "4544"
763
+ },
764
+ {
765
+ "code": "EE",
766
+ "position": 127,
767
+ "token_id": 6878,
768
+ "utf8_hex": "4545"
769
+ },
770
+ {
771
+ "code": "EF",
772
+ "position": 128,
773
+ "token_id": 14453,
774
+ "utf8_hex": "4546"
775
+ },
776
+ {
777
+ "code": "EG",
778
+ "position": 129,
779
+ "token_id": 9097,
780
+ "utf8_hex": "4547"
781
+ },
782
+ {
783
+ "code": "EH",
784
+ "position": 130,
785
+ "token_id": 44072,
786
+ "utf8_hex": "4548"
787
+ },
788
+ {
789
+ "code": "EI",
790
+ "position": 131,
791
+ "token_id": 76089,
792
+ "utf8_hex": "4549"
793
+ },
794
+ {
795
+ "code": "EK",
796
+ "position": 132,
797
+ "token_id": 68720,
798
+ "utf8_hex": "454b"
799
+ },
800
+ {
801
+ "code": "EL",
802
+ "position": 133,
803
+ "token_id": 2662,
804
+ "utf8_hex": "454c"
805
+ },
806
+ {
807
+ "code": "EM",
808
+ "position": 134,
809
+ "token_id": 2629,
810
+ "utf8_hex": "454d"
811
+ },
812
+ {
813
+ "code": "EN",
814
+ "position": 135,
815
+ "token_id": 923,
816
+ "utf8_hex": "454e"
817
+ },
818
+ {
819
+ "code": "EO",
820
+ "position": 136,
821
+ "token_id": 6548,
822
+ "utf8_hex": "454f"
823
+ },
824
+ {
825
+ "code": "EP",
826
+ "position": 137,
827
+ "token_id": 8924,
828
+ "utf8_hex": "4550"
829
+ },
830
+ {
831
+ "code": "EQ",
832
+ "position": 138,
833
+ "token_id": 52194,
834
+ "utf8_hex": "4551"
835
+ },
836
+ {
837
+ "code": "ER",
838
+ "position": 139,
839
+ "token_id": 622,
840
+ "utf8_hex": "4552"
841
+ },
842
+ {
843
+ "code": "ES",
844
+ "position": 140,
845
+ "token_id": 1515,
846
+ "utf8_hex": "4553"
847
+ },
848
+ {
849
+ "code": "ET",
850
+ "position": 141,
851
+ "token_id": 1300,
852
+ "utf8_hex": "4554"
853
+ },
854
+ {
855
+ "code": "EU",
856
+ "position": 142,
857
+ "token_id": 37523,
858
+ "utf8_hex": "4555"
859
+ },
860
+ {
861
+ "code": "EV",
862
+ "position": 143,
863
+ "token_id": 44473,
864
+ "utf8_hex": "4556"
865
+ },
866
+ {
867
+ "code": "EW",
868
+ "position": 144,
869
+ "token_id": 36530,
870
+ "utf8_hex": "4557"
871
+ },
872
+ {
873
+ "code": "EX",
874
+ "position": 145,
875
+ "token_id": 3152,
876
+ "utf8_hex": "4558"
877
+ },
878
+ {
879
+ "code": "EZ",
880
+ "position": 146,
881
+ "token_id": 93924,
882
+ "utf8_hex": "455a"
883
+ },
884
+ {
885
+ "code": "FA",
886
+ "position": 147,
887
+ "token_id": 3505,
888
+ "utf8_hex": "4641"
889
+ },
890
+ {
891
+ "code": "FB",
892
+ "position": 148,
893
+ "token_id": 15731,
894
+ "utf8_hex": "4642"
895
+ },
896
+ {
897
+ "code": "FC",
898
+ "position": 149,
899
+ "token_id": 6542,
900
+ "utf8_hex": "4643"
901
+ },
902
+ {
903
+ "code": "FD",
904
+ "position": 150,
905
+ "token_id": 14176,
906
+ "utf8_hex": "4644"
907
+ },
908
+ {
909
+ "code": "FE",
910
+ "position": 151,
911
+ "token_id": 11091,
912
+ "utf8_hex": "4645"
913
+ },
914
+ {
915
+ "code": "FF",
916
+ "position": 152,
917
+ "token_id": 1686,
918
+ "utf8_hex": "4646"
919
+ },
920
+ {
921
+ "code": "FG",
922
+ "position": 153,
923
+ "token_id": 11660,
924
+ "utf8_hex": "4647"
925
+ },
926
+ {
927
+ "code": "FH",
928
+ "position": 154,
929
+ "token_id": 80440,
930
+ "utf8_hex": "4648"
931
+ },
932
+ {
933
+ "code": "FI",
934
+ "position": 155,
935
+ "token_id": 18836,
936
+ "utf8_hex": "4649"
937
+ },
938
+ {
939
+ "code": "FK",
940
+ "position": 156,
941
+ "token_id": 26132,
942
+ "utf8_hex": "464b"
943
+ },
944
+ {
945
+ "code": "FL",
946
+ "position": 157,
947
+ "token_id": 5934,
948
+ "utf8_hex": "464c"
949
+ },
950
+ {
951
+ "code": "FM",
952
+ "position": 158,
953
+ "token_id": 24794,
954
+ "utf8_hex": "464d"
955
+ },
956
+ {
957
+ "code": "FN",
958
+ "position": 159,
959
+ "token_id": 40229,
960
+ "utf8_hex": "464e"
961
+ },
962
+ {
963
+ "code": "FO",
964
+ "position": 160,
965
+ "token_id": 3660,
966
+ "utf8_hex": "464f"
967
+ },
968
+ {
969
+ "code": "FP",
970
+ "position": 161,
971
+ "token_id": 11361,
972
+ "utf8_hex": "4650"
973
+ },
974
+ {
975
+ "code": "FR",
976
+ "position": 162,
977
+ "token_id": 10191,
978
+ "utf8_hex": "4652"
979
+ },
980
+ {
981
+ "code": "FS",
982
+ "position": 163,
983
+ "token_id": 8225,
984
+ "utf8_hex": "4653"
985
+ },
986
+ {
987
+ "code": "FT",
988
+ "position": 164,
989
+ "token_id": 3860,
990
+ "utf8_hex": "4654"
991
+ },
992
+ {
993
+ "code": "FU",
994
+ "position": 165,
995
+ "token_id": 78413,
996
+ "utf8_hex": "4655"
997
+ },
998
+ {
999
+ "code": "FW",
1000
+ "position": 166,
1001
+ "token_id": 17680,
1002
+ "utf8_hex": "4657"
1003
+ },
1004
+ {
1005
+ "code": "FX",
1006
+ "position": 167,
1007
+ "token_id": 15900,
1008
+ "utf8_hex": "4658"
1009
+ },
1010
+ {
1011
+ "code": "FY",
1012
+ "position": 168,
1013
+ "token_id": 78138,
1014
+ "utf8_hex": "4659"
1015
+ },
1016
+ {
1017
+ "code": "GA",
1018
+ "position": 169,
1019
+ "token_id": 15653,
1020
+ "utf8_hex": "4741"
1021
+ },
1022
+ {
1023
+ "code": "GB",
1024
+ "position": 170,
1025
+ "token_id": 5213,
1026
+ "utf8_hex": "4742"
1027
+ },
1028
+ {
1029
+ "code": "GC",
1030
+ "position": 171,
1031
+ "token_id": 22150,
1032
+ "utf8_hex": "4743"
1033
+ },
1034
+ {
1035
+ "code": "GD",
1036
+ "position": 172,
1037
+ "token_id": 39500,
1038
+ "utf8_hex": "4744"
1039
+ },
1040
+ {
1041
+ "code": "GE",
1042
+ "position": 173,
1043
+ "token_id": 10460,
1044
+ "utf8_hex": "4745"
1045
+ },
1046
+ {
1047
+ "code": "GF",
1048
+ "position": 174,
1049
+ "token_id": 35131,
1050
+ "utf8_hex": "4746"
1051
+ },
1052
+ {
1053
+ "code": "GG",
1054
+ "position": 175,
1055
+ "token_id": 21563,
1056
+ "utf8_hex": "4747"
1057
+ },
1058
+ {
1059
+ "code": "GH",
1060
+ "position": 176,
1061
+ "token_id": 43194,
1062
+ "utf8_hex": "4748"
1063
+ },
1064
+ {
1065
+ "code": "GI",
1066
+ "position": 177,
1067
+ "token_id": 27127,
1068
+ "utf8_hex": "4749"
1069
+ },
1070
+ {
1071
+ "code": "GL",
1072
+ "position": 178,
1073
+ "token_id": 3697,
1074
+ "utf8_hex": "474c"
1075
+ },
1076
+ {
1077
+ "code": "GM",
1078
+ "position": 179,
1079
+ "token_id": 20011,
1080
+ "utf8_hex": "474d"
1081
+ },
1082
+ {
1083
+ "code": "GN",
1084
+ "position": 180,
1085
+ "token_id": 24368,
1086
+ "utf8_hex": "474e"
1087
+ },
1088
+ {
1089
+ "code": "GO",
1090
+ "position": 181,
1091
+ "token_id": 15058,
1092
+ "utf8_hex": "474f"
1093
+ },
1094
+ {
1095
+ "code": "GP",
1096
+ "position": 182,
1097
+ "token_id": 23658,
1098
+ "utf8_hex": "4750"
1099
+ },
1100
+ {
1101
+ "code": "GR",
1102
+ "position": 183,
1103
+ "token_id": 8362,
1104
+ "utf8_hex": "4752"
1105
+ },
1106
+ {
1107
+ "code": "GS",
1108
+ "position": 184,
1109
+ "token_id": 16035,
1110
+ "utf8_hex": "4753"
1111
+ },
1112
+ {
1113
+ "code": "GT",
1114
+ "position": 185,
1115
+ "token_id": 24583,
1116
+ "utf8_hex": "4754"
1117
+ },
1118
+ {
1119
+ "code": "GU",
1120
+ "position": 186,
1121
+ "token_id": 52857,
1122
+ "utf8_hex": "4755"
1123
+ },
1124
+ {
1125
+ "code": "GV",
1126
+ "position": 187,
1127
+ "token_id": 38403,
1128
+ "utf8_hex": "4756"
1129
+ },
1130
+ {
1131
+ "code": "GW",
1132
+ "position": 188,
1133
+ "token_id": 60456,
1134
+ "utf8_hex": "4757"
1135
+ },
1136
+ {
1137
+ "code": "GX",
1138
+ "position": 189,
1139
+ "token_id": 81519,
1140
+ "utf8_hex": "4758"
1141
+ },
1142
+ {
1143
+ "code": "GY",
1144
+ "position": 190,
1145
+ "token_id": 40097,
1146
+ "utf8_hex": "4759"
1147
+ },
1148
+ {
1149
+ "code": "HA",
1150
+ "position": 191,
1151
+ "token_id": 16522,
1152
+ "utf8_hex": "4841"
1153
+ },
1154
+ {
1155
+ "code": "HB",
1156
+ "position": 192,
1157
+ "token_id": 29722,
1158
+ "utf8_hex": "4842"
1159
+ },
1160
+ {
1161
+ "code": "HC",
1162
+ "position": 193,
1163
+ "token_id": 21756,
1164
+ "utf8_hex": "4843"
1165
+ },
1166
+ {
1167
+ "code": "HD",
1168
+ "position": 194,
1169
+ "token_id": 18567,
1170
+ "utf8_hex": "4844"
1171
+ },
1172
+ {
1173
+ "code": "HE",
1174
+ "position": 195,
1175
+ "token_id": 1736,
1176
+ "utf8_hex": "4845"
1177
+ },
1178
+ {
1179
+ "code": "HF",
1180
+ "position": 196,
1181
+ "token_id": 48043,
1182
+ "utf8_hex": "4846"
1183
+ },
1184
+ {
1185
+ "code": "HG",
1186
+ "position": 197,
1187
+ "token_id": 87013,
1188
+ "utf8_hex": "4847"
1189
+ },
1190
+ {
1191
+ "code": "HH",
1192
+ "position": 198,
1193
+ "token_id": 22456,
1194
+ "utf8_hex": "4848"
1195
+ },
1196
+ {
1197
+ "code": "HI",
1198
+ "position": 199,
1199
+ "token_id": 23165,
1200
+ "utf8_hex": "4849"
1201
+ },
1202
+ {
1203
+ "code": "HK",
1204
+ "position": 200,
1205
+ "token_id": 55523,
1206
+ "utf8_hex": "484b"
1207
+ },
1208
+ {
1209
+ "code": "HL",
1210
+ "position": 201,
1211
+ "token_id": 13097,
1212
+ "utf8_hex": "484c"
1213
+ },
1214
+ {
1215
+ "code": "HM",
1216
+ "position": 202,
1217
+ "token_id": 50397,
1218
+ "utf8_hex": "484d"
1219
+ },
1220
+ {
1221
+ "code": "HN",
1222
+ "position": 203,
1223
+ "token_id": 41741,
1224
+ "utf8_hex": "484e"
1225
+ },
1226
+ {
1227
+ "code": "HO",
1228
+ "position": 204,
1229
+ "token_id": 23073,
1230
+ "utf8_hex": "484f"
1231
+ },
1232
+ {
1233
+ "code": "HP",
1234
+ "position": 205,
1235
+ "token_id": 6401,
1236
+ "utf8_hex": "4850"
1237
+ },
1238
+ {
1239
+ "code": "HQ",
1240
+ "position": 206,
1241
+ "token_id": 86538,
1242
+ "utf8_hex": "4851"
1243
+ },
1244
+ {
1245
+ "code": "HR",
1246
+ "position": 207,
1247
+ "token_id": 16585,
1248
+ "utf8_hex": "4852"
1249
+ },
1250
+ {
1251
+ "code": "HS",
1252
+ "position": 208,
1253
+ "token_id": 11622,
1254
+ "utf8_hex": "4853"
1255
+ },
1256
+ {
1257
+ "code": "HT",
1258
+ "position": 209,
1259
+ "token_id": 2464,
1260
+ "utf8_hex": "4854"
1261
+ },
1262
+ {
1263
+ "code": "HU",
1264
+ "position": 210,
1265
+ "token_id": 84982,
1266
+ "utf8_hex": "4855"
1267
+ },
1268
+ {
1269
+ "code": "HV",
1270
+ "position": 211,
1271
+ "token_id": 75516,
1272
+ "utf8_hex": "4856"
1273
+ },
1274
+ {
1275
+ "code": "HW",
1276
+ "position": 212,
1277
+ "token_id": 36984,
1278
+ "utf8_hex": "4857"
1279
+ },
1280
+ {
1281
+ "code": "HX",
1282
+ "position": 213,
1283
+ "token_id": 58795,
1284
+ "utf8_hex": "4858"
1285
+ },
1286
+ {
1287
+ "code": "HY",
1288
+ "position": 214,
1289
+ "token_id": 47217,
1290
+ "utf8_hex": "4859"
1291
+ },
1292
+ {
1293
+ "code": "HZ",
1294
+ "position": 215,
1295
+ "token_id": 59675,
1296
+ "utf8_hex": "485a"
1297
+ },
1298
+ {
1299
+ "code": "IA",
1300
+ "position": 216,
1301
+ "token_id": 5681,
1302
+ "utf8_hex": "4941"
1303
+ },
1304
+ {
1305
+ "code": "IB",
1306
+ "position": 217,
1307
+ "token_id": 3151,
1308
+ "utf8_hex": "4942"
1309
+ },
1310
+ {
1311
+ "code": "IC",
1312
+ "position": 218,
1313
+ "token_id": 1271,
1314
+ "utf8_hex": "4943"
1315
+ },
1316
+ {
1317
+ "code": "ID",
1318
+ "position": 219,
1319
+ "token_id": 887,
1320
+ "utf8_hex": "4944"
1321
+ },
1322
+ {
1323
+ "code": "IE",
1324
+ "position": 220,
1325
+ "token_id": 5203,
1326
+ "utf8_hex": "4945"
1327
+ },
1328
+ {
1329
+ "code": "IF",
1330
+ "position": 221,
1331
+ "token_id": 2685,
1332
+ "utf8_hex": "4946"
1333
+ },
1334
+ {
1335
+ "code": "IG",
1336
+ "position": 222,
1337
+ "token_id": 1849,
1338
+ "utf8_hex": "4947"
1339
+ },
1340
+ {
1341
+ "code": "IH",
1342
+ "position": 223,
1343
+ "token_id": 72470,
1344
+ "utf8_hex": "4948"
1345
+ },
1346
+ {
1347
+ "code": "II",
1348
+ "position": 224,
1349
+ "token_id": 5370,
1350
+ "utf8_hex": "4949"
1351
+ },
1352
+ {
1353
+ "code": "IJ",
1354
+ "position": 225,
1355
+ "token_id": 74063,
1356
+ "utf8_hex": "494a"
1357
+ },
1358
+ {
1359
+ "code": "IK",
1360
+ "position": 226,
1361
+ "token_id": 27629,
1362
+ "utf8_hex": "494b"
1363
+ },
1364
+ {
1365
+ "code": "IL",
1366
+ "position": 227,
1367
+ "token_id": 1655,
1368
+ "utf8_hex": "494c"
1369
+ },
1370
+ {
1371
+ "code": "IM",
1372
+ "position": 228,
1373
+ "token_id": 1728,
1374
+ "utf8_hex": "494d"
1375
+ },
1376
+ {
1377
+ "code": "IN",
1378
+ "position": 229,
1379
+ "token_id": 669,
1380
+ "utf8_hex": "494e"
1381
+ },
1382
+ {
1383
+ "code": "IO",
1384
+ "position": 230,
1385
+ "token_id": 3682,
1386
+ "utf8_hex": "494f"
1387
+ },
1388
+ {
1389
+ "code": "IP",
1390
+ "position": 231,
1391
+ "token_id": 3191,
1392
+ "utf8_hex": "4950"
1393
+ },
1394
+ {
1395
+ "code": "IQ",
1396
+ "position": 232,
1397
+ "token_id": 59865,
1398
+ "utf8_hex": "4951"
1399
+ },
1400
+ {
1401
+ "code": "IR",
1402
+ "position": 233,
1403
+ "token_id": 2712,
1404
+ "utf8_hex": "4952"
1405
+ },
1406
+ {
1407
+ "code": "IS",
1408
+ "position": 234,
1409
+ "token_id": 1580,
1410
+ "utf8_hex": "4953"
1411
+ },
1412
+ {
1413
+ "code": "IT",
1414
+ "position": 235,
1415
+ "token_id": 922,
1416
+ "utf8_hex": "4954"
1417
+ },
1418
+ {
1419
+ "code": "IU",
1420
+ "position": 236,
1421
+ "token_id": 77243,
1422
+ "utf8_hex": "4955"
1423
+ },
1424
+ {
1425
+ "code": "IV",
1426
+ "position": 237,
1427
+ "token_id": 2990,
1428
+ "utf8_hex": "4956"
1429
+ },
1430
+ {
1431
+ "code": "IW",
1432
+ "position": 238,
1433
+ "token_id": 78493,
1434
+ "utf8_hex": "4957"
1435
+ },
1436
+ {
1437
+ "code": "IX",
1438
+ "position": 239,
1439
+ "token_id": 5228,
1440
+ "utf8_hex": "4958"
1441
+ },
1442
+ {
1443
+ "code": "IZ",
1444
+ "position": 240,
1445
+ "token_id": 2750,
1446
+ "utf8_hex": "495a"
1447
+ },
1448
+ {
1449
+ "code": "JA",
1450
+ "position": 241,
1451
+ "token_id": 42711,
1452
+ "utf8_hex": "4a41"
1453
+ },
1454
+ {
1455
+ "code": "JB",
1456
+ "position": 242,
1457
+ "token_id": 44568,
1458
+ "utf8_hex": "4a42"
1459
+ },
1460
+ {
1461
+ "code": "JC",
1462
+ "position": 243,
1463
+ "token_id": 56402,
1464
+ "utf8_hex": "4a43"
1465
+ },
1466
+ {
1467
+ "code": "JD",
1468
+ "position": 244,
1469
+ "token_id": 48236,
1470
+ "utf8_hex": "4a44"
1471
+ },
1472
+ {
1473
+ "code": "JE",
1474
+ "position": 245,
1475
+ "token_id": 38993,
1476
+ "utf8_hex": "4a45"
1477
+ },
1478
+ {
1479
+ "code": "JI",
1480
+ "position": 246,
1481
+ "token_id": 43559,
1482
+ "utf8_hex": "4a49"
1483
+ },
1484
+ {
1485
+ "code": "JJ",
1486
+ "position": 247,
1487
+ "token_id": 61250,
1488
+ "utf8_hex": "4a4a"
1489
+ },
1490
+ {
1491
+ "code": "JK",
1492
+ "position": 248,
1493
+ "token_id": 32942,
1494
+ "utf8_hex": "4a4b"
1495
+ },
1496
+ {
1497
+ "code": "JM",
1498
+ "position": 249,
1499
+ "token_id": 86302,
1500
+ "utf8_hex": "4a4d"
1501
+ },
1502
+ {
1503
+ "code": "JO",
1504
+ "position": 250,
1505
+ "token_id": 25310,
1506
+ "utf8_hex": "4a4f"
1507
+ },
1508
+ {
1509
+ "code": "JP",
1510
+ "position": 251,
1511
+ "token_id": 26313,
1512
+ "utf8_hex": "4a50"
1513
+ },
1514
+ {
1515
+ "code": "JR",
1516
+ "position": 252,
1517
+ "token_id": 81770,
1518
+ "utf8_hex": "4a52"
1519
+ },
1520
+ {
1521
+ "code": "JS",
1522
+ "position": 253,
1523
+ "token_id": 12185,
1524
+ "utf8_hex": "4a53"
1525
+ },
1526
+ {
1527
+ "code": "JT",
1528
+ "position": 254,
1529
+ "token_id": 78382,
1530
+ "utf8_hex": "4a54"
1531
+ }
1532
+ ]
assets/examples/bounding-box.png ADDED

Git LFS Details

  • SHA256: 0d6b5fe346dc05e7c6cd002bc041ceba816ab6f1082879111d8b87a3eafe893a
  • Pointer size: 131 Bytes
  • Size of remote file: 616 kB
assets/examples/intent-classification.png ADDED
assets/examples/visual-coordinates.png ADDED

Git LFS Details

  • SHA256: 335e079982a8f7ad6d8e18964dbce07899fdd15928d456cb11a711251cab5111
  • Pointer size: 131 Bytes
  • Size of remote file: 416 kB
assets/runtime_manifest.json ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "adapter": {
3
+ "adapter_config_sha256": "a17d24feae695bb21f1635d865f41ecc0e65f3b9e05f8a5e8e8ad5deb97535be",
4
+ "adapter_model_sha256": "2b2521cff2a98afdd42e3051c8c6de60ad928b1268244f2c13b4193059c6e1f4",
5
+ "adapter_tensor_digest": "f0e421ca7439a84b50f840d34dea59454c4cc389215101a79ae35c38d0309a00",
6
+ "default_path": "adapter",
7
+ "distribution": "bundled_sanitized_adapter"
8
+ },
9
+ "admission": {
10
+ "accepted_modes": [
11
+ "atomic",
12
+ "numeric101_text",
13
+ "numeric101_image",
14
+ "finite_joint"
15
+ ],
16
+ "default_max_input_tokens": 262144,
17
+ "note": "Operational default, not a model context-length claim. Requests over the configured limit fail without truncation."
18
+ },
19
+ "assets": {
20
+ "base_files": "assets/base_files.sha256.json",
21
+ "base_files_sha256": "c163d38c5dcd10d307ec81f5b4709b8ec154ef3640d2197bdd1bba3ce27e5993",
22
+ "codebook255": "assets/codebook255.json",
23
+ "codebook255_sha256": "4e2e52b4baf578c66bc1d9cf713cb9208ebbd44ce4bd09ed55d79f48fc0f67b7"
24
+ },
25
+ "base": {
26
+ "attention": "sdpa",
27
+ "dtype": "torch.bfloat16",
28
+ "model_id": "harshatheg/GPC-1",
29
+ "revision": "base-278124e95f96d21b",
30
+ "tokenizer_json_sha256": "5f9e4d4901a92b997e463c1f46055088b6cca5ca61a6522d1b9f64c4bb81cb42",
31
+ "upstream_model_id": "Qwen/Qwen3.5-35B-A3B",
32
+ "upstream_revision": "59d61f3ce65a6d9863b86d2e96597125219dc754"
33
+ },
34
+ "protocol": "gpc1-inference-release-runtime-v1",
35
+ "served_model_id": "gpc-1",
36
+ "weight_format": "adapter"
37
+ }
config.json ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "Qwen3_5MoeForConditionalGeneration"
4
+ ],
5
+ "gpc1_release": {
6
+ "adapter_path": "adapter",
7
+ "format_version": 1,
8
+ "model_path": "model",
9
+ "runtime_manifest": "assets/runtime_manifest.json",
10
+ "runtime_sha256": "d6b0407aef04676d26d54c8dabd20de72e3ecb977172ebcd843837e059f60d91"
11
+ },
12
+ "image_token_id": 248056,
13
+ "model_type": "qwen3_5_moe",
14
+ "text_config": {
15
+ "attention_bias": false,
16
+ "attention_dropout": 0.0,
17
+ "attn_output_gate": true,
18
+ "dtype": "bfloat16",
19
+ "eos_token_id": 248044,
20
+ "full_attention_interval": 4,
21
+ "head_dim": 256,
22
+ "hidden_act": "silu",
23
+ "hidden_size": 2048,
24
+ "initializer_range": 0.02,
25
+ "layer_types": [
26
+ "linear_attention",
27
+ "linear_attention",
28
+ "linear_attention",
29
+ "full_attention",
30
+ "linear_attention",
31
+ "linear_attention",
32
+ "linear_attention",
33
+ "full_attention",
34
+ "linear_attention",
35
+ "linear_attention",
36
+ "linear_attention",
37
+ "full_attention",
38
+ "linear_attention",
39
+ "linear_attention",
40
+ "linear_attention",
41
+ "full_attention",
42
+ "linear_attention",
43
+ "linear_attention",
44
+ "linear_attention",
45
+ "full_attention",
46
+ "linear_attention",
47
+ "linear_attention",
48
+ "linear_attention",
49
+ "full_attention",
50
+ "linear_attention",
51
+ "linear_attention",
52
+ "linear_attention",
53
+ "full_attention",
54
+ "linear_attention",
55
+ "linear_attention",
56
+ "linear_attention",
57
+ "full_attention",
58
+ "linear_attention",
59
+ "linear_attention",
60
+ "linear_attention",
61
+ "full_attention",
62
+ "linear_attention",
63
+ "linear_attention",
64
+ "linear_attention",
65
+ "full_attention"
66
+ ],
67
+ "linear_conv_kernel_dim": 4,
68
+ "linear_key_head_dim": 128,
69
+ "linear_num_key_heads": 16,
70
+ "linear_num_value_heads": 32,
71
+ "linear_value_head_dim": 128,
72
+ "mamba_ssm_dtype": "float32",
73
+ "max_position_embeddings": 262144,
74
+ "mlp_only_layers": [],
75
+ "model_type": "qwen3_5_moe_text",
76
+ "moe_intermediate_size": 512,
77
+ "mtp_num_hidden_layers": 1,
78
+ "mtp_use_dedicated_embeddings": false,
79
+ "num_attention_heads": 16,
80
+ "num_experts": 256,
81
+ "num_experts_per_tok": 8,
82
+ "num_hidden_layers": 40,
83
+ "num_key_value_heads": 2,
84
+ "rms_norm_eps": 1e-06,
85
+ "rope_parameters": {
86
+ "mrope_interleaved": true,
87
+ "mrope_section": [
88
+ 11,
89
+ 11,
90
+ 10
91
+ ],
92
+ "partial_rotary_factor": 0.25,
93
+ "rope_theta": 10000000,
94
+ "rope_type": "default"
95
+ },
96
+ "router_aux_loss_coef": 0.001,
97
+ "shared_expert_intermediate_size": 512,
98
+ "use_cache": true,
99
+ "vocab_size": 248320
100
+ },
101
+ "tie_word_embeddings": false,
102
+ "transformers_version": "4.57.0.dev0",
103
+ "video_token_id": 248057,
104
+ "vision_config": {
105
+ "deepstack_visual_indexes": [],
106
+ "depth": 27,
107
+ "hidden_act": "gelu_pytorch_tanh",
108
+ "hidden_size": 1152,
109
+ "in_channels": 3,
110
+ "initializer_range": 0.02,
111
+ "intermediate_size": 4304,
112
+ "model_type": "qwen3_5_moe",
113
+ "num_heads": 16,
114
+ "num_position_embeddings": 2304,
115
+ "out_hidden_size": 2048,
116
+ "patch_size": 16,
117
+ "spatial_merge_size": 2,
118
+ "temporal_patch_size": 2
119
+ },
120
+ "vision_end_token_id": 248054,
121
+ "vision_start_token_id": 248053
122
+ }
demo/README.md ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: GPC-1
3
+ emoji: ◈
4
+ colorFrom: gray
5
+ colorTo: green
6
+ sdk: gradio
7
+ sdk_version: 6.9.0
8
+ python_version: '3.12'
9
+ app_file: app.py
10
+ models:
11
+ - harshatheg/GPC-1
12
+ short_description: Context in. Structured predictions out.
13
+ license: apache-2.0
14
+ header: mini
15
+ ---
16
+
17
+ # GPC-1 demo
18
+
19
+ Upload context, define your output, and select **Run**. Presets cover classification, numerical analysis, boxes, pose, pixel art, and structured decisions.
20
+
21
+ ## Hosted demo: ZeroGPU
22
+
23
+ The [hosted demo](https://huggingface.co/spaces/harshatheg/GPC-1-Demo) loads GPC-1 directly on Hugging Face ZeroGPU. No separate inference server is needed. To host that version yourself, duplicate the Space, select ZeroGPU hardware, and add an `HF_TOKEN` secret with read access if the model requires it.
24
+
25
+ ## Server-backed demo
26
+
27
+ This directory contains a lightweight UI for an existing GPC-1 NVIDIA server. Start the server using the [setup guide](../README.md#get-started), then run these commands from the release directory:
28
+
29
+ ```bash
30
+ cd demo
31
+ python -m pip install -r requirements.txt
32
+ export GPC1_API_URL=http://127.0.0.1:8000
33
+ export GPC1_API_KEY=YOUR_SERVER_KEY
34
+ python app.py
35
+ ```
36
+
37
+ ### Host the server-backed UI on Spaces
38
+
39
+ Copy this directory to a Gradio Space. Configure `GPC1_API_URL` as the HTTPS origin of your NVIDIA server and `GPC1_API_KEY` as a **Space secret**. The lightweight UI calls the server; it does not load model weights on the Space CPU.
40
+
41
+ The key stays server-side. Requests are not retried automatically. Uploaded context is sent only to the configured model endpoint; temporary uploads are periodically removed. Images are accepted for numeric outputs. Numeric fields use finite-resolution ranges; JSON records select from the records supplied.
42
+
43
+ ## Input limits
44
+
45
+ The UI accepts up to 256 KiB of context text and a 4 MiB request body. The hosted ZeroGPU version also limits compiled inputs to 8,192 tokens. Use the server API directly for larger inputs; see [context configuration](../API.md#context-window) for its 262,144-token ceiling and hardware requirements.
46
+
47
+ [Model and API](https://huggingface.co/harshatheg/GPC-1)
48
+
49
+ The person photo used by the bounding-box and pose presets is [“Tim running” by Liber4l](https://commons.wikimedia.org/wiki/File:Tim_running.jpg), public domain.
demo/app.py ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from pathlib import Path
4
+
5
+ import gradio as gr
6
+ from PIL import Image, ImageDraw
7
+ from client import MODES, build_request, infer
8
+ from presets import preset
9
+
10
+ CSS = """
11
+ body, .gradio-container { background: #fff !important; color-scheme: light; }
12
+ .gradio-container { max-width: 1160px !important; padding: 36px 24px !important; }
13
+ #heading h1 { font-size: 32px; font-weight: 650; letter-spacing: -1.1px; margin-bottom: 4px; }
14
+ #heading p { color: #677078; font-size: 15px; }
15
+ #main { gap: 32px; margin-top: 24px; }
16
+ #run { min-height: 46px; border-radius: 10px; }
17
+ button:active { transform: scale(.96); }
18
+ button:focus-visible, input:focus-visible, textarea:focus-visible { outline: 2px solid #147f85; outline-offset: 2px; }
19
+ #timing { color: #677078; font-size: 12px; min-height: 18px; }
20
+ .image-container { outline: 1px solid rgb(0 0 0 / .1); }
21
+ .gradio-container .cm-editor, .gradio-container .cm-content,
22
+ .gradio-container .cm-gutters { background: #f8fafb !important; color: #17252b !important; }
23
+ .gradio-container .cm-content span { color: #17252b !important; }
24
+ .gradio-container .cm-gutters { color: #52616b !important; }
25
+ .gradio-container .cm-cursor { border-left-color: #17252b !important; }
26
+ .gradio-container .cm-selectionBackground { background: #cce8ea !important; }
27
+ .gradio-container .cm-activeLine, .gradio-container .cm-activeLineGutter { background: #edf3f4 !important; }
28
+ .gradio-container .icon-button { color: #52616b !important; }
29
+ .gradio-container button[aria-label="Paste from clipboard"] { color: #52616b !important; }
30
+ .gradio-container button { transition-property: background-color, color, border-color; transition-duration: 120ms; }
31
+ @media(max-width: 650px) { #heading, #main { margin-inline: -24px; width: calc(100% + 48px); } #main { gap: 24px; } }
32
+ @media(prefers-reduced-motion: reduce) { button:active { transform: none; } }
33
+ """
34
+ LIGHT_JS = """() => { document.documentElement.classList.remove('dark'); document.body.classList.remove('dark'); }"""
35
+
36
+ def visual_result(image, result, details):
37
+ fields=details.get('fields',{})
38
+ means={k:v['expected_value'] for k,v in fields.items()}
39
+ if image is not None:
40
+ output=image.convert('RGB').copy(); draw=ImageDraw.Draw(output); w,h=output.size
41
+ annotated=False
42
+ if {'x_min','y_min','x_max','y_max'} <= set(means):
43
+ box=[means[k] for k in ('x_min','y_min','x_max','y_max')]
44
+ if all(0<=v<=1 for v in box) and box[0]<box[2] and box[1]<box[3]:
45
+ draw.rectangle((box[0]*w,box[1]*h,box[2]*w,box[3]*h),outline='#008f95',width=max(2,w//180))
46
+ annotated=True
47
+ for key,x in means.items():
48
+ if key.endswith('_x') and key[:-2]+'_y' in means:
49
+ y=means[key[:-2]+'_y']; r=max(3,w//100)
50
+ if 0<=x<=1 and 0<=y<=1:
51
+ draw.ellipse((x*w-r,y*h-r,x*w+r,y*h+r), fill='#f56538',outline='white',width=2)
52
+ annotated=True
53
+ return output if annotated else None
54
+ keys=[f'p{r}_{c}' for r in range(5) for c in range(5)]
55
+ if set(result)==set(keys):
56
+ palette=['#ffffff','#238454','#ef625c','#f3bd43']
57
+ out=Image.new('RGB',(5,5),'white')
58
+ for r in range(5):
59
+ for c in range(5):
60
+ value=result[f'p{r}_{c}']
61
+ out.putpixel((c,r),tuple(bytes.fromhex(palette[max(0,min(3,round(value)))][1:])))
62
+ return out.resize((400,400),Image.Resampling.NEAREST)
63
+ return None
64
+
65
+ def run(context, image, mode, definition, context_file, infer_function=None):
66
+ try:
67
+ payload=build_request(context,image,mode,definition,context_file)
68
+ result,details,elapsed=(infer_function or infer)(payload)
69
+ picture=visual_result(image,result,details)
70
+ summaries={k:{'most_likely':v['map_value'],'mean':v['expected_value']} for k,v in details.get('fields',{}).items()}
71
+ timing_label='Model execution' if infer_function else 'API round trip'
72
+ return result,gr.update(value=picture,visible=True if picture is not None else 'hidden'),f'{elapsed:.2f} s · {timing_label}',summaries or details,details
73
+ except (ValueError, RuntimeError) as error:
74
+ raise gr.Error(str(error)) from None
75
+
76
+ def select_preset(name):
77
+ mode,context,definition=preset(name)
78
+ picture=Path(__file__).parent/'assets/person.jpg'
79
+ image=str(picture) if name in {'Bounding box','Human pose'} and picture.exists() else None
80
+ return mode,context,definition,image,None,None,gr.update(value=None,visible='hidden'),'',None,None,gr.update(open=image is not None)
81
+
82
+ def mode_default(mode):
83
+ names={'Categories':'Support routing','Numeric ranges':'Numerical analysis','JSON records':'Workflow decision'}
84
+ return preset(names[mode])[2]
85
+
86
+ def clear_output():
87
+ return None, gr.update(value=None,visible='hidden'), '', None, None
88
+
89
+ def create_demo(infer_function=None):
90
+ mode,context,definition=preset('Support routing')
91
+ with gr.Blocks(title='GPC-1',analytics_enabled=False,delete_cache=(600,600)) as demo:
92
+ gr.Markdown('# GPC-1\nContext in. Structured predictions out.',elem_id='heading')
93
+ with gr.Row(elem_id='main'):
94
+ with gr.Column(scale=1,min_width=300):
95
+ examples=gr.Dropdown(['Support routing','Numerical analysis','Bounding box','Human pose','Pixel art','Workflow decision'],value='Support routing',label='Try an example',filterable=False)
96
+ text=gr.Textbox(value=context,label='Context',placeholder='Describe the task and add your context…',lines=5,max_lines=12)
97
+ with gr.Accordion('Add an image or text file',open=False) as attachments:
98
+ image=gr.Image(type='pil',sources=['upload','clipboard'],label='Image · numeric ranges',height=220,format='png')
99
+ file=gr.File(file_types=['.txt','.md','.json'],file_count='single',label='Context file',type='filepath')
100
+ mode_input=gr.Radio(MODES,value=mode,label='Output type')
101
+ fields=gr.Code(value=definition,language='json',label='Define your JSON output',lines=9,max_lines=16,interactive=True)
102
+ button=gr.Button('Run',variant='primary',elem_id='run')
103
+ with gr.Column(scale=1,min_width=300):
104
+ result=gr.JSON(label='Output',value=None)
105
+ timing=gr.Markdown('',elem_id='timing')
106
+ picture=gr.Image(label='Visualization',visible='hidden',interactive=False,type='pil',format='png')
107
+ with gr.Accordion('Scores',open=False):
108
+ summaries=gr.JSON(label='Summary')
109
+ gr.Markdown('Numeric overlays use probability-weighted means. Pixel art rounds the most likely value to the nearest palette entry. [Score interpretation](https://huggingface.co/harshatheg/GPC-1/blob/main/API.md#interpreting-scores).')
110
+ details=gr.JSON(label='Full distributions')
111
+ mode_input.input(mode_default,inputs=mode_input,outputs=fields,queue=False)
112
+ examples.input(select_preset,inputs=examples,outputs=[mode_input,text,fields,image,file,result,picture,timing,summaries,details,attachments],queue=False)
113
+ for component in (text,image,mode_input,fields):
114
+ component.input(clear_output,outputs=[result,picture,timing,summaries,details],queue=False)
115
+ file.change(clear_output,outputs=[result,picture,timing,summaries,details],queue=False)
116
+ from functools import partial
117
+ runner=partial(run,infer_function=infer_function) if infer_function else run
118
+ button.click(runner,inputs=[text,image,mode_input,fields,file],outputs=[result,picture,timing,summaries,details],concurrency_limit=1,api_name='predict')
119
+ return demo.queue(max_size=8,default_concurrency_limit=1)
120
+
121
+ def light_theme():
122
+ theme=gr.themes.Base(primary_hue='teal',neutral_hue='gray',font=['Inter','system-ui','sans-serif']).set(
123
+ background_fill_primary='#ffffff', background_fill_secondary='#f8fafb',
124
+ body_background_fill='#ffffff', body_text_color='#17252b', body_text_color_subdued='#52616b',
125
+ block_background_fill='#ffffff', block_label_background_fill='#ffffff',
126
+ block_label_text_color='#52616b', block_title_text_color='#52616b',
127
+ input_background_fill='#f8fafb', input_placeholder_color='#52616b',
128
+ input_border_color='#a7b2b9', input_border_width='1px', input_border_color_focus='#147f85',
129
+ button_primary_background_fill='#147f85', button_primary_background_fill_hover='#106970',
130
+ button_primary_border_color='#147f85', button_primary_border_color_hover='#106970',
131
+ button_primary_text_color='#ffffff', button_primary_text_color_hover='#ffffff',
132
+ button_secondary_background_fill='#f0f4f5', button_secondary_background_fill_hover='#e3eaed',
133
+ button_secondary_text_color='#17252b', button_secondary_text_color_hover='#17252b',
134
+ checkbox_label_background_fill='#f0f4f5', checkbox_label_background_fill_selected='#deeff0',
135
+ checkbox_label_text_color='#17252b', checkbox_label_text_color_selected='#114e53',
136
+ checkbox_background_color_selected='#147f85', checkbox_border_color_selected='#147f85',
137
+ checkbox_border_color='#87969f', checkbox_border_color_hover='#52616b',
138
+ color_accent='#147f85', code_background_fill='#f8fafb')
139
+ # Gradio may inherit dark mode from the host page. Keep every token paired.
140
+ tokens=theme.to_dict()['theme']
141
+ return theme.set(background_fill_primary=tokens['background_fill_primary'],
142
+ background_fill_secondary=tokens['background_fill_secondary'],
143
+ **{key:tokens[key[:-5]] for key in tokens if key.endswith('_dark') and key[:-5] in tokens})
144
+
145
+ def launch_demo(infer_function=None,blocked_paths=None):
146
+ create_demo(infer_function).launch(server_name=os.getenv('GRADIO_SERVER_NAME','127.0.0.1'),server_port=int(os.getenv('PORT','7860')),
147
+ theme=light_theme(),css=CSS,js=LIGHT_JS,show_error=False,max_file_size='3mb',footer_links=[],blocked_paths=blocked_paths)
148
+
149
+ if __name__=='__main__':
150
+ launch_demo()
demo/assets/person.jpg ADDED

Git LFS Details

  • SHA256: 4f9833442873c5bbc3b403a7072d8827a71f2632a0fec4d5d799254fab3c7c0e
  • Pointer size: 131 Bytes
  • Size of remote file: 106 kB
demo/client.py ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Build typed requests and call a configured GPC-1 server."""
2
+ import base64
3
+ from io import BytesIO
4
+ import json
5
+ import math
6
+ import os
7
+ from pathlib import Path
8
+ import time
9
+ from urllib.parse import urlsplit
10
+
11
+ import httpx
12
+
13
+ MODES = ('Categories', 'Numeric ranges', 'JSON records')
14
+ MAX_TEXT_BYTES = 256 * 1024
15
+ MAX_IMAGE_BYTES = 3 * 1024 * 1024
16
+
17
+ def parse_json(value):
18
+ if not isinstance(value, str): raise ValueError('Define the output as JSON.')
19
+ def reject(value): raise ValueError('Use finite JSON numbers.')
20
+ def pairs(items):
21
+ result = {}
22
+ for key, item in items:
23
+ if key in result: raise ValueError('JSON keys must be unique.')
24
+ result[key] = item
25
+ return result
26
+ if len(value.encode()) > 64 * 1024: raise ValueError('Output definition is too large.')
27
+ try: return json.loads(value, parse_constant=reject, object_pairs_hook=pairs)
28
+ except json.JSONDecodeError as error: raise ValueError(f'Check JSON at line {error.lineno}.') from None
29
+ except RecursionError: raise ValueError('Output definition is too deeply nested.') from None
30
+
31
+ def key_name(key):
32
+ if not isinstance(key, str) or not key.strip() or key != key.strip() or len(key) > 64:
33
+ raise ValueError('Use a nonempty JSON key of up to 64 characters.')
34
+ return key
35
+
36
+ def value_type(value):
37
+ if value is None: return 'null'
38
+ if isinstance(value, bool): return 'boolean'
39
+ if isinstance(value, str): return 'string'
40
+ if isinstance(value, (int, float)) and math.isfinite(value): return 'number'
41
+ raise ValueError('Record values must be strings, finite numbers, booleans, or null.')
42
+
43
+ def enum_schema(values):
44
+ types = {value_type(x) for x in values}
45
+ if len(types) != 1: raise ValueError('Use the same value type for each key.')
46
+ unique = list({json.dumps(x, sort_keys=True): x for x in values}.values())
47
+ return {'type': types.pop(), 'enum': unique}
48
+
49
+ def build_request(context, image, mode, definition, context_file=None):
50
+ if mode not in MODES: raise ValueError('Choose an output type.')
51
+ if context is None: context = ''
52
+ if not isinstance(context, str): raise ValueError('Enter text for the context.')
53
+ if context_file:
54
+ try: path = Path(context_file)
55
+ except TypeError: raise ValueError('Upload a text file.') from None
56
+ if path.suffix.lower() not in {'.txt', '.md', '.json'}:
57
+ raise ValueError('Upload a UTF-8 text, Markdown, or JSON file.')
58
+ try:
59
+ if path.stat().st_size > MAX_TEXT_BYTES: raise ValueError('Context file exceeds 256 KiB.')
60
+ uploaded = path.read_text(encoding='utf-8')
61
+ except UnicodeError: raise ValueError('Context file must contain UTF-8 text.') from None
62
+ except OSError: raise ValueError('Cannot read the context file. Upload it again.') from None
63
+ context = context + '\n\n' + uploaded
64
+ if not context.strip(): raise ValueError('Add context or upload a text file.')
65
+ if len(context.encode()) > MAX_TEXT_BYTES: raise ValueError('Context exceeds 256 KiB. Nothing was truncated.')
66
+ definition = parse_json(definition)
67
+ properties = {}
68
+ if mode == 'Categories':
69
+ if not isinstance(definition, dict) or len(definition) != 1:
70
+ raise ValueError('Define one key with a list of choices, such as {"intent": ["sales", "support"]}.')
71
+ key, choices = next(iter(definition.items())); key_name(key)
72
+ if not isinstance(choices, list) or not 2 <= len(choices) <= 255:
73
+ raise ValueError('Supply 2 to 255 choices.')
74
+ properties[key] = enum_schema(choices)
75
+ if len(properties[key]['enum']) != len(choices): raise ValueError('Choices must be unique.')
76
+ options = dict(mode='atomic', field_key=key, choices=choices)
77
+ elif mode == 'Numeric ranges':
78
+ if not isinstance(definition, list) or not 1 <= len(definition) <= 128:
79
+ raise ValueError('Supply 1 to 128 field definitions.')
80
+ fields = []
81
+ for field in definition:
82
+ if not isinstance(field, dict) or set(field) != {'key','description','minimum','maximum','unit'}:
83
+ raise ValueError('Each field needs key, description, minimum, maximum, and unit.')
84
+ key = key_name(field['key'])
85
+ if key in properties: raise ValueError('Field keys must be unique.')
86
+ for name in ('description', 'unit'):
87
+ if not isinstance(field[name], str) or not field[name].strip() or field[name] != field[name].strip():
88
+ raise ValueError('Add a description and unit without surrounding whitespace.')
89
+ low, high = field['minimum'], field['maximum']
90
+ if any(isinstance(x, bool) or not isinstance(x,(int,float)) or not math.isfinite(x) for x in (low,high)) or low >= high:
91
+ raise ValueError('Minimum and maximum must be finite numbers, with minimum below maximum.')
92
+ fields.append(field)
93
+ properties[key] = dict(type='number', minimum=low, maximum=high)
94
+ options = dict(mode='numeric101', numeric_fields=fields)
95
+ else:
96
+ if not isinstance(definition, list) or not 2 <= len(definition) <= 32 or any(not isinstance(x,dict) or not x for x in definition):
97
+ raise ValueError('Supply 2 to 32 complete JSON records.')
98
+ if len({json.dumps(x,sort_keys=True) for x in definition}) != len(definition): raise ValueError('Records must be unique.')
99
+ keys = list(definition[0])
100
+ if any(set(x) != set(keys) for x in definition): raise ValueError('Every record must have the same keys.')
101
+ for key in keys:
102
+ key_name(key); properties[key] = enum_schema([x[key] for x in definition])
103
+ options = dict(mode='finite_joint', allowed_records=definition)
104
+ content = context
105
+ if image is not None:
106
+ if mode != 'Numeric ranges': raise ValueError('Images support Numeric ranges. Choose that type or remove the image.')
107
+ from PIL import Image
108
+ if not isinstance(image, Image.Image): raise ValueError('Upload a valid image.')
109
+ if image.width*image.height > 16777216: raise ValueError('Image exceeds 16 megapixels. Resize before uploading.')
110
+ try:
111
+ buffer = BytesIO(); image.convert('RGB').save(buffer, format='PNG')
112
+ except (OSError, ValueError): raise ValueError('Cannot read the image. Upload it again.') from None
113
+ if buffer.tell() > MAX_IMAGE_BYTES: raise ValueError('Encoded image exceeds 3 MiB. Resize before uploading.')
114
+ uri = 'data:image/png;base64,' + base64.b64encode(buffer.getvalue()).decode()
115
+ content = [dict(type='text', text=context), dict(type='image_url', image_url=dict(url=uri))]
116
+ payload = dict(model='gpc-1', messages=[dict(role='user', content=content)],
117
+ response_format=dict(type='json_schema', json_schema=dict(name='result',strict=True,
118
+ schema=dict(type='object',properties=properties,required=list(properties),additionalProperties=False))),
119
+ gpc1=options, stream=False)
120
+ if len(json.dumps(payload).encode()) > 4*1024*1024: raise ValueError('Request exceeds 4 MiB. Nothing was truncated.')
121
+ return payload
122
+
123
+ def infer(payload):
124
+ endpoint = os.getenv('GPC1_API_URL','').rstrip('/')
125
+ token = os.getenv('GPC1_API_KEY','')
126
+ parsed = urlsplit(endpoint)
127
+ if not token or not parsed.hostname or parsed.username or parsed.password or parsed.query or parsed.fragment:
128
+ raise RuntimeError('The model endpoint is not configured.')
129
+ if parsed.scheme != 'https' and not (parsed.scheme=='http' and parsed.hostname in {'localhost','127.0.0.1','::1'}):
130
+ raise RuntimeError('The model endpoint requires HTTPS.')
131
+ started = time.perf_counter()
132
+ try:
133
+ with httpx.Client(timeout=httpx.Timeout(180, connect=10), follow_redirects=False, trust_env=False) as client:
134
+ response = client.post(endpoint+'/v1/chat/completions', json=payload,
135
+ headers={'Authorization': 'Bearer '+token})
136
+ except httpx.TimeoutException: raise RuntimeError('The request timed out. It was not retried.') from None
137
+ except httpx.HTTPError: raise RuntimeError('Cannot reach the model. Try again later.') from None
138
+ if response.status_code == 429: raise RuntimeError('The model is busy. Try again shortly.')
139
+ if response.status_code == 413: raise ValueError('The request is too large. Reduce context or image size.')
140
+ if response.status_code != 200: raise RuntimeError('The model could not process this request. Check the field definitions and context length.')
141
+ try:
142
+ data=response.json(); result=json.loads(data['choices'][0]['message']['content'])
143
+ details=data['gpc1']
144
+ if not isinstance(result, dict) or not isinstance(details, dict): raise ValueError
145
+ expected=set(payload['response_format']['json_schema']['schema']['properties'])
146
+ if set(result) != expected: raise ValueError
147
+ if payload['gpc1']['mode']=='atomic':
148
+ value=json.dumps(result[payload['gpc1']['field_key']],sort_keys=True,allow_nan=False)
149
+ if value not in {json.dumps(x,sort_keys=True,allow_nan=False) for x in payload['gpc1']['choices']}: raise ValueError
150
+ elif payload['gpc1']['mode']=='finite_joint':
151
+ value=json.dumps(result,sort_keys=True,allow_nan=False)
152
+ if value not in {json.dumps(x,sort_keys=True,allow_nan=False) for x in payload['gpc1']['allowed_records']}: raise ValueError
153
+ else:
154
+ fields=details.get('fields')
155
+ if not isinstance(fields, dict) or set(fields) != expected: raise ValueError
156
+ for key, value in result.items():
157
+ field=fields[key]
158
+ if (isinstance(value,bool) or not isinstance(value,(int,float)) or not math.isfinite(value)
159
+ or not isinstance(field,dict) or any(isinstance(field.get(k),bool) or not isinstance(field.get(k),(int,float)) or not math.isfinite(field[k]) for k in ('map_value','expected_value'))):
160
+ raise ValueError
161
+ bounds=payload['response_format']['json_schema']['schema']['properties'][key]
162
+ if value != field['map_value'] or not bounds['minimum'] <= value <= bounds['maximum'] or not bounds['minimum'] <= field['expected_value'] <= bounds['maximum']:
163
+ raise ValueError
164
+ except (ValueError, KeyError, IndexError, TypeError): raise RuntimeError('The model returned an invalid response.') from None
165
+ elapsed=time.perf_counter()-started
166
+ return result, details, elapsed
demo/presets.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+
3
+ def field(key, description, minimum=0, maximum=1, unit='image fraction'):
4
+ return dict(key=key, description=description, minimum=minimum, maximum=maximum, unit=unit)
5
+
6
+ PRESETS = {
7
+ 'Support routing': ('Categories', 'I was charged twice for my subscription this month. Please refund the duplicate payment.',
8
+ {'intent': ['billing', 'technical_support', 'sales', 'cancellation']}),
9
+ 'Numerical analysis': ('Numeric ranges', 'A tank holds 200 liters. It is 35% full. Return its current volume in liters.',
10
+ [field('volume', 'Current volume of liquid in the tank', 0, 200, 'liters')]),
11
+ 'Bounding box': ('Numeric ranges', 'Locate the runner in the red shirt. Return their full visible body bounding box. Origin is top-left; x increases right and y increases down. All coordinates are normalized from 0 to 1.',
12
+ [field(k, d) for k,d in zip(['x_min','y_min','x_max','y_max'],['Left edge of runner in red','Top edge of runner in red','Right edge of runner in red','Bottom edge of runner in red'])]),
13
+ 'Human pose': ('Numeric ranges', 'Locate the runner in the red shirt\'s nose and left wrist (the runner\'s left, not the viewer\'s). Return visible landmark coordinates normalized to 0–1. Origin is top-left, x increases right and y down.',
14
+ [field(k, d) for k,d in zip(['nose_x','nose_y','left_wrist_x','left_wrist_y'],['Runner nose horizontal coordinate','Runner nose vertical coordinate','Runner left wrist horizontal coordinate','Runner left wrist vertical coordinate'])]),
15
+ 'Pixel art': ('Numeric ranges', 'Draw a recognizable flower on a 5 by 5 pixel grid. Each key is row_column, zero-indexed from top-left. Pixel codes: 0=white background, 1=green stem or leaf, 2=red petals, 3=yellow center. Use a cohesive shape with one stem and a centered blossom.',
16
+ [field(f'p{r}_{c}',f'Pixel at row {r}, column {c}',0,3,'palette index') for r in range(5) for c in range(5)]),
17
+ 'Workflow decision': ('JSON records', 'The customer reports a duplicate subscription charge and requests a refund. Select the appropriate team and action.',
18
+ [{'team':'billing','action':'review_refund'}, {'team':'support','action':'troubleshoot'}, {'team':'sales','action':'contact_customer'}]),
19
+ }
20
+
21
+ def preset(name):
22
+ mode,context,definition=PRESETS[name]
23
+ return mode, context, json.dumps(definition,indent=2,ensure_ascii=False)
demo/requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio==6.9.0
2
+ httpx==0.28.1
3
+ Pillow==12.3.0
docs/ARCHITECTURE.md ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Architecture
2
+
3
+ GPC-1 uses the Qwen3.5-35B-A3B multimodal mixture-of-experts architecture with schema-bound inference. You define the output space; the server scores it and returns typed values.
4
+
5
+ ## Model package
6
+
7
+ `model/` contains GPC-1's modified backbone weights; `adapter/` contains the matching adapter applied at load time. Use both components from the same release. Qwen identifies the upstream architecture; the packaged weights belong to GPC-1.
8
+
9
+ The server loads the backbone in BF16 and the adapter in FP32. It uses the existing language-model output projection to score the request's allowed outputs.
10
+
11
+ | Mode | Output |
12
+ | --- | --- |
13
+ | Categorical | A selected label and probabilities over your choices |
14
+ | Numeric | A grid estimate, probability-weighted mean, and distribution per field |
15
+ | Image numeric | Numeric fields conditioned on one image |
16
+ | Finite joint | A selected complete record and probabilities over allowed records |
17
+
18
+ ## Numeric ranges
19
+
20
+ For bounds `[minimum, maximum]`, the 101-point grid is:
21
+
22
+ `value(i) = minimum + (maximum - minimum) × i / 100`
23
+
24
+ Numeric fields share one model pass. Each returns a marginal distribution; shared context does not make them a full joint distribution. The probability-weighted mean can fall between grid positions. Angles and other circular quantities need application-specific interpretation.
25
+
26
+ ## Dependent records
27
+
28
+ Finite-joint mode scores complete records rather than choosing their fields independently. Probabilities are conditional on the supplied records. Compute and memory grow with the number and length of candidates.
29
+
30
+ ## Serving
31
+
32
+ The runtime verifies packaged asset identities, uses request-local execution, and rejects over-limit inputs without truncation. Its input ceiling is 256K tokens (262,144), including compiled request overhead. See [context configuration](../API.md#context-window) for deployment requirements. Preserve the supplied precision, prompt formatting, and token mapping when reproducing outputs. Scale concurrent traffic with separate replicas.
33
+
34
+ See the [API guide](../API.md) for request formats and limits.
docs/DATA_AND_LICENSES.md ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Third-party notices
2
+
3
+ The following notices preserve applicable attribution. They do not grant additional rights to third-party material.
4
+
5
+ ## Code and base model
6
+
7
+ - **GPC-1 inference code:** Apache-2.0.
8
+ - **Qwen3.5-35B-A3B:** the pinned upstream revision `59d61f3ce65a6d9863b86d2e96597125219dc754` is published under Apache-2.0. Preserve its upstream license, copyright, patent, trademark, and attribution notices.
9
+
10
+ The GPC-1 code license does not replace the terms of the base model, adapter artifacts, source material, or dependencies.
11
+
12
+ ## Material source notices
13
+
14
+ - **CLINC OOS:** CC-BY-3.0; preserve the Larson et al. source attribution and license notice.
15
+ - **UCI SMS Spam Collection:** CC-BY-4.0; Almeida, T. and Hidalgo, J. (2011), *SMS Spam Collection*, UCI Machine Learning Repository, DOI `10.24432/C5CC84`.
16
+ - **HelpSteer2:** CC-BY-4.0 at revision `990b2711a36180dd19d9c94b8627844866f8982a`.
17
+ - **Example photograph:** Liber4l, [Tim running](https://commons.wikimedia.org/wiki/File:Tim_running.jpg), public domain. The bounding-box illustration adds the model's predicted coordinates and presentation layout. No endorsement is implied.
18
+ - **Pose illustration photograph:** [Misty / sub_lime79](https://www.flickr.com/photos/16932921@N08/4668205945/), [CC BY 2.0](https://creativecommons.org/licenses/by/2.0/), with a point overlay. No endorsement is implied.
19
+ - **Intent illustration text:** [Larson et al., CLINC OOS](https://github.com/clinc/oos-eval), [CC BY 3.0](https://creativecommons.org/licenses/by/3.0/).
20
+ - **CMU motion capture:** retain CMU attribution and NSF EIA-0196217 funding acknowledgment. CMU's published permission does not permit direct resale of the data or converted data. No capture data are distributed here.
21
+ - **GLiClass logic, Typed Decisions, OpenBookQA adaptation, and TruthfulQA MC1 adaptation:** preserve their recorded Apache-2.0 notices; retain the OpenBookQA transport repository's MIT notice.
22
+ - **Google Civil Comments and public Open-Jev:** CC0-1.0. Open-Jev revision: `c67699e13d0ae25e35b77165a4b6b079bedc8aba`.
23
+ - **CFPB Consumer Complaint Database:** official public-use source. This project does not recast that public-use statement as an SPDX license.
24
+ - **U.S. Census Bureau:** 2024 American Community Survey 5-year Summary File, tract estimates. U.S. federal government public-domain material; source credit retained.
25
+ - **Material UI:** MIT, copyright (c) 2014 Call-Em-All. The notice below applies to the upstream component declarations.
26
+ - **PushT:** `lerobot/pusht`, revision `7628202a2180972f291ba1bc6723834921e72c19`, published under MIT. No source images or action records are distributed here.
27
+
28
+ Source-specific rights and restrictions remain applicable independently of the GPC-1 code license.
29
+
30
+ ## Material UI license
31
+
32
+ Copyright (c) 2014 Call-Em-All
33
+
34
+ Permission is hereby granted, free of charge, to any person obtaining a copy
35
+ of this software and associated documentation files (the "Software"), to deal
36
+ in the Software without restriction, including without limitation the rights
37
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
38
+ copies of the Software, and to permit persons to whom the Software is
39
+ furnished to do so, subject to the following conditions:
40
+
41
+ The above copyright notice and this permission notice shall be included in
42
+ all copies or substantial portions of the Software.
43
+
44
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
45
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
46
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
47
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
48
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
49
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
50
+ THE SOFTWARE.
docs/DOWNLOADS.md ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Download GPC-1
2
+
3
+ Download the complete GPC-1 release: the GPC-1 backbone in `model/`, its matching adapter in `adapter/`, and the serving package. Keep both weight components and release metadata together; the server loads them automatically.
4
+
5
+ ## Command line
6
+
7
+ ```bash
8
+ python -m pip install "huggingface-hub>=1.7,<2"
9
+ hf download harshatheg/GPC-1 --local-dir gpc-1
10
+ python gpc-1/download.py verify gpc-1
11
+ ```
12
+
13
+ For a reproducible deployment, pin a commit SHA and use the same revision in every environment:
14
+
15
+ ```bash
16
+ hf download harshatheg/GPC-1 --revision YOUR_COMMIT_SHA --local-dir gpc-1
17
+ python gpc-1/download.py verify gpc-1
18
+ ```
19
+
20
+ Private or gated access uses `HF_TOKEN` in the environment. Keep the token in your secret manager; do not put it in command arguments or logs.
21
+
22
+ ## Python
23
+
24
+ If you already have `download.py` from the release package, run Python from its directory:
25
+
26
+ ```python
27
+ from pathlib import Path
28
+ from download import fetch
29
+
30
+ package = fetch(
31
+ "harshatheg/GPC-1",
32
+ revision="YOUR_COMMIT_SHA",
33
+ local_dir=Path("gpc-1"),
34
+ cache_dir=None,
35
+ offline=False,
36
+ )
37
+ print(package["model"])
38
+ ```
39
+
40
+ `fetch` uses `huggingface_hub.snapshot_download` for the complete repository. Verification checks that the root and packaged base configs match, all inventoried base files and indexed weight shards are present, and the runtime manifest, base metadata, and adapter hashes match. It does not hash every base weight shard; the server checks those hashes when it loads the model. No tracking endpoint is called.
41
+
42
+ ## Cache and offline use
43
+
44
+ After an online download, verify a copied package without contacting the Hub:
45
+
46
+ ```bash
47
+ python gpc-1/download.py verify gpc-1
48
+ ```
49
+
50
+ For offline deployment, download the complete package with `--local-dir` while online, then copy that directory to the offline machine. Run the verification command above on the copied directory. Verification makes no Hub request. Use the materialized package directory, not a cache snapshot containing symlinks.
51
+
52
+ ## CI
53
+
54
+ Pin the revision in your pipeline and keep the token in a CI secret. A complete package can then be mounted read-only by the serving process:
55
+
56
+ ```bash
57
+ hf download harshatheg/GPC-1 --revision "$GPC1_REVISION" --local-dir gpc-1
58
+ python gpc-1/download.py verify gpc-1
59
+ export GPC1_MODEL_PATH="$PWD/gpc-1/model"
60
+ ```
docs/HUB_METRICS.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hub metrics for maintainers
2
+
3
+ Read the repository's download counts without fetching model weights:
4
+
5
+ ```bash
6
+ python gpc-1/download.py counts
7
+ ```
8
+
9
+ The command reads Hub metadata and reports `downloads` and, when available, `downloads_all_time`. It sends no tracking event.
10
+
11
+ [Hugging Face counts requests for query files](https://huggingface.co/docs/hub/models-download-stats); `config.json` is the default query file. Complete downloads through the CLI, Python, or CI normally request it. Counts are not unique users or verified installations. Offline copies and weights-only transfers may not be counted.
download.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Fetch and verify the complete GPC-1 release with the standard Hub client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import hashlib
7
+ import json
8
+ import os
9
+ import re
10
+ import sys
11
+ from pathlib import Path, PurePosixPath
12
+
13
+ DEFAULT_REPO = "harshatheg/GPC-1"
14
+ SHA256 = re.compile(r"^[0-9a-f]{64}$")
15
+
16
+
17
+ def _release_path(root: Path, value: object, *, directory: bool) -> Path:
18
+ if not isinstance(value, str) or not value:
19
+ raise ValueError("Release metadata contains an empty path")
20
+ if "\\" in value or any(part in ("", ".", "..") for part in value.split("/")):
21
+ raise ValueError(f"Invalid release path: {value}")
22
+ relative = PurePosixPath(value)
23
+ if relative.is_absolute():
24
+ raise ValueError(f"Invalid release path: {value}")
25
+ path = root.joinpath(*relative.parts)
26
+ if not path.resolve().is_relative_to(root.resolve()):
27
+ raise ValueError(f"Release path escapes package: {value}")
28
+ if directory and not path.is_dir():
29
+ raise ValueError(f"Missing release directory: {value}")
30
+ if not directory and not path.is_file():
31
+ raise ValueError(f"Missing release file: {value}")
32
+ return path
33
+
34
+
35
+ def _sha256_file(path: Path) -> str:
36
+ digest = hashlib.sha256()
37
+ with path.open("rb") as handle:
38
+ for chunk in iter(lambda: handle.read(1024 * 1024), b""):
39
+ digest.update(chunk)
40
+ return digest.hexdigest()
41
+
42
+
43
+ def verify_package(root: Path) -> dict[str, str]:
44
+ """Validate metadata, adapter hashes, and the complete listed base-file set."""
45
+ root = root.resolve()
46
+ config_file = root / "config.json"
47
+ if not config_file.is_file() or config_file.is_symlink():
48
+ raise ValueError("Missing root config.json; fetch the full model repository")
49
+ config = json.loads(config_file.read_text(encoding="utf-8"))
50
+ if not isinstance(config, dict) or not config.get("model_type"):
51
+ raise ValueError("Root config.json is not a model config")
52
+ release = config.get("gpc1_release")
53
+ if not isinstance(release, dict) or release.get("format_version") != 1:
54
+ raise ValueError("Root config.json lacks supported gpc1_release metadata")
55
+ model = _release_path(root, release.get("model_path"), directory=True)
56
+ adapter = _release_path(root, release.get("adapter_path"), directory=True)
57
+ manifest_file = _release_path(root, release.get("runtime_manifest"), directory=False)
58
+ if model == adapter or model == root or adapter == root:
59
+ raise ValueError("Release model and adapter paths must be distinct package directories")
60
+ expected = release.get("runtime_sha256")
61
+ if not isinstance(expected, str) or not SHA256.fullmatch(expected):
62
+ raise ValueError("Invalid runtime_sha256 in release metadata")
63
+ actual = _sha256_file(manifest_file)
64
+ if actual != expected:
65
+ raise ValueError("Runtime manifest checksum mismatch")
66
+ model_config_file = model / "config.json"
67
+ if not model_config_file.is_file():
68
+ raise ValueError("Missing model/config.json")
69
+ model_config = json.loads(model_config_file.read_text(encoding="utf-8"))
70
+ root_base_config = dict(config)
71
+ root_base_config.pop("gpc1_release")
72
+ if root_base_config != model_config:
73
+ raise ValueError("Root model config does not match packaged base config")
74
+ manifest = json.loads(manifest_file.read_text(encoding="utf-8"))
75
+ if not isinstance(manifest, dict) or manifest.get("served_model_id") != "gpc-1":
76
+ raise ValueError("Runtime manifest is not for GPC-1")
77
+ assets = manifest.get("assets", {})
78
+ inventory_file = _release_path(root, assets.get("base_files"), directory=False)
79
+ if _sha256_file(inventory_file) != assets.get("base_files_sha256"):
80
+ raise ValueError("Base-file inventory checksum mismatch")
81
+ inventory = json.loads(inventory_file.read_text(encoding="utf-8"))
82
+ if (inventory.get("model_id") != manifest.get("base", {}).get("model_id")
83
+ or inventory.get("revision") != manifest.get("base", {}).get("revision")):
84
+ raise ValueError("Base-file inventory identity mismatch")
85
+ files = inventory.get("files")
86
+ if not isinstance(files, dict) or not files:
87
+ raise ValueError("Base-file inventory is empty")
88
+ for name, digest in files.items():
89
+ if not isinstance(digest, str) or not SHA256.fullmatch(digest):
90
+ raise ValueError("Invalid base-file inventory digest")
91
+ path = _release_path(model, name, directory=False)
92
+ if path.stat().st_size == 0:
93
+ raise ValueError(f"Empty base file: {name}")
94
+ if name in ("config.json", "model.safetensors.index.json") and _sha256_file(path) != digest:
95
+ raise ValueError(f"Base file checksum mismatch: {name}")
96
+ index_file = model / "model.safetensors.index.json"
97
+ if "model.safetensors.index.json" not in files or not index_file.is_file():
98
+ raise ValueError("Missing model shard index")
99
+ index = json.loads(index_file.read_text(encoding="utf-8"))
100
+ shards = set(index.get("weight_map", {}).values())
101
+ if not shards or not shards.issubset(files.keys()) or any(not isinstance(s, str) or not s.endswith(".safetensors") for s in shards):
102
+ raise ValueError("Model shard index does not match base-file inventory")
103
+ adapter_manifest = manifest.get("adapter", {})
104
+ for name, key in (("adapter_config.json", "adapter_config_sha256"),
105
+ ("adapter_model.safetensors", "adapter_model_sha256")):
106
+ path = _release_path(adapter, name, directory=False)
107
+ if _sha256_file(path) != adapter_manifest.get(key):
108
+ raise ValueError(f"Adapter file checksum mismatch: {name}")
109
+ return {"root": str(root), "model": str(model), "adapter": str(adapter), "runtime_sha256": actual}
110
+
111
+
112
+ def fetch(repo: str, revision: str | None, local_dir: Path | None, cache_dir: Path | None, offline: bool) -> dict[str, str]:
113
+ try:
114
+ from huggingface_hub import snapshot_download
115
+ except ImportError as exc:
116
+ raise RuntimeError("Install huggingface-hub to fetch the package") from exc
117
+
118
+ try:
119
+ path = snapshot_download(
120
+ repo_id=repo,
121
+ repo_type="model",
122
+ revision=revision,
123
+ local_dir=str(local_dir) if local_dir else None,
124
+ cache_dir=str(cache_dir) if cache_dir else None,
125
+ local_files_only=offline,
126
+ token=os.environ.get("HF_TOKEN") or None,
127
+ )
128
+ except Exception:
129
+ raise RuntimeError("Hub fetch failed; check access, revision, cache, and connectivity") from None
130
+ return verify_package(Path(path))
131
+
132
+
133
+ def counts(repo: str) -> dict[str, object]:
134
+ """Read a Hub model-info snapshot; never fetch query files for analytics."""
135
+ try:
136
+ from huggingface_hub import HfApi
137
+ except ImportError as exc:
138
+ raise RuntimeError("Install huggingface-hub to read counts") from exc
139
+
140
+ try:
141
+ info = HfApi(token=os.environ.get("HF_TOKEN") or None).model_info(
142
+ repo_id=repo, expand=["downloads", "downloadsAllTime"]
143
+ )
144
+ except Exception:
145
+ raise RuntimeError("Hub model-info request failed; check access and connectivity") from None
146
+ return {
147
+ "repo": repo,
148
+ "downloads_30d": getattr(info, "downloads", None),
149
+ "downloads_all_time": getattr(info, "downloads_all_time", None),
150
+ }
151
+
152
+
153
+ def main(argv: list[str] | None = None) -> int:
154
+ parser = argparse.ArgumentParser(description=__doc__)
155
+ sub = parser.add_subparsers(dest="command", required=True)
156
+ fetch_parser = sub.add_parser("fetch", help="Download the full model package and verify its release metadata")
157
+ fetch_parser.add_argument("--repo", default=DEFAULT_REPO)
158
+ fetch_parser.add_argument("--revision", help="Pin a tag or, preferably, a commit SHA")
159
+ fetch_parser.add_argument("--local-dir", type=Path)
160
+ fetch_parser.add_argument("--cache-dir", type=Path)
161
+ fetch_parser.add_argument("--offline", action="store_true", help="Use cached files only; no Hub request")
162
+ verify_parser = sub.add_parser("verify", help="Verify a package already on disk without network access")
163
+ verify_parser.add_argument("path", type=Path)
164
+ count_parser = sub.add_parser("counts", help="Read current Hub download counts")
165
+ count_parser.add_argument("--repo", default=DEFAULT_REPO)
166
+ args = parser.parse_args(argv)
167
+ try:
168
+ if args.command == "fetch":
169
+ result = fetch(args.repo, args.revision, args.local_dir, args.cache_dir, args.offline)
170
+ elif args.command == "verify":
171
+ result = verify_package(args.path)
172
+ else:
173
+ result = counts(args.repo)
174
+ except (OSError, ValueError, RuntimeError) as exc:
175
+ parser.exit(1, f"{args.command}: {exc}\n")
176
+ print(json.dumps(result, sort_keys=True))
177
+ return 0
178
+
179
+
180
+ if __name__ == "__main__":
181
+ sys.exit(main())
examples/atomic_request.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"model":"gpc-1","messages":[{"role":"user","content":"Is the invoice approved? The record says approved."}],"response_format":{"type":"json_schema","json_schema":{"name":"decision","strict":true,"schema":{"type":"object","properties":{"label":{"type":"string","enum":["approved","rejected"]}},"required":["label"],"additionalProperties":false}}},"gpc1":{"mode":"atomic","field_key":"label","choices":["approved","rejected"]},"stream":false}
examples/numeric_request.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "gpc-1",
3
+ "messages": [{"role": "user", "content": "A 200 cm rail is filled to 75%. Report filled length."}],
4
+ "response_format": {"type": "json_schema", "json_schema": {"name": "rail", "strict": true, "schema": {"type": "object", "properties": {"filled_length": {"type": "number", "minimum": 0, "maximum": 200}}, "required": ["filled_length"], "additionalProperties": false}}},
5
+ "gpc1": {"mode": "numeric101", "numeric_fields": [{"key": "filled_length", "description": "filled rail length", "minimum": 0, "maximum": 200, "unit": "cm"}]},
6
+ "stream": false
7
+ }
gpc1_server/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ """GPC-1 inference-only OpenAI-style serving package."""
2
+
3
+ __version__ = "0.1.0"
gpc1_server/api.py ADDED
@@ -0,0 +1,315 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import base64
5
+ import binascii
6
+ from contextlib import asynccontextmanager
7
+ from io import BytesIO
8
+ import json
9
+ import math
10
+ import os
11
+ import secrets
12
+ import time
13
+ import uuid
14
+ from typing import Any
15
+
16
+ from fastapi import FastAPI, Header, Request
17
+ from fastapi.responses import JSONResponse
18
+ from PIL import Image
19
+ from pydantic import ValidationError
20
+ from jsonschema import Draft202012Validator
21
+
22
+ from .compiler import NumericField
23
+ from .errors import APIError
24
+ from .runtime import Backend, TransformersBackend
25
+ from .schemas import ChatRequest
26
+
27
+ MAX_BODY_BYTES = int(os.getenv("GPC1_MAX_BODY_BYTES", str(16 * 1024 * 1024)))
28
+ MAX_IMAGE_BYTES = int(os.getenv("GPC1_MAX_IMAGE_BYTES", str(3 * 1024 * 1024)))
29
+ MAX_IMAGE_PIXELS = int(os.getenv("GPC1_MAX_IMAGE_PIXELS", "16777216"))
30
+ Image.MAX_IMAGE_PIXELS = MAX_IMAGE_PIXELS
31
+ MAX_QUEUE_WAIT_SECONDS = float(os.getenv("GPC1_MAX_QUEUE_WAIT_SECONDS", "2"))
32
+ SUPPORTED_SCHEMA_KEYS = {"type", "properties", "required", "additionalProperties", "description",
33
+ "enum", "items", "minimum", "maximum"}
34
+
35
+
36
+ def error_response(error: APIError, request_id: str | None = None) -> JSONResponse:
37
+ headers = {"x-request-id": request_id} if request_id else {}
38
+ if error.status_code == 429:
39
+ headers["Retry-After"] = "1"
40
+ return JSONResponse(error.body(), status_code=error.status_code, headers=headers)
41
+
42
+
43
+ def validate_schema(schema: Any, path: str = "response_format.json_schema.schema", depth: int = 0) -> None:
44
+ if depth > 16: raise APIError("JSON Schema nesting exceeds 16",param=path)
45
+ if not isinstance(schema, dict):
46
+ raise APIError("JSON Schema must be an object", param=path)
47
+ unknown = set(schema) - SUPPORTED_SCHEMA_KEYS
48
+ if unknown:
49
+ raise APIError(f"unsupported JSON Schema keywords: {sorted(unknown)}", param=path)
50
+ def finite(value: Any) -> bool:
51
+ if isinstance(value,float): return math.isfinite(value)
52
+ if isinstance(value,dict): return all(finite(k) and finite(v) for k,v in value.items())
53
+ if isinstance(value,list): return all(finite(v) for v in value)
54
+ return True
55
+ if not finite(schema):
56
+ raise APIError("JSON Schema contains a non-finite number", param=path)
57
+ kind = schema.get("type")
58
+ if not isinstance(kind, str) or kind not in {"object", "array", "string", "number", "integer", "boolean", "null"}:
59
+ raise APIError("schema type must be one supported explicit type", param=f"{path}.type")
60
+ if kind == "object":
61
+ properties = schema.get("properties")
62
+ required = schema.get("required")
63
+ if not isinstance(properties, dict) or not isinstance(required, list):
64
+ raise APIError("objects require properties and required", param=path)
65
+ if any(not isinstance(v, str) for v in required) or len(required) != len(set(required)):
66
+ raise APIError("required must contain unique property-name strings", param=f"{path}.required")
67
+ if set(required) != set(properties) or schema.get("additionalProperties") is not False:
68
+ raise APIError("strict objects require every property and additionalProperties=false", param=path)
69
+ for name, child in properties.items(): validate_schema(child, f"{path}.properties.{name}",depth+1)
70
+ if kind == "array":
71
+ validate_schema(schema.get("items"), f"{path}.items",depth+1)
72
+
73
+
74
+ def validate_instance(value: Any, schema: dict[str, Any], path: str = "output") -> None:
75
+ def finite(v: Any) -> bool:
76
+ if isinstance(v, float): return math.isfinite(v)
77
+ if isinstance(v, dict): return all(finite(k) and finite(x) for k, x in v.items())
78
+ if isinstance(v, list): return all(finite(x) for x in v)
79
+ return True
80
+ if not finite(value): raise APIError(f"{path} contains a non-finite number", param="gpc1")
81
+ errors=sorted(Draft202012Validator(schema).iter_errors(value),key=lambda e:list(e.path))
82
+ if errors: raise APIError(f"{path} violates schema: {errors[0].message}",param="gpc1")
83
+
84
+
85
+ def _decode_image(url: str) -> Image.Image:
86
+ if not url.startswith("data:image/"):
87
+ raise APIError("only inline data:image URIs are accepted; remote URLs are disabled",
88
+ param="messages.content.image_url.url", code="remote_image_disabled")
89
+ try:
90
+ header, encoded = url.split(",", 1)
91
+ media = header[5:].split(";", 1)[0].lower()
92
+ if header != f"data:{media};base64" or media not in {"image/png", "image/jpeg", "image/webp"}:
93
+ raise ValueError
94
+ raw = base64.b64decode(encoded, validate=True)
95
+ except (ValueError, binascii.Error) as error:
96
+ raise APIError("invalid base64 image data URI", param="messages.content.image_url.url") from error
97
+ if len(raw) > MAX_IMAGE_BYTES:
98
+ raise APIError("decoded image exceeds byte limit", status_code=413, code="image_too_large")
99
+ try:
100
+ image = Image.open(BytesIO(raw))
101
+ if image.width * image.height > MAX_IMAGE_PIXELS:
102
+ raise APIError("decoded image exceeds pixel limit", status_code=413, code="image_too_large")
103
+ image.verify()
104
+ image = Image.open(BytesIO(raw)).convert("RGB")
105
+ except APIError:
106
+ raise
107
+ except Exception as error:
108
+ raise APIError("image payload failed safe decode", param="messages.content.image_url.url") from error
109
+ return image
110
+
111
+
112
+ def flatten_messages(messages: list[Any]) -> tuple[str, Image.Image | None]:
113
+ texts=[]; image=None
114
+ for message in messages:
115
+ if isinstance(message.content, str):
116
+ texts.append(f"{message.role.upper()}: {message.content}")
117
+ continue
118
+ for part in message.content:
119
+ if set(part) == {"type", "text"} and part["type"] == "text" and isinstance(part["text"], str):
120
+ texts.append(f"{message.role.upper()}: {part['text']}")
121
+ elif set(part) == {"type", "image_url"} and part["type"] == "image_url" \
122
+ and isinstance(part["image_url"], dict) and set(part["image_url"]) <= {"url", "detail"}:
123
+ if image is not None:
124
+ raise APIError("at most one image is supported", param="messages")
125
+ image = _decode_image(str(part["image_url"].get("url", "")))
126
+ else:
127
+ raise APIError("unsupported message content part", param="messages.content")
128
+ context="\n".join(texts)
129
+ if not context.strip(): raise APIError("messages must include text context", param="messages")
130
+ return context, image
131
+
132
+
133
+ def create_app(backend: Backend | None = None) -> FastAPI:
134
+ selected = backend
135
+ if int(os.getenv("GPC1_MAX_CONCURRENCY", "1")) != 1:
136
+ raise RuntimeError("canonical request-local runtime requires GPC1_MAX_CONCURRENCY=1")
137
+ max_pending = int(os.getenv("GPC1_MAX_PENDING_REQUESTS", "4"))
138
+ if max_pending < 0:
139
+ raise RuntimeError("GPC1_MAX_PENDING_REQUESTS must be nonnegative")
140
+ if not math.isfinite(MAX_QUEUE_WAIT_SECONDS) or MAX_QUEUE_WAIT_SECONDS <= 0:
141
+ raise RuntimeError("GPC1_MAX_QUEUE_WAIT_SECONDS must be finite and positive")
142
+ semaphore = asyncio.Semaphore(1)
143
+ admitted = 0
144
+
145
+ @asynccontextmanager
146
+ async def lifespan(app: FastAPI):
147
+ nonlocal selected
148
+ if selected is None:
149
+ if not os.getenv("GPC1_API_KEY"): raise RuntimeError("GPC1_API_KEY is required")
150
+ selected = TransformersBackend()
151
+ yield
152
+
153
+ app = FastAPI(title="GPC-1 OpenAI-style API", version="0.1.0", lifespan=lifespan)
154
+
155
+ async def run_native(call, *args):
156
+ task=asyncio.create_task(asyncio.to_thread(call,*args))
157
+ try: return await asyncio.shield(task)
158
+ except asyncio.CancelledError:
159
+ # Retain the model lease until the worker thread finishes.
160
+ while not task.done():
161
+ try:
162
+ await asyncio.shield(task)
163
+ except asyncio.CancelledError:
164
+ continue
165
+ except Exception:
166
+ break
167
+ if not task.cancelled():
168
+ task.exception()
169
+ raise
170
+
171
+ @app.exception_handler(APIError)
172
+ async def api_error_handler(_: Request, error: APIError): return error_response(error)
173
+
174
+ @app.middleware("http")
175
+ async def admission(request: Request, call_next):
176
+ request_id = "req_" + uuid.uuid4().hex
177
+ if request.method == "POST" and "content-length" not in request.headers:
178
+ return error_response(APIError("Content-Length is required", 411, code="length_required"), request_id)
179
+ try: content_length = int(request.headers.get("content-length", "0") or 0)
180
+ except ValueError: return error_response(APIError("invalid Content-Length", 400), request_id)
181
+ if content_length < 0 or content_length > MAX_BODY_BYTES:
182
+ return error_response(APIError("request body exceeds byte limit", 413, code="request_too_large"), request_id)
183
+ configured = os.getenv("GPC1_API_KEY")
184
+ supplied = request.headers.get("authorization", "")
185
+ if request.url.path not in {"/healthz", "/readyz"} and configured and not secrets.compare_digest(supplied, f"Bearer {configured}"):
186
+ return error_response(APIError("invalid API key", 401, "authentication_error", code="invalid_api_key"), request_id)
187
+ response = await call_next(request); response.headers["x-request-id"] = request_id; return response
188
+
189
+ @app.get("/healthz", include_in_schema=False)
190
+ async def health():
191
+ return {"status": "ok"}
192
+
193
+ @app.get("/readyz", include_in_schema=False)
194
+ async def ready():
195
+ if selected is None:
196
+ return JSONResponse({"status": "not_ready"}, status_code=503)
197
+ return {"status": "ready"}
198
+
199
+ @app.get("/v1/models")
200
+ async def models():
201
+ assert selected is not None
202
+ return {"object": "list", "data": [{"id": selected.model_id, "object": "model",
203
+ "created": 0, "owned_by": "gpc-1"}]}
204
+
205
+ @app.post("/v1/chat/completions")
206
+ async def completions(request: Request):
207
+ nonlocal admitted
208
+ if admitted >= 1 + max_pending:
209
+ raise APIError("server is at capacity; retry with backoff", 429, "rate_limit_error", code="server_busy")
210
+ admitted += 1
211
+ try:
212
+ try:
213
+ await asyncio.wait_for(semaphore.acquire(), timeout=MAX_QUEUE_WAIT_SECONDS)
214
+ except TimeoutError as error:
215
+ raise APIError("server is at capacity; retry with backoff", 429, "rate_limit_error", code="server_busy") from error
216
+ try:
217
+ return await complete_admitted(request)
218
+ finally:
219
+ semaphore.release()
220
+ finally:
221
+ admitted -= 1
222
+
223
+ async def complete_admitted(request: Request):
224
+ assert selected is not None
225
+ try:
226
+ chunks=[]; actual=0
227
+ async for chunk in request.stream():
228
+ actual += len(chunk)
229
+ if actual > MAX_BODY_BYTES: raise APIError("request body exceeds byte limit",413,code="request_too_large")
230
+ chunks.append(chunk)
231
+ raw=await asyncio.to_thread(json.loads, b"".join(chunks),
232
+ parse_constant=lambda value: (_ for _ in ()).throw(
233
+ ValueError("JSON contains a non-finite number; use finite numeric values")))
234
+ parsed = await asyncio.to_thread(ChatRequest.model_validate, raw)
235
+ except (json.JSONDecodeError, UnicodeDecodeError, ValueError, ValidationError) as error:
236
+ raise APIError(str(error), param="body") from error
237
+ if parsed.model != selected.model_id:
238
+ raise APIError(f"model {parsed.model!r} not found", 404, "invalid_request_error", "model", "model_not_found")
239
+ wrapper = parsed.response_format.json_schema
240
+ if wrapper.get("strict") is not True or not isinstance(wrapper.get("name"), str) or set(wrapper) != {"name", "strict", "schema"}:
241
+ raise APIError("json_schema requires exactly name, strict=true, and schema", param="response_format.json_schema")
242
+ await asyncio.to_thread(validate_schema, wrapper["schema"])
243
+ try: await asyncio.to_thread(Draft202012Validator.check_schema, wrapper["schema"])
244
+ except Exception as error: raise APIError(f"invalid JSON Schema: {error}",param="response_format.json_schema.schema") from error
245
+ context, image = await asyncio.to_thread(flatten_messages, parsed.messages)
246
+ if parsed.temperature is not None or parsed.max_completion_tokens is not None:
247
+ raise APIError("temperature and max_completion_tokens are unsupported; omit them",param="temperature")
248
+ try:
249
+ if parsed.gpc1.mode == "atomic":
250
+ if image is not None: raise APIError("atomic image mode is not supported",param="messages")
251
+ choices=parsed.gpc1.choices or []
252
+ try:
253
+ for choice in choices: json.dumps(choice,allow_nan=False)
254
+ except (TypeError,ValueError) as error: raise APIError("atomic choices must be finite JSON",param="gpc1.choices") from error
255
+ field_key=str(parsed.gpc1.field_key)
256
+ schema=wrapper["schema"]
257
+ properties=schema.get("properties",{})
258
+ if set(properties)!={field_key} or properties[field_key].get("enum") != choices:
259
+ raise APIError("atomic field and choices must exactly match the schema enum",param="gpc1")
260
+ result=await run_native(selected.atomic,context,field_key,choices)
261
+ value={field_key:result["value"]}
262
+ elif parsed.gpc1.mode == "numeric101":
263
+ raw_fields = parsed.gpc1.numeric_fields or []
264
+ if not 1 <= len(raw_fields) <= 512:
265
+ raise APIError("numeric101 requires 1..512 fields", param="gpc1.numeric_fields")
266
+ expected_field_keys = {"key", "description", "minimum", "maximum", "unit"}
267
+ optional_field_keys = {"aliases", "reference"}
268
+ if any(not expected_field_keys <= set(v) or set(v) - expected_field_keys - optional_field_keys for v in raw_fields):
269
+ raise APIError("numeric field keys are invalid", param="gpc1.numeric_fields")
270
+ fields = [NumericField(
271
+ key=str(v["key"]), description=str(v["description"]),
272
+ minimum=float(v["minimum"]), maximum=float(v["maximum"]), unit=str(v["unit"]),
273
+ aliases=tuple(map(str, v.get("aliases", []))), reference=str(v.get("reference", "declared_by_caller")))
274
+ for v in raw_fields]
275
+ if len({field.key for field in fields}) != len(fields):
276
+ raise APIError("numeric field keys must be unique", param="gpc1.numeric_fields")
277
+ names=[name for field in fields for name in (field.key,*field.aliases)]
278
+ if len(names) != len(set(names)):
279
+ raise APIError("numeric canonical keys and aliases must be globally unique",param="gpc1.numeric_fields")
280
+ properties = wrapper["schema"].get("properties", {})
281
+ if set(properties) != {field.key for field in fields} or any(
282
+ properties[f.key].get("type") != "number"
283
+ or properties[f.key].get("minimum") != f.minimum
284
+ or properties[f.key].get("maximum") != f.maximum for f in fields):
285
+ raise APIError("numeric fields and bounds must exactly match number properties", param="gpc1.numeric_fields")
286
+ result = await run_native(selected.numeric, context, fields, image)
287
+ value = {key: field["value"] for key, field in result["fields"].items()}
288
+ else:
289
+ if image is not None: raise APIError("finite_joint image mode is not supported", param="messages")
290
+ records = parsed.gpc1.allowed_records or []
291
+ if not 2 <= len(records) <= 255: raise APIError("finite_joint requires 2..255 allowed records", param="gpc1.allowed_records")
292
+ try:
293
+ for record in records: json.dumps(record,allow_nan=False)
294
+ except (TypeError,ValueError) as error: raise APIError("allowed records must be finite JSON",param="gpc1.allowed_records") from error
295
+ for index, record in enumerate(records):
296
+ await asyncio.to_thread(validate_instance, record, wrapper["schema"], f"allowed_records[{index}]")
297
+ encoded = [json.dumps(v, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False) for v in records]
298
+ if len(encoded) != len(set(encoded)): raise APIError("allowed records must be unique", param="gpc1.allowed_records")
299
+ result = await run_native(selected.finite_joint, context, wrapper["schema"], records)
300
+ value = result["value"]
301
+ await asyncio.to_thread(validate_instance, value, wrapper["schema"])
302
+ except APIError: raise
303
+ except (KeyError, TypeError, ValueError) as error:
304
+ raise APIError(str(error), param="gpc1") from error
305
+ content = json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
306
+ now = int(time.time()); completion_id = "chatcmpl_" + uuid.uuid4().hex
307
+ return {"id": completion_id, "object": "chat.completion", "created": now,
308
+ "model": selected.model_id, "choices": [{"index": 0,
309
+ "message": {"role": "assistant", "content": content}, "finish_reason": "stop", "logprobs": None}],
310
+ "usage": None,
311
+ "gpc1": result}
312
+ return app
313
+
314
+
315
+ app = create_app()
gpc1_server/assets.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import json
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ PACKAGE_ROOT = Path(__file__).resolve().parents[1]
9
+ ASSET_ROOT = PACKAGE_ROOT / "assets"
10
+
11
+
12
+ def sha256_file(path: Path) -> str:
13
+ digest = hashlib.sha256()
14
+ with path.open("rb") as handle:
15
+ for chunk in iter(lambda: handle.read(1024 * 1024), b""):
16
+ digest.update(chunk)
17
+ return digest.hexdigest()
18
+
19
+
20
+ def load_runtime_manifest() -> dict[str, Any]:
21
+ manifest = json.loads((ASSET_ROOT / "runtime_manifest.json").read_text())
22
+ codebook = ASSET_ROOT / "codebook255.json"
23
+ if sha256_file(codebook) != manifest["assets"]["codebook255_sha256"]:
24
+ raise RuntimeError("frozen codebook hash mismatch")
25
+ base_files = ASSET_ROOT / "base_files.sha256.json"
26
+ if sha256_file(base_files) != manifest["assets"]["base_files_sha256"]:
27
+ raise RuntimeError("frozen base-file manifest hash mismatch")
28
+ return manifest
29
+
30
+
31
+ def load_codebook() -> tuple[dict[str, Any], ...]:
32
+ manifest = load_runtime_manifest()
33
+ rows = json.loads((ASSET_ROOT / "codebook255.json").read_text())
34
+ if len(rows) != 255 or [r["position"] for r in rows] != list(range(255)):
35
+ raise RuntimeError("frozen codebook must contain positions 0..254")
36
+ if len({r["code"] for r in rows}) != 255 or len({r["token_id"] for r in rows}) != 255:
37
+ raise RuntimeError("frozen codebook codes/token IDs are not unique")
38
+ return tuple(rows)
gpc1_server/bootstrap.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Verify or copy the exact model bundled with this release."""
2
+ from __future__ import annotations
3
+ import argparse
4
+ import json
5
+ from pathlib import Path
6
+ import shutil
7
+ from .assets import ASSET_ROOT,sha256_file
8
+
9
+ def main():
10
+ parser=argparse.ArgumentParser();parser.add_argument('--output',type=Path,required=True)
11
+ args=parser.parse_args();source=ASSET_ROOT.parent/'model'
12
+ inventory=json.loads((ASSET_ROOT/'base_files.sha256.json').read_text())
13
+ if args.output.resolve()!=source.resolve() and args.output.exists() and any(args.output.iterdir()):
14
+ raise RuntimeError('--output must be absent or empty')
15
+ for name,digest in inventory['files'].items():
16
+ if not (source/name).is_file() or sha256_file(source/name)!=digest:
17
+ raise RuntimeError(f'Bundled model identity mismatch: {name}')
18
+ if args.output.resolve()!=source.resolve():
19
+ args.output.mkdir(parents=True,exist_ok=True)
20
+ for name in inventory['files']:shutil.copyfile(source/name,args.output/name)
21
+ print(args.output.resolve())
22
+
23
+ if __name__=='__main__':main()
gpc1_server/compiler.py ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Compile numeric requests and decode range predictions."""
2
+ from __future__ import annotations
3
+
4
+ from dataclasses import dataclass
5
+ import json
6
+ import math
7
+ from typing import Any, Sequence
8
+
9
+
10
+ @dataclass(frozen=True, slots=True)
11
+ class NumericField:
12
+ key: str
13
+ description: str
14
+ minimum: float
15
+ maximum: float
16
+ unit: str
17
+ aliases: tuple[str, ...] = ()
18
+ reference: str = "declared_by_caller"
19
+
20
+ def __post_init__(self) -> None:
21
+ values=(self.key,self.description,self.unit,self.reference,*self.aliases)
22
+ if any(not isinstance(v,str) or not v.strip() or v != v.strip() for v in values):
23
+ raise ValueError("numeric names and descriptions must be nonempty and edge-whitespace-free")
24
+ if self.key in self.aliases or len(self.aliases) != len(set(self.aliases)):
25
+ raise ValueError("numeric aliases must be unique and exclude the canonical key")
26
+ if not math.isfinite(self.minimum) or not math.isfinite(self.maximum) or self.minimum >= self.maximum:
27
+ raise ValueError("numeric bounds must be finite and strictly increasing")
28
+
29
+
30
+ @dataclass(frozen=True, slots=True)
31
+ class CompiledNumeric:
32
+ prompt: str
33
+ input_ids: tuple[int, ...]
34
+ positions: tuple[int, ...]
35
+ candidate_token_ids: tuple[int, ...]
36
+
37
+
38
+ @dataclass(frozen=True, slots=True)
39
+ class CompiledVisionNumeric:
40
+ numeric: CompiledNumeric
41
+ processor_inputs: dict[str, Any]
42
+ positions: tuple[int, ...]
43
+
44
+
45
+ def _query(field: NumericField) -> str:
46
+ # Preserve formatting for tokenizer compatibility.
47
+ return ("QUERY canonical_key=" + json.dumps(field.key, ensure_ascii=False)
48
+ + " unit=" + json.dumps(field.unit, ensure_ascii=False)
49
+ + f" bounds=[{field.minimum!r},{field.maximum!r}]\nAnswer code:\n")
50
+
51
+
52
+ def render_prefix(context: str, fields: Sequence[NumericField], codes: Sequence[str]) -> str:
53
+ shared_map = [{"transport_code": codes[i], "percentage_index": i,
54
+ "normalized_point": i / 100.0} for i in range(101)]
55
+ definitions = [{"canonical_key": f.key, "aliases": list(f.aliases),
56
+ "meaning": f.description, "range": [f.minimum, f.maximum],
57
+ "unit": f.unit, "reference": f.reference,
58
+ "affine_decode": "value=lower+normalized_point*(upper-lower)"}
59
+ for f in fields]
60
+ return ("NATIVE_NUMERIC_SHARED_CODEBOOK_QUERY_SLOTS_V1\n"
61
+ "Return exactly one listed opaque transport code for every point field. "
62
+ "The shared map binds each transport code to one normalized point. "
63
+ "Each field definition supplies its own affine bounds and unit. Transport strings "
64
+ "are labels, not pretrained numeric semantics.\n"
65
+ "All maps and field definitions precede all answer-query slots. "
66
+ "No answer is present in this input.\nSHARED_TRANSPORT_MAP="
67
+ + json.dumps(shared_map, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
68
+ + "\nFIELD_DEFINITIONS="
69
+ + json.dumps(definitions, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
70
+ + "\nCONTEXT_BEGIN\n" + context + "\nCONTEXT_END\nBEGIN_QUERY_SLOTS\n")
71
+
72
+
73
+ def compile_numeric(tokenizer: Any, context: str, fields: Sequence[NumericField],
74
+ codebook: Sequence[dict[str, Any]], max_input_tokens: int) -> CompiledNumeric:
75
+ if not context.strip() or not fields:
76
+ raise ValueError("context and fields must be nonempty")
77
+ names=[name for field in fields for name in (field.key,*field.aliases)]
78
+ if len(names) != len(set(names)):
79
+ raise ValueError("numeric canonical keys and aliases must be globally unique")
80
+ codes = [str(row["code"]) for row in codebook[:101]]
81
+ prefix = render_prefix(context, fields, codes)
82
+ cumulative = prefix
83
+ previous = tuple(tokenizer.encode(cumulative, add_special_tokens=False))
84
+ positions: list[int] = []
85
+ for field in fields:
86
+ cumulative += _query(field)
87
+ current = tuple(tokenizer.encode(cumulative, add_special_tokens=False))
88
+ if len(current) <= len(previous) or current[:len(previous)] != previous:
89
+ raise ValueError("tokenizer resegmented an earlier query boundary")
90
+ # Native LM logits at the last prompt token predict the opaque code.
91
+ positions.append(len(current) - 1)
92
+ previous = current
93
+ if len(previous) > max_input_tokens:
94
+ raise ValueError(f"request needs {len(previous)} tokens; limit is {max_input_tokens}; no truncation performed")
95
+ return CompiledNumeric(cumulative, previous, tuple(positions),
96
+ tuple(int(row["token_id"]) for row in codebook[:101]))
97
+
98
+
99
+ def decode_numeric(probabilities: Sequence[float], field: NumericField) -> dict[str, Any]:
100
+ if len(probabilities) != 101:
101
+ raise ValueError("numeric probability vector must have 101 entries")
102
+ total = math.fsum(float(p) for p in probabilities)
103
+ if not math.isfinite(total) or total <= 0 or any(float(p) < 0 for p in probabilities):
104
+ raise ValueError("invalid numeric probability vector")
105
+ probs = [float(p) / total for p in probabilities]
106
+ map_index = max(range(101), key=probs.__getitem__)
107
+ expected_index = math.fsum(i * p for i, p in enumerate(probs))
108
+ span = field.maximum - field.minimum
109
+ return {
110
+ "value": field.minimum + span * map_index / 100.0,
111
+ "map_value": field.minimum + span * map_index / 100.0,
112
+ "expected_value": field.minimum + span * expected_index / 100.0,
113
+ "map_index": map_index,
114
+ "normalized_grid": [i / 100.0 for i in range(101)],
115
+ "probabilities": probs,
116
+ "minimum": field.minimum, "maximum": field.maximum, "unit": field.unit,
117
+ "calibrated": False,
118
+ }
119
+
120
+
121
+ def _unique_subsequence_offset(values: Sequence[int], needle: Sequence[int]) -> int:
122
+ """Find one token span in linear time, rejecting absent or repeated spans."""
123
+ if not needle:
124
+ raise ValueError("target-free numeric prompt must occur exactly once")
125
+ failure = [0] * len(needle)
126
+ matched = 0
127
+ for index in range(1, len(needle)):
128
+ while matched and needle[index] != needle[matched]:
129
+ matched = failure[matched - 1]
130
+ if needle[index] == needle[matched]:
131
+ matched += 1
132
+ failure[index] = matched
133
+ matched = 0
134
+ offset = -1
135
+ for index, value in enumerate(values):
136
+ while matched and value != needle[matched]:
137
+ matched = failure[matched - 1]
138
+ if value == needle[matched]:
139
+ matched += 1
140
+ if matched == len(needle):
141
+ if offset >= 0:
142
+ raise ValueError("target-free numeric prompt must occur exactly once")
143
+ offset = index + 1 - len(needle)
144
+ matched = failure[matched - 1]
145
+ if offset < 0:
146
+ raise ValueError("target-free numeric prompt must occur exactly once")
147
+ return offset
148
+
149
+
150
+ def compile_vision_numeric(processor: Any, image: Any, context: str,
151
+ fields: Sequence[NumericField], codebook: Sequence[dict[str, Any]],
152
+ max_input_tokens: int, model_config: Any) -> CompiledVisionNumeric:
153
+ """Compile one image and numeric fields using the native processor."""
154
+ import torch
155
+ width, height = getattr(image, "size", (0, 0))
156
+ if width <= 0 or height <= 0:
157
+ raise ValueError("source raster dimensions must be positive")
158
+ numeric = compile_numeric(processor.tokenizer, context, fields, codebook, max_input_tokens)
159
+ messages = [[
160
+ {"role": "user", "content": [
161
+ {"type": "image", "image": image},
162
+ {"type": "text", "text": (
163
+ "Inspect only this supplied full uncropped image. The following assistant "
164
+ "schema contains target-free coordinate query slots for this image.")}]},
165
+ {"role": "assistant", "content": [{"type": "text",
166
+ "text": numeric.prompt + "END_TARGET_FREE_QUERY_BLOCK\n"}]},
167
+ ]]
168
+ batch = processor.apply_chat_template(messages, tokenize=True, truncation=False,
169
+ add_generation_prompt=False, continue_final_message=False, enable_thinking=False,
170
+ return_dict=True, return_tensors="pt", return_mm_token_type_ids=True)
171
+ tensors = {k: v for k, v in batch.items() if isinstance(v, torch.Tensor)}
172
+ required = {"input_ids", "attention_mask", "pixel_values", "image_grid_thw", "mm_token_type_ids"}
173
+ if set(tensors) != required:
174
+ raise ValueError(f"processor tensor keys must be exactly {sorted(required)}, got {sorted(tensors)}")
175
+ ids, mask, mm = tensors["input_ids"], tensors["attention_mask"], tensors["mm_token_type_ids"]
176
+ if ids.ndim != 2 or ids.shape[0] != 1 or mask.shape != ids.shape or mm.shape != ids.shape:
177
+ raise ValueError("vision request requires aligned singleton text tensors")
178
+ if not bool(mask.eq(1).all()) or ids.shape[1] + 1 > max_input_tokens:
179
+ raise ValueError("vision request exceeds token admission or contains padding; no truncation performed")
180
+ grid = tensors["image_grid_thw"]
181
+ if grid.shape != (1, 3) or not bool((grid > 0).all()):
182
+ raise ValueError("image_grid_thw must be one positive [t,h,w] row")
183
+ patch_rows = int(grid.prod().item())
184
+ if tensors["pixel_values"].ndim != 2 or tensors["pixel_values"].shape[0] != patch_rows:
185
+ raise ValueError("pixel_values and processor grid disagree")
186
+ config = model_config.to_dict() if hasattr(model_config, "to_dict") else dict(model_config)
187
+ if config.get("model_type") != "qwen3_5_moe" or not isinstance(config.get("vision_config"), dict):
188
+ raise ValueError("vision compiler requires the pinned multimodal Qwen3.5 MoE config")
189
+ merge = int(processor.image_processor.merge_size)
190
+ if patch_rows % (merge * merge): raise ValueError("image grid is not divisible by merge area")
191
+ image_token = int(config["image_token_id"])
192
+ active = [int(v) for v in ids[0].tolist()]
193
+ expected_image_tokens = patch_rows // (merge * merge)
194
+ if active.count(image_token) != expected_image_tokens:
195
+ raise ValueError("image token count and processor grid disagree")
196
+ vision_start = int(config["vision_start_token_id"]); vision_end = int(config["vision_end_token_id"])
197
+ if active.count(vision_start) != 1 or active.count(vision_end) != 1:
198
+ raise ValueError("vision boundary token count is not exactly one pair")
199
+ mm_values = [int(v) for v in mm[0].tolist()]
200
+ if any((kind == 1) != (token == image_token) for token, kind in zip(active, mm_values, strict=True)):
201
+ raise ValueError("image tokens and mm_token_type_ids disagree")
202
+ needle = numeric.input_ids
203
+ offset = _unique_subsequence_offset(active, needle)
204
+ positions = tuple(offset + p for p in numeric.positions)
205
+ return CompiledVisionNumeric(numeric, tensors, positions)
gpc1_server/errors.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass(slots=True)
7
+ class APIError(Exception):
8
+ message: str
9
+ status_code: int = 400
10
+ error_type: str = "invalid_request_error"
11
+ param: str | None = None
12
+ code: str | None = None
13
+
14
+ def body(self) -> dict:
15
+ return {"error": {"message": self.message, "type": self.error_type,
16
+ "param": self.param, "code": self.code}}
gpc1_server/native_onepass.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Score complete candidate records in one model call."""
2
+ from __future__ import annotations
3
+
4
+ from dataclasses import dataclass
5
+ from typing import Any, Callable, Sequence
6
+
7
+ import torch
8
+
9
+
10
+ @dataclass(frozen=True, slots=True)
11
+ class Row:
12
+ index: int
13
+ candidate_id: str
14
+ targets: tuple[int, ...]
15
+ positions: tuple[int, ...]
16
+ prefix_tokens: int
17
+ input_tokens: int
18
+
19
+
20
+ @dataclass(frozen=True, slots=True)
21
+ class CompiledJoint:
22
+ input_ids: torch.Tensor
23
+ attention_mask: torch.Tensor
24
+ position_ids: torch.Tensor
25
+ logits_to_keep: torch.Tensor
26
+ rows: tuple[Row, ...]
27
+ candidate_order: tuple[str, ...]
28
+ padded_tokens: int
29
+ logit_vectors: int
30
+
31
+
32
+ def _contains(values: tuple[int, ...], needle: tuple[int, ...]) -> bool:
33
+ return len(needle) <= len(values) and any(
34
+ values[i:i + len(needle)] == needle for i in range(len(values) - len(needle) + 1)
35
+ )
36
+
37
+
38
+ def compile_joint(prefix: Sequence[int], candidate_ids: Sequence[str],
39
+ suffixes: Sequence[Sequence[int]], terminal: Sequence[int], *,
40
+ pad_token_id: int, max_rows: int, max_padded_tokens: int,
41
+ max_logit_vectors: int, max_input_tokens: int) -> CompiledJoint:
42
+ prefix = tuple(prefix); terminal = tuple(terminal)
43
+ ids = tuple(candidate_ids); suffixes = tuple(tuple(v) for v in suffixes)
44
+ if not prefix or not terminal or any(type(v) is not int or v < 0 for v in prefix + terminal):
45
+ raise ValueError("prefix and terminal must contain nonnegative token IDs")
46
+ if not ids or len(ids) != len(set(ids)) or any(not isinstance(v, str) or not v for v in ids):
47
+ raise ValueError("candidate IDs must be unique nonempty strings")
48
+ if len(ids) != len(suffixes) or len(suffixes) != len(set(suffixes)):
49
+ raise ValueError("candidate suffixes must be unique and align with candidate IDs")
50
+ if any(prefix[-n:] == terminal[:n] for n in range(1, min(len(prefix), len(terminal)) + 1)):
51
+ raise ValueError("prefix overlaps the declared terminal boundary")
52
+ for suffix in suffixes:
53
+ if (len(suffix) < len(terminal) or suffix[-len(terminal):] != terminal
54
+ or _contains(suffix[:-len(terminal)], terminal)):
55
+ raise ValueError("every candidate must contain the common terminal exactly at its end")
56
+ for i, left in enumerate(suffixes):
57
+ for j, right in enumerate(suffixes):
58
+ if i != j and len(left) < len(right) and right[:len(left)] == left:
59
+ raise ValueError("candidate tokenizations prefix-overlap")
60
+ if any(any(type(v) is not int or v < 0 for v in suffix) for suffix in suffixes):
61
+ raise ValueError("candidate suffixes must contain nonnegative token IDs")
62
+ if len(suffixes) > max_rows:
63
+ raise ValueError(f"compiled rows {len(suffixes)} exceed max_rows {max_rows}")
64
+ sequences = tuple(prefix + suffix for suffix in suffixes)
65
+ if max(map(len,sequences)) > max_input_tokens:
66
+ raise ValueError(f"joint request exceeds max_input_tokens {max_input_tokens}; no truncation performed")
67
+ width = max(map(len, sequences)); padded = len(sequences) * width
68
+ if padded > max_padded_tokens:
69
+ raise ValueError(f"compiled padded tokens {padded} exceed max_padded_tokens {max_padded_tokens}")
70
+ positions = sorted({p for seq in sequences for p in range(len(prefix) - 1, len(seq) - 1)})
71
+ vectors = len(sequences) * len(positions)
72
+ if vectors > max_logit_vectors:
73
+ raise ValueError(f"compiled logit vectors {vectors} exceed max_logit_vectors {max_logit_vectors}")
74
+ input_ids = torch.full((len(sequences), width), pad_token_id, dtype=torch.long)
75
+ mask = torch.zeros_like(input_ids)
76
+ rows = []
77
+ for i, (candidate_id, suffix, sequence) in enumerate(zip(ids, suffixes, sequences, strict=True)):
78
+ input_ids[i, :len(sequence)] = torch.tensor(sequence)
79
+ mask[i, :len(sequence)] = 1
80
+ rows.append(Row(i, candidate_id, suffix,
81
+ tuple(range(len(prefix) - 1, len(sequence) - 1)), len(prefix), len(sequence)))
82
+ position_ids = mask.cumsum(-1) - 1; position_ids.masked_fill_(mask == 0, 0)
83
+ return CompiledJoint(input_ids, mask, position_ids, torch.tensor(positions), tuple(rows), ids,
84
+ padded, vectors)
85
+
86
+
87
+ def score_joint(model_forward: Callable[..., Any], compiled: CompiledJoint, *, device: Any) -> dict[str, Any]:
88
+ expected_positions = compiled.attention_mask.cumsum(-1) - 1
89
+ expected_positions.masked_fill_(compiled.attention_mask == 0, 0)
90
+ if not torch.equal(compiled.position_ids, expected_positions):
91
+ raise ValueError("compiled position IDs mutated before forward")
92
+ keep = [int(value) for value in compiled.logits_to_keep.tolist()]
93
+ with torch.inference_mode():
94
+ output = model_forward(input_ids=compiled.input_ids.to(device),
95
+ attention_mask=compiled.attention_mask.to(device),
96
+ position_ids=compiled.position_ids.to(device), logits_to_keep=keep,
97
+ use_cache=False, return_dict=True)
98
+ logits = output.logits
99
+ if logits.ndim != 3 or tuple(logits.shape[:2]) != (len(compiled.rows), len(keep)):
100
+ raise ValueError("native sparse logits do not align to compiled joint positions")
101
+ logits = logits.float()
102
+ if not bool(torch.isfinite(logits).all()):
103
+ raise ValueError("model logits contain non-finite values")
104
+ columns = {int(p): i for i, p in enumerate(compiled.logits_to_keep.tolist())}
105
+ scores = []
106
+ for row in compiled.rows:
107
+ terms = []
108
+ for position, target in zip(row.positions, row.targets, strict=True):
109
+ distribution = logits[row.index, columns[position]]
110
+ if target >= distribution.numel(): raise ValueError("target token is outside vocabulary")
111
+ terms.append(distribution[target] - torch.logsumexp(distribution, -1))
112
+ scores.append(torch.stack(terms).sum())
113
+ scores_t = torch.stack(scores); probs = torch.exp(scores_t - torch.logsumexp(scores_t, 0))
114
+ selected = int(torch.argmax(scores_t))
115
+ return {"selected_index": selected, "probabilities": probs.cpu().tolist(),
116
+ "log_scores": scores_t.cpu().tolist(),
117
+ "row_map": [{"row_index": r.index, "candidate_id": r.candidate_id,
118
+ "prediction_positions": list(r.positions)} for r in compiled.rows],
119
+ "accounting": {"backbone_calls": 1, "physical_rows": len(compiled.rows),
120
+ "padded_tokens": compiled.padded_tokens,
121
+ "logit_vectors": compiled.logit_vectors, "truncated": False},
122
+ "calibrated": False}
gpc1_server/runtime.py ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import hashlib
5
+ import os
6
+ from pathlib import Path
7
+ from typing import Any, Protocol, Sequence
8
+
9
+ from .assets import load_codebook, load_runtime_manifest, sha256_file
10
+ from .compiler import NumericField, compile_numeric, compile_vision_numeric, decode_numeric
11
+ from .native_onepass import compile_joint, score_joint
12
+
13
+
14
+ class Backend(Protocol):
15
+ model_id: str
16
+ def numeric(self, context: str, fields: Sequence[NumericField], image: Any | None = None) -> dict[str, Any]: ...
17
+ def atomic(self, context: str, field_key: str, choices: Sequence[Any]) -> dict[str, Any]: ...
18
+ def finite_joint(self, context: str, schema: dict[str, Any], candidates: Sequence[dict[str, Any]]) -> dict[str, Any]: ...
19
+
20
+
21
+ class TransformersBackend:
22
+ """Pinned Transformers/PEFT backend; no custom CUDA kernels or generation."""
23
+
24
+ @staticmethod
25
+ def _context_limits(manifest: dict[str, Any], model_config: dict[str, Any],
26
+ environ: dict[str, str]) -> tuple[int, int]:
27
+ capacity = int(model_config["text_config"]["max_position_embeddings"])
28
+ default = int(manifest["admission"]["default_max_input_tokens"])
29
+ limit = int(environ.get("GPC1_MAX_INPUT_TOKENS", str(default)))
30
+ if not 0 < limit <= capacity:
31
+ raise ValueError(f"GPC1_MAX_INPUT_TOKENS must be between 1 and {capacity}")
32
+ padded = int(environ.get("GPC1_MAX_PADDED_TOKENS", str(max(131072, 2 * limit))))
33
+ if padded <= 0:
34
+ raise ValueError("GPC1_MAX_PADDED_TOKENS must be positive")
35
+ return limit, padded
36
+
37
+ @staticmethod
38
+ def _verify_model_file_closure(model_path: Path, expected_files: set[str]) -> None:
39
+ top_files={p.name for p in model_path.iterdir() if p.is_file()}
40
+ suspicious={name for name in top_files if (
41
+ name.endswith((".safetensors",".json",".jinja",".txt"))
42
+ or "config" in name or "tokenizer" in name or "processor" in name or "index" in name)}
43
+ if suspicious != expected_files:
44
+ raise RuntimeError(f"loader-visible model file closure mismatch: {sorted(suspicious ^ expected_files)}")
45
+
46
+ @staticmethod
47
+ def _verify_adapter_file_closure(adapter_path: Path) -> None:
48
+ adapter_files={p.name for p in adapter_path.iterdir() if p.is_file()}
49
+ if adapter_files != {"adapter_config.json","adapter_model.safetensors"}:
50
+ raise RuntimeError("adapter directory must contain exactly the two bound release files")
51
+
52
+ @staticmethod
53
+ def _adapter_path(manifest: dict[str, Any], package_root: Path,
54
+ environ: dict[str, str]) -> Path:
55
+ if manifest.get("weight_format") != "adapter" or not isinstance(manifest.get("adapter"), dict) \
56
+ or manifest.get("merged_full_weights") is not None:
57
+ raise RuntimeError("release runtime requires adapter weight metadata only")
58
+ return Path(environ.get("GPC1_ADAPTER_PATH", package_root / "adapter"))
59
+
60
+ def __init__(self) -> None:
61
+ import torch
62
+ from transformers import AutoProcessor
63
+ from transformers import Qwen3_5MoeForConditionalGeneration
64
+
65
+ self.torch = torch
66
+ torch.backends.cuda.matmul.allow_tf32 = False
67
+ torch.backends.cudnn.allow_tf32 = False
68
+ torch.set_float32_matmul_precision("highest")
69
+ self.manifest = load_runtime_manifest()
70
+ package_root = Path(__file__).resolve().parents[1]
71
+ model_path = Path(os.environ["GPC1_MODEL_PATH"])
72
+ self.max_input_tokens, self.max_padded_tokens = self._context_limits(
73
+ self.manifest, json.loads((model_path / "config.json").read_text()), os.environ)
74
+ adapter_path = self._adapter_path(self.manifest, package_root, os.environ)
75
+ self._verify_artifacts(model_path, adapter_path)
76
+ self.processor = AutoProcessor.from_pretrained(model_path, local_files_only=True,
77
+ trust_remote_code=False)
78
+ base = Qwen3_5MoeForConditionalGeneration.from_pretrained(
79
+ model_path, local_files_only=True, trust_remote_code=False,
80
+ dtype=torch.bfloat16, attn_implementation="sdpa",
81
+ low_cpu_mem_usage=True, device_map={"": 0})
82
+ from peft import PeftModel
83
+ self.model = PeftModel.from_pretrained(base, adapter_path, is_trainable=False,
84
+ autocast_adapter_dtype=True)
85
+ self.model.eval()
86
+ for parameter in self.model.parameters():
87
+ parameter.requires_grad_(False)
88
+ if base.__class__.__name__ != "Qwen3_5MoeForConditionalGeneration" \
89
+ or getattr(base.config, "model_type", None) != "qwen3_5_moe":
90
+ raise RuntimeError("loaded base is not the pinned Qwen3.5 MoE architecture")
91
+ adapter_parameters = {name: p for name, p in self.model.named_parameters() if "lora_" in name}
92
+ base_dtypes = {p.dtype for name, p in self.model.named_parameters()
93
+ if p.is_floating_point() and "lora_" not in name}
94
+ if base_dtypes != {torch.bfloat16} or not adapter_parameters \
95
+ or {p.dtype for p in adapter_parameters.values()} != {torch.float32}:
96
+ raise RuntimeError("loaded base/adapter dtype inventory differs from BF16/FP32 contract")
97
+ from peft.utils import get_peft_model_state_dict
98
+ digest = hashlib.sha256()
99
+ state = get_peft_model_state_dict(self.model)
100
+ for name in sorted(state):
101
+ tensor = state[name].detach().cpu().contiguous()
102
+ metadata = json.dumps({"dtype":str(tensor.dtype),"name":name,"shape":list(tensor.shape)},
103
+ sort_keys=True,separators=(",", ":")).encode()
104
+ raw = tensor.view(torch.uint8).numpy().tobytes()
105
+ digest.update(len(metadata).to_bytes(8,"big")); digest.update(metadata)
106
+ digest.update(len(raw).to_bytes(8,"big")); digest.update(raw)
107
+ if digest.hexdigest() != self.manifest["adapter"]["adapter_tensor_digest"]:
108
+ raise RuntimeError("loaded adapter tensor digest mismatch")
109
+ self.model_id = self.manifest["served_model_id"]
110
+ self.codebook = load_codebook()
111
+ self.max_joint_rows = int(os.environ.get("GPC1_MAX_JOINT_ROWS", "32"))
112
+ self.max_logit_vectors = int(os.environ.get("GPC1_MAX_LOGIT_VECTORS", "512"))
113
+ self.max_logits_bytes = int(os.environ.get("GPC1_MAX_LOGITS_BYTES", str(512 * 1024 * 1024)))
114
+ accepted_modes = set(self.manifest["admission"]["accepted_modes"])
115
+ self.admit_vision = "numeric101_image" in accepted_modes \
116
+ and os.environ.get("GPC1_ADMIT_VISION_NUMERIC", "accepted") == "accepted"
117
+ self.admit_joint = "finite_joint" in accepted_modes \
118
+ and os.environ.get("GPC1_ADMIT_FINITE_JOINT", "accepted") == "accepted"
119
+ cache_mb = int(os.environ.get("GPC1_IMAGE_CACHE_MB", "64"))
120
+ if not 0 <= cache_mb <= 64:
121
+ raise ValueError("GPC1_IMAGE_CACHE_MB must be an integer between 0 and 64")
122
+ self._vision_cache = None
123
+ if cache_mb:
124
+ from .vision_cache import VisionFeatureCache
125
+ self._vision_cache = VisionFeatureCache(
126
+ self.model.base_model.model.model.visual, torch, cache_mb * 1024 * 1024)
127
+
128
+ def _verify_artifacts(self, model_path: Path, adapter_path: Path) -> None:
129
+ expected = self.manifest
130
+ package_root = Path(__file__).resolve().parents[1]
131
+ inventory_path = package_root / "assets" / "base_files.sha256.json"
132
+ inventory = json.loads(inventory_path.read_text())
133
+ if inventory["model_id"] != expected["base"]["model_id"] or inventory["revision"] != expected["base"]["revision"]:
134
+ raise RuntimeError("model-file inventory identity mismatch")
135
+ self._verify_model_file_closure(model_path, set(inventory["files"]))
136
+ for relative, digest in inventory["files"].items():
137
+ path = model_path / relative
138
+ if not path.is_file() or sha256_file(path) != digest:
139
+ raise RuntimeError(f"pinned model file identity mismatch: {relative}")
140
+ self._verify_adapter_file_closure(adapter_path)
141
+ if sha256_file(adapter_path / "adapter_model.safetensors") != expected["adapter"]["adapter_model_sha256"]:
142
+ raise RuntimeError("bundled adapter identity mismatch")
143
+ if sha256_file(adapter_path / "adapter_config.json") != expected["adapter"]["adapter_config_sha256"]:
144
+ raise RuntimeError("bundled adapter config identity mismatch")
145
+
146
+ def _forward(self, inputs: dict[str, Any], positions: Sequence[int]) -> Any:
147
+ torch = self.torch
148
+ device = next(self.model.parameters()).device
149
+ moved = {k: v.to(device) if hasattr(v, "to") else v for k, v in inputs.items()}
150
+ with torch.inference_mode():
151
+ # Project logits only at requested positions.
152
+ output = self.model(**moved, logits_to_keep=[int(p) for p in positions],
153
+ use_cache=False, return_dict=True)
154
+ if output.logits.ndim != 3 or tuple(output.logits.shape[:2]) != (1, len(positions)):
155
+ raise RuntimeError("native sparse logits do not align to query positions")
156
+ return output.logits[0].float()
157
+
158
+ def numeric(self, context: str, fields: Sequence[NumericField], image: Any | None = None) -> dict[str, Any]:
159
+ vocab = int(self.model.config.text_config.vocab_size)
160
+ if len(fields) * vocab * 4 > self.max_logits_bytes:
161
+ raise ValueError("numeric sparse logits exceed byte admission")
162
+ if image is None:
163
+ compiled = compile_numeric(self.processor.tokenizer, context, fields, self.codebook,
164
+ self.max_input_tokens)
165
+ ids = self.torch.tensor([compiled.input_ids], dtype=self.torch.long)
166
+ mask = self.torch.ones_like(ids)
167
+ inputs = {"input_ids": ids, "attention_mask": mask,
168
+ "position_ids": mask.cumsum(dim=-1) - 1}
169
+ positions = compiled.positions
170
+ else:
171
+ if not self.admit_vision:
172
+ raise ValueError("vision numeric requires an accepted same-hardware parity gate")
173
+ vision = compile_vision_numeric(self.processor, image, context, fields, self.codebook,
174
+ self.max_input_tokens, self.model.config)
175
+ compiled = vision.numeric; inputs = vision.processor_inputs; positions = vision.positions
176
+ logits = self._forward(inputs, positions)
177
+ token_ids = self.torch.tensor(compiled.candidate_token_ids, device=logits.device)
178
+ results = {}
179
+ for index, field in enumerate(fields):
180
+ raw = logits[index].index_select(0, token_ids).float()
181
+ probs = self.torch.softmax(raw, dim=-1).cpu().tolist()
182
+ results[field.key] = decode_numeric(probs, field)
183
+ return {"fields": results, "accounting": {"backbone_calls": 1,
184
+ "physical_rows": 1, "field_slots": len(fields), "truncated": False},
185
+ "semantics": "per-field marginals; not a learned full joint distribution"}
186
+
187
+ def atomic(self, context: str, field_key: str, choices: Sequence[Any]) -> dict[str, Any]:
188
+ if not 2 <= len(choices) <= 255 or len({json.dumps(v,sort_keys=True) for v in choices}) != len(choices):
189
+ raise ValueError("atomic choices must contain 2..255 unique JSON values")
190
+ schema={"field_order":[field_key],"fields":{field_key:{"type":"enum","choices":list(choices)}}}
191
+ support="\n".join(f'{self.codebook[i]["code"]} = '+json.dumps(v,ensure_ascii=False,sort_keys=True,separators=(",",":")) for i,v in enumerate(choices))
192
+ user=("Classify the active field from the complete input.\n\nContext:\n"+context+"\n\n"
193
+ "Registered schema gpc1-public-atomic@1:\n"+json.dumps(schema,ensure_ascii=False,sort_keys=True,separators=(",",":"))
194
+ +"\n\nAllowed complete outputs in registered semantic order:\n"+support+"\n\nReturn exactly one allowed output.")
195
+ prompt=self.processor.tokenizer.apply_chat_template([
196
+ {"role":"system","content":"You are a fixed-schema lookup model. Return exactly one listed opaque code and no other text."},
197
+ {"role":"user","content":user}],tokenize=False,add_generation_prompt=True,enable_thinking=False)
198
+ ids=tuple(self.processor.tokenizer.encode(prompt,add_special_tokens=False))
199
+ if len(ids)>self.max_input_tokens: raise ValueError("atomic request exceeds token admission; no truncation performed")
200
+ for row in self.codebook[:len(choices)]:
201
+ combined=tuple(self.processor.tokenizer.encode(prompt+str(row["code"]),add_special_tokens=False))
202
+ if combined != ids + (int(row["token_id"]),):
203
+ raise ValueError("atomic response-boundary tokenization changed")
204
+ tensor=self.torch.tensor([ids]); logits=self._forward({"input_ids":tensor,"attention_mask":self.torch.ones_like(tensor)},[len(ids)-1])[0]
205
+ indices=self.torch.tensor([int(r["token_id"]) for r in self.codebook[:len(choices)]],device=logits.device)
206
+ probs=self.torch.softmax(logits.index_select(0,indices).float(),dim=-1).cpu().tolist(); selected=max(range(len(choices)),key=probs.__getitem__)
207
+ return {"value":choices[selected],"probabilities":probs,"selected_index":selected,"calibrated":False,
208
+ "accounting":{"backbone_calls":1,"physical_rows":1,"truncated":False},
209
+ "semantics":"conditional distribution over the complete caller-declared choice set"}
210
+
211
+ def finite_joint(self, context: str, schema: dict[str, Any], candidates: Sequence[dict[str, Any]]) -> dict[str, Any]:
212
+ if not self.admit_joint:
213
+ raise ValueError("finite_joint requires an accepted same-hardware parity gate")
214
+ tokenizer = self.processor.tokenizer
215
+ canonical = [json.dumps(v, ensure_ascii=False, sort_keys=True,
216
+ separators=(",", ":"), allow_nan=False) for v in candidates]
217
+ support = "\n".join(f"{value} = {value}" for value in canonical)
218
+ user = ("Select one complete allowed JSON record from the complete input.\n\nContext:\n" + context
219
+ + "\n\nRegistered schema gpc1-public-finite-joint@1:\n"
220
+ + json.dumps(schema, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False)
221
+ + "\n\nAllowed complete outputs in registered semantic order:\n" + support
222
+ + "\n\nReturn exactly one allowed output.")
223
+ prompt = tokenizer.apply_chat_template([
224
+ {"role":"system","content":"You are a fixed-schema lookup model. Return exactly one listed complete JSON record and no other text."},
225
+ {"role":"user","content":user}], tokenize=False, add_generation_prompt=True,
226
+ enable_thinking=False)
227
+ prefix = tuple(tokenizer.encode(prompt, add_special_tokens=False))
228
+ terminal_text = tokenizer.eos_token or "<|im_end|>"
229
+ terminal = tuple(tokenizer.encode(terminal_text, add_special_tokens=False))
230
+ suffixes = []
231
+ for value in canonical:
232
+ complete = tuple(tokenizer.encode(prompt + value + terminal_text, add_special_tokens=False))
233
+ if complete[:len(prefix)] != prefix: raise ValueError("candidate tokenization changed prompt prefix")
234
+ suffixes.append(complete[len(prefix):])
235
+ compiled = compile_joint(prefix, tuple(str(i) for i in range(len(candidates))), suffixes,
236
+ terminal, pad_token_id=int(tokenizer.pad_token_id), max_rows=self.max_joint_rows,
237
+ max_padded_tokens=self.max_padded_tokens, max_logit_vectors=self.max_logit_vectors,
238
+ max_input_tokens=self.max_input_tokens)
239
+ vocab = int(self.model.config.text_config.vocab_size)
240
+ if compiled.logit_vectors * vocab * 4 > self.max_logits_bytes:
241
+ raise ValueError("joint sparse logits exceed byte admission")
242
+ device = next(self.model.parameters()).device
243
+ result = score_joint(self.model, compiled, device=device)
244
+ result["value"] = candidates[result["selected_index"]]
245
+ result["semantics"] = "conditional distribution over the complete caller-declared joint support"
246
+ return result
gpc1_server/schemas.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Literal
4
+ from pydantic import BaseModel, ConfigDict, Field, model_validator
5
+
6
+
7
+ class Message(BaseModel):
8
+ model_config = ConfigDict(extra="forbid")
9
+ role: Literal["system", "developer", "user", "assistant"]
10
+ content: str | list[dict[str, Any]]
11
+
12
+
13
+ class ResponseFormat(BaseModel):
14
+ model_config = ConfigDict(extra="forbid")
15
+ type: Literal["json_schema"]
16
+ json_schema: dict[str, Any]
17
+
18
+
19
+ class GPC1Options(BaseModel):
20
+ model_config = ConfigDict(extra="forbid")
21
+ mode: Literal["atomic", "numeric101", "finite_joint"]
22
+ field_key: str | None = None
23
+ choices: list[Any] | None = None
24
+ numeric_fields: list[dict[str, Any]] | None = None
25
+ allowed_records: list[dict[str, Any]] | None = None
26
+
27
+ @model_validator(mode="after")
28
+ def match_mode(self):
29
+ active = {
30
+ "atomic": (self.field_key is not None and self.choices is not None,
31
+ self.numeric_fields is None and self.allowed_records is None),
32
+ "numeric101": (self.numeric_fields is not None,
33
+ self.field_key is None and self.choices is None and self.allowed_records is None),
34
+ "finite_joint": (self.allowed_records is not None,
35
+ self.field_key is None and self.choices is None and self.numeric_fields is None),
36
+ }[self.mode]
37
+ if not all(active):
38
+ raise ValueError(f"{self.mode} requires only its mode-specific members")
39
+ return self
40
+
41
+
42
+ class ChatRequest(BaseModel):
43
+ model_config = ConfigDict(extra="forbid")
44
+ model: str
45
+ messages: list[Message] = Field(min_length=1)
46
+ response_format: ResponseFormat
47
+ gpc1: GPC1Options
48
+ stream: Literal[False] = False
49
+ temperature: float | None = None
50
+ max_completion_tokens: int | None = None
gpc1_server/vision_cache.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import threading
5
+ from typing import Any
6
+
7
+
8
+ class VisionFeatureCache:
9
+ """One-image, memory-bounded cache for a frozen vision encoder."""
10
+
11
+ def __init__(self, visual: Any, torch: Any, max_bytes: int = 64 * 1024 * 1024):
12
+ self.torch = torch
13
+ self.max_bytes = max_bytes
14
+ self._key = None
15
+ self._value = None
16
+ self._lock = threading.RLock()
17
+ original = visual.forward
18
+
19
+ def forward(*args, **kwargs):
20
+ if visual.training or torch.is_grad_enabled() or self.max_bytes <= 0:
21
+ self.clear()
22
+ return original(*args, **kwargs)
23
+ with self._lock:
24
+ key = self._fingerprint((args, kwargs))
25
+ if key is not None and key == self._key:
26
+ return self._clone(self._value)
27
+ output = original(*args, **kwargs)
28
+ self._key = self._value = None
29
+ size = self._size(output)
30
+ if key is not None and size is not None and size <= self.max_bytes:
31
+ self._value = self._clone(output)
32
+ self._key = key
33
+ return output
34
+
35
+ visual.forward = forward
36
+
37
+ def clear(self):
38
+ with self._lock:
39
+ self._key = self._value = None
40
+
41
+ def _fingerprint(self, value):
42
+ torch = self.torch
43
+ digest = hashlib.sha256()
44
+
45
+ def update(item):
46
+ if isinstance(item, torch.Tensor):
47
+ if item.layout != torch.strided:
48
+ raise TypeError
49
+ digest.update(repr((str(item.dtype), str(item.device), tuple(item.shape), tuple(item.stride()))).encode())
50
+ digest.update(item.detach().contiguous().cpu().view(torch.uint8).numpy().tobytes())
51
+ elif isinstance(item, (tuple, list)):
52
+ digest.update(type(item).__name__.encode())
53
+ for child in item:
54
+ update(child)
55
+ digest.update(b'\0')
56
+ elif isinstance(item, dict):
57
+ digest.update(b'dict')
58
+ for key in sorted(item):
59
+ update(key)
60
+ update(item[key])
61
+ elif item is None or type(item) in (bool, int, float, str):
62
+ digest.update(repr((type(item).__name__, item)).encode())
63
+ else:
64
+ raise TypeError
65
+
66
+ try:
67
+ update(value)
68
+ update((torch.backends.cuda.matmul.allow_tf32, torch.backends.cudnn.allow_tf32,
69
+ torch.backends.cudnn.enabled, torch.backends.cudnn.benchmark,
70
+ torch.backends.cudnn.deterministic, torch.get_float32_matmul_precision()))
71
+ except (TypeError, ValueError):
72
+ return None
73
+ return digest.digest()
74
+
75
+ def _clone(self, value):
76
+ if isinstance(value, self.torch.Tensor):
77
+ return value.detach().clone()
78
+ if isinstance(value, dict):
79
+ cloned = {k: self._clone(v) for k, v in value.items()}
80
+ return cloned if type(value) is dict else type(value)(**cloned)
81
+ if isinstance(value, tuple):
82
+ return tuple(self._clone(v) for v in value)
83
+ if isinstance(value, list):
84
+ return [self._clone(v) for v in value]
85
+ return value
86
+
87
+ def _size(self, value):
88
+ if isinstance(value, self.torch.Tensor):
89
+ return value.numel() * value.element_size()
90
+ if isinstance(value, dict):
91
+ sizes = [self._size(v) for v in value.values()]
92
+ elif isinstance(value, (tuple, list)):
93
+ sizes = [self._size(v) for v in value]
94
+ elif value is None or type(value) in (bool, int, float, str):
95
+ return 0
96
+ else:
97
+ return None
98
+ return None if any(v is None for v in sizes) else sum(sizes)
model/LICENSE ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2026 Alibaba Cloud
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
model/chat_template.jinja ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- set image_count = namespace(value=0) %}
2
+ {%- set video_count = namespace(value=0) %}
3
+ {%- macro render_content(content, do_vision_count, is_system_content=false) %}
4
+ {%- if content is string %}
5
+ {{- content }}
6
+ {%- elif content is iterable and content is not mapping %}
7
+ {%- for item in content %}
8
+ {%- if 'image' in item or 'image_url' in item or item.type == 'image' %}
9
+ {%- if is_system_content %}
10
+ {{- raise_exception('System message cannot contain images.') }}
11
+ {%- endif %}
12
+ {%- if do_vision_count %}
13
+ {%- set image_count.value = image_count.value + 1 %}
14
+ {%- endif %}
15
+ {%- if add_vision_id %}
16
+ {{- 'Picture ' ~ image_count.value ~ ': ' }}
17
+ {%- endif %}
18
+ {{- '<|vision_start|><|image_pad|><|vision_end|>' }}
19
+ {%- elif 'video' in item or item.type == 'video' %}
20
+ {%- if is_system_content %}
21
+ {{- raise_exception('System message cannot contain videos.') }}
22
+ {%- endif %}
23
+ {%- if do_vision_count %}
24
+ {%- set video_count.value = video_count.value + 1 %}
25
+ {%- endif %}
26
+ {%- if add_vision_id %}
27
+ {{- 'Video ' ~ video_count.value ~ ': ' }}
28
+ {%- endif %}
29
+ {{- '<|vision_start|><|video_pad|><|vision_end|>' }}
30
+ {%- elif 'text' in item %}
31
+ {{- item.text }}
32
+ {%- else %}
33
+ {{- raise_exception('Unexpected item type in content.') }}
34
+ {%- endif %}
35
+ {%- endfor %}
36
+ {%- elif content is none or content is undefined %}
37
+ {{- '' }}
38
+ {%- else %}
39
+ {{- raise_exception('Unexpected content type.') }}
40
+ {%- endif %}
41
+ {%- endmacro %}
42
+ {%- if not messages %}
43
+ {{- raise_exception('No messages provided.') }}
44
+ {%- endif %}
45
+ {%- if tools and tools is iterable and tools is not mapping %}
46
+ {{- '<|im_start|>system\n' }}
47
+ {{- "# Tools\n\nYou have access to the following functions:\n\n<tools>" }}
48
+ {%- for tool in tools %}
49
+ {{- "\n" }}
50
+ {{- tool | tojson }}
51
+ {%- endfor %}
52
+ {{- "\n</tools>" }}
53
+ {{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
54
+ {%- if messages[0].role == 'system' %}
55
+ {%- set content = render_content(messages[0].content, false, true)|trim %}
56
+ {%- if content %}
57
+ {{- '\n\n' + content }}
58
+ {%- endif %}
59
+ {%- endif %}
60
+ {{- '<|im_end|>\n' }}
61
+ {%- else %}
62
+ {%- if messages[0].role == 'system' %}
63
+ {%- set content = render_content(messages[0].content, false, true)|trim %}
64
+ {{- '<|im_start|>system\n' + content + '<|im_end|>\n' }}
65
+ {%- endif %}
66
+ {%- endif %}
67
+ {%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
68
+ {%- for message in messages[::-1] %}
69
+ {%- set index = (messages|length - 1) - loop.index0 %}
70
+ {%- if ns.multi_step_tool and message.role == "user" %}
71
+ {%- set content = render_content(message.content, false)|trim %}
72
+ {%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}
73
+ {%- set ns.multi_step_tool = false %}
74
+ {%- set ns.last_query_index = index %}
75
+ {%- endif %}
76
+ {%- endif %}
77
+ {%- endfor %}
78
+ {%- if ns.multi_step_tool %}
79
+ {{- raise_exception('No user query found in messages.') }}
80
+ {%- endif %}
81
+ {%- for message in messages %}
82
+ {%- set content = render_content(message.content, true)|trim %}
83
+ {%- if message.role == "system" %}
84
+ {%- if not loop.first %}
85
+ {{- raise_exception('System message must be at the beginning.') }}
86
+ {%- endif %}
87
+ {%- elif message.role == "user" %}
88
+ {{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
89
+ {%- elif message.role == "assistant" %}
90
+ {%- set reasoning_content = '' %}
91
+ {%- if message.reasoning_content is string %}
92
+ {%- set reasoning_content = message.reasoning_content %}
93
+ {%- else %}
94
+ {%- if '</think>' in content %}
95
+ {%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
96
+ {%- set content = content.split('</think>')[-1].lstrip('\n') %}
97
+ {%- endif %}
98
+ {%- endif %}
99
+ {%- set reasoning_content = reasoning_content|trim %}
100
+ {%- if loop.index0 > ns.last_query_index %}
101
+ {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content + '\n</think>\n\n' + content }}
102
+ {%- else %}
103
+ {{- '<|im_start|>' + message.role + '\n' + content }}
104
+ {%- endif %}
105
+ {%- if message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping %}
106
+ {%- for tool_call in message.tool_calls %}
107
+ {%- if tool_call.function is defined %}
108
+ {%- set tool_call = tool_call.function %}
109
+ {%- endif %}
110
+ {%- if loop.first %}
111
+ {%- if content|trim %}
112
+ {{- '\n\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
113
+ {%- else %}
114
+ {{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
115
+ {%- endif %}
116
+ {%- else %}
117
+ {{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
118
+ {%- endif %}
119
+ {%- if tool_call.arguments is defined %}
120
+ {%- for args_name, args_value in tool_call.arguments|items %}
121
+ {{- '<parameter=' + args_name + '>\n' }}
122
+ {%- set args_value = args_value | tojson | safe if args_value is mapping or (args_value is sequence and args_value is not string) else args_value | string %}
123
+ {{- args_value }}
124
+ {{- '\n</parameter>\n' }}
125
+ {%- endfor %}
126
+ {%- endif %}
127
+ {{- '</function>\n</tool_call>' }}
128
+ {%- endfor %}
129
+ {%- endif %}
130
+ {{- '<|im_end|>\n' }}
131
+ {%- elif message.role == "tool" %}
132
+ {%- if loop.previtem and loop.previtem.role != "tool" %}
133
+ {{- '<|im_start|>user' }}
134
+ {%- endif %}
135
+ {{- '\n<tool_response>\n' }}
136
+ {{- content }}
137
+ {{- '\n</tool_response>' }}
138
+ {%- if not loop.last and loop.nextitem.role != "tool" %}
139
+ {{- '<|im_end|>\n' }}
140
+ {%- elif loop.last %}
141
+ {{- '<|im_end|>\n' }}
142
+ {%- endif %}
143
+ {%- else %}
144
+ {{- raise_exception('Unexpected message role.') }}
145
+ {%- endif %}
146
+ {%- endfor %}
147
+ {%- if add_generation_prompt %}
148
+ {{- '<|im_start|>assistant\n' }}
149
+ {%- if enable_thinking is defined and enable_thinking is false %}
150
+ {{- '<think>\n\n</think>\n\n' }}
151
+ {%- else %}
152
+ {{- '<think>\n' }}
153
+ {%- endif %}
154
+ {%- endif %}
model/config.json ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "Qwen3_5MoeForConditionalGeneration"
4
+ ],
5
+ "image_token_id": 248056,
6
+ "model_type": "qwen3_5_moe",
7
+ "text_config": {
8
+ "attention_bias": false,
9
+ "attention_dropout": 0.0,
10
+ "attn_output_gate": true,
11
+ "dtype": "bfloat16",
12
+ "eos_token_id": 248044,
13
+ "full_attention_interval": 4,
14
+ "head_dim": 256,
15
+ "hidden_act": "silu",
16
+ "hidden_size": 2048,
17
+ "initializer_range": 0.02,
18
+ "layer_types": [
19
+ "linear_attention",
20
+ "linear_attention",
21
+ "linear_attention",
22
+ "full_attention",
23
+ "linear_attention",
24
+ "linear_attention",
25
+ "linear_attention",
26
+ "full_attention",
27
+ "linear_attention",
28
+ "linear_attention",
29
+ "linear_attention",
30
+ "full_attention",
31
+ "linear_attention",
32
+ "linear_attention",
33
+ "linear_attention",
34
+ "full_attention",
35
+ "linear_attention",
36
+ "linear_attention",
37
+ "linear_attention",
38
+ "full_attention",
39
+ "linear_attention",
40
+ "linear_attention",
41
+ "linear_attention",
42
+ "full_attention",
43
+ "linear_attention",
44
+ "linear_attention",
45
+ "linear_attention",
46
+ "full_attention",
47
+ "linear_attention",
48
+ "linear_attention",
49
+ "linear_attention",
50
+ "full_attention",
51
+ "linear_attention",
52
+ "linear_attention",
53
+ "linear_attention",
54
+ "full_attention",
55
+ "linear_attention",
56
+ "linear_attention",
57
+ "linear_attention",
58
+ "full_attention"
59
+ ],
60
+ "linear_conv_kernel_dim": 4,
61
+ "linear_key_head_dim": 128,
62
+ "linear_num_key_heads": 16,
63
+ "linear_num_value_heads": 32,
64
+ "linear_value_head_dim": 128,
65
+ "max_position_embeddings": 262144,
66
+ "mlp_only_layers": [],
67
+ "model_type": "qwen3_5_moe_text",
68
+ "moe_intermediate_size": 512,
69
+ "mtp_num_hidden_layers": 1,
70
+ "mtp_use_dedicated_embeddings": false,
71
+ "num_attention_heads": 16,
72
+ "num_experts": 256,
73
+ "num_experts_per_tok": 8,
74
+ "num_hidden_layers": 40,
75
+ "num_key_value_heads": 2,
76
+ "rms_norm_eps": 1e-06,
77
+ "router_aux_loss_coef": 0.001,
78
+ "shared_expert_intermediate_size": 512,
79
+ "use_cache": true,
80
+ "vocab_size": 248320,
81
+ "mamba_ssm_dtype": "float32",
82
+ "rope_parameters": {
83
+ "mrope_interleaved": true,
84
+ "mrope_section": [
85
+ 11,
86
+ 11,
87
+ 10
88
+ ],
89
+ "rope_type": "default",
90
+ "rope_theta": 10000000,
91
+ "partial_rotary_factor": 0.25
92
+ }
93
+ },
94
+ "tie_word_embeddings": false,
95
+ "transformers_version": "4.57.0.dev0",
96
+ "video_token_id": 248057,
97
+ "vision_config": {
98
+ "deepstack_visual_indexes": [],
99
+ "depth": 27,
100
+ "hidden_act": "gelu_pytorch_tanh",
101
+ "hidden_size": 1152,
102
+ "in_channels": 3,
103
+ "initializer_range": 0.02,
104
+ "intermediate_size": 4304,
105
+ "model_type": "qwen3_5_moe",
106
+ "num_heads": 16,
107
+ "num_position_embeddings": 2304,
108
+ "out_hidden_size": 2048,
109
+ "patch_size": 16,
110
+ "spatial_merge_size": 2,
111
+ "temporal_patch_size": 2
112
+ },
113
+ "vision_end_token_id": 248054,
114
+ "vision_start_token_id": 248053
115
+ }
model/generation_config.json ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token_id": 248044,
3
+ "do_sample": true,
4
+ "eos_token_id": [
5
+ 248046,
6
+ 248044
7
+ ],
8
+ "pad_token_id": 248044,
9
+ "temperature": 1.0,
10
+ "top_k": 20,
11
+ "top_p": 0.95,
12
+ "transformers_version": "4.57.0.dev0"
13
+ }
model/merges.txt ADDED
The diff for this file is too large to render. See raw diff
 
model/model-00001-of-00016.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:43a908b3d324d59b66c73a33e5c182809eb2df7da474b2fd2cbdabb16baeee38
3
+ size 4323955448
model/model-00002-of-00016.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4de54e39c2ecc6f3fb5e79e752f84b65c96c316d0d848c9b55d36a062df69e70
3
+ size 4506431768
model/model-00003-of-00016.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:847472ab2e3e10da0baea3993890ce393cc1b364f53674a00a92ac6ed856ec0d
3
+ size 4988775056
model/model-00004-of-00016.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:81b2b46905d62b66c6a0d2360050c266ea9416f913708e9a1447b28465f458f0
3
+ size 3962207584