import json from crewai.tools import BaseTool from pydantic import BaseModel, Field from credibility.scorer import score_source import config class CredibilityInput(BaseModel): sources_json: str = Field( description="JSON string of list of source dicts with keys: url, title, content, published_date" ) fast_moving_topic: bool = Field( default=False, description="True if topic is time-sensitive (news, recent tech), False for evergreen topics" ) class CredibilityScorerTool(BaseTool): name: str = "Source Credibility Scorer" description: str = ( "Score a list of sources for credibility (0.0–1.0). " "Filters out unreliable sources. Returns only accepted sources with their scores. " "Sources below 0.35 are dropped. Sources 0.35–0.55 are flagged low-confidence." ) args_schema: type[BaseModel] = CredibilityInput def _run(self, sources_json: str, fast_moving_topic: bool = False) -> str: try: sources = json.loads(sources_json) except json.JSONDecodeError as e: return json.dumps({"error": f"Invalid JSON: {e}", "accepted": [], "dropped": []}) accepted = [] dropped = [] for src in sources: result = score_source( url=src.get("url", ""), title=src.get("title", ""), content=src.get("content", ""), pub_date=src.get("published_date"), fast_moving=fast_moving_topic, ) if result.get("blocked") or result["score"] < config.CREDIBILITY_CUTOFF: dropped.append({"url": src.get("url"), "score": result["score"], "reason": "below threshold"}) continue confidence = ( "high" if result["score"] >= config.LOW_CONFIDENCE_CUTOFF else "low" ) accepted.append({ **src, "credibility_score": result["score"], "confidence": confidence, "score_breakdown": result.get("breakdown", {}), }) accepted.sort(key=lambda x: x["credibility_score"], reverse=True) accepted = accepted[:config.MAX_ACCEPTED_SOURCES] return json.dumps({ "accepted": accepted, "dropped_count": len(dropped), "accepted_count": len(accepted), })