Config is via env vars (all have working defaults), read from the environment or a git-ignored .env at the repo root. You rarely need to touch files — set your model from the UI (Customize → Models). To configure by env instead, copy .env.example to .env. OPENAI4S_SKIP_DOTENV=1 skips the .env loader entirely — the offline test suite sets it so a developer's real .env can never configure the tests.
One OPENAI4S_LLM_PROVIDER selects a wire adapter; each ships a default base_url and model, so usually you only set the key. Four wire formats live behind one normalized host.llm: OpenAI-compatible /chat/completions, OpenAI /responses, Anthropic /v1/messages, and Gemini generateContent.
| provider | wire | default model | vision |
|---|---|---|---|
ark |
openai | doubao-seed-2.0-pro (+10 more via plan/v3) |
✅ |
chatgpt |
openai | gpt-5 |
✅ |
openai_responses |
responses | gpt-5 |
— |
claude |
anthropic | claude-sonnet-4-5 |
✅ |
gemini |
gemini | gemini-2.5-flash |
✅ |
ark is Volcengine's plan/v3 gateway — one endpoint + key serving doubao-seed-2.0-{pro,code,lite,mini}, glm-5.2, kimi-k2.7-code, kimi-k2.6, deepseek-v4-{pro,flash}, minimax-{m3,m2.7} — all pre-registered as switchable model profiles. Without a key the daemon still starts; the UI shows a "configure your API key" banner until you set one.
Each of api_key / base_url / model resolves per-provider var → generic var → provider default (e.g. OPENAI4S_CLAUDE_API_KEY → OPENAI4S_LLM_API_KEY → default). The openai_responses provider uses the stateless Responses API wire and preserves function-call/reasoning output items across turns; its current adapter is text/tool-only.
The openai and anthropic wires stream token by token whenever the caller supplies a delta callback, and fall back to one blocking request if the stream fails before its first event. responses is always SSE; gemini is always blocking. OPENAI4S_LLM_STREAM=0 (also false/no/off) forces the blocking path on the two that choose — worth reaching for behind a proxy that mishandles SSE, at the cost of the reply arriving as one blob per turn.
Provider identity, model presets, capabilities, and wire transport are separate. A deployment or plugin can register another provider over one of the four shipped wires without editing the chat router:
from openai4s.llm import register_model_preset, register_provider
register_provider(
"lab_openai",
wire="openai",
base_url="http://127.0.0.1:11434/v1",
model="science-model",
tool_calling=False,
context_window_tokens=16_384,
)
register_model_preset(
"lab_openai",
"science-model",
"Local science model",
)
Registration is validated, process-local, and limited to the shipped
openai, responses, anthropic, and gemini adapters; it cannot load
arbitrary transport code. Use a startup plugin or deployment composition layer
to repeat registrations after restart. provider_specs(), model_presets(),
and get_model_capabilities() expose detached or immutable catalog views.
Long runs compact automatically: OPENAI4S_CONTEXT_WINDOW (262144) is the
window used when the model's capability entry does not declare one, and
OPENAI4S_COMPACTION_TRIGGER_RATIO (0.75) is the fraction of that window
at which older turns are summarized. The summary call asks the model for
max(8192, OPENAI4S_LLM_MAX_TOKENS) completion tokens, clamped to the
model's declared output cap; OPENAI4S_COMPACTION_SUMMARY_MAX_TOKENS sets it
explicitly. For a self-hosted endpoint that declares no cap (every loopback
or private address does), the request is bounded by the room the window
leaves after the summarized chunk, so a small context_window_tokens in a
register_provider(...) call is honoured rather than exceeded.
The core engine stays stdlib-only, and the control .venv carries just the optional science extra (numpy / pandas / matplotlib). A baseline scientific stack (scipy / seaborn / scikit-learn / biopython / httpx / …, see CORE_PACKAGES in openai4s/kernel/preinstall.py) is available on top of that, but serve does not install it.
Starting the daemon never modifies your Python environment. serve only reports what is missing — on stderr and via GET /api/v1/kernel/packages (preinstall.phase == "needs_provision"). Installing is an explicit act:
openai4s setup # provision the kernel environments
or Customize → Compute → "Install package" in the UI, or host.pip_install from a cell.
This used to be implicit: serve resolved ~23 unpinned package names against PyPI on a background thread and installed them with --break-system-packages. That made booting the daemon mutate the user's interpreter, made two cold starts a week apart produce different environments, and made an offline cold start fail where nobody was looking. Diagnosis and mutation are now separate.
Heavier toolchains live in ready-to-use Conda specs instead, so native dependencies are solved for the user's own macOS or Linux platform rather than copying one machine's build strings. Create the everyday Python + R pair with ./setup.sh --with-kernel-envs or openai4s setup --profile standard; use --update (or ./setup.sh --update-kernel-envs) to synchronize an existing environment without pruning user-installed packages. openai4s setup with no profile preserves the historical behavior of creating all four environments. Use --dry-run to preview and --only <name> for one. Specs live in envs/:
python (default) — NumPy / pandas / SciPy / matplotlib / seaborn / Pillow / PDFium / SOCKS, plus scanpy / anndata / Leiden / UMAP / scikit-learn / RDKit / fair-esm.struct — torch + fair-esm + biotite.phylo — MAFFT / IQ-TREE / FastTree / trimAl / BioPython / ete3.r — R 4.5 / tidyverse / data.table / ggplot2 / R Markdown / knitr / jsonlite / Pandoc.When OPENAI4S_STAGE1_TRUSTED_DELIVERY=1, startup also reports the
standard profile's exact local readiness. The check compares the 32 direct
Python and 8 direct R dependencies in the shipped manifests with package
metadata from the discovered python and r environments. It starts no
interpreter, imports no science package, contacts no network, and changes
nothing. A missing/incomplete environment blocks the first routed Code Cell
before a pending environment switch, safety classification, identity/attempt,
or worker exists; an unreadable inventory fails closed as unavailable. Native
control tools and sole structured finalization remain available because they
need no kernel. Approved/resumed scientific plans are checked before their
status transition, while plan drafting remains available. openai4s run
applies the same typed check at its first Code Cell; serve and doctor report
it without installing anything.
The persistent workbench banner and Customize → Compute card show the complete missing-environment/package list. Remediation remains an explicit, managed transaction:
openai4s env plan python r --repair
openai4s env apply python r --repair
plan is read-only. apply builds fresh generations and runs the actual
Python/R interpreter plus the complete direct-package verification before it
atomically moves either environment's current pointer. A build or
verification failure leaves the previous pointer unchanged. The UI only copies
these commands; it never runs them automatically.
OPENAI4S_HOST (127.0.0.1) · OPENAI4S_PORT (8760) · OPENAI4S_DATA_DIR (~/.openai4s, holds the SQLite db, artifacts, logs, pidfile). See Security for remote / SSH-tunnel access.
OPENAI4S_TRUSTED_PROXY_ORIGINS (empty) is a comma-separated list of exact
external browser origins allowed when a trusted TLS proxy rewrites Host to
the loopback upstream. It accepts no wildcards or non-root URL paths (one
trailing slash is normalized). Configuring an https:// origin also makes
team browser session cookies Secure. Setting any trusted proxy origin also
disables the team-mode machine-token SERVICE_IDENTITY on the HTTP listener,
because a loopback reverse proxy makes public clients indistinguishable from
the local CLI at the TCP peer boundary. Proxy deployments use a normal admin
login; the local access-token CLI path remains available while this setting is
empty. See
Team Server.
OPENAI4S_SEED_DEMO (0) — set to 1 to run the bundled example analysis at
startup. The default changed and the variable's sense reversed. It used to
default to 1, and on a fresh data dir that meant the daemon bound its port and
then, on a background thread, started a Python kernel, executed six cells,
called the UniProt and RCSB REST APIs, spawned the bundled MCP connector and
wrote four artifacts — before the user had typed anything. A fresh boot now does
none of that.
The example is still there; it moved behind a button. The dashboard offers Run
the example analysis when a session list is empty, and the button posts
{"confirm": true} to POST /api/v1/example/session (see
Web app API). Set the variable to 1 on a demo machine that
should come up pre-populated. Either way the seed runs at most once — the two
paths share one seeder.
OPENAI4S_TOKEN — the daemon access token, for CLI subcommands when the token
file under the data dir is not readable by the calling user (a daemon running
under another account). Normally unset: the CLI reads the file.
OPENAI4S_REQUIRE_TOKEN — removed in 0.3.0; ignored. Up to 0.2.x, 0
turned the access-token gate off on a loopback bind. That opt-out was granted
for exactly one minor release (decision D1), so the daemon now requires its
token on every bind whatever this variable says; see Security
for what the gate stands in front of.
OPENAI4S_NOTEBOOK_REPL (off) — set to 1 to re-enable the web UI's in-Notebook developer REPL (arbitrary kernel code from the right panel); off by default, so the Notebook is a read-only execution trace (see Security).
OPENAI4S_WEBUI — unset (the default) serves the committed Vite workbench (openai4s/server/webui/dist/index.html) as the SPA shell at / and at deep links such as /projects/{pid}/frames/{fid}. Set to exactly legacy to serve the frozen webui/index.html + app.js escape hatch. Any other value (including 1 / next / true) keeps the new UI, so a typo cannot silently fall back. /static/dist/ is ordinary static files under WEBUI_DIR either way. The retired OPENAI4S_WEBUI_NEXT name is ignored.
Every rollout flag defaults off. Stage 1 implements trusted delivery, Stage 2 implements the durable Auto Run/configuration/projection foundation, and Stage 3 implements Scientific Reviewer V2 shadow recording, and Stage 4 implements the review-only completion gate, and Stage 5 implements bounded auto-fix / re-review, Stage 6 records Permission Guardian shadow assessments, and Stages 7–12 provide their listed independently gated behavior. No later flag implicitly enables an earlier one or grants standing authority.
Config field under Config.roadmap_features |
Environment variable | Behavior |
|---|---|---|
stage1_trusted_delivery |
OPENAI4S_STAGE1_TRUSTED_DELIVERY |
Implemented opt-in: exact immutable Artifact delivery, same-head capture observations, and standard-profile readiness admission. |
stage2_auto_run_storage |
OPENAI4S_STAGE2_AUTO_RUN_STORAGE |
Implemented opt-in: durable Auto Run/review/finding/repair/permission-assessment records, canonical post-commit events, CAS selection PATCH, and REST/reopen projection. No Reviewer/Repair/Guardian execution. |
stage3_scientific_review_shadow |
OPENAI4S_STAGE3_SCIENTIFIC_REVIEW_SHADOW |
Implemented opt-in: immutable Evidence Snapshot, independent V2 Reviewer, read-only scratch/adapters, and shadow recording that does not gate completion. |
stage4_review_completion_gate |
OPENAI4S_STAGE4_REVIEW_COMPLETION_GATE |
Implemented opt-in: candidate stays provisional until review; pass promotes Verified, issues become completed_with_issues, failures become review_unavailable. Does not start Repair. |
stage5_auto_repair |
OPENAI4S_STAGE5_AUTO_REPAIR |
Implemented opt-in: bounded Repair Agent plus independent re-review. Reviewer stays read-only; Repair cannot self-certify; identical bytes reuse the prior version. |
stage6_guardian_shadow |
OPENAI4S_STAGE6_GUARDIAN_SHADOW |
Implemented opt-in: exact-action Guardian shadow assessment. It does not execute, cannot create standing allow, and fails closed on hash mismatch. |
stage7_guardian_enforcement |
OPENAI4S_STAGE7_GUARDIAN_ENFORCEMENT |
Implemented opt-in: unattended ask may allow_once only for non-dangerous exact actions. The credential review fence promotes a matching default file allow to an audited ask; an attached human may decide it, while the headless deterministic policy refuses it before Guardian. Standing allow remains forbidden. |
stage8_live_notebook_lineage |
OPENAI4S_STAGE8_LIVE_NOTEBOOK_LINEAGE |
Implemented opt-in: official live Notebook on the shared kernel generation, host-side Python/R read→version mapping, and automatic write lineage. |
stage9_artifact_workbench |
OPENAI4S_STAGE9_ARTIFACT_WORKBENCH |
Implemented opt-in: interactive CSV/Parquet tables, version diffs, PDF/HTML locators in the next turn, and real vendored Ketcher 3.7.0. |
stage10_scientific_connectors |
OPENAI4S_STAGE10_SCIENTIFIC_CONNECTORS |
Implemented opt-in: ClinVar, PubMed, and ClinicalTrials.gov with pagination, cache, honest errors, and versioned Artifact provenance. |
stage11_durable_remote_compute |
OPENAI4S_STAGE11_DURABLE_REMOTE_COMPUTE |
Implemented opt-in: the lazily constructed manager rehydrates/reconciles on first remote-compute access without resubmitting, and harvest Artifacts record remote env, input versions, job receipt, and checksums. |
stage12_auto_mode_ga |
OPENAI4S_STAGE12_AUTO_MODE_GA |
Implemented opt-in: GA kill-switch declaration. It does not turn earlier stages on. |
With the flag off (the default), a final completion links each Artifact's
mutable current head at /api/v1/artifacts/{artifact_id}.
With OPENAI4S_STAGE1_TRUSTED_DELIVERY=1, a final completion links only to
/api/v1/artifacts/versions/{version_id} generated by the server URL helper. Artifact
bytes are frozen and verified before their version is claimed; the final
assistant message and delivery manifest commit together before the
link-bearing event is emitted. Repeating the current head's SHA-256 creates a
new scoped capture observation for the producing Cell and lineage, not a fake
version. If snapshot, checksum, relation, or delivery persistence validation
fails, the turn publishes no success link. The delivery event carries a stable
delivery_id; REST reopen recovers its already committed message if the socket
event is lost. The delivery ledger does not drive automatic replay; the
ordinary bounded WS sequence buffer may replay it while the turn remains live,
and terminal/restart recovery uses REST. The flag does not enable result
review, repair, Guardian, live Notebook, or any later-stage surface.
With the flag unset or false, the pre-Stage-1 completion and Cell-execution
behavior is preserved. GET /api/v1/environments/status still includes a
standard_profile_readiness object, but it is the explicit
enabled:false/reason:"feature_disabled" projection and performs no
discovery.
The product selections live under Config.auto_mode:
| Environment variable | Allowed values | Default |
|---|---|---|
OPENAI4S_AUTO_MODE |
0/1, false/true, no/yes, off/on, autonomous |
off |
OPENAI4S_RESULT_REVIEW_MODE |
off, review_only, auto_fix |
off |
OPENAI4S_APPROVALS_REVIEWER |
user, auto_review |
user |
These are closed vocabularies. An unknown or explicitly blank value (including
a misspelled boolean such as flase) rejects configuration rather than
becoming truthy. on/autonomous is one non-contradictory preset: it always
normalizes the effective sub-modes to auto_fix + auto_review. Explicit
sub-modes apply only while the preset is off.
The preset's frozen ceilings live under Config.auto_mode.budgets; an
environment override may tighten but never exceed them. The rolling circuit
window is fixed at 50 so changing its denominator cannot weaken the rate rule:
| Config field | Environment variable | Ceiling |
|---|---|---|
max_review_rounds |
OPENAI4S_AUTO_MAX_REVIEW_ROUNDS |
2 attempts per candidate |
max_repair_rounds |
OPENAI4S_AUTO_MAX_REPAIR_ROUNDS |
2 |
repair_turns_per_round |
OPENAI4S_AUTO_REPAIR_TURNS_PER_ROUND |
12 |
max_extra_cells |
OPENAI4S_AUTO_MAX_EXTRA_CELLS |
30 |
wall_time_s |
OPENAI4S_AUTO_WALL_TIME_S |
900 |
extra_token_multiplier |
OPENAI4S_AUTO_EXTRA_TOKEN_MULTIPLIER |
1.5 |
repeated_finding_limit |
OPENAI4S_AUTO_REPEATED_FINDING_LIMIT |
2 |
same_action_no_delta_limit |
OPENAI4S_AUTO_SAME_ACTION_NO_DELTA_LIMIT |
3 |
no_progress_turn_limit |
OPENAI4S_AUTO_NO_PROGRESS_TURN_LIMIT |
5 |
guardian_timeout_s |
OPENAI4S_AUTO_GUARDIAN_TIMEOUT_S |
90 |
guardian_consecutive_denial_limit |
OPENAI4S_AUTO_GUARDIAN_CONSECUTIVE_DENIAL_LIMIT |
3 |
guardian_window_size |
OPENAI4S_AUTO_GUARDIAN_WINDOW_SIZE |
fixed 50 |
guardian_window_denial_limit |
OPENAI4S_AUTO_GUARDIAN_WINDOW_DENIAL_LIMIT |
10 |
Stage 2 durable selection precedence is import quarantine
(forces off/user) → explicit frame → explicit project → explicitly configured
deployment → legacy review:auto:{root} → built-in defaults. Legacy true maps
only to review_only; it can never enable repair or permission automation. An
unset deployment value is distinct from an explicit off for migration. Hard
sandbox, egress, biosecurity, secret/credential, cost, and deterministic
permission policy remains outside and above this precedence order. None is
attributed to Guardian. Its durable reason separately projects policy setup,
budget exhaustion, safe rollback unavailable, unknown external outcome, loop
detection, or a hard/integrity safety boundary; the subsystem name alone never
chooses the terminal label.
Auto Mode is a bounded preset, not full access. With only Stage 2 enabled it is
configuration and durable history only; selecting autonomous does not itself
start Reviewer, Repair, or Guardian execution. Those consumers remain behind
their own later-stage flags. The authoritative
budget, state, recovery, and projection meanings are in the
Auto Mode product contract.
The daemon and KernelSpec tooling remain zero-dependency. Install the optional wire stack only when an external Jupyter client should launch a standalone OpenAI4S Python/R worker:
python -m pip install 'ipykernel>=7,<8'
openai4s jupyter describe
openai4s jupyter install
openai4s jupyter export <directory> writes specs without installing them;
install --prefix <prefix> targets <prefix>/share/jupyter/kernels. See
Optional Jupyter compatibility for the independent-namespace and
Host-RPC limitations.
openai4s init # guided first-run model configuration (headless-friendly)
openai4s serve # daemon + web UI (foreground; --detached to background,
# plus --host/--port/--no-browser; the detached parent waits
# up to 60s for /health, OPENAI4S_DETACHED_READY_TIMEOUT overrides)
openai4s status # is it up? (plain URL, never the access token)
openai4s url # the sign-in URL; it carries the access token, so keep it out of logs
openai4s stop # stop the daemon; waits up to --timeout (default 30s), --force to SIGKILL
openai4s run "…" # one Code-as-Action task in-process, no daemon
openai4s setup --profile standard # build Python + R
openai4s setup --profile standard --update # sync Python + R, no pruning
openai4s setup # build all four environments
openai4s jupyter describe # inspect optional bridge availability
openai4s jupyter export ./kernel-specs # pure-stdlib KernelSpec export
openai4s jupyter install # install user KernelSpecs
openai4s run exits with a status a script can branch on:
| Exit | Meaning |
|---|---|
| 0 | The run completed (stop_reason is submitted). |
| 1 | An unhandled error; a Python traceback is on stderr. |
| 2 | Refused before or at the first Code Cell: a usage error (an empty task, empty_task; an invalid --allow-test-command, invalid_allow_test_command), an explicit --mode reusable_pipeline\|codebase_change whose test command nothing can authorize (code_mode_test_runner_unauthorized), the standard environment is not ready, the database schema is newer than this build (future_schema), or an upgrade of an older database failed and was rolled back (migration_failed; the error line names the kept backup). --json prints the error and its code on stdout; the two database refusals also keep their error: line on stderr. |
| 3 | The run ended without completing: stop_reason is max_turns, no_progress, cancelled, or any other value. |
--json prints the full result, stop_reason included, for exit 0 and 3
alike. With --auto the status still follows stop_reason alone; the review
verdict is auto_mode.terminal.
openai4s init stores the selected provider/model/base URL in the normal
OpenAI4S settings database. Interactive API-key input is hidden; automation may
pipe one line to openai4s init --api-key-stdin --non-interactive. An API key
is never accepted as a command-line value or returned by --json, keeping it
out of shell history and structured command output.
The native runtime is supported on Linux and macOS. The current persistent-kernel transport, resource accounting, process interruption, and OS sandbox adapters depend on Unix primitives; installing the wheel with native Windows Python does not imply that scientific Cell execution is supported there — the kernel spawn path refuses it outright.
Windows users run the Linux build under WSL2. The release ships
OpenAI4S-<version>-windows-<arch>.zip, which is that Linux build plus a
launcher that installs it into WSL2 and opens the Windows browser at the
forwarded port; it is not a native Windows build and does not pretend to be
one. The full matrix, and what actually ships per platform, is
platforms.md.