Skip to content

Pi.dev agent on the dual-3090 backend

Current workstation guide, audited 2026-09-06 against Pi 0.85.0, its installed provider source, and the live LiteLLM → vLLM endpoint. Pi is the coding agent from pi.dev, not Raspberry Pi. These files configure a workstation; cluster changes still go through Git and ArgoCD.

Use vanillax-vllm/qwen3.8-27b, medium thinking, preserved reasoning, native vision, and automatic compaction. Keep the existing provider identity and cloud providers. The audit found a stale qwen3.6-27b default and the built-in qwen-chat-template toggle, which omitted the selected reasoning effort.

Provider configuration

Back up ~/.pi/agent/models.json, settings.json, and AGENTS.md before editing. Merge this provider into models.json; do not overwrite other providers or credentials. vanillax-vllm is a custom models.json provider, so /login cannot configure it -- that picker offers only Pi's built-in providers, and a custom provider ID never appears in the list. Supply the LiteLLM key from 1Password (homelab-prod/litellm/master_key) through the provider's apiKey field, which resolves "$VAR" and "!command" values as well as literals:

"apiKey": "$LITELLM_API_KEY"

Export that variable from a file outside Git (the workstation uses ~/.ai-keys, sourced by .zshrc), or read it directly with "apiKey": "!op read 'op://homelab-prod/litellm/master_key'". Resolution order is CLI --api-key, auth.json, environment variable, then the models.json value. A placeholder key fails against this authenticated gateway. Keep credentials out of Git.

{
  "providers": {
    "vanillax-vllm": {
      "baseUrl": "https://litellm.vanillax.me/v1",
      "api": "openai-completions",
      "apiKey": "$LITELLM_API_KEY",
      "compat": {
        "supportsDeveloperRole": false,
        "supportsReasoningEffort": false,
        "supportsUsageInStreaming": true,
        "maxTokensField": "max_tokens",
        "thinkingFormat": "chat-template",
        "chatTemplateKwargs": {
          "enable_thinking": { "$var": "thinking.enabled" },
          "reasoning_effort": {
            "$var": "thinking.effort",
            "omitWhenOff": true
          },
          "preserve_thinking": { "$var": "thinking.enabled" }
        }
      },
      "models": [
        {
          "id": "qwen3.8-27b",
          "name": "Qwen3.8 27B (vLLM FP8, 2x3090, 262K)",
          "reasoning": true,
          "thinkingLevelMap": {
            "off": "off",
            "minimal": null,
            "low": "low",
            "medium": "medium",
            "high": null,
            "xhigh": "xhigh",
            "max": null
          },
          "input": ["text", "image"],
          "contextWindow": 262144,
          "maxTokens": 32768,
          "cost": {
            "input": 0,
            "output": 0,
            "cacheRead": 0,
            "cacheWrite": 0
          }
        }
      ]
    }
  }
}

thinkingFormat: chat-template resolves Pi's selected thinking level into Qwen's native kwargs. supportsReasoningEffort: false prevents a conflicting top-level field. The model-level mapping exposes only supported choices:

Pi level Thinking Effort sent Preserve reasoning
off false omitted false
low true low true
medium (normal coding) true medium true
xhigh (explicit difficult task) true xhigh true

Qwen accepts no high value. Unsupported Pi levels are null, so the selector skips them. Upstream Qwen defaults to xhigh when effort is omitted; explicit client mapping and the server's medium fallback prevent that accident. Preservation is on for agent continuity and unchanged-prefix reuse. Stateless chats may explicitly disable preservation without changing the server default. Official Qwen controls, Pi model schema.

Requests pass through LiteLLM for Prometheus metrics and Langfuse AI analytics. The Qwen sampler extension also attaches Pi’s session ID and a pi tag so generations from one coding session can be grouped in Langfuse. Explicit caller metadata takes precedence. Local tool execution needs separate instrumentation; the gateway records model requests and returned tool calls. The provider ID stays vanillax-vllm, preserving its thinking mapping and sampler extension. The backend is still stock vLLM with the same context and GPUs. Telemetry verification and direct-access fallback explains how to confirm actual event storage; successful inference alone is insufficient.

Settings and usable context

Merge these fields into ~/.pi/agent/settings.json; keep packages, other model preferences, authentication, and UI settings:

{
  "defaultProvider": "vanillax-vllm",
  "defaultModel": "qwen3.8-27b",
  "defaultThinkingLevel": "medium",
  "modelThinkingLevels": {
    "vanillax-vllm/qwen3.8-27b": "medium"
  },
  "compaction": {
    "enabled": true,
    "reserveTokens": 49152,
    "keepRecentTokens": 20000
  }
}

The 262,144-token window includes input, tool schemas/results, images, reasoning, and the answer. maxTokens: 32768 is the output budget, not an extra window. Pi compacts when estimated context exceeds the window minus reserveTokens: approximately 212,992 tokens here. The 49,152 reserve is our operating recommendation: 32,768 output tokens plus 16,384 for tool growth. It leaves the full server ceiling available while starting cleanup before a long tool result exhausts it. This is not an upstream-required value or a hard protection against arbitrarily large tool output. Compaction settings are global in Pi; smaller cloud models may need a project-specific override.

