Skip to content

Improve historical data querying for logs and metrics - #905

Merged
arikalon1 merged 13 commits into
masterfrom
improve-historical-data
Aug 30, 2025
Merged

arikalon1 merged 13 commits into
masterfrom
improve-historical-data

Conversation

@aantn

@aantn aantn commented Aug 25, 2025 •

Copy link
Copy Markdown
Collaborator

This should be good to review and merge. Holmes is now more reliable at querying historical data in datadog for pods no longer in the cluster.

Full evals run for '-m logging or datadog'

Results table from CLI:

Screenshot 2025-08-26 at 8 18 11

I reviewed the residual failures and they are because:

  1. In 91f, holmes queried pod_name: api-gateway-* instead of pod_name: api-gateway-* (see run here) - this is a real failure and a target for future improvement, either by letting holmes run datadog queries raw or by updating tool description to mention * explicitly

  2. In 91a, if the entire namespace no longer exists in the cluster (not just the pod) holmes sometimes doesn't even try to fetch logs - this is a target for future improvement too

  3. Holmes brought metrics correctly from datadog but didn't render a graph due to ambiguous user questions (e.g. here)

Screenshot 2025-08-26 at 8 12 14

I verified in these cases that holmes really fetched the information with the right queries.

  1. Holmes brought metrics correctly and rendered a graph, but it was in intermediate output not the final output

@aantn
aantn requested a review from moshemorad August 25, 2025 07:00
@coderabbitai

coderabbitai Bot commented Aug 25, 2025 •

Copy link
Copy Markdown
Contributor

Walkthrough

Multiple logging toolsets delay creating their PodLoggingTool until after the base class finishes initializing so self.name exists. A new LoggingCapability.HISTORICAL_DATA was added and PodLoggingTool.description now references the toolset name and optionally notes historical-data support. Datadog docs, instructions, and test fixtures were updated to exercise historical-metrics/logs scenarios.

Changes

