logdetective 0.10.0__py3-none-any.whl → 0.11.2__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/server/database/models/merge_request_jobs.py +17 -1
- logdetective/server/llm.py +6 -3
- logdetective/server/metric.py +18 -1
- logdetective/server/plot.py +157 -9
- logdetective/server/server.py +11 -2
- {logdetective-0.10.0.dist-info → logdetective-0.11.2.dist-info}/METADATA +17 -8
- {logdetective-0.10.0.dist-info → logdetective-0.11.2.dist-info}/RECORD +10 -10
- {logdetective-0.10.0.dist-info → logdetective-0.11.2.dist-info}/LICENSE +0 -0
- {logdetective-0.10.0.dist-info → logdetective-0.11.2.dist-info}/WHEEL +0 -0
- {logdetective-0.10.0.dist-info → logdetective-0.11.2.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import enum
|
|
2
2
|
import datetime
|
|
3
|
-
from typing import Optional, List
|
|
3
|
+
from typing import Optional, List, Tuple
|
|
4
4
|
|
|
5
5
|
import backoff
|
|
6
6
|
|
|
@@ -585,3 +585,19 @@ class Reactions(Base):
|
|
|
585
585
|
with transaction(commit=True) as session:
|
|
586
586
|
session.delete(reaction)
|
|
587
587
|
session.flush()
|
|
588
|
+
|
|
589
|
+
@classmethod
|
|
590
|
+
def get_since(
|
|
591
|
+
cls, time: datetime.datetime
|
|
592
|
+
) -> List[Tuple[datetime.datetime, "Comments"]]:
|
|
593
|
+
"""Get all the reactions on comments created after the given time
|
|
594
|
+
and the comment creation time."""
|
|
595
|
+
with transaction(commit=False) as session:
|
|
596
|
+
reactions = (
|
|
597
|
+
session.query(Comments.created_at, cls)
|
|
598
|
+
.join(Comments, cls.comment_id == Comments.id)
|
|
599
|
+
.filter(Comments.created_at > time)
|
|
600
|
+
.all()
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
return reactions
|
logdetective/server/llm.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import asyncio
|
|
3
3
|
import json
|
|
4
|
+
import random
|
|
4
5
|
from typing import List, Tuple, Dict, Any, Union
|
|
5
6
|
|
|
6
7
|
import backoff
|
|
@@ -102,7 +103,7 @@ def should_we_giveup(exc: aiohttp.ClientResponseError) -> bool:
|
|
|
102
103
|
> a truthy value if the exception should not be retried
|
|
103
104
|
"""
|
|
104
105
|
LOG.info("Should we give up on retrying error %s", exc)
|
|
105
|
-
return exc.status <
|
|
106
|
+
return exc.status < 400
|
|
106
107
|
|
|
107
108
|
|
|
108
109
|
def we_give_up(details: backoff._typing.Details):
|
|
@@ -110,14 +111,16 @@ def we_give_up(details: backoff._typing.Details):
|
|
|
110
111
|
retries didn't work (or we got a different exc)
|
|
111
112
|
we give up and raise proper 500 for our API endpoint
|
|
112
113
|
"""
|
|
114
|
+
LOG.error("Last exception: %s", details["exception"])
|
|
113
115
|
LOG.error("Inference error: %s", details["args"])
|
|
114
116
|
raise HTTPException(500, "Request to the inference API failed")
|
|
115
117
|
|
|
116
118
|
|
|
117
119
|
@backoff.on_exception(
|
|
118
|
-
backoff.
|
|
120
|
+
lambda: backoff.constant([10, 30, 120]),
|
|
119
121
|
aiohttp.ClientResponseError,
|
|
120
|
-
max_tries=3
|
|
122
|
+
max_tries=4, # 4 tries and 3 retries
|
|
123
|
+
jitter=lambda wait_gen_value: random.uniform(wait_gen_value, wait_gen_value + 30),
|
|
121
124
|
giveup=should_we_giveup,
|
|
122
125
|
raise_on_giveup=False,
|
|
123
126
|
on_giveup=we_give_up,
|
logdetective/server/metric.py
CHANGED
|
@@ -84,7 +84,24 @@ def update_metrics(
|
|
|
84
84
|
|
|
85
85
|
def track_request(name=None):
|
|
86
86
|
"""
|
|
87
|
-
Decorator to track requests metrics
|
|
87
|
+
Decorator to track requests/responses metrics
|
|
88
|
+
|
|
89
|
+
On entering the decorated function, it registers the time for the request
|
|
90
|
+
and saves the passed log content.
|
|
91
|
+
On exiting the decorated function, it registers the time for the response
|
|
92
|
+
and saves the generated response.
|
|
93
|
+
|
|
94
|
+
Use it to decorate server endpoints that generate a llm response
|
|
95
|
+
as in the following example:
|
|
96
|
+
|
|
97
|
+
>>> @app.post("/analyze", response_model=Response)
|
|
98
|
+
>>> @track_request()
|
|
99
|
+
>>> async def analyze_log(build_log)
|
|
100
|
+
>>> pass
|
|
101
|
+
|
|
102
|
+
Warning: the decorators' order is important!
|
|
103
|
+
The function returned by the *track_request* decorator is the
|
|
104
|
+
server API function we want to be called by FastAPI.
|
|
88
105
|
"""
|
|
89
106
|
|
|
90
107
|
def decorator(f):
|
logdetective/server/plot.py
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import datetime
|
|
2
|
-
from typing import Optional, Union
|
|
2
|
+
from typing import Optional, Union, Dict
|
|
3
3
|
|
|
4
4
|
import numpy
|
|
5
5
|
import matplotlib
|
|
6
6
|
import matplotlib.figure
|
|
7
7
|
import matplotlib.pyplot
|
|
8
8
|
|
|
9
|
+
from matplotlib.pyplot import cm
|
|
9
10
|
from logdetective.server import models
|
|
10
|
-
from logdetective.server.database.models import
|
|
11
|
+
from logdetective.server.database.models import (
|
|
12
|
+
AnalyzeRequestMetrics,
|
|
13
|
+
EndpointType,
|
|
14
|
+
Reactions,
|
|
15
|
+
)
|
|
11
16
|
|
|
12
17
|
|
|
13
18
|
class Definition:
|
|
@@ -145,13 +150,19 @@ def _add_bar_chart(
|
|
|
145
150
|
ax.grid(True, alpha=0.3)
|
|
146
151
|
|
|
147
152
|
|
|
148
|
-
def _add_line_chart(
|
|
149
|
-
ax: matplotlib.figure.Axes,
|
|
150
|
-
|
|
153
|
+
def _add_line_chart( # pylint: disable=too-many-arguments disable=too-many-positional-arguments
|
|
154
|
+
ax: matplotlib.figure.Axes,
|
|
155
|
+
timestamps: numpy.array,
|
|
156
|
+
values: numpy.array,
|
|
157
|
+
label: str,
|
|
158
|
+
color: str = "red",
|
|
159
|
+
set_label: bool = True,
|
|
160
|
+
):
|
|
151
161
|
"""Add a red line chart"""
|
|
152
|
-
ax.plot(timestamps, values, "
|
|
153
|
-
|
|
154
|
-
|
|
162
|
+
ax.plot(timestamps, values, color=color, linestyle="-", linewidth=2, label=label)
|
|
163
|
+
if set_label:
|
|
164
|
+
ax.set_ylabel(label, color=color)
|
|
165
|
+
ax.tick_params(axis="y", labelcolor=color)
|
|
155
166
|
|
|
156
167
|
|
|
157
168
|
def requests_per_time(
|
|
@@ -249,7 +260,9 @@ def average_time_per_responses( # pylint: disable=too-many-locals
|
|
|
249
260
|
)
|
|
250
261
|
|
|
251
262
|
fig, ax1 = matplotlib.pyplot.subplots(figsize=(12, 6))
|
|
252
|
-
_add_bar_chart(
|
|
263
|
+
_add_bar_chart(
|
|
264
|
+
ax1, plot_def, timestamps, average_time, "average response time (seconds)"
|
|
265
|
+
)
|
|
253
266
|
|
|
254
267
|
responses_average_length = (
|
|
255
268
|
AnalyzeRequestMetrics.get_responses_average_length_in_period(
|
|
@@ -279,3 +292,138 @@ def average_time_per_responses( # pylint: disable=too-many-locals
|
|
|
279
292
|
matplotlib.pyplot.tight_layout()
|
|
280
293
|
|
|
281
294
|
return fig
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def _collect_emoji_data(
|
|
298
|
+
start_time: datetime.datetime, plot_def: Definition
|
|
299
|
+
) -> Dict[str, Dict[datetime.datetime, int]]:
|
|
300
|
+
"""Collect and organize emoji feedback data
|
|
301
|
+
|
|
302
|
+
Counts all emojis given to logdetective comments created since start_time.
|
|
303
|
+
Collect counts in time accordingly to the plot definition.
|
|
304
|
+
"""
|
|
305
|
+
reactions = Reactions.get_since(start_time)
|
|
306
|
+
reactions_values_dict: Dict[str, Dict] = {}
|
|
307
|
+
for comment_created_at, reaction in reactions:
|
|
308
|
+
comment_created_at_formatted = comment_created_at.strptime(
|
|
309
|
+
comment_created_at.strftime(plot_def.time_format), plot_def.time_format
|
|
310
|
+
)
|
|
311
|
+
if reaction.reaction_type in reactions_values_dict:
|
|
312
|
+
reaction_values_dict = reactions_values_dict[reaction.reaction_type]
|
|
313
|
+
if comment_created_at_formatted in reaction_values_dict:
|
|
314
|
+
reaction_values_dict[comment_created_at_formatted] += reaction.count
|
|
315
|
+
else:
|
|
316
|
+
reaction_values_dict[comment_created_at_formatted] = reaction.count
|
|
317
|
+
else:
|
|
318
|
+
reaction_values_dict = {comment_created_at_formatted: reaction.count}
|
|
319
|
+
reactions_values_dict.update({reaction.reaction_type: reaction_values_dict})
|
|
320
|
+
|
|
321
|
+
return reactions_values_dict
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _plot_emoji_data( # pylint: disable=too-many-locals
|
|
325
|
+
ax: matplotlib.figure.Axes,
|
|
326
|
+
reactions_values_dict: Dict[str, Dict[datetime.datetime, int]],
|
|
327
|
+
plot_def: Definition,
|
|
328
|
+
start_time: datetime.datetime,
|
|
329
|
+
end_time: datetime.datetime,
|
|
330
|
+
):
|
|
331
|
+
"""Plot each emoji's data on its own axis."""
|
|
332
|
+
emoji_lines = {}
|
|
333
|
+
emoji_labels = {}
|
|
334
|
+
|
|
335
|
+
# Find global min and max y values to set consistent scale
|
|
336
|
+
all_counts = []
|
|
337
|
+
for emoji, dict_counts in reactions_values_dict.items():
|
|
338
|
+
timestamps, counts = create_time_series_arrays(
|
|
339
|
+
dict_counts, plot_def, start_time, end_time
|
|
340
|
+
)
|
|
341
|
+
all_counts.extend(counts)
|
|
342
|
+
|
|
343
|
+
colors = [cm.viridis(i) for i in numpy.linspace(0, 1, len(reactions_values_dict))] # pylint: disable=no-member
|
|
344
|
+
|
|
345
|
+
first_emoji = True
|
|
346
|
+
for i, (emoji, dict_counts) in enumerate(reactions_values_dict.items()):
|
|
347
|
+
timestamps, counts = create_time_series_arrays(
|
|
348
|
+
dict_counts, plot_def, start_time, end_time
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
if first_emoji:
|
|
352
|
+
current_ax = ax
|
|
353
|
+
first_emoji = False
|
|
354
|
+
else:
|
|
355
|
+
current_ax = ax.twinx()
|
|
356
|
+
current_ax.spines["right"].set_position(("outward", 60 * (i - 1)))
|
|
357
|
+
|
|
358
|
+
_add_line_chart(current_ax, timestamps, counts, f"{emoji}", colors[i], False)
|
|
359
|
+
emoji_lines[emoji], emoji_labels[emoji] = current_ax.get_legend_handles_labels()
|
|
360
|
+
|
|
361
|
+
# Set the same y-limits for all axes
|
|
362
|
+
current_ax.set_ylim(0, max(all_counts) * 1.1)
|
|
363
|
+
|
|
364
|
+
# Only show y-ticks on the first axis to avoid clutter
|
|
365
|
+
if 0 < i < len(reactions_values_dict):
|
|
366
|
+
current_ax.set_yticks([])
|
|
367
|
+
|
|
368
|
+
return emoji_lines, emoji_labels
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def emojis_per_time(
|
|
372
|
+
period_of_time: models.TimePeriod,
|
|
373
|
+
end_time: Optional[datetime.datetime] = None,
|
|
374
|
+
) -> matplotlib.figure.Figure:
|
|
375
|
+
"""
|
|
376
|
+
Generate a visualization of overall emoji feedback
|
|
377
|
+
over a specified time period.
|
|
378
|
+
|
|
379
|
+
This function creates a multiple-axis plot showing
|
|
380
|
+
a line chart for every found emoji
|
|
381
|
+
|
|
382
|
+
The time intervals are determined by the provided TimePeriod object, which defines
|
|
383
|
+
the granularity and formatting of the time axis.
|
|
384
|
+
|
|
385
|
+
Args:
|
|
386
|
+
period_of_time: A TimePeriod object that defines the time period and interval
|
|
387
|
+
for the analysis (e.g., hourly, daily, weekly)
|
|
388
|
+
end_time: The end time for the analysis period. If None, defaults to the current
|
|
389
|
+
UTC time
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
A matplotlib Figure object containing the generated visualization
|
|
393
|
+
"""
|
|
394
|
+
plot_def = Definition(period_of_time)
|
|
395
|
+
end_time = end_time or datetime.datetime.now(datetime.timezone.utc)
|
|
396
|
+
start_time = period_of_time.get_period_start_time(end_time)
|
|
397
|
+
reactions_values_dict = _collect_emoji_data(start_time, plot_def)
|
|
398
|
+
|
|
399
|
+
fig, ax = matplotlib.pyplot.subplots(figsize=(12, 6))
|
|
400
|
+
|
|
401
|
+
emoji_lines, emoji_labels = _plot_emoji_data(
|
|
402
|
+
ax, reactions_values_dict, plot_def, start_time, end_time
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
matplotlib.pyplot.title(
|
|
406
|
+
f"Emoji feedback ({start_time.strftime(plot_def.time_format)} "
|
|
407
|
+
f"to {end_time.strftime(plot_def.time_format)})"
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
all_lines = []
|
|
411
|
+
for lines in emoji_lines.values():
|
|
412
|
+
all_lines.extend(lines)
|
|
413
|
+
all_labels = []
|
|
414
|
+
for labels in emoji_labels.values():
|
|
415
|
+
all_labels.extend(labels)
|
|
416
|
+
|
|
417
|
+
ax.legend(all_lines, all_labels, loc="upper left")
|
|
418
|
+
ax.set_xlabel("Time")
|
|
419
|
+
ax.set_ylabel("Count")
|
|
420
|
+
|
|
421
|
+
# Format x-axis
|
|
422
|
+
ax.xaxis.set_major_formatter(matplotlib.dates.DateFormatter(plot_def.time_format))
|
|
423
|
+
ax.xaxis.set_major_locator(plot_def.locator)
|
|
424
|
+
ax.tick_params(axis="x", labelrotation=45)
|
|
425
|
+
ax.grid(True, alpha=0.3)
|
|
426
|
+
|
|
427
|
+
matplotlib.pyplot.tight_layout()
|
|
428
|
+
|
|
429
|
+
return fig
|
logdetective/server/server.py
CHANGED
|
@@ -158,8 +158,8 @@ async def analyze_log(
|
|
|
158
158
|
return Response(explanation=response, response_certainty=certainty)
|
|
159
159
|
|
|
160
160
|
|
|
161
|
-
@track_request()
|
|
162
161
|
@app.post("/analyze/staged", response_model=StagedResponse)
|
|
162
|
+
@track_request()
|
|
163
163
|
async def analyze_log_staged(
|
|
164
164
|
build_log: BuildLog, http_session: aiohttp.ClientSession = Depends(get_http_session)
|
|
165
165
|
):
|
|
@@ -420,6 +420,7 @@ class Plot(str, Enum):
|
|
|
420
420
|
|
|
421
421
|
REQUESTS = "requests"
|
|
422
422
|
RESPONSES = "responses"
|
|
423
|
+
EMOJIS = "emojis"
|
|
423
424
|
BOTH = ""
|
|
424
425
|
|
|
425
426
|
|
|
@@ -450,12 +451,16 @@ async def get_metrics(
|
|
|
450
451
|
period_since_now, endpoint_type
|
|
451
452
|
)
|
|
452
453
|
return _svg_figure_response(fig)
|
|
454
|
+
if plot == Plot.EMOJIS:
|
|
455
|
+
fig = plot_engine.emojis_per_time(period_since_now)
|
|
456
|
+
return _svg_figure_response(fig)
|
|
453
457
|
# BOTH
|
|
454
458
|
fig_requests = plot_engine.requests_per_time(period_since_now, endpoint_type)
|
|
455
459
|
fig_responses = plot_engine.average_time_per_responses(
|
|
456
460
|
period_since_now, endpoint_type
|
|
457
461
|
)
|
|
458
|
-
|
|
462
|
+
fig_emojis = plot_engine.emojis_per_time(period_since_now)
|
|
463
|
+
return _multiple_svg_figures_response([fig_requests, fig_responses, fig_emojis])
|
|
459
464
|
|
|
460
465
|
descriptions = {
|
|
461
466
|
Plot.REQUESTS: (
|
|
@@ -466,6 +471,10 @@ async def get_metrics(
|
|
|
466
471
|
"Show statistics for responses given in the specified period of time "
|
|
467
472
|
f"for the /{endpoint_type.value} API endpoint."
|
|
468
473
|
),
|
|
474
|
+
Plot.EMOJIS: (
|
|
475
|
+
"Show statistics for emoji feedback in the specified period of time "
|
|
476
|
+
f"for the /{endpoint_type.value} API endpoint."
|
|
477
|
+
),
|
|
469
478
|
Plot.BOTH: (
|
|
470
479
|
"Show statistics for requests and responses in the given period of time "
|
|
471
480
|
f"for the /{endpoint_type.value} API endpoint."
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: logdetective
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.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
|
|
@@ -48,14 +48,16 @@ Log Detective
|
|
|
48
48
|
|
|
49
49
|
[PyPI Releases]: https://pypi.org/project/logdetective/#history
|
|
50
50
|
|
|
51
|
-
A
|
|
51
|
+
A tool, service and RHEL process integration to analyze logs using a Large Language Model (LLM) and a [Drain template miner](https://github.com/logpai/Drain3).
|
|
52
|
+
|
|
53
|
+
The service that explains logs is available here: https://logdetective.com/explain
|
|
52
54
|
|
|
53
55
|
Note: if you are looking for code of website logdetective.com it is in [github.com/fedora-copr/logdetective-website](https://github.com/fedora-copr/logdetective-website).
|
|
54
56
|
|
|
55
57
|
Installation
|
|
56
58
|
------------
|
|
57
59
|
|
|
58
|
-
**Fedora
|
|
60
|
+
**Fedora 41+**
|
|
59
61
|
|
|
60
62
|
dnf install logdetective
|
|
61
63
|
|
|
@@ -70,11 +72,12 @@ First, ensure that the necessary dependencies for the `llama-cpp-python` project
|
|
|
70
72
|
|
|
71
73
|
Then, install the `logdetective` project using pip:
|
|
72
74
|
|
|
73
|
-
# then install logdetective project
|
|
74
75
|
pip install logdetective
|
|
75
76
|
|
|
76
77
|
**Local repository install**
|
|
77
78
|
|
|
79
|
+
Clone this repository and install with pip:
|
|
80
|
+
|
|
78
81
|
pip install .
|
|
79
82
|
|
|
80
83
|
Usage
|
|
@@ -111,14 +114,14 @@ Example of altered prompts:
|
|
|
111
114
|
logdetective https://kojipkgs.fedoraproject.org//work/tasks/3367/131313367/build.log --prompts ~/my-prompts.yml
|
|
112
115
|
|
|
113
116
|
|
|
114
|
-
Note that streaming with some models (notably Meta-Llama-3 is broken) is broken and can be
|
|
117
|
+
Note that streaming with some models (notably Meta-Llama-3 is broken) is broken and can be worked around by `no-stream` option:
|
|
115
118
|
|
|
116
119
|
logdetective https://example.com/logs.txt --model QuantFactory/Meta-Llama-3-8B-Instruct-GGUF --no-stream
|
|
117
120
|
|
|
118
121
|
|
|
119
122
|
Real Example
|
|
120
123
|
------------
|
|
121
|
-
Let's have a look at a real world example. Log Detective can work with any logs though we optimize it for build logs.
|
|
124
|
+
Let's have a look at a real world example. Log Detective can work with any logs though we optimize it for RPM build logs.
|
|
122
125
|
|
|
123
126
|
We're going to analyze a failed build of a python-based library that happened in Fedora Koji buildsystem:
|
|
124
127
|
```
|
|
@@ -184,8 +187,13 @@ Contributing
|
|
|
184
187
|
------------
|
|
185
188
|
|
|
186
189
|
Contributions are welcome! Please submit a pull request if you have any improvements or new features to add. Make sure your changes pass all existing tests before submitting.
|
|
190
|
+
For bigger code changes, please consult us first by creating an issue.
|
|
191
|
+
|
|
192
|
+
We are always looking for more annotated snippets that will increase the quality of Log Detective's results. The contributions happen in our website: https://logdetective.com/
|
|
193
|
+
|
|
194
|
+
Log Detective performs several inference queries while evaluating a log file. Prompts are stored in a separate file (more info below: https://github.com/fedora-copr/logdetective?tab=readme-ov-file#system-prompts). If you have an idea for improvements to our prompts, please open a PR and we'd happy to test it out.
|
|
187
195
|
|
|
188
|
-
To develop
|
|
196
|
+
To develop Log Detective, you should fork this repository, clone your fork, and install dependencies using pip:
|
|
189
197
|
|
|
190
198
|
git clone https://github.com/yourusername/logdetective.git
|
|
191
199
|
cd logdetective
|
|
@@ -358,7 +366,7 @@ using either a browser, the `curl` or the `http` command (provided by the `httpi
|
|
|
358
366
|
|
|
359
367
|
When no time period is specified, the query defaults to the last 2 days:
|
|
360
368
|
|
|
361
|
-
You can view requests and
|
|
369
|
+
You can view requests, responses and emojis statistics
|
|
362
370
|
- for the `/analyze` endpoint at http://localhost:8080/metrics/analyze
|
|
363
371
|
- for the `/analyze-staged` endpoint at http://localhost:8080/metrics/analyze-staged.
|
|
364
372
|
- for the requests coming from gitlab: http://localhost:8080/metrics/analyze-gitlab.
|
|
@@ -370,6 +378,7 @@ You can retrieve single svg images at the following endpoints:
|
|
|
370
378
|
- http://localhost:8080/metrics/analyze-staged/responses
|
|
371
379
|
- http://localhost:8080/metrics/analyze-gitlab/requests
|
|
372
380
|
- http://localhost:8080/metrics/analyze-gitlab/responses
|
|
381
|
+
- http://localhost:8080/metrics/analyze-gitlab/emojis
|
|
373
382
|
|
|
374
383
|
Examples:
|
|
375
384
|
|
|
@@ -12,20 +12,20 @@ logdetective/server/config.py,sha256=S2kuvzEo801Kq0vJpRr2fVxSqPghg9kW1L0Ml2yH8Zk
|
|
|
12
12
|
logdetective/server/database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
logdetective/server/database/base.py,sha256=1mcjEbhwLl4RalvT3oy6XVctjJoWIW3H9aI_sMWJBK8,1728
|
|
14
14
|
logdetective/server/database/models/__init__.py,sha256=xy2hkygyw6_87zPKkG20i7g7_LXTGR__PUeojhbvv94,496
|
|
15
|
-
logdetective/server/database/models/merge_request_jobs.py,sha256=
|
|
15
|
+
logdetective/server/database/models/merge_request_jobs.py,sha256=hw88wV1-3x7i53sX7ZotKClc6OsH1njPpbRSZofnqr4,18670
|
|
16
16
|
logdetective/server/database/models/metrics.py,sha256=yl9fS4IPVFWDeFvPAxO6zOVu6oLF319ApvVLAgnD5yU,13928
|
|
17
17
|
logdetective/server/emoji.py,sha256=g9GtMChwznD8g1xonsh-I_3xqRn6LBeg3sjPJWcI0Yg,3333
|
|
18
18
|
logdetective/server/gitlab.py,sha256=fpJp28YsvHvm4DjrvzrgamLk31Fo5UyvT6GNWway9KM,15227
|
|
19
|
-
logdetective/server/llm.py,sha256=
|
|
20
|
-
logdetective/server/metric.py,sha256=
|
|
19
|
+
logdetective/server/llm.py,sha256=JtSCZj8SLnoyTCUdhA0TwcsMZfmHFFru2bJ9txI3GuU,8727
|
|
20
|
+
logdetective/server/metric.py,sha256=B3ew_qSmtEMj6xl-FoOtS4F_bkplp-shhtfHF1cG_Io,4010
|
|
21
21
|
logdetective/server/models.py,sha256=eNEB3WJWeZ9Pe6qsmTKQwAE8wu8u51OwLILzV9__YJM,11248
|
|
22
|
-
logdetective/server/plot.py,sha256=
|
|
23
|
-
logdetective/server/server.py,sha256=
|
|
22
|
+
logdetective/server/plot.py,sha256=eZs4r9gua-nW3yymSMIz1leL9mb4QKlh6FJZSeOfZ5M,14872
|
|
23
|
+
logdetective/server/server.py,sha256=9shFgRkWcJVM2L7HHoQBMCfKuJamh2L4tC96duFPEOA,18127
|
|
24
24
|
logdetective/server/templates/gitlab_full_comment.md.j2,sha256=DQZ2WVFedpuXI6znbHIW4wpF9BmFS8FaUkowh8AnGhE,1627
|
|
25
25
|
logdetective/server/templates/gitlab_short_comment.md.j2,sha256=fzScpayv2vpRLczP_0O0YxtA8rsKvR6gSv4ntNdWb98,1443
|
|
26
26
|
logdetective/utils.py,sha256=hdExAC8FtDIxvdgIq-Ro6LVM-JZ-k_UofaMzaDAHvzM,6088
|
|
27
|
-
logdetective-0.
|
|
28
|
-
logdetective-0.
|
|
29
|
-
logdetective-0.
|
|
30
|
-
logdetective-0.
|
|
31
|
-
logdetective-0.
|
|
27
|
+
logdetective-0.11.2.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
28
|
+
logdetective-0.11.2.dist-info/METADATA,sha256=BVRCWRVzlm-Aa0b51d0ZCbKz2ty1htTzHzX9XGiXALI,17137
|
|
29
|
+
logdetective-0.11.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
30
|
+
logdetective-0.11.2.dist-info/entry_points.txt,sha256=3K_vXja6PmcA8sNdUi63WdImeiNhVZcEGPTaoJmltfA,63
|
|
31
|
+
logdetective-0.11.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|