Skip to content

ROB-3410 Disable unconfigured toolsets by default in CLI - #1658

Merged
aantn merged 5 commits into
masterfrom
claude/disable-failed-toolsets-SuEEV
Mar 8, 2026
Merged

aantn merged 5 commits into
masterfrom
claude/disable-failed-toolsets-SuEEV

Conversation

@naomi-robusta

@naomi-robusta naomi-robusta commented Mar 4, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

This PR implements intelligent auto-enable logic for toolsets that respects their configuration requirements. Toolsets are now only auto-enabled if they either have no required configuration, have all optional configuration fields, or have been provided with the necessary configuration by the user.

Key Changes

  • Added ToolsetConfig.has_required_fields() method (holmes/utils/pydantic_utils.py)

    • Inspects Pydantic model fields to determine if any required fields exist (fields without defaults or default factories)
    • Handles both Pydantic v2 and fallback cases
  • Added Toolset.should_auto_enable() method (holmes/core/tools.py)

    • Implements decision logic for auto-enabling toolsets based on:
      1. Already enabled or marked as default → enable
      2. No config classes (YAML/simple Python toolsets) → enable
      3. All config fields have defaults → enable
      4. Required config was provided by user → enable
      5. Required config missing → disable
    • Provides clear, documented rules for the auto-enable behavior
  • Updated ToolsetManager._list_all_toolsets() (holmes/core/toolset_manager.py)

    • Modified enable_all_toolsets logic to use should_auto_enable() instead of blindly enabling all toolsets
    • Added debug logging when toolsets are skipped due to missing required configuration
  • Added comprehensive test suite (tests/test_toolset_auto_enable.py)

    • Tests for has_required_fields() with various config scenarios
    • Tests for should_auto_enable() covering all decision paths
    • Helper utilities for creating test toolsets with different configurations

Implementation Details

The solution uses Pydantic's model_fields introspection to detect required fields by checking for the presence of defaults or default factories. The PydanticUndefined sentinel from pydantic_core is used to distinguish between explicitly set None defaults and truly required fields.

The auto-enable logic is conservative: toolsets with required configuration are only enabled if that configuration has been explicitly provided, preventing silent failures from missing required settings.

https://claude.ai/code/session_01XFmKgriPCWgsAZ6B6fCkBg

Summary by CodeRabbit

  • New Features
    • Toolsets auto-enable when no user-required configuration is needed or when configuration is already provided; if required configuration is missing, the toolset remains disabled and a debug message explains why.
  • Tests
    • Added tests covering detection of required config fields and multiple auto-enable scenarios.
  • Chores
    • Consolidated imports and improved compatibility for configuration-model handling.

Instead of blindly enabling all toolsets in CLI mode, only auto-enable
toolsets that have a reasonable chance of initializing:
- No config required (YAML toolsets, simple Python toolsets) → enable
- All config fields have defaults (e.g. Prometheus) → enable
- Config provided by user → enable
- Required config fields with no config provided → disable

This avoids slow and noisy prerequisite checks for toolsets like
Elasticsearch, Grafana, ServiceNow etc. when no config is provided.

https://claude.ai/code/session_01XFmKgriPCWgsAZ6B6fCkBg
Signed-off-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Mar 4, 2026 •

Copy link
Copy Markdown
Contributor

📂 Previous Runs

