personal memory agent
1# SPDX-License-Identifier: AGPL-3.0-only
2# Copyright (c) 2026 sol pbc
3
4"""Generic helper for logging extraction (IncompleteJSONError) failures."""
5
6import logging
7
8
9def log_extraction_failure(e: Exception, name: str) -> None:
10 """Log enhanced diagnostics for extraction generation failures.
11
12 Handles IncompleteJSONError specially by logging a single-line summary
13 with a head+tail sample and degenerate repetition detection.
14
15 Args:
16 e: The exception from generate().
17 name: Generator name for log context.
18 """
19 from think.models import IncompleteJSONError
20
21 if not isinstance(e, IncompleteJSONError):
22 logging.error("Extraction generation failed for %s: %s", name, e)
23 return
24
25 partial = e.partial_text
26 length = len(partial)
27
28 # Build single-line head+tail sample (newlines collapsed for log grep)
29 def _collapse(s: str) -> str:
30 return s.replace("\n", "\\n").replace("\r", "")
31
32 if length <= 300:
33 sample = _collapse(partial)
34 else:
35 sample = f"{_collapse(partial[:150])} ... {_collapse(partial[-150:])}"
36
37 # Repetition detection: count unique chars in last 1000
38 tail = partial[-1000:] if length >= 1000 else partial
39 unique_count = len(set(tail))
40 repetition_flag = ""
41 if unique_count < 20:
42 repetition_flag = (
43 f" [POSSIBLE DEGENERATE REPETITION: "
44 f"{unique_count} unique chars in last {len(tail)}]"
45 )
46
47 logging.error(
48 "Extraction generation failed for %s: %s "
49 "(partial_text: %d chars, %d unique in tail%s) sample: %s",
50 name,
51 e,
52 length,
53 unique_count,
54 repetition_flag,
55 sample,
56 )