""" note_payload.py — mindnet core payload builders Version: 1.3.1 (2025-11-08) Purpose ------- Build a robust, forward-compatible note payload for Qdrant upserts. This module is intentionally defensive: - Accepts both dict-like "parsed note" objects and dataclass/objects with attributes. - Tolerates extra kwargs from different callers (e.g., `vault_root`, `prefix`, etc.). - Ensures `retriever_weight` is resolved and present in the payload if available. Contract -------- make_note_payload(note, **kwargs) -> Dict[str, Any] Expected minimal fields in returned payload: - note_id (str) - title (str) - type (str) - path (str or None) # relative to vault_root when provided - tags (List[str]) - retriever_weight (float or None) # if available """ from __future__ import annotations from pathlib import Path from typing import Any, Dict, Iterable, List, Mapping, Optional, Union def _get(obj: Any, key: str, default: Any = None) -> Any: """Try to read `key` from mapping or attribute; else default.""" if obj is None: return default if isinstance(obj, Mapping): return obj.get(key, default) # attribute access return getattr(obj, key, default) def _get_frontmatter(note: Any) -> Mapping[str, Any]: fm = _get(note, "frontmatter", {}) if isinstance(fm, Mapping): return fm return {} # be safe def _resolve_retriever_weight(explicit: Any, fm: Mapping[str, Any]) -> Optional[float]: """ Priority: 1) explicit kwarg retriever_weight 2) frontmatter['retriever_weight'] 3) frontmatter['retriever']['weight'] """ def to_float(v: Any) -> Optional[float]: try: if v is None: return None return float(v) except Exception: return None if explicit is not None: return to_float(explicit) if "retriever_weight" in fm: return to_float(fm.get("retriever_weight")) retr = fm.get("retriever") if isinstance(retr, Mapping) and "weight" in retr: return to_float(retr.get("weight")) return None def _to_rel_path(abs_path: Optional[Union[str, Path]], vault_root: Optional[Union[str, Path]]) -> Optional[str]: if abs_path is None: return None try: p = Path(abs_path) if vault_root: try: rp = p.relative_to(Path(vault_root)) return str(rp) except Exception: return str(p) return str(p) except Exception: return str(abs_path) def make_note_payload( note: Any, *args, # tolerate older/other callers **kwargs: Any, ) -> Dict[str, Any]: """ Build a normalized note payload for Qdrant. Unknown kwargs are ignored to keep the function forward-compatible. Recognized kwargs: - vault_root: base path to make `path` relative (optional) - retriever_weight: explicit override (optional) """ vault_root = kwargs.get("vault_root") explicit_weight = kwargs.get("retriever_weight") fm = _get_frontmatter(note) note_id = _get(note, "note_id") or _get(note, "id") if not note_id: # Try from frontmatter note_id = fm.get("id") title = _get(note, "title") or fm.get("title") ntype = _get(note, "type") or fm.get("type") tags = _get(note, "tags") or fm.get("tags") or [] if not isinstance(tags, list): tags = list(tags) if tags else [] path_val = _get(note, "path") or _get(note, "abs_path") or fm.get("path") payload: Dict[str, Any] = { "note_id": note_id, "title": title, "type": ntype, "tags": tags, "path": _to_rel_path(path_val, vault_root), "retriever_weight": _resolve_retriever_weight(explicit_weight, fm), } # Also surface explicit frontmatter fields (non-conflicting) if present for k in ("status", "created", "updated"): v = fm.get(k) if v is not None and k not in payload: payload[k] = v return payload