mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-11-09 23:47:43 +01:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3accef6173 |
No files matched your search
+14
-9
@@ -129,8 +129,9 @@ The top level includes `deployment_id`, `start_time`, `end_time`, and
|
||||
point bounds. The CLI resolves omitted bounds once: end defaults to now (UTC),
|
||||
and start defaults to 15 minutes before the resolved end. With only `--end-time`,
|
||||
start is 15 minutes before that value; with only `--start-time`, end is now. Both
|
||||
values are sent explicitly to the backend and output, even when there are no points. `step_seconds` is the most common gap between consecutive
|
||||
timestamps within series, with ties broken by the smallest gap. Whole seconds
|
||||
values are sent explicitly to the backend and included in default compact output,
|
||||
even when there are no points. `step_seconds` is the most common gap between
|
||||
consecutive timestamps within series, with ties broken by the smallest gap. Whole seconds
|
||||
are integers; fractional seconds are preserved. It is `null` only when every
|
||||
series has fewer than two timestamps. UI metadata and repeated series IDs are
|
||||
omitted. The host-backend response itself is unchanged.
|
||||
@@ -140,16 +141,20 @@ langgraph deploy metrics DEPLOYMENT_ID --summary
|
||||
langgraph deploy metrics DEPLOYMENT_ID --raw
|
||||
```
|
||||
|
||||
`--summary` returns the same shape without `points`. `--raw` returns the original
|
||||
UI chart schema without reshaping or rounding. These flags are mutually exclusive.
|
||||
Neither mode returns traces.
|
||||
`--summary` requests server-side aggregation with `summary=true` and prints the
|
||||
response without reshaping or rounding. It includes `deployment_id`,
|
||||
`step_seconds`, and `sections`, but no `start_time`, `end_time`, or `points`.
|
||||
Series contain `metric`, `unit`, `latest`, `min`, `max`, and `avg`; units include
|
||||
`percent`, `count`, `milliseconds`, and `responses_per_second`.
|
||||
`--raw` returns the original UI chart schema without reshaping or rounding.
|
||||
These flags are mutually exclusive. Neither mode returns traces.
|
||||
|
||||
Size comparison using the realistic synthetic unit-test fixture (compact JSON
|
||||
serialization for all modes; not a live deployment benchmark):
|
||||
serialization; not a live deployment benchmark):
|
||||
|
||||
| Before: raw UI schema | After: default | After: summary |
|
||||
| --- | --- | --- |
|
||||
| 4,302 bytes | 1,226 bytes (72% smaller) | 806 bytes (81% smaller) |
|
||||
| Before: raw UI schema | After: default |
|
||||
| --- | --- |
|
||||
| 4,302 bytes | 1,226 bytes (72% smaller) |
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
@@ -2694,19 +2694,20 @@ def deploy_metrics(
|
||||
}.items()
|
||||
if value is not None
|
||||
}
|
||||
if summary:
|
||||
params["summary"] = "true"
|
||||
client = _create_host_backend_client(host_url, api_key, no_input=True)
|
||||
result = _call_host_backend_with_optional_tenant(
|
||||
client,
|
||||
lambda c: c.get_deployment_metrics(str(deployment_id), params),
|
||||
no_input=True,
|
||||
)
|
||||
if not raw:
|
||||
if not raw and not summary:
|
||||
result = compact_deployment_metrics(
|
||||
result,
|
||||
str(deployment_id),
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
summary=summary,
|
||||
)
|
||||
click.echo(json_mod.dumps(result, separators=(",", ":")))
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ def compact_deployment_metrics(
|
||||
*,
|
||||
start_time: str,
|
||||
end_time: str,
|
||||
summary: bool = False,
|
||||
) -> dict[str, Any]:
|
||||
"""Reshape UI charts without mutating the response; step is in seconds."""
|
||||
sections = {}
|
||||
@@ -74,14 +73,13 @@ def compact_deployment_metrics(
|
||||
"max": round(max(values), 3) if values else None,
|
||||
"avg": round(fmean(values), 3) if values else None,
|
||||
}
|
||||
if not summary:
|
||||
stats["points"] = [
|
||||
[
|
||||
_iso(timestamp),
|
||||
round(value, 3) if value is not None else None,
|
||||
]
|
||||
for timestamp, value in points
|
||||
stats["points"] = [
|
||||
[
|
||||
_iso(timestamp),
|
||||
round(value, 3) if value is not None else None,
|
||||
]
|
||||
for timestamp, value in points
|
||||
]
|
||||
series_output[series["name"]] = stats
|
||||
charts[chart["title"]] = series_output or {"_empty": True}
|
||||
sections[section["title"]] = charts or {"_empty": True}
|
||||
|
||||
@@ -34,6 +34,30 @@ def invoke(monkeypatch, args, handler, input=None):
|
||||
@pytest.mark.parametrize("mode", [None, "--summary", "--raw"])
|
||||
def test_metrics(monkeypatch, bounds, mode, deployment_metrics_response):
|
||||
charts = deployment_metrics_response
|
||||
server_summary = {
|
||||
"deployment_id": DEPLOYMENT_ID,
|
||||
"step_seconds": 60,
|
||||
"sections": {
|
||||
"API": {
|
||||
"Metrics": {
|
||||
metric: {
|
||||
"metric": metric,
|
||||
"unit": unit,
|
||||
"latest": 1.234567,
|
||||
"min": 1.234567,
|
||||
"max": 1.234567,
|
||||
"avg": 1.234567,
|
||||
}
|
||||
for metric, unit in (
|
||||
("cpu_usage", "percent"),
|
||||
("replica_count", "count"),
|
||||
("p95_latency", "milliseconds"),
|
||||
("responses_per_second", "responses_per_second"),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
class FrozenDatetime(datetime):
|
||||
@classmethod
|
||||
@@ -57,6 +81,8 @@ def test_metrics(monkeypatch, bounds, mode, deployment_metrics_response):
|
||||
"end_time": "2026-10-28T23:02:33+00:00",
|
||||
**options,
|
||||
}
|
||||
if mode == "--summary":
|
||||
params["summary"] = "true"
|
||||
|
||||
def handler(request):
|
||||
assert request.method == "GET"
|
||||
@@ -64,7 +90,9 @@ def test_metrics(monkeypatch, bounds, mode, deployment_metrics_response):
|
||||
assert dict(request.url.params) == params
|
||||
assert request.headers["x-tenant-id"] == "workspace-id"
|
||||
assert request.headers["x-api-key"] == "key"
|
||||
return httpx.Response(200, json=charts)
|
||||
return httpx.Response(
|
||||
200, json=server_summary if mode == "--summary" else charts
|
||||
)
|
||||
|
||||
args = ["metrics", DEPLOYMENT_ID]
|
||||
for key, value in options.items():
|
||||
@@ -73,19 +101,19 @@ def test_metrics(monkeypatch, bounds, mode, deployment_metrics_response):
|
||||
args.append(mode)
|
||||
result = invoke(monkeypatch, args, handler)
|
||||
assert result.exit_code == 0, result.output
|
||||
expected = (
|
||||
charts
|
||||
if mode == "--raw"
|
||||
else compact_deployment_metrics(
|
||||
if mode == "--summary":
|
||||
expected = server_summary
|
||||
elif mode == "--raw":
|
||||
expected = charts
|
||||
else:
|
||||
expected = compact_deployment_metrics(
|
||||
charts,
|
||||
DEPLOYMENT_ID,
|
||||
start_time=params.get("start_time"),
|
||||
end_time=params.get("end_time"),
|
||||
summary=mode == "--summary",
|
||||
start_time=params["start_time"],
|
||||
end_time=params["end_time"],
|
||||
)
|
||||
)
|
||||
assert json.loads(result.output) == expected
|
||||
if mode != "--raw":
|
||||
if mode is None:
|
||||
assert expected["start_time"] == params["start_time"]
|
||||
assert expected["end_time"] == params["end_time"]
|
||||
assert result.output.strip() == json.dumps(expected, separators=(",", ":"))
|
||||
|
||||
@@ -53,22 +53,15 @@ def test_compact_metrics(deployment_metrics_response):
|
||||
assert result["sections"]["API"]["Latency"]["P95 Latency"]["unit"] is None
|
||||
|
||||
|
||||
def test_summary_and_requested_bounds(deployment_metrics_response):
|
||||
def test_requested_bounds(deployment_metrics_response):
|
||||
result = reshape(
|
||||
deployment_metrics_response,
|
||||
"id",
|
||||
start_time="2026-10-27T23:00:00Z",
|
||||
end_time="2026-10-28T01:00:00Z",
|
||||
summary=True,
|
||||
)
|
||||
assert result["start_time"] == "2026-10-27T23:00:00Z"
|
||||
assert result["end_time"] == "2026-10-28T01:00:00Z"
|
||||
for section in result["sections"].values():
|
||||
if section == {"_empty": True}:
|
||||
continue
|
||||
for chart in section.values():
|
||||
for series in chart.values():
|
||||
assert set(series) == {"metric", "unit", "latest", "min", "max", "avg"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("values", [[], [None], [float("nan"), float("inf")]])
|
||||
|
||||
Reference in new issue
Block a user