logdetective 2.0.0__tar.gz → 2.0.2__tar.gz
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-2.0.0 → logdetective-2.0.2}/PKG-INFO +12 -5
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/extractors.py +4 -2
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/models.py +2 -2
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/server.py +10 -8
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/templates/gitlab_full_comment.md.j2 +1 -1
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/utils.py +1 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/utils.py +2 -2
- {logdetective-2.0.0 → logdetective-2.0.2}/pyproject.toml +37 -18
- {logdetective-2.0.0 → logdetective-2.0.2}/LICENSE +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/README.md +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/__init__.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/constants.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/drain3.ini +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/logdetective.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/models.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/prompts-summary-first.yml +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/prompts-summary-only.yml +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/prompts.yml +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/remote_log.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/__init__.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/compressors.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/config.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/database/__init__.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/database/base.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/database/models/__init__.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/database/models/exceptions.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/database/models/koji.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/database/models/merge_request_jobs.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/database/models/metrics.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/emoji.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/exceptions.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/gitlab.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/koji.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/llm.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/metric.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/plot.py +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/templates/gitlab_short_comment.md.j2 +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective/skip_snippets.yml +0 -0
- {logdetective-2.0.0 → logdetective-2.0.2}/logdetective.1.asciidoc +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: logdetective
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.2
|
|
4
4
|
Summary: Log using LLM AI to search for build/test failures and provide ideas for fixing these.
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Author: Jiri Podivin
|
|
@@ -20,23 +20,30 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
|
20
20
|
Classifier: Topic :: Software Development :: Debuggers
|
|
21
21
|
Provides-Extra: server
|
|
22
22
|
Provides-Extra: server-testing
|
|
23
|
-
|
|
23
|
+
Provides-Extra: testing
|
|
24
|
+
Requires-Dist: aiohttp (>=3.7.4,<4.0.0)
|
|
24
25
|
Requires-Dist: aiolimiter (>=1.0.0,<2.0.0) ; extra == "server"
|
|
26
|
+
Requires-Dist: aioresponses (>=0.7.8,<0.8.0) ; extra == "testing"
|
|
25
27
|
Requires-Dist: alembic (>=1.13.3,<2.0.0) ; extra == "server" or extra == "server-testing"
|
|
26
28
|
Requires-Dist: backoff (==2.2.1) ; extra == "server" or extra == "server-testing"
|
|
27
29
|
Requires-Dist: drain3 (>=0.9.11,<0.10.0)
|
|
28
|
-
Requires-Dist: fastapi (>=0.111.1) ; extra == "server" or extra == "server-testing"
|
|
29
|
-
Requires-Dist:
|
|
30
|
+
Requires-Dist: fastapi (>=0.111.1,<1.0.0) ; extra == "server" or extra == "server-testing"
|
|
31
|
+
Requires-Dist: flexmock (>=0.12.2,<0.13.0) ; extra == "testing"
|
|
32
|
+
Requires-Dist: huggingface-hub (>0.23.2,<0.35.0)
|
|
30
33
|
Requires-Dist: koji (>=1.35.0,<2.0.0) ; extra == "server" or extra == "server-testing"
|
|
31
|
-
Requires-Dist: llama-cpp-python (>0.2.56,!=0.2.86)
|
|
34
|
+
Requires-Dist: llama-cpp-python (>0.2.56,!=0.2.86,<1.0.0)
|
|
32
35
|
Requires-Dist: matplotlib (>=3.8.4,<4.0.0) ; extra == "server" or extra == "server-testing"
|
|
33
36
|
Requires-Dist: numpy (>=1.26.0)
|
|
34
37
|
Requires-Dist: openai (>=1.82.1,<2.0.0) ; extra == "server" or extra == "server-testing"
|
|
35
38
|
Requires-Dist: psycopg2 (>=2.9.9,<3.0.0) ; extra == "server"
|
|
36
39
|
Requires-Dist: psycopg2-binary (>=2.9.9,<3.0.0) ; extra == "server-testing"
|
|
37
40
|
Requires-Dist: pydantic (>=2.8.2,<3.0.0)
|
|
41
|
+
Requires-Dist: pytest (>=8.4.1,<9.0.0) ; extra == "testing"
|
|
42
|
+
Requires-Dist: pytest-asyncio (>=1.1.0,<2.0.0) ; extra == "testing"
|
|
43
|
+
Requires-Dist: pytest-mock (>=3.14.1,<4.0.0) ; extra == "server-testing"
|
|
38
44
|
Requires-Dist: python-gitlab (>=4.4.0)
|
|
39
45
|
Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
|
|
46
|
+
Requires-Dist: responses (>=0.25.7,<0.26.0) ; extra == "server-testing"
|
|
40
47
|
Requires-Dist: sentry-sdk[fastapi] (>=2.17.0,<3.0.0) ; extra == "server" or extra == "server-testing"
|
|
41
48
|
Requires-Dist: sqlalchemy (>=2.0.36,<3.0.0) ; extra == "server" or extra == "server-testing"
|
|
42
49
|
Project-URL: homepage, https://github.com/fedora-copr/logdetective
|
|
@@ -20,7 +20,8 @@ class DrainExtractor:
|
|
|
20
20
|
context: bool = False,
|
|
21
21
|
max_clusters=8,
|
|
22
22
|
skip_snippets: SkipSnippets = SkipSnippets({}),
|
|
23
|
-
|
|
23
|
+
max_snippet_len: int = 2000
|
|
24
|
+
): # pylint: disable=R0913,R0917
|
|
24
25
|
config = TemplateMinerConfig()
|
|
25
26
|
config.load(f"{os.path.dirname(__file__)}/drain3.ini")
|
|
26
27
|
config.profiling_enabled = verbose
|
|
@@ -29,11 +30,12 @@ class DrainExtractor:
|
|
|
29
30
|
self.verbose = verbose
|
|
30
31
|
self.context = context
|
|
31
32
|
self.skip_snippets = skip_snippets
|
|
33
|
+
self.max_snippet_len = max_snippet_len
|
|
32
34
|
|
|
33
35
|
def __call__(self, log: str) -> list[Tuple[int, str]]:
|
|
34
36
|
out = []
|
|
35
37
|
# Create chunks
|
|
36
|
-
chunks = list(get_chunks(log))
|
|
38
|
+
chunks = list(get_chunks(log, self.max_snippet_len))
|
|
37
39
|
# Keep only chunks that don't match any of the excluded patterns
|
|
38
40
|
chunks = [
|
|
39
41
|
(_, chunk)
|
|
@@ -244,18 +244,18 @@ class InferenceConfig(BaseModel): # pylint: disable=too-many-instance-attribute
|
|
|
244
244
|
class ExtractorConfig(BaseModel):
|
|
245
245
|
"""Model for extractor configuration of logdetective server."""
|
|
246
246
|
|
|
247
|
-
context: bool = True
|
|
248
247
|
max_clusters: int = 8
|
|
249
248
|
verbose: bool = False
|
|
249
|
+
max_snippet_len: int = 2000
|
|
250
250
|
|
|
251
251
|
def __init__(self, data: Optional[dict] = None):
|
|
252
252
|
super().__init__()
|
|
253
253
|
if data is None:
|
|
254
254
|
return
|
|
255
255
|
|
|
256
|
-
self.context = data.get("context", True)
|
|
257
256
|
self.max_clusters = data.get("max_clusters", 8)
|
|
258
257
|
self.verbose = data.get("verbose", False)
|
|
258
|
+
self.max_snippet_len = data.get("max_snippet_len", 2000)
|
|
259
259
|
|
|
260
260
|
|
|
261
261
|
class GitLabInstanceConfig(BaseModel): # pylint: disable=too-many-instance-attributes
|
|
@@ -117,23 +117,25 @@ def requires_token_when_set(authentication: Annotated[str | None, Header()] = No
|
|
|
117
117
|
LOG.info("LOGDETECTIVE_TOKEN env var not set, authentication disabled")
|
|
118
118
|
# no token required, means local dev environment
|
|
119
119
|
return
|
|
120
|
-
token = None
|
|
121
120
|
if authentication:
|
|
122
121
|
try:
|
|
123
122
|
token = authentication.split(" ", 1)[1]
|
|
124
|
-
except (ValueError, IndexError):
|
|
123
|
+
except (ValueError, IndexError) as ex:
|
|
125
124
|
LOG.warning(
|
|
126
|
-
"Authentication header has invalid structure
|
|
125
|
+
"Authentication header has invalid structure '%s', it should be 'Bearer TOKEN'",
|
|
127
126
|
authentication,
|
|
128
127
|
)
|
|
129
128
|
# eat the exception and raise 401 below
|
|
130
|
-
|
|
129
|
+
raise HTTPException(
|
|
130
|
+
status_code=401,
|
|
131
|
+
detail=f"Invalid authentication, HEADER '{authentication}' not valid.",
|
|
132
|
+
) from ex
|
|
131
133
|
if token == API_TOKEN:
|
|
132
134
|
return
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
)
|
|
136
|
-
raise HTTPException(status_code=401, detail=
|
|
135
|
+
LOG.info("Provided token '%s' does not match expected value.", token)
|
|
136
|
+
raise HTTPException(status_code=401, detail=f"Token '{token}' not valid.")
|
|
137
|
+
LOG.error("No authentication header provided but LOGDETECTIVE_TOKEN env var is set")
|
|
138
|
+
raise HTTPException(status_code=401, detail="No token provided.")
|
|
137
139
|
|
|
138
140
|
|
|
139
141
|
app = FastAPI(dependencies=[Depends(requires_token_when_set)], lifespan=lifespan)
|
{logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/templates/gitlab_full_comment.md.j2
RENAMED
|
@@ -12,7 +12,7 @@ In this case, we are {{ "%.2f" | format(certainty) }}% certain of the response {
|
|
|
12
12
|
{% for snippet in snippets %}
|
|
13
13
|
<li>
|
|
14
14
|
<b>Line {{ snippet.line_number }}:</b> <code>{{ snippet.text }}</code>
|
|
15
|
-
{{ snippet.explanation }}
|
|
15
|
+
{{ snippet.explanation.text }}
|
|
16
16
|
</li>
|
|
17
17
|
{% endfor %}
|
|
18
18
|
</ul>
|
|
@@ -39,7 +39,7 @@ def chunk_continues(text: str, index: int) -> bool:
|
|
|
39
39
|
return False
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
def get_chunks(text: str) -> Generator[Tuple[int, str], None, None]:
|
|
42
|
+
def get_chunks(text: str, max_len: int = 2000) -> Generator[Tuple[int, str], None, None]:
|
|
43
43
|
"""Split log into chunks according to heuristic
|
|
44
44
|
based on whitespace and backslash presence.
|
|
45
45
|
"""
|
|
@@ -54,7 +54,7 @@ def get_chunks(text: str) -> Generator[Tuple[int, str], None, None]:
|
|
|
54
54
|
chunk += text[i]
|
|
55
55
|
if text[i] == "\n":
|
|
56
56
|
next_line_number += 1
|
|
57
|
-
if i + 1 < text_len and chunk_continues(text, i):
|
|
57
|
+
if i + 1 < text_len and chunk_continues(text, i) and i + 1 < max_len:
|
|
58
58
|
i += 1
|
|
59
59
|
continue
|
|
60
60
|
yield (original_line_number, chunk)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "logdetective"
|
|
3
|
-
version = "2.0.
|
|
3
|
+
version = "2.0.2"
|
|
4
4
|
description = "Log using LLM AI to search for build/test failures and provide ideas for fixing these."
|
|
5
5
|
authors = ["Jiri Podivin <jpodivin@gmail.com>"]
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -32,32 +32,51 @@ issues = "https://github.com/fedora-copr/logdetective/issues"
|
|
|
32
32
|
|
|
33
33
|
[tool.poetry.dependencies]
|
|
34
34
|
python = "^3.11"
|
|
35
|
-
llama-cpp-python = ">0.2.56,!=0.2.86"
|
|
36
|
-
drain3 = "
|
|
37
|
-
huggingface-hub = ">0.23.2"
|
|
35
|
+
llama-cpp-python = ">0.2.56,!=0.2.86,<1.0.0"
|
|
36
|
+
drain3 = ">=0.9.11,<0.10.0"
|
|
37
|
+
huggingface-hub = ">0.23.2,<0.35.0"
|
|
38
38
|
# rawhide has numpy 2, F40 and F41 are still on 1.26
|
|
39
39
|
# we need to support both versions
|
|
40
40
|
numpy = ">=1.26.0"
|
|
41
41
|
python-gitlab = ">=4.4.0"
|
|
42
|
-
pydantic = "
|
|
43
|
-
pyyaml = "
|
|
44
|
-
aiohttp = ">=3.7.4" # latest version in el8
|
|
45
|
-
aiolimiter = "
|
|
42
|
+
pydantic = ">=2.8.2,<3.0.0"
|
|
43
|
+
pyyaml = ">=6.0.1,<7.0.0"
|
|
44
|
+
aiohttp = ">=3.7.4,<4.0.0" # latest version in el8
|
|
45
|
+
aiolimiter = ">=1.0.0,<2.0.0"
|
|
46
46
|
|
|
47
|
-
fastapi = {version = ">=0.111.1", optional = true }
|
|
48
|
-
sqlalchemy = {version = "
|
|
49
|
-
psycopg2-binary = {version = "
|
|
50
|
-
psycopg2 = {version = "
|
|
51
|
-
alembic = {version = "
|
|
52
|
-
matplotlib = {version = "
|
|
47
|
+
fastapi = {version = ">=0.111.1,<1.0.0", optional = true }
|
|
48
|
+
sqlalchemy = {version = ">=2.0.36,<3.0.0", optional = true }
|
|
49
|
+
psycopg2-binary = {version = ">=2.9.9,<3.0.0", optional = true }
|
|
50
|
+
psycopg2 = {version = ">=2.9.9,<3.0.0", optional = true }
|
|
51
|
+
alembic = {version = ">=1.13.3,<2.0.0", optional = true }
|
|
52
|
+
matplotlib = {version = ">=3.8.4,<4.0.0", optional = true }
|
|
53
53
|
backoff = {version = "2.2.1", optional = true }
|
|
54
|
-
sentry-sdk = {version = "
|
|
55
|
-
openai = {version = "
|
|
56
|
-
koji = {version = "
|
|
54
|
+
sentry-sdk = {version = ">=2.17.0,<3.0.0", optional = true, extras = ["fastapi"]}
|
|
55
|
+
openai = {version = ">=1.82.1,<2.0.0", optional = true}
|
|
56
|
+
koji = {version = ">=1.35.0,<2.0.0", optional = true}
|
|
57
|
+
responses = {version = ">=0.25.7,<0.26.0", optional = true}
|
|
58
|
+
aioresponses = {version = ">=0.7.8,<0.8.0", optional = true}
|
|
59
|
+
pytest-asyncio = {version = ">=1.1.0,<2.0.0", optional = true}
|
|
60
|
+
pytest-mock = {version = ">=3.14.1,<4.0.0", optional = true}
|
|
61
|
+
flexmock = {version = ">=0.12.2,<0.13.0", optional = true}
|
|
62
|
+
pytest = {version = ">=8.4.1,<9.0.0", optional = true}
|
|
57
63
|
|
|
58
64
|
[tool.poetry.extras]
|
|
59
65
|
server = ["fastapi", "sqlalchemy", "psycopg2", "alembic", "matplotlib", "backoff", "aiolimiter", "sentry-sdk", "openai", "koji"]
|
|
60
|
-
server-testing = [
|
|
66
|
+
server-testing = [
|
|
67
|
+
"fastapi",
|
|
68
|
+
"sqlalchemy",
|
|
69
|
+
"psycopg2-binary",
|
|
70
|
+
"alembic",
|
|
71
|
+
"matplotlib",
|
|
72
|
+
"backoff",
|
|
73
|
+
"sentry-sdk",
|
|
74
|
+
"openai",
|
|
75
|
+
"koji",
|
|
76
|
+
"pytest-mock",
|
|
77
|
+
"responses",
|
|
78
|
+
]
|
|
79
|
+
testing = ["pytest", "pytest-asyncio", "aioresponses", "flexmock"]
|
|
61
80
|
|
|
62
81
|
[build-system]
|
|
63
82
|
requires = ["poetry-core"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/database/models/merge_request_jobs.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{logdetective-2.0.0 → logdetective-2.0.2}/logdetective/server/templates/gitlab_short_comment.md.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|