Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[workspace]
members = ["src/frontend", "src/runtime", "src/models/clm", "src/models/cua_s1/native", "src/models/qwen3_5/native", "src/models/open_jev/native", "src/models/jev_vl/native", "src/models/omnijev/native", "src/models/laya", "src/backends/cuda", "src/models/decider/native"]
members = ["src/frontend", "src/runtime", "src/models/clm", "src/models/cua_s1/native", "src/models/qwen3_5/native", "src/models/open_jev/native", "src/models/pplx_decider/native", "src/models/jev_vl/native", "src/models/omnijev/native", "src/models/laya", "src/backends/cuda", "src/models/decider/native"]
resolver = "3"
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,9 @@ worker, or a native Rust/CUDA worker on Hopper. The Cua-S1 4B 0.2 `text` adapter
runs as a Python worker or as a native worker on CUDA. Open-Jev-27B-v1.1
and Open-Jev-9B run on the same native Rust/CUDA worker. Cua-S1 also has a
Python screenshot worker, Valen-Preview-0923 has a Python reference worker for
text and one-image state, CLM has a stub-encoder contract recipe, and OmniJev-4B
v1.1 has its checkpoint export and CPU contract, with no worker yet:
text and one-image state, CLM has a stub-encoder contract recipe, OmniJev-4B
v1.1 has its checkpoint export and CPU contract, and pplx-decider-v1.1-27b has its
CPU contract, both with no worker yet:

| Model | Status |
| --- | --- |
Expand All @@ -176,6 +177,7 @@ v1.1 has its checkpoint export and CPU contract, with no worker yet:
| CLM-v0.1-8B | [External worker with a CPU stub encoder](recipe/clm/README.md); contract checks only, real Qwen3-8B decisions unverified by this recipe |
| LFM2.5-350M | [Transformers worker](recipe/lfm2/README.md); text state and independent choice questions |
| OmniJev-4B v1.1 | [Checkpoint export and CPU contract](recipe/omnijev/README.md) only; the native worker is in progress ([#114](https://github.com/ThinkFlowLab/system1-omni/issues/114)) |
| pplx-decider-v1.1-27b | [CPU contract](recipe/pplx_decider/README.md) only, checked against the checkpoint's own code; no worker yet |

[Supported models and hardware](docs/supported-models.md) lists the devices
and where each worker has been run.
Expand Down
1 change: 1 addition & 0 deletions docs/supported-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Models that are being added are also tracked in issues labeled [new model](https
| LFM2.5-350M | [Transformers choice worker](../recipe/lfm2/README.md) | Tiny-model regression tests; full checkpoint unverified | L40S FP16 [recorded execution checks](../recipe/lfm2/RESULTS.md) | Unverified | Python 3.12, [pinned dependencies](../src/models/lfm2/requirements.lock.txt), explicit candidate batch size |
| CLM-v0.1-8B | [Native Rust engine](../src/models/clm/README.md), `clm-run`, in front of a Qwen3-8B `/v1/embeddings` server | Engine-only agreement **4.5e-06** on identical vectors ([#29](https://github.com/ThinkFlowLab/system1-omni/pull/29), [validation](../recipe/clm/native/VALIDATION.md)); contract checks documented ([#23](https://github.com/ThinkFlowLab/system1-omni/pull/23)) | **Validated on compute capability 8.9** ([#29](https://github.com/ThinkFlowLab/system1-omni/pull/29), [validation](../recipe/clm/native/VALIDATION.md)) | Unverified | Rust to build and a separate Qwen3-8B `/v1/embeddings` server for the encoder; the engine itself has no device-specific code |
| OmniJev-4B v1.1 | None yet: [checkpoint export and CPU contract](../recipe/omnijev/README.md); the native worker is in progress ([#114](https://github.com/ThinkFlowLab/system1-omni/issues/114)) | Request contract, preparation, heads and finishing checked against the reference ([#120](https://github.com/ThinkFlowLab/system1-omni/pull/120)); no inference | Not yet | Not supported | Python 3.10 reference environment for the export and fixtures |
| pplx-decider-v1.1-27b | None yet: [CPU contract](../recipe/pplx_decider/README.md); no worker | Request validation, prompts, tokenization and typed answers checked against the checkpoint's own code ([#140](https://github.com/ThinkFlowLab/system1-omni/pull/140)); no inference | Not yet | Not supported | Python 3.12 reference environment for the fixtures; a worker needs about 49 GiB for BF16 weights |

- **Validated:** covered by the recipe on `main` or by the checks in the linked merged pull request.
- **Author-run:** recorded by the recipe author on the stated hardware and not independently reproduced; the row names its owner.
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ nav:
- JEV-27B-VL experimental worker: recipe/jev_vl/README.md
- JEV-27B-VL validation status: recipe/jev_vl/validation.md
- OmniJev preparation: recipe/omnijev/README.md
- pplx-decider preparation: recipe/pplx_decider/README.md
- CLM stub-encoder contract: recipe/clm/README.md
- LFM2.5-350M choice worker: recipe/lfm2/README.md
- Contributing: CONTRIBUTING.md
2 changes: 2 additions & 0 deletions recipe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ For a first real decision, follow the [complete CPU walkthrough](../docs/getting
image decisions; bounded H800 validation with explicit deployment limits.
- [OmniJev-4B v1.1 preparation](omnijev/README.md): export the checkpoint and
regenerate the CPU fixtures; the native worker is not available yet.
- [pplx-decider-v1.1-27b preparation](pplx_decider/README.md): regenerate the CPU
fixtures from the checkpoint's own code; the native worker is not available yet.
- [CLM behind the frontend](clm/README.md): run CLM's own server behind the frontend on
CPU with a stub encoder, and what the response comparison has to allow for.

Expand Down
61 changes: 61 additions & 0 deletions recipe/pplx_decider/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# pplx-decider-v1.1-27b preparation

This recipe prepares [perplexity-ai/pplx-decider-v1.1-27b](https://huggingface.co/perplexity-ai/pplx-decider-v1.1-27b/tree/14c9d90137962d300f3de95633f7b6535e80c5ce)
for a native worker: it regenerates the CPU fixtures that the contract and tokenization tests use, and records the
hosted model's answers for a later parity check. The worker itself is not available yet;
[the model README](../../src/models/pplx_decider/README.md) describes what is implemented. Run the commands from the
repository root.

## Reference environment

The fixtures run the checkpoint's own `source/src/autojev` code on the CPU, without the weights; no GPU is needed.
They were generated with Python 3.12, whose `sum` adds floats with Neumaier compensation, as the reference's
`answer` relies on.

```bash
python3.12 -m venv .venv-pplx-decider
.venv-pplx-decider/bin/pip install --extra-index-url https://download.pytorch.org/whl/cpu \
torch==2.14.0 torchvision==0.29.0 transformers==5.17.0 pillow==12.3.0 fastapi==0.141.1 huggingface-hub==1.31.0
```

## Download the pinned files

Only the code, the tokenizer and the configuration; the 54 GB of weights are not needed here.

```bash
hf download perplexity-ai/pplx-decider-v1.1-27b --revision 14c9d90137962d300f3de95633f7b6535e80c5ce \
--include '*.json' '*.jinja' 'source/**' --local-dir pplx-decider
```

`tests/pplx_decider/make_fixtures.py` checks `model.py`, `server.py`, `tokenizer.json`, `chat_template.jinja` and
`decision_config.json` against their SHA-256 before it runs.

## Regenerate the fixtures

```bash
.venv-pplx-decider/bin/python tests/pplx_decider/make_fixtures.py pplx-decider tests/pplx_decider/data
```

It writes `tests/pplx_decider/data/contract.json`: 15 questions in 9 requests, one of them the recorded
System1-Agents Google Flights step in `data/flights_step.json`, and 11 requests the checkpoint's server rejects.

## Check

```bash
cargo test --locked -p omni-pplx-decider-native
PPLX_DECIDER_MODEL=pplx-decider cargo test --locked -p omni-pplx-decider-native --test tokenization -- --ignored
```

## Hosted answers (optional)

```bash
OPENROUTER_API_KEY=... python tests/pplx_decider/record_hosted.py tests/pplx_decider/data
```

This records `data/hosted.json`, what the hosted model answers to the same requests, for the executor's parity
check later. It costs a few thousandths of a cent.

## Limits

No worker, no inference and no images yet. A worker needs noncausal attention in the full-attention layers of the
shared Qwen3.5/3.8 prefill, the 255-row readout, and about 49 GiB of GPU memory for the BF16 weights.
58 changes: 58 additions & 0 deletions src/models/pplx_decider/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# pplx-decider-v1.1-27b

First step toward serving [perplexity-ai/pplx-decider-v1.1-27b](https://huggingface.co/perplexity-ai/pplx-decider-v1.1-27b/tree/14c9d90137962d300f3de95633f7b6535e80c5ce)
(Apache 2.0): a Qwen3.8-27B decision model that answers `choice`, `score` and `noul` from a 255-row readout at the
last position, with noncausal full attention and a fitted temperature. Its own server already speaks
`/v1/systemone`, so System1-Agents reaches it with no client change.

This crate holds the CPU half: the processor. There is no executor or worker binary yet; the
[preparation recipe](../../../recipe/pplx_decider/README.md) regenerates the fixtures.

- [`native/src/contract.rs`](native/src/contract.rs): request validation as the checkpoint's `server.EvaluationRequest`
does it (unknown fields, 1 to 255 choice options, 2 to 10 score levels, `noul` criteria keyed `true`/`false`
only, a string, object or array state); one prompt per question (`State:`, `Question:`, `Options:` with
`A: key: description` lines and the 255 one-token answer codes `A`..`Z`, `AA`..`JT`); and the typed answers from
the readout probabilities: the temperature and float32 softmax, then `answer` in float64 with Python 3.12's
Neumaier `sum`, nothing rounded.
- [`native/src/processing.rs`](native/src/processing.rs): the system and user messages in the checkpoint's chat
template with thinking off, the prompt token ids, the 8,192-token limit per question (rejected, never truncated),
and the readout rows with their vocabulary ids for an `lm_head` export. Loading checks `decision_config.json`
against the contract: format 1, codes, single-token ids, temperature, noncausal attention, last-token pooling.

## Reference

Everything follows `source/src/autojev` at 14c9d901 (`model.py` sha256 `02940963...`, `server.py` sha256
`84008893...`), the code the checkpoint ships as its evaluated implementation. Requests are accepted and rejected as
its server's request model does; the error messages are this crate's own.

## Limits (rejected with an error, not approximated)

- `images`: the vision lane is a later step.
- A prompt over 8,192 tokens.
- The executor must apply noncausal attention in the full-attention layers (the linear-attention layers keep their
recurrence) and read the last position; the shared Qwen3.5/3.8 prefill is causal today.

## Tests

`tests/pplx_decider/data/contract.json` is generated by
[`tests/pplx_decider/make_fixtures.py`](../../../tests/pplx_decider/make_fixtures.py), which runs the checkpoint's code
unmodified without the weights: the server's request model, the prompt, the processor and chat template, and
`answer` on fixed readout logits. It records 15 questions in 9 requests (one is a recorded System1-Agents Google
Flights step) and 11 requests the server model rejects.

`tests/pplx_decider/data/hosted.json` is what the hosted model (OpenRouter, `perplexity/pplx-decider-v1.1-27b`,
build 20261006) answered to the same requests, recorded by
[`tests/pplx_decider/record_hosted.py`](../../../tests/pplx_decider/record_hosted.py), for the executor's parity
check later. Its `input_tokens` equal this processor's on every accepted request once questions with a single
option are left out: the hosted service answers those without the model (probability 1, 0 tokens), which the
checkpoint's server does not do. OpenRouter rejected three of the requests (a `noul` without criteria, object score
levels), which the checkpoint's server accepts.

```sh
cargo test --locked -p omni-pplx-decider-native
# with the pinned tokenizer.json and decision_config.json in a folder (no weights):
PPLX_DECIDER_MODEL=path/to/pplx-decider cargo test --locked -p omni-pplx-decider-native --test tokenization -- --ignored
```

The checkpoint and its code are Apache 2.0; see [`native/LICENSE.pplx-decider`](native/LICENSE.pplx-decider) and
[`native/NOTICE.pplx-decider`](native/NOTICE.pplx-decider).
21 changes: 21 additions & 0 deletions src/models/pplx_decider/native/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "omni-pplx-decider-native"
version = "0.1.0"
edition = "2024"
publish = false
description = "pplx-decider-v1.1-27b decision contract: prompts, answer codes and calibrated typed answers"

[dependencies]
anyhow = "1.0.100"
omni-qwen3-5-native = { path = "../../qwen3_5/native" }
serde_json = { version = "1.0.149", features = ["float_roundtrip", "preserve_order"] }
# the onig regex backend, as in the Python tokenizers wheel
tokenizers = { version = "=0.22.2", default-features = false, features = ["onig"] }

[[test]]
name = "contract"
path = "../../../../tests/pplx_decider/contract.rs"

[[test]]
name = "tokenization"
path = "../../../../tests/pplx_decider/tokenization.rs"
Loading