Cohort / File(s) Summary
Logging API & capability
holmes/plugins/toolsets/logging_utils/logging_api.py
Add LoggingCapability.HISTORICAL_DATA; include toolset name in PodLoggingTool.description and append historical-data text when capability present.
Two‑phase PodLoggingTool initialization (logging toolsets)
holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py, holmes/plugins/toolsets/datadog/toolset_datadog_logs.py, holmes/plugins/toolsets/grafana/toolset_grafana_loki.py, holmes/plugins/toolsets/kubernetes_logs.py, holmes/plugins/toolsets/opensearch/opensearch_logs.py
Defer PodLoggingTool creation: call super().__init__(tools=[]) then assign self.tools = [PodLoggingTool(self)] after parent init; add explanatory comments.
Datadog logs capability & init
holmes/plugins/toolsets/datadog/toolset_datadog_logs.py, holmes/plugins/toolsets/datadog/datadog_logs_instructions.jinja2
Declare HISTORICAL_DATA support, update description, add lazy reload of Datadog logs troubleshooting instructions (new template), and load instructions via helper.
Datadog metrics descriptions & instructions
holmes/plugins/toolsets/datadog/toolset_datadog_metrics.py, holmes/plugins/toolsets/datadog/datadog_metrics_instructions.jinja2
Update descriptive strings and instructions to explicitly reference Datadog, clarify exact-pod vs workload-name flows, and note historical-metrics behavior.
Tests — logging API
tests/plugins/toolsets/test_logging_api.py
Set mock_toolset.name = "test-logging-backend" in five tests so PodLoggingTool descriptions resolve during tests.
LLM test fixtures — Datadog logs/metrics
tests/llm/fixtures/test_ask_holmes/91f_datadog_logs_historical_pod/toolsets.yaml, tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/*, tests/llm/fixtures/test_ask_holmes/92_cpu_graph_conversation/*
Add/modify fixtures: disable kubernetes/logs in one fixture; add namespace/secret/deployment manifest and test_case for mismatched-pod Datadog metrics; add toolsets.yaml with Datadog keys; update conversation history and add before_test metric sender call.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Construct as Toolset constructor
    participant Base as BaseToolset
    participant PodTool as PodLoggingTool

    Construct->>Base: super().__init__(tools=[])
    activate Base
    Base-->>Construct: base init complete (self.name set)
    deactivate Base

    Note right of Construct: post-init create logging tool
    Construct->>PodTool: self.tools = [PodLoggingTool(self)]
    activate PodTool
    PodTool-->>Construct: initialized (description may include HISTORICAL_DATA)
    deactivate PodTool
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • moshemorad
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch improve-historical-data

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

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

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@aantn
aantn marked this pull request as ready for review August 25, 2025 18:15

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
holmes/plugins/toolsets/logging_utils/logging_api.py (1)

50-56: Add missing type hints to the Pydantic validator

Type hints are required by the repo guidelines. Please annotate the validator’s argument and return type to keep mypy happy with Pydantic v2’s expectations.

Apply this diff:

-    def convert_start_time_to_string(cls, v):
+    def convert_start_time_to_string(cls, v: object) -> object:
🧹 Nitpick comments (6)
tests/llm/fixtures/test_ask_holmes/91f_datadog_logs_historical_pod/toolsets.yaml (1)

5-7: Normalize boolean casing for consistency.

YAML booleans are case-insensitive, but we already use lowercase true for Datadog below. Recommend using false here for consistency.

-  kubernetes/logs:
-    enabled: False
+  kubernetes/logs:
+    enabled: false
holmes/plugins/toolsets/logging_utils/logging_api.py (2)

89-93: Avoid duplicating “historical data” messaging across toolset and tool descriptions

The tool description appends the historical-data note based on capability, while some toolsets (e.g., Datadog) also hard-code it in their toolset description. Consider relying on capability-driven text only to keep messages consistent in one place.


124-131: Documentation: start_time accepts integers too (converted to strings)

The parameter description says “negative string number,” but the validator also accepts ints. Clarify to reduce confusion and reflect test behavior.

Apply this diff:

-            "start_time": ToolParameter(
-                description=f"Start time for logs. Can be an RFC3339 formatted datetime (e.g. '2023-03-01T10:30:00Z') for absolute time or a negative string number (e.g. -3600) for relative seconds before end_time. Default: -{DEFAULT_TIME_SPAN_SECONDS} (last {DEFAULT_TIME_SPAN_SECONDS // SECONDS_PER_DAY} days)",
+            "start_time": ToolParameter(
+                description=f"Start time for logs. Can be an RFC3339 formatted datetime (e.g. '2023-03-01T10:30:00Z') for absolute time or a negative integer/string number (e.g. -3600) for relative seconds before end_time. Default: -{DEFAULT_TIME_SPAN_SECONDS} (last {DEFAULT_TIME_SPAN_SECONDS // SECONDS_PER_DAY} days)",
tests/plugins/toolsets/test_logging_api.py (1)

21-21: Good: tests now provide a toolset name for description augmentation

Setting mock_toolset.name in each test aligns with the new description logic and avoids attribute errors. To DRY the setup, consider a small helper factory inside this module to build a mock toolset with defaults (name, supported_capabilities, and fetch_pod_logs stub).

Example helper (optional, no markers involved):

def make_mock_toolset(name="test-logging-backend", capabilities=None):
    m = MagicMock(spec=BasePodLoggingToolset)
    m.name = name
    m.supported_capabilities = capabilities or set()
    m.fetch_pod_logs.return_value = StructuredToolResult(
        data="Sample logs", status=ToolResultStatus.SUCCESS
    )
    return m

You could also add one new test to assert the description wiring:

def test_description_includes_name_and_historical_capability():
    mock_toolset = make_mock_toolset(
        capabilities={LoggingCapability.HISTORICAL_DATA}
    )
    tool = PodLoggingTool(mock_toolset)
    assert "from test-logging-backend" in tool.description
    assert "including historical data" in tool.description

Also applies to: 60-60, 92-92, 121-121, 145-145

holmes/plugins/toolsets/datadog/toolset_datadog_logs.py (1)

85-88: Nit: avoid shadowing built-in ‘filter’ and escape backslashes too

Minor polish: rename the local variable to avoid shadowing Python’s built-in and also escape backslashes before quotes to keep the Datadog query well-formed when payload is JSON-encoded.

Apply this diff:

-    if params.filter:
-        filter = params.filter.replace('"', '\\"')
-        query += f' "{filter}"'
+    if params.filter:
+        filter_term = params.filter.replace("\\", "\\\\").replace('"', '\\"')
+        query += f' "{filter_term}"'
holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py (1)

31-36: Declare ‘config’ with a type to satisfy mypy (attribute set outside init)

This class sets self.config in prerequisites_callable, which can trip mypy for “attribute defined outside init”. Add a typed attribute at class level (or set it in init) to appease static checks.

Add near the top of the class:

 class CoralogixLogsToolset(BasePodLoggingToolset):
+    config: Optional[CoralogixConfig] = None
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 73b63fa and 9d88421.

📒 Files selected for processing (9)
  • holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py (1 hunks)
  • holmes/plugins/toolsets/datadog/toolset_datadog_logs.py (1 hunks)
  • holmes/plugins/toolsets/datadog/toolset_datadog_metrics.py (3 hunks)
  • holmes/plugins/toolsets/grafana/toolset_grafana_loki.py (1 hunks)
  • holmes/plugins/toolsets/kubernetes_logs.py (1 hunks)
  • holmes/plugins/toolsets/logging_utils/logging_api.py (2 hunks)
  • holmes/plugins/toolsets/opensearch/opensearch_logs.py (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/91f_datadog_logs_historical_pod/toolsets.yaml (1 hunks)
  • tests/plugins/toolsets/test_logging_api.py (5 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Always place Python imports at the top of the file, not inside functions or methods
Type hints are required (mypy is configured in pyproject.toml)

Files:

  • holmes/plugins/toolsets/logging_utils/logging_api.py
  • holmes/plugins/toolsets/kubernetes_logs.py
  • holmes/plugins/toolsets/opensearch/opensearch_logs.py
  • holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py
  • holmes/plugins/toolsets/grafana/toolset_grafana_loki.py
  • holmes/plugins/toolsets/datadog/toolset_datadog_logs.py
  • tests/plugins/toolsets/test_logging_api.py
  • holmes/plugins/toolsets/datadog/toolset_datadog_metrics.py
holmes/plugins/toolsets/**

📄 CodeRabbit inference engine (CLAUDE.md)

Toolsets must live under holmes/plugins/toolsets as either {name}.yaml or a {name}/ directory

Files:

  • holmes/plugins/toolsets/logging_utils/logging_api.py
  • holmes/plugins/toolsets/kubernetes_logs.py
  • holmes/plugins/toolsets/opensearch/opensearch_logs.py
  • holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py
  • holmes/plugins/toolsets/grafana/toolset_grafana_loki.py
  • holmes/plugins/toolsets/datadog/toolset_datadog_logs.py
  • holmes/plugins/toolsets/datadog/toolset_datadog_metrics.py
tests/llm/**/toolsets.yaml

