The AI copilot
The Studio's chat pane sends a description to a model, compiles and backtests what comes back on held-out data, and shows a diff before anything touches the editor.
The chat pane is a thin front over one library loop. crates/vike-studio/src/chat.rs's ChatPane
owns the transcript, the provider selector and the last result; the work happens in vike-ai's
develop_strategy loop (crates/vike-ai/src/agent.rs) — generate, compile-validate, backtest
out-of-sample, repair. The pane runs it the way the Run toolbar runs a backtest:
ChatPane::send spawns a worker thread and hands back a receiver, and StudioState::poll drains
it beside the run, sweep and walk-forward receivers.
What Send needs
Send is enabled only when four things hold at once: no chat round trip is already running, the
selected provider has a key, the input is non-empty, and a data slice is selected in the picker.
The last is not decoration — StudioState::start_chat_send says the model needs a
(venue, symbol, interval) to backtest against, the same slice the Run toolbar uses (see
Data and slices).
Providers and keys
crates/vike-ai/src/provider.rs's Provider offers two: Anthropic, the default, and Cerebras. Each
needs one key — ANTHROPIC_API_KEY or CEREBRAS_API_KEY, the names chat.rs's key_env_var fixes
for both the lookup and the "no key" error, so the failure message names the variable to set.
ChatApiKeys says where they come from: <project>/settings/secrets.env, the same store every
venue credential lives in, resolved by the same walk. The pane itself opens no store and reads no
environment — the desktop app resolves the keys from the credential map it already loads and hands
them in as a parameter. And they are not venue credentials: absent, the pane offers no provider and Send stays disabled, and
nothing about trading changes.
Keys are read once, at construction
The provider list is computed when the pane is built, not per frame. A key added to the store
afterwards needs a restart — and a present-but-blank line (ANTHROPIC_API_KEY=) counts as
present: the provider is offered and the request fails at the provider. The doc pins that
deliberately rather than silently changing which providers the picker lists.
What the model is told
Two things precede every message. The system prompt, agent.rs's STRATEGY_SYSTEM_PROMPT, is the
Rhai contract (the scripting tree's host surface): define fn on_bar(); read
position(), price() and the host-bound indicators (NaN until warmed up); order with
market(dir, qty) or buy/sell; declare sweepable knobs with param("x", default); submit
through a submit_strategy tool carrying code and a one-line explanation. An unbound name
raises on the first call rather than returning a plausible number.
Then chat.rs's grounding_preamble: the target slice — venue, symbol, interval — a note that the
strategy is backtested on that instrument's out-of-sample data, and one valid template as a worked
example. It deliberately does not enumerate the indicator registry. The doc records that the reason
inverted: the host used to bind only three indicators; now it binds most of the
registry, and what remains is a token budget — a fixed roster in every message
crowds out the context specific to this chat.
The loop, and how it scores
develop_strategy scores only on the out-of-sample tail the model never saw (holdout_frac). A
rejected submission goes back with the compile error or the reason it did not trade, and the model
resubmits. max_repairs caps the number of submission attempts actually compiled and backtested —
not the number of tool calls — and once the cap is reached every further submit_strategy is
refused with a terminal message.
Reviewing the result
The transcript line is summary_of's: accepted, with the OOS Sharpe and trade count; not accepted
after N attempts, with the problems; or an error. Below it the pane shows a result chip and a
Review changes section — diff_rows, a line-level diff of the current editor buffer against
the generated script. apply_result's doc states the contract: the diff comes first so Apply is a
reviewed action rather than a blind clobber, and the write itself is still a whole-buffer replace
once you accept. Until you press Apply to editor, the editor is
untouched.
Memory across sessions
The loop has a ledger. crates/vike-ai/src/ledger.rs keeps two best-effort JSON files under the
store root — ai_trials.json and ai_learnings.json — colocated with studio_strategies.json
(see Saved strategies and workspace); the Studio resolves
the path with LedgerPaths::under because the library reads no environment. Per agent.rs's
ledger section it buys two things: prompt grounding, so the loop stops rediscovering duds it
already rejected on this exact slice, and cross-session deflation — the deflated Sharpe ratio
(Bailey and López de Prado, 2014) counts every prior accepted trial on the same
(venue, symbol, interval), so a single-candidate chat that is accepted on a slice already carrying
an accepted trial gets a real deflated_sharpe instead of the 0.0 a ledger-less call reports.
Fewer than two trials in total — the first chat on a fresh slice included — leaves deflation
undefined and the field at 0.0. With a ledger present the model is also offered a
record_learning tool for notes future sessions read; without one it is not advertised.
The ledger is bounded — MAX_TRIALS 500 rows, oldest dropped first; equity curves kept on the
newest MAX_CURVE_ROWS 50 accepted rows; MAX_LEARNINGS 200 notes of at most MAX_LEARNING_TEXT
1024 bytes — and best-effort: a missing file is empty, a corrupt one is empty plus a warning, a
failed write is a warning. The loop never fails because the ledger is unavailable.
ChatOutcome's error arm covers the no-key case and a worker that panicked; both land in the
transcript as a readable line rather than a silent stop. The Connect to Claude button at the
bottom of the pane uses none of these keys and has its own page.
Saved strategies and workspace
Two files with two owners — a workspace snapshot of restorable UI state under the project's state directory, and the legacy saved-strategies JSON blob beside the store that the file-tree migration moves off, since a user's strategies are files now.
Connect your own Claude
The chat pane's Connect to Claude button writes the claude mcp add line that registers vike-cli mcp against the datahub the Studio already dials — and what that button does in a build that left the feature off.