holmesgpt 0.11.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of holmesgpt might be problematic. Click here for more details.
- holmes/.git_archival.json +7 -0
- holmes/__init__.py +76 -0
- holmes/__init__.py.bak +76 -0
- holmes/clients/robusta_client.py +24 -0
- holmes/common/env_vars.py +47 -0
- holmes/config.py +526 -0
- holmes/core/__init__.py +0 -0
- holmes/core/conversations.py +578 -0
- holmes/core/investigation.py +152 -0
- holmes/core/investigation_structured_output.py +264 -0
- holmes/core/issue.py +54 -0
- holmes/core/llm.py +250 -0
- holmes/core/models.py +157 -0
- holmes/core/openai_formatting.py +51 -0
- holmes/core/performance_timing.py +72 -0
- holmes/core/prompt.py +42 -0
- holmes/core/resource_instruction.py +17 -0
- holmes/core/runbooks.py +26 -0
- holmes/core/safeguards.py +120 -0
- holmes/core/supabase_dal.py +540 -0
- holmes/core/tool_calling_llm.py +798 -0
- holmes/core/tools.py +566 -0
- holmes/core/tools_utils/__init__.py +0 -0
- holmes/core/tools_utils/tool_executor.py +65 -0
- holmes/core/tools_utils/toolset_utils.py +52 -0
- holmes/core/toolset_manager.py +418 -0
- holmes/interactive.py +229 -0
- holmes/main.py +1041 -0
- holmes/plugins/__init__.py +0 -0
- holmes/plugins/destinations/__init__.py +6 -0
- holmes/plugins/destinations/slack/__init__.py +2 -0
- holmes/plugins/destinations/slack/plugin.py +163 -0
- holmes/plugins/interfaces.py +32 -0
- holmes/plugins/prompts/__init__.py +48 -0
- holmes/plugins/prompts/_current_date_time.jinja2 +1 -0
- holmes/plugins/prompts/_default_log_prompt.jinja2 +11 -0
- holmes/plugins/prompts/_fetch_logs.jinja2 +36 -0
- holmes/plugins/prompts/_general_instructions.jinja2 +86 -0
- holmes/plugins/prompts/_global_instructions.jinja2 +12 -0
- holmes/plugins/prompts/_runbook_instructions.jinja2 +13 -0
- holmes/plugins/prompts/_toolsets_instructions.jinja2 +56 -0
- holmes/plugins/prompts/generic_ask.jinja2 +36 -0
- holmes/plugins/prompts/generic_ask_conversation.jinja2 +32 -0
- holmes/plugins/prompts/generic_ask_for_issue_conversation.jinja2 +50 -0
- holmes/plugins/prompts/generic_investigation.jinja2 +42 -0
- holmes/plugins/prompts/generic_post_processing.jinja2 +13 -0
- holmes/plugins/prompts/generic_ticket.jinja2 +12 -0
- holmes/plugins/prompts/investigation_output_format.jinja2 +32 -0
- holmes/plugins/prompts/kubernetes_workload_ask.jinja2 +84 -0
- holmes/plugins/prompts/kubernetes_workload_chat.jinja2 +39 -0
- holmes/plugins/runbooks/README.md +22 -0
- holmes/plugins/runbooks/__init__.py +100 -0
- holmes/plugins/runbooks/catalog.json +14 -0
- holmes/plugins/runbooks/jira.yaml +12 -0
- holmes/plugins/runbooks/kube-prometheus-stack.yaml +10 -0
- holmes/plugins/runbooks/networking/dns_troubleshooting_instructions.md +66 -0
- holmes/plugins/runbooks/upgrade/upgrade_troubleshooting_instructions.md +44 -0
- holmes/plugins/sources/github/__init__.py +77 -0
- holmes/plugins/sources/jira/__init__.py +123 -0
- holmes/plugins/sources/opsgenie/__init__.py +93 -0
- holmes/plugins/sources/pagerduty/__init__.py +147 -0
- holmes/plugins/sources/prometheus/__init__.py +0 -0
- holmes/plugins/sources/prometheus/models.py +104 -0
- holmes/plugins/sources/prometheus/plugin.py +154 -0
- holmes/plugins/toolsets/__init__.py +171 -0
- holmes/plugins/toolsets/aks-node-health.yaml +65 -0
- holmes/plugins/toolsets/aks.yaml +86 -0
- holmes/plugins/toolsets/argocd.yaml +70 -0
- holmes/plugins/toolsets/atlas_mongodb/instructions.jinja2 +8 -0
- holmes/plugins/toolsets/atlas_mongodb/mongodb_atlas.py +307 -0
- holmes/plugins/toolsets/aws.yaml +76 -0
- holmes/plugins/toolsets/azure_sql/__init__.py +0 -0
- holmes/plugins/toolsets/azure_sql/apis/alert_monitoring_api.py +600 -0
- holmes/plugins/toolsets/azure_sql/apis/azure_sql_api.py +309 -0
- holmes/plugins/toolsets/azure_sql/apis/connection_failure_api.py +445 -0
- holmes/plugins/toolsets/azure_sql/apis/connection_monitoring_api.py +251 -0
- holmes/plugins/toolsets/azure_sql/apis/storage_analysis_api.py +317 -0
- holmes/plugins/toolsets/azure_sql/azure_base_toolset.py +55 -0
- holmes/plugins/toolsets/azure_sql/azure_sql_instructions.jinja2 +137 -0
- holmes/plugins/toolsets/azure_sql/azure_sql_toolset.py +183 -0
- holmes/plugins/toolsets/azure_sql/install.md +66 -0
- holmes/plugins/toolsets/azure_sql/tools/__init__.py +1 -0
- holmes/plugins/toolsets/azure_sql/tools/analyze_connection_failures.py +324 -0
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_connections.py +243 -0
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_health_status.py +205 -0
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_performance.py +249 -0
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_storage.py +373 -0
- holmes/plugins/toolsets/azure_sql/tools/get_active_alerts.py +237 -0
- holmes/plugins/toolsets/azure_sql/tools/get_slow_queries.py +172 -0
- holmes/plugins/toolsets/azure_sql/tools/get_top_cpu_queries.py +170 -0
- holmes/plugins/toolsets/azure_sql/tools/get_top_data_io_queries.py +188 -0
- holmes/plugins/toolsets/azure_sql/tools/get_top_log_io_queries.py +180 -0
- holmes/plugins/toolsets/azure_sql/utils.py +83 -0
- holmes/plugins/toolsets/bash/__init__.py +0 -0
- holmes/plugins/toolsets/bash/bash_instructions.jinja2 +14 -0
- holmes/plugins/toolsets/bash/bash_toolset.py +208 -0
- holmes/plugins/toolsets/bash/common/bash.py +52 -0
- holmes/plugins/toolsets/bash/common/config.py +14 -0
- holmes/plugins/toolsets/bash/common/stringify.py +25 -0
- holmes/plugins/toolsets/bash/common/validators.py +24 -0
- holmes/plugins/toolsets/bash/grep/__init__.py +52 -0
- holmes/plugins/toolsets/bash/kubectl/__init__.py +100 -0
- holmes/plugins/toolsets/bash/kubectl/constants.py +96 -0
- holmes/plugins/toolsets/bash/kubectl/kubectl_describe.py +66 -0
- holmes/plugins/toolsets/bash/kubectl/kubectl_events.py +88 -0
- holmes/plugins/toolsets/bash/kubectl/kubectl_get.py +108 -0
- holmes/plugins/toolsets/bash/kubectl/kubectl_logs.py +20 -0
- holmes/plugins/toolsets/bash/kubectl/kubectl_run.py +46 -0
- holmes/plugins/toolsets/bash/kubectl/kubectl_top.py +81 -0
- holmes/plugins/toolsets/bash/parse_command.py +103 -0
- holmes/plugins/toolsets/confluence.yaml +19 -0
- holmes/plugins/toolsets/consts.py +5 -0
- holmes/plugins/toolsets/coralogix/api.py +158 -0
- holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py +103 -0
- holmes/plugins/toolsets/coralogix/utils.py +181 -0
- holmes/plugins/toolsets/datadog.py +153 -0
- holmes/plugins/toolsets/docker.yaml +46 -0
- holmes/plugins/toolsets/git.py +756 -0
- holmes/plugins/toolsets/grafana/__init__.py +0 -0
- holmes/plugins/toolsets/grafana/base_grafana_toolset.py +54 -0
- holmes/plugins/toolsets/grafana/common.py +68 -0
- holmes/plugins/toolsets/grafana/grafana_api.py +31 -0
- holmes/plugins/toolsets/grafana/loki_api.py +89 -0
- holmes/plugins/toolsets/grafana/tempo_api.py +124 -0
- holmes/plugins/toolsets/grafana/toolset_grafana.py +102 -0
- holmes/plugins/toolsets/grafana/toolset_grafana_loki.py +102 -0
- holmes/plugins/toolsets/grafana/toolset_grafana_tempo.jinja2 +10 -0
- holmes/plugins/toolsets/grafana/toolset_grafana_tempo.py +299 -0
- holmes/plugins/toolsets/grafana/trace_parser.py +195 -0
- holmes/plugins/toolsets/helm.yaml +42 -0
- holmes/plugins/toolsets/internet/internet.py +275 -0
- holmes/plugins/toolsets/internet/notion.py +137 -0
- holmes/plugins/toolsets/kafka.py +638 -0
- holmes/plugins/toolsets/kubernetes.yaml +255 -0
- holmes/plugins/toolsets/kubernetes_logs.py +426 -0
- holmes/plugins/toolsets/kubernetes_logs.yaml +42 -0
- holmes/plugins/toolsets/logging_utils/__init__.py +0 -0
- holmes/plugins/toolsets/logging_utils/logging_api.py +217 -0
- holmes/plugins/toolsets/logging_utils/types.py +0 -0
- holmes/plugins/toolsets/mcp/toolset_mcp.py +135 -0
- holmes/plugins/toolsets/newrelic.py +222 -0
- holmes/plugins/toolsets/opensearch/__init__.py +0 -0
- holmes/plugins/toolsets/opensearch/opensearch.py +245 -0
- holmes/plugins/toolsets/opensearch/opensearch_logs.py +151 -0
- holmes/plugins/toolsets/opensearch/opensearch_traces.py +211 -0
- holmes/plugins/toolsets/opensearch/opensearch_traces_instructions.jinja2 +12 -0
- holmes/plugins/toolsets/opensearch/opensearch_utils.py +166 -0
- holmes/plugins/toolsets/prometheus/prometheus.py +818 -0
- holmes/plugins/toolsets/prometheus/prometheus_instructions.jinja2 +38 -0
- holmes/plugins/toolsets/rabbitmq/api.py +398 -0
- holmes/plugins/toolsets/rabbitmq/rabbitmq_instructions.jinja2 +37 -0
- holmes/plugins/toolsets/rabbitmq/toolset_rabbitmq.py +222 -0
- holmes/plugins/toolsets/robusta/__init__.py +0 -0
- holmes/plugins/toolsets/robusta/robusta.py +235 -0
- holmes/plugins/toolsets/robusta/robusta_instructions.jinja2 +24 -0
- holmes/plugins/toolsets/runbook/__init__.py +0 -0
- holmes/plugins/toolsets/runbook/runbook_fetcher.py +78 -0
- holmes/plugins/toolsets/service_discovery.py +92 -0
- holmes/plugins/toolsets/servicenow/install.md +37 -0
- holmes/plugins/toolsets/servicenow/instructions.jinja2 +3 -0
- holmes/plugins/toolsets/servicenow/servicenow.py +198 -0
- holmes/plugins/toolsets/slab.yaml +20 -0
- holmes/plugins/toolsets/utils.py +137 -0
- holmes/plugins/utils.py +14 -0
- holmes/utils/__init__.py +0 -0
- holmes/utils/cache.py +84 -0
- holmes/utils/cert_utils.py +40 -0
- holmes/utils/default_toolset_installation_guide.jinja2 +44 -0
- holmes/utils/definitions.py +13 -0
- holmes/utils/env.py +53 -0
- holmes/utils/file_utils.py +56 -0
- holmes/utils/global_instructions.py +20 -0
- holmes/utils/holmes_status.py +22 -0
- holmes/utils/holmes_sync_toolsets.py +80 -0
- holmes/utils/markdown_utils.py +55 -0
- holmes/utils/pydantic_utils.py +54 -0
- holmes/utils/robusta.py +10 -0
- holmes/utils/tags.py +97 -0
- holmesgpt-0.11.5.dist-info/LICENSE.txt +21 -0
- holmesgpt-0.11.5.dist-info/METADATA +400 -0
- holmesgpt-0.11.5.dist-info/RECORD +183 -0
- holmesgpt-0.11.5.dist-info/WHEEL +4 -0
- holmesgpt-0.11.5.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Summarize the investigation below using the following points
|
|
2
|
+
|
|
3
|
+
1. Summarize the output provided to be short and concise without removing any specific details about any resourse or the root cause.
|
|
4
|
+
2. Remove any sections on 'to gather more information' or 'next steps' unless explicitly requested in the prompt.
|
|
5
|
+
3. Do not compliment the user or mention their investigation.
|
|
6
|
+
4. Avoid introductory or concluding remarks; provide only the direct summary of issues.
|
|
7
|
+
5. Preserve the answer format.
|
|
8
|
+
|
|
9
|
+
This is the original prompt:
|
|
10
|
+
{{ prompt }}
|
|
11
|
+
|
|
12
|
+
This is the investigation to summarize:
|
|
13
|
+
{{ investigation }}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{% include 'generic_ask.jinja2' %}
|
|
2
|
+
|
|
3
|
+
{% if output_instructions %}
|
|
4
|
+
You are updating tickets in {{ source }} so verify you follow all the following instructions:
|
|
5
|
+
|
|
6
|
+
Always validate pod names before generating links.
|
|
7
|
+
When creating Grafana dashboard links, ensure that **EVERY** link is relevant to the issue investigated and express it in the link text.
|
|
8
|
+
{% for instruction in output_instructions %}
|
|
9
|
+
{{ instruction }}
|
|
10
|
+
{% endfor %}
|
|
11
|
+
|
|
12
|
+
{% endif %}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
{% if structured_output %}
|
|
3
|
+
Give your answer in a pure JSON format with the following sections.
|
|
4
|
+
The content of each section should be formatted with markdown:
|
|
5
|
+
|
|
6
|
+
{
|
|
7
|
+
{% for title, description in sections.items() %}
|
|
8
|
+
"{{ title }}": "{{ description | replace("\n", "\\n") }}",{% endfor %}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
You MUST set a section as `null` if:
|
|
12
|
+
- It's not relevant to the investigation
|
|
13
|
+
- It does not contain relevant information
|
|
14
|
+
- Its content is not related to a possible root cause
|
|
15
|
+
|
|
16
|
+
<DO NOT list tools used and DO NOT add a `Tools` section>
|
|
17
|
+
{% else %}
|
|
18
|
+
Give your answer returning a markdown document structured with the following sections. Use top level markdown titles format `#`.
|
|
19
|
+
Ignore any section that is not relevant to the investigation.
|
|
20
|
+
|
|
21
|
+
{% for title, description in sections.items() %}
|
|
22
|
+
# {{ title }}
|
|
23
|
+
{{ description }}
|
|
24
|
+
{% endfor %}
|
|
25
|
+
|
|
26
|
+
# <DO NOT list tools used and DO NOT add a `# Tools` section>
|
|
27
|
+
|
|
28
|
+
You MUST ignore a section and skip it:
|
|
29
|
+
- It's not relevant to the investigation
|
|
30
|
+
- It does not contain relevant information
|
|
31
|
+
- Its content is not related to a possible root cause
|
|
32
|
+
{% endif %}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
You are a tool-calling AI assist provided with common devops and IT tools that you can use to troubleshoot problems or answer questions.
|
|
2
|
+
Whenever possible you MUST first use tools to investigate then answer the question.
|
|
3
|
+
Do not say 'based on the tool output' or explicitly refer to tools at all.
|
|
4
|
+
If you output an answer and then realize you need to call more tools or there are possible next steps, you may do so by calling tools at that point in time.
|
|
5
|
+
|
|
6
|
+
If the user provides you with extra instructions in a triple single quotes section, ALWAYS perform their instructions and then perform your investigation.
|
|
7
|
+
{% include '_current_date_time.jinja2' %}
|
|
8
|
+
|
|
9
|
+
Global Instructions
|
|
10
|
+
You may receive a set of “Global Instructions” that describe how to perform certain tasks, handle certain situations, or apply certain best practices. They are not mandatory for every request, but serve as a reference resource and must be used if the current scenario or user request aligns with one of the described methods or conditions.
|
|
11
|
+
Use these rules when deciding how to apply them:
|
|
12
|
+
|
|
13
|
+
* If the user prompt includes Global Instructions, treat them as a reference resource.
|
|
14
|
+
* Some Global Instructions may describe how to handle specific tasks or scenarios. If the user's current request or the instructions in a triple quotes section reference one of these tasks, ALWAYS follow the Global Instruction for that task.
|
|
15
|
+
* Some Global Instructions may define general conditions that always apply if a certain scenario occurs (e.g., "whenever investigating a memory issue, always check resource limits"). If such a condition matches the current situation, apply the Global Instruction accordingly.
|
|
16
|
+
* If user's prompt or the instructions in a triple quotes section direct you to perform a task (e.g., “Find owner”) and there is a Global Instruction on how to do that task, ALWAYS follow the Global Instructions on how to perform it.
|
|
17
|
+
* If multiple Global Instructions are relevant, apply all that fit.
|
|
18
|
+
* If no Global Instruction is relevant, or no condition applies, ignore them and proceed as normal.
|
|
19
|
+
* Before finalizing your answer double-check if any Global Instructions apply. If so, ensure you have correctly followed those instructions.
|
|
20
|
+
|
|
21
|
+
In general:
|
|
22
|
+
* when it can provide extra information, first run as many tools as you need to gather more information, then respond.
|
|
23
|
+
* if possible, do so repeatedly with different tool calls each time to gather more information.
|
|
24
|
+
* do not stop investigating until you are at the final root cause you are able to find.
|
|
25
|
+
* use the "five whys" methodology to find the root cause.
|
|
26
|
+
* for example, if you found a problem in microservice A that is due to an error in microservice B, look at microservice B too and find the error in that.
|
|
27
|
+
* if you cannot find the resource/application that the user referred to, assume they made a typo or included/excluded characters like - and.
|
|
28
|
+
* in this case, try to find substrings or search for the correct spellings
|
|
29
|
+
* if you are unable to investigate something properly because you do not have access to the right data, explicitly tell the user that you are missing an integration to access XYZ which you would need to investigate. you should specifically use the templated phrase "I don't have access to <details>. Please add a Holmes integration for <XYZ> so that I can investigate this."
|
|
30
|
+
* always provide detailed information like exact resource names, versions, labels, etc
|
|
31
|
+
* even if you found the root cause, keep investigating to find other possible root causes and to gather data for the answer like exact names
|
|
32
|
+
* if a runbook url is present as well as tool that can fetch it, you MUST fetch the runbook before beginning your investigation.
|
|
33
|
+
* if you don't know, say that the analysis was inconclusive.
|
|
34
|
+
* if there are multiple possible causes list them in a numbered list.
|
|
35
|
+
* there will often be errors in the data that are not relevant or that do not have an impact - ignore them in your conclusion if you were not able to tie them to an actual error.
|
|
36
|
+
* run as many kubectl commands as you need to gather more information, then respond.
|
|
37
|
+
* if possible, do so repeatedly on different Kubernetes objects.
|
|
38
|
+
* for example, for deployments first run kubectl on the deployment then a replicaset inside it, then a pod inside that.
|
|
39
|
+
* when investigating a pod that crashed or application errors, always run kubectl_describe and fetch the pod's logs so that you see current logs and any logs from before a crash.
|
|
40
|
+
* do not give an answer like "The pod is pending" as that doesn't state why the pod is pending and how to fix it.
|
|
41
|
+
* do not give an answer like "Pod's node affinity/selector doesn't match any available nodes" because that doesn't include data on WHICH label doesn't match
|
|
42
|
+
* if investigating an issue on many pods, there is no need to check more than 3 individual pods in the same deployment. pick up to a representative 3 from each deployment if relevant
|
|
43
|
+
* if you find errors and warning in a pods logs and you believe they indicate a real issue. consider the pod as not healthy.
|
|
44
|
+
* if the user says something isn't working, ALWAYS:
|
|
45
|
+
** use kubectl_describe on the owner workload + individual pods and look for any transient issues they might have been referring to
|
|
46
|
+
** check the application aspects by accessing the application logs and other relevant tools
|
|
47
|
+
** look for misconfigured ingresses/services etc
|
|
48
|
+
|
|
49
|
+
{% include '_toolsets_instructions.jinja2' %}
|
|
50
|
+
|
|
51
|
+
{% include '_fetch_logs.jinja2' %}
|
|
52
|
+
|
|
53
|
+
Style guide:
|
|
54
|
+
* Be painfully concise.
|
|
55
|
+
* Leave out "the" and filler words when possible.
|
|
56
|
+
* Be terse but not at the expense of leaving out important data like the root cause and how to fix.
|
|
57
|
+
* if asked by Global Instructions or instructions in a triple single quotes section to explicitly include something in the answer, don't leave it out.
|
|
58
|
+
* return a json object with the following schema as a result:
|
|
59
|
+
{
|
|
60
|
+
"type": "object",
|
|
61
|
+
"properties": {
|
|
62
|
+
"root_cause_summary": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"description": "concise short explaination leading to the workload_healthy result, pinpoint reason and root cause for the workload issues if any."
|
|
65
|
+
},
|
|
66
|
+
"workload_healthy": {
|
|
67
|
+
"type": "boolean",
|
|
68
|
+
"description": "is the workload in healthy state or in error state"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"required": [
|
|
72
|
+
"reasoning",
|
|
73
|
+
"workload_healthy"
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
{% if alerts %}
|
|
80
|
+
Here are issues and configuration changes that happend to this kubernetes workload in recent time. Check if these can help you understand the issue.
|
|
81
|
+
{% for a in alerts %}
|
|
82
|
+
{{ a }}
|
|
83
|
+
{% endfor %}
|
|
84
|
+
{% endif %}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
You are a tool-calling AI assist provided with common DevOps and IT tools that you can use to troubleshoot problems or answer questions.
|
|
2
|
+
Whenever possible, you MUST first use tools to investigate, then answer the question.
|
|
3
|
+
Do not say 'based on the tool output' or explicitly refer to tools at all.
|
|
4
|
+
If you output an answer and then realize you need to call more tools or there are possible next steps, you may do so by calling tools at that point in time.
|
|
5
|
+
{% include '_current_date_time.jinja2' %}
|
|
6
|
+
|
|
7
|
+
### Context Awareness:
|
|
8
|
+
Be aware that this conversation is follow-up questions to a prior investigation conducted for the {{resource}}.
|
|
9
|
+
However, not all questions may be directly related to that investigation.
|
|
10
|
+
Use results of the investigation and conversation history to maintain continuity when appropriate, ensuring efficiency in your responses.
|
|
11
|
+
|
|
12
|
+
#### Results of Workload Health Check Analysis:
|
|
13
|
+
{{workload_analysis}}
|
|
14
|
+
|
|
15
|
+
{% if tools_called_for_workload %}
|
|
16
|
+
Tools used for the workload analysis:
|
|
17
|
+
{% for tool in tools_called_for_workload %}
|
|
18
|
+
{{ tool }}
|
|
19
|
+
{% endfor %}
|
|
20
|
+
{% endif %}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
{% include '_global_instructions.jinja2' %}
|
|
24
|
+
{% include '_general_instructions.jinja2' %}
|
|
25
|
+
|
|
26
|
+
Style guide:
|
|
27
|
+
* Reply with terse output.
|
|
28
|
+
* Be painfully concise.
|
|
29
|
+
* Leave out "the" and filler words when possible.
|
|
30
|
+
* Be terse but not at the expense of leaving out important data like the root cause and how to fix.
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
|
|
34
|
+
User: Why did the workload-example app crash?
|
|
35
|
+
(Call tool kubectl_find_resource kind=pod keyword=workload`)
|
|
36
|
+
(Call tool kubectl_previous_logs namespace=demos pod=workload-example-1299492-d9g9d # this pod name was found from the previous tool call)
|
|
37
|
+
|
|
38
|
+
AI: `workload-example-1299492-d9g9d` crashed due to email validation error during HTTP request for /api/create_user
|
|
39
|
+
Relevant logs:
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Runbooks
|
|
2
|
+
|
|
3
|
+
Runbooks folder contains operational runbooks for the HolmesGPT project. Runbooks provide step-by-step instructions for common tasks, troubleshooting, and maintenance procedures related to the plugins in this directory.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
- Standardize operational processes
|
|
8
|
+
- Enable quick onboarding for new team members
|
|
9
|
+
- Reduce downtime by providing clear troubleshooting steps
|
|
10
|
+
|
|
11
|
+
## Structure
|
|
12
|
+
|
|
13
|
+
### Structured Runbook
|
|
14
|
+
|
|
15
|
+
Structured runbooks are designed for specific issues when conditions like issue name, id or source match, the corresponding instructions will be returned for investigation.
|
|
16
|
+
For example, the investigation in [kube-prometheus-stack.yaml](kube-prometheus-stack.yaml) will be returned when the issue to be investigated match either KubeSchedulerDown or KubeControllerManagerDown.
|
|
17
|
+
This runbook is mainly used for `holmes investigate`
|
|
18
|
+
|
|
19
|
+
### Catalog
|
|
20
|
+
|
|
21
|
+
Catalog specified in [catalog.json](catalog.json) contains a collection of runbooks written in markdown.
|
|
22
|
+
During runtime, LLM will compare the runbook description with the user question and return the most matched runbook for investigation. It's possible no runbook is returned for no match.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
import os.path
|
|
5
|
+
from datetime import date
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import List, Optional, Pattern, Union
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, PrivateAttr
|
|
10
|
+
|
|
11
|
+
from holmes.utils.pydantic_utils import RobustaBaseConfig, load_model_from_file
|
|
12
|
+
|
|
13
|
+
THIS_DIR = os.path.abspath(os.path.dirname(__file__))
|
|
14
|
+
|
|
15
|
+
CATALOG_FILE = "catalog.json"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class IssueMatcher(RobustaBaseConfig):
|
|
19
|
+
issue_id: Optional[Pattern] = None # unique id
|
|
20
|
+
issue_name: Optional[Pattern] = None # not necessary unique
|
|
21
|
+
source: Optional[Pattern] = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Runbook(RobustaBaseConfig):
|
|
25
|
+
match: IssueMatcher
|
|
26
|
+
instructions: str
|
|
27
|
+
|
|
28
|
+
_path = PrivateAttr()
|
|
29
|
+
|
|
30
|
+
def set_path(self, path: str):
|
|
31
|
+
self._path = path
|
|
32
|
+
|
|
33
|
+
def get_path(self) -> str:
|
|
34
|
+
return self._path
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ListOfRunbooks(BaseModel):
|
|
38
|
+
runbooks: List[Runbook]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def load_runbooks_from_file(path: Union[str, Path]) -> List[Runbook]:
|
|
42
|
+
data: ListOfRunbooks = load_model_from_file(ListOfRunbooks, file_path=path) # type: ignore
|
|
43
|
+
for runbook in data.runbooks:
|
|
44
|
+
runbook.set_path(path) # type: ignore
|
|
45
|
+
return data.runbooks
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def load_builtin_runbooks() -> List[Runbook]:
|
|
49
|
+
all_runbooks = []
|
|
50
|
+
for filename in os.listdir(THIS_DIR):
|
|
51
|
+
if not filename.endswith(".yaml"):
|
|
52
|
+
continue
|
|
53
|
+
path = os.path.join(THIS_DIR, filename)
|
|
54
|
+
all_runbooks.extend(load_runbooks_from_file(path))
|
|
55
|
+
return all_runbooks
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class RunbookCatalogEntry(BaseModel):
|
|
59
|
+
"""
|
|
60
|
+
RunbookCatalogEntry contains metadata about a runbook
|
|
61
|
+
Different from runbooks provided by Runbook class, this entry points to markdown file containing the runbook content.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
update_date: date
|
|
65
|
+
description: str
|
|
66
|
+
link: str
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class RunbookCatalog(BaseModel):
|
|
70
|
+
"""
|
|
71
|
+
RunbookCatalog is a collection of runbook entries, each entry contains metadata about the runbook.
|
|
72
|
+
The correct runbook can be selected from the list by comparing the description with the user question.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
catalog: List[RunbookCatalogEntry]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def load_runbook_catalog() -> Optional[RunbookCatalog]:
|
|
79
|
+
dir_path = os.path.dirname(os.path.realpath(__file__))
|
|
80
|
+
|
|
81
|
+
catalogPath = os.path.join(dir_path, CATALOG_FILE)
|
|
82
|
+
if not os.path.isfile(catalogPath):
|
|
83
|
+
return None
|
|
84
|
+
try:
|
|
85
|
+
with open(catalogPath) as file:
|
|
86
|
+
catalog_dict = json.load(file)
|
|
87
|
+
return RunbookCatalog(**catalog_dict)
|
|
88
|
+
except json.JSONDecodeError as e:
|
|
89
|
+
logging.error(f"Error decoding JSON from {catalogPath}: {e}")
|
|
90
|
+
except Exception as e:
|
|
91
|
+
logging.error(
|
|
92
|
+
f"Unexpected error while loading runbook catalog from {catalogPath}: {e}"
|
|
93
|
+
)
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def get_runbook_by_path(runbook_relative_path: str) -> str:
|
|
98
|
+
runbook_folder = os.path.dirname(os.path.realpath(__file__))
|
|
99
|
+
runbook_path = os.path.join(runbook_folder, runbook_relative_path)
|
|
100
|
+
return runbook_path
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"catalog": [
|
|
3
|
+
{
|
|
4
|
+
"update_date": "2025-06-17",
|
|
5
|
+
"description": "Runbook to investigate DNS resolution issue in Kubernetes clusters",
|
|
6
|
+
"link": "networking/dns_troubleshooting_instructions.md"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"update_date": "2025-07-08",
|
|
10
|
+
"description": "Runbook to troubleshoot upgrade issues in Azure Kubernetes Service clusters",
|
|
11
|
+
"link": "upgrade/upgrade_troubleshooting_instructions.md"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# runbooks for jira alerts
|
|
2
|
+
# the AI will follow the instructions inside these runbooks to investigate alerts!
|
|
3
|
+
# please feel free to open PRs adding your own runboks
|
|
4
|
+
runbooks:
|
|
5
|
+
- match:
|
|
6
|
+
source: "jira"
|
|
7
|
+
instructions: >
|
|
8
|
+
Investigate and try to solve whatever is written in the title and description of the ticket.
|
|
9
|
+
Ignore issues related to jira itself, like plugin or licensing problems.
|
|
10
|
+
Never give an answer like "XYZ is experiencing an issue, as indicated by the Jira issue. Further investigation is needed to determine the exact cause."
|
|
11
|
+
You are the agent that is supposed to investigate so do so!
|
|
12
|
+
If you have references to a service or a component, start by searching for related infrastructure or resources using tools that take keywords
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# runbooks for alerts in kube-prometheus-stack
|
|
2
|
+
# the AI will follow the instructions inside these runbooks to investigate alerts!
|
|
3
|
+
# please feel free to open PRs adding your own runboks
|
|
4
|
+
runbooks:
|
|
5
|
+
- match:
|
|
6
|
+
issue_name: "(KubeSchedulerDown)|(KubeControllerManagerDown)"
|
|
7
|
+
instructions: >
|
|
8
|
+
Check if the cluster is a managed cluster like EKS by fetching nodes and looking at their labels.
|
|
9
|
+
If so, tell the user this is likely a known false positive in the kube-prometheus-stack alert because Prometheus can't scrape the scheduler which is managed by the cloud provider.
|
|
10
|
+
On the other hand, if this is a self-managed Kubernetes, either the scheduler is really down (unlikely) or it is running but Prometheus can't scrape it.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# DNS Troubleshooting Guidelines (Kubernetes)
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
Your primary goal when using these tools is to diagnose DNS resolution issues within a Kubernetes cluster, focusing on identifying common problems like incorrect CoreDNS/kube-dns setup, network policies, or service discovery failures by strictly following the workflow for DNS diagnosis.
|
|
5
|
+
|
|
6
|
+
* Use the tools to gather information about the DNS pods, services, and configuration.
|
|
7
|
+
* Clearly present the key findings from the tool outputs in your analysis.
|
|
8
|
+
* Instead of provide next steps to the user, you need to follow the troubleshoot guide to execute the steps.
|
|
9
|
+
* When getting pod logs, always try to get the log filter by log_filter toolset to filter out unnecessary logs by tool kubectl_logs_grep_no_match
|
|
10
|
+
|
|
11
|
+
## Workflow for DNS Diagnosis
|
|
12
|
+
|
|
13
|
+
1. **Check CoreDNS/kube-dns Pods:**
|
|
14
|
+
* Verify that the DNS pods (e.g., CoreDNS or kube-dns) are running in the `kube-system` namespace.
|
|
15
|
+
* Look for restarts or crashes in the DNS pods.
|
|
16
|
+
|
|
17
|
+
2. **Examine DNS Service:**
|
|
18
|
+
* Ensure the DNS service is correctly defined: `kubectl get svc kube-dns -n kube-system` (or the equivalent for your DNS provider).
|
|
19
|
+
* Verify the ClusterIP of the DNS service and the ports (usually 53/UDP and 53/TCP).
|
|
20
|
+
|
|
21
|
+
3. **Test DNS Resolution from a Pod:**
|
|
22
|
+
* Launch a debugging pod (e.g., using `busybox` or `nslookup` tools).
|
|
23
|
+
* **Inside the debug pod:**
|
|
24
|
+
* Check `/etc/resolv.conf`:
|
|
25
|
+
* The `nameserver` should point to the DNS service's ClusterIP.
|
|
26
|
+
* The `search` path should be appropriate for your namespaces (e.g., `your-namespace.svc.cluster.local svc.cluster.local cluster.local`).
|
|
27
|
+
* The `options` (like `ndots:5`) can affect resolution behavior.
|
|
28
|
+
* Attempt to resolve internal cluster names:
|
|
29
|
+
* A service in the same namespace (e.g., `myservice`).
|
|
30
|
+
* A service in a different namespace (e.g., `myservice.othernamespace`).
|
|
31
|
+
* A fully qualified domain name (FQDN) (e.g., `myservice.othernamespace.svc.cluster.local`).
|
|
32
|
+
* Attempt to resolve external names (e.g., `www.google.com`).
|
|
33
|
+
* Use tools like `nslookup <hostname>` or `dig <hostname>` for detailed query information.
|
|
34
|
+
|
|
35
|
+
4. **Check NetworkPolicies:**
|
|
36
|
+
* If NetworkPolicies are in place, ensure they allow DNS traffic (to port 53 UDP/TCP) from your application pods to the DNS pods/service.
|
|
37
|
+
* List NetworkPolicies and Examine policies that might be affecting the source or destination pods.
|
|
38
|
+
|
|
39
|
+
5. **Review CoreDNS Configuration (if applicable):**
|
|
40
|
+
* Inspect the CoreDNS ConfigMap: `kubectl get configmap coredns -n kube-system -o yaml`.
|
|
41
|
+
* Look for errors or misconfigurations in the Corefile (e.g., incorrect upstream resolvers, plugin issues).
|
|
42
|
+
* Inspect the customized CoreDNS ConfigMap: `kubectl get configmap coredns-custom -n kube-system -o yaml`.
|
|
43
|
+
* Look for errors or misconfigurations in the customizated CoreDNS config (e.g., incorrect upstream resolvers, plugin issues).
|
|
44
|
+
|
|
45
|
+
6. **Check the DNS trace**
|
|
46
|
+
* Use findings from the DNS trace to pinpoint where DNS resolution is failing (e.g., query not reaching DNS server, invalid FQDN, or error response from DNS server).
|
|
47
|
+
* DNS Server should always respond to the requests from the client. Valid FQDN should return NOERROR, and invalid FQDN should return NXDOMAIN
|
|
48
|
+
|
|
49
|
+
## Synthesize Findings
|
|
50
|
+
Based on the outputs from the above steps, describe the DNS issue clearly. For example:
|
|
51
|
+
* "DNS resolution for internal service 'myservice' is failing from pods in namespace 'app-ns'. The CoreDNS pods in `kube-system` are running but show 'connection refused' errors in their logs when trying to reach upstream resolvers."
|
|
52
|
+
* "Pods in namespace 'secure-ns' cannot resolve any hostnames. `/etc/resolv.conf` in these pods is missing the correct `nameserver` entry. This is likely due to a misconfiguration in the pod's `dnsPolicy` or the underlying node's DNS setup."
|
|
53
|
+
* "External DNS resolution is failing cluster-wide. The CoreDNS ConfigMap shows that the `forward` plugin is pointing to an incorrect upstream DNS server IP address."
|
|
54
|
+
* "DNS lookups for 'service-a.namespace-b' are timing out. A NetworkPolicy in 'namespace-b' is blocking egress traffic on port 53 to the kube-dns service."
|
|
55
|
+
|
|
56
|
+
## Recommend Remediation Steps (Based on Docs)
|
|
57
|
+
* **CRITICAL:** ALWAYS refer to the official Kubernetes DNS debugging guide for detailed troubleshooting and solutions:
|
|
58
|
+
* Main guide: https://kubernetes.io/docs/tasks/administer-cluster/dns-debugging-resolution/
|
|
59
|
+
* CoreDNS specific: https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/ (for CoreDNS customization which might be relevant)
|
|
60
|
+
* **DO NOT invent recovery procedures.** Your role is to diagnose and *point* to the correct documentation or standard procedures.
|
|
61
|
+
* Based on the findings, suggest which sections of the documentation are most relevant.
|
|
62
|
+
* If DNS pods are not running, guide towards checking pod deployment and node health.
|
|
63
|
+
* If `/etc/resolv.conf` is incorrect, point to sections on Pod `dnsPolicy` and `dnsConfig`.
|
|
64
|
+
* If NetworkPolicies are suspected, suggest reviewing policy definitions to allow DNS.
|
|
65
|
+
* If CoreDNS configuration seems problematic, refer to CoreDNS documentation and the Kubernetes guide on customizing it.
|
|
66
|
+
* If upstream DNS resolution is failing, suggest checking the upstream DNS servers and CoreDNS forward configuration.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# AKS Upgrade Troubleshooting Guidelines
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
Your primary goal when using these tools is to diagnose cluster and nodepool upgrade failures within an AKS cluster by strictly following the workflow for AKS upgrade diagnosis.
|
|
5
|
+
|
|
6
|
+
* Use the tools to gather information about the cluster configuration and upgrade failure reason.
|
|
7
|
+
* Clearly present the key findings from the tool outputs in your analysis.
|
|
8
|
+
* Instead of providing next steps to the user, you need to follow the troubleshoot guide to execute the steps.
|
|
9
|
+
|
|
10
|
+
## Workflow for AKS Upgrade Diagnosis
|
|
11
|
+
|
|
12
|
+
1. **Check Cluster and Nodepool Status:**
|
|
13
|
+
* Get the current cluster context - cluster name, resource group, and subscription ID. Get the configuration details for this current cluster only. If there is no cluster context set up, ask the user to set the right AKS cluster context using Azure CLI.
|
|
14
|
+
* Check the provisioning status of your AKS cluster to see if it's in a Failed state. If the cluster is in a Succeeded or Upgrading state, this means that no recent or ongoing upgrades have failed.
|
|
15
|
+
* Check the provisioning status of your AKS nodepools. If any nodepools have a Failed status, that means that a recent nodepool upgrade has failed.
|
|
16
|
+
* If the cluster or nodepool is in a Failed state, check the error message and read any documentation links that may be included in the error message details (starting with aka.ms).
|
|
17
|
+
|
|
18
|
+
If the cluster is in a Succeeded state, follow the steps below to identify if there are any potential issues which could results in a failed cluster - for example, check for any restrictive PDBs or that outbound connectivity endpoints are blocked. If the cluster is in Failed state, check the error message and follow the steps below for that specific error code.
|
|
19
|
+
|
|
20
|
+
2. **Error code: 'VMExtensionError_OutBoundConnFail'**
|
|
21
|
+
* If the upgrade failure error message contains 'VMExtensionError_OutBoundConnFail', this means that the cluster upgrade failed due to one or more essential outbound connectivity endpoints being blocked. Most commonly, this is due to a misconfiguration in a network policy, NSG, or Azure firewall which is denying traffic to the endpoint. Check if any of the required rules/endpoints in https://learn.microsoft.com/en-us/azure/aks/outbound-rules-control-egress#azure-global-required-network-rules are blocked due to a network policy, NSG, or Azure firewall and identify the problematic rule.
|
|
22
|
+
* Check if the outbound connectivity issue is due to an NSG rule by first getting all of the NSGs in the current subscription. Find the NSGs which have a ResourceGroup which contains both the current resource group and cluster name. For each NSG, list all of the rules and determine if any rule is blocking an essential endpoint in the doc linked above. Do this analysis on behalf of the user and only return the problematic NSG rules.
|
|
23
|
+
|
|
24
|
+
3. **PDB blocking upgrade: Error code "UnsatisfiablePDB"**
|
|
25
|
+
* If the upgrade fails due to one or more restrictive PDB, this means that one or more PDBs were set in the cluster which do not allow for any pod disruptions. Get all the PDBs configured on the cluster by running 'kubectl get pdb' and identify all PDBs where MaxUnavailable is set to 0 - if any are found, call them out specifically and ask the customer to follow the guidance in this troubleshooting guide for restrictive PDBs: https://learn.microsoft.com/en-us/troubleshoot/azure/azure-kubernetes/error-codes/unsatisfiablepdb-error.
|
|
26
|
+
|
|
27
|
+
4. **Quota exhaustion issues: Error code "QuotaExceeded"**
|
|
28
|
+
* If the upgrade fails due to insufficient quota with error code "QuotaExceeded", this means that your subscription doesn't have available resources that are required for upgrading your cluster. You will need to raise the limit or quota for your subscription by filing a "Service and subscription limits (quotas)" support ticket to increase the quota for compute cores. Provide detailed instructions in the response on how to open a specific "Service and subscription limits" support ticket through the Azure Portal for an AKS cluster.
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
## Synthesize Findings
|
|
32
|
+
Based on the outputs from the above steps, describe the upgrade issue clearly and recommend specific and actionable remediation steps. For example:
|
|
33
|
+
* "Upgrade is failing due to outbound connectivity to x endpoint is being blocked by your NSG rules. Please remove the blocking NSG rule x and retry upgrade."
|
|
34
|
+
* If upgrade is failing due to a restrictive PDB, check how many pods for that deployment are currently deployed. If there is only 1 pod, advise the customer to scale up their replicas to allow for upgrades while maintaining availability. "Upgrade is failing due to a restrictive PDB on your x pods which does not tolerate any disruptions. Please set the minAvailable to allow for 1 pod to be disrupted at a time to allow upgrades while maintaining availability."
|
|
35
|
+
|
|
36
|
+
## Recommend Remediation Steps (Based on Docs)
|
|
37
|
+
* **CRITICAL:** ALWAYS refer to the official AKS upgrade troubleshooting guide for detailed troubleshooting and solutions: https://learn.microsoft.com/en-us/troubleshoot/azure/azure-kubernetes/create-upgrade-delete/upgrading-or-scaling-does-not-succeed
|
|
38
|
+
* **DO NOT invent recovery procedures.** Your role is to diagnose and *point* to the correct documentation or standard procedures.
|
|
39
|
+
* Based on the findings, suggest which sections of the documentation are most relevant.
|
|
40
|
+
* If upgrade is failing due to PDB blocking, provide specific guidance from the documentation towards adjusting PDB settings.
|
|
41
|
+
* If upgrade is failing due to quota exhaustion, provide specific guidance from the documentation towards reviewing resource quotas.
|
|
42
|
+
* If upgrade is failing due to node issues, provide specific guidance from the documentation towards reviewing node status and health.
|
|
43
|
+
* If upgrade is failing due to network issues, provide specific guidance from the documentation towards reviewing network configuration.
|
|
44
|
+
* If upgrade is failing due to other issues, provide specific guidance from the documentation towards reviewing the upgrade logs and configuration.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import List
|
|
3
|
+
from holmes.core.tool_calling_llm import LLMResult
|
|
4
|
+
from holmes.plugins.interfaces import SourcePlugin
|
|
5
|
+
from holmes.core.issue import Issue
|
|
6
|
+
import requests # type: ignore
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class GitHubSource(SourcePlugin):
|
|
10
|
+
def __init__(self, url: str, owner: str, repository: str, pat: str, query: str):
|
|
11
|
+
self.url = url
|
|
12
|
+
self.owner = owner
|
|
13
|
+
self.repository = repository
|
|
14
|
+
self.pat = pat
|
|
15
|
+
self.query = query
|
|
16
|
+
|
|
17
|
+
def fetch_issues(self) -> List[Issue]:
|
|
18
|
+
logging.info(
|
|
19
|
+
f"Fetching All issues from {self.url} for repository {self.owner}/{self.repository}"
|
|
20
|
+
)
|
|
21
|
+
try:
|
|
22
|
+
data = []
|
|
23
|
+
url = f"{self.url}/search/issues"
|
|
24
|
+
headers = {
|
|
25
|
+
"Authorization": f"token {self.pat}",
|
|
26
|
+
"Accept": "application/vnd.github.v3+json",
|
|
27
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
28
|
+
}
|
|
29
|
+
params = {"per_page": "100"}
|
|
30
|
+
default_q = f"repo:{self.owner}/{self.repository}"
|
|
31
|
+
params["q"] = f"{default_q} {self.query}"
|
|
32
|
+
while url:
|
|
33
|
+
response = requests.get(url=url, headers=headers, params=params)
|
|
34
|
+
if response.status_code != 200:
|
|
35
|
+
raise Exception(
|
|
36
|
+
f"Failed to get issues:{response.status_code} {response.text}"
|
|
37
|
+
)
|
|
38
|
+
logging.info(f"Got {response}")
|
|
39
|
+
response.raise_for_status()
|
|
40
|
+
data.extend(response.json().get("items", []))
|
|
41
|
+
links = response.headers.get("Link", "")
|
|
42
|
+
url = None # type: ignore
|
|
43
|
+
for link in links.split(","):
|
|
44
|
+
if 'rel="next"' in link:
|
|
45
|
+
url = link.split(";")[0].strip()[1:-1]
|
|
46
|
+
return [self.convert_to_issue(issue) for issue in data]
|
|
47
|
+
except requests.RequestException as e:
|
|
48
|
+
raise ConnectionError("Failed to fetch data from GitHub.") from e
|
|
49
|
+
|
|
50
|
+
def convert_to_issue(self, github_issue):
|
|
51
|
+
return Issue(
|
|
52
|
+
id=str(github_issue["number"]),
|
|
53
|
+
name=github_issue["title"],
|
|
54
|
+
source_type="github",
|
|
55
|
+
source_instance_id=f"{self.owner}/{self.repository}",
|
|
56
|
+
url=github_issue["html_url"],
|
|
57
|
+
raw=github_issue,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def write_back_result(self, issue_id: str, result_data: LLMResult) -> None:
|
|
61
|
+
url = f"{self.url}/repos/{self.owner}/{self.repository}/issues/{issue_id}/comments"
|
|
62
|
+
headers = {
|
|
63
|
+
"Authorization": f"token {self.pat}",
|
|
64
|
+
"Accept": "application/vnd.github.v3+json",
|
|
65
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
66
|
+
}
|
|
67
|
+
response = requests.post(
|
|
68
|
+
url=url,
|
|
69
|
+
json={
|
|
70
|
+
"body": f"Automatic AI Investigation by Robusta:\n\n{result_data.result}\n"
|
|
71
|
+
},
|
|
72
|
+
headers=headers,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
response.raise_for_status()
|
|
76
|
+
data = response.json()
|
|
77
|
+
logging.debug(f"Posted comment to issue #{issue_id} at {data['html_url']}")
|