unique_toolkit 2026.24.0.dev6__py3-none-any.whl → 2026.24.0.dev7__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.
- unique_toolkit/monitoring/gunicorn.py +24 -0
- unique_toolkit/monitoring/middleware.py +4 -3
- unique_toolkit/monitoring/registry.py +58 -4
- {unique_toolkit-2026.24.0.dev6.dist-info → unique_toolkit-2026.24.0.dev7.dist-info}/METADATA +1 -1
- {unique_toolkit-2026.24.0.dev6.dist-info → unique_toolkit-2026.24.0.dev7.dist-info}/RECORD +6 -5
- {unique_toolkit-2026.24.0.dev6.dist-info → unique_toolkit-2026.24.0.dev7.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def child_exit(server, worker) -> None:
|
|
7
|
+
"""Gunicorn child_exit hook: clean up dead worker's per-PID metric files.
|
|
8
|
+
|
|
9
|
+
Wire this up in gunicorn.conf.py:
|
|
10
|
+
|
|
11
|
+
from unique_toolkit.monitoring.gunicorn import child_exit # noqa: F401
|
|
12
|
+
|
|
13
|
+
Gunicorn calls any function named child_exit(server, worker) in the
|
|
14
|
+
config file whenever a worker process exits. Without this, dead workers'
|
|
15
|
+
.db files accumulate in PROMETHEUS_MULTIPROC_DIR and inflate aggregated
|
|
16
|
+
metric values on every future scrape.
|
|
17
|
+
|
|
18
|
+
No-ops when PROMETHEUS_MULTIPROC_DIR is not set (single-process mode).
|
|
19
|
+
"""
|
|
20
|
+
if not os.environ.get("PROMETHEUS_MULTIPROC_DIR"):
|
|
21
|
+
return
|
|
22
|
+
from prometheus_client import multiprocess # pyright: ignore[reportMissingImports]
|
|
23
|
+
|
|
24
|
+
multiprocess.mark_process_dead(worker.pid)
|
|
@@ -11,22 +11,23 @@ from prometheus_client import ( # pyright: ignore[reportMissingImports]
|
|
|
11
11
|
from .registry import REGISTRY
|
|
12
12
|
|
|
13
13
|
_http_requests_total = Counter(
|
|
14
|
-
"
|
|
14
|
+
"python_http_requests_total",
|
|
15
15
|
"Total HTTP requests",
|
|
16
16
|
["method", "path", "status_code"],
|
|
17
17
|
registry=REGISTRY,
|
|
18
18
|
)
|
|
19
19
|
_http_request_duration_seconds = Histogram(
|
|
20
|
-
"
|
|
20
|
+
"python_http_request_duration_seconds",
|
|
21
21
|
"HTTP request duration",
|
|
22
22
|
["method", "path"],
|
|
23
23
|
registry=REGISTRY,
|
|
24
24
|
)
|
|
25
25
|
_http_requests_in_progress = Gauge(
|
|
26
|
-
"
|
|
26
|
+
"python_http_requests_in_progress",
|
|
27
27
|
"In-flight HTTP requests",
|
|
28
28
|
["method"],
|
|
29
29
|
registry=REGISTRY,
|
|
30
|
+
multiprocess_mode="livesum",
|
|
30
31
|
)
|
|
31
32
|
|
|
32
33
|
_DEFAULT_EXCLUDED_PATHS = frozenset({"/health", "/metrics", "/"})
|
|
@@ -2,9 +2,11 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import functools
|
|
5
|
+
import os
|
|
5
6
|
import time
|
|
7
|
+
import warnings
|
|
6
8
|
from contextlib import contextmanager
|
|
7
|
-
from typing import Any, Callable
|
|
9
|
+
from typing import Any, Callable, Literal
|
|
8
10
|
|
|
9
11
|
from prometheus_client import ( # pyright: ignore[reportMissingImports]
|
|
10
12
|
CollectorRegistry,
|
|
@@ -18,7 +20,36 @@ REGISTRY = CollectorRegistry()
|
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
def get_metrics() -> bytes:
|
|
21
|
-
"""Returns all registered metrics in Prometheus text format.
|
|
23
|
+
"""Returns all registered metrics in Prometheus text format.
|
|
24
|
+
|
|
25
|
+
In multiprocess mode (PROMETHEUS_MULTIPROC_DIR is set), aggregates
|
|
26
|
+
per-PID .db files from all Gunicorn workers via MultiProcessCollector.
|
|
27
|
+
In single-process mode, returns metrics from the in-process REGISTRY.
|
|
28
|
+
|
|
29
|
+
NOTE: PROMETHEUS_MULTIPROC_DIR must be set in the shell/entrypoint
|
|
30
|
+
*before* Python starts. Setting it from Python after prometheus_client
|
|
31
|
+
has been imported has no effect — metrics will silently bypass per-PID
|
|
32
|
+
files and this function will emit a warning.
|
|
33
|
+
"""
|
|
34
|
+
if os.environ.get("PROMETHEUS_MULTIPROC_DIR"):
|
|
35
|
+
from prometheus_client import ( # pyright: ignore[reportMissingImports]
|
|
36
|
+
multiprocess,
|
|
37
|
+
values,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if not getattr(values.ValueClass, "_multiprocess", False):
|
|
41
|
+
warnings.warn(
|
|
42
|
+
"PROMETHEUS_MULTIPROC_DIR is set but prometheus_client was imported "
|
|
43
|
+
"before the env var — metrics will silently bypass per-PID files. "
|
|
44
|
+
"Set PROMETHEUS_MULTIPROC_DIR in the shell/entrypoint before launching Python.",
|
|
45
|
+
RuntimeWarning,
|
|
46
|
+
stacklevel=2,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
registry = CollectorRegistry()
|
|
50
|
+
multiprocess.MultiProcessCollector(registry)
|
|
51
|
+
return generate_latest(registry)
|
|
52
|
+
|
|
22
53
|
return generate_latest(REGISTRY)
|
|
23
54
|
|
|
24
55
|
|
|
@@ -43,8 +74,31 @@ class MetricNamespace:
|
|
|
43
74
|
def counter(self, name: str, description: str, labels: list[str]) -> Counter:
|
|
44
75
|
return Counter(f"{self._prefix}_{name}", description, labels, registry=REGISTRY)
|
|
45
76
|
|
|
46
|
-
def gauge(
|
|
47
|
-
|
|
77
|
+
def gauge(
|
|
78
|
+
self,
|
|
79
|
+
name: str,
|
|
80
|
+
description: str,
|
|
81
|
+
labels: list[str],
|
|
82
|
+
multiprocess_mode: Literal[
|
|
83
|
+
"all",
|
|
84
|
+
"liveall",
|
|
85
|
+
"min",
|
|
86
|
+
"livemin",
|
|
87
|
+
"max",
|
|
88
|
+
"livemax",
|
|
89
|
+
"sum",
|
|
90
|
+
"livesum",
|
|
91
|
+
"mostrecent",
|
|
92
|
+
"livemostrecent",
|
|
93
|
+
] = "all",
|
|
94
|
+
) -> Gauge:
|
|
95
|
+
return Gauge(
|
|
96
|
+
f"{self._prefix}_{name}",
|
|
97
|
+
description,
|
|
98
|
+
labels,
|
|
99
|
+
registry=REGISTRY,
|
|
100
|
+
multiprocess_mode=multiprocess_mode,
|
|
101
|
+
)
|
|
48
102
|
|
|
49
103
|
|
|
50
104
|
@contextmanager
|
{unique_toolkit-2026.24.0.dev6.dist-info → unique_toolkit-2026.24.0.dev7.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: unique-toolkit
|
|
3
|
-
Version: 2026.24.0.
|
|
3
|
+
Version: 2026.24.0.dev7
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Cedric Klinkert, Martin Fadler, Sadique Sheik, Fabian Schläpfer, Pascal Hauri, Ejiroghene Oki-Peter, Rami Azouz, Ricardo Visini, Sanzio Monti
|
|
6
6
|
Author-email: Cedric Klinkert <cedric.klinkert@unique.ch>, Martin Fadler <martin.fadler@unique.ch>, Sadique Sheik <sadique@unique.ch>, Fabian Schläpfer <fabian@unique.ch>, Pascal Hauri <pascal@unique.ch>, Ejiroghene Oki-Peter <oki-peter.ejiroghene@unique.ai>, Rami Azouz <rami.ext@unique.ch>, Ricardo Visini <ricardo.visini@unique.ch>, Sanzio Monti <sanzio.monti@unique.ai>
|
|
@@ -376,8 +376,9 @@ unique_toolkit/language_model/settings.py,sha256=xkoMv3ATb2ft5V3MA8JvqW-EO8AnZ5t
|
|
|
376
376
|
unique_toolkit/language_model/stream_transform.py,sha256=iNNGpLY_7G6Bpp9Y4KtbIkQm3WB4yZt75BXcqx0DVkI,2775
|
|
377
377
|
unique_toolkit/language_model/utils.py,sha256=bPQ4l6_YO71w-zaIPanUUmtbXC1_hCvLK0tAFc3VCRc,1902
|
|
378
378
|
unique_toolkit/monitoring/__init__.py,sha256=z1gkOCLxGcg936MFuLSeWFa0tREcBFSUbV09NxilBw4,1065
|
|
379
|
-
unique_toolkit/monitoring/
|
|
380
|
-
unique_toolkit/monitoring/
|
|
379
|
+
unique_toolkit/monitoring/gunicorn.py,sha256=urTFFS5zYHKoxb1TEQHcLqb2byi-aYgFRHGwgYRzYYI,844
|
|
380
|
+
unique_toolkit/monitoring/middleware.py,sha256=JNh5v_INAkexuLtXhCG9n5n2t6Pnf8ZdPTnimAVS0_s,2931
|
|
381
|
+
unique_toolkit/monitoring/registry.py,sha256=8r9jqaQoRHZveGRZe_GQvBzvQpmUV4cpw8Ej1ssDIdE,5056
|
|
381
382
|
unique_toolkit/protocols/support.py,sha256=x5edzbBmKAUuvm3oLFh52gkgQsqR5LU7M0t8RHK0DZk,5161
|
|
382
383
|
unique_toolkit/services/__init__.py,sha256=b9Wl7Wvz1JmZDbDexIZzBoXZ0wscXuczGAH8LoL-Qak,572
|
|
383
384
|
unique_toolkit/services/chat_service.py,sha256=FPY-GTFAkD0C9pYfPfkfAvyIKnL9DX9iDk75gDeghCc,95209
|
|
@@ -391,6 +392,6 @@ unique_toolkit/short_term_memory/service.py,sha256=Ncw0_0CNM6kmzJSLVJGG5GcKpBduI
|
|
|
391
392
|
unique_toolkit/smart_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
392
393
|
unique_toolkit/smart_rules/compile.py,sha256=vRe0aMJWuVIK-VsqomgxQImzke7MDmwgSxxJfYCa7zw,1386
|
|
393
394
|
unique_toolkit/test_utilities/events.py,sha256=FLrhMh0A_mQutAUjoD0FdMcVQ4Gq31r35OVGzRjvLro,6760
|
|
394
|
-
unique_toolkit-2026.24.0.
|
|
395
|
-
unique_toolkit-2026.24.0.
|
|
396
|
-
unique_toolkit-2026.24.0.
|
|
395
|
+
unique_toolkit-2026.24.0.dev7.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
396
|
+
unique_toolkit-2026.24.0.dev7.dist-info/METADATA,sha256=ThX3w1DhE99W9OR-nA3Kmd-hNVmqxdGU2xtsV9mUvqg,7392
|
|
397
|
+
unique_toolkit-2026.24.0.dev7.dist-info/RECORD,,
|
|
File without changes
|