Spaces:
Build error
Build error
Commit ·
6a51c39
1
Parent(s): 257873b
Fixing the build AND the perf logs
Browse files- headroom/perf/analyzer.py +26 -2
- headroom/proxy/server.py +19 -3
headroom/perf/analyzer.py
CHANGED
|
@@ -39,8 +39,18 @@ _TOIN_RE = re.compile(
|
|
| 39 |
|
| 40 |
|
| 41 |
def _parse_kv(kv_str: str) -> dict[str, str]:
|
| 42 |
-
"""Parse key=value pairs from a PERF log line.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
result: dict[str, str] = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
for part in kv_str.split():
|
| 45 |
if "=" in part:
|
| 46 |
k, v = part.split("=", 1)
|
|
@@ -143,7 +153,21 @@ def parse_log_files(last_n_hours: float = 168.0) -> PerfReport:
|
|
| 143 |
if m:
|
| 144 |
kv = _parse_kv(m.group("kv"))
|
| 145 |
transforms_str = kv.get("transforms", "none")
|
| 146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
report.perf_records.append(
|
| 148 |
PerfRecord(
|
| 149 |
timestamp=m.group("ts"),
|
|
|
|
| 39 |
|
| 40 |
|
| 41 |
def _parse_kv(kv_str: str) -> dict[str, str]:
|
| 42 |
+
"""Parse key=value pairs from a PERF log line.
|
| 43 |
+
|
| 44 |
+
The ``transforms=`` field is always last and its value may contain spaces
|
| 45 |
+
(e.g. ``transforms=router:excluded:tool*32 read_lifecycle:stale*17``).
|
| 46 |
+
Everything after ``transforms=`` is captured as a single value.
|
| 47 |
+
"""
|
| 48 |
result: dict[str, str] = {}
|
| 49 |
+
# Handle transforms= specially since its value contains spaces
|
| 50 |
+
if "transforms=" in kv_str:
|
| 51 |
+
before, transforms_val = kv_str.split("transforms=", 1)
|
| 52 |
+
result["transforms"] = transforms_val.strip()
|
| 53 |
+
kv_str = before
|
| 54 |
for part in kv_str.split():
|
| 55 |
if "=" in part:
|
| 56 |
k, v = part.split("=", 1)
|
|
|
|
| 153 |
if m:
|
| 154 |
kv = _parse_kv(m.group("kv"))
|
| 155 |
transforms_str = kv.get("transforms", "none")
|
| 156 |
+
# Handle both old comma-separated and new space-separated *N format
|
| 157 |
+
if transforms_str == "none":
|
| 158 |
+
transforms: list[str] = []
|
| 159 |
+
elif "*" in transforms_str or " " in transforms_str:
|
| 160 |
+
# New format: "router:excluded:tool*32 read_lifecycle:stale*17"
|
| 161 |
+
transforms = []
|
| 162 |
+
for part in transforms_str.split():
|
| 163 |
+
if "*" in part:
|
| 164 |
+
name, _ = part.rsplit("*", 1)
|
| 165 |
+
else:
|
| 166 |
+
name = part
|
| 167 |
+
transforms.append(name)
|
| 168 |
+
else:
|
| 169 |
+
# Old comma-separated format
|
| 170 |
+
transforms = transforms_str.split(",")
|
| 171 |
report.perf_records.append(
|
| 172 |
PerfRecord(
|
| 173 |
timestamp=m.group("ts"),
|
headroom/proxy/server.py
CHANGED
|
@@ -175,6 +175,22 @@ def _setup_file_logging() -> None:
|
|
| 175 |
|
| 176 |
_setup_file_logging()
|
| 177 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
# Maximum request body size (100MB - increased to support image-heavy requests)
|
| 179 |
MAX_REQUEST_BODY_SIZE = 100 * 1024 * 1024
|
| 180 |
|
|
@@ -2286,7 +2302,7 @@ class HeadroomProxy:
|
|
| 2286 |
f"tok_saved={tokens_saved} "
|
| 2287 |
f"cache_read={cr} cache_write={cw} cache_hit_pct={chp} "
|
| 2288 |
f"opt_ms={optimization_latency:.0f} "
|
| 2289 |
-
f"transforms={
|
| 2290 |
)
|
| 2291 |
|
| 2292 |
# Remove compression headers since httpx already decompressed the response
|
|
@@ -3909,7 +3925,7 @@ class HeadroomProxy:
|
|
| 3909 |
f"cache_read={cache_read_tokens} cache_write={cache_write_tokens} "
|
| 3910 |
f"cache_hit_pct={cache_hit_pct} "
|
| 3911 |
f"opt_ms={optimization_latency:.0f} "
|
| 3912 |
-
f"transforms={
|
| 3913 |
)
|
| 3914 |
|
| 3915 |
# Normalize input tokens based on provider semantics:
|
|
@@ -4098,7 +4114,7 @@ class HeadroomProxy:
|
|
| 4098 |
f"tok_saved={tokens_saved} "
|
| 4099 |
f"cache_read=0 cache_write=0 cache_hit_pct=0 "
|
| 4100 |
f"opt_ms={optimization_latency:.0f} "
|
| 4101 |
-
f"transforms={
|
| 4102 |
)
|
| 4103 |
|
| 4104 |
return StreamingResponse(
|
|
|
|
| 175 |
|
| 176 |
_setup_file_logging()
|
| 177 |
|
| 178 |
+
|
| 179 |
+
def _summarize_transforms(transforms: list[str]) -> str:
|
| 180 |
+
"""Collapse repeated transforms into counted summary.
|
| 181 |
+
|
| 182 |
+
e.g. ['router:excluded:tool', 'router:excluded:tool', 'read_lifecycle:stale']
|
| 183 |
+
→ 'router:excluded:tool*2 read_lifecycle:stale'
|
| 184 |
+
"""
|
| 185 |
+
if not transforms:
|
| 186 |
+
return "none"
|
| 187 |
+
counts: dict[str, int] = {}
|
| 188 |
+
for t in transforms:
|
| 189 |
+
counts[t] = counts.get(t, 0) + 1
|
| 190 |
+
parts = [f"{k}*{v}" if v > 1 else k for k, v in counts.items()]
|
| 191 |
+
return " ".join(parts)
|
| 192 |
+
|
| 193 |
+
|
| 194 |
# Maximum request body size (100MB - increased to support image-heavy requests)
|
| 195 |
MAX_REQUEST_BODY_SIZE = 100 * 1024 * 1024
|
| 196 |
|
|
|
|
| 2302 |
f"tok_saved={tokens_saved} "
|
| 2303 |
f"cache_read={cr} cache_write={cw} cache_hit_pct={chp} "
|
| 2304 |
f"opt_ms={optimization_latency:.0f} "
|
| 2305 |
+
f"transforms={_summarize_transforms(transforms_applied)}"
|
| 2306 |
)
|
| 2307 |
|
| 2308 |
# Remove compression headers since httpx already decompressed the response
|
|
|
|
| 3925 |
f"cache_read={cache_read_tokens} cache_write={cache_write_tokens} "
|
| 3926 |
f"cache_hit_pct={cache_hit_pct} "
|
| 3927 |
f"opt_ms={optimization_latency:.0f} "
|
| 3928 |
+
f"transforms={_summarize_transforms(transforms_applied)}"
|
| 3929 |
)
|
| 3930 |
|
| 3931 |
# Normalize input tokens based on provider semantics:
|
|
|
|
| 4114 |
f"tok_saved={tokens_saved} "
|
| 4115 |
f"cache_read=0 cache_write=0 cache_hit_pct=0 "
|
| 4116 |
f"opt_ms={optimization_latency:.0f} "
|
| 4117 |
+
f"transforms={_summarize_transforms(transforms_applied)}"
|
| 4118 |
)
|
| 4119 |
|
| 4120 |
return StreamingResponse(
|