Skip to content

feat(toolsets): code_execution toolset ("code mode") to cut tool-call token cost - #2333

Open
naomi-robusta wants to merge 12 commits into
masterfrom
claude/holmes-integration-plan-rxa8ay
Open

naomi-robusta wants to merge 12 commits into
masterfrom
claude/holmes-integration-plan-rxa8ay

Conversation

@naomi-robusta

@naomi-robusta naomi-robusta commented Jul 29, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds an opt-in code_execution toolset implementing "code mode": instead of the LLM emitting one tool call per agentic step (re-sending all prior results each turn), it writes one Python script that composes many read-only Holmes tools and filters results in code, so only what it print()s enters the model's context. This targets the biggest token sink in production — intermediate-result bloat on multi-step investigations (≥6-iteration requests are ~66% of spend).

Design doc: docs/design/2026-07-28_holmes-code-mode.md (included in this PR — design and implementation reviewed together). Ticket: ROB-723.

How it works

  • run_python_code(code, timeout?) runs the script in a subprocess. A generated holmes object exposes one function per eligible tool; each call is relayed over a unix-domain socket back to the parent, which dispatches into the real ToolExecutor. Credentials never leave the parent process — the subprocess can only ask the parent to run an allow-listed tool.
  • Read-only surface only. is_core toolsets, bash/kubectl_run, and any approval_required_tools are excluded from the generated client. Because a synchronous script can't pause for interactive approval, an APPROVAL_REQUIRED result at dispatch is denied (the model is told to call that tool directly) — mirroring the trust model in the remote-tool-execution design.
  • Reuses the existing ulimit memory cap; oversized stdout flows through the loop's existing spill_oversized_tool_result guard. Off by default (feature-flagged via enabled=False).
  • ToolCallingLLM gains a small generic set_tool_executor wiring hook (no code-mode-specific coupling).

Testing

Deterministic token-reduction proof (test_code_mode_token_reduction.py) — measures the exact context-token cost of a tool's output, classic vs code mode, for identical data, using the same formatter (format_tool_result_data) and tokenizer (litellm.token_counter, gpt-4o) the product uses to size/spill results. No LLM, no cluster, no RNG:

Sink Classic (tokens in context) Code mode Reduction
Result filtering (6000-event log → "how many ERROR?") 250,029 136 99.95%
Call consolidation (8 sub-calls → 1 result) 30,952 275 99.11%

(Correctness asserted too — code mode computes the right answer from the same data.)

Unit / integration — 21 tests (real subprocess, no mocking of the runner): eligibility (is_core/bash/approval-gated/self excluded), the token-saving property, and failure modes (syntax error, runtime exception, timeout, unavailable tool, erroring sub-tool, approval-gated sub-tool denied-not-paused, unwired executor, timeout clamping). No regressions in test_tool_calling_llm.py / test_tool_executor.py (47 passing).

