spinf-serve 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- spinf_serve-0.1.1/.github/workflows/ci.yml +29 -0
- spinf_serve-0.1.1/.gitignore +10 -0
- spinf_serve-0.1.1/.python-version +1 -0
- spinf_serve-0.1.1/ACKNOWLEDGEMENTS.md +57 -0
- spinf_serve-0.1.1/LICENSE +202 -0
- spinf_serve-0.1.1/NOTICE +11 -0
- spinf_serve-0.1.1/PKG-INFO +240 -0
- spinf_serve-0.1.1/README.md +207 -0
- spinf_serve-0.1.1/benchmarks/README.md +53 -0
- spinf_serve-0.1.1/benchmarks/compare.py +168 -0
- spinf_serve-0.1.1/benchmarks/wikinews_fixture.py +235 -0
- spinf_serve-0.1.1/docs/ARCHITECTURE.md +194 -0
- spinf_serve-0.1.1/docs/BATCH_INVARIANCE.md +161 -0
- spinf_serve-0.1.1/docs/EXTRACTION.md +179 -0
- spinf_serve-0.1.1/docs/PRECISION.md +162 -0
- spinf_serve-0.1.1/docs/QWEN.md +492 -0
- spinf_serve-0.1.1/docs/README.md +61 -0
- spinf_serve-0.1.1/docs/calibration.md +113 -0
- spinf_serve-0.1.1/docs/configuration.md +133 -0
- spinf_serve-0.1.1/docs/examples.md +278 -0
- spinf_serve-0.1.1/docs/faq.md +56 -0
- spinf_serve-0.1.1/docs/guides/aspect-based-sentiment-analysis.md +95 -0
- spinf_serve-0.1.1/docs/guides/custom-content-moderation.md +117 -0
- spinf_serve-0.1.1/docs/guides/decision-models.md +102 -0
- spinf_serve-0.1.1/docs/guides/entity-sentiment-analysis.md +98 -0
- spinf_serve-0.1.1/docs/guides/index.md +39 -0
- spinf_serve-0.1.1/docs/guides/intent-classification.md +119 -0
- spinf_serve-0.1.1/docs/guides/llm-probabilities.md +97 -0
- spinf_serve-0.1.1/docs/guides/no-output-tokens.md +81 -0
- spinf_serve-0.1.1/docs/guides/open-ended-survey-coding.md +93 -0
- spinf_serve-0.1.1/docs/guides/probabilistic-if.md +101 -0
- spinf_serve-0.1.1/docs/guides/zero-shot-few-shot-classification.md +95 -0
- spinf_serve-0.1.1/docs/prompt-packs.md +212 -0
- spinf_serve-0.1.1/docs/quickstart.md +171 -0
- spinf_serve-0.1.1/docs/scoring-api.md +267 -0
- spinf_serve-0.1.1/docs/writing-questions.md +155 -0
- spinf_serve-0.1.1/fixtures/README.md +41 -0
- spinf_serve-0.1.1/fixtures/wikinews_20261024.json.gz +0 -0
- spinf_serve-0.1.1/fixtures/wikinews_20261024.spinf-gemma-4-12b-fp8.json.gz +0 -0
- spinf_serve-0.1.1/fixtures/wikinews_20261024.spinf-gemma-4-31b-fp8.json.gz +0 -0
- spinf_serve-0.1.1/fixtures/wikinews_20261024.spinf-qwen3.8-27b-int4.json.gz +0 -0
- spinf_serve-0.1.1/pyproject.toml +59 -0
- spinf_serve-0.1.1/quantizations/README.md +38 -0
- spinf_serve-0.1.1/quantizations/export_outliers.py +144 -0
- spinf_serve-0.1.1/quantizations/fp8_quantize.py +232 -0
- spinf_serve-0.1.1/quantizations/gemma-4-12b-fp8/MODEL_CARD.md +82 -0
- spinf_serve-0.1.1/quantizations/gemma-4-12b-fp8/calibration.json +1024 -0
- spinf_serve-0.1.1/quantizations/gemma-4-12b-fp8/recipe.yaml +20 -0
- spinf_serve-0.1.1/quantizations/gemma-4-31b-fp8/MODEL_CARD.md +80 -0
- spinf_serve-0.1.1/quantizations/gemma-4-31b-fp8/calibration.json +1270 -0
- spinf_serve-0.1.1/quantizations/gemma-4-31b-fp8/recipe.yaml +21 -0
- spinf_serve-0.1.1/quantizations/gptq_quantize.py +279 -0
- spinf_serve-0.1.1/quantizations/pplx-decider-v1-27b-int4/MODEL_CARD.md +98 -0
- spinf_serve-0.1.1/quantizations/pplx-decider-v1-27b-int4/quantize_config.json +48 -0
- spinf_serve-0.1.1/quantizations/pplx-decider-v1-27b-int4/recipe.yaml +24 -0
- spinf_serve-0.1.1/quantizations/pplx-decider-v1-27b-int4/spinf_outliers.json +1 -0
- spinf_serve-0.1.1/quantizations/qwen3.8-27b-int4/MODEL_CARD.md +83 -0
- spinf_serve-0.1.1/quantizations/qwen3.8-27b-int4/recipe.yaml +25 -0
- spinf_serve-0.1.1/quantizations/qwen3.8-27b-int4/spinf_outliers.json +1 -0
- spinf_serve-0.1.1/spinf_serve/__init__.py +5 -0
- spinf_serve-0.1.1/spinf_serve/__main__.py +33 -0
- spinf_serve-0.1.1/spinf_serve/cli.py +191 -0
- spinf_serve-0.1.1/spinf_serve/compiler.py +656 -0
- spinf_serve-0.1.1/spinf_serve/config.py +119 -0
- spinf_serve-0.1.1/spinf_serve/decode.py +402 -0
- spinf_serve-0.1.1/spinf_serve/extract.py +268 -0
- spinf_serve-0.1.1/spinf_serve/manifests/__init__.py +135 -0
- spinf_serve-0.1.1/spinf_serve/manifests/gemma-4-12b-fp8.toml +24 -0
- spinf_serve-0.1.1/spinf_serve/manifests/gemma-4-31b-fp8.toml +24 -0
- spinf_serve-0.1.1/spinf_serve/manifests/pplx-decider-v1-27b-int4.toml +23 -0
- spinf_serve-0.1.1/spinf_serve/manifests/qwen3.8-27b-int4.toml +24 -0
- spinf_serve-0.1.1/spinf_serve/media.py +153 -0
- spinf_serve-0.1.1/spinf_serve/models/__init__.py +1 -0
- spinf_serve-0.1.1/spinf_serve/models/base.py +88 -0
- spinf_serve-0.1.1/spinf_serve/models/gemma4.py +435 -0
- spinf_serve-0.1.1/spinf_serve/models/qwen35/__init__.py +1 -0
- spinf_serve-0.1.1/spinf_serve/models/qwen35/gdn.py +235 -0
- spinf_serve-0.1.1/spinf_serve/models/qwen35/qwen.py +1165 -0
- spinf_serve-0.1.1/spinf_serve/models/qwen35/qwen_decode.py +349 -0
- spinf_serve-0.1.1/spinf_serve/models/qwen35/qwen_vision.py +195 -0
- spinf_serve-0.1.1/spinf_serve/models/registry.py +25 -0
- spinf_serve-0.1.1/spinf_serve/options.py +27 -0
- spinf_serve-0.1.1/spinf_serve/packer.py +491 -0
- spinf_serve-0.1.1/spinf_serve/packs/__init__.py +454 -0
- spinf_serve-0.1.1/spinf_serve/packs/__main__.py +87 -0
- spinf_serve-0.1.1/spinf_serve/packs/catalog.py +316 -0
- spinf_serve-0.1.1/spinf_serve/packs/pipeline.py +234 -0
- spinf_serve-0.1.1/spinf_serve/packs/refs.py +178 -0
- spinf_serve-0.1.1/spinf_serve/packs/sources.py +192 -0
- spinf_serve-0.1.1/spinf_serve/parity.py +89 -0
- spinf_serve-0.1.1/spinf_serve/protocol.py +410 -0
- spinf_serve-0.1.1/spinf_serve/quant/__init__.py +6 -0
- spinf_serve-0.1.1/spinf_serve/quant/calibrate.py +217 -0
- spinf_serve-0.1.1/spinf_serve/quant/fp8.py +72 -0
- spinf_serve-0.1.1/spinf_serve/quant/i8gemm.py +407 -0
- spinf_serve-0.1.1/spinf_serve/quant/linears.py +135 -0
- spinf_serve-0.1.1/spinf_serve/quant/tables/btbtyler09__Qwen3.8-27B-GPTQ-4bit/b64f44dfedb71b73287219e0ec348b0a20c447e2-outliers.json +1 -0
- spinf_serve-0.1.1/spinf_serve/quant/tables/google__gemma-4-12B/023679ed352de9bb66cc873c9009ce3482585c08.json +1024 -0
- spinf_serve-0.1.1/spinf_serve/quant/tables/google__gemma-4-31B/5bbc2fb1c1b2c611d06e3d9f23c170ba21659d89.json +1270 -0
- spinf_serve-0.1.1/spinf_serve/quant/tables.py +51 -0
- spinf_serve-0.1.1/spinf_serve/quant/w4gemm.py +66 -0
- spinf_serve-0.1.1/spinf_serve/scorer.py +891 -0
- spinf_serve-0.1.1/spinf_serve/server/__init__.py +1 -0
- spinf_serve-0.1.1/spinf_serve/server/__main__.py +8 -0
- spinf_serve-0.1.1/spinf_serve/server/api.py +410 -0
- spinf_serve-0.1.1/spinf_serve/server/app.py +272 -0
- spinf_serve-0.1.1/spinf_serve/server/config.py +232 -0
- spinf_serve-0.1.1/spinf_serve/service.py +368 -0
- spinf_serve-0.1.1/spinf_serve/weights.py +105 -0
- spinf_serve-0.1.1/tests/conformance/all_combinations.json +38 -0
- spinf_serve-0.1.1/tests/conformance/case_and_floor_off.json +38 -0
- spinf_serve-0.1.1/tests/conformance/chat_completions.json +27 -0
- spinf_serve-0.1.1/tests/conformance/err_duplicate_id.json +36 -0
- spinf_serve-0.1.1/tests/conformance/err_invalid_template.json +26 -0
- spinf_serve-0.1.1/tests/conformance/err_missing_value.json +26 -0
- spinf_serve-0.1.1/tests/conformance/err_option_boundary.json +27 -0
- spinf_serve-0.1.1/tests/conformance/err_pack_media.json +33 -0
- spinf_serve-0.1.1/tests/conformance/err_scoring_required.json +20 -0
- spinf_serve-0.1.1/tests/conformance/err_unknown_pack.json +22 -0
- spinf_serve-0.1.1/tests/conformance/err_unknown_query_id.json +35 -0
- spinf_serve-0.1.1/tests/conformance/extraction.json +37 -0
- spinf_serve-0.1.1/tests/conformance/generation.json +24 -0
- spinf_serve-0.1.1/tests/conformance/many_inputs.json +84 -0
- spinf_serve-0.1.1/tests/conformance/one_content.json +51 -0
- spinf_serve-0.1.1/tests/conformance/pack_included_queries.json +38 -0
- spinf_serve-0.1.1/tests/conformance/pack_moderation.json +38 -0
- spinf_serve-0.1.1/tests/conformance.py +164 -0
- spinf_serve-0.1.1/tests/test_config.py +26 -0
- spinf_serve-0.1.1/tests/test_decode_chunks.py +112 -0
- spinf_serve-0.1.1/tests/test_extraction.py +318 -0
- spinf_serve-0.1.1/tests/test_fp8_checkpoint.py +47 -0
- spinf_serve-0.1.1/tests/test_manifests.py +95 -0
- spinf_serve-0.1.1/tests/test_media.py +204 -0
- spinf_serve-0.1.1/tests/test_pack_fixtures.py +83 -0
- spinf_serve-0.1.1/tests/test_packer.py +283 -0
- spinf_serve-0.1.1/tests/test_packs.py +783 -0
- spinf_serve-0.1.1/tests/test_protocol_compiler.py +396 -0
- spinf_serve-0.1.1/tests/test_public_hygiene.py +72 -0
- spinf_serve-0.1.1/tests/test_qwen_plan.py +190 -0
- spinf_serve-0.1.1/tests/test_qwen_vision.py +104 -0
- spinf_serve-0.1.1/tests/test_server_app.py +471 -0
- spinf_serve-0.1.1/tests/test_server_config.py +100 -0
- spinf_serve-0.1.1/tests/test_stacks.py +75 -0
- spinf_serve-0.1.1/tests/test_weights.py +129 -0
- spinf_serve-0.1.1/uv.lock +1423 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# The CPU suite and lint on every push and pull request. The GPU parity gate (README "Develop") runs by hand on a card: there is
|
|
2
|
+
# no GPU runner. The official packs' golden fixtures need a checkout of spinf-prompts next to the tests (SPINF_PROMPTS_DIR);
|
|
3
|
+
# without one those tests skip. SPINF_PROMPTS_REPO (a repository variable) and SPINF_PROMPTS_TOKEN (a secret) point a fork or a
|
|
4
|
+
# mirror at its own copy of the pack repository; unset, the public one is used.
|
|
5
|
+
name: ci
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
pull_request:
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v7
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
|
+
id: prompts
|
|
19
|
+
continue-on-error: true
|
|
20
|
+
with:
|
|
21
|
+
repository: ${{ vars.SPINF_PROMPTS_REPO || 'spinfinc/spinf-prompts' }}
|
|
22
|
+
token: ${{ secrets.SPINF_PROMPTS_TOKEN || github.token }}
|
|
23
|
+
path: .spinf-prompts
|
|
24
|
+
- uses: astral-sh/setup-uv@v10.2.0
|
|
25
|
+
- run: uv sync --frozen --extra server
|
|
26
|
+
- run: uv run ruff check . && uv run ruff format --check .
|
|
27
|
+
- run: uv run pytest
|
|
28
|
+
env:
|
|
29
|
+
SPINF_PROMPTS_DIR: ${{ steps.prompts.outcome == 'success' && format('{0}/.spinf-prompts', github.workspace) || '' }}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Acknowledgements
|
|
2
|
+
|
|
3
|
+
spinf-serve stands on other people's open work. This file names it: the models we run and re-quantize, the projects we
|
|
4
|
+
build on, the techniques we implement, and the data we checked our calibrations and prompts on. The legal attributions
|
|
5
|
+
for code included in this repository are in `NOTICE`; each checkpoint we publish credits its source in its model card
|
|
6
|
+
(`quantizations/*/MODEL_CARD.md`). The prompt packs have their own acknowledgements in the spinf-prompts repository.
|
|
7
|
+
|
|
8
|
+
Rule for contributors: the change that adds a dependency, vendors code, adopts a recipe, adds a model or checks results
|
|
9
|
+
on a dataset also adds the line here (and in `NOTICE` when code is included).
|
|
10
|
+
|
|
11
|
+
## Models and checkpoints
|
|
12
|
+
|
|
13
|
+
| what | by | terms | how we use it |
|
|
14
|
+
|---|---|---|---|
|
|
15
|
+
| [Gemma 4](https://huggingface.co/google) 12B and 31B | Google DeepMind | Apache 2.0 (Gemma 4; Google's licence page: ai.google.dev/gemma/docs/gemma_4_license) | the `spinf/gemma-4-12b-fp8` / `spinf/gemma-4-31b-fp8` models: quantized copies with the original notices retained |
|
|
16
|
+
| [Qwen3.8-27B](https://huggingface.co/Qwen/Qwen3.8-27B) | Alibaba Qwen team | Apache 2.0 | the `spinf/qwen3.8-27b-int4` model (our GPTQ int4 copy) |
|
|
17
|
+
| [pplx-decider-v1-27b](https://huggingface.co/perplexity-ai/pplx-decider-v1-27b) | Perplexity AI | Apache 2.0 (derived from Qwen3.8-27B, revision 1d4bf0f2) | the decision model with a 255-code readout; our GPTQ int4 copy |
|
|
18
|
+
| [btbtyler09/Qwen3.8-27B-GPTQ-4bit](https://huggingface.co/btbtyler09/Qwen3.8-27B-GPTQ-4bit) | btbtyler09 | see the repo | the GPTQ recipe we adopted (GPTQModel 6.0.3, group 32, FOEM) and the checkpoint we served before publishing our own |
|
|
19
|
+
|
|
20
|
+
## Projects
|
|
21
|
+
|
|
22
|
+
- [PyTorch](https://pytorch.org) (inductor, FlexAttention, `torch.compile`), [Triton](https://github.com/triton-lang/triton): the kernels.
|
|
23
|
+
- [Hugging Face](https://huggingface.co) transformers, tokenizers, safetensors, huggingface_hub: model definitions (the Gemma 4 and
|
|
24
|
+
Qwen3.5 modules, the vision towers), tokenization, weights.
|
|
25
|
+
- [flash-linear-attention](https://github.com/fla-org/flash-linear-attention) (Songlin Yang, Yu Zhang and contributors): the Gated
|
|
26
|
+
DeltaNet chunk kernels; one state kernel is vendored with changes (`NOTICE`).
|
|
27
|
+
- [GPTQModel](https://github.com/modelcloud/gptqmodel) (ModelCloud): the int4 quantization of our Qwen-architecture checkpoints.
|
|
28
|
+
- [vLLM](https://github.com/vllm-project/vllm): the reference for the fp8 scheme's numerics (`--quantization fp8`), the engine we
|
|
29
|
+
compare throughput against, and the OpenAI-compatible server shape we follow.
|
|
30
|
+
- [FastAPI](https://fastapi.tiangolo.com), [uvicorn](https://www.uvicorn.org), [pydantic](https://docs.pydantic.dev): the server and the protocol models.
|
|
31
|
+
|
|
32
|
+
## Techniques
|
|
33
|
+
|
|
34
|
+
- GPTQ (Frantar, Ashkboos, Hoefler, Alistarh, 2022) and FOEM (First-Order Error Matters, AAAI 2026): the int4 weights.
|
|
35
|
+
- LLM.int8() (Dettmers, Lewis, Belkada, Zettlemoyer, 2022): the mixed-precision decomposition our i8 split scheme fixes in advance
|
|
36
|
+
(the outlier channels kept exact, the rest int8 per token).
|
|
37
|
+
- SmoothQuant (Xiao et al., 2022): evaluated as an alternative to the outlier split (docs/findings/QWEN.md).
|
|
38
|
+
- Gated DeltaNet (Yang, Kautz, Hatamizadeh, 2024): the recurrent layers of the Qwen3.5 architecture.
|
|
39
|
+
- FlexAttention (PyTorch): the tree masks that let many prompts share one read of the content.
|
|
40
|
+
|
|
41
|
+
## Data
|
|
42
|
+
|
|
43
|
+
Used to calibrate the fp8 activation scales and the outlier channels and to check accuracy; never bundled, never used to
|
|
44
|
+
train anything that ships in this repository.
|
|
45
|
+
|
|
46
|
+
| dataset | used for | terms |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| [Wikinews](https://en.wikinews.org) articles | the public parity fixture and benchmark job set (`fixtures/`, 24 articles; each one's title, URL and revision in the fixture's `sources`) | CC BY 2.5 |
|
|
49
|
+
| Imagenette and Imagewoof (fast.ai) | image calibration and accuracy suites | Apache 2.0 |
|
|
50
|
+
| UCF101 | video suites | see the dataset's terms |
|
|
51
|
+
| ESC-50 | audio calibration and suite | CC BY-NC 3.0 (calibration and checks only) |
|
|
52
|
+
| GTZAN | audio suite | see the dataset's terms |
|
|
53
|
+
| RAGTruth | the outlier-channel calibration stream; a decision benchmark | see the dataset's terms |
|
|
54
|
+
| evol-codealpaca-v1 and C4 | GPTQ calibration samples (the adopted recipe) | see each dataset's terms (C4: ODC-BY) |
|
|
55
|
+
| the decision benchmarks of the pplx-decider card (Belebele, TruthfulQA, WinoGrande, Circa, FinancialPhraseBank, TabFact, RAGTruth, ContractNLI, JudgeBench, BBH, JevBench) | accuracy checks of the Qwen-architecture models | each set's own terms |
|
|
56
|
+
|
|
57
|
+
The news and moderation texts of our internal test bed are not redistributed; their sources are listed in that repository.
|
|
@@ -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 spinf Inc.
|
|
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.
|
spinf_serve-0.1.1/NOTICE
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
spinf-serve
|
|
2
|
+
Copyright 2026 spinf Inc.
|
|
3
|
+
|
|
4
|
+
This product includes software developed by third parties:
|
|
5
|
+
|
|
6
|
+
- flash-linear-attention (fla-core 0.5.2), MIT License, Copyright (c) 2023-2026 Songlin Yang, Yu Zhang, Zhiyuan Li.
|
|
7
|
+
spinf_serve/models/qwen35/gdn.py vendors its Gated DeltaNet chunk state kernel (chunk_gated_delta_rule_fwd_kernel_h_blockdim64)
|
|
8
|
+
with changes (initial and final states addressed by row index).
|
|
9
|
+
|
|
10
|
+
The model checkpoints this engine runs are distributed by their authors under their own terms (see ACKNOWLEDGEMENTS.md);
|
|
11
|
+
none are included in this repository.
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: spinf-serve
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: The open-source inference engine for decision models: one input read once, hundreds of questions scored in parallel on one GPU
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
License-File: NOTICE
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Requires-Dist: pydantic<3,>=2.11
|
|
10
|
+
Provides-Extra: engine
|
|
11
|
+
Requires-Dist: flash-linear-attention==0.5.2; extra == 'engine'
|
|
12
|
+
Requires-Dist: huggingface-hub==1.30.0; extra == 'engine'
|
|
13
|
+
Requires-Dist: numpy==2.5.2; extra == 'engine'
|
|
14
|
+
Requires-Dist: pillow>=11; extra == 'engine'
|
|
15
|
+
Requires-Dist: safetensors==0.8.0; extra == 'engine'
|
|
16
|
+
Requires-Dist: tokenizers==0.23.2; extra == 'engine'
|
|
17
|
+
Requires-Dist: torch==2.14.0; extra == 'engine'
|
|
18
|
+
Requires-Dist: transformers==5.16.1; extra == 'engine'
|
|
19
|
+
Requires-Dist: triton==3.8.0; extra == 'engine'
|
|
20
|
+
Provides-Extra: server
|
|
21
|
+
Requires-Dist: fastapi>=0.118; extra == 'server'
|
|
22
|
+
Requires-Dist: flash-linear-attention==0.5.2; extra == 'server'
|
|
23
|
+
Requires-Dist: huggingface-hub==1.30.0; extra == 'server'
|
|
24
|
+
Requires-Dist: numpy==2.5.2; extra == 'server'
|
|
25
|
+
Requires-Dist: pillow>=11; extra == 'server'
|
|
26
|
+
Requires-Dist: safetensors==0.8.0; extra == 'server'
|
|
27
|
+
Requires-Dist: tokenizers==0.23.2; extra == 'server'
|
|
28
|
+
Requires-Dist: torch==2.14.0; extra == 'server'
|
|
29
|
+
Requires-Dist: transformers==5.16.1; extra == 'server'
|
|
30
|
+
Requires-Dist: triton==3.8.0; extra == 'server'
|
|
31
|
+
Requires-Dist: uvicorn>=0.37; extra == 'server'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# spinf-serve
|
|
35
|
+
|
|
36
|
+
**The open-source inference engine for decision models.**
|
|
37
|
+
|
|
38
|
+
spinf-serve reads a piece of content once and answers hundreds of questions about it in parallel, as probabilities
|
|
39
|
+
rather than text. It turns a standard open model (Gemma 4, Qwen3.8) into a decision model through **decision mode**:
|
|
40
|
+
one input, many questions, scores instead of generation, and the same score whatever else is in the batch. It runs on
|
|
41
|
+
one 24 GB GPU and serves an OpenAI-compatible API that is also the API of the hosted spinf service.
|
|
42
|
+
|
|
43
|
+
## What it does
|
|
44
|
+
|
|
45
|
+
- **Scoring.** A question is a template that ends where the answer goes, with the answers to score there. The engine
|
|
46
|
+
returns the probability of each answer, its log-probability, and the same question's probability on empty content
|
|
47
|
+
(the floor), so a score can be read as a lift over the question's own bias.
|
|
48
|
+
- **Many questions, one read.** The content is read once; every question is a branch of the same pass. Adding a
|
|
49
|
+
question costs its own tokens only. Placeholders turn one template into a grid of questions.
|
|
50
|
+
- **Extraction and generation** from the same read: `{*}` text, `{n}` numbers and `{$}` amounts decoded with their
|
|
51
|
+
probabilities, or free text generated into a slot.
|
|
52
|
+
- **Batch invariance.** With the calibrated fp8 scheme, an input's scores are bit-identical whether it is scored
|
|
53
|
+
alone or packed with thousands of others. Scores compare across calls, days and servers; a `system_fingerprint`
|
|
54
|
+
says when they do not.
|
|
55
|
+
- **Media.** Images, audio and video frames in the content, read by the models that support them.
|
|
56
|
+
- **Prompt packs.** Ready-made, calibrated question sets preloaded into the server and named in a request
|
|
57
|
+
([below](#prompt-packs)).
|
|
58
|
+
- **Throughput.** Inputs are packed into 16k-token passes on one GPU: about 10,000 tokens per second on an RTX 4090
|
|
59
|
+
with the 12B model.
|
|
60
|
+
- **One API, hosted or self-hosted.** The request and response are those of `api.spinf.com`; a client switches by
|
|
61
|
+
changing the base URL and the key.
|
|
62
|
+
|
|
63
|
+
## Speed and scale
|
|
64
|
+
|
|
65
|
+
spinf-serve exists for one workload: **many questions about one content**. Our own first use, a daily oil-market
|
|
66
|
+
index read from 15 million news articles, taught us that asking thousands of narrow, calibrated questions of every
|
|
67
|
+
article gives a far more accurate read than any single prompt, and that no engine built for chat could do it at a
|
|
68
|
+
price that made sense. A chat engine re-reads the content for every question; even with prefix caching it generates
|
|
69
|
+
one token at a time per prompt and treats every question as a separate request. spinf-serve reads the content once
|
|
70
|
+
and lays every question out as a branch of that read, packed with other inputs into 16k-token passes on one GPU.
|
|
71
|
+
|
|
72
|
+
Measured on one RTX 4090 with Gemma 4 12B in fp8, the same 1,344 questions (24 news articles of about 470 tokens, 56
|
|
73
|
+
questions each, every answer a probability over two options: the public job set in `fixtures/`) sent over HTTP to
|
|
74
|
+
spinf-serve and to **vLLM 0.26 with prefix caching**, which is what most people would reach for (`benchmarks/`):
|
|
75
|
+
|
|
76
|
+
| one RTX 4090, warm, per second | spinf-serve | vLLM 0.26, prefix caching, 128 requests in flight |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| questions answered | **491** | 200 |
|
|
79
|
+
| tokens of work processed (content once, then each question's own tokens) | 11,200 | 4,600 |
|
|
80
|
+
| the same result for an engine that re-reads the content per question | 236,000 tokens | 96,000 tokens |
|
|
81
|
+
| the 1,344 questions | **2.7 s** | 6.7 s |
|
|
82
|
+
|
|
83
|
+
2.5 times faster on the same card for the same answers, and vLLM is already doing the right thing here: its prefix
|
|
84
|
+
cache reuses each article across its 56 questions. What it cannot do is answer the questions in one forward pass, so it
|
|
85
|
+
runs 1,344 single-token requests against the cache, while spinf-serve lays the 56 questions out as branches of one read
|
|
86
|
+
and packs the 24 articles into two 16k-token passes. The gap grows with the number of questions per content: at 56
|
|
87
|
+
questions the shared read saves 21 times the tokens; at a thousand it saves hundreds of times, and a thousand questions
|
|
88
|
+
on one content still cost about what the questions themselves weigh. Processed-token throughput stays at the engine's
|
|
89
|
+
rate, about 11,000 per second per 4090 for the 12B model (on an earlier set of 18 shorter articles with 74 questions each
|
|
90
|
+
the ratio was 2.9). The int4 Qwen3.8 27B model showed the same ratio range (2.2 to 2.8 times vLLM with prefix caching) on
|
|
91
|
+
2,211 decision-benchmark jobs in our internal measurements. Anyone can rerun the comparison: `benchmarks/README.md`.
|
|
92
|
+
|
|
93
|
+
What this means for a job:
|
|
94
|
+
|
|
95
|
+
- **Content moderation**: the `spinf/moderation` pack asks 106 questions of every message (22 categories, three
|
|
96
|
+
decision modes). One 4090 moderates about 20,000 messages per hour through the full pack, each message scored for
|
|
97
|
+
every category in one read.
|
|
98
|
+
- **News and document analysis**: 74 calibrated questions per article, aggregated into indexes, at nearly 700
|
|
99
|
+
questions per second per GPU.
|
|
100
|
+
- **Triage, routing, tagging**: intent in two levels, urgency, sentiment, risk flags, personal-data checks and a
|
|
101
|
+
policy check in one pass, each a question in the same list, each costing its own tokens only.
|
|
102
|
+
|
|
103
|
+
**Extraction and generation** ride the same read but add decoding steps: a searched slot (`{*}` `{n}` `{$}`) costs
|
|
104
|
+
about as much GPU time as 200 to 600 tokens of scoring, a generated slot up to about 2,000. They still benefit from
|
|
105
|
+
the shared read and the packing: the content and the questions are not re-read, the slots of many inputs are decoded
|
|
106
|
+
together, and a few slots next to hundreds of scored questions change the pass's cost only a little.
|
|
107
|
+
|
|
108
|
+
The example packs (triage, product listings, entertainment metadata, reviews, brand analysis) are in
|
|
109
|
+
[spinf-prompts](https://github.com/spinfinc/spinf-prompts).
|
|
110
|
+
|
|
111
|
+
## Prompt packs
|
|
112
|
+
|
|
113
|
+
A specialised use case is rarely one question: content moderation is 106 of them, each calibrated against normal
|
|
114
|
+
traffic and combined into category scores and a decision. spinf-serve standardises a format for this: a **prompt pack**
|
|
115
|
+
is one file holding a set of questions, how to present the content to the model, how the answers turn into scores,
|
|
116
|
+
and how the scores turn into a decision. Packs are preloaded into the server, so a request names one (`"packs": [{"id":
|
|
117
|
+
"spinf/moderation"}]`) instead of carrying hundreds of questions over and over, and gets the pack's categories and
|
|
118
|
+
decision back next to its own questions, from the same read of the content.
|
|
119
|
+
|
|
120
|
+
Because a pack is a file, it is also a **ready-to-go, shareable set**: the server loads packs from
|
|
121
|
+
[spinf-prompts](https://github.com/spinfinc/spinf-prompts), the official repository (content moderation first), and
|
|
122
|
+
from any GitHub repository or local folder you point it at, enabling the ones written for the model it serves. Publish
|
|
123
|
+
your own with a manifest at the root of your repository. Details, the file format and the request fields:
|
|
124
|
+
[docs/prompt-packs.md](docs/prompt-packs.md).
|
|
125
|
+
|
|
126
|
+
## Install and run
|
|
127
|
+
|
|
128
|
+
Linux, an NVIDIA GPU with 24 GB (fp8 tensor cores for the batch-invariant scheme: RTX 4090 / 5090, L4, L40S, RTX 6000
|
|
129
|
+
Ada, H100), a driver supporting CUDA 13, Python 3.12.
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
pip install "spinf-serve[server]"
|
|
133
|
+
spinf-serve serve spinf/gemma-4-12b-fp8 # downloads the checkpoint on first start; serves on port 8000
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Then ask. Two classifications and one extraction, answered from a single read of the content:
|
|
137
|
+
|
|
138
|
+
```sh
|
|
139
|
+
curl http://localhost:8000/v1/score \
|
|
140
|
+
-H "Content-Type: application/json" \
|
|
141
|
+
-d '{
|
|
142
|
+
"model": "spinf/gemma-4-12b-fp8",
|
|
143
|
+
"messages": [
|
|
144
|
+
{ "role": "user",
|
|
145
|
+
"content": "Customer support message:\nI was charged twice for September, please refund one of the charges." }
|
|
146
|
+
],
|
|
147
|
+
"scoring": {
|
|
148
|
+
"queries": [
|
|
149
|
+
{ "id": "topic",
|
|
150
|
+
"template": "\n\nTopic (billing, technical, account, shipping, or cancellation):{?}",
|
|
151
|
+
"options": [" billing", " technical", " account", " shipping", " cancellation"] },
|
|
152
|
+
{ "id": "refund",
|
|
153
|
+
"template": "\n\nIs the customer asking for a refund?\nAnswer:{?}",
|
|
154
|
+
"options": [" yes", " no"] },
|
|
155
|
+
{ "id": "month",
|
|
156
|
+
"template": "\n\nQ: Which month was charged twice?\nA: \"{*}\"" }
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
}'
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The response gives, for `topic` and `refund`, the probability of each answer and the same probability on empty
|
|
163
|
+
content (the lift between the two is what the message says), and for `month` the most likely answers with their
|
|
164
|
+
probabilities. The same body, with an API key, runs against the hosted API at `api.spinf.com`; the OpenAI SDKs work
|
|
165
|
+
through `/v1/chat/completions` with the `scoring` block in `extra_body` ([quickstart](docs/quickstart.md)).
|
|
166
|
+
|
|
167
|
+
As a library: `from spinf_serve import Engine; Engine("spinf/gemma-4-12b-fp8").score(request)`.
|
|
168
|
+
|
|
169
|
+
## Models
|
|
170
|
+
|
|
171
|
+
| name | based on | GPU | reads |
|
|
172
|
+
|---|---|---|---|
|
|
173
|
+
| `spinf/gemma-4-12b-fp8` (default) | Google Gemma 4 12B, FP8 with static activation scales | one 24 GB | text, images, audio, video |
|
|
174
|
+
| `spinf/gemma-4-31b-fp8` | Google Gemma 4 31B, FP8 static | one 48 GB | text, images, video |
|
|
175
|
+
| `spinf/qwen3.8-27b-int4` | Qwen3.8-27B, GPTQ int4 | one 24 GB | text, images, video |
|
|
176
|
+
| `spinf/pplx-decider-v1-27b-int4` | Perplexity pplx-decider-v1-27b, GPTQ int4: a decision model with a 255-code readout | one 24 GB | text; scores only, with its own prompts and packs |
|
|
177
|
+
|
|
178
|
+
Each is a checkpoint we quantized and calibrated, published on the Hugging Face Hub with its recipe
|
|
179
|
+
(`quantizations/`); the engine pins the revision it was parity-tested on. Any other Hub checkpoint of a supported
|
|
180
|
+
architecture family runs by its path, quantized at load time.
|
|
181
|
+
|
|
182
|
+
## Documentation
|
|
183
|
+
|
|
184
|
+
[docs/](docs/README.md): [quickstart](docs/quickstart.md), [examples](docs/examples.md) (curl calls for one question,
|
|
185
|
+
parallel classifications, classification plus extraction, many contents, a prompt pack),
|
|
186
|
+
[scoring API](docs/scoring-api.md), [configuration](docs/configuration.md), [writing questions](docs/writing-questions.md),
|
|
187
|
+
[calibration](docs/calibration.md), [prompt packs](docs/prompt-packs.md), [FAQ](docs/faq.md); the [guides](docs/guides/index.md)
|
|
188
|
+
(decision models, probabilities you can act on, few-shot classification, sentiment, survey coding, moderation, intent routing)
|
|
189
|
+
and the engineering documents ([docs/README.md](docs/README.md#engineering-documents)).
|
|
190
|
+
|
|
191
|
+
## Status
|
|
192
|
+
|
|
193
|
+
The engine (scoring, extraction, generation, media, batch invariance, the two model stacks) is complete and
|
|
194
|
+
parity-tested against recorded references. The server speaks the public API of the hosted service: `spinf-serve serve`,
|
|
195
|
+
`/v1/score` and its chat-completions alias, `/v1/models`, `/v1/packs`, pack sources, the response and error envelopes.
|
|
196
|
+
The `spinf/...` checkpoints are being published; [docs/README.md](docs/README.md#status) keeps the list of what is
|
|
197
|
+
left. How the engine works and the measurements behind it: the engineering documents in
|
|
198
|
+
[docs/](docs/README.md#engineering-documents) (architecture, batch invariance, precision, the Qwen stack, extraction).
|
|
199
|
+
|
|
200
|
+
## Layout
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
spinf_serve/
|
|
204
|
+
protocol.py, compiler.py, packer.py, scorer.py, decode.py, extract.py, media.py, service.py
|
|
205
|
+
the engine: request shape, templates to token tries, packing, one packed forward, extraction
|
|
206
|
+
manifests/ the served models (TOML): pinned checkpoint, family, schemes, capabilities, media, limits
|
|
207
|
+
packs/ prompt packs (pure Python): the file and its validation, the evaluator, a request's packs, the sources
|
|
208
|
+
(GitHub, folders, the official repository) and the catalog; `python -m spinf_serve.packs validate | list`
|
|
209
|
+
models/ one stack per architecture family behind one interface (base.py, registry.py): gemma4.py, qwen35/
|
|
210
|
+
quant/ the quantization schemes (linears, kernels, the fp8-static layout), the per-revision tables, the maintainer
|
|
211
|
+
commands that produce our checkpoints
|
|
212
|
+
server/ the HTTP server (FastAPI, one process per GPU): the public API around the engine (api.py), the settings
|
|
213
|
+
(config.py: flags > environment > config file > manifest), the app
|
|
214
|
+
cli.py the `spinf-serve` command: serve, score, packs, parity, calibrate
|
|
215
|
+
quantizations/ one folder per published checkpoint: recipe, calibration output, model card
|
|
216
|
+
fixtures/ the public parity fixture (24 Wikinews articles x 56 cells) and one recorded reference per checkpoint
|
|
217
|
+
benchmarks/ the throughput comparison script (spinf-serve against a completions server), its results, the fixture builder
|
|
218
|
+
tests/ CPU tests (run in CI); the GPU parity gate runs by hand (below)
|
|
219
|
+
docs/ the user guides and the engineering documents
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Develop
|
|
223
|
+
|
|
224
|
+
```sh
|
|
225
|
+
uv sync --extra server
|
|
226
|
+
uv run pytest # CPU tests (the official packs' golden fixtures need ../spinf-prompts or SPINF_PROMPTS_DIR)
|
|
227
|
+
uv run ruff check . && uv run ruff format --check .
|
|
228
|
+
CUDA_VISIBLE_DEVICES=0 uv run spinf-serve parity --fixture fixtures/wikinews_20261024.json.gz \
|
|
229
|
+
--reference fixtures/wikinews_20261024.spinf-gemma-4-12b-fp8.json.gz --passes 2
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Every change that touches a kernel, a compile boundary or a quantization scheme ships with the GPU parity run:
|
|
233
|
+
bit-identical log-probabilities on the recorded reference of each published checkpoint ([fixtures/](fixtures/README.md):
|
|
234
|
+
24 Wikinews articles x 56 two-option cells, a reference per model recorded and verified on its card). Scores come from
|
|
235
|
+
exact dependency pins; they change only together with a parity run.
|
|
236
|
+
|
|
237
|
+
## Licence and acknowledgements
|
|
238
|
+
|
|
239
|
+
Apache 2.0 (`LICENSE`, `NOTICE`). The models we run and re-quantize, the projects we build on, the techniques we
|
|
240
|
+
implement and the data we checked on are credited in [ACKNOWLEDGEMENTS.md](ACKNOWLEDGEMENTS.md).
|