mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-16 22:14:07 +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
@@ -38,7 +38,23 @@ def _start_guardrail_span(stage: Stage, controls) -> "tracing.Span":
|
||||
)
|
||||
|
||||
|
||||
def _describe_decision(span, decision: StageDecision) -> None:
|
||||
def _content_fired(decision: StageDecision) -> bool:
|
||||
return bool(decision.triggered or decision.blocked or decision.redacted)
|
||||
|
||||
|
||||
def _decision_key(decision: StageDecision) -> tuple:
|
||||
"""Identity of a guardrail outcome, for recording each distinct one once."""
|
||||
return (
|
||||
"guardrail",
|
||||
decision.stage.value,
|
||||
tuple(sorted(v.check for v in decision.triggered)),
|
||||
tuple(sorted(v.check for v in decision.unevaluated)),
|
||||
decision.blocked,
|
||||
decision.redacted,
|
||||
)
|
||||
|
||||
|
||||
def _describe_decision(span: "tracing.Span", decision: StageDecision) -> None:
|
||||
"""Record a stage decision on its span; a firing guardrail drops trace previews."""
|
||||
triggered = [v.check for v in decision.triggered]
|
||||
span.set(
|
||||
@@ -50,7 +66,7 @@ def _describe_decision(span, decision: StageDecision) -> None:
|
||||
"docsgpt.guardrail.unevaluated": [v.check for v in decision.unevaluated] or None,
|
||||
}
|
||||
)
|
||||
if triggered or decision.blocked or decision.redacted:
|
||||
if _content_fired(decision):
|
||||
# The scanned text (or text near it) sits in other spans' previews:
|
||||
# the retrieval query, tool results, the answer. Keep none of it.
|
||||
tracing.mark_content_blocked()
|
||||
@@ -136,10 +152,15 @@ class GuardrailEngine:
|
||||
decision.verdicts = [self._run_control(c, text, stage) for c in controls]
|
||||
self._reduce(decision)
|
||||
# The output guard evaluates every streamed segment; tracing each
|
||||
# clean local scan would bury the trace, so only a firing is kept.
|
||||
# clean local scan would bury the trace, so only a firing is kept,
|
||||
# and a firing that repeats unchanged segment after segment is
|
||||
# recorded once so it cannot use up the trace's span cap.
|
||||
if not decision.clean or decision.unevaluated:
|
||||
with _start_guardrail_span(stage, controls) as span:
|
||||
_describe_decision(span, decision)
|
||||
if tracing.first_occurrence(_decision_key(decision)):
|
||||
with _start_guardrail_span(stage, controls) as span:
|
||||
_describe_decision(span, decision)
|
||||
elif _content_fired(decision):
|
||||
tracing.mark_content_blocked()
|
||||
|
||||
self._record(decision)
|
||||
return decision
|
||||
|
||||
Reference in new issue
Block a user