jevify 0.1.0__tar.gz → 0.3.0__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.
- jevify-0.3.0/.github/workflows/publish.yml +133 -0
- jevify-0.3.0/Makefile +50 -0
- {jevify-0.1.0 → jevify-0.3.0}/PKG-INFO +70 -10
- {jevify-0.1.0 → jevify-0.3.0}/README.md +69 -9
- {jevify-0.1.0 → jevify-0.3.0}/SKILL.md +53 -8
- {jevify-0.1.0 → jevify-0.3.0}/docs/00-invariants.md +2 -1
- {jevify-0.1.0 → jevify-0.3.0}/docs/adr/0002-backend-port-and-adapter-kinds.md +53 -4
- {jevify-0.1.0 → jevify-0.3.0}/docs/adr/0003-jev-compatible-api.md +30 -3
- {jevify-0.1.0 → jevify-0.3.0}/docs/adr/0004-recipes.md +1 -0
- jevify-0.3.0/docs/adr/0007-release-publishing.md +65 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/adr/README.md +2 -1
- jevify-0.3.0/docs/playbook.md +303 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/__init__.py +1 -1
- {jevify-0.1.0 → jevify-0.3.0}/jevify/adapters/endpoint/adapter.py +82 -30
- {jevify-0.1.0 → jevify-0.3.0}/jevify/adapters/endpoint/client.py +82 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/adapters/endpoint/dialects.py +53 -7
- {jevify-0.1.0 → jevify-0.3.0}/jevify/adapters/endpoint/probe.py +175 -45
- {jevify-0.1.0 → jevify-0.3.0}/jevify/adapters/endpoint/readout.py +138 -9
- {jevify-0.1.0 → jevify-0.3.0}/jevify/cli.py +2 -2
- {jevify-0.1.0 → jevify-0.3.0}/jevify/compose.py +25 -30
- {jevify-0.1.0 → jevify-0.3.0}/jevify/domain/distribution.py +6 -5
- {jevify-0.1.0 → jevify-0.3.0}/jevify/ports/backend.py +5 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/recipes/schema.py +61 -1
- {jevify-0.1.0 → jevify-0.3.0}/pyproject.toml +1 -1
- jevify-0.3.0/recipes/qwen38-27b.vllm.yaml +186 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/fakes/fake_openai_server.py +169 -4
- jevify-0.3.0/tests/fixtures/recipes/fake-tensorfold-messages.yaml +41 -0
- jevify-0.3.0/tests/fixtures/recipes/fake-tensorfold.yaml +39 -0
- jevify-0.3.0/tests/guards/test_request_fields.py +105 -0
- jevify-0.3.0/tests/guards/test_rung_registry.py +27 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_distribution.py +8 -3
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_endpoint_adapter.py +61 -0
- jevify-0.3.0/tests/test_endpoint_client.py +112 -0
- jevify-0.3.0/tests/test_prompt_logprobs_messages.py +149 -0
- jevify-0.3.0/tests/test_prompt_logprobs_rung.py +149 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_recipes.py +75 -0
- jevify-0.3.0/tools/play_doom.py +393 -0
- {jevify-0.1.0 → jevify-0.3.0}/tools/record_doom_suite.py +7 -1
- {jevify-0.1.0 → jevify-0.3.0}/uv.lock +11 -11
- jevify-0.1.0/tools/play_doom.py +0 -209
- {jevify-0.1.0 → jevify-0.3.0}/.github/workflows/ci.yml +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/.gitignore +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/AGENTS.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/LICENSE +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/adr/0001-founding-choices.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/adr/0005-evaluation-evidence-and-calibration.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/adr/0006-toolchain-dependency-budget-and-portability.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/bart-large-mnli.local--policy-29.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/bart-large-mnli.local--policy-29.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/bart-large-mnli.local--policy-hard-52.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/bart-large-mnli.local--policy-hard-52.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--calibration.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--calibration.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--doom-frames-41.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--doom-frames-41.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--long-state-24.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--long-state-24.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--policy-29.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--policy-29.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--policy-hard-52.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--policy-hard-52.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/gemma-4-e4b-it.llamacpp--doom-frames-41.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/gemma-4-e4b-it.llamacpp--doom-frames-41.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/gemma-4-e4b-it.llamacpp--long-state-24.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/gemma-4-e4b-it.llamacpp--long-state-24.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/gemma-4-e4b-it.llamacpp--policy-29.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/gemma-4-e4b-it.llamacpp--policy-29.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/gemma-4-e4b-it.llamacpp--policy-hard-52.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/gemma-4-e4b-it.llamacpp--policy-hard-52.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/qwen3-embedding-0.6b.local--policy-29.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/qwen3-embedding-0.6b.local--policy-29.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/qwen3-embedding-0.6b.local--policy-hard-52.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/qwen3-embedding-0.6b.local--policy-hard-52.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/qwen3-vl-reranker-2b.llamacpp--doom-frames-41.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/qwen3-vl-reranker-2b.llamacpp--doom-frames-41.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/qwen3-vl-reranker-2b.llamacpp--policy-29.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/qwen3-vl-reranker-2b.llamacpp--policy-29.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/qwen3-vl-reranker-2b.llamacpp--policy-hard-52.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/qwen3-vl-reranker-2b.llamacpp--policy-hard-52.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/qwen3-vl-reranker-8b.llamacpp--policy-29.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/qwen3-vl-reranker-8b.llamacpp--policy-29.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/ternary-bonsai-2-27b.llamacpp--doom-frames-41.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/ternary-bonsai-2-27b.llamacpp--doom-frames-41.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/ternary-bonsai-2-27b.llamacpp--long-state-24.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/ternary-bonsai-2-27b.llamacpp--long-state-24.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/ternary-bonsai-2-27b.llamacpp--policy-29.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/ternary-bonsai-2-27b.llamacpp--policy-29.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/ternary-bonsai-2-27b.llamacpp--policy-hard-52.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/docs/evidence/ternary-bonsai-2-27b.llamacpp--policy-hard-52.md +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/__main__.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/adapters/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/adapters/embedding/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/adapters/embedding/adapter.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/adapters/embedding/local.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/adapters/encoder/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/adapters/encoder/adapter.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/adapters/encoder/local.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/adapters/endpoint/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/adapters/rerank/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/adapters/rerank/adapter.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/api/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/api/app.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/api/schema.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/api/translate.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/domain/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/domain/engine.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/domain/questions.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/evaluation/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/evaluation/calibrate.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/evaluation/controls.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/evaluation/metrics.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/evaluation/scorecard.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/evaluation/suites.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/ports/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/recipes/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/recipes/probe_case.yaml +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/recipes/render.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/jevify/recipes/store.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/recipes/bart-large-mnli.local.yaml +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/recipes/deepseek-v4-flash-vision-exp-keys.vllm.yaml +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/recipes/gemma-4-e4b-it.llamacpp.yaml +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/recipes/qwen3-embedding-0.6b.local.yaml +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/recipes/qwen3-vl-reranker-2b.llamacpp.yaml +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/recipes/qwen3-vl-reranker-8b.llamacpp.yaml +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/recipes/ternary-bonsai-2-27b.llamacpp.yaml +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/suites/doom-frames-41/doom-frames-41.jsonl +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/suites/doom-frames-41/doom-frames-41.manifest.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/suites/long-state-24.jsonl +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/suites/long-state-24.manifest.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/suites/policy-29.jsonl +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/suites/policy-29.manifest.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/suites/policy-hard-52.jsonl +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/suites/policy-hard-52.manifest.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/conftest.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/fakes/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/fakes/fake_backend.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/fixtures/recipes/fake-embedding.yaml +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/fixtures/recipes/fake-encoder.yaml +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/fixtures/recipes/fake-raw.yaml +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/fixtures/recipes/fake-rerank.yaml +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/fixtures/recipes/fake-vllm.yaml +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/fixtures/recipes/local-embedding.yaml +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/fixtures/recipes/local-encoder.yaml +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/fixtures/responses/llamacpp-completions-grammar.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/fixtures/responses/llamacpp-completions-top_k.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/fixtures/responses/llamacpp-native-completion.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/fixtures/responses/vllm-chat-top_k.json +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/guards/test_prompt_literals.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/guards/test_sdk_schema_mirror.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/guards/test_structure.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/live/test_live_ask.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/live/test_live_sdk.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_api.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_calibrate.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_classify_and_states.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_cli.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_controls.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_embedding_adapter.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_encoder_adapter.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_engine.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_evaluation.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_llamacpp_dialect.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_local_embedding.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_multimodal.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_probe.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_readout_ladder.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_rerank_adapter.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_sdk_acceptance.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_translate.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tests/test_warm.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tools/author_long_state.py +0 -0
- {jevify-0.1.0 → jevify-0.3.0}/tools/author_policy_hard.py +0 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# ADR-0007: a published GitHub release is built here from its tag and sent
|
|
2
|
+
# to PyPI by trusted publishing. No token is stored anywhere.
|
|
3
|
+
name: publish
|
|
4
|
+
|
|
5
|
+
on:
|
|
6
|
+
release:
|
|
7
|
+
types: [published]
|
|
8
|
+
# Dry run against an existing tag: builds, checks and compares, never publishes.
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
inputs:
|
|
11
|
+
tag:
|
|
12
|
+
description: "Release tag to build and check, e.g. v0.2.0"
|
|
13
|
+
required: true
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
concurrency:
|
|
19
|
+
group: publish-${{ inputs.tag || github.event.release.tag_name }}
|
|
20
|
+
cancel-in-progress: false
|
|
21
|
+
|
|
22
|
+
env:
|
|
23
|
+
TAG: ${{ inputs.tag || github.event.release.tag_name }}
|
|
24
|
+
|
|
25
|
+
jobs:
|
|
26
|
+
build:
|
|
27
|
+
name: build and check
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
outputs:
|
|
30
|
+
version: ${{ steps.version.outputs.version }}
|
|
31
|
+
attach: ${{ steps.compare.outputs.attach }}
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
with:
|
|
35
|
+
ref: refs/tags/${{ env.TAG }}
|
|
36
|
+
- uses: astral-sh/setup-uv@v6
|
|
37
|
+
with:
|
|
38
|
+
python-version: "3.12"
|
|
39
|
+
- name: Tag matches the package version
|
|
40
|
+
id: version
|
|
41
|
+
run: |
|
|
42
|
+
version="$(uv version --short)"
|
|
43
|
+
if [ "v${version}" != "${TAG}" ]; then
|
|
44
|
+
echo "::error::tag ${TAG} does not match version ${version} in pyproject.toml"
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
echo "version=${version}" >> "$GITHUB_OUTPUT"
|
|
48
|
+
- name: Build
|
|
49
|
+
run: uv build
|
|
50
|
+
- name: twine check
|
|
51
|
+
run: uvx twine check --strict dist/*
|
|
52
|
+
- name: Install the wheel as a tool and run it from another directory
|
|
53
|
+
env:
|
|
54
|
+
VERSION: ${{ steps.version.outputs.version }}
|
|
55
|
+
run: |
|
|
56
|
+
uv tool install dist/*.whl
|
|
57
|
+
cd "${RUNNER_TEMP}"
|
|
58
|
+
got="$(jevify --version)"
|
|
59
|
+
if [ "${got}" != "jevify ${VERSION}" ]; then
|
|
60
|
+
echo "::error::installed wheel reports '${got}', expected 'jevify ${VERSION}'"
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
|
63
|
+
- name: Compare with the files attached to the release
|
|
64
|
+
id: compare
|
|
65
|
+
env:
|
|
66
|
+
GH_TOKEN: ${{ github.token }}
|
|
67
|
+
GH_REPO: ${{ github.repository }}
|
|
68
|
+
run: |
|
|
69
|
+
attached_names="$(gh release view "${TAG}" --json assets \
|
|
70
|
+
-q '.assets[].name | select(endswith(".whl") or endswith(".tar.gz"))')"
|
|
71
|
+
if [ -z "${attached_names}" ]; then
|
|
72
|
+
echo "No wheel or sdist attached to ${TAG}; this build is attached after publishing."
|
|
73
|
+
echo "attach=true" >> "$GITHUB_OUTPUT"
|
|
74
|
+
exit 0
|
|
75
|
+
fi
|
|
76
|
+
dir="${RUNNER_TEMP}/attached"
|
|
77
|
+
mkdir -p "${dir}"
|
|
78
|
+
gh release download "${TAG}" -p '*.whl' -p '*.tar.gz' -D "${dir}"
|
|
79
|
+
built="$(cd dist && sha256sum -- *.whl *.tar.gz)"
|
|
80
|
+
attached="$(cd "${dir}" && sha256sum -- *.whl *.tar.gz)"
|
|
81
|
+
if [ "${built}" != "${attached}" ]; then
|
|
82
|
+
echo "::error::the files attached to ${TAG} differ from this build of the tag"
|
|
83
|
+
echo "built:"; echo "${built}"
|
|
84
|
+
echo "attached:"; echo "${attached}"
|
|
85
|
+
exit 1
|
|
86
|
+
fi
|
|
87
|
+
echo "Attached files are byte-identical to this build:"
|
|
88
|
+
echo "${built}"
|
|
89
|
+
echo "attach=false" >> "$GITHUB_OUTPUT"
|
|
90
|
+
- uses: actions/upload-artifact@v4
|
|
91
|
+
with:
|
|
92
|
+
name: dist
|
|
93
|
+
path: dist/
|
|
94
|
+
if-no-files-found: error
|
|
95
|
+
|
|
96
|
+
publish:
|
|
97
|
+
name: publish to PyPI
|
|
98
|
+
needs: build
|
|
99
|
+
if: github.event_name == 'release'
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
environment:
|
|
102
|
+
name: pypi
|
|
103
|
+
url: https://pypi.org/project/jevify/${{ needs.build.outputs.version }}/
|
|
104
|
+
permissions:
|
|
105
|
+
id-token: write
|
|
106
|
+
steps:
|
|
107
|
+
- uses: actions/download-artifact@v4
|
|
108
|
+
with:
|
|
109
|
+
name: dist
|
|
110
|
+
path: dist/
|
|
111
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
112
|
+
|
|
113
|
+
attach:
|
|
114
|
+
name: attach build to the release
|
|
115
|
+
needs: [build, publish]
|
|
116
|
+
if: github.event_name == 'release' && needs.build.outputs.attach == 'true'
|
|
117
|
+
runs-on: ubuntu-latest
|
|
118
|
+
permissions:
|
|
119
|
+
contents: write
|
|
120
|
+
steps:
|
|
121
|
+
- uses: actions/download-artifact@v4
|
|
122
|
+
with:
|
|
123
|
+
name: dist
|
|
124
|
+
path: dist/
|
|
125
|
+
- name: Upload wheel, sdist and SHA256SUMS
|
|
126
|
+
env:
|
|
127
|
+
GH_TOKEN: ${{ github.token }}
|
|
128
|
+
GH_REPO: ${{ github.repository }}
|
|
129
|
+
run: |
|
|
130
|
+
cd dist
|
|
131
|
+
sha256sum -- *.whl *.tar.gz > SHA256SUMS
|
|
132
|
+
cat SHA256SUMS
|
|
133
|
+
gh release upload "${TAG}" *.whl *.tar.gz SHA256SUMS
|
jevify-0.3.0/Makefile
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Shortcuts for the two everyday jevify commands: `make ask` asks typed questions
|
|
2
|
+
# about one state and prints the answer as JSON; `make serve` serves Jev's HTTP API
|
|
3
|
+
# for one recipe. Values reach jevify through the environment, so a state may hold
|
|
4
|
+
# spaces, quotes and apostrophes. `make help` lists the variables and the recipes.
|
|
5
|
+
|
|
6
|
+
JEVIFY ?= uv run jevify
|
|
7
|
+
HOST ?= 127.0.0.1
|
|
8
|
+
PORT ?= 8600
|
|
9
|
+
|
|
10
|
+
export RECIPE STATE CHOICE SCORE NOUL IMAGE RUNG HOST PORT
|
|
11
|
+
|
|
12
|
+
.DEFAULT_GOAL := help
|
|
13
|
+
.PHONY: help ask serve
|
|
14
|
+
|
|
15
|
+
help:
|
|
16
|
+
@echo "make ask RECIPE=<file> STATE=<text|@file> [CHOICE=...] [SCORE=...] [NOUL=...] [IMAGE=<png|jpg|webp>] [RUNG=<rung>]"
|
|
17
|
+
@echo "make serve RECIPE=<file> [HOST=$(HOST)] [PORT=$(PORT)]"
|
|
18
|
+
@echo ""
|
|
19
|
+
@echo " CHOICE id:question=option1,option2,... one option from a menu"
|
|
20
|
+
@echo " SCORE id:question=level0,level1,... a position on ordered levels, low to high"
|
|
21
|
+
@echo " NOUL id:statement the probability that the statement is true"
|
|
22
|
+
@echo " Give at least one of CHOICE, SCORE, NOUL. For several of one type, call jevify ask directly."
|
|
23
|
+
@echo " make reads \$$ in a value: write \$$\$$49.99 for \$$49.99, or put the text in a file and pass STATE=@path."
|
|
24
|
+
@echo " HOST other than 127.0.0.1 needs JEVIFY_API_KEY set: clients must then send it as a bearer key."
|
|
25
|
+
@echo ""
|
|
26
|
+
@echo "Example:"
|
|
27
|
+
@echo " make ask RECIPE=recipes/gemma-4-e4b-it.llamacpp.yaml \\"
|
|
28
|
+
@echo " STATE=\"Customer: I was charged twice. Fix it today or I cancel.\" \\"
|
|
29
|
+
@echo " CHOICE=\"dept:Which team should handle this?=billing,support,sales\" \\"
|
|
30
|
+
@echo " SCORE=\"urgency:How urgent is this?=low,medium,high\" \\"
|
|
31
|
+
@echo " NOUL=\"cancel:The customer threatens to cancel.\""
|
|
32
|
+
@echo ""
|
|
33
|
+
@echo "Recipes:"
|
|
34
|
+
@ls recipes/*.yaml | sed 's/^/ /'
|
|
35
|
+
|
|
36
|
+
ask:
|
|
37
|
+
@test -n "$$RECIPE" || { echo "make ask: set RECIPE=recipes/<name>.yaml (make help lists them)" >&2; exit 2; }
|
|
38
|
+
@test -n "$$STATE" || { echo "make ask: set STATE=\"...\" (the text, or @path to a file)" >&2; exit 2; }
|
|
39
|
+
@set -- --state "$$STATE"; \
|
|
40
|
+
[ -z "$$CHOICE" ] || set -- "$$@" --choice "$$CHOICE"; \
|
|
41
|
+
[ -z "$$SCORE" ] || set -- "$$@" --score "$$SCORE"; \
|
|
42
|
+
[ -z "$$NOUL" ] || set -- "$$@" --noul "$$NOUL"; \
|
|
43
|
+
[ -z "$$IMAGE" ] || set -- "$$@" --image "$$IMAGE"; \
|
|
44
|
+
[ -z "$$RUNG" ] || set -- "$$@" --rung "$$RUNG"; \
|
|
45
|
+
$(JEVIFY) ask "$$RECIPE" "$$@"
|
|
46
|
+
|
|
47
|
+
serve:
|
|
48
|
+
@test -n "$$RECIPE" || { echo "make serve: set RECIPE=recipes/<name>.yaml (make help lists them)" >&2; exit 2; }
|
|
49
|
+
@case "$$HOST" in 127.0.0.1|localhost|::1) ;; *) test -n "$$JEVIFY_API_KEY" || { echo "make serve: HOST=$$HOST opens the API to the network; export JEVIFY_API_KEY first" >&2; exit 2; } ;; esac
|
|
50
|
+
$(JEVIFY) serve "$$RECIPE" --host "$$HOST" --port "$$PORT"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: jevify
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Make a pretrained model you already have answer typed questions in one pass, and measure how well it does.
|
|
5
5
|
Project-URL: Homepage, https://github.com/fidecastro/jevify
|
|
6
6
|
Author: Felipe Infante de Castro
|
|
@@ -39,7 +39,8 @@ Jev does, and measure how well it does.**
|
|
|
39
39
|
[](https://www.python.org/)
|
|
40
40
|
|
|
41
41
|
You bring a model behind an OpenAI-compatible endpoint (vLLM, llama.cpp,
|
|
42
|
-
anything
|
|
42
|
+
TensorFold, anything that returns logprobs for a reply token or for the
|
|
43
|
+
prompt) or a small model that runs in-process. jevify sends it
|
|
43
44
|
a **state** once, then asks **typed questions** about it: a choice among
|
|
44
45
|
options, a score on ordered levels, a yes/no with a probability. Each answer
|
|
45
46
|
is read straight off the model's next-token distribution, no generation, no
|
|
@@ -81,10 +82,36 @@ jevify ask recipes/gemma-4-e4b-it.llamacpp.yaml \
|
|
|
81
82
|
jevify serve recipes/gemma-4-e4b-it.llamacpp.yaml --port 8600
|
|
82
83
|
```
|
|
83
84
|
|
|
85
|
+
Any model behind an OpenAI-compatible server works, not only the ones under
|
|
86
|
+
`recipes/`: copy the nearest recipe, set `endpoint.base_url` and
|
|
87
|
+
`model.name`, and run the probe, which re-verifies the answer token ids for
|
|
88
|
+
that model and writes what it found into the file. vLLM and llama.cpp get
|
|
89
|
+
their dialect extras; any other server is driven through the plain OpenAI
|
|
90
|
+
surface. A server that refuses logprobs on the reply token but returns
|
|
91
|
+
vLLM's `prompt_logprobs`, such as TensorFold's native server with
|
|
92
|
+
GLM-5.3-Flash, is read through the `prompt_logprobs` rung on
|
|
93
|
+
`/v1/completions`: with a raw-mode recipe (the template written out with the
|
|
94
|
+
model's own control tokens), or with a chat-template recipe where the server
|
|
95
|
+
also takes `messages` on that route, as TensorFold's does. The chat form is
|
|
96
|
+
the one that carries images.
|
|
97
|
+
|
|
98
|
+
`serve` binds to `127.0.0.1`. To reach it from other machines pass
|
|
99
|
+
`--host 0.0.0.0`, and set `JEVIFY_API_KEY` first so that every request must
|
|
100
|
+
carry that bearer key; to leave it running, detach it and keep its log:
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
jevify serve recipes/<name>.yaml --host 0.0.0.0 --port 8600 > ~/jevify.log 2>&1 &
|
|
104
|
+
curl -s http://<this-host>:8600/health
|
|
105
|
+
```
|
|
106
|
+
|
|
84
107
|
Every answer carries Jev's fields (`choice`, `confidence`, `probabilities`,
|
|
85
108
|
`score`, `legend`, `noul`) plus an `x_jevify` block naming the readout method,
|
|
86
109
|
the cached tokens, the latency and the recipe hash that produced it.
|
|
87
110
|
|
|
111
|
+
From a clone, `make ask` and `make serve` wrap the two commands;
|
|
112
|
+
[`docs/playbook.md`](https://github.com/fidecastro/jevify/blob/main/docs/playbook.md)
|
|
113
|
+
walks through them and explains every answer field.
|
|
114
|
+
|
|
88
115
|
## How it works
|
|
89
116
|
|
|
90
117
|
1. **Recipe.** A hashed YAML file: the endpoint, the prompt template (chat
|
|
@@ -99,7 +126,9 @@ the cached tokens, the latency and the recipe hash that produced it.
|
|
|
99
126
|
that reuses the server's prefix cache. A 32k-token state costs one warm and
|
|
100
127
|
then a few hundred milliseconds per question.
|
|
101
128
|
4. **Readout ladder.** Named-token logprobs, grammar with post-sampling
|
|
102
|
-
probabilities, top-k,
|
|
129
|
+
probabilities, top-k, prompt logprobs (each answer token appended to the
|
|
130
|
+
prompt and its logprob read back, for servers that give none on the reply
|
|
131
|
+
token), equal bias, and a floored top-k as the last resort.
|
|
103
132
|
Auto walks down the proven rungs; the answer names the rung that read it
|
|
104
133
|
and whether it was degraded.
|
|
105
134
|
5. **Evidence.** `jevify eval` runs a frozen suite with controls (shuffled
|
|
@@ -110,7 +139,7 @@ the cached tokens, the latency and the recipe hash that produced it.
|
|
|
110
139
|
|
|
111
140
|
| Kind | Talks to | Proven live with |
|
|
112
141
|
|---|---|---|
|
|
113
|
-
| `endpoint` | Any OpenAI-compatible server; dialect extras for vLLM and llama.cpp | DeepSeek-V4-Flash on vLLM; Ternary Bonsai 2 27B, Gemma 4 E4B and Qwen3-VL-Reranker 8B/2B on llama.cpp; text and images |
|
|
142
|
+
| `endpoint` | Any OpenAI-compatible server that returns reply-token or prompt logprobs; dialect extras for vLLM and llama.cpp | DeepSeek-V4-Flash on vLLM; Ternary Bonsai 2 27B, Gemma 4 E4B and Qwen3-VL-Reranker 8B/2B on llama.cpp; text and images |
|
|
114
143
|
| `embedding` | A `/v1/embeddings` route, or a model in-process | Qwen3-Embedding-0.6B in-process |
|
|
115
144
|
| `encoder` | An NLI sequence-classification head in-process | facebook/bart-large-mnli in-process |
|
|
116
145
|
| `rerank` | A `/v1/rerank` route | Fakes only |
|
|
@@ -123,6 +152,28 @@ Four frozen suites, every recipe on each, one run each, latencies per
|
|
|
123
152
|
question on one machine on one day. Every row links to the scorecard that
|
|
124
153
|
records the command, the recipe hash, the suite hash and the raw file digest.
|
|
125
154
|
|
|
155
|
+
### Latency per question
|
|
156
|
+
|
|
157
|
+
The median of each suite, combined across the suites a model ran; one run
|
|
158
|
+
each. A question is one request that reads the answer off the next-token
|
|
159
|
+
probabilities, after the state was sent once and cached.
|
|
160
|
+
|
|
161
|
+
| Model | How it runs | Hardware | Latency per question |
|
|
162
|
+
|---|---|---|---|
|
|
163
|
+
| facebook/bart-large-mnli | in-process NLI classifier | RTX 4090 | 7–9 ms, avg 8 |
|
|
164
|
+
| Qwen3-Embedding-0.6B | in-process embedding, cosine | RTX 4090 | 8–10 ms, avg 9 |
|
|
165
|
+
| Gemma 4 E4B (Q8_0) | llama.cpp, chat | RTX 4090 | 28–37 ms, avg 32 |
|
|
166
|
+
| Qwen3-VL-Reranker-2B (Q8_0) | llama.cpp, yes/no readout | RTX 4090 | 47–64 ms, avg 53 |
|
|
167
|
+
| Qwen3-VL-Reranker-8B (Q8_0) | llama.cpp, yes/no readout | RTX 4090 | 68 ms (one suite) |
|
|
168
|
+
| Ternary Bonsai 2 27B (PQ2_0) | llama.cpp (PrismML fork), raw prompt | RTX 4090 | 97–113 ms, avg 102 |
|
|
169
|
+
| DeepSeek-V4-Flash | vLLM, chat | 2× DGX Spark, over LAN | 359–672 ms, avg 483 |
|
|
170
|
+
|
|
171
|
+
Image questions cost the llama.cpp models nothing extra, since the frame is
|
|
172
|
+
paid once in the warm; DeepSeek pays about half again on images. The two
|
|
173
|
+
in-process models are the fastest and the least accurate.
|
|
174
|
+
|
|
175
|
+
### Accuracy by suite
|
|
176
|
+
|
|
126
177
|
**policy-29**, the diagnostic suite carried over from the author's earlier experiments (choice only):
|
|
127
178
|
|
|
128
179
|
| Recipe | Kind | Accuracy | Median latency | Scorecard |
|
|
@@ -221,6 +272,8 @@ jevify is written to be operated through a coding agent. Point yours at
|
|
|
221
272
|
| [`tools/`](https://github.com/fidecastro/jevify/blob/main/tools) | Suite authoring, the Doom recorder and player |
|
|
222
273
|
| [`AGENTS.md`](https://github.com/fidecastro/jevify/blob/main/AGENTS.md) | The engineering principles that bind every change |
|
|
223
274
|
| [`SKILL.md`](https://github.com/fidecastro/jevify/blob/main/SKILL.md) | How an agent operates jevify |
|
|
275
|
+
| [`docs/playbook.md`](https://github.com/fidecastro/jevify/blob/main/docs/playbook.md) | How a person operates jevify: ask, read an answer, serve, add a model, fix common problems |
|
|
276
|
+
| [`Makefile`](https://github.com/fidecastro/jevify/blob/main/Makefile) | `make ask` and `make serve` shortcuts; `make help` lists the variables and recipes |
|
|
224
277
|
|
|
225
278
|
To work on jevify: `uv sync --group dev` (add `--extra encoder --extra
|
|
226
279
|
games` as needed) and `uv run pytest`. Live and in-process tests skip unless
|
|
@@ -229,14 +282,21 @@ you opt in.
|
|
|
229
282
|
## Not yet
|
|
230
283
|
|
|
231
284
|
Named so that nobody has to discover it: a direct-call control, so no
|
|
232
|
-
"faster than generation" claim exists yet;
|
|
233
|
-
|
|
285
|
+
"faster than generation" claim exists yet; clearing Doom's `deadly_corridor`
|
|
286
|
+
(in `defend_the_center` Bonsai's perception plus a fixed rule scores 10 kills
|
|
287
|
+
in 16 seconds, but in the corridor even the rule fed the engine's own labels
|
|
288
|
+
dies before the armour, so the corridor needs a policy with memory, not
|
|
289
|
+
better perception); the rerank kind and the
|
|
234
290
|
embedding route against a live server; a marker-slot encoder layout; the
|
|
235
291
|
grammar rung on the PrismML llama.cpp fork, which accepts a grammar without
|
|
236
|
-
constraining the reported probabilities;
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
292
|
+
constraining the reported probabilities; the `prompt_logprobs` rung on any
|
|
293
|
+
server but TensorFold's native one with GLM-5.3-Flash, where it has been
|
|
294
|
+
probed and asked by hand (raw and chat recipes, text and images) but no
|
|
295
|
+
evaluation suite has been scored on it yet (that server skips its prefix
|
|
296
|
+
cache for such requests, so every question reads the whole state again,
|
|
297
|
+
images included, which the probe measures and notes); Windows. Choice menus on the endpoint kind are bounded by the
|
|
298
|
+
single-token identifier alphabet the probe verifies (26 on the DeepSeek
|
|
299
|
+
recipe); Jev's 255-option ceiling is not reachable there.
|
|
240
300
|
|
|
241
301
|
## Licence
|
|
242
302
|
|
|
@@ -9,7 +9,8 @@ Jev does, and measure how well it does.**
|
|
|
9
9
|
[](https://www.python.org/)
|
|
10
10
|
|
|
11
11
|
You bring a model behind an OpenAI-compatible endpoint (vLLM, llama.cpp,
|
|
12
|
-
anything
|
|
12
|
+
TensorFold, anything that returns logprobs for a reply token or for the
|
|
13
|
+
prompt) or a small model that runs in-process. jevify sends it
|
|
13
14
|
a **state** once, then asks **typed questions** about it: a choice among
|
|
14
15
|
options, a score on ordered levels, a yes/no with a probability. Each answer
|
|
15
16
|
is read straight off the model's next-token distribution, no generation, no
|
|
@@ -51,10 +52,36 @@ jevify ask recipes/gemma-4-e4b-it.llamacpp.yaml \
|
|
|
51
52
|
jevify serve recipes/gemma-4-e4b-it.llamacpp.yaml --port 8600
|
|
52
53
|
```
|
|
53
54
|
|
|
55
|
+
Any model behind an OpenAI-compatible server works, not only the ones under
|
|
56
|
+
`recipes/`: copy the nearest recipe, set `endpoint.base_url` and
|
|
57
|
+
`model.name`, and run the probe, which re-verifies the answer token ids for
|
|
58
|
+
that model and writes what it found into the file. vLLM and llama.cpp get
|
|
59
|
+
their dialect extras; any other server is driven through the plain OpenAI
|
|
60
|
+
surface. A server that refuses logprobs on the reply token but returns
|
|
61
|
+
vLLM's `prompt_logprobs`, such as TensorFold's native server with
|
|
62
|
+
GLM-5.3-Flash, is read through the `prompt_logprobs` rung on
|
|
63
|
+
`/v1/completions`: with a raw-mode recipe (the template written out with the
|
|
64
|
+
model's own control tokens), or with a chat-template recipe where the server
|
|
65
|
+
also takes `messages` on that route, as TensorFold's does. The chat form is
|
|
66
|
+
the one that carries images.
|
|
67
|
+
|
|
68
|
+
`serve` binds to `127.0.0.1`. To reach it from other machines pass
|
|
69
|
+
`--host 0.0.0.0`, and set `JEVIFY_API_KEY` first so that every request must
|
|
70
|
+
carry that bearer key; to leave it running, detach it and keep its log:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
jevify serve recipes/<name>.yaml --host 0.0.0.0 --port 8600 > ~/jevify.log 2>&1 &
|
|
74
|
+
curl -s http://<this-host>:8600/health
|
|
75
|
+
```
|
|
76
|
+
|
|
54
77
|
Every answer carries Jev's fields (`choice`, `confidence`, `probabilities`,
|
|
55
78
|
`score`, `legend`, `noul`) plus an `x_jevify` block naming the readout method,
|
|
56
79
|
the cached tokens, the latency and the recipe hash that produced it.
|
|
57
80
|
|
|
81
|
+
From a clone, `make ask` and `make serve` wrap the two commands;
|
|
82
|
+
[`docs/playbook.md`](https://github.com/fidecastro/jevify/blob/main/docs/playbook.md)
|
|
83
|
+
walks through them and explains every answer field.
|
|
84
|
+
|
|
58
85
|
## How it works
|
|
59
86
|
|
|
60
87
|
1. **Recipe.** A hashed YAML file: the endpoint, the prompt template (chat
|
|
@@ -69,7 +96,9 @@ the cached tokens, the latency and the recipe hash that produced it.
|
|
|
69
96
|
that reuses the server's prefix cache. A 32k-token state costs one warm and
|
|
70
97
|
then a few hundred milliseconds per question.
|
|
71
98
|
4. **Readout ladder.** Named-token logprobs, grammar with post-sampling
|
|
72
|
-
probabilities, top-k,
|
|
99
|
+
probabilities, top-k, prompt logprobs (each answer token appended to the
|
|
100
|
+
prompt and its logprob read back, for servers that give none on the reply
|
|
101
|
+
token), equal bias, and a floored top-k as the last resort.
|
|
73
102
|
Auto walks down the proven rungs; the answer names the rung that read it
|
|
74
103
|
and whether it was degraded.
|
|
75
104
|
5. **Evidence.** `jevify eval` runs a frozen suite with controls (shuffled
|
|
@@ -80,7 +109,7 @@ the cached tokens, the latency and the recipe hash that produced it.
|
|
|
80
109
|
|
|
81
110
|
| Kind | Talks to | Proven live with |
|
|
82
111
|
|---|---|---|
|
|
83
|
-
| `endpoint` | Any OpenAI-compatible server; dialect extras for vLLM and llama.cpp | DeepSeek-V4-Flash on vLLM; Ternary Bonsai 2 27B, Gemma 4 E4B and Qwen3-VL-Reranker 8B/2B on llama.cpp; text and images |
|
|
112
|
+
| `endpoint` | Any OpenAI-compatible server that returns reply-token or prompt logprobs; dialect extras for vLLM and llama.cpp | DeepSeek-V4-Flash on vLLM; Ternary Bonsai 2 27B, Gemma 4 E4B and Qwen3-VL-Reranker 8B/2B on llama.cpp; text and images |
|
|
84
113
|
| `embedding` | A `/v1/embeddings` route, or a model in-process | Qwen3-Embedding-0.6B in-process |
|
|
85
114
|
| `encoder` | An NLI sequence-classification head in-process | facebook/bart-large-mnli in-process |
|
|
86
115
|
| `rerank` | A `/v1/rerank` route | Fakes only |
|
|
@@ -93,6 +122,28 @@ Four frozen suites, every recipe on each, one run each, latencies per
|
|
|
93
122
|
question on one machine on one day. Every row links to the scorecard that
|
|
94
123
|
records the command, the recipe hash, the suite hash and the raw file digest.
|
|
95
124
|
|
|
125
|
+
### Latency per question
|
|
126
|
+
|
|
127
|
+
The median of each suite, combined across the suites a model ran; one run
|
|
128
|
+
each. A question is one request that reads the answer off the next-token
|
|
129
|
+
probabilities, after the state was sent once and cached.
|
|
130
|
+
|
|
131
|
+
| Model | How it runs | Hardware | Latency per question |
|
|
132
|
+
|---|---|---|---|
|
|
133
|
+
| facebook/bart-large-mnli | in-process NLI classifier | RTX 4090 | 7–9 ms, avg 8 |
|
|
134
|
+
| Qwen3-Embedding-0.6B | in-process embedding, cosine | RTX 4090 | 8–10 ms, avg 9 |
|
|
135
|
+
| Gemma 4 E4B (Q8_0) | llama.cpp, chat | RTX 4090 | 28–37 ms, avg 32 |
|
|
136
|
+
| Qwen3-VL-Reranker-2B (Q8_0) | llama.cpp, yes/no readout | RTX 4090 | 47–64 ms, avg 53 |
|
|
137
|
+
| Qwen3-VL-Reranker-8B (Q8_0) | llama.cpp, yes/no readout | RTX 4090 | 68 ms (one suite) |
|
|
138
|
+
| Ternary Bonsai 2 27B (PQ2_0) | llama.cpp (PrismML fork), raw prompt | RTX 4090 | 97–113 ms, avg 102 |
|
|
139
|
+
| DeepSeek-V4-Flash | vLLM, chat | 2× DGX Spark, over LAN | 359–672 ms, avg 483 |
|
|
140
|
+
|
|
141
|
+
Image questions cost the llama.cpp models nothing extra, since the frame is
|
|
142
|
+
paid once in the warm; DeepSeek pays about half again on images. The two
|
|
143
|
+
in-process models are the fastest and the least accurate.
|
|
144
|
+
|
|
145
|
+
### Accuracy by suite
|
|
146
|
+
|
|
96
147
|
**policy-29**, the diagnostic suite carried over from the author's earlier experiments (choice only):
|
|
97
148
|
|
|
98
149
|
| Recipe | Kind | Accuracy | Median latency | Scorecard |
|
|
@@ -191,6 +242,8 @@ jevify is written to be operated through a coding agent. Point yours at
|
|
|
191
242
|
| [`tools/`](https://github.com/fidecastro/jevify/blob/main/tools) | Suite authoring, the Doom recorder and player |
|
|
192
243
|
| [`AGENTS.md`](https://github.com/fidecastro/jevify/blob/main/AGENTS.md) | The engineering principles that bind every change |
|
|
193
244
|
| [`SKILL.md`](https://github.com/fidecastro/jevify/blob/main/SKILL.md) | How an agent operates jevify |
|
|
245
|
+
| [`docs/playbook.md`](https://github.com/fidecastro/jevify/blob/main/docs/playbook.md) | How a person operates jevify: ask, read an answer, serve, add a model, fix common problems |
|
|
246
|
+
| [`Makefile`](https://github.com/fidecastro/jevify/blob/main/Makefile) | `make ask` and `make serve` shortcuts; `make help` lists the variables and recipes |
|
|
194
247
|
|
|
195
248
|
To work on jevify: `uv sync --group dev` (add `--extra encoder --extra
|
|
196
249
|
games` as needed) and `uv run pytest`. Live and in-process tests skip unless
|
|
@@ -199,14 +252,21 @@ you opt in.
|
|
|
199
252
|
## Not yet
|
|
200
253
|
|
|
201
254
|
Named so that nobody has to discover it: a direct-call control, so no
|
|
202
|
-
"faster than generation" claim exists yet;
|
|
203
|
-
|
|
255
|
+
"faster than generation" claim exists yet; clearing Doom's `deadly_corridor`
|
|
256
|
+
(in `defend_the_center` Bonsai's perception plus a fixed rule scores 10 kills
|
|
257
|
+
in 16 seconds, but in the corridor even the rule fed the engine's own labels
|
|
258
|
+
dies before the armour, so the corridor needs a policy with memory, not
|
|
259
|
+
better perception); the rerank kind and the
|
|
204
260
|
embedding route against a live server; a marker-slot encoder layout; the
|
|
205
261
|
grammar rung on the PrismML llama.cpp fork, which accepts a grammar without
|
|
206
|
-
constraining the reported probabilities;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
262
|
+
constraining the reported probabilities; the `prompt_logprobs` rung on any
|
|
263
|
+
server but TensorFold's native one with GLM-5.3-Flash, where it has been
|
|
264
|
+
probed and asked by hand (raw and chat recipes, text and images) but no
|
|
265
|
+
evaluation suite has been scored on it yet (that server skips its prefix
|
|
266
|
+
cache for such requests, so every question reads the whole state again,
|
|
267
|
+
images included, which the probe measures and notes); Windows. Choice menus on the endpoint kind are bounded by the
|
|
268
|
+
single-token identifier alphabet the probe verifies (26 on the DeepSeek
|
|
269
|
+
recipe); Jev's 255-option ceiling is not reachable there.
|
|
210
270
|
|
|
211
271
|
## Licence
|
|
212
272
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jevify
|
|
3
|
-
description: Operate jevify, the bring-your-own-model harness that makes a pretrained model answer Jev-shaped typed questions (choice, score, noul) over a shared state and measures how well it does. Use when asked to connect a model behind an OpenAI-compatible endpoint (vLLM, llama.cpp, others) or an in-process encoder or embedding model, write or probe a recipe, ask questions, run a suite, read a scorecard, calibrate, or serve Jev's API.
|
|
3
|
+
description: Operate jevify, the bring-your-own-model harness that makes a pretrained model answer Jev-shaped typed questions (choice, score, noul) over a shared state and measures how well it does. Use when asked to connect a model behind an OpenAI-compatible endpoint (vLLM, llama.cpp, TensorFold, others) or an in-process encoder or embedding model, write or probe a recipe, ask questions, run a suite, read a scorecard, calibrate, or serve Jev's API.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Operating jevify
|
|
@@ -31,7 +31,9 @@ result.
|
|
|
31
31
|
rows carry `images`, regenerate the frames with
|
|
32
32
|
`uv run python tools/record_doom_suite.py` after `uv sync --extra games`),
|
|
33
33
|
`long-state-24` (2k to 32k tokens). `tools/play_doom.py <recipe> --scenario
|
|
34
|
-
defend_the_center --policy perception` records a video of a recipe playing
|
|
34
|
+
defend_the_center --policy perception` records a video of a recipe playing;
|
|
35
|
+
`--policy expert` replays the same rule from the engine's labels, the
|
|
36
|
+
ceiling any perception can reach with that rule.
|
|
35
37
|
Raw decisions land in `runs/` (git-ignored); the summary in `docs/evidence/`
|
|
36
38
|
(committed). Read the Markdown summary, then the `wrong` list in the JSON.
|
|
37
39
|
5. **Serve**: `jevify serve recipes/<name>.yaml --port 8600`. Jev's SDK works
|
|
@@ -51,15 +53,37 @@ Every recipe has `schema_version: 1`, `model: {name, kind, revision}` and
|
|
|
51
53
|
the rest.
|
|
52
54
|
|
|
53
55
|
**`endpoint`** (any OpenAI-compatible server): `endpoint: {base_url,
|
|
54
|
-
api_key_env, dialect: auto|vllm|llamacpp|generic, timeout_s
|
|
55
|
-
and `answers`.
|
|
56
|
+
api_key_env, dialect: auto|vllm|llamacpp|generic, timeout_s, extra_body,
|
|
57
|
+
headers, headers_env}`, `template` and `answers`.
|
|
56
58
|
- `template.mode: messages` sends chat messages; put thinking off in
|
|
57
59
|
`template.kwargs` (`{thinking: false}` for DeepSeek, `{enable_thinking:
|
|
58
60
|
false}` for Qwen3-class models; names are model-specific, the probe checks
|
|
59
61
|
that no `<think>` token opens the answer).
|
|
62
|
+
- `endpoint.extra_body` is merged into the top level of every request, for
|
|
63
|
+
servers that take a switch there rather than as a template argument. The
|
|
64
|
+
`generic` dialect sends no template arguments, so on Ollama's `/v1` route
|
|
65
|
+
a thinking model needs `extra_body: {reasoning_effort: none}` (`think:
|
|
66
|
+
false` is ignored there). A key jevify itself sets (`max_tokens`,
|
|
67
|
+
`logprobs`, `logit_bias`, ...) is refused when the backend is built, before
|
|
68
|
+
any request, so probe, ask, eval and serve all fail the same way. The
|
|
69
|
+
fields also reach the tokenize and template routes the probe uses.
|
|
70
|
+
- `endpoint.headers` adds static HTTP headers to every request, for a router
|
|
71
|
+
or gateway that requires one (`{x-client-id: agenticApp}`). The values are
|
|
72
|
+
written in the recipe and hashed with it, so never put a secret there: a
|
|
73
|
+
bearer key goes in the env var that `api_key_env` names, and any other
|
|
74
|
+
secret header in `endpoint.headers_env` (header name to env var name, read
|
|
75
|
+
when the backend is built; an unset variable fails then). A static header
|
|
76
|
+
whose name looks like a credential (`key`, `token`, `secret`, `auth`,
|
|
77
|
+
`cookie`, ...) is refused when the recipe loads; `Content-Type` and
|
|
78
|
+
`Authorization` are refused when the backend is built. Every
|
|
79
|
+
endpoint-backed kind sends them (endpoint, rerank, embedding).
|
|
60
80
|
- `template.mode: raw` sends a completion prompt you write with the model's
|
|
61
81
|
own control tokens; use it when the chat template would not put the model
|
|
62
|
-
in its trained regime (the Qwen3-VL reranker recipe is the example)
|
|
82
|
+
in its trained regime (the Qwen3-VL reranker recipe is the example), and
|
|
83
|
+
for a server that refuses reply-token logprobs (TensorFold's native server
|
|
84
|
+
with GLM-5.3-Flash) when the state is text. The `prompt_logprobs` rung also
|
|
85
|
+
reads a messages-mode recipe where the server takes `messages` on
|
|
86
|
+
`/v1/completions` (TensorFold's does); that is the form that carries images.
|
|
63
87
|
- `template.questions.choice_strategy`: `identifiers` (one request, options
|
|
64
88
|
labelled A, B, C) or `per_option` (one yes/no request per option; needed
|
|
65
89
|
for a model that never emits letters, such as a reranker).
|
|
@@ -67,8 +91,12 @@ and `answers`.
|
|
|
67
91
|
the probe fills their ids.
|
|
68
92
|
- `readout.rung: auto` walks the rungs the probe proved, strongest first:
|
|
69
93
|
`named_token_logprobs` (vLLM with `logprob_token_ids`), `grammar`
|
|
70
|
-
(llama.cpp), `top_k`, `
|
|
71
|
-
|
|
94
|
+
(llama.cpp), `top_k`, `prompt_logprobs` (each answer spelling appended to
|
|
95
|
+
a raw prompt, or sent as the assistant's reply in messages mode, and read
|
|
96
|
+
back from vLLM-style `prompt_logprobs` rows, one
|
|
97
|
+
request more for every spelling the rows did not show; exact, off-menu mass
|
|
98
|
+
visible), `equal_bias`, `top_k_floor` (degraded, missing labels floored and
|
|
99
|
+
listed). A rung that cannot see every label hands the
|
|
72
100
|
question to the next; the answer names the rung that read it. A pinned rung
|
|
73
101
|
never walks: it fails loudly instead.
|
|
74
102
|
|
|
@@ -88,7 +116,10 @@ sequence-classification head; all three question types; semantics `readout`.
|
|
|
88
116
|
## Reading an answer
|
|
89
117
|
|
|
90
118
|
Jev's fields come first (`choice`, `confidence`, `probabilities`; `score`,
|
|
91
|
-
`legend`; `noul`).
|
|
119
|
+
`legend`; `noul`). `confidence` is Jev's statistic, `(n × peak − 1) / (n − 1)`
|
|
120
|
+
over the options or levels: 0 when uniform, 1 when certain, and the number
|
|
121
|
+
TypeSafe's published thresholds refer to. Everything jevify adds is under
|
|
122
|
+
`x_jevify`:
|
|
92
123
|
|
|
93
124
|
| Field | Meaning |
|
|
94
125
|
|---|---|
|
|
@@ -138,6 +169,20 @@ Jev's fields come first (`choice`, `confidence`, `probabilities`; `score`,
|
|
|
138
169
|
checkpoint at the end of an earlier prompt, and a chat template closes the
|
|
139
170
|
user turn right after the state. Use `template.mode: raw` so the warm prompt
|
|
140
171
|
ends at the state boundary; the Bonsai recipe is the example.
|
|
172
|
+
- **Probe notes "reply-token logprobs refused"**: the server gives no
|
|
173
|
+
logprobs on a generated token (TensorFold with GLM-5.3-Flash). The probe
|
|
174
|
+
then tries `prompt_logprobs`: a raw recipe on any such server, a chat
|
|
175
|
+
recipe where the server takes `messages` on `/v1/completions`; elsewhere a
|
|
176
|
+
chat recipe ends in "no readout is possible".
|
|
177
|
+
- **"is not at the prompt row the probe measured"**: in messages mode the
|
|
178
|
+
server's template renders the answer turn differently than when the probe
|
|
179
|
+
ran (thinking switched on or off, a new template); re-run the probe.
|
|
180
|
+
- **"did not come back as its own prompt token"**: the appended answer fuses
|
|
181
|
+
with the end of the raw template; end the template where the answer starts
|
|
182
|
+
a token of its own (after a newline, or before a leading-space spelling).
|
|
183
|
+
- **Probe notes "prompt_logprobs requests reuse no cached prefix"**: the
|
|
184
|
+
server reads the whole state again for every question on that rung; keep
|
|
185
|
+
states short there, or serve the model where reply-token logprobs exist.
|
|
141
186
|
- **Missing encoder extra**: the error names the install command.
|
|
142
187
|
|
|
143
188
|
## House rules that bind you here
|
|
@@ -126,7 +126,7 @@ API response may claim more than it permits.
|
|
|
126
126
|
|
|
127
127
|
Rules that follow:
|
|
128
128
|
|
|
129
|
-
- **Confidence** is
|
|
129
|
+
- **Confidence** is Jev's statistic of the returned distribution: `(n × peak − 1) / (n − 1)` over the n options or levels for choice and score, so a uniform distribution scores 0 and a certain one scores 1 (TypeSafe's published definition). Jev's noul answers carry no confidence; jevify reports the distance of P(true) from one half, scaled to [0, 1], under `x_jevify` only. It is not an independent estimate that the decision is right, and copy may not describe it as one.
|
|
130
130
|
- **"Faster"** may be claimed only against a measured direct call on the same backend, same model, same state, with the measurement in a scorecard.
|
|
131
131
|
- **"Accuracy"** names its suite and the suite's hash. A suite whose expected answers came from a teacher model measures agreement with that teacher, and copy says so.
|
|
132
132
|
- **A fine-tuned result is never compared with a zero-shot result in one table** without the words fine-tuned and zero-shot in the row.
|
|
@@ -150,6 +150,7 @@ number and the previous wording. The edit and the ADR land in the same commit.
|
|
|
150
150
|
| Date | Invariant | ADR | Change |
|
|
151
151
|
|---|---|---|---|
|
|
152
152
|
| 2026-09-19 | all | ADR-0001 | Adopted. |
|
|
153
|
+
| 2026-09-23 | §5, confidence rule | ADR-0003 D8 | Confidence is Jev's peak statistic, `(n × peak − 1) / (n − 1)`. Previous wording: "a statistic of the returned distribution: one minus its normalized entropy for choice and score, distance from one half for noul". The entropy statistic disagreed with Jev's published definition, so the SDK-facing `confidence` field did not carry Jev's meaning. |
|
|
153
154
|
|
|
154
155
|
## 8. Document map
|
|
155
156
|
|