mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-14 22:17:00 +00:00
Keep trace summaries from breaking Logs and fix their counts
A failed trace-summary lookup now leaves the Logs page intact without chips. Tool-call counts include only calls that ran, not their paused, denied or skipped records. A local guardrail that fires unchanged on every streamed segment is recorded once, so it cannot use up the span cap.
This commit is contained in:
1 parent
24796ae61f
commit
9f7f0b2f1e
7 files changed
+134
-19
No files matched your search
@@ -193,3 +193,22 @@ class TestLogsTraceSummaries:
|
||||
|
||||
def test_unknown_event_type_is_rejected(self, app, pg_conn):
|
||||
assert _logs(app, pg_conn, "owner", {"event_type": "bogus"}).status_code == 400
|
||||
|
||||
|
||||
class TestSummaryFailure:
|
||||
def test_page_survives_a_failed_trace_lookup(self, app, pg_conn):
|
||||
from docsgpt.storage.db.repositories.user_logs import UserLogsRepository
|
||||
|
||||
UserLogsRepository(pg_conn).insert(
|
||||
user_id="owner",
|
||||
endpoint="stream_answer",
|
||||
data={"question": "q", "request_id": "req-1"},
|
||||
)
|
||||
with patch(
|
||||
"docsgpt.api.user.analytics.routes.RequestTracesRepository.summaries_for_refs",
|
||||
side_effect=RuntimeError("statement timeout"),
|
||||
):
|
||||
response = _logs(app, pg_conn, "owner", {})
|
||||
assert response.status_code == 200
|
||||
(row,) = response.json["logs"]
|
||||
assert "trace" not in row
|
||||
@@ -388,6 +388,16 @@ class TestTraceSpans:
|
||||
assert span.attributes["docsgpt.guardrail.triggered"] == ["_test_always"]
|
||||
assert self.trace.content_blocked is True
|
||||
|
||||
def test_a_repeating_firing_is_recorded_once(self):
|
||||
"""The output guard re-scans every segment; one firing must not fill the span cap."""
|
||||
engine = GuardrailEngine(
|
||||
_config(controls=[{"check": "_test_always", "stage": "output", "action": "flag"}])
|
||||
)
|
||||
for _ in range(50):
|
||||
engine.evaluate("segment", Stage.OUTPUT)
|
||||
assert len(self.trace.spans) == 1
|
||||
assert self.trace.content_blocked is True
|
||||
|
||||
def test_remote_scan_is_traced_with_judge_nested(self):
|
||||
engine = GuardrailEngine(
|
||||
_config(controls=[{"check": "_test_quick_remote", "stage": "input", "action": "block"}])
|
||||
|
||||
@@ -372,3 +372,26 @@ class TestRecordQuery:
|
||||
tracing.mark_content_blocked()
|
||||
trace.finish()
|
||||
assert "query" not in trace.to_record()["summary"]
|
||||
|
||||
|
||||
class TestToolCount:
|
||||
def test_only_tool_calls_that_ran_are_counted(self):
|
||||
trace = tracing.start_trace(source="stream")
|
||||
with tracing.activate(trace):
|
||||
tracing.start_span(tracing.KIND_TOOL, "paused").end(tracing.STATUS_PENDING)
|
||||
tracing.start_span(tracing.KIND_TOOL, "denied").end(tracing.STATUS_DENIED)
|
||||
tracing.start_span(tracing.KIND_TOOL, "skipped").end(tracing.STATUS_SKIPPED)
|
||||
tracing.start_span(tracing.KIND_TOOL, "ran").end()
|
||||
tracing.start_span(tracing.KIND_TOOL, "failed").end(tracing.STATUS_ERROR)
|
||||
trace.finish()
|
||||
assert trace.summary()["tool_calls"] == 2
|
||||
|
||||
|
||||
class TestFirstOccurrence:
|
||||
def test_key_is_new_once_per_trace(self):
|
||||
trace = tracing.start_trace(source="stream")
|
||||
with tracing.activate(trace):
|
||||
assert tracing.first_occurrence(("a", 1)) is True
|
||||
assert tracing.first_occurrence(("a", 1)) is False
|
||||
assert tracing.first_occurrence(("a", 2)) is True
|
||||
assert tracing.first_occurrence(("a", 3)) is False # no active trace
|
||||
Reference in new issue
Block a user