mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-15 04:17:56 +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
@@ -43,6 +43,7 @@ from docsgpt.tracing.core import (
|
||||
bind,
|
||||
bind_if_unset,
|
||||
current_trace,
|
||||
first_occurrence,
|
||||
mark_content_blocked,
|
||||
span,
|
||||
start_span,
|
||||
@@ -78,6 +79,7 @@ __all__ = [
|
||||
"bind_if_unset",
|
||||
"current_trace",
|
||||
"discard",
|
||||
"first_occurrence",
|
||||
"flush",
|
||||
"mark_content_blocked",
|
||||
"span",
|
||||
|
||||
+34
-5
@@ -272,6 +272,7 @@ class Trace:
|
||||
self.attributes: Dict[str, Any] = {}
|
||||
self.finished = False
|
||||
self.flushed = False
|
||||
self._seen_keys: set = set()
|
||||
self._lock = threading.Lock()
|
||||
self._stacks: Dict[int, List[Any]] = {}
|
||||
|
||||
@@ -288,7 +289,7 @@ class Trace:
|
||||
*,
|
||||
parent: Any = None,
|
||||
attributes: Optional[Dict[str, Any]] = None,
|
||||
):
|
||||
) -> Any:
|
||||
"""Record a new span; returns :data:`NOOP_SPAN` once finished or over the cap."""
|
||||
with self._lock:
|
||||
if self.finished:
|
||||
@@ -344,10 +345,18 @@ class Trace:
|
||||
|
||||
return _undo
|
||||
|
||||
def current_parent(self) -> Optional["_Seed | Span"]:
|
||||
def current_parent(self) -> Optional[Any]:
|
||||
stack = self._stacks.get(threading.get_ident())
|
||||
return stack[-1] if stack else None
|
||||
|
||||
def first_occurrence(self, key: Any) -> bool:
|
||||
"""True the first time ``key`` is seen in this trace, then False."""
|
||||
with self._lock:
|
||||
if key in self._seen_keys:
|
||||
return False
|
||||
self._seen_keys.add(key)
|
||||
return True
|
||||
|
||||
# -- ids --------------------------------------------------------------
|
||||
|
||||
def bind(self, *, only_if_unset: bool = False, **ids: Any) -> None:
|
||||
@@ -409,7 +418,12 @@ class Trace:
|
||||
if s.kind == KIND_RETRIEVAL
|
||||
and not (s.parent_id in by_id and by_id[s.parent_id].kind == KIND_RETRIEVAL)
|
||||
]
|
||||
tools = [s for s in self.spans if s.kind == KIND_TOOL]
|
||||
# Only calls that ran count: a call paused for approval is recorded
|
||||
# again when it runs in the next round, and denied or skipped calls
|
||||
# never ran at all.
|
||||
tools = [
|
||||
s for s in self.spans if s.kind == KIND_TOOL and s.status in (STATUS_OK, STATUS_ERROR)
|
||||
]
|
||||
|
||||
def _tokens(key: str) -> int:
|
||||
total = 0
|
||||
@@ -534,7 +548,9 @@ def activate(trace: Optional[Trace]) -> Iterator[Optional[Trace]]:
|
||||
_current.set(None)
|
||||
|
||||
|
||||
def start_span(kind: str, name: str, *, parent: Any = None, attributes: Optional[Dict[str, Any]] = None):
|
||||
def start_span(
|
||||
kind: str, name: str, *, parent: Any = None, attributes: Optional[Dict[str, Any]] = None
|
||||
) -> Any:
|
||||
"""Start a span in the current trace; returns :data:`NOOP_SPAN` without one."""
|
||||
trace = _current.get()
|
||||
if trace is None:
|
||||
@@ -546,7 +562,9 @@ def start_span(kind: str, name: str, *, parent: Any = None, attributes: Optional
|
||||
return NOOP_SPAN
|
||||
|
||||
|
||||
def span(kind: str, name: str, *, parent: Any = None, attributes: Optional[Dict[str, Any]] = None):
|
||||
def span(
|
||||
kind: str, name: str, *, parent: Any = None, attributes: Optional[Dict[str, Any]] = None
|
||||
) -> Any:
|
||||
"""Context-manager form of :func:`start_span` (errors mark the span and re-raise)."""
|
||||
return start_span(kind, name, parent=parent, attributes=attributes)
|
||||
|
||||
@@ -565,6 +583,17 @@ def bind_if_unset(**ids: Any) -> None:
|
||||
trace.bind(only_if_unset=True, **ids)
|
||||
|
||||
|
||||
def first_occurrence(key: Any) -> bool:
|
||||
"""True the first time ``key`` is seen in the current trace.
|
||||
|
||||
For steps that repeat identically many times in one request (a guardrail
|
||||
re-firing on every streamed segment): record the first, skip the rest, so
|
||||
they cannot use up the span cap. False without an active trace.
|
||||
"""
|
||||
trace = _current.get()
|
||||
return trace.first_occurrence(key) if trace is not None else False
|
||||
|
||||
|
||||
def mark_content_blocked() -> None:
|
||||
"""A guardrail blocked or retracted content: drop every preview from the stored trace."""
|
||||
trace = _current.get()
|
||||
|
||||
Reference in new issue
Block a user