📄 CodeRabbit inference engine (CLAUDE.md)

tests/llm/**/toolsets.yaml: Eval toolset overrides must be defined in a separate toolsets.yaml file in the test directory (do not put toolset config in test_case.yaml)
In toolsets.yaml, all toolset-specific configuration must be nested under a config field
Only the following top-level fields are allowed in toolsets YAML: enabled, name, description, additional_instructions, prerequisites, tools, docs_url, icon_url, installation_instructions, config, url (MCP only)

Files:

  • tests/llm/fixtures/test_ask_holmes/91f_datadog_logs_historical_pod/toolsets.yaml
tests/llm/**/*.{yaml,yml}

📄 CodeRabbit inference engine (CLAUDE.md)

tests/llm/**/*.{yaml,yml}: Each LLM eval test must use a dedicated Kubernetes namespace named app-
For Kubernetes-related eval assets, always use Secrets for scripts; do not embed scripts in inline manifests or ConfigMaps

Files:

  • tests/llm/fixtures/test_ask_holmes/91f_datadog_logs_historical_pod/toolsets.yaml
tests/llm/**

📄 CodeRabbit inference engine (CLAUDE.md)

Resource and file naming in evals should be neutral and must not hint at the problem (avoid names like broken-pod or crashloop-app)

Files:

  • tests/llm/fixtures/test_ask_holmes/91f_datadog_logs_historical_pod/toolsets.yaml
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Do not use invalid pytest markers; only use markers/tags declared in pyproject.toml

Files:

  • tests/plugins/toolsets/test_logging_api.py
🧠 Learnings (4)
📚 Learning: 2025-08-05T00:42:23.792Z
Learnt from: vishiy
PR: robusta-dev/holmesgpt#782
File: config.example.yaml:31-49
Timestamp: 2025-08-05T00:42:23.792Z
Learning: In robusta-dev/holmesgpt config.example.yaml, the azuremonitorlogs toolset configuration shows "enabled: true" as an example of how to enable the toolset, not as a default setting. The toolset is disabled by default and requires explicit enablement in user configurations.

Applied to files:

  • tests/llm/fixtures/test_ask_holmes/91f_datadog_logs_historical_pod/toolsets.yaml
📚 Learning: 2025-08-24T07:21:02.579Z
Learnt from: CR
PR: robusta-dev/holmesgpt#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-24T07:21:02.579Z
Learning: Applies to tests/llm/**/toolsets.yaml : Only the following top-level fields are allowed in toolsets YAML: enabled, name, description, additional_instructions, prerequisites, tools, docs_url, icon_url, installation_instructions, config, url (MCP only)

Applied to files:

  • tests/llm/fixtures/test_ask_holmes/91f_datadog_logs_historical_pod/toolsets.yaml
📚 Learning: 2025-08-24T07:21:02.579Z
Learnt from: CR
PR: robusta-dev/holmesgpt#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-24T07:21:02.579Z
Learning: Applies to tests/llm/**/toolsets.yaml : Eval toolset overrides must be defined in a separate toolsets.yaml file in the test directory (do not put toolset config in test_case.yaml)

Applied to files:

  • tests/llm/fixtures/test_ask_holmes/91f_datadog_logs_historical_pod/toolsets.yaml
📚 Learning: 2025-05-15T05:13:43.169Z
Learnt from: nherment
PR: robusta-dev/holmesgpt#408
File: holmes/plugins/toolsets/kubernetes_logs.py:90-97
Timestamp: 2025-05-15T05:13:43.169Z
Learning: In the Kubernetes logs toolset for Holmes, both current and previous logs are intentionally fetched and combined for each pod, even though this requires more API calls. This design ensures all logs are captured even when pods restart but retain their name, providing complete diagnostic information.

