diff --git a/forecasting_tools/ai_models/general_llm.py b/forecasting_tools/ai_models/general_llm.py index e60783b9..cbe5c140 100644 --- a/forecasting_tools/ai_models/general_llm.py +++ b/forecasting_tools/ai_models/general_llm.py @@ -359,9 +359,10 @@ def _extract_citations( return typeguard.check_type(citations, list[str]) # OpenRouter returns Perplexity citations as url_citation annotations - # rather than in model_extra["citations"]. URLs may be duplicated - # (e.g. once with titles, once without). We deduplicate while - # preserving first-seen order so urls[i] corresponds to [i+1]. + # rather than in model_extra["citations"]. Annotations may arrive as + # dicts (older SDKs) or pydantic objects (newer SDKs). URLs may be + # duplicated (e.g. once with titles, once without). We deduplicate + # while preserving first-seen order so urls[i] corresponds to [i+1]. message = choices[0].message annotations = getattr(message, "annotations", None) if not annotations: @@ -369,6 +370,8 @@ def _extract_citations( seen: set[str] = set() unique_urls: list[str] = [] for annotation in annotations: + if hasattr(annotation, "model_dump"): + annotation = annotation.model_dump() if not isinstance(annotation, dict): continue if annotation.get("type") != "url_citation":