LLM evals — two ask_holmes A/B evals (toolsets_matrix: [default] vs [codemode]), 4/4 passing in CI on opus-4.6 (run 30478931611). These assert correctness parity (code mode doesn't regress the answer) — they are deliberately not the token-win proof: Holmes's server-side filters (kubernetes_jq_query, log filter) already keep k8s data out of context, and the live A/B run confirmed the model kept using those rather than code mode, so the small fixtures show no token delta. The deterministic test above is where the token win is proven. Run on demand in eval-regression.yaml.

Toolset checklist

  • Unit tests (+ deterministic token-reduction proof)
  • config_classes defined (CodeExecutionConfig)
  • Prerequisite check (verifies python3 availability)
  • ask_holmes evals — A/B toolsets_matrix, 4/4 passing in CI (correctness parity)
  • Documentation page — follow-up (docs/data-sources/builtin-toolsets/code-execution.md)

Notes / follow-ups

  • Observability: sub-calls are recorded (name/status/timing) and summarized in the result today. Live per-sub-call SSE streaming to the UI is a follow-up requiring a loop-level streaming hook + a parent_tool_call_id field (see the design doc; frontend PR to follow).
  • This is a first, conservative v1 (read-only, subprocess + ulimit, no language-level sandbox). Hardening the sandbox is a documented Future Goal.

🤖 Generated with Claude Code


Generated by Claude Code

…ll tokens

Adds an opt-in `code_execution` toolset whose `run_python_code` tool lets the
LLM write one Python script that composes many read-only Holmes tools and
filters results in code, so only what it print()s enters the model context —
cutting token cost on multi-step investigations.

- Subprocess execution with a unix-socket bridge: the script's `holmes` object
  relays each tool call back to the parent, which dispatches into the real
  ToolExecutor. Credentials never leave the parent process.
- Read-only surface only: is_core, bash/kubectl_run, and approval-gated tools
  are excluded from the generated client; an APPROVAL_REQUIRED result at
  dispatch is denied (a synchronous script cannot pause for approval).
- Reuses the ulimit memory cap; oversized stdout flows through the existing
  spill-to-disk guard in the loop. Off by default (feature-flagged).
- ToolCallingLLM gains a generic set_tool_executor wiring hook.

Tests: 21 unit/integration tests (real subprocess) covering eligibility,
call consolidation, result filtering, and failure modes (syntax/runtime error,
timeout, unavailable/approval-gated/erroring sub-tools, unwired executor,
timeout clamping).

Design: relay/docs/design/2026-07-28_holmes-code-mode.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WMU8v729xhwopf4QcrJcPU
Signed-off-by: Claude <noreply@anthropic.com>

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds an opt-in Python code execution toolset. User scripts run in a subprocess and call eligible Holmes tools through a Unix socket bridge, with timeout handling, structured results, dynamic instructions, executor wiring, and integration tests.

Changes

Code mode execution

Layer / File(s) Summary
Executor wiring and toolset registration
holmes/core/tool_calling_llm.py, holmes/core/tools.py, holmes/plugins/toolsets/*, tests/plugins/toolsets/code_execution/test_code_execution_wiring.py
Registers CodeExecutionToolset, adds executor context, and wires compatible toolsets during ToolCallingLLM construction.
Eligible tool client generation
holmes/plugins/toolsets/code_execution/client_generator.py, holmes/plugins/toolsets/code_execution/code_execution_config.py
Filters eligible tools, sanitizes Python attributes, generates tool specifications and API references, and defines timeout settings.
Subprocess bridge and runner
holmes/plugins/toolsets/code_execution/bridge.py, holmes/plugins/toolsets/code_execution/runner.py
Adds Unix-socket request dispatch, structured responses, subcall records, generated holmes functions, and runner exit handling.
Code execution orchestration and validation
holmes/plugins/toolsets/code_execution/code_execution_toolset.py, tests/plugins/toolsets/code_execution/test_code_execution.py, docs/design/2026-07-28_holmes-code-mode.md
Runs scripts with allowlisted tool calls, enforces timeouts, aggregates output, maps failures to structured results, and documents and tests execution behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ToolCallingLLM
  participant CodeExecutionToolset
  participant ToolCallBridge
  participant runner.py
  participant ToolExecutor
  ToolCallingLLM->>CodeExecutionToolset: wire shared ToolExecutor
  CodeExecutionToolset->>ToolCallBridge: start bridge and runner process
  runner.py->>ToolCallBridge: request eligible tool call
  ToolCallBridge->>ToolExecutor: dispatch tool and params
  ToolExecutor-->>ToolCallBridge: return structured result
  ToolCallBridge-->>runner.py: send JSON response
  runner.py-->>CodeExecutionToolset: return script output and status
Loading

Suggested reviewers: moshemorad, sheeproid, avi-robusta

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.84% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the new opt-in code_execution toolset and its purpose of reducing tool-call token cost.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@netlify

netlify Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for holmes-docs ready!

Name Link
🔨 Latest commit 49847b1
🔍 Latest deploy log https://app.netlify.com/projects/holmes-docs/deploys/6a807bab20eb73000814cc0f
😎 Deploy Preview https://deploy-preview-2333--holmes-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions

github-actions Bot commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

✅ Docker images ready for 3bb208bb2 (built in 6m 9s)

⚠️ Warning: does not support ARM (ARM images are built on release only - not on every PR)

Use these tags to pull the images for testing.

📋 Copy commands

⚠️ Temporary images are deleted after 30 days. Copy to a permanent registry before using them:

gcloud auth configure-docker us-central1-docker.pkg.dev
docker pull us-central1-docker.pkg.dev/robusta-development/temporary-builds/holmes:3bb208bb2
docker tag us-central1-docker.pkg.dev/robusta-development/temporary-builds/holmes:3bb208bb2 me-west1-docker.pkg.dev/robusta-development/development/holmes-dev:3bb208bb2
docker push me-west1-docker.pkg.dev/robusta-development/development/holmes-dev:3bb208bb2
docker pull us-central1-docker.pkg.dev/robusta-development/temporary-builds/holmes-operator:3bb208bb2
docker tag us-central1-docker.pkg.dev/robusta-development/temporary-builds/holmes-operator:3bb208bb2 me-west1-docker.pkg.dev/robusta-development/development/holmes-operator-dev:3bb208bb2
docker push me-west1-docker.pkg.dev/robusta-development/development/holmes-operator-dev:3bb208bb2

Patch Helm values in one line (choose the chart you use):

HolmesGPT chart:

helm upgrade --install holmesgpt ./helm/holmes \
  --set registry=me-west1-docker.pkg.dev/robusta-development/development \
  --set image=holmes-dev:3bb208bb2 \
  --set operator.registry=me-west1-docker.pkg.dev/robusta-development/development \
  --set operator.image=holmes-operator-dev:3bb208bb2

Robusta wrapper chart:

helm upgrade --install robusta robusta/robusta \
  --reuse-values \
  --set holmes.registry=me-west1-docker.pkg.dev/robusta-development/development \
  --set holmes.image=holmes-dev:3bb208bb2 \
  --set holmes.operator.registry=me-west1-docker.pkg.dev/robusta-development/development \
  --set holmes.operator.image=holmes-operator-dev:3bb208bb2

Copy link
Copy Markdown
Collaborator Author

The Benchmark Master and Benchmark PR checks both fail with the identical RuntimeError: Cold start e2e benchmark failed (scripts/cli_performance_benchmark.py). Since the Benchmark Master job runs the master baseline — which this PR's diff cannot affect — this is a pre-existing benchmark-infra failure (the cold-start e2e holmes ask needs an LLM key/model that isn't available to this run), not something introduced here.

This change adds a new opt-in, off-by-default toolset, so I'm not touching the benchmark. I'm watching the actual unit-test gate (build-and-test) and will drive that to green if anything there fails.


Generated by Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@holmes/core/tool_calling_llm.py`:
- Around line 216-237: Avoid invoking _wire_toolset_executors during every
ToolCallingLLM construction, especially clones created by with_executor(),
because it mutates shared toolset state and can race across requests. Move
wiring to the owning initialization path or otherwise ensure each request
receives isolated toolset instances before set_tool_executor is called, while
preserving sibling tool dispatch behavior.

In `@holmes/plugins/toolsets/code_execution/code_execution_toolset.py`:
- Around line 337-345: Make the executor association used by run_python_code
request-scoped rather than mutating shared CodeExecutionToolset state in
set_tool_executor. Update the ToolCallingLLM wiring and relevant execution path
so concurrent with_executor() requests cannot overwrite each other’s executor,
while preserving instruction rendering with the current executor context.
- Around line 1-12: Update the subprocess environment construction in the code
execution toolset to use an explicit minimal allowlist instead of merging
os.environ. Preserve only the required runtime variables, such as PATH and the
configured Python/runtime settings, while excluding credentials and unrelated
parent environment values; apply the same change to the additional environment
construction around the second referenced location.
- Around line 302-305: Update get_parameterized_one_liner to treat a null code
value the same as a missing or empty value before calling string methods,
returning an empty one-liner safely while preserving the existing first-line
truncation behavior for valid strings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 70806f45-45cb-4ad0-aab1-a79b18477673

📥 Commits

Reviewing files that changed from the base of the PR and between 4047b5f and 154f824.

📒 Files selected for processing (10)
  • holmes/core/tool_calling_llm.py
  • holmes/plugins/toolsets/__init__.py
  • holmes/plugins/toolsets/code_execution/__init__.py
  • holmes/plugins/toolsets/code_execution/bridge.py
  • holmes/plugins/toolsets/code_execution/client_generator.py
  • holmes/plugins/toolsets/code_execution/code_execution_config.py
  • holmes/plugins/toolsets/code_execution/code_execution_toolset.py
  • holmes/plugins/toolsets/code_execution/runner.py
  • tests/plugins/toolsets/code_execution/test_code_execution.py
  • tests/plugins/toolsets/code_execution/test_code_execution_wiring.py

Comment on lines +216 to +237
self._wire_toolset_executors()

def _wire_toolset_executors(self) -> None:
"""Give toolsets that opt in (via a ``set_tool_executor`` method) a
back-reference to the ToolExecutor so they can dispatch sibling tool
calls — used by the code_execution ("code mode") toolset. Generic hook:
no code-mode-specific coupling here.
"""
toolsets = getattr(self.tool_executor, "toolsets", None)
if not isinstance(toolsets, (list, tuple)):
return # e.g. a mocked tool_executor in tests
for toolset in toolsets:
setter = getattr(toolset, "set_tool_executor", None)
if callable(setter):
try:
setter(self.tool_executor)
except Exception:
logging.warning(
"failed wiring tool_executor into toolset %s",
getattr(toolset, "name", "?"),
exc_info=True,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Wiring on every construction mutates shared toolset state — see cross-file race in code_execution_toolset.py.

_wire_toolset_executors() runs on every ToolCallingLLM.__init__, including the per-request clones created by with_executor() (Line 239-252, whose docstring says it exists to avoid "mutating the shared ToolCallingLLM instance"). Since it writes into the toolset objects themselves (not into this new instance), and those toolset instances appear to be reused across requests, concurrent with_executor() calls can race on CodeExecutionToolset._tool_executor. See consolidated comment for the full analysis and fix options.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holmes/core/tool_calling_llm.py` around lines 216 - 237, Avoid invoking
_wire_toolset_executors during every ToolCallingLLM construction, especially
clones created by with_executor(), because it mutates shared toolset state and
can race across requests. Move wiring to the owning initialization path or
otherwise ensure each request receives isolated toolset instances before
set_tool_executor is called, while preserving sibling tool dispatch behavior.

Comment thread holmes/plugins/toolsets/code_execution/code_execution_toolset.py
Comment thread holmes/plugins/toolsets/code_execution/code_execution_toolset.py
Comment thread holmes/plugins/toolsets/code_execution/code_execution_toolset.py
claude added 2 commits July 29, 2026 12:57
CodeExecutionConfig must subclass ToolsetConfig (not BaseModel) so it
implements build_schema_entry(); otherwise holmes_sync_toolsets_status ->
get_config_schema() raises AttributeError, failing test_toolsets_dumpable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WMU8v729xhwopf4QcrJcPU
Signed-off-by: Claude <noreply@anthropic.com>
…st-scoped executor

- Security (critical): the subprocess no longer inherits the parent os.environ.
  A minimal allowlist (PATH/locale + the bridge handoff vars) is built instead,
  so LLM-authored code can't read Holmes's credentials. Matches the "credentials
  never leave the parent" contract. Adds a regression test.
- Stability: get_parameterized_one_liner tolerates {"code": null} (runs outside
  the invoke try/except, so it must not raise). Adds a test.
- Concurrency: run_python_code resolves the ToolExecutor from the request-scoped
  ToolInvokeContext.tool_executor (set by the loop) instead of shared toolset
  state, so concurrent with_executor() clones can't race. set_tool_executor is
  kept only for rendering llm_instructions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WMU8v729xhwopf4QcrJcPU
Signed-off-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Thanks for the review — all four findings were valid and are addressed in a7954cc:

  1. 🔴 Subprocess inherited the full parent env (credential leak). Correct and important — it contradicted the "credentials never leave the parent" contract. The subprocess env is now built from a minimal allowlist (PATH, locale vars, and the three HOLMES_CODE_* bridge vars) instead of {**os.environ}. Added a regression test (test_subprocess_env_excludes_parent_secrets) that sets a secret env var and asserts the script can't see it.
  2. 🟠 get_parameterized_one_liner crashed on {"code": null}. Fixed with params.get("code") or ""; added test_null_code_one_liner_does_not_crash (it runs outside the invoke try/except, so this matters).
    3–4. 🟠 Shared, externally-mutable _tool_executor race across with_executor() clones. run_python_code now resolves the executor from the request-scoped ToolInvokeContext.tool_executor (set by the loop in _directly_invoke_tool_call) rather than shared toolset state, so concurrent clones can't overwrite each other. set_tool_executor is retained only for rendering llm_instructions.

All 23 code-execution tests + the test_holmes_sync_toolsets / test_tool_calling_llm suites pass locally. The Benchmark Master/PR checks remain red on the pre-existing cold-start-e2e infra failure (reproduces on the master baseline, as noted above) — not related to these changes.


Generated by Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@holmes/core/tools.py`:
- Around line 268-271: Exclude the request-scoped tool_executor field from
ToolInvokeContext.model_dump() serialization by declaring it with
Field(exclude=True), preserving its runtime availability while preventing
executor, toolset, and configuration state from appearing in dumped context.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8093bebb-446a-4bd0-a4e3-3efb1d57fe03

📥 Commits

Reviewing files that changed from the base of the PR and between 38a3783 and a7954cc.

📒 Files selected for processing (4)
  • holmes/core/tool_calling_llm.py
  • holmes/core/tools.py
  • holmes/plugins/toolsets/code_execution/code_execution_toolset.py
  • tests/plugins/toolsets/code_execution/test_code_execution.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • holmes/core/tool_calling_llm.py
  • holmes/plugins/toolsets/code_execution/code_execution_toolset.py
  • tests/plugins/toolsets/code_execution/test_code_execution.py

Comment thread holmes/core/tools.py Outdated
claude added 2 commits July 29, 2026 13:04
…dump

Mark ToolInvokeContext.tool_executor with Field(exclude=True) so the executor
(and the toolset/config state it references) never appears in serialized/logged
context. Runtime access is unchanged. Adds a regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WMU8v729xhwopf4QcrJcPU
Signed-off-by: Claude <noreply@anthropic.com>
Move the code-mode design doc into the holmesgpt repo (docs/design/)
alongside the implementation, instead of tracking it in the relay repo.
This keeps the design and its implementation in a single PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WMU8v729xhwopf4QcrJcPU
Signed-off-by: Claude <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/design/2026-07-28_holmes-code-mode.md`:
- Around line 274-280: Update both fenced documentation blocks in the design
document, including the block containing the holmes module listing and the block
around the additional referenced section, to specify an appropriate language
identifier such as text. Preserve the existing diagram and code-like content
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7d126761-2abb-47dc-bb7e-94e25ed81901

📥 Commits

Reviewing files that changed from the base of the PR and between 7061a90 and d544f64.

📒 Files selected for processing (1)
  • docs/design/2026-07-28_holmes-code-mode.md

Comment thread docs/design/2026-07-28_holmes-code-mode.md Outdated
claude added 2 commits July 29, 2026 17:37
Specify 'text' on the two bare code fences (module listing + data-flow
diagram) so markdownlint MD040 passes and rendering stays consistent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WMU8v729xhwopf4QcrJcPU
Signed-off-by: Claude <noreply@anthropic.com>
Two ask_holmes evals exercising the new code_execution toolset as an A/B
matrix over toolset config (toolsets_matrix):

- 285_code_mode_count_configmaps: count ConfigMaps across namespaces
  ([default] classic vs [codemode]). Targets the call-consolidation sink.
- 286_code_mode_large_configmap_filter: find one needle in a ~56k-token
  ConfigMap ([default] vs [codemode]). Targets the intermediate-result
  bloat sink; classic path mirrors the passing eval 247.

Both are correctness-gated on hallucination-proof exact values (parity: code
mode must not regress the answer); per-variant token/LLM-call counts recorded
by the harness quantify the win. Runnable in eval-regression.yaml on demand.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WMU8v729xhwopf4QcrJcPU
Signed-off-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

@naomi-robusta Your eval run has finished. ✅ Completed successfully


🧪 Manual Eval Results

Parameter Value
Triggered via workflow_dispatch
Branch claude/holmes-integration-plan-rxa8ay
Model opus-4.6
Tags all LLM tests
ID (-k) 285_code_mode_count_configmaps or 286_code_mode_large_configmap_filter
Iterations 1
Duration 1m 56s
Workflow View logs | Rerun

Results of HolmesGPT evals

  • ask_holmes: 4/4 test cases were successful, 0 regressions

⚠️ Warning: this eval run contains 2 denied bash commands.

Status Test case Time Turns Tools Cost Total tokens Input Max input Output Max output Cached Non-cached Reasoning Skill Generated Skills Read Compactions Denied commands Src
✅ 285_code_mode_count_configmaps[0][codemode] 12.8s 3 4 $0.1495 49,261 48,734 17,165 527 275 31,565 17,169 36 — — — — src
✅ 285_code_mode_count_configmaps[0][default] 12.1s 3 4 $0.1412 46,071 45,552 16,103 519 275 29,445 16,107 29 — — — — src
✅ 286_code_mode_large_configmap_filter[codemode] 15.3s 4 3 $0.1609 65,825 65,192 17,020 633 239 48,167 17,025 127 — — — kubectl get configmap platform-config -n app-286 -o jsonpath='{.data.platform-config\.yaml}' | sed -n '3580,3610p' src
✅ 286_code_mode_large_configmap_filter[default] 14.7s 4 3 $0.1502 61,535 60,964 15,962 571 238 44,997 15,967 81 — — — kubectl get configmap platform-config -n app-286 -o jsonpath='{.data.platform-config\.yaml}' | sed -n '3580,3610p' src
Total 13.7s avg 3.5 avg 3.5 avg $0.6018 222,692 220,442 17,165 2,250 275 154,174 66,268 273 — — — 2
Benchmark Comparison Details

Master baseline: latest master-* experiment (post-merge regression eval)
Status: 14 test/model combinations loaded

Benchmark baseline: latest ci-benchmark experiment on master
Status: 260 test/model combinations loaded

No baseline data available for comparison.

Comparison indicators:

  • ±0% — diff under 10% (within noise threshold)
  • ↑N%/↓N% — diff 10-25%
  • ↑N%/↓N% — diff over 25% (significant)
📖 Legend
Icon Meaning
✅ The test was successful
➖ The test was skipped
⚠️ The test failed but is known to be flaky or known to fail
🚧 The test had a setup failure (not a code regression)
🔧 The test failed due to mock data issues (not a code regression)
🚫 The test was throttled by API rate limits/overload
❌ The test failed and should be fixed before merging the PR
🔄 Re-run evals manually

⚠️ Warning: /eval comments always run using the workflow from master, not from this PR branch. If you modified the GitHub Action (e.g., added secrets or env vars), those changes won't take effect.

To test workflow changes, use the GitHub CLI or Actions UI instead:

gh workflow run eval-regression.yaml --repo HolmesGPT/holmesgpt --ref claude/holmes-integration-plan-rxa8ay -f markers=regression -f filter=

Option 1: Comment on this PR with /eval:

/eval
tags: regression

Or with more options (one per line):

/eval
model: gpt-4o
tags: regression
id: 09_crashpod
iterations: 5

Run evals on a different branch (e.g., master) for comparison:

/eval
branch: master
tags: regression
Option Description
model Model(s) to test (default: same as automatic runs)
tags Pytest tags / markers (no default - runs all tests!)
id Eval ID / pytest -k filter (use /list to see valid eval names)
iterations Number of runs, max 10
branch Run evals on a different branch (for cross-branch comparison)

Quick re-run: Use /rerun to re-run the most recent /eval on this PR with the same parameters.

Option 2: Trigger via GitHub Actions UI → "Run workflow"

Option 3: Add PR labels to include extra evals (applies to both automatic runs and /eval comments):

Label Effect
evals-tag-<name> Run tests with tag <name> alongside regression
evals-id-<name> Run a specific eval by test ID
evals-model-<name> Override the model (use model list name, e.g. sonnet-4.5)

Examples: evals-tag-easy, evals-id-09_crashpod, evals-model-sonnet-4.5

🏷️ Valid tags

benchmark, chain-of-causation, compaction, confluence, context_window, conversation_worker, coralogix, counting, database, datadog, datetime, db-connectors, easy, elasticsearch, embeds, fable-not-opus, fast, frontend, grafana, hard, images, integration, kafka, kubernetes, leaked-information, logs, loki, manual, mcp, medium, metrics, multi-cluster, network, newrelic, no-cicd, numerical, one-test, port-forward, prometheus, question-answer, regression, skills, slackbot, storage, token-limit, toolset-limitation, traces, transparency, victorialogs

🤖 Valid models

deepseek-chat, deepseek-r1-reasoner, deepseek-reasoner, deepseek-v3.2-chat, fable-5, gemini-3-flash-preview, gemini-3-pro-preview, gemini-3.1-pro-preview, gpt-4.1, gpt-5.2-high-reasoning, gpt-5.3-codex, gpt-5.4, gpt-5.5, haiku-4.5, kimi-2.5, kimi-2.5-openrouter, opus-4.5, opus-4.6, opus-4.7, opus-4.8, qwen-next-80B-instruct, qwen-next-80B-thinking, sonnet-4.5, sonnet-4.6


Commands: /eval · /rerun · /list

CLI: gh workflow run eval-regression.yaml --repo HolmesGPT/holmesgpt --ref claude/holmes-integration-plan-rxa8ay -f markers=regression -f filter=

claude added 4 commits August 2, 2026 12:11
Measures the exact context-token cost of a tool's output — classic (result
returned directly) vs code mode (filtered in a script, only the summary
returned) — using the SAME formatter (format_tool_result_data) and the SAME
tokenizer (litellm.token_counter, gpt-4o) the product uses to size/spill
results. No LLM, no cluster, no RNG.

Measured on this data:
- result filtering: 250,029 -> 136 tokens (99.95% reduction), answer correct
- call consolidation (8 sub-calls -> 1 result): 30,952 -> 275 (99.11%)

Unlike the live A/B evals (which only assert correctness parity, since Holmes's
server-side filters already keep k8s data out of context and the model may not
choose code mode), this deterministically proves the token-saving mechanism.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WMU8v729xhwopf4QcrJcPU
Signed-off-by: Claude <noreply@anthropic.com>
Make the code-mode trust boundary explicit and tested:
- Prove the boundary is the parent-side allow-list, not the generated client:
  a script is given HOLMES_CODE_SOCKET, so tests forge raw socket requests
  (bypassing holmes.* stubs) to excluded tools (bash, is_core, approval-gated,
  unknown) and assert each is denied without the tool running; positive
  control confirms an eligible tool is reachable.
- Document the accepted v1 residual risk (no fs sandbox) with a test that a
  local file is still readable, to be flipped if isolation is added.
- Add a Security model section to the design doc: enforced boundary
  (allow-list, approval-not-bypassable, env isolation, per-tool validation,
  ulimit/timeout) and residual risk (no language/OS sandbox -> network + on-disk
  secret exfil under prompt injection; off by default; sandboxing a Future Goal).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WMU8v729xhwopf4QcrJcPU
Signed-off-by: Claude <noreply@anthropic.com>
Rewrite the Open Questions section to capture the current state: mark the
execution-bridge and tool-eligibility questions resolved (with how), keep the
still-open design questions (routing, cost attribution), and add the
implementation-stage questions surfaced while building — sandbox-before-default,
the live-eval demonstration gap, default-on criteria, live sub-call streaming,
serial sub-calls, prompt-overhead crossover, and the docs page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WMU8v729xhwopf4QcrJcPU
Signed-off-by: Claude <noreply@anthropic.com>
Rewrite to match the relay docs/design house template and read top-down:
overview/background/goals first, code-level detail moved into Implementation &
Verification and Detailed Implementation & Context. Present the current design
directly (no 'option A -> option B' history; resolved bridge/eligibility
decisions are stated as the design). Add a Code-mode-vs-bash-mode comparison
across memory, CPU, security, approval, whitelisting, and injection patterns.
Open Questions now lists only genuinely-open items.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WMU8v729xhwopf4QcrJcPU
Signed-off-by: Claude <noreply@anthropic.com>
Evals 285/286 assert correctness parity only; this one verifies the model
actually invokes code mode end-to-end. The prompt directs use of run_python_code,
bash is disabled so the model cannot shell-script around it, and
include_tool_calls lets the evaluator assert the run_python_code tool was called.
A pass means: the model chose code mode, the script dispatched the real
kubernetes tools through the bridge, and the aggregated count came back correct
(hallucination-proof exact values).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WMU8v729xhwopf4QcrJcPU
Signed-off-by: Claude <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

2 participants