Skip to content

chore(pi): add session affinity and cache diagnostics - #336

Open
dgokeeffe wants to merge 9 commits into
databricks:mainfrom
dgokeeffe:review/pi-cache-affinity-independent
Open

chore(pi): add session affinity and cache diagnostics#336
dgokeeffe wants to merge 9 commits into
databricks:mainfrom
dgokeeffe:review/pi-cache-affinity-independent

Conversation

@dgokeeffe

Copy link
Copy Markdown

Issue and stack

Closes #331.

Depends on #217, #333, #223, #239, and #243, but is independent of #334/#335. Review only the session-affinity/diagnostic range relative to the pushed integrated base:

dgokeeffe/ucode@review/integrated-consolidation-base...review/pi-cache-affinity-independent

Equivalent immutable range: 168038b...e672d4e. Do not merge before the prerequisite stack. The only production behavior in this unit is Pi session affinity; it does not force or claim short GPT-subagent retention.


Unit 07 — Pi cache/session-affinity diagnostics

Objective and user-visible behavior

Enable Pi's production-safe session-affinity headers so a conversation remains on one UAG destination when traffic splitting is configured. Add sanitized, explicit diagnostic tools/tests for prompt-cache investigations without asserting a backend retention guarantee.

Candidate: review/pi-cache-affinity-independent (e672d4e), independent incremental base dev (168038b).

Exact scope

Production: one Pi Claude-provider compat flag in src/ucode/agents/pi.py.
Diagnostics: scripts/prompt_cache_repro.py, scripts/prompt_cache_http_trace.py.
Tests: one Pi compat assertion plus tests/test_prompt_cache_repro.py, tests/test_prompt_cache_http_trace.py.
Non-goals: forcing PI_CACHE_RETENTION, installing a child/subagent cache extension, claiming short GPT-subagent retention, MLflow discovery/proxying.

Before / after reproduction

git show dev:src/ucode/agents/pi.py | rg 'sendSessionAffinityHeaders' || echo '<absent>'
# <absent>

git show review/pi-cache-affinity-independent:src/ucode/agents/pi.py \
  | rg 'sendSessionAffinityHeaders'
# "sendSessionAffinityHeaders": True

git grep -nE 'short retention|PI_CACHE_RETENTION|in_memory.*supported' \
  review/pi-cache-affinity-independent -- src/ucode scripts tests \
  || echo '<no production short-retention claim>'

Focused validation:

uv run --frozen pytest tests/test_agent_pi.py \
  tests/test_prompt_cache_repro.py tests/test_prompt_cache_http_trace.py -q
# 74 passed
uv run --frozen ruff check .
# All checks passed
uv run --frozen ruff format --check src tests scripts
# candidate files formatted (focused check: 6 files already formatted)

Full suite:

uv run --frozen pytest -q
# 2 failed, 1751 passed, 36 skipped

The installed Claude capture and preferred-Pi-model state assertion reproduce on integrated dev; neither is touched by this six-file range.

Retained cache investigation evidence

  • Pi long mode constructs requests with prompt_cache_key and prompt_cache_retention: "24h".
  • UAG accepts/validates 24h; invalid values report in_memory and 24h as supported values.
  • Direct UAG: GPT-5.6 Sol missed after 45 minutes despite requested 24h.
  • Direct UAG: GPT-5.5 hit at 45 and 50 minutes, then missed at 60 minutes.
  • These facts rule out a Pi request-construction failure but do not distinguish UAG forwarding, backend eviction, or prefix matching.
  • GPT-5.6 Sol rejects explicit in_memory; therefore this unit makes no short-retention claim for GPT subagents.

The trace script prints sanitized request/response JSON, never prints authorization headers, redacts an echoed exact token, and persists only a cache key, assistant text, and usage metadata. Tests verify omission vs 24h vs in_memory, stable prefixes, cache usage extraction, phase/state behavior, malformed saved-state rejection without traceback, and redaction.

Impact map

  • Agent/provider: Pi's Databricks Claude provider gains session-affinity headers; diagnostics call the Codex Responses endpoint only when explicitly run.
  • Config: one compat flag in generated Pi models config; no environment/cache policy override.
  • Compatibility: if a Pi release ignores the flag, behavior falls back to current routing; tests lock generated config shape.
  • Managed config/MCP/skills/MLflow: unchanged.

