jevify 0.1.0__tar.gz → 0.2.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.2.0/Makefile +50 -0
- {jevify-0.1.0 → jevify-0.2.0}/PKG-INFO +68 -10
- {jevify-0.1.0 → jevify-0.2.0}/README.md +67 -9
- {jevify-0.1.0 → jevify-0.2.0}/SKILL.md +46 -8
- {jevify-0.1.0 → jevify-0.2.0}/docs/00-invariants.md +2 -1
- {jevify-0.1.0 → jevify-0.2.0}/docs/adr/0002-backend-port-and-adapter-kinds.md +31 -4
- {jevify-0.1.0 → jevify-0.2.0}/docs/adr/0003-jev-compatible-api.md +30 -3
- {jevify-0.1.0 → jevify-0.2.0}/docs/adr/0004-recipes.md +1 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/adr/README.md +1 -1
- jevify-0.2.0/docs/playbook.md +301 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/__init__.py +1 -1
- {jevify-0.1.0 → jevify-0.2.0}/jevify/adapters/endpoint/adapter.py +47 -24
- {jevify-0.1.0 → jevify-0.2.0}/jevify/adapters/endpoint/client.py +82 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/adapters/endpoint/dialects.py +22 -2
- {jevify-0.1.0 → jevify-0.2.0}/jevify/adapters/endpoint/probe.py +103 -40
- {jevify-0.1.0 → jevify-0.2.0}/jevify/adapters/endpoint/readout.py +115 -9
- {jevify-0.1.0 → jevify-0.2.0}/jevify/cli.py +2 -2
- {jevify-0.1.0 → jevify-0.2.0}/jevify/compose.py +25 -30
- {jevify-0.1.0 → jevify-0.2.0}/jevify/domain/distribution.py +6 -5
- {jevify-0.1.0 → jevify-0.2.0}/jevify/ports/backend.py +2 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/recipes/schema.py +61 -1
- {jevify-0.1.0 → jevify-0.2.0}/pyproject.toml +1 -1
- jevify-0.2.0/recipes/qwen38-27b.vllm.yaml +186 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/fakes/fake_openai_server.py +86 -0
- jevify-0.2.0/tests/fixtures/recipes/fake-tensorfold.yaml +39 -0
- jevify-0.2.0/tests/guards/test_request_fields.py +91 -0
- jevify-0.2.0/tests/guards/test_rung_registry.py +27 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_distribution.py +8 -3
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_endpoint_adapter.py +61 -0
- jevify-0.2.0/tests/test_endpoint_client.py +112 -0
- jevify-0.2.0/tests/test_prompt_logprobs_rung.py +159 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_recipes.py +75 -0
- jevify-0.2.0/tools/play_doom.py +393 -0
- {jevify-0.1.0 → jevify-0.2.0}/tools/record_doom_suite.py +7 -1
- {jevify-0.1.0 → jevify-0.2.0}/uv.lock +1 -1
- jevify-0.1.0/tools/play_doom.py +0 -209
- {jevify-0.1.0 → jevify-0.2.0}/.github/workflows/ci.yml +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/.gitignore +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/AGENTS.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/LICENSE +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/adr/0001-founding-choices.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/adr/0005-evaluation-evidence-and-calibration.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/adr/0006-toolchain-dependency-budget-and-portability.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/bart-large-mnli.local--policy-29.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/bart-large-mnli.local--policy-29.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/bart-large-mnli.local--policy-hard-52.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/bart-large-mnli.local--policy-hard-52.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--calibration.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--calibration.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--doom-frames-41.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--doom-frames-41.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--long-state-24.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--long-state-24.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--policy-29.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--policy-29.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--policy-hard-52.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/deepseek-v4-flash-vision-exp-keys.vllm--policy-hard-52.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/gemma-4-e4b-it.llamacpp--doom-frames-41.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/gemma-4-e4b-it.llamacpp--doom-frames-41.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/gemma-4-e4b-it.llamacpp--long-state-24.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/gemma-4-e4b-it.llamacpp--long-state-24.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/gemma-4-e4b-it.llamacpp--policy-29.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/gemma-4-e4b-it.llamacpp--policy-29.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/gemma-4-e4b-it.llamacpp--policy-hard-52.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/gemma-4-e4b-it.llamacpp--policy-hard-52.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/qwen3-embedding-0.6b.local--policy-29.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/qwen3-embedding-0.6b.local--policy-29.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/qwen3-embedding-0.6b.local--policy-hard-52.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/qwen3-embedding-0.6b.local--policy-hard-52.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/qwen3-vl-reranker-2b.llamacpp--doom-frames-41.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/qwen3-vl-reranker-2b.llamacpp--doom-frames-41.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/qwen3-vl-reranker-2b.llamacpp--policy-29.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/qwen3-vl-reranker-2b.llamacpp--policy-29.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/qwen3-vl-reranker-2b.llamacpp--policy-hard-52.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/qwen3-vl-reranker-2b.llamacpp--policy-hard-52.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/qwen3-vl-reranker-8b.llamacpp--policy-29.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/qwen3-vl-reranker-8b.llamacpp--policy-29.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/ternary-bonsai-2-27b.llamacpp--doom-frames-41.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/ternary-bonsai-2-27b.llamacpp--doom-frames-41.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/ternary-bonsai-2-27b.llamacpp--long-state-24.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/ternary-bonsai-2-27b.llamacpp--long-state-24.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/ternary-bonsai-2-27b.llamacpp--policy-29.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/ternary-bonsai-2-27b.llamacpp--policy-29.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/ternary-bonsai-2-27b.llamacpp--policy-hard-52.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/docs/evidence/ternary-bonsai-2-27b.llamacpp--policy-hard-52.md +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/__main__.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/adapters/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/adapters/embedding/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/adapters/embedding/adapter.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/adapters/embedding/local.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/adapters/encoder/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/adapters/encoder/adapter.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/adapters/encoder/local.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/adapters/endpoint/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/adapters/rerank/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/adapters/rerank/adapter.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/api/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/api/app.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/api/schema.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/api/translate.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/domain/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/domain/engine.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/domain/questions.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/evaluation/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/evaluation/calibrate.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/evaluation/controls.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/evaluation/metrics.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/evaluation/scorecard.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/evaluation/suites.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/ports/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/recipes/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/recipes/probe_case.yaml +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/recipes/render.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/jevify/recipes/store.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/recipes/bart-large-mnli.local.yaml +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/recipes/deepseek-v4-flash-vision-exp-keys.vllm.yaml +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/recipes/gemma-4-e4b-it.llamacpp.yaml +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/recipes/qwen3-embedding-0.6b.local.yaml +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/recipes/qwen3-vl-reranker-2b.llamacpp.yaml +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/recipes/qwen3-vl-reranker-8b.llamacpp.yaml +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/recipes/ternary-bonsai-2-27b.llamacpp.yaml +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/suites/doom-frames-41/doom-frames-41.jsonl +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/suites/doom-frames-41/doom-frames-41.manifest.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/suites/long-state-24.jsonl +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/suites/long-state-24.manifest.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/suites/policy-29.jsonl +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/suites/policy-29.manifest.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/suites/policy-hard-52.jsonl +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/suites/policy-hard-52.manifest.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/conftest.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/fakes/__init__.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/fakes/fake_backend.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/fixtures/recipes/fake-embedding.yaml +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/fixtures/recipes/fake-encoder.yaml +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/fixtures/recipes/fake-raw.yaml +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/fixtures/recipes/fake-rerank.yaml +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/fixtures/recipes/fake-vllm.yaml +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/fixtures/recipes/local-embedding.yaml +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/fixtures/recipes/local-encoder.yaml +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/fixtures/responses/llamacpp-completions-grammar.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/fixtures/responses/llamacpp-completions-top_k.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/fixtures/responses/llamacpp-native-completion.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/fixtures/responses/vllm-chat-top_k.json +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/guards/test_prompt_literals.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/guards/test_sdk_schema_mirror.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/guards/test_structure.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/live/test_live_ask.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/live/test_live_sdk.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_api.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_calibrate.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_classify_and_states.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_cli.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_controls.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_embedding_adapter.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_encoder_adapter.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_engine.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_evaluation.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_llamacpp_dialect.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_local_embedding.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_multimodal.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_probe.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_readout_ladder.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_rerank_adapter.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_sdk_acceptance.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_translate.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tests/test_warm.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tools/author_long_state.py +0 -0
- {jevify-0.1.0 → jevify-0.2.0}/tools/author_policy_hard.py +0 -0
jevify-0.2.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.2.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,34 @@ 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, which needs a
|
|
93
|
+
raw-mode recipe (the template written out with the model's own control
|
|
94
|
+
tokens, served on `/v1/completions`).
|
|
95
|
+
|
|
96
|
+
`serve` binds to `127.0.0.1`. To reach it from other machines pass
|
|
97
|
+
`--host 0.0.0.0`, and set `JEVIFY_API_KEY` first so that every request must
|
|
98
|
+
carry that bearer key; to leave it running, detach it and keep its log:
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
jevify serve recipes/<name>.yaml --host 0.0.0.0 --port 8600 > ~/jevify.log 2>&1 &
|
|
102
|
+
curl -s http://<this-host>:8600/health
|
|
103
|
+
```
|
|
104
|
+
|
|
84
105
|
Every answer carries Jev's fields (`choice`, `confidence`, `probabilities`,
|
|
85
106
|
`score`, `legend`, `noul`) plus an `x_jevify` block naming the readout method,
|
|
86
107
|
the cached tokens, the latency and the recipe hash that produced it.
|
|
87
108
|
|
|
109
|
+
From a clone, `make ask` and `make serve` wrap the two commands;
|
|
110
|
+
[`docs/playbook.md`](https://github.com/fidecastro/jevify/blob/main/docs/playbook.md)
|
|
111
|
+
walks through them and explains every answer field.
|
|
112
|
+
|
|
88
113
|
## How it works
|
|
89
114
|
|
|
90
115
|
1. **Recipe.** A hashed YAML file: the endpoint, the prompt template (chat
|
|
@@ -99,7 +124,9 @@ the cached tokens, the latency and the recipe hash that produced it.
|
|
|
99
124
|
that reuses the server's prefix cache. A 32k-token state costs one warm and
|
|
100
125
|
then a few hundred milliseconds per question.
|
|
101
126
|
4. **Readout ladder.** Named-token logprobs, grammar with post-sampling
|
|
102
|
-
probabilities, top-k,
|
|
127
|
+
probabilities, top-k, prompt logprobs (each answer token appended to the
|
|
128
|
+
prompt and its logprob read back, for servers that give none on the reply
|
|
129
|
+
token), equal bias, and a floored top-k as the last resort.
|
|
103
130
|
Auto walks down the proven rungs; the answer names the rung that read it
|
|
104
131
|
and whether it was degraded.
|
|
105
132
|
5. **Evidence.** `jevify eval` runs a frozen suite with controls (shuffled
|
|
@@ -110,7 +137,7 @@ the cached tokens, the latency and the recipe hash that produced it.
|
|
|
110
137
|
|
|
111
138
|
| Kind | Talks to | Proven live with |
|
|
112
139
|
|---|---|---|
|
|
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 |
|
|
140
|
+
| `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
141
|
| `embedding` | A `/v1/embeddings` route, or a model in-process | Qwen3-Embedding-0.6B in-process |
|
|
115
142
|
| `encoder` | An NLI sequence-classification head in-process | facebook/bart-large-mnli in-process |
|
|
116
143
|
| `rerank` | A `/v1/rerank` route | Fakes only |
|
|
@@ -123,6 +150,28 @@ Four frozen suites, every recipe on each, one run each, latencies per
|
|
|
123
150
|
question on one machine on one day. Every row links to the scorecard that
|
|
124
151
|
records the command, the recipe hash, the suite hash and the raw file digest.
|
|
125
152
|
|
|
153
|
+
### Latency per question
|
|
154
|
+
|
|
155
|
+
The median of each suite, combined across the suites a model ran; one run
|
|
156
|
+
each. A question is one request that reads the answer off the next-token
|
|
157
|
+
probabilities, after the state was sent once and cached.
|
|
158
|
+
|
|
159
|
+
| Model | How it runs | Hardware | Latency per question |
|
|
160
|
+
|---|---|---|---|
|
|
161
|
+
| facebook/bart-large-mnli | in-process NLI classifier | RTX 4090 | 7–9 ms, avg 8 |
|
|
162
|
+
| Qwen3-Embedding-0.6B | in-process embedding, cosine | RTX 4090 | 8–10 ms, avg 9 |
|
|
163
|
+
| Gemma 4 E4B (Q8_0) | llama.cpp, chat | RTX 4090 | 28–37 ms, avg 32 |
|
|
164
|
+
| Qwen3-VL-Reranker-2B (Q8_0) | llama.cpp, yes/no readout | RTX 4090 | 47–64 ms, avg 53 |
|
|
165
|
+
| Qwen3-VL-Reranker-8B (Q8_0) | llama.cpp, yes/no readout | RTX 4090 | 68 ms (one suite) |
|
|
166
|
+
| Ternary Bonsai 2 27B (PQ2_0) | llama.cpp (PrismML fork), raw prompt | RTX 4090 | 97–113 ms, avg 102 |
|
|
167
|
+
| DeepSeek-V4-Flash | vLLM, chat | 2× DGX Spark, over LAN | 359–672 ms, avg 483 |
|
|
168
|
+
|
|
169
|
+
Image questions cost the llama.cpp models nothing extra, since the frame is
|
|
170
|
+
paid once in the warm; DeepSeek pays about half again on images. The two
|
|
171
|
+
in-process models are the fastest and the least accurate.
|
|
172
|
+
|
|
173
|
+
### Accuracy by suite
|
|
174
|
+
|
|
126
175
|
**policy-29**, the diagnostic suite carried over from the author's earlier experiments (choice only):
|
|
127
176
|
|
|
128
177
|
| Recipe | Kind | Accuracy | Median latency | Scorecard |
|
|
@@ -221,6 +270,8 @@ jevify is written to be operated through a coding agent. Point yours at
|
|
|
221
270
|
| [`tools/`](https://github.com/fidecastro/jevify/blob/main/tools) | Suite authoring, the Doom recorder and player |
|
|
222
271
|
| [`AGENTS.md`](https://github.com/fidecastro/jevify/blob/main/AGENTS.md) | The engineering principles that bind every change |
|
|
223
272
|
| [`SKILL.md`](https://github.com/fidecastro/jevify/blob/main/SKILL.md) | How an agent operates jevify |
|
|
273
|
+
| [`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 |
|
|
274
|
+
| [`Makefile`](https://github.com/fidecastro/jevify/blob/main/Makefile) | `make ask` and `make serve` shortcuts; `make help` lists the variables and recipes |
|
|
224
275
|
|
|
225
276
|
To work on jevify: `uv sync --group dev` (add `--extra encoder --extra
|
|
226
277
|
games` as needed) and `uv run pytest`. Live and in-process tests skip unless
|
|
@@ -229,14 +280,21 @@ you opt in.
|
|
|
229
280
|
## Not yet
|
|
230
281
|
|
|
231
282
|
Named so that nobody has to discover it: a direct-call control, so no
|
|
232
|
-
"faster than generation" claim exists yet;
|
|
233
|
-
|
|
283
|
+
"faster than generation" claim exists yet; clearing Doom's `deadly_corridor`
|
|
284
|
+
(in `defend_the_center` Bonsai's perception plus a fixed rule scores 10 kills
|
|
285
|
+
in 16 seconds, but in the corridor even the rule fed the engine's own labels
|
|
286
|
+
dies before the armour, so the corridor needs a policy with memory, not
|
|
287
|
+
better perception); the rerank kind and the
|
|
234
288
|
embedding route against a live server; a marker-slot encoder layout; the
|
|
235
289
|
grammar rung on the PrismML llama.cpp fork, which accepts a grammar without
|
|
236
|
-
constraining the reported probabilities;
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
290
|
+
constraining the reported probabilities; the `prompt_logprobs` rung against a
|
|
291
|
+
live server (written for TensorFold's native server with GLM-5.3-Flash, run
|
|
292
|
+
only against the fake server in `tests/` so far; a server that skips its
|
|
293
|
+
prefix cache for such requests, as TensorFold's source shows it does, makes
|
|
294
|
+
every question read the whole state again, which the probe measures and
|
|
295
|
+
notes); Windows. Choice menus on the endpoint kind are bounded by the
|
|
296
|
+
single-token identifier alphabet the probe verifies (26 on the DeepSeek
|
|
297
|
+
recipe); Jev's 255-option ceiling is not reachable there.
|
|
240
298
|
|
|
241
299
|
## Licence
|
|
242
300
|
|
|
@@ -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,34 @@ 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, which needs a
|
|
63
|
+
raw-mode recipe (the template written out with the model's own control
|
|
64
|
+
tokens, served on `/v1/completions`).
|
|
65
|
+
|
|
66
|
+
`serve` binds to `127.0.0.1`. To reach it from other machines pass
|
|
67
|
+
`--host 0.0.0.0`, and set `JEVIFY_API_KEY` first so that every request must
|
|
68
|
+
carry that bearer key; to leave it running, detach it and keep its log:
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
jevify serve recipes/<name>.yaml --host 0.0.0.0 --port 8600 > ~/jevify.log 2>&1 &
|
|
72
|
+
curl -s http://<this-host>:8600/health
|
|
73
|
+
```
|
|
74
|
+
|
|
54
75
|
Every answer carries Jev's fields (`choice`, `confidence`, `probabilities`,
|
|
55
76
|
`score`, `legend`, `noul`) plus an `x_jevify` block naming the readout method,
|
|
56
77
|
the cached tokens, the latency and the recipe hash that produced it.
|
|
57
78
|
|
|
79
|
+
From a clone, `make ask` and `make serve` wrap the two commands;
|
|
80
|
+
[`docs/playbook.md`](https://github.com/fidecastro/jevify/blob/main/docs/playbook.md)
|
|
81
|
+
walks through them and explains every answer field.
|
|
82
|
+
|
|
58
83
|
## How it works
|
|
59
84
|
|
|
60
85
|
1. **Recipe.** A hashed YAML file: the endpoint, the prompt template (chat
|
|
@@ -69,7 +94,9 @@ the cached tokens, the latency and the recipe hash that produced it.
|
|
|
69
94
|
that reuses the server's prefix cache. A 32k-token state costs one warm and
|
|
70
95
|
then a few hundred milliseconds per question.
|
|
71
96
|
4. **Readout ladder.** Named-token logprobs, grammar with post-sampling
|
|
72
|
-
probabilities, top-k,
|
|
97
|
+
probabilities, top-k, prompt logprobs (each answer token appended to the
|
|
98
|
+
prompt and its logprob read back, for servers that give none on the reply
|
|
99
|
+
token), equal bias, and a floored top-k as the last resort.
|
|
73
100
|
Auto walks down the proven rungs; the answer names the rung that read it
|
|
74
101
|
and whether it was degraded.
|
|
75
102
|
5. **Evidence.** `jevify eval` runs a frozen suite with controls (shuffled
|
|
@@ -80,7 +107,7 @@ the cached tokens, the latency and the recipe hash that produced it.
|
|
|
80
107
|
|
|
81
108
|
| Kind | Talks to | Proven live with |
|
|
82
109
|
|---|---|---|
|
|
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 |
|
|
110
|
+
| `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
111
|
| `embedding` | A `/v1/embeddings` route, or a model in-process | Qwen3-Embedding-0.6B in-process |
|
|
85
112
|
| `encoder` | An NLI sequence-classification head in-process | facebook/bart-large-mnli in-process |
|
|
86
113
|
| `rerank` | A `/v1/rerank` route | Fakes only |
|
|
@@ -93,6 +120,28 @@ Four frozen suites, every recipe on each, one run each, latencies per
|
|
|
93
120
|
question on one machine on one day. Every row links to the scorecard that
|
|
94
121
|
records the command, the recipe hash, the suite hash and the raw file digest.
|
|
95
122
|
|
|
123
|
+
### Latency per question
|
|
124
|
+
|
|
125
|
+
The median of each suite, combined across the suites a model ran; one run
|
|
126
|
+
each. A question is one request that reads the answer off the next-token
|
|
127
|
+
probabilities, after the state was sent once and cached.
|
|
128
|
+
|
|
129
|
+
| Model | How it runs | Hardware | Latency per question |
|
|
130
|
+
|---|---|---|---|
|
|
131
|
+
| facebook/bart-large-mnli | in-process NLI classifier | RTX 4090 | 7–9 ms, avg 8 |
|
|
132
|
+
| Qwen3-Embedding-0.6B | in-process embedding, cosine | RTX 4090 | 8–10 ms, avg 9 |
|
|
133
|
+
| Gemma 4 E4B (Q8_0) | llama.cpp, chat | RTX 4090 | 28–37 ms, avg 32 |
|
|
134
|
+
| Qwen3-VL-Reranker-2B (Q8_0) | llama.cpp, yes/no readout | RTX 4090 | 47–64 ms, avg 53 |
|
|
135
|
+
| Qwen3-VL-Reranker-8B (Q8_0) | llama.cpp, yes/no readout | RTX 4090 | 68 ms (one suite) |
|
|
136
|
+
| Ternary Bonsai 2 27B (PQ2_0) | llama.cpp (PrismML fork), raw prompt | RTX 4090 | 97–113 ms, avg 102 |
|
|
137
|
+
| DeepSeek-V4-Flash | vLLM, chat | 2× DGX Spark, over LAN | 359–672 ms, avg 483 |
|
|
138
|
+
|
|
139
|
+
Image questions cost the llama.cpp models nothing extra, since the frame is
|
|
140
|
+
paid once in the warm; DeepSeek pays about half again on images. The two
|
|
141
|
+
in-process models are the fastest and the least accurate.
|
|
142
|
+
|
|
143
|
+
### Accuracy by suite
|
|
144
|
+
|
|
96
145
|
**policy-29**, the diagnostic suite carried over from the author's earlier experiments (choice only):
|
|
97
146
|
|
|
98
147
|
| Recipe | Kind | Accuracy | Median latency | Scorecard |
|
|
@@ -191,6 +240,8 @@ jevify is written to be operated through a coding agent. Point yours at
|
|
|
191
240
|
| [`tools/`](https://github.com/fidecastro/jevify/blob/main/tools) | Suite authoring, the Doom recorder and player |
|
|
192
241
|
| [`AGENTS.md`](https://github.com/fidecastro/jevify/blob/main/AGENTS.md) | The engineering principles that bind every change |
|
|
193
242
|
| [`SKILL.md`](https://github.com/fidecastro/jevify/blob/main/SKILL.md) | How an agent operates jevify |
|
|
243
|
+
| [`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 |
|
|
244
|
+
| [`Makefile`](https://github.com/fidecastro/jevify/blob/main/Makefile) | `make ask` and `make serve` shortcuts; `make help` lists the variables and recipes |
|
|
194
245
|
|
|
195
246
|
To work on jevify: `uv sync --group dev` (add `--extra encoder --extra
|
|
196
247
|
games` as needed) and `uv run pytest`. Live and in-process tests skip unless
|
|
@@ -199,14 +250,21 @@ you opt in.
|
|
|
199
250
|
## Not yet
|
|
200
251
|
|
|
201
252
|
Named so that nobody has to discover it: a direct-call control, so no
|
|
202
|
-
"faster than generation" claim exists yet;
|
|
203
|
-
|
|
253
|
+
"faster than generation" claim exists yet; clearing Doom's `deadly_corridor`
|
|
254
|
+
(in `defend_the_center` Bonsai's perception plus a fixed rule scores 10 kills
|
|
255
|
+
in 16 seconds, but in the corridor even the rule fed the engine's own labels
|
|
256
|
+
dies before the armour, so the corridor needs a policy with memory, not
|
|
257
|
+
better perception); the rerank kind and the
|
|
204
258
|
embedding route against a live server; a marker-slot encoder layout; the
|
|
205
259
|
grammar rung on the PrismML llama.cpp fork, which accepts a grammar without
|
|
206
|
-
constraining the reported probabilities;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
260
|
+
constraining the reported probabilities; the `prompt_logprobs` rung against a
|
|
261
|
+
live server (written for TensorFold's native server with GLM-5.3-Flash, run
|
|
262
|
+
only against the fake server in `tests/` so far; a server that skips its
|
|
263
|
+
prefix cache for such requests, as TensorFold's source shows it does, makes
|
|
264
|
+
every question read the whole state again, which the probe measures and
|
|
265
|
+
notes); Windows. Choice menus on the endpoint kind are bounded by the
|
|
266
|
+
single-token identifier alphabet the probe verifies (26 on the DeepSeek
|
|
267
|
+
recipe); Jev's 255-option ceiling is not reachable there.
|
|
210
268
|
|
|
211
269
|
## Licence
|
|
212
270
|
|
|
@@ -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,35 @@ 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): only a raw prompt can carry the `prompt_logprobs` rung.
|
|
63
85
|
- `template.questions.choice_strategy`: `identifiers` (one request, options
|
|
64
86
|
labelled A, B, C) or `per_option` (one yes/no request per option; needed
|
|
65
87
|
for a model that never emits letters, such as a reranker).
|
|
@@ -67,8 +89,11 @@ and `answers`.
|
|
|
67
89
|
the probe fills their ids.
|
|
68
90
|
- `readout.rung: auto` walks the rungs the probe proved, strongest first:
|
|
69
91
|
`named_token_logprobs` (vLLM with `logprob_token_ids`), `grammar`
|
|
70
|
-
(llama.cpp), `top_k`, `
|
|
71
|
-
|
|
92
|
+
(llama.cpp), `top_k`, `prompt_logprobs` (each answer spelling appended to
|
|
93
|
+
a raw prompt and read back from vLLM-style `prompt_logprobs` rows, one
|
|
94
|
+
request more for every spelling the rows did not show; exact, off-menu mass
|
|
95
|
+
visible), `equal_bias`, `top_k_floor` (degraded, missing labels floored and
|
|
96
|
+
listed). A rung that cannot see every label hands the
|
|
72
97
|
question to the next; the answer names the rung that read it. A pinned rung
|
|
73
98
|
never walks: it fails loudly instead.
|
|
74
99
|
|
|
@@ -88,7 +113,10 @@ sequence-classification head; all three question types; semantics `readout`.
|
|
|
88
113
|
## Reading an answer
|
|
89
114
|
|
|
90
115
|
Jev's fields come first (`choice`, `confidence`, `probabilities`; `score`,
|
|
91
|
-
`legend`; `noul`).
|
|
116
|
+
`legend`; `noul`). `confidence` is Jev's statistic, `(n × peak − 1) / (n − 1)`
|
|
117
|
+
over the options or levels: 0 when uniform, 1 when certain, and the number
|
|
118
|
+
TypeSafe's published thresholds refer to. Everything jevify adds is under
|
|
119
|
+
`x_jevify`:
|
|
92
120
|
|
|
93
121
|
| Field | Meaning |
|
|
94
122
|
|---|---|
|
|
@@ -138,6 +166,16 @@ Jev's fields come first (`choice`, `confidence`, `probabilities`; `score`,
|
|
|
138
166
|
checkpoint at the end of an earlier prompt, and a chat template closes the
|
|
139
167
|
user turn right after the state. Use `template.mode: raw` so the warm prompt
|
|
140
168
|
ends at the state boundary; the Bonsai recipe is the example.
|
|
169
|
+
- **Probe notes "reply-token logprobs refused"**: the server gives no
|
|
170
|
+
logprobs on a generated token (TensorFold with GLM-5.3-Flash). Write a raw
|
|
171
|
+
recipe so the probe can prove `prompt_logprobs`; with a chat recipe the
|
|
172
|
+
probe ends in "no readout is possible".
|
|
173
|
+
- **"did not come back as its own prompt token"**: the appended answer fuses
|
|
174
|
+
with the end of the raw template; end the template where the answer starts
|
|
175
|
+
a token of its own (after a newline, or before a leading-space spelling).
|
|
176
|
+
- **Probe notes "prompt_logprobs requests reuse no cached prefix"**: the
|
|
177
|
+
server reads the whole state again for every question on that rung; keep
|
|
178
|
+
states short there, or serve the model where reply-token logprobs exist.
|
|
141
179
|
- **Missing encoder extra**: the error names the install command.
|
|
142
180
|
|
|
143
181
|
## 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
|
|
|
@@ -66,7 +66,7 @@ on an adapter module; a structure guard enforces the import ban.
|
|
|
66
66
|
|
|
67
67
|
| Kind | Talks to | Readout | Semantics |
|
|
68
68
|
|---|---|---|---|
|
|
69
|
-
| `endpoint` | Any OpenAI-compatible HTTP server | One generated token; distribution from logprobs at the answer position | `readout` |
|
|
69
|
+
| `endpoint` | Any OpenAI-compatible HTTP server | One generated token, or an answer token appended to the prompt; distribution from logprobs at the answer position | `readout` |
|
|
70
70
|
| `encoder` | An in-process encoder classifier loaded through Transformers | The head's scores at option marker slots (Laya's layout) or label logits (GLiClass, NLI) | `readout` |
|
|
71
71
|
| `rerank` | A rerank or score endpoint, or a reranker model in-process | One independent relevance score per option | `relevance` |
|
|
72
72
|
| `embedding` | An embeddings endpoint or model | Cosine between instruction-conditioned state vector and cached option vectors | `similarity` |
|
|
@@ -91,7 +91,7 @@ requests and records:
|
|
|
91
91
|
- whether each answer token under the recipe's template is a single token
|
|
92
92
|
(via the tokenizer or the server's tokenize endpoint);
|
|
93
93
|
- whether all answer tokens come back in the logprobs unbiased, biased, or
|
|
94
|
-
only through named-token logprobs or
|
|
94
|
+
only through named-token logprobs, a grammar or prompt logprobs;
|
|
95
95
|
- whether assistant prefill and template arguments (thinking off) are honored;
|
|
96
96
|
- whether a second request over the same state is measurably faster than the
|
|
97
97
|
first, which is the only proof that the prefix is cached;
|
|
@@ -115,8 +115,11 @@ allows, in this order:
|
|
|
115
115
|
(exact over the answer set, off-menu mass hidden);
|
|
116
116
|
3. an unbiased top-k request when every answer token is present (exact, and
|
|
117
117
|
off-menu mass visible);
|
|
118
|
-
4.
|
|
119
|
-
|
|
118
|
+
4. prompt logprobs at an answer token appended to a raw prompt, one request
|
|
119
|
+
more for each answer spelling the rows did not show (exact, off-menu mass
|
|
120
|
+
visible; added 2026-11-04, below);
|
|
121
|
+
5. equal bias on all answer tokens (exact ratios, off-menu mass hidden);
|
|
122
|
+
6. top-k with a floor for missing tokens, marked degraded in the answer.
|
|
120
123
|
|
|
121
124
|
The method used is recorded on every answer. A recipe may pin a rung; it may
|
|
122
125
|
not skip the ladder's recording.
|
|
@@ -131,6 +134,30 @@ also proves two things it used to assume: that an image adds prompt tokens
|
|
|
131
134
|
question request reuses (a recurrent model resumes only from a checkpoint at
|
|
132
135
|
the end of an earlier prompt, so a chat-template warm can buy nothing).
|
|
133
136
|
|
|
137
|
+
Amended 2026-11-04, for a server that refuses reply-token logprobs:
|
|
138
|
+
TensorFold's native server (pin `f350cd3e`) serves logprobs on a generated
|
|
139
|
+
token only for a family that gives the first token's row, and GLM-5.3-Flash
|
|
140
|
+
does not, so every rung above that reads a generated token is refused with a
|
|
141
|
+
400. The same server, like vLLM, returns `prompt_logprobs` on
|
|
142
|
+
`/v1/completions`: null, then one `{id: {logprob, rank, decoded_token}}` row
|
|
143
|
+
for each prompt token, the token the prompt holds listed first. The
|
|
144
|
+
`prompt_logprobs` rung appends an answer spelling to the raw prompt and reads
|
|
145
|
+
the last row: the appended token's exact logprob and the top-k beside it, all
|
|
146
|
+
from the one distribution at the answer slot. A spelling the rows did not
|
|
147
|
+
show is appended in a request of its own, so every label is exact and the
|
|
148
|
+
off-menu mass visible. The rung refuses a row whose prompt token is not the
|
|
149
|
+
appended one: an answer that fuses with the template's tail would be read at
|
|
150
|
+
the wrong position. It reads raw prompts only (TensorFold serves
|
|
151
|
+
`prompt_logprobs` on the completions route alone). It sits below `top_k`,
|
|
152
|
+
which needs one request, and above `equal_bias`, which hides the off-menu
|
|
153
|
+
mass. The probe no longer assumes reply-token logprobs: a 400 on its first
|
|
154
|
+
readout is recorded, the rungs that need them stay unproven, its own usage
|
|
155
|
+
and timing requests stop asking for them, and the probe proves
|
|
156
|
+
`prompt_logprobs` by appending an answer and checking the row. It also sends
|
|
157
|
+
that request twice and records when the repeat reuses no cached prefix,
|
|
158
|
+
since TensorFold skips its prefix cache for any request with
|
|
159
|
+
`prompt_logprobs` and each question then reads the whole state again.
|
|
160
|
+
|
|
134
161
|
### D5 — Choice questions on decoders
|
|
135
162
|
|
|
136
163
|
Options are labeled with single-token identifiers and the distribution is
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# ADR-0003 — Jev's evaluate call is the API; extensions are namespaced; the SDK is the acceptance test
|
|
2
2
|
|
|
3
|
-
- **Status:** accepted (2026-09-19)
|
|
3
|
+
- **Status:** accepted (2026-09-19); D8 added 2026-09-23: `confidence` is
|
|
4
|
+
Jev's peak statistic, amending `docs/00-invariants.md` §5.
|
|
4
5
|
- **Extends:** ADR-0001 D2. Implements INV-8; constrained by INV-4, INV-9.
|
|
5
6
|
|
|
6
7
|
## Context
|
|
@@ -77,6 +78,31 @@ implementation, and a structure guard forbids it from touching the port.
|
|
|
77
78
|
The library is the product; the server is one caller of it, and a program
|
|
78
79
|
that imports jevify gets identical behaviour without HTTP.
|
|
79
80
|
|
|
81
|
+
### D8 — `confidence` is Jev's statistic, computed Jev's way
|
|
82
|
+
|
|
83
|
+
Jev's `confidence` on a `choice` or `score` answer is
|
|
84
|
+
`(n × peak − 1) / (n − 1)`, where `n` is the number of options or levels and
|
|
85
|
+
`peak` is the largest probability: 0 for a uniform distribution, 1 for a
|
|
86
|
+
certain one. This is TypeSafe's published definition
|
|
87
|
+
(<https://docs.typesafe.ai/confidence>, worked example
|
|
88
|
+
`[0.90, 0.06, 0.04] → 0.85`), and it is what the thresholds TypeSafe
|
|
89
|
+
recommends to clients (act above 0.9, review between 0.5 and 0.9, route to
|
|
90
|
+
a human below 0.5) are calibrated against. Jev's `noul` answers carry no
|
|
91
|
+
confidence.
|
|
92
|
+
|
|
93
|
+
jevify computes exactly this in the one statistics chokepoint
|
|
94
|
+
(`jevify/domain/distribution.py`) and nowhere else. Until 2026-09-23 it
|
|
95
|
+
served one minus the normalized entropy under Jev's field name, which is
|
|
96
|
+
systematically lower (`[0.90, 0.06, 0.04]` gave 0.64) and would have moved
|
|
97
|
+
a client's threshold decisions; a reader of the NVIDIA forum thread caught
|
|
98
|
+
it. For `noul`, jevify keeps its own statistic (distance of P(true) from one
|
|
99
|
+
half, scaled to [0, 1]) under `x_jevify.confidence`, never on Jev's field.
|
|
100
|
+
|
|
101
|
+
Scorecards under `docs/evidence/` whose `run_at` precedes 2026-09-23 record
|
|
102
|
+
the entropy statistic in their wrong-case column; the reliability table bins
|
|
103
|
+
on the top probability and is unaffected. They are not rewritten, because a
|
|
104
|
+
number in a document comes from a run.
|
|
105
|
+
|
|
80
106
|
## Consequences
|
|
81
107
|
|
|
82
108
|
- Compatibility is tested, not asserted. A client written for Jev runs
|
|
@@ -84,8 +110,9 @@ that imports jevify gets identical behaviour without HTTP.
|
|
|
84
110
|
- The `x_jevify` namespace grows without ever touching Jev's fields.
|
|
85
111
|
- The explicit ingest introduces server-side state and its expiry; the
|
|
86
112
|
stateless route remains the default and the ingest is opt-in.
|
|
87
|
-
- Jev's `confidence` is served with Jev's meaning
|
|
88
|
-
distribution, and the claims contract forbids describing
|
|
113
|
+
- Jev's `confidence` is served with Jev's meaning and Jev's formula (D8), a
|
|
114
|
+
statistic of the distribution, and the claims contract forbids describing
|
|
115
|
+
it otherwise.
|
|
89
116
|
|
|
90
117
|
## Rejected alternatives
|
|
91
118
|
|
|
@@ -32,6 +32,7 @@ hash is the recipe's identity in every answer and every scorecard.
|
|
|
32
32
|
| Section | Holds |
|
|
33
33
|
|---|---|
|
|
34
34
|
| `model` | Identifier as the backend knows it, revision when known, the adapter kind, and for endpoints the dialect the probe detected. |
|
|
35
|
+
| `endpoint` | For endpoint-backed kinds: base URL, the environment variable holding an API key, the dialect, the timeout, and `extra_body`, server-specific fields merged into the top level of every request (a field jevify itself sets is refused when the backend is built, from one registry in the endpoint client; omitted from the hash when empty, so older recipes keep theirs), `headers`, static HTTP headers sent on every request (`Content-Type` and `Authorization` are refused when the backend is built; never a secret, since the recipe is hashed and shared, so a name that looks like a credential is refused when the recipe loads; omitted from the hash when empty), and `headers_env`, header names mapped to environment variables whose values are read when the backend is built and never written to the recipe (an unset variable fails then; omitted from the hash when empty). |
|
|
35
36
|
| `template` | For endpoints: how state, instructions and options are rendered into messages or a raw prompt; where the question goes; the answer-slot suffix; template arguments such as thinking off; whether assistant prefill is used. For encoders: the sequence layout and marker scheme. |
|
|
36
37
|
| `answers` | The answer tokens for `noul`, the identifier scheme for `choice`, the level tokens for `score`, each with the token ids the probe verified. |
|
|
37
38
|
| `readout` | The rung of the ladder to use or `auto`; the permutation policy and call count for `choice`. |
|
|
@@ -14,7 +14,7 @@ later documents can cite "ADR-0002 D3".
|
|
|
14
14
|
|---|---|---|
|
|
15
15
|
| [0001](0001-founding-choices.md) | Founding choices: name, scope, licence, toolchain, conventions | Accepted (2026-09-19); D3 amended 2026-09-20: GPL-3.0 to MIT |
|
|
16
16
|
| [0002](0002-backend-port-and-adapter-kinds.md) | One backend port, four adapter kinds, a capability probe, a readout ladder | Accepted (2026-09-19) |
|
|
17
|
-
| [0003](0003-jev-compatible-api.md) | Jev's evaluate call is the API; extensions are namespaced; the SDK is the acceptance test | Accepted (2026-09-19) |
|
|
17
|
+
| [0003](0003-jev-compatible-api.md) | Jev's evaluate call is the API; extensions are namespaced; the SDK is the acceptance test | Accepted (2026-09-19); D8 added 2026-09-23: `confidence` is Jev's peak statistic, amends invariants §5 |
|
|
18
18
|
| [0004](0004-recipes.md) | Recipes: the hashed file that is the only way a model is asked | Accepted (2026-09-19) |
|
|
19
19
|
| [0005](0005-evaluation-evidence-and-calibration.md) | Suites, scorecards, committed evidence, post-hoc calibration | Accepted (2026-09-19) |
|
|
20
20
|
| [0006](0006-toolchain-dependency-budget-and-portability.md) | Toolchain, dependency budget and portability: five light core packages, extras, argparse, uv/pipx, Python 3.12–3.14, no lock-in | Accepted (2026-09-19); makes ADR-0001 D4 precise |
|