jevlikedecisions
jevlikedecisions.decide(...) is one typed decision entrypoint over a family of provider adapters. Shared evidence is sent once, in full, and responses are checked against both the provider shape and the originating questions. Backend selection is mandatory: no call silently falls back to the deterministic mock_jevlike backend.
Installation and spend-gate setup
Python 3.11 or newer is required.
Install the package and its sole runtime dependency, llm-spendguard, with pip:
python -m pip install jevlikedecisions
Pending first release: The package is not yet published on PyPI. Until the first release is published, clone this repository, change into the clone, and run
python -m pip install .. Delete this note when publishing the first release.
Before making a model call, arm the spend gate in the virtual environment. This is required: jevlikedecisions deliberately refuses to call a model from an interpreter where spendguard is not enforcing. With the target virtual environment activated, run:
spendguard install-hook --venv "$VIRTUAL_ENV" --no-pkg
Temporary upstream workaround:
--no-pkgavoids aFileNotFoundErrorfor pip-installed, non-editablellm-spendguardenvironments whereinstall-hookotherwise looks for a repositorypyproject.toml. Remove this flag and note after that upstream defect is fixed.
Verify the activated interpreter:
spendguard doctor
The successful verdict line is ENFORCING HERE: 🟢 YES — calls from this interpreter are gated.
Temporary upstream workaround:
spendguard doctormay currently exit non-zero after printing that successful verdict, withValueError: Pricing returned no token cost. Read the verdict line rather than the exit code; remove this note after that upstream defect is fixed.
If gate setup is skipped, decide() refuses model calls through require_enforcing_spendguard.
Request and question schema
Every surface accepts the same JSON object:
{
"input_parts": ["complete evidence"],
"questions": [{"name": "safe", "type": "predicate", "description": "Is it safe?"}],
"backend": "mock_jevlike",
"intent": "documented-example",
"prompt_cache_key": null
}
intent and backend are required. Every decision result identifies backend, provider, model, route, via, ledger_id, usage, cost_complete, and known_input_cost_floor_usd. Inspect route to distinguish in-process and metered results. cost_complete says whether the cost is fully settled; when a provider omits output-token usage, a metered result has cost_complete: false, no ledger id, and only the known input-cost floor. In-process results are settled at zero cost. Every question has name, type, and description. type is predicate, choice, or score. A choice also has choices, whose entries contain value and description. A score also has levels, whose entries contain label and description. Library validation is authoritative for every surface.
Decision suitability
assess_decision_suitability asks one configured classification question through decide() and returns the same contract through Python, CLI suitability, HTTP /suitability, and MCP assess_decision_suitability. Its bounded shapes are derived from the intersection of every configured backend's declared question_types, so a framing is backend-independent. In the shipped configuration these are predicate; choice with one or more caller-declared {value, description} entries; and score with caller-declared {label, description} levels within the configured minimum and maximum. A fourth configured outcome identifies prompts that require unbounded generation.
The result uses decision_shape for the selected question type, required_declarations for the schema the caller still owes, decide_request for a complete request that can be sent to decide(), and question_type_disagreement for a nullable object containing classified_question_type and supplied_question_type. On disagreement, the complete paid decision and suitability classification remain available, decide_request stays null, and required_declarations states what a conforming question for the classified shape requires. out_of_scope_explanation explains why an unsuitable prompt does not fit, and general_llm_generation_pointer remains present for work outside the bounded engine. The old reframing field is not part of this contract because an already-valid request is not a reframing, while an unsuitable prompt needs an explanation and declaration requirements rather than a fabricated request.
The optional candidate_question is the no-generation boundary. When supplied, it must already be a complete valid question of a type supported by every configured backend; malformed candidates are refused before decide() at zero spend. It is included whole in the same paid classification request and, when its type agrees with the selected shape, is returned unchanged inside decide_request. A post-call type disagreement is a reported finding, not a refusal. When the candidate is omitted, prose may still be classified as a bounded shape, but decide_request is null and required_declarations tells the caller what to supply. The capability never extracts choices or score levels from prose and never invents them. The zero-spend estimate accepts the same optional question, so it serializes and counts the same assessment request the paid path would submit.
Backend names and aliases come from the package configuration. An alias is accepted only at selection; estimates, approvals, receipts, audit evidence, and returned provenance always use the canonical backend name. Omitting backend never selects a default and produces a typed DecisionUnavailable refusal.
| Canonical backend | Accepted aliases | Provider | Model | Usability and precondition |
|---|---|---|---|---|
openai_decisions |
openai |
openai |
gpt-6-luna |
Usable only with OPENAI_API_KEY, an enforcing spendguard interpreter, and a matching estimate approval. The sole proven call is ledger 2bed00b5f31d4858: 319 input tokens costing $3.19e-05; its output side is unproven. |
typesafe_jev |
jev |
typesafe |
jev-1.14.0 |
Usable only with TYPESAFE_API_KEY, an enforcing spendguard interpreter, and a matching estimate approval. Input costs $0.042 per 1M tokens and output is free, as documented at docs.typesafe.ai/models; the wire schema is documented at docs.typesafe.ai/api/system-one. No live TypeSafe call has yet been proven. |
mock_jevlike |
none | mock |
deterministic |
Usable in-process and never selected implicitly. Predicate probability is exactly 0.5; choice selects the first option with probability/confidence 1.0 and all others 0.0; score selects the first level with score 0.0, probability/confidence 1.0, and all other levels 0.0; reported input/output tokens are both 0. |
Estimate, approve, then spend
The shipped configuration contains exactly three backends: metered typesafe_jev and openai_decisions, plus the in-process mock_jevlike test double. Their only transport kinds are http and in_process. A backend that declares the removed subscription transport is refused when configuration loads; subscription-lane LLM access belongs in llm-spendguard.
Choice questions may declare "calibrated_choice_distribution": "required" or "calibrated_choice_distribution": "omitted". Omitting the declaration preserves the current required contract: choice, probabilities for every declared choice in order, and confidence. Declaring omitted explicitly requests only name, type, and choice; returning either probabilities or confidence is a named nonconformance, even if null. Unknown or malformed declarations are refused with the permitted values from the packaged choice_distribution_contract configuration beside limits. Mixed declarations in one call are supported. The shared validator enforces the declared answer fields across Python, CLI, HTTP, and MCP. Score questions are unchanged.
Self-reported confidence is not calibrated. In the measured eight-question probe with twenty declared choices per question, gpt-6-luna returned top probability/confidence 1.0 on every item (entropy approximately zero; effective support 1.00), while glm-5.3 returned top probability/confidence 0.99 on every item (entropy 0.0629; effective support 1.06). Those degenerate vectors do not establish calibrated uncertainty. At the working shape of 32 questions × 29 choices, the measured output grew from 2,149 to 23,270 tokens. The declaration allows callers to omit that unvalidated distribution rather than pay for it. The field describes the requested distribution contract; it does not certify calibration when required.
Paid decisions fail closed. First call decide(..., estimate_only=True): it counts the complete provider request, prices it through spendguard.pricing, spends $0, and persists a matching approval. Only a later identical decide(...) may reach the gated metered transport. Provider usage remains authoritative for the real receipt. Configure approval storage with JEVLIKEDECISIONS_APPROVAL_DIR, approval signing with JEVLIKEDECISIONS_APPROVAL_INTEGRITY_KEY_FILE, receipt storage with JEVLIKEDECISIONS_RECEIPT_DIR, and an alternate package configuration with JEVLIKEDECISIONS_CONFIG. The legacy by-value integrity variable is retained only to produce a migration refusal. The integrity file must contain a non-whitespace UTF-8 key and permit no non-owner access.
Run callers under an enforcing llm-spendguard interpreter: the gate supplies model credentials from its governed keys file before this package starts. The packaged backend key_env declarations currently name OPENAI_API_KEY for openai_decisions and TYPESAFE_API_KEY for typesafe_jev; jevlikedecisions has no default-path credential loader. For a repository-specific fallback only, set the environment variable named by credential_fallback_file_env (JEVLIKEDECISIONS_KEYS_FILE in the packaged configuration). If unset, its configured default is .jevlikedecisions/jdlkeys.env beneath the caller's home directory. An absent fallback is normal. The file may supply only names declared by backend key_env, never overwrites a name already supplied by the gate or caller, must permit no non-owner access, and must not have a replaceable non-sticky ancestor.
Provider requests have a socket bound. Python, CLI, HTTP, and MCP callers may override it per call with request_timeout_seconds; omitting it on any surface uses the positive finite decision_request_timeout_seconds value from decision configuration. The packaged 300-second value is a conservative ceiling, not a measured bound: historical decide rows contain no latency distribution. It is intentionally well above a plausible healthy decision and must be re-derived after this release records request latency.
The decision audit store uses SQLite WAL journaling and the configured busy timeout on every connection opened for attempts, outcomes, queries, and wire-evidence reads. WAL permits readers to continue while a writer commits; SQLite still serializes writers. An append returns succeeded after a durable database write, retries busy/locked contention for the configured attempt count and backoff, then atomically spools the complete row and returns pending; malformed data, non-contention storage errors, or spool failure return failed. The first append creates the store with owner-only permissions using one atomic O_CREAT open; later appends do not change an existing store's mode. Opening an existing rollback-journal store switches its persistent journal setting to WAL and may create -wal and -shm sidecar files. This does not rewrite the table schema or make the database incompatible, and it is reversible by setting PRAGMA journal_mode=DELETE after all connections have closed and the WAL has been checkpointed.
Python, CLI, HTTP, and MCP
Python:
from jevlikedecisions import decide
estimate = decide(input_parts, questions, backend=backend, intent=intent, estimate_only=True)
result = decide(
input_parts, questions, backend=backend, intent=intent,
request_timeout_seconds=request_timeout_seconds,
)
CLI input comes from a JSON file or stdin. estimate prints the estimate and its approval_path; decide prints answers and receipt fields. The backend value accepts either a canonical name or an alias reported by backends.
jevlikedecisions backends
jevlikedecisions estimate --backend "$JEVLIKEDECISIONS_BACKEND" request.json
jevlikedecisions decide --backend "$JEVLIKEDECISIONS_BACKEND" --request-timeout-seconds "$JEVLIKEDECISIONS_REQUEST_TIMEOUT_SECONDS" request.json
The CLI flag fills a missing request backend; if both the flag and JSON field are present they must agree or the command fails. CLI exit codes follow sysexits: 0 (EX_OK) for success, 64 (EX_USAGE) for malformed input, 65 (EX_DATAERR) for a typed decision refusal, and 70 (EX_SOFTWARE) for an internal fault. The CLI reports every failure as TypeName: message on stderr; catching internal faults gives automation a stable code without hiding the fault.
The standard-library HTTP server exposes POST /backends, POST /estimate, and POST /decide; its backend field accepts the same config-derived canonical names and aliases, and /decide accepts the optional numeric request_timeout_seconds field. HTTP 400 identifies a malformed request, 422 identifies a typed decision refusal, 503 specifically identifies DecisionUnavailable, and 500 identifies an internal fault. Keeping unavailable decisions at 503 preserves the established retry signal, while the JSON error field retains the exact typed refusal. Set JEVLIKEDECISIONS_HTTP_PORT; optionally set JEVLIKEDECISIONS_HTTP_HOST. With no JEVLIKEDECISIONS_OPERATOR_TOKEN it binds only to loopback. When that token is set, callers must send it as an operator bearer token. A paid caller must successfully call /estimate before /decide with the identical request.
Both HTTP listeners derive their maximum request-body size from four UTF-8 bytes for every token in the largest configured backend input context. A declared body above that ceiling is refused with HTTP 413 before the body is read; the capability listener uses its normal error/message JSON shape and the MCP listener uses JSON-RPC error JSON.
jevlikedecisions-http --host "$JEVLIKEDECISIONS_HTTP_HOST" --port "$JEVLIKEDECISIONS_HTTP_PORT"
The package's sole runtime dependency is llm-spendguard; its MCP surface is a standard-library JSON-RPC stdio server. The list_decision_backends, estimate_decision, and make_decision tools expose the same selection contract and send evidence whole. Enumeration is derived from configuration and reports canonical name, aliases, provider, model, usability, and every missing precondition; both decision tools accept exactly those canonical names and aliases. make_decision also exposes estimate_mode with the same decide semantics as the other surfaces and accepts optional numeric request_timeout_seconds. The approval from the estimate tool must exist before a paid call through the decision tool.
jevlikedecisions-mcp
The same registry-derived MCP tools are also available as a long-running, JSON-only MCP
Streamable HTTP service at MCP_HTTP_ENDPOINT_PATH in mcp_http_transport.py; this is a
transport for the stdio MCP surface, not a separate capability surface. Set
JEVLIKEDECISIONS_MCP_HTTP_PORT; optionally set JEVLIKEDECISIONS_MCP_HTTP_HOST. With no
JEVLIKEDECISIONS_MCP_HTTP_OPERATOR_TOKEN it binds only to loopback. When that token is set,
the host is required and callers must send the token as an operator bearer token.
jevlikedecisions-mcp-http --host "$JEVLIKEDECISIONS_MCP_HTTP_HOST" --port "$JEVLIKEDECISIONS_MCP_HTTP_PORT"
On macOS, scripts/generate_mcp_http_launchd_agent.py generates a launchd user-agent plist
from the repository without putting a platform-specific entry point in the wheel. The port,
approval directory, audit store path, and receipt directory are required. A wrong record
location works silently and forks the decision history, so the generator will not guess any of
these three locations. The executable defaults to jevlikedecisions-mcp-http beside the Python interpreter
running the generator; the owner and home come from the current environment, and the label,
plist destination, and per-tool log paths derive from them. The generated daemon environment
uses the three operator-supplied record locations and defaults only the
approval_integrity.key owner-only key-file path. A wrong key-file path fails loudly and closed.
The bind host defaults through the
server's existing unauthenticated loopback resolution. Every derived value has an explicit
override flag, and the plist contains only the key file path, never the key value. Generate and
load an agent like this:
python scripts/generate_mcp_http_launchd_agent.py --port "$JEVLIKEDECISIONS_MCP_HTTP_PORT" \
--approval-directory "$JEVLIKEDECISIONS_APPROVAL_DIRECTORY" \
--audit-store-path "$JEVLIKEDECISIONS_AUDIT_STORE_PATH" \
--receipt-directory "$JEVLIKEDECISIONS_RECEIPT_DIRECTORY"
launchctl load "$HOME/Library/LaunchAgents/com.$USER.jevlikedecisions-mcp-http.plist"
launchctl kickstart -k "gui/$(id -u)/com.$USER.jevlikedecisions-mcp-http"
Use kickstart -k to restart the loaded label; do not load a second copy. This daemon is not a
singleton: another process on the same port fails to bind, and KeepAlive then retries it at
the configured ThrottleInterval. Without JEVLIKEDECISIONS_MCP_HTTP_OPERATOR_TOKEN, the
daemon binds loopback only. A coding tool's HTTP MCP server entry should point to
http://127.0.0.1:<port>/mcp, replacing <port> with the required operator-selected port.
Paid live-proof utility
scripts/run_paid_decision_live_proof.py preserves the reproducible paid-call proof harness in the repository. It takes a configuration path rather than embedding one; examples/paid_decision_live_proof.example.json shows the input shape. By default it estimates every configured backend, prints the per-backend and total token/USD estimates, and exits without making a paid call. After reviewing that complete estimate, rerun with the explicit --authorize-paid-calls flag to execute every configured proof. Its records include reported-output-token status, cost completeness, and the known input-cost floor alongside the ledger and receipt evidence.
PYTHONPATH=src python scripts/run_paid_decision_live_proof.py examples/paid_decision_live_proof.example.json
PYTHONPATH=src python scripts/run_paid_decision_live_proof.py examples/paid_decision_live_proof.example.json --authorize-paid-calls
assert_all_decisions_via_jevlikedecisions(...) cross-checks package-owned receipts against spendguard rows by ledger_id and fails closed when there are no auditable receipts. Spendguard's calls table has no endpoint, route, or via columns, so this proves receipt-backed package calls; it cannot prove that no outside caller contacted a Decisions endpoint.
Capability documentation verdict maintenance
Capability documentation verdicts accumulate by exact (subject, sha256(documentation)) pairs.
The documentation judge normally evaluates only pairs absent from the schema-current record,
retains byte-identical entries with their original per-entry instrument provenance and timestamp,
and reports every stale pair it prunes. Use --force-full-rejudge when the judging instrument
changes, including its model, prompt, sample count, or independent-run count; that mode replaces
every current pair rather than retaining evidence produced by a different instrument. Older record
schemas are refused and must be migrated explicitly before judging.
Troubleshooting
A framework Python without a usable certificate bundle may need SSL_CERT_FILE set to the path of an installed CA bundle, such as the one supplied by certifi, before a real HTTPS call. Never put keys or secret values in configuration or examples.
anyjev remains a named v1 schema gap: its authoritative independent wire contract was unavailable, so no fields were invented. It can be added as another adapter without changing decide().
General LLM generation
This package wraps the jev and OpenAI Decisions APIs only. General text generation was removed; use llm-spendguard for general LLM calls.
Metadata
Release files for jevlikedecisions 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| jevlikedecisions-0.4.0.tar.gz | 3.3 MB | With provenance | Details for jevlikedecisions-0.4.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| jevlikedecisions-0.4.0-py3-none-any.whl | Python 3 | none | any | Details for jevlikedecisions-0.4.0-py3-none-any.whl |
Total release size: 3.4 MB
Release files / jevlikedecisions-0.4.0.tar.gz
| Download URL | jevlikedecisions-0.4.0.tar.gz |
|---|---|
| Size | 3.3 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
96d26c40d19a7cb18360ec4b5d2b8f191cf5177e1fd71059d5bba92a58b70ffb
|
|
BLAKE2b-256 checksum How to use checksums |
2f4c665d21cdc247d1eef8bf402418666faf853aeef96df6f6f4e407d378ab7d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logRelease files / jevlikedecisions-0.4.0-py3-none-any.whl
| Download URL | jevlikedecisions-0.4.0-py3-none-any.whl |
|---|---|
| Size | 91.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8eae430246d7cf7c17a993c756b7d639c536dbfbbc53bc4ed46dabdec04c46b0
|
|
BLAKE2b-256 checksum How to use checksums |
b09eb0bdf5b1ba39a5c862cd32db04ae11e213122aa6cf1d3071db3bb9328815
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logSecurity for jevlikedecisions 0.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for jevlikedecisions-0.4.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.