Applied to files:

  • holmes/plugins/toolsets/kubernetes_logs.py
🧬 Code graph analysis (6)
holmes/plugins/toolsets/logging_utils/logging_api.py (6)
holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py (1)
  • supported_capabilities (33-35)
holmes/plugins/toolsets/datadog/toolset_datadog_logs.py (1)
  • supported_capabilities (143-147)
holmes/plugins/toolsets/grafana/toolset_grafana_loki.py (1)
  • supported_capabilities (39-41)
holmes/plugins/toolsets/kubernetes_logs.py (1)
  • supported_capabilities (54-59)
holmes/plugins/toolsets/opensearch/opensearch_logs.py (1)
  • supported_capabilities (37-39)
tests/plugins/toolsets/test_toolset_utils.py (1)
  • supported_capabilities (175-176)
holmes/plugins/toolsets/kubernetes_logs.py (2)
holmes/core/tools.py (1)
  • ToolsetTag (109-112)
holmes/plugins/toolsets/logging_utils/logging_api.py (1)
  • PodLoggingTool (76-202)
holmes/plugins/toolsets/opensearch/opensearch_logs.py (1)
holmes/plugins/toolsets/logging_utils/logging_api.py (1)
  • PodLoggingTool (76-202)
holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py (2)
holmes/core/tools.py (1)
  • ToolsetTag (109-112)
holmes/plugins/toolsets/logging_utils/logging_api.py (1)
  • PodLoggingTool (76-202)
holmes/plugins/toolsets/grafana/toolset_grafana_loki.py (1)
holmes/plugins/toolsets/logging_utils/logging_api.py (1)
  • PodLoggingTool (76-202)
holmes/plugins/toolsets/datadog/toolset_datadog_logs.py (1)
holmes/plugins/toolsets/logging_utils/logging_api.py (2)
  • LoggingCapability (25-32)
  • PodLoggingTool (76-202)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: build (3.10)
  • GitHub Check: build (3.12)
  • GitHub Check: build (3.11)
  • GitHub Check: build (3.11)
  • GitHub Check: build (3.12)
  • GitHub Check: build (3.10)
  • GitHub Check: llm_evals
🔇 Additional comments (11)
tests/llm/fixtures/test_ask_holmes/91f_datadog_logs_historical_pod/toolsets.yaml (2)

5-7: Disable k8s logs explicitly here — good separation of concerns for this eval.

  • Using a separate toolsets.yaml to override toolset enablement is aligned with the guidelines.
  • Per the learnings, toolset-specific config is correctly nested under config for Datadog, and the only fields used under each toolset key are allowed.

4-21: No namespace issues found in 91f_datadog_logs_historical_pod fixture

I’ve checked the contents of this test fixture:

  • There are no standalone Kubernetes manifests in this directory that declare a namespace: field.
  • The test_case.yaml’s user_prompt correctly specifies app-91f as the namespace.
  • The before_test script invokes send_datadog_logs.sh app-91f, so all log‐pushing operations use the intended namespace.

No changes are needed.

holmes/plugins/toolsets/opensearch/opensearch_logs.py (1)

48-55: Two-phase tool initialization avoids premature access to toolset.name — LGTM.

Initializing with tools=[] and assigning self.tools = [PodLoggingTool(self)] after super().init ensures the PodLoggingTool can read the finalized name/capabilities to build accurate descriptions.

holmes/plugins/toolsets/grafana/toolset_grafana_loki.py (1)

50-54: Deferred PodLoggingTool creation is correct and consistent with the shared logging API.

This keeps tool descriptions accurate and prevents ordering bugs stemming from name/capabilities not being set yet.

holmes/plugins/toolsets/datadog/toolset_datadog_metrics.py (1)

57-58: Descriptions now clearly communicate Datadog as the source and mention historical data — good UX improvement.

  • Improves clarity around where data comes from and sets correct expectations about pods that no longer exist.

Also applies to: 185-186, 566-566

holmes/plugins/toolsets/kubernetes_logs.py (1)

70-75: Two-phase initialization is applied correctly.

Creating PodLoggingTool only after the base Toolset is initialized prevents description/capability mismatches and keeps behavior consistent with other toolsets.

holmes/plugins/toolsets/logging_utils/logging_api.py (2)

30-32: HISTORICAL_DATA capability addition looks good

Enum extension is backward-compatible and clearly documented. No impact on toolsets that return an empty capability set.


84-87: Nice: tool description now includes the toolset name

Good UX improvement with a sensible fallback when the name is empty.

holmes/plugins/toolsets/datadog/toolset_datadog_logs.py (2)

144-147: Capability declaration is correct

Declaring HISTORICAL_DATA here makes the PodLoggingTool description auto-augment correctly. No regex/exclude filters exposed, which matches the current implementation.


150-162: Two-phase initialization is appropriate here

