Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog.d/tsk-22rp6s-unboundlocalerror-and-coercion-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
### Fixed
- Fixed `UnboundLocalError` in `_format_hit` when `timestamp` was referenced before definition
- Defensive coercion: `as_of` now uses `try/except (TypeError, ValueError)` with `logger.warning` instead of unguarded `float()`, so ISO-8601 timestamps from caller metadata degrade to `0.0` rather than raising
- `review_by` comparison gated on `isinstance(review_by, str)` to prevent `TypeError` when non-string metadata arrives through `ingest_batch`
54 changes: 49 additions & 5 deletions taosmd/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,12 +449,16 @@ def _format_hit(hit: dict) -> dict:
user_md = dict(user_md) # copy: never mutate the stored row metadata
for key, value in preserved.items():
user_md.setdefault(key, value)
# Warn (not assert) if critical provenance keys were lost during
# unwrap. A bare assert is stripped under ``-O`` and would convert a
# metadata-shape anomaly into a 500 on the search request path.
for key in ("archive_span_id", "agent", "project"):
if key in (md or {}) and key not in user_md:
logger.warning(
"_format_hit lost critical key %r during metadata unwrap",
key,
)

confidence = (
md.get("similarity")
if isinstance(md, dict) and md.get("similarity") is not None
else hit.get("source_score", 0.0)
)
timestamp = (
user_md.get("timestamp")
if isinstance(user_md, dict) and user_md.get("timestamp") is not None
Expand All @@ -463,6 +467,46 @@ def _format_hit(hit: dict) -> dict:
or 0
)

# Collection doc-currency metadata lives in the inner user-metadata dict
# (that is how ingest_folder's chunk_md stores it), so read from user_md
# after the unwrap, not from the outer envelope.
# is_current is False for superseded/history rows (hidden_by set);
# always True on the default active-recall path.
# as_of is the indexing timestamp: indexed_at (float epoch) or the
# row's created_at/timestamp, coerced to float for a single type.
# is_past_review is True only when review_by is present and overdue.
if isinstance(user_md, dict):
is_current = not (isinstance(md, dict) and "hidden_by" in md)
as_of = user_md.get("indexed_at")
if as_of is None:
as_of = timestamp
try:
as_of = float(as_of)
except (TypeError, ValueError):
logger.warning(
"taosmd: could not convert as_of %r to float, defaulting to 0.0",
as_of,
)
as_of = 0.0
review_by = user_md.get("review_by")
has_review_by = review_by is not None
is_past_review = has_review_by and isinstance(review_by, str) and review_by < time.strftime("%Y-%m-%d")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WARNING]: Empty string review_by treated as past due; date comparison uses local time

An empty string "" passes isinstance(review_by, str) and "" < time.strftime("%Y-%m-%d") is True, so is_past_review becomes True for empty review_by values. Additionally, time.strftime("%Y-%m-%d") returns local time; if the server and review_by dates are in different timezones, the comparison may be off by a day.

Suggested change
is_past_review = has_review_by and isinstance(review_by, str) and review_by < time.strftime("%Y-%m-%d")
is_past_review = has_review_by and isinstance(review_by, str) and review_by and review_by < time.strftime("%Y-%m-%d")

Reply with @kilocode-bot fix it to have Kilo Code address this issue.


user_md["is_current"] = is_current
user_md["as_of"] = as_of
if has_review_by:
user_md["is_past_review"] = is_past_review
# doc_id / version / review_by are already in user_md when present;
# no extraction needed. Only superseded_by comes from the envelope.
if isinstance(md, dict) and "hidden_by" in md:
user_md["superseded_by"] = md["hidden_by"]

confidence = (
md.get("similarity")
if isinstance(md, dict) and md.get("similarity") is not None
else hit.get("source_score", 0.0)
)

return {
"text": hit.get("text", ""),
"source": hit.get("source", "unknown"),
Expand Down
2 changes: 1 addition & 1 deletion tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ def test_format_hit_prefers_similarity_over_source_score():
assert formatted["confidence"] == 0.85
assert formatted["source"] == "vector"
assert formatted["timestamp"] == 1700000000
assert formatted["metadata"] == {"position": 7, "timestamp": 1700000000}
assert formatted["metadata"] == {"position": 7, "timestamp": 1700000000, "as_of": 1700000000.0, "is_current": True}


def test_format_hit_falls_back_to_source_score():
Expand Down