Compaction summarizes older history and retains a recent tail. It is lossy: keep task decisions, file paths, verification results, and remaining work in a concise handoff. Use /compact at milestones and /new between unrelated tasks. Avoid whole-repository dumps; search and read relevant sections. Pi compaction behavior, settings and project overrides.

Two GPU cards do not mean two independent model servers. The live shared pool holds about 325K tokens; two simultaneous 262K sessions do not fit. Use one long coding session near the ceiling. A second light request can share the pool, but parallel agent fanout competes for the same capacity. See the measured capacity audit.

Correct sampling when switching thinking off

Pi's template mapping switches reasoning but does not switch sampling. The small repo-owned Qwen sampler extension uses Pi's before_provider_request hook to select the deployment's six mode-specific sampling values after serialization. It applies only to vanillax-vllm/qwen3.8-27b, leaves messages/tools/template mapping intact, and sets mode-specific values even if a stale client temperature was selected.

From the repository root, back up any existing copy, then install:

mkdir -p ~/.pi/agent/extensions
cp scripts/pi/qwen-sampling.ts ~/.pi/agent/extensions/qwen-sampling.ts

Restart Pi or use /reload. Thinking requests use temperature 1.0, top-p 0.95, top-k 20, min-p 0, presence penalty 0, repetition penalty 1.05. Off requests use 0.7, 0.8, 20, 0, 1.5, 1.0 respectively. The thinking penalty is a local, community-reported mitigation candidate, not Qwen's official default or a proven fix. It matches the server and WebUI policy; see the runbook's caveats. After pulling a sampler update, repeat the copy above and /reload: Git/Argo cannot update an already installed workstation copy. Without this extension, Pi off still disables reasoning, but needs another per-request sampler override to match Qwen's recommendation. Pi request hook, canonical server policy and API examples.

Vision and browser tools

The current server permits one image in the entire submitted request and no video. Pi can resend images from earlier turns: one new screenshot plus an old screenshot can already exceed the limit. This is unrelated to the size of the text context window. Keep text/DOM extraction as the browser default and use a screenshot when visual evidence is needed.

If the image limit is reached, do not blindly retry. Start /new with a text handoff and the required image. /compact can help only if the old image is in the portion discarded; a recent image may remain. Do not promise that compaction always resets the image count. Keep this rule in workstation AGENTS.md.

Start with Pi's built-in file and shell tools. Use the existing LSP integration for symbol/type diagnostics, a web tool for current documentation, and browser DevTools when the task needs a logged-in page. Keep installed packages; avoid adding overlapping tool suites just because the context window is larger. Inspect /session and tool output growth during long work. Small, relevant outputs preserve room for reasoning and reduce prefill work.

Use a new session for clean validation; resumed sessions may retain their old model or thinking level. No provider rename, shell alias, or Pi upgrade is required for this configuration. The workstation was updated through Homebrew to 0.85.0, preserving provider, authentication and extension files. A fresh medium-thinking request through LiteLLM passed. Restart an existing Pi process to use the new executable.

Verification and rollback

pi --version
pi --list-models qwen3.8-27b
pi --provider vanillax-vllm --model qwen3.8-27b --thinking medium

Expected: vanillax-vllm, qwen3.8-27b, roughly 262K context and 32K output, with thinking and image support. Start normally with pi after setting the defaults. Use /model to reload model metadata and select low, medium, xhigh, or off explicitly.

For an isolated smoke request from the repo root:

pi --no-session --no-extensions --no-skills --no-prompt-templates \
  --no-context-files --no-tools -e ./scripts/pi/qwen-sampling.ts \
  --provider vanillax-vllm --model qwen3.8-27b --thinking medium \
  -p 'What is 37 times 43? Give the answer.'

Expected answer: 1591. Repeat with --thinking low, xhigh, and off. Inspect the emitted request when validating effort: answer length does not prove the selected mode. The server acceptance matrix also covers tool calls, images, and multi-turn reasoning. Streaming usage must be present so Pi can track context; do not disable it to hide an API error.

For offline policy checks (Node 24+ and Python with PyYAML):

node --test scripts/pi/qwen-sampling.test.mjs
uv run --with pyyaml python -m unittest discover -s scripts/tests -p test_qwen_reasoning.py -v

The September audit also exercised the installed Pi serializer against a local HTTP capture server: default/low/medium/xhigh/off produced the expected kwargs, sampler and usage request. A real medium request through the LAN endpoint returned 1591 with separate reasoning and streaming token counts. Those checks validate plumbing, not agent task quality.

To roll back workstation changes, restore the backed-up JSON/AGENTS files and remove the newly installed sampler extension (or restore its previous copy), then restart Pi. No Kubernetes rollback is needed for workstation files. For server-policy rollback, revert the reasoning-policy commit through Git.