Initializing with tools=[] and constructing PodLoggingTool after super().init ensures toolset.name is available for description building. This mirrors the pattern elsewhere.

holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py (1)

44-49: Two-phase init: LGTM

Deferring PodLoggingTool construction until after base init prevents name access issues and aligns with the new pattern.

@aantn
aantn requested a review from arikalon1 August 26, 2025 05:07

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

🧹 Nitpick comments (11)
holmes/plugins/toolsets/datadog/datadog_metrics_instructions.jinja2 (3)

36-40: Solid historical-pod guidance; add scoping, wildcard, and time-window guardrails

Great call to skip k8s existence checks for exact pod names to unlock historical metrics. To reduce false positives and avoid empty graphs, I suggest tightening the instructions with namespace/cluster scoping, explicit handling of wildcards, and a fallback to widen the time window for historical pods.

Apply this diff to enrich the section:

 **When user provides an exact pod name** (e.g., `my-workload-5f9d8b7c4d-x2km9`):
 - Query Datadog directly with that pod name using appropriate metrics and tags
 - Do NOT try to verify if the pod exists in Kubernetes first
 - This allows querying historical pods that have been deleted/replaced
+ - When namespace/cluster are known from the conversation or context, include them to avoid cross-environment leakage, e.g.:
+   `container.cpu.usage{pod_name:...,kube_namespace:...,kube_cluster_name:...}`
+ - If the user-provided "pod name" contains wildcards (e.g., `*`) or lacks the random suffix, treat it as a generic workload and use the workflow below (do not send wildcard tag values).
+ - For historical pods, expand the time window (e.g., last 24h or 7d) if the default 1h window returns no data before concluding that no metrics exist.

41-46: Be explicit about deployment/service tag alternatives and no-pod-found fallback

The alternative path is good; spell out concrete tag keys and behavior when kubectl_find_resource returns nothing (e.g., deleted namespaces). This also helps avoid the “* in pattern” pitfall mentioned in the PR notes.

Apply this diff to clarify the workflow and provide examples:

 **When user provides a generic workload name** (e.g., "my-workload", "nginx", "telemetry-processor"):
 - First use `kubectl_find_resource` to find actual pod names
 - Example: `kubectl_find_resource` with "my-workload" → finds pods like "my-workload-8f8cdfxyz-c7zdr"
 - Then use those specific pod names in Datadog queries
- - Alternative: Use deployment-level tags when appropriate
+ - Alternative: Use workload-level tags when appropriate:
+   - Deployment-level: `kube_deployment:my-workload`
+   - Service-level: `kube_service:my-service`
+   - Always add `kube_namespace:<ns>` and, when available, `kube_cluster_name:<cluster>` to scope results.
+   - Examples:
+     - `container.cpu.usage{kube_deployment:my-workload,kube_namespace:default}`
+     - `container.cpu.usage{kube_service:frontend,kube_namespace:payments}`
+ - If `kubectl_find_resource` returns no pods (e.g., the namespace was deleted), still query Datadog using workload-level tags and expand the time window to cover when the workload last existed.
+ - Avoid wildcard tag values (e.g., `pod_name:my-workload-*`). Resolve exact pod names or use workload-level tags. Use `list_datadog_metric_tags` to discover valid tag values.

49-50: Reinforce pod-name ephemerality; prefer workload tags for stable views

Nice clarification. Add one more line to guide stable dashboards/answers toward workload-level tags by default, using pod_name only when the ask targets a specific pod instance.

Apply this diff:

 **Why this matters:**
 - Pod names in Datadog are the actual Kubernetes pod names (with random suffixes)
 - Historical pods that no longer exist in the cluster can still have metrics in Datadog
 - Deployment/service names alone are NOT pod names (they need the suffix)
+ - For long-lived or comparative views, prefer workload-level tags (`kube_deployment`, `kube_service`) and add `kube_namespace`/`kube_cluster_name` to avoid flapping as pods roll.
tests/llm/fixtures/test_ask_holmes/92_cpu_graph_conversation/test_case.yaml (1)

1-3: Add fail-fast and verify helper script in before_test

To ensure the fixture setup fails clearly if the helper is missing and stops on any error, wrap the commands in a strict shell invocation and check for the script’s existence. You can keep the namespace as default to match the conversation context.

Apply this diff to tests/llm/fixtures/test_ask_holmes/92_cpu_graph_conversation/test_case.yaml:

 before_test: |
+  set -euo pipefail
+  test -f ../../shared/send_datadog_metrics.sh
   # Send CPU and memory metrics to Datadog for a specific robusta-runner mentioned in conversation history
   bash ../../shared/send_datadog_metrics.sh default robusta-runner-78599b764d-f847h robusta-runner
tests/llm/fixtures/test_ask_holmes/92_cpu_graph_conversation/conversation_history.json (2)

4-4: Optional: Clarify namespace in the user message or keep it namespace-agnostic.

Given the guideline to use app-, you can either:

  • keep the user line namespace-agnostic (fine); or
  • explicitly reference app-92 for clarity.

