Skip to content
Merged
123 changes: 84 additions & 39 deletions praisonai_tools/tools/joy_trust_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def delegate_to_agent(agent, task):

import os
import logging
import time
from typing import Any, Dict, Optional, Union, Callable, List
from dataclasses import dataclass
from functools import wraps
Expand Down Expand Up @@ -110,11 +111,15 @@ def check_trust(self, agent_name: str, min_score: Optional[float] = None) -> Dic
if not agent_name:
return {
"agent_name": "",
"agent_id": None,
"trust_score": 0.0,
"verified": False,
"meets_threshold": False,
"reputation": {},
"recommendations": 0,
"threshold_used": 0.0,
"vouch_count": 0,
"capabilities": [],
"tier": None,
"badges": [],
"error": "agent_name is required"
}

Expand All @@ -130,15 +135,18 @@ def check_trust(self, agent_name: str, min_score: Optional[float] = None) -> Dic

try:
import httpx
import time
except ImportError:
return {
"agent_name": agent_name,
"agent_id": None,
"trust_score": 0.0,
"verified": False,
"meets_threshold": False,
"reputation": {},
"recommendations": 0,
"threshold_used": min_threshold,
"vouch_count": 0,
"capabilities": [],
"tier": None,
"badges": [],
"error": (
"httpx is required for Joy Trust Network integration. "
"Install with: pip install praisonai-tools[marketplace] or pip install httpx"
Expand All @@ -147,80 +155,117 @@ def check_trust(self, agent_name: str, min_score: Optional[float] = None) -> Dic

try:
with httpx.Client(timeout=self.config.timeout_seconds) as client:
params = {"name": agent_name}
headers = {}
if self.api_key:
params["api_key"] = self.api_key
headers["x-api-key"] = self.api_key

response = client.get(
"https://joy-connect.fly.dev/agents/discover",
params=params
params={"query": agent_name},
headers=headers
)
response.raise_for_status()

data = response.json()
trust_score = data.get("trust_score", 0.0)


# FIX: Extract agent from the agents array, not top level
# Use 'or' to handle both missing key AND null value
agents = data.get("agents") or []

# Find matching agent by name (case-insensitive, exact match only)
# Security: Do NOT fallback to first result - could return wrong agent's trust
agent = next((a for a in agents if a.get("name", "").lower() == agent_name.lower()), None)

if not agent:
return {
"agent_name": agent_name,
"agent_id": None,
"trust_score": 0.0,
"verified": False,
"meets_threshold": False,
"threshold_used": min_threshold,
"vouch_count": 0,
"capabilities": [],
"tier": None,
"badges": [],
"error": f"Agent '{agent_name}' not found on Joy Trust Network"
}
Comment on lines +185 to +197
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The structure of the dictionary returned here when an agent is not found is inconsistent with the structure of the successful response. This response includes legacy fields reputation and recommendations, while the successful response (lines 192-205) includes new fields like agent_id, vouch_count, and capabilities. To ensure consumers of this function receive a consistent data structure, this error response should be updated to match the new format, using default values for the fields.

Suggested change
return {
"agent_name": agent_name,
"trust_score": 0.0,
"verified": False,
"meets_threshold": False,
"threshold_used": min_threshold,
"reputation": {},
"recommendations": 0,
"error": f"Agent '{agent_name}' not found on Joy Trust Network"
}
return {
"agent_name": agent_name,
"agent_id": None,
"trust_score": 0.0,
"verified": False,
"meets_threshold": False,
"threshold_used": min_threshold,
"vouch_count": 0,
"capabilities": [],
"tier": "unknown",
"badges": [],
"error": f"Agent '{agent_name}' not found on Joy Trust Network"
}


# Read from the agent object, not top level
# Use 'or' to handle both missing key AND null value
trust_score = agent.get("trust_score") or 0.0

result = {
"agent_name": agent_name,
"agent_name": agent.get("name", agent_name),
"agent_id": agent.get("id"),
"trust_score": trust_score,
"verified": data.get("verified", False),
"verified": agent.get("verified", False),
"meets_threshold": trust_score >= min_threshold,
"threshold_used": min_threshold,
"reputation": data.get("reputation", {}),
"recommendations": data.get("recommendations", 0),
"last_activity": data.get("last_activity"),
"network_rank": data.get("network_rank"),
"vouch_count": agent.get("vouch_count", 0),
"capabilities": agent.get("capabilities", []),
"tier": agent.get("tier", "free"),
"badges": agent.get("badges", []),
"error": None,
"_cached_at": time.time()
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# Cache the result
self._cache[cache_key] = result

return result

except httpx.RequestError as e:
logger.error(f"Joy Trust request error: {e}")
error_result = {
# Security: Fail closed - errors should deny handoffs, not allow them
return {
"agent_name": agent_name,
"agent_id": None,
"trust_score": 0.0,
"verified": False,
"meets_threshold": self.config.fallback_on_error, # Fallback behavior
"meets_threshold": False,
"threshold_used": min_threshold,
"reputation": {},
"recommendations": 0,
"vouch_count": 0,
"capabilities": [],
"tier": None,
"badges": [],
"error": f"Connection error: {e}",
"fallback_used": self.config.fallback_on_error
"fallback_used": True
}
return error_result
except httpx.HTTPStatusError as e:
logger.error(f"Joy Trust API error: {e.response.status_code}")
error_result = {
# Security: Fail closed - errors should deny handoffs, not allow them
return {
"agent_name": agent_name,
"agent_id": None,
"trust_score": 0.0,
"verified": False,
"meets_threshold": self.config.fallback_on_error, # Fallback behavior
"meets_threshold": False,
"threshold_used": min_threshold,
"reputation": {},
"recommendations": 0,
"vouch_count": 0,
"capabilities": [],
"tier": None,
"badges": [],
"error": f"API error ({e.response.status_code}): {e.response.text}",
"fallback_used": self.config.fallback_on_error
"fallback_used": True
}
return error_result
except Exception as e:
logger.error(f"Joy Trust unexpected error: {e}")
error_result = {
logger.exception("Joy Trust unexpected error")
# Security: Fail closed - errors should deny handoffs, not allow them
return {
"agent_name": agent_name,
"agent_id": None,
"trust_score": 0.0,
"verified": False,
"meets_threshold": self.config.fallback_on_error, # Fallback behavior
"meets_threshold": False,
"threshold_used": min_threshold,
"reputation": {},
"recommendations": 0,
"vouch_count": 0,
"capabilities": [],
"tier": None,
"badges": [],
"error": f"Unexpected error: {e}",
"fallback_used": self.config.fallback_on_error
"fallback_used": True
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Comment thread
greptile-apps[bot] marked this conversation as resolved.
return error_result

def verify_handoff_safety(self, agent_name: str, min_score: Optional[float] = None) -> Dict[str, Any]:
"""Verify if it's safe to hand off to the specified agent.
Expand Down