📜 Run @ 97a474e (#22713968495)

✅ Results of HolmesGPT evals

Automatically triggered by commit 97a474e on branch claude/disable-failed-toolsets-SuEEV

View workflow logs

Results of HolmesGPT evals

  • ask_holmes: 9/9 test cases were successful, 0 regressions
Status Test case Time Turns Tools Cost Tokens Compactions
✅ 09_crashpod 33.2s 5 11 $0.2416 107,978 —
✅ 101_loki_historical_logs_pod_deleted 41.0s 6 11 $0.2657 130,854 —
✅ 111_pod_names_contain_service 35.6s 6 11 $0.2379 122,626 —
✅ 112_find_pvcs_by_uuid 29.6s 5 7 $0.2267 105,084 —
✅ 12_job_crashing 36.6s 5 12 $0.2497 109,933 —
✅ 176_network_policy_blocking_traffic_no_runbooks 46.8s 7 16 $0.3079 160,016 —
✅ 24_misconfigured_pvc 35.0s 5 14 $0.2452 106,560 —
✅ 43_current_datetime_from_prompt 5.2s 1 — $0.1125 17,543 —
✅ 61_exact_match_counting 16.3s 3 2 $0.1558 57,044 —
Total 31.0s avg 4.8 avg 10.5 avg $2.0431 917,638 —
📜 Run @ f6fd301 (#22713644290)

✅ Results of HolmesGPT evals

Automatically triggered by commit f6fd301 on branch claude/disable-failed-toolsets-SuEEV

View workflow logs

Results of HolmesGPT evals

  • ask_holmes: 9/9 test cases were successful, 0 regressions
Status Test case Time Turns Tools Cost Tokens Compactions
✅ 09_crashpod 34.5s 6 11 $0.2446 125,022 —
✅ 101_loki_historical_logs_pod_deleted 43.1s 5 11 $0.2715 113,423 —
✅ 111_pod_names_contain_service 27.6s 4 9 $0.2026 80,601 —
✅ 112_find_pvcs_by_uuid 29.2s 5 6 $0.2253 106,689 —
✅ 12_job_crashing 38.3s 6 15 $0.2757 135,905 —
✅ 176_network_policy_blocking_traffic_no_runbooks 45.7s 6 14 $0.2753 134,635 —
✅ 24_misconfigured_pvc 35.1s 5 15 $0.2455 107,246 —
✅ 43_current_datetime_from_prompt 5.5s 1 — $0.0131 17,561 —
✅ 61_exact_match_counting 18.0s 4 4 $0.1709 77,896 —
Total 30.8s avg 4.7 avg 10.6 avg $1.9245 898,978 —
📜 Run @ bc64db8 (#22671174264)

✅ Results of HolmesGPT evals

Automatically triggered by commit bc64db8 on branch claude/disable-failed-toolsets-SuEEV

View workflow logs

Results of HolmesGPT evals

  • ask_holmes: 9/9 test cases were successful, 0 regressions
Status Test case Time Turns Tools Cost Tokens Compactions
✅ 09_crashpod 32.2s 5 12 $0.2398 108,040 —
✅ 101_loki_historical_logs_pod_deleted 47.4s 6 10 $0.2607 128,286 —
✅ 111_pod_names_contain_service 33.1s 5 12 $0.2367 105,737 —
✅ 112_find_pvcs_by_uuid 29.1s 5 7 $0.2197 103,958 —
✅ 12_job_crashing 34.9s 5 12 $0.2491 110,047 —
✅ 176_network_policy_blocking_traffic_no_runbooks 45.6s 7 17 $0.3079 154,587 —
✅ 24_misconfigured_pvc 42.6s 6 15 $0.2596 129,916 —
✅ 43_current_datetime_from_prompt 4.6s 1 — $0.1117 17,511 —
✅ 61_exact_match_counting 18.5s 4 4 $0.1696 77,808 —
Total 32.0s avg 4.9 avg 11.1 avg $2.0549 935,890 —

✅ Results of HolmesGPT evals

Automatically triggered by commit 0e48ad1 on branch claude/disable-failed-toolsets-SuEEV

View workflow logs

Results of HolmesGPT evals

  • ask_holmes: 9/9 test cases were successful, 0 regressions
Status Test case Time Turns Tools Cost Total tokens Input Output Cached Non-cached Reasoning Max output Compactions
✅ 09_crashpod 32.3s 6 11 $0.2506 125,463 123,609 1,854 98,589 25,020 — 563 —
✅ 101_loki_historical_logs_pod_deleted 29.8s 4 8 $0.2246 84,375 82,358 2,017 59,179 23,179 — 844 —
✅ 111_pod_names_contain_service 25.9s 4 9 $0.2119 81,231 79,570 1,661 56,691 22,879 — 617 —
✅ 112_find_pvcs_by_uuid 31.3s 5 8 $0.2448 111,371 109,521 1,850 84,509 25,012 — 636 —
✅ 12_job_crashing 29.7s 5 11 $0.2380 108,368 106,739 1,629 81,415 25,324 — 439 —
✅ 176_network_policy_blocking_traffic_no_runbooks 44.8s 8 18 $0.3121 183,538 181,012 2,526 153,314 27,698 — 439 —
✅ 24_misconfigured_pvc 36.8s 7 16 $0.2684 147,684 145,564 2,120 120,468 25,096 — 556 —
✅ 43_current_datetime_from_prompt 4.4s 1 — $0.1117 17,511 17,388 123 0 17,388 — 123 —
✅ 61_exact_match_counting 12.7s 3 2 $0.1514 56,510 56,082 428 36,495 19,587 — 241 —
Total 27.5s avg 4.8 avg 10.4 avg $2.0135 916,051 901,843 14,208 690,660 211,183 — 844 —

Benchmark comparison unavailable: No ci-benchmark experiments found

Benchmark Comparison Details

Baseline: latest ci-benchmark experiment on master

Status: No ci-benchmark experiments found

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/disable-failed-toolsets-SuEEV -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
filter: 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!)
filter 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 in automatic regression runs:

Label Effect
evals-tag-<name> Run tests with tag <name> alongside regression
evals-id-<name> Run a specific eval by test ID

Examples: evals-tag-easy, evals-id-09_crashpod

🏷️ Valid tags

benchmark, chain-of-causation, compaction, confluence, context_window, coralogix, counting, database, datadog, datetime, db-connectors, easy, elasticsearch, embeds, fast, frontend, grafana-dashboard, hard, integration, kafka, kubernetes, leaked-information, logs, loki, medium, metrics, network, newrelic, no-cicd, numerical, one-test, port-forward, prometheus, question-answer, regression, runbooks, slackbot, storage, toolset-limitation, traces, transparency


Commands: /eval · /rerun · /list

CLI: gh workflow run eval-regression.yaml --repo HolmesGPT/holmesgpt --ref claude/disable-failed-toolsets-SuEEV -f markers=regression -f filter=

@github-actions

github-actions Bot commented Mar 4, 2026 •

Copy link
Copy Markdown
Contributor

✅ Docker images ready for 7c13d55a (built in 7m 19s)

⚠️ 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:7c13d55a
docker tag us-central1-docker.pkg.dev/robusta-development/temporary-builds/holmes:7c13d55a me-west1-docker.pkg.dev/robusta-development/development/holmes-dev:7c13d55a
docker push me-west1-docker.pkg.dev/robusta-development/development/holmes-dev:7c13d55a
docker pull us-central1-docker.pkg.dev/robusta-development/temporary-builds/holmes-operator:7c13d55a
docker tag us-central1-docker.pkg.dev/robusta-development/temporary-builds/holmes-operator:7c13d55a me-west1-docker.pkg.dev/robusta-development/development/holmes-operator-dev:7c13d55a
docker push me-west1-docker.pkg.dev/robusta-development/development/holmes-operator-dev:7c13d55a

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:7c13d55a \
  --set operator.registry=me-west1-docker.pkg.dev/robusta-development/development \
  --set operator.image=holmes-operator-dev:7c13d55a

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:7c13d55a \
  --set holmes.operator.registry=me-west1-docker.pkg.dev/robusta-development/development \
  --set holmes.operator.image=holmes-operator-dev:7c13d55a

@coderabbitai

coderabbitai Bot commented Mar 4, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f5eb6a54-b3f1-41f8-bb8d-abaffd0289f4

📥 Commits

Reviewing files that changed from the base of the PR and between 97a474e and 0e48ad1.

📒 Files selected for processing (1)
  • holmes/core/tools.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • holmes/core/tools.py

Walkthrough

Adds Toolset.auto-enable decision logic via a new Toolset.should_auto_enable() method, a ToolsetConfig.has_required_fields() helper, integrates the auto-enable check into ToolsetManager._list_all_toolsets, centralizes Pydantic undefined handling, and adds tests validating auto-enable scenarios.

Changes

Cohort / File(s) Summary
Toolset core
holmes/core/tools.py
Added Toolset.should_auto_enable() to determine whether a toolset should be auto-enabled based on enabled/is_default, presence of config_classes, whether those classes have required fields, and whether explicit config is provided.
Toolset manager
holmes/core/toolset_manager.py
When enable_all_toolsets is true, only set toolset.enabled if toolset.should_auto_enable() returns true; otherwise emit a debug log indicating required configuration is missing.
Pydantic utilities
holmes/utils/pydantic_utils.py
Consolidated cross-version PydanticUndefined import and added ToolsetConfig.has_required_fields() to detect required fields on pydantic models.
TUI import fix
holmes/toolset_config_tui.py
Replaced local guarded import of PydanticUndefined with import from holmes.utils.pydantic_utils.
Tests
tests/test_toolset_auto_enable.py
New test module covering has_required_fields() and should_auto_enable() for optional vs required fields, provided vs missing config, and enabled/is_default variants.

Sequence Diagram(s)

sequenceDiagram
    participant Manager as ToolsetManager
    participant Toolset as Toolset
    participant Model as ToolsetConfigModel

    Manager->>Toolset: iterate toolsets (enable_all_toolsets)
    Toolset->>Toolset: should_auto_enable()
    Toolset->>Model: if config_classes present -> has_required_fields()
    Model-->>Toolset: returns True/False
    alt has_required == True and no config provided
        Toolset-->>Manager: return False (cannot auto-enable)
        Toolset->>Manager: debug log "requires configuration"
    else
        Toolset-->>Manager: return True (auto-enable)
        Manager->>Toolset: set enabled = True
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • moshemorad
  • RoiGlinik
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main objective of the PR: implementing smart auto-enable logic for toolsets based on configuration requirements, preventing unconfigured toolsets from being auto-enabled in the CLI.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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 and usage tips.

@netlify

netlify Bot commented Mar 4, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for holmes-docs ready!

Name Link
🔨 Latest commit 0e48ad1
🔍 Latest deploy log https://app.netlify.com/projects/holmes-docs/deploys/69ad41cdbe96c100070b5d47
😎 Deploy Preview https://deploy-preview-1658--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 Mar 4, 2026 •

Copy link
Copy Markdown
Contributor

🔬 CLI Performance Benchmark

🟡 Startup Time (no LLM)

Measures holmes version execution time (imports + initialization)

Metric PR Master Change
Cold Start 11.48s 10.83s +6.0%
Warm Mean 5.29s 4.90s +7.9%
Warm Min 5.27s 4.87s
Warm Max 5.31s 4.93s

🟡 Full CLI with LLM

Measures holmes ask execution time (OpenRouter + Haiku 4.5)

Metric PR Master Change
Cold Start 17.89s 25.60s -30.1%
Warm Mean 7.56s 7.35s +2.8%
Warm Min 7.51s 7.25s
Warm Max 7.63s 7.52s

PR: 7c13d55a | Master: a66714e7 | Iterations: 5

@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: 3

🧹 Nitpick comments (1)
tests/test_toolset_auto_enable.py (1)

1-1: Consider placing this test module under source-mirrored test paths.

This file tests behavior from holmes/core/tools.py and holmes/utils/pydantic_utils.py; placing it under matching tests/core/ and/or tests/utils/ paths would align with project test layout conventions.

As per coding guidelines, tests/**: “Tests should match source structure under tests/”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_toolset_auto_enable.py` at line 1, The test module is placed at
tests/test_toolset_auto_enable.py but exercises code in holmes/core/tools.py and
holmes/utils/pydantic_utils.py; move the file into a source-mirrored location
(e.g., tests/core/test_toolset_auto_enable.py or
tests/utils/test_toolset_auto_enable.py) so the tests mirror the package
structure, and update any imports or test discovery paths accordingly (ensure
references to functions/classes in holmes.core.tools and
holmes.utils.pydantic_utils remain correct after relocating).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@holmes/core/tools.py`:
- Around line 806-807: The current truthiness check "if self.config: return
True" treats an explicitly provided empty config (e.g., {}) as not provided;
change the check to explicitly compare against None (use "if self.config is not
None") so the method recognizes an explicitly provided empty config as provided;
update the condition in the method that references self.config to use "is not
None" rather than a truthy check.

In `@holmes/utils/pydantic_utils.py`:
- Around line 27-28: The fallback assignment for PydanticUndefined currently
sits under a broad "except Exception:" which can hide unrelated errors; change
that catch to "except ImportError:" (which covers ModuleNotFoundError) so only
import failures trigger the fallback where PydanticUndefined is defined, keeping
the rest of the code unchanged (look for the try/except that defines
PydanticUndefined in pydantic_utils.py).

In `@tests/test_toolset_auto_enable.py`:
- Around line 52-55: The helper is mutating the shared Toolset class by
assigning config_classes on type(toolset), which can leak between tests; instead
dynamically create a per-test subclass of Toolset (e.g., subclass_name =
type("TestToolset", (Toolset,), {"config_classes": config_classes}) ) and
instantiate that subclass so only the subclass's class attribute is modified;
update the helper to instantiate the new subclass rather than setting
type(toolset).config_classes to ensure test isolation for
Toolset.config_classes.

---

Nitpick comments:
In `@tests/test_toolset_auto_enable.py`:
- Line 1: The test module is placed at tests/test_toolset_auto_enable.py but
exercises code in holmes/core/tools.py and holmes/utils/pydantic_utils.py; move
the file into a source-mirrored location (e.g.,
tests/core/test_toolset_auto_enable.py or
tests/utils/test_toolset_auto_enable.py) so the tests mirror the package
structure, and update any imports or test discovery paths accordingly (ensure
references to functions/classes in holmes.core.tools and
holmes.utils.pydantic_utils remain correct after relocating).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e0a8f022-1134-4362-9219-e9192858e365

📥 Commits

Reviewing files that changed from the base of the PR and between 8b1aca7 and bc64db8.

📒 Files selected for processing (4)
  • holmes/core/tools.py
  • holmes/core/toolset_manager.py
  • holmes/utils/pydantic_utils.py
  • tests/test_toolset_auto_enable.py

Comment thread holmes/core/tools.py Outdated
Comment thread holmes/utils/pydantic_utils.py
Comment thread tests/test_toolset_auto_enable.py
claude added 3 commits March 5, 2026 10:28
- Use `is not None` instead of truthiness for self.config so an
  explicitly provided empty config ({}) is recognized as provided.
- Create per-test Toolset subclasses in tests to avoid leaking
  config_classes between test cases.
- Add test for empty-config edge case.

https://claude.ai/code/session_01XFmKgriPCWgsAZ6B6fCkBg
Signed-off-by: Claude <noreply@anthropic.com>
Import PydanticUndefined from holmes.utils.pydantic_utils instead of
duplicating the try/except block from pydantic_core.

https://claude.ai/code/session_01XFmKgriPCWgsAZ6B6fCkBg
Signed-off-by: Claude <noreply@anthropic.com>
@naomi-robusta naomi-robusta changed the title Add smart auto-enable logic for toolsets based on config requirements ROB-3410 Disable unconfigured toolsets by default Mar 8, 2026
@naomi-robusta naomi-robusta changed the title ROB-3410 Disable unconfigured toolsets by default ROB-3410 Disable unconfigured toolsets by default in CLI Mar 8, 2026
@aantn
aantn enabled auto-merge (squash) March 8, 2026 09:33
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.

3 participants