If you prefer explicitness, apply:

-    "content": "Can you show the memory usage of the robusta-runner-78599b764d-f847h pod?",
+    "content": "Can you show the memory usage of the robusta-runner-78599b764d-f847h pod in the app-92 namespace?",

1-12: Naming consistency: folder suggests CPU, prompt is about memory.

The fixture directory is 92_cpu_graph_conversation, while the conversation asks about memory. This can confuse future maintainers and result triage.

Consider either:

  • Renaming the folder to 92_metrics_graph_conversation; or
  • Switching the user prompt to CPU to align with the folder name.
tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/test_case.yaml (2)

13-15: Reduce flakiness: increase wait timeout to 120s

Clusters can be slow in CI; 60s may flake. 120s is safer.

-  kubectl wait --for=condition=ready pod -l app=orbiter-monitor -n app-91g --timeout=60s
+  kubectl wait --for=condition=ready pod -l app=orbiter-monitor -n app-91g --timeout=120s

1-1: Optional: neutralize scenario naming — rename tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod

Directory name contains "mismatched_pod" which hints at the failure; please rename to a neutral name (example: 91g_datadog_metrics or 91g_orbiter).

  • Affected path: tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/ (e.g., test_case.yaml).
  • Note: an rg scan shows many other non‑neutral names under tests/llm/fixtures (crashpod, broken, leak, error). Consider a short repository sweep to align all tests/llm/** with the project guideline to use neutral scenario names.

This uses the project guideline (tests/llm/**) to avoid revealing the root cause in fixture/resource names.

tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/manifest.yaml (3)

44-58: Harden pod/container security (allowPrivilegeEscalation, non-root, RoFS)

Satisfy CKV_K8S_20 and CKV_K8S_23 by setting securityContext at pod/container level. Even for tests, this reduces risk without impacting behavior.

     spec:
+      securityContext:
+        runAsNonRoot: true
+        seccompProfile:
+          type: RuntimeDefault
       volumes:
@@
       containers:
       - name: app
         image: busybox:latest
+        securityContext:
+          allowPrivilegeEscalation: false
+          readOnlyRootFilesystem: true
+          runAsUser: 1000
         command: ["/bin/sh", "-c"]

46-58: Pin BusyBox image to a stable tag to avoid CI drift

latest can change underneath you. Use a fixed tag (or digest) to stabilize tests.

-        image: busybox:latest
+        image: busybox:1.36.1

14-22: Nit: simplify CPU load loop

The echo + awk pipeline is a bit odd (the echoed string is unused). A simpler CPU burner keeps intent clear.

   script.sh: |
     #!/bin/sh
     echo "Starting orbiter-monitor pod..."
     # Simulate some CPU and memory usage
     while true; do
-      # Consume some CPU with a calculation
-      echo "scale=5000; 4*a(1)" | busybox awk 'BEGIN{for(i=0;i<100;i++){}}' 2>/dev/null
-      # Sleep to create varying pattern
+      # Consume some CPU
+      busybox awk 'BEGIN{for(i=0;i<5e7;i++) s+=i}'
+      # Sleep to create a varying pattern
       sleep 5
     done
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9d88421 and 127e6a4.

📒 Files selected for processing (6)
  • holmes/plugins/toolsets/datadog/datadog_metrics_instructions.jinja2 (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/toolsets.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/92_cpu_graph_conversation/conversation_history.json (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/92_cpu_graph_conversation/test_case.yaml (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
tests/llm/**/toolsets.yaml

📄 CodeRabbit inference engine (CLAUDE.md)

tests/llm/**/toolsets.yaml: Eval toolset overrides must be defined in a separate toolsets.yaml file in the test directory (do not put toolset config in test_case.yaml)
In toolsets.yaml, all toolset-specific configuration must be nested under a config field
Only the following top-level fields are allowed in toolsets YAML: enabled, name, description, additional_instructions, prerequisites, tools, docs_url, icon_url, installation_instructions, config, url (MCP only)

Files:

  • tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/toolsets.yaml
tests/llm/**/*.{yaml,yml}

📄 CodeRabbit inference engine (CLAUDE.md)

tests/llm/**/*.{yaml,yml}: Each LLM eval test must use a dedicated Kubernetes namespace named app-
For Kubernetes-related eval assets, always use Secrets for scripts; do not embed scripts in inline manifests or ConfigMaps

Files:

  • tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/toolsets.yaml
  • tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/92_cpu_graph_conversation/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/manifest.yaml
tests/llm/**

📄 CodeRabbit inference engine (CLAUDE.md)

Resource and file naming in evals should be neutral and must not hint at the problem (avoid names like broken-pod or crashloop-app)

Files:

  • tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/toolsets.yaml
  • tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/92_cpu_graph_conversation/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/92_cpu_graph_conversation/conversation_history.json
  • tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/manifest.yaml
tests/llm/**/test_case.yaml

