Skip to content

AI tab

The AI tab is a chat assistant that operates the control plane for you. Type a request in natural language and it calls the right RPCs on your behalf -- creating triggers, subscribing to topics, starting workers, connecting worlds -- the same calls the Triggers / Events / Workers / World pages make.

It appears just before Config under both the Trading and Infra top-level tabs.

AI

Quick start

  1. The openai SDK is bundled with the base install — no extra step needed. If you're on an older version, uv pip install 'virtufin-tui[ai]' is the legacy install; current installs just need uv pip install virtufin-tui.

  2. Set an API key for one of the shipped providers (any one):

export DEEPSEEK_API_KEY=...     # or OPENAI_API_KEY / MINIMAX_API_KEY
  1. Open the AI tab, type a command, press Enter:
create trigger firing every minute publishing to trigger.everyminute

If no provider is configured yet, the tab shows a "configure" hint and the input is disabled.

Keybindings

Key Action
Enter Send the typed message
Ctrl+L Clear the transcript and start a fresh conversation
P Open the provider picker (switch the active LLM provider)

Providers (llm_providers.toml)

The provider catalog lives at ~/.config/virtufin/tui/llm_providers.toml (seeded from the shipped default on first run; edit your copy freely). Every entry is an OpenAI-compatible Chat Completions endpoint -- the openai SDK is used for all of them, so only base_url / api_key / model differ.

[providers.openai]
base_url    = "https://api.openai.com/v1"
api_key_env = "OPENAI_API_KEY"
model       = "gpt-4o-mini"

[providers.deepseek]
base_url    = "https://api.deepseek.com"
api_key_env = "DEEPSEEK_API_KEY"
model       = "deepseek-v4-flash"

[providers.minimax]
base_url    = "https://api.minimax.io/v1"
api_key_env = "MINIMAX_API_KEY"
model       = "MiniMax-M3"

[providers.ollama]            # local server, no key
base_url = "http://localhost:11434/v1"
model    = "llama3.1"

API key resolution

For each provider, in order:

  1. api_key set inline in the toml (optional, discouraged for secrets).
  2. The env var named by api_key_env -- defaults to <NAME>_API_KEY, so DEEPSEEK_API_KEY, MINIMAX_API_KEY, and OPENAI_API_KEY work with no extra config.
  3. The global VIRTUFIN_TUI_AI_API_KEY override.

Providers with no key (e.g. local Ollama) simply omit api_key / api_key_env.

Which provider is active

Resolution order:

  1. VIRTUFIN_TUI_AI_PROVIDER env var (explicit session override).
  2. The runtime choice you picked with P -- this is state, persisted to ~/.local/state/virtufin/tui/state.json (default_llm_provider), not the catalog file. The catalog holds only definitions.
  3. The first provider in the catalog (file order) that has a resolvable key.
  4. None -- the tab shows its "configure" state.

Other env overrides:

  • VIRTUFIN_TUI_AI_MODEL -- override the model id for the active provider.
  • VIRTUFIN_TUI_AI_API_KEY -- override the key for the active provider.

Adding a custom provider

Add another [providers.<name>] table -- e.g. an internal gateway or a different hosted model:

[providers.mygateway]
base_url    = "https://llm.internal.example.com/v1"
api_key_env = "MY_GATEWAY_KEY"
model       = "our-finetune"

Then press P in the AI tab to select it (or set VIRTUFIN_TUI_AI_PROVIDER=mygateway).

What it can do

The assistant works through a fixed tool catalog (function-calling). Each tool maps 1:1 to an existing control-plane call -- it has no privileged path:

Tool Does
create_trigger Schedule a cron/point-in-time trigger (the Triggers page's a flow)
delete_trigger Delete a trigger by name
set_event_topics Replace the event-stream topic filter (subscribe/unsubscribe)
create_worker / start_worker / stop_worker Worker lifecycle
add_world / connect_world / remove_world World tag/connect/remove

Each turn, the assistant is given a small state snapshot (active world/scenario, current triggers/topics/workers) so it can use real ids and names instead of guessing. If a model replies with text instead of calling a tool, it is nudged once; if it still doesn't act, its text is shown as a normal chat reply (so models that only half-support tool-calling still work).

Example commands:

  • create trigger firing every minute publishing to trigger.everyminute
  • subscribe to workmanager.lifecycle
  • stop worker abc123
  • connect the binance top10 world

Privacy

Each turn sends your message plus the state snapshot (active world/scenario and summaries of current triggers, topics, and workers) to the configured LLM provider. No secrets (API keys, connection credentials) are included in the snapshot. Be aware of this before pointing the tab at a third-party endpoint -- run a local model (Ollama entry) if the control plane's state must not leave the host.

Implementation notes

  • Agent logic is Textual-free under src/virtufin_tui/ai/ (config.py, llm_client.py, tools.py, agent.py) so it is unit-tested without a Pilot harness.
  • The openai dependency is bundled (a core dependency in the base install).
  • Anthropic-native and streaming token rendering are not yet supported; any OpenAI-compatible endpoint (including local servers) works today.