logdetective 0.4.0__py3-none-any.whl → 2.11.0__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.
- logdetective/constants.py +33 -12
- logdetective/extractors.py +137 -68
- logdetective/logdetective.py +102 -33
- logdetective/models.py +99 -0
- logdetective/prompts-summary-first.yml +20 -0
- logdetective/prompts-summary-only.yml +13 -0
- logdetective/prompts.yml +90 -0
- logdetective/remote_log.py +67 -0
- logdetective/server/compressors.py +186 -0
- logdetective/server/config.py +78 -0
- logdetective/server/database/base.py +34 -26
- logdetective/server/database/models/__init__.py +33 -0
- logdetective/server/database/models/exceptions.py +17 -0
- logdetective/server/database/models/koji.py +143 -0
- logdetective/server/database/models/merge_request_jobs.py +623 -0
- logdetective/server/database/models/metrics.py +427 -0
- logdetective/server/emoji.py +148 -0
- logdetective/server/exceptions.py +37 -0
- logdetective/server/gitlab.py +451 -0
- logdetective/server/koji.py +159 -0
- logdetective/server/llm.py +309 -0
- logdetective/server/metric.py +75 -30
- logdetective/server/models.py +426 -23
- logdetective/server/plot.py +432 -0
- logdetective/server/server.py +580 -468
- logdetective/server/templates/base_response.html.j2 +59 -0
- logdetective/server/templates/gitlab_full_comment.md.j2 +73 -0
- logdetective/server/templates/gitlab_short_comment.md.j2 +62 -0
- logdetective/server/utils.py +98 -32
- logdetective/skip_snippets.yml +12 -0
- logdetective/utils.py +187 -73
- logdetective-2.11.0.dist-info/METADATA +568 -0
- logdetective-2.11.0.dist-info/RECORD +40 -0
- {logdetective-0.4.0.dist-info → logdetective-2.11.0.dist-info}/WHEEL +1 -1
- logdetective/server/database/models.py +0 -88
- logdetective-0.4.0.dist-info/METADATA +0 -333
- logdetective-0.4.0.dist-info/RECORD +0 -19
- {logdetective-0.4.0.dist-info → logdetective-2.11.0.dist-info}/entry_points.txt +0 -0
- {logdetective-0.4.0.dist-info → logdetective-2.11.0.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<b>Log Detective report
|
|
2
|
+
{% if project %}for {{ project }}{% endif %}
|
|
3
|
+
</b>
|
|
4
|
+
<p>
|
|
5
|
+
Analysis:
|
|
6
|
+
<br>
|
|
7
|
+
{{ explanation }}
|
|
8
|
+
<br>
|
|
9
|
+
<details>
|
|
10
|
+
<summary>Analysis details</summary>
|
|
11
|
+
<ul>
|
|
12
|
+
{% for snippet in snippets %}
|
|
13
|
+
<li>
|
|
14
|
+
<b>Line {{ snippet.line_number }}:</b> <code>{{ snippet.text }}</code>
|
|
15
|
+
{{ snippet.explanation.text }}
|
|
16
|
+
</li>
|
|
17
|
+
{% endfor %}
|
|
18
|
+
</ul>
|
|
19
|
+
</details>
|
|
20
|
+
</p>
|
|
21
|
+
<br>
|
|
22
|
+
<details>
|
|
23
|
+
<summary>Help</summary>
|
|
24
|
+
<p>
|
|
25
|
+
Log Detective analyses logs using template mining algorithm and LLMs.
|
|
26
|
+
Providing overview of the primary issue, while highlighting potentially important log lines.
|
|
27
|
+
</p>
|
|
28
|
+
<p>
|
|
29
|
+
Log Detective may offer potential solution to problem.
|
|
30
|
+
However, it is designed to do so only when sufficiently confident in the response.
|
|
31
|
+
<br>
|
|
32
|
+
Even so, as with any LLM based AI system, recommendations of Log Detective must be taken with caution.
|
|
33
|
+
Before acting on any recommendations of Log Detective, consider following:
|
|
34
|
+
</p>
|
|
35
|
+
<ul>
|
|
36
|
+
<li>
|
|
37
|
+
<b>Do you understand how does the proposed solution work? And why should it solve your problem?</b>
|
|
38
|
+
</li>
|
|
39
|
+
<li>Did Log Detective work with sufficient information to make this suggestion?</li>
|
|
40
|
+
<li>Are the suggested actions safe in the context of your work?</li>
|
|
41
|
+
<li>Do results of snippet analysis align with results of final analysis?</li>
|
|
42
|
+
<li>Are there any potentially adverse outcomes from implementing proposed solution?</li>
|
|
43
|
+
</ul>
|
|
44
|
+
</details>
|
|
45
|
+
<br>
|
|
46
|
+
{% render_contacts %}
|
|
47
|
+
<details>
|
|
48
|
+
<summary>Contacts</summary>
|
|
49
|
+
<p>Don't hesitate to reach out with your questions or suggestions.</p>
|
|
50
|
+
<ul>
|
|
51
|
+
<li>
|
|
52
|
+
<a href="https://github.com/fedora-copr/logdetective">Source</a>
|
|
53
|
+
</li>
|
|
54
|
+
<li>
|
|
55
|
+
<a href="https://github.com/fedora-copr/logdetective/issues">Issue tracker</a>
|
|
56
|
+
</li>
|
|
57
|
+
</ul>
|
|
58
|
+
</details>
|
|
59
|
+
{% endif %}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
The package {{ package }} build has experienced an issue.
|
|
2
|
+
Please know that the explanation was provided by AI and may be incorrect.
|
|
3
|
+
{% if certainty > 0 %}
|
|
4
|
+
In this case, we are {{ "%.2f" | format(certainty) }}% certain of the response {{ emoji_face }}.
|
|
5
|
+
{% endif %}
|
|
6
|
+
|
|
7
|
+
<details open>
|
|
8
|
+
<summary>Description</summary>
|
|
9
|
+
{{ explanation }}
|
|
10
|
+
</details>
|
|
11
|
+
<details>
|
|
12
|
+
{#
|
|
13
|
+
Formatted so that we don't trigger GitLab markdown
|
|
14
|
+
#}
|
|
15
|
+
<summary>Snippets</summary>
|
|
16
|
+
<ul>
|
|
17
|
+
{% for snippet in snippets -%}
|
|
18
|
+
<li><div><b>Line {{ snippet.line_number }}:</b> <code>{{ snippet.text | e }}</code><br>{{ snippet.explanation.text | e }}</div></li>
|
|
19
|
+
{%- endfor %}
|
|
20
|
+
</ul>
|
|
21
|
+
</details>
|
|
22
|
+
<details>
|
|
23
|
+
<summary>Logs</summary>
|
|
24
|
+
<p>Log Detective analyzed the following logs files to provide an explanation:</p>
|
|
25
|
+
<ul>
|
|
26
|
+
<li>
|
|
27
|
+
<a href="{{ log_url }}">{{ log_url }}</a>
|
|
28
|
+
</li>
|
|
29
|
+
</ul>
|
|
30
|
+
<p>
|
|
31
|
+
Additional logs are available from:
|
|
32
|
+
<ul>
|
|
33
|
+
<li>
|
|
34
|
+
<a href="{{ artifacts_url }}">artifacts.zip</a>
|
|
35
|
+
</li>
|
|
36
|
+
</ul>
|
|
37
|
+
</p>
|
|
38
|
+
<p>
|
|
39
|
+
Please know that these log files are automatically removed after some
|
|
40
|
+
time, so you might need a backup.
|
|
41
|
+
</p>
|
|
42
|
+
</details>
|
|
43
|
+
<details>
|
|
44
|
+
<summary>Help</summary>
|
|
45
|
+
<p>Don't hesitate to reach out.</p>
|
|
46
|
+
<ul>
|
|
47
|
+
<li>
|
|
48
|
+
<a href="https://github.com/fedora-copr/logdetective">Upstream</a>
|
|
49
|
+
</li>
|
|
50
|
+
<li>
|
|
51
|
+
<a href="https://github.com/fedora-copr/logdetective/issues">Issue tracker</a>
|
|
52
|
+
</li>
|
|
53
|
+
<li>
|
|
54
|
+
<a href="https://redhat.enterprise.slack.com/archives/C06DWNVKKDE">Slack</a>
|
|
55
|
+
</li>
|
|
56
|
+
<li>
|
|
57
|
+
<a href="https://log-detective.com/documentation">Documentation</a>
|
|
58
|
+
</li>
|
|
59
|
+
</ul>
|
|
60
|
+
</details>
|
|
61
|
+
|
|
62
|
+
<hr>
|
|
63
|
+
|
|
64
|
+
This explanation was provided by AI (<a href="https://logdetective.com">Log Detective</a>).
|
|
65
|
+
Always review AI generated content prior to use.
|
|
66
|
+
Was the provided feedback accurate and helpful?
|
|
67
|
+
<br>
|
|
68
|
+
Please vote with :thumbsup:
|
|
69
|
+
or :thumbsdown: to help us improve.
|
|
70
|
+
<br>
|
|
71
|
+
<i>If this Log Detective report contains harmful content,
|
|
72
|
+
please use the <a href="https://docs.gitlab.com/user/report_abuse/">Gitlab reporting feature for harmful content</a>
|
|
73
|
+
and contact the <a href="https://github.com/fedora-copr/logdetective/issues">Log Detective developers</a>.</i>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
The package {{ package }} build has experienced an issue.
|
|
2
|
+
Please know that the explanation was provided by AI and may be incorrect.
|
|
3
|
+
{% if certainty > 0 %}
|
|
4
|
+
In this case, we are {{ "%.2f" | format(certainty) }}% certain of the response {{ emoji_face }}.
|
|
5
|
+
{% endif %}
|
|
6
|
+
|
|
7
|
+
<details open>
|
|
8
|
+
<summary>Description</summary>
|
|
9
|
+
{{ explanation }}
|
|
10
|
+
</details>
|
|
11
|
+
<details>
|
|
12
|
+
<summary>Logs</summary>
|
|
13
|
+
<p>Log Detective analyzed the following logs files to provide an explanation:</p>
|
|
14
|
+
<ul>
|
|
15
|
+
<li>
|
|
16
|
+
<a href="{{ log_url }}">{{ log_url }}</a>
|
|
17
|
+
</li>
|
|
18
|
+
</ul>
|
|
19
|
+
<p>
|
|
20
|
+
Additional logs are available from:
|
|
21
|
+
<ul>
|
|
22
|
+
<li>
|
|
23
|
+
<a href="{{ artifacts_url }}">artifacts.zip</a>
|
|
24
|
+
</li>
|
|
25
|
+
</ul>
|
|
26
|
+
</p>
|
|
27
|
+
<p>
|
|
28
|
+
Please know that these log files are automatically removed after some
|
|
29
|
+
time, so you might need a backup.
|
|
30
|
+
</p>
|
|
31
|
+
</details>
|
|
32
|
+
<details>
|
|
33
|
+
<summary>Help</summary>
|
|
34
|
+
<p>Don't hesitate to reach out.</p>
|
|
35
|
+
<ul>
|
|
36
|
+
<li>
|
|
37
|
+
<a href="https://github.com/fedora-copr/logdetective">Upstream</a>
|
|
38
|
+
</li>
|
|
39
|
+
<li>
|
|
40
|
+
<a href="https://github.com/fedora-copr/logdetective/issues">Issue tracker</a>
|
|
41
|
+
</li>
|
|
42
|
+
<li>
|
|
43
|
+
<a href="https://redhat.enterprise.slack.com/archives/C06DWNVKKDE">Slack</a>
|
|
44
|
+
</li>
|
|
45
|
+
<li>
|
|
46
|
+
<a href="https://log-detective.com/documentation">Documentation</a>
|
|
47
|
+
</li>
|
|
48
|
+
</ul>
|
|
49
|
+
</details>
|
|
50
|
+
|
|
51
|
+
<hr>
|
|
52
|
+
|
|
53
|
+
This explanation was provided by AI (<a href="https://logdetective.com">Log Detective</a>).
|
|
54
|
+
Always review AI generated content prior to use.
|
|
55
|
+
Was the provided feedback accurate and helpful?
|
|
56
|
+
<br>
|
|
57
|
+
Please vote with :thumbsup:
|
|
58
|
+
or :thumbsdown: to help us improve.
|
|
59
|
+
<br>
|
|
60
|
+
<i>If this Log Detective report contains harmful content,
|
|
61
|
+
please use the <a href="https://docs.gitlab.com/user/report_abuse/">Gitlab reporting feature for harmful content</a>
|
|
62
|
+
and contact the <a href="https://github.com/fedora-copr/logdetective/issues">Log Detective developers</a>.</i>
|
logdetective/server/utils.py
CHANGED
|
@@ -1,44 +1,110 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
from logdetective.server.models import Config
|
|
1
|
+
from typing import List
|
|
2
|
+
from importlib.metadata import version
|
|
4
3
|
|
|
4
|
+
import aiohttp
|
|
5
|
+
from fastapi import HTTPException
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
from logdetective.constants import SNIPPET_DELIMITER
|
|
8
|
+
from logdetective.server.config import LOG
|
|
9
|
+
from logdetective.server.exceptions import LogDetectiveConnectionError
|
|
10
|
+
from logdetective.server.models import AnalyzedSnippet, RatedSnippetAnalysis
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def format_analyzed_snippets(snippets: list[AnalyzedSnippet]) -> str:
|
|
14
|
+
"""Format snippets for submission into staged prompt."""
|
|
15
|
+
summary = f"\n{SNIPPET_DELIMITER}\n".join(
|
|
16
|
+
[f"[{e.text}] at line [{e.line_number}]: [{e.explanation}]" for e in snippets]
|
|
17
|
+
)
|
|
18
|
+
return summary
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def connection_error_giveup(details: dict) -> None:
|
|
22
|
+
"""Too many connection errors, give up.
|
|
9
23
|
"""
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return Config(yaml.safe_load(config_file))
|
|
14
|
-
except FileNotFoundError:
|
|
15
|
-
pass
|
|
16
|
-
return Config()
|
|
24
|
+
LOG.error("Too many connection errors, giving up. %s", details["exception"])
|
|
25
|
+
raise LogDetectiveConnectionError() from details["exception"]
|
|
26
|
+
|
|
17
27
|
|
|
28
|
+
def should_we_giveup(exc: aiohttp.ClientResponseError) -> bool:
|
|
29
|
+
"""From backoff's docs:
|
|
18
30
|
|
|
19
|
-
|
|
31
|
+
> a function which accepts the exception and returns
|
|
32
|
+
> a truthy value if the exception should not be retried
|
|
20
33
|
"""
|
|
21
|
-
|
|
34
|
+
LOG.info("Should we give up on retrying error %s", exc)
|
|
35
|
+
return exc.status < 400
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def we_give_up(details: dict):
|
|
39
|
+
"""Retries didn't work (or we got a different exc)
|
|
40
|
+
we give up and raise proper 500 for our API endpoint
|
|
22
41
|
"""
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
42
|
+
LOG.error("Last exception: %s", details["exception"])
|
|
43
|
+
LOG.error("Inference error: %s", details["args"])
|
|
44
|
+
raise HTTPException(500, "Request to the inference API failed")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def select_relevance(snippet: AnalyzedSnippet) -> float:
|
|
48
|
+
"""Retrieve relevance value from structure, if there is one."""
|
|
49
|
+
if not isinstance(snippet.explanation, RatedSnippetAnalysis):
|
|
50
|
+
LOG.exception("Only rated snippets can be ordered by relevance.")
|
|
51
|
+
raise ValueError
|
|
52
|
+
return snippet.explanation.relevance
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def select_line_number(explanation: AnalyzedSnippet) -> int:
|
|
56
|
+
"""Returns line number of original snippet."""
|
|
57
|
+
return explanation.line_number
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def filter_snippets(
|
|
61
|
+
processed_snippets: List[AnalyzedSnippet], top_k: int
|
|
62
|
+
) -> List[AnalyzedSnippet]:
|
|
63
|
+
"""Filter snippets according to criteria in config while keeping them ordered by line number.
|
|
64
|
+
If all snippets recieved the same score, return them all.
|
|
65
|
+
AnalyzedSnippet objects must have `explanation` attribute set to `RatedSnippetAnalysis`,
|
|
66
|
+
otherwise raise `ValueError`."""
|
|
67
|
+
|
|
68
|
+
if top_k >= len(processed_snippets):
|
|
69
|
+
LOG.warning(
|
|
70
|
+
"The `top-k` parameter >= number of original snippets, skipping filtering."
|
|
71
|
+
)
|
|
72
|
+
return processed_snippets
|
|
73
|
+
|
|
74
|
+
# Sorting invokes `select_relevance` which also tests if objects actually
|
|
75
|
+
# have the score assigned. Otherwise it raises exception.
|
|
76
|
+
processed_snippets = sorted(processed_snippets, key=select_relevance, reverse=True)
|
|
77
|
+
|
|
78
|
+
# Check for failure mode when all snippets have
|
|
79
|
+
# the same relevance. In such cases there is no point in filtering
|
|
80
|
+
# and all snippets are returned.
|
|
81
|
+
max_relevance = processed_snippets[0].explanation.relevance
|
|
82
|
+
min_relevance = processed_snippets[-1].explanation.relevance
|
|
83
|
+
|
|
84
|
+
LOG.info(
|
|
85
|
+
"Analyzed snippets sorted. Max relevance: %d Min relevance: %e",
|
|
86
|
+
max_relevance,
|
|
87
|
+
min_relevance,
|
|
88
|
+
)
|
|
89
|
+
if max_relevance == min_relevance:
|
|
90
|
+
LOG.warning("All snippets recieved the same rating. Filtering disabled.")
|
|
91
|
+
return processed_snippets
|
|
92
|
+
|
|
93
|
+
processed_snippets = processed_snippets[:top_k]
|
|
94
|
+
|
|
95
|
+
# Re-sorting snippets by line number
|
|
96
|
+
processed_snippets = sorted(processed_snippets, key=select_line_number)
|
|
97
|
+
|
|
98
|
+
return processed_snippets
|
|
26
99
|
|
|
27
|
-
log.setLevel(config.log.level)
|
|
28
100
|
|
|
29
|
-
|
|
30
|
-
|
|
101
|
+
def construct_final_prompt(formatted_snippets: str, prompt_template: str) -> str:
|
|
102
|
+
"""Create final prompt from processed snippets and csgrep output, if it is available."""
|
|
31
103
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
stream_handler.setFormatter(logging.Formatter(config.log.format))
|
|
35
|
-
log.addHandler(stream_handler)
|
|
104
|
+
final_prompt = prompt_template.format(formatted_snippets)
|
|
105
|
+
return final_prompt
|
|
36
106
|
|
|
37
|
-
# Log to file
|
|
38
|
-
if config.log.path:
|
|
39
|
-
file_handler = logging.FileHandler(config.log.path)
|
|
40
|
-
file_handler.setFormatter(logging.Formatter(config.log.format))
|
|
41
|
-
log.addHandler(file_handler)
|
|
42
107
|
|
|
43
|
-
|
|
44
|
-
|
|
108
|
+
def get_version() -> str:
|
|
109
|
+
"""Obtain the version number using importlib"""
|
|
110
|
+
return version('logdetective')
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# This file holds patterns you want to skip during log parsing.
|
|
2
|
+
# By default, no patterns are supplied.
|
|
3
|
+
# Patterns are to be specified as values of dictionary,
|
|
4
|
+
# with each key being a descriptive name of the pattern.
|
|
5
|
+
# Patterns themselves are evaluated as a regular expression.
|
|
6
|
+
# Make sure to avoid regular expressions that may be interpreted
|
|
7
|
+
# as yaml syntax.
|
|
8
|
+
# Example:
|
|
9
|
+
|
|
10
|
+
# contains_capital_a: "^.*A.*"
|
|
11
|
+
# starts_with_numeric: "^[0-9].*"
|
|
12
|
+
child_exit_code_zero: "Child return code was: 0"
|