📄 CodeRabbit inference engine (CLAUDE.md)

Eval test cases may declare runbooks in test_case.yaml using either runbooks: {} or runbooks: {catalog: [...]}; if omitted, defaults are used

Files:

  • tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/92_cpu_graph_conversation/test_case.yaml
holmes/plugins/toolsets/**

📄 CodeRabbit inference engine (CLAUDE.md)

Toolsets must live under holmes/plugins/toolsets as either {name}.yaml or a {name}/ directory

Files:

  • holmes/plugins/toolsets/datadog/datadog_metrics_instructions.jinja2
🧠 Learnings (3)
📚 Learning: 2025-08-24T07:21:02.579Z
Learnt from: CR
PR: robusta-dev/holmesgpt#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-24T07:21:02.579Z
Learning: Applies to tests/llm/**/toolsets.yaml : Only the following top-level fields are allowed in toolsets YAML: enabled, name, description, additional_instructions, prerequisites, tools, docs_url, icon_url, installation_instructions, config, url (MCP only)

Applied to files:

  • tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/toolsets.yaml
📚 Learning: 2025-08-24T07:21:02.579Z
Learnt from: CR
PR: robusta-dev/holmesgpt#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-24T07:21:02.579Z
Learning: Applies to tests/llm/**/toolsets.yaml : Eval toolset overrides must be defined in a separate toolsets.yaml file in the test directory (do not put toolset config in test_case.yaml)

Applied to files:

  • tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/toolsets.yaml
📚 Learning: 2025-08-24T07:21:02.579Z
Learnt from: CR
PR: robusta-dev/holmesgpt#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-24T07:21:02.579Z
Learning: Applies to tests/llm/**/toolsets.yaml : In toolsets.yaml, all toolset-specific configuration must be nested under a config field

Applied to files:

  • tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/toolsets.yaml
🪛 Checkov (3.2.334)
tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/manifest.yaml

[MEDIUM] 24-58: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 24-58: Minimize the admission of root containers

(CKV_K8S_23)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Pre-commit checks
  • GitHub Check: llm_evals
  • GitHub Check: Pre-commit checks
🔇 Additional comments (5)
tests/llm/fixtures/test_ask_holmes/92_cpu_graph_conversation/test_case.yaml (1)

16-21: Test oracle looks good and matches PR goal (ensure final output embeds a Datadog graph).

The expected_output constraints explicitly enforce emitting a datadogql embed via query_datadog_metrics in the final answer (not only intermediate output), which addresses point (4) in the PR description. Looks solid.

tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/toolsets.yaml (2)

1-9: Structure and config placement look good

  • Toolset overrides are in a dedicated toolsets.yaml.
  • Tool-specific settings are correctly nested under config.
  • Secrets are sourced from env vars (good for CI).

7-9: Updated Datadog keys verification script