Rollback and residual risk

Revert the two unit-07 commits (945b0c2, e672d4e); session affinity and diagnostic scripts disappear with no state migration. Residual risk remains backend cache eviction/forwarding/prefix behavior; the scripts diagnose but do not promise retention.

Hygiene

git diff --check dev..review/pi-cache-affinity-independent passes. Six-file range; no MLflow production files, generated outputs/state files, credentials, uv.lock, .pi-subagents/, or goal.md; no merge markers or unresolved index entries.

…directing HOME

Pi honors the PI_CODING_AGENT_DIR env var to resolve its config
directory (~/.pi/agent), so redirecting /Users/david.okeeffe to APP_DIR/pi-home was
unnecessary. The HOME redirect broke macOS keychain default resolution
under ucode: the Security framework looks for the login keychain under
the redirected HOME, finds none, and security default-keychain returns
'A default keychain could not be found'. As a result gh auth, the git
credential helper, and any keychain-backed tool failed inside pi.

Setting PI_CODING_AGENT_DIR to the existing PI_CONFIG_DIR preserves
config isolation (models.json/settings.json/sessions still land under
APP_DIR/pi-home/.pi/agent) while leaving /Users/david.okeeffe as the user's real home,
so the login keychain stays discoverable.

Tests: the two pi e2e sites monkeypatched PI_UCODE_HOME/PI_CONFIG_PATH
to redirect pi at a tmp home. They now also patch PI_CONFIG_DIR (read by
build_runtime_env) and PI_SETTINGS_PATH/PI_SETTINGS_BACKUP_PATH (previously
masked because the HOME redirect made pi read settings from the un-patched
real APP_DIR path).
Expose the GLM and Kimi coding-model cohort through Pi and OpenCode with shared token limits and reasoning metadata. Keep unsupported chat models out of discovery, including Inkling until gateway issue databricks#215 is fixed, and retain the GPT-OSS Responses API routing guard.
Centralize Claude family/version parsing so Pi metadata, adaptive-thinking compatibility, and Claude Code's [1m] selector cannot drift. Cover Sonnet 4.5, Opus 4.6, future major versions, Fable fallback, and prefixed model IDs.
Configure the Databricks OpenAI Responses provider alongside the validated GLM/Kimi provider, and fall back to foundation-model serving endpoints when UC model services are unavailable.
…/consolidated-pi-opencode-upstream

# Conflicts:
#	tests/test_e2e.py
`_pi_gpt_model_entry` declared `reasoning: True` without an off-state, so for
the thinking-off case Pi's Responses builder fell back to
`reasoning: {effort: "none"}` (pi-ai openai-responses.js, the
`thinkingLevelMap?.off !== null` branch). `"none"` is only valid on gpt-5.1+,
so every request to gpt-5, gpt-5-mini, gpt-5-nano and gpt-5-5-pro was rejected:

  BAD_REQUEST: Unsupported value: 'none' is not supported with the 'gpt-5'
  model. Supported values are: 'minimal', 'low', 'medium', and 'high'.

Setting `thinkingLevelMap: {"off": None}` makes Pi omit `reasoning` entirely,
which the gateway accepts for all 14 codex ids. Verified against
/ai-gateway/codex/v1/responses: effort="none" 400s on gpt-5/-mini/-nano/-5-5-pro
and 200s on gpt-5-1..-5-6; omitting `reasoning` is 200 everywhere.
`{"off": "minimal"}` was rejected as an alternative because gpt-5-5-pro 400s on
it too. Same pattern already used for the Gemini 3.x entries.

The rest of Pi's Responses payload was bisected against the gateway and is
fine: store:false, prompt_cache_key, prompt_cache_retention:"24h",
prompt_cache_options, include:["reasoning.encrypted_content"], developer role,
flat tool schemas, and the session_id / x-client-request-id affinity headers.

Regression was hard to spot because the gateway returns
{"error_code","message"} rather than OpenAI's {"error":...}, so Pi's
error-body.js recovery no-ops and every 400 renders as
"OpenAI API error (400): 400 status code (no body)". Reported upstream as
earendil-works/pi#7748.

Refs databricks#286
…i-opencode-upstream

# Conflicts:
#	src/ucode/agents/pi.py
#	src/ucode/databricks.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pi: add session-affinity headers and sanitized prompt-cache diagnostics

1 participant