The original snippet failed under set -u due to an invalid ${#VAR:-0} expansion. To both check for presence and report lengths safely, please replace it with:

#!/bin/bash
set -euo pipefail

# Safely default to empty strings if unset
api_key="${DATADOG_API_KEY:-}"
app_key="${DATADOG_APP_KEY:-}"

echo "DATADOG_API_KEY length: ${#api_key}"
echo "DATADOG_APP_KEY length: ${#app_key}"
echo "Configured site_api_url: https://api.datadoghq.eu"

# Fail if either key is missing
if [[ -z "$api_key" || -z "$app_key" ]]; then
  echo "Error: Missing Datadog keys (ensure EU-site API and APP keys are set)"
  exit 1
fi

• This will correctly handle undefined variables under set -u.
• Once updated, please re-run the check in your CI to confirm both DATADOG_API_KEY and DATADOG_APP_KEY are populated for the EU site.

tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/test_case.yaml (1)

24-31: No action required: tool_name is stable

Verified that the Datadog metrics tool remains named "query_datadog_metrics" in both its implementation (holmes/plugins/toolsets/datadog/toolset_datadog_metrics.py:184) and across all related fixtures and tests. The existing assertion in the YAML fixture is therefore correct and does not need updating.

tests/llm/fixtures/test_ask_holmes/91g_datadog_metrics_mismatched_pod/manifest.yaml (1)

40-43: Confirm script mounting and permissions

Secret volume with defaultMode 0755 ensures /etc/scripts/script.sh is executable. Mount path aligns with args. LGTM.

Also applies to: 49-51

@arikalon1 arikalon1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

looks good, nice work

@github-actions

Copy link
Copy Markdown
Contributor

Results of HolmesGPT evals

  • ask_holmes: 34/37 test cases were successful, 0 regressions, 2 skipped, 1 setup failures
Test suite Test case Status
ask 01_how_many_pods ✅
ask 02_what_is_wrong_with_pod ✅
ask 04_related_k8s_events ↪️
ask 05_image_version ✅
ask 09_crashpod ✅
ask 10_image_pull_backoff ✅
ask 110_k8s_events_image_pull ✅
ask 11_init_containers ✅
ask 13a_pending_node_selector_basic ✅
ask 14_pending_resources ✅
ask 15_failed_readiness_probe ✅
ask 17_oom_kill ✅
ask 18_crash_looping_v2 ✅
ask 19_detect_missing_app_details ✅
ask 20_long_log_file_search ✅
ask 24_misconfigured_pvc ✅
ask 24a_misconfigured_pvc_basic ✅
ask 28_permissions_error 🚧
ask 29_events_from_alert_manager ↪️
ask 39_failed_toolset ✅
ask 41_setup_argo ✅
ask 42_dns_issues_steps_new_tools ✅
ask 43_current_datetime_from_prompt ✅
ask 45_fetch_deployment_logs_simple ✅
ask 51_logs_summarize_errors ✅
ask 53_logs_find_term ✅
ask 54_not_truncated_when_getting_pods ✅
ask 59_label_based_counting ✅
ask 60_count_less_than ✅
ask 61_exact_match_counting ✅
ask 63_fetch_error_logs_no_errors ✅
ask 79_configmap_mount_issue ✅
ask 83_secret_not_found ✅
ask 86_configmap_like_but_secret ✅
ask 93_calling_datadog[0] ✅
ask 93_calling_datadog[1] ✅
ask 93_calling_datadog[2] ✅

Legend

  • ✅ 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 failed and should be fixed before merging the PR

@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

🧹 Nitpick comments (3)
holmes/plugins/toolsets/datadog/toolset_datadog_logs.py (3)

145-148: Clarify capability docstring (this property enumerates optional features only).

Substring matching is the default fallback when regex isn’t supported; it’s not an explicit capability. Suggest tightening the docstring to avoid confusion.

-        """Datadog logs API supports historical data and substring matching"""
+        """Optional capabilities: Datadog logs support historical data (no regex/exclude)."""

153-153: Nit: brand consistency ("Datadog").

Description uses “Datadog” while logger_name() returns “DataDog”. Consider aligning to “Datadog” everywhere for consistency.

Outside this hunk:

def logger_name(self) -> str:
    return "Datadog"

279-286: Add return type hint for mypy compliance.

Project guideline: type hints are required. This private helper should declare -> None.

-    def _reload_instructions(self):
+    def _reload_instructions(self) -> None:
         """Load Datadog logs specific troubleshooting instructions."""
         template_file_path = os.path.abspath(
             os.path.join(os.path.dirname(__file__), "datadog_logs_instructions.jinja2")
         )
         self._load_llm_instructions(jinja_template=f"file://{template_file_path}")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 127e6a4 and 3be1ca6.

📒 Files selected for processing (2)
  • holmes/plugins/toolsets/datadog/datadog_logs_instructions.jinja2 (1 hunks)
  • holmes/plugins/toolsets/datadog/toolset_datadog_logs.py (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • holmes/plugins/toolsets/datadog/datadog_logs_instructions.jinja2
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Always place Python imports at the top of the file, not inside functions or methods
Type hints are required (mypy is configured in pyproject.toml)

Files:

  • holmes/plugins/toolsets/datadog/toolset_datadog_logs.py
holmes/plugins/toolsets/**

📄 CodeRabbit inference engine (CLAUDE.md)

Toolsets must live under holmes/plugins/toolsets as either {name}.yaml or a {name}/ directory

Files:

  • holmes/plugins/toolsets/datadog/toolset_datadog_logs.py
🧬 Code graph analysis (1)
holmes/plugins/toolsets/datadog/toolset_datadog_logs.py (4)
holmes/plugins/toolsets/logging_utils/logging_api.py (2)
  • LoggingCapability (25-32)
  • PodLoggingTool (76-202)
holmes/core/tools.py (3)
  • CallablePrerequisite (329-330)
  • ToolsetTag (109-112)
  • _load_llm_instructions (479-484)
holmes/plugins/toolsets/datadog/toolset_datadog_metrics.py (1)
  • _reload_instructions (634-641)
holmes/plugins/toolsets/datadog/toolset_datadog_traces.py (1)
  • _reload_instructions (65-72)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: llm_evals
  • GitHub Check: Pre-commit checks
  • GitHub Check: Pre-commit checks
🔇 Additional comments (2)
holmes/plugins/toolsets/datadog/toolset_datadog_logs.py (2)

1-1: Import placement is correct.

os is imported at the top-level as required.


157-157: Two-phase tool initialization looks good.

Deferring PodLoggingTool creation until after BasePodLoggingToolset init avoids name/description ordering footguns and matches other toolsets.

Comment thread holmes/plugins/toolsets/datadog/toolset_datadog_logs.py
@arikalon1
arikalon1 merged commit 135031e into master Aug 30, 2025
11 checks passed
@arikalon1
arikalon1 deleted the improve-historical-data branch August 30, 2025 18:12
@coderabbitai coderabbitai Bot mentioned this pull request Sep 15, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Jan 2, 2026
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