feat(coordinator-api): integrate dynamic pricing engine with GPU marketplace and add agent identity router
- Add DynamicPricingEngine and MarketDataCollector dependencies to GPU marketplace endpoints
- Implement dynamic pricing calculation for GPU registration with market_balance strategy
- Calculate real-time dynamic prices at booking time with confidence scores and pricing factors
- Enhance /marketplace/pricing/{model} endpoint with comprehensive dynamic pricing analysis
- Add static vs dynamic price
This commit is contained in:
478
apps/coordinator-api/src/app/reputation/aggregator.py
Normal file
478
apps/coordinator-api/src/app/reputation/aggregator.py
Normal file
@@ -0,0 +1,478 @@
|
||||
"""
|
||||
Cross-Chain Reputation Aggregator
|
||||
Aggregates reputation data from multiple blockchains and normalizes scores
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, List, Optional, Any, Set
|
||||
from uuid import uuid4
|
||||
import json
|
||||
from aitbc.logging import get_logger
|
||||
|
||||
from sqlmodel import Session, select, update, delete, func
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
from ..domain.reputation import AgentReputation, ReputationEvent
|
||||
from ..domain.cross_chain_reputation import (
|
||||
CrossChainReputationAggregation, CrossChainReputationEvent,
|
||||
CrossChainReputationConfig, ReputationMetrics
|
||||
)
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class CrossChainReputationAggregator:
|
||||
"""Aggregates reputation data from multiple blockchains"""
|
||||
|
||||
def __init__(self, session: Session, blockchain_clients: Optional[Dict[int, Any]] = None):
|
||||
self.session = session
|
||||
self.blockchain_clients = blockchain_clients or {}
|
||||
|
||||
async def collect_chain_reputation_data(self, chain_id: int) -> List[Dict[str, Any]]:
|
||||
"""Collect reputation data from a specific blockchain"""
|
||||
|
||||
try:
|
||||
# Get all reputations for the chain
|
||||
stmt = select(AgentReputation).where(
|
||||
AgentReputation.chain_id == chain_id if hasattr(AgentReputation, 'chain_id') else True
|
||||
)
|
||||
|
||||
# Handle case where reputation doesn't have chain_id
|
||||
if not hasattr(AgentReputation, 'chain_id'):
|
||||
# For now, return all reputations (assume they're on the primary chain)
|
||||
stmt = select(AgentReputation)
|
||||
|
||||
reputations = self.session.exec(stmt).all()
|
||||
|
||||
chain_data = []
|
||||
for reputation in reputations:
|
||||
chain_data.append({
|
||||
'agent_id': reputation.agent_id,
|
||||
'trust_score': reputation.trust_score,
|
||||
'reputation_level': reputation.reputation_level,
|
||||
'total_transactions': getattr(reputation, 'transaction_count', 0),
|
||||
'success_rate': getattr(reputation, 'success_rate', 0.0),
|
||||
'dispute_count': getattr(reputation, 'dispute_count', 0),
|
||||
'last_updated': reputation.updated_at,
|
||||
'chain_id': getattr(reputation, 'chain_id', chain_id)
|
||||
})
|
||||
|
||||
return chain_data
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error collecting reputation data for chain {chain_id}: {e}")
|
||||
return []
|
||||
|
||||
async def normalize_reputation_scores(self, scores: Dict[int, float]) -> float:
|
||||
"""Normalize reputation scores across chains"""
|
||||
|
||||
try:
|
||||
if not scores:
|
||||
return 0.0
|
||||
|
||||
# Get chain configurations
|
||||
chain_configs = {}
|
||||
for chain_id in scores.keys():
|
||||
config = await self._get_chain_config(chain_id)
|
||||
chain_configs[chain_id] = config
|
||||
|
||||
# Apply chain-specific normalization
|
||||
normalized_scores = {}
|
||||
total_weight = 0.0
|
||||
weighted_sum = 0.0
|
||||
|
||||
for chain_id, score in scores.items():
|
||||
config = chain_configs.get(chain_id)
|
||||
|
||||
if config and config.is_active:
|
||||
# Apply chain weight
|
||||
weight = config.chain_weight
|
||||
normalized_score = score * weight
|
||||
|
||||
normalized_scores[chain_id] = normalized_score
|
||||
total_weight += weight
|
||||
weighted_sum += normalized_score
|
||||
|
||||
# Calculate final normalized score
|
||||
if total_weight > 0:
|
||||
final_score = weighted_sum / total_weight
|
||||
else:
|
||||
# If no valid configurations, use simple average
|
||||
final_score = sum(scores.values()) / len(scores)
|
||||
|
||||
return max(0.0, min(1.0, final_score))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error normalizing reputation scores: {e}")
|
||||
return 0.0
|
||||
|
||||
async def apply_chain_weighting(self, scores: Dict[int, float]) -> Dict[int, float]:
|
||||
"""Apply chain-specific weighting to reputation scores"""
|
||||
|
||||
try:
|
||||
weighted_scores = {}
|
||||
|
||||
for chain_id, score in scores.items():
|
||||
config = await self._get_chain_config(chain_id)
|
||||
|
||||
if config and config.is_active:
|
||||
weight = config.chain_weight
|
||||
weighted_scores[chain_id] = score * weight
|
||||
else:
|
||||
# Default weight if no config
|
||||
weighted_scores[chain_id] = score
|
||||
|
||||
return weighted_scores
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error applying chain weighting: {e}")
|
||||
return scores
|
||||
|
||||
async def detect_reputation_anomalies(self, agent_id: str) -> List[Dict[str, Any]]:
|
||||
"""Detect reputation anomalies across chains"""
|
||||
|
||||
try:
|
||||
anomalies = []
|
||||
|
||||
# Get cross-chain aggregation
|
||||
stmt = select(CrossChainReputationAggregation).where(
|
||||
CrossChainReputationAggregation.agent_id == agent_id
|
||||
)
|
||||
aggregation = self.session.exec(stmt).first()
|
||||
|
||||
if not aggregation:
|
||||
return anomalies
|
||||
|
||||
# Check for consistency anomalies
|
||||
if aggregation.consistency_score < 0.7:
|
||||
anomalies.append({
|
||||
'agent_id': agent_id,
|
||||
'anomaly_type': 'low_consistency',
|
||||
'detected_at': datetime.utcnow(),
|
||||
'description': f"Low consistency score: {aggregation.consistency_score:.2f}",
|
||||
'severity': 'high' if aggregation.consistency_score < 0.5 else 'medium',
|
||||
'consistency_score': aggregation.consistency_score,
|
||||
'score_variance': aggregation.score_variance,
|
||||
'score_range': aggregation.score_range
|
||||
})
|
||||
|
||||
# Check for score variance anomalies
|
||||
if aggregation.score_variance > 0.25:
|
||||
anomalies.append({
|
||||
'agent_id': agent_id,
|
||||
'anomaly_type': 'high_variance',
|
||||
'detected_at': datetime.utcnow(),
|
||||
'description': f"High score variance: {aggregation.score_variance:.2f}",
|
||||
'severity': 'high' if aggregation.score_variance > 0.5 else 'medium',
|
||||
'score_variance': aggregation.score_variance,
|
||||
'score_range': aggregation.score_range,
|
||||
'chain_scores': aggregation.chain_scores
|
||||
})
|
||||
|
||||
# Check for missing chain data
|
||||
expected_chains = await self._get_active_chain_ids()
|
||||
missing_chains = set(expected_chains) - set(aggregation.active_chains)
|
||||
|
||||
if missing_chains:
|
||||
anomalies.append({
|
||||
'agent_id': agent_id,
|
||||
'anomaly_type': 'missing_chain_data',
|
||||
'detected_at': datetime.utcnow(),
|
||||
'description': f"Missing data for chains: {list(missing_chains)}",
|
||||
'severity': 'medium',
|
||||
'missing_chains': list(missing_chains),
|
||||
'active_chains': aggregation.active_chains
|
||||
})
|
||||
|
||||
return anomalies
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error detecting reputation anomalies for agent {agent_id}: {e}")
|
||||
return []
|
||||
|
||||
async def batch_update_reputations(self, updates: List[Dict[str, Any]]) -> Dict[str, bool]:
|
||||
"""Batch update reputation scores for multiple agents"""
|
||||
|
||||
try:
|
||||
results = {}
|
||||
|
||||
for update in updates:
|
||||
agent_id = update['agent_id']
|
||||
chain_id = update.get('chain_id', 1)
|
||||
new_score = update['score']
|
||||
|
||||
try:
|
||||
# Get existing reputation
|
||||
stmt = select(AgentReputation).where(
|
||||
AgentReputation.agent_id == agent_id,
|
||||
AgentReputation.chain_id == chain_id if hasattr(AgentReputation, 'chain_id') else True
|
||||
)
|
||||
|
||||
if not hasattr(AgentReputation, 'chain_id'):
|
||||
stmt = select(AgentReputation).where(AgentReputation.agent_id == agent_id)
|
||||
|
||||
reputation = self.session.exec(stmt).first()
|
||||
|
||||
if reputation:
|
||||
# Update reputation
|
||||
reputation.trust_score = new_score * 1000 # Convert to 0-1000 scale
|
||||
reputation.reputation_level = self._determine_reputation_level(new_score)
|
||||
reputation.updated_at = datetime.utcnow()
|
||||
|
||||
# Create event record
|
||||
event = ReputationEvent(
|
||||
agent_id=agent_id,
|
||||
event_type='batch_update',
|
||||
impact_score=new_score - (reputation.trust_score / 1000.0),
|
||||
trust_score_before=reputation.trust_score,
|
||||
trust_score_after=reputation.trust_score,
|
||||
event_data=update,
|
||||
occurred_at=datetime.utcnow()
|
||||
)
|
||||
|
||||
self.session.add(event)
|
||||
results[agent_id] = True
|
||||
else:
|
||||
# Create new reputation
|
||||
reputation = AgentReputation(
|
||||
agent_id=agent_id,
|
||||
trust_score=new_score * 1000,
|
||||
reputation_level=self._determine_reputation_level(new_score),
|
||||
created_at=datetime.utcnow(),
|
||||
updated_at=datetime.utcnow()
|
||||
)
|
||||
|
||||
self.session.add(reputation)
|
||||
results[agent_id] = True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating reputation for agent {agent_id}: {e}")
|
||||
results[agent_id] = False
|
||||
|
||||
self.session.commit()
|
||||
|
||||
# Update cross-chain aggregations
|
||||
for agent_id in updates:
|
||||
if results.get(agent_id):
|
||||
await self._update_cross_chain_aggregation(agent_id)
|
||||
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in batch reputation update: {e}")
|
||||
return {update['agent_id']: False for update in updates}
|
||||
|
||||
async def get_chain_statistics(self, chain_id: int) -> Dict[str, Any]:
|
||||
"""Get reputation statistics for a specific chain"""
|
||||
|
||||
try:
|
||||
# Get all reputations for the chain
|
||||
stmt = select(AgentReputation).where(
|
||||
AgentReputation.chain_id == chain_id if hasattr(AgentReputation, 'chain_id') else True
|
||||
)
|
||||
|
||||
if not hasattr(AgentReputation, 'chain_id'):
|
||||
# For now, get all reputations
|
||||
stmt = select(AgentReputation)
|
||||
|
||||
reputations = self.session.exec(stmt).all()
|
||||
|
||||
if not reputations:
|
||||
return {
|
||||
'chain_id': chain_id,
|
||||
'total_agents': 0,
|
||||
'average_reputation': 0.0,
|
||||
'reputation_distribution': {},
|
||||
'total_transactions': 0,
|
||||
'success_rate': 0.0
|
||||
}
|
||||
|
||||
# Calculate statistics
|
||||
total_agents = len(reputations)
|
||||
total_reputation = sum(rep.trust_score for rep in reputations)
|
||||
average_reputation = total_reputation / total_agents / 1000.0 # Convert to 0-1 scale
|
||||
|
||||
# Reputation distribution
|
||||
distribution = {}
|
||||
for reputation in reputations:
|
||||
level = reputation.reputation_level.value
|
||||
distribution[level] = distribution.get(level, 0) + 1
|
||||
|
||||
# Transaction statistics
|
||||
total_transactions = sum(getattr(rep, 'transaction_count', 0) for rep in reputations)
|
||||
successful_transactions = sum(
|
||||
getattr(rep, 'transaction_count', 0) * getattr(rep, 'success_rate', 0) / 100.0
|
||||
for rep in reputations
|
||||
)
|
||||
success_rate = successful_transactions / max(total_transactions, 1)
|
||||
|
||||
return {
|
||||
'chain_id': chain_id,
|
||||
'total_agents': total_agents,
|
||||
'average_reputation': average_reputation,
|
||||
'reputation_distribution': distribution,
|
||||
'total_transactions': total_transactions,
|
||||
'success_rate': success_rate,
|
||||
'last_updated': datetime.utcnow()
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting chain statistics for chain {chain_id}: {e}")
|
||||
return {
|
||||
'chain_id': chain_id,
|
||||
'error': str(e),
|
||||
'total_agents': 0,
|
||||
'average_reputation': 0.0
|
||||
}
|
||||
|
||||
async def sync_cross_chain_reputations(self, agent_ids: List[str]) -> Dict[str, bool]:
|
||||
"""Synchronize reputation data across chains for multiple agents"""
|
||||
|
||||
try:
|
||||
results = {}
|
||||
|
||||
for agent_id in agent_ids:
|
||||
try:
|
||||
# Re-aggregate cross-chain reputation
|
||||
await self._update_cross_chain_aggregation(agent_id)
|
||||
results[agent_id] = True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error syncing cross-chain reputation for agent {agent_id}: {e}")
|
||||
results[agent_id] = False
|
||||
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in cross-chain reputation sync: {e}")
|
||||
return {agent_id: False for agent_id in agent_ids}
|
||||
|
||||
async def _get_chain_config(self, chain_id: int) -> Optional[CrossChainReputationConfig]:
|
||||
"""Get configuration for a specific chain"""
|
||||
|
||||
stmt = select(CrossChainReputationConfig).where(
|
||||
CrossChainReputationConfig.chain_id == chain_id,
|
||||
CrossChainReputationConfig.is_active == True
|
||||
)
|
||||
|
||||
config = self.session.exec(stmt).first()
|
||||
|
||||
if not config:
|
||||
# Create default config
|
||||
config = CrossChainReputationConfig(
|
||||
chain_id=chain_id,
|
||||
chain_weight=1.0,
|
||||
base_reputation_bonus=0.0,
|
||||
transaction_success_weight=0.1,
|
||||
transaction_failure_weight=-0.2,
|
||||
dispute_penalty_weight=-0.3,
|
||||
minimum_transactions_for_score=5,
|
||||
reputation_decay_rate=0.01,
|
||||
anomaly_detection_threshold=0.3
|
||||
)
|
||||
|
||||
self.session.add(config)
|
||||
self.session.commit()
|
||||
|
||||
return config
|
||||
|
||||
async def _get_active_chain_ids(self) -> List[int]:
|
||||
"""Get list of active chain IDs"""
|
||||
|
||||
try:
|
||||
stmt = select(CrossChainReputationConfig.chain_id).where(
|
||||
CrossChainReputationConfig.is_active == True
|
||||
)
|
||||
|
||||
configs = self.session.exec(stmt).all()
|
||||
return [config.chain_id for config in configs]
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting active chain IDs: {e}")
|
||||
return [1] # Default to Ethereum mainnet
|
||||
|
||||
async def _update_cross_chain_aggregation(self, agent_id: str) -> None:
|
||||
"""Update cross-chain aggregation for an agent"""
|
||||
|
||||
try:
|
||||
# Get all reputations for the agent
|
||||
stmt = select(AgentReputation).where(AgentReputation.agent_id == agent_id)
|
||||
reputations = self.session.exec(stmt).all()
|
||||
|
||||
if not reputations:
|
||||
return
|
||||
|
||||
# Extract chain scores
|
||||
chain_scores = {}
|
||||
for reputation in reputations:
|
||||
chain_id = getattr(reputation, 'chain_id', 1)
|
||||
chain_scores[chain_id] = reputation.trust_score / 1000.0 # Convert to 0-1 scale
|
||||
|
||||
# Apply weighting
|
||||
weighted_scores = await self.apply_chain_weighting(chain_scores)
|
||||
|
||||
# Calculate aggregation metrics
|
||||
if chain_scores:
|
||||
avg_score = sum(chain_scores.values()) / len(chain_scores)
|
||||
variance = sum((score - avg_score) ** 2 for score in chain_scores.values()) / len(chain_scores)
|
||||
score_range = max(chain_scores.values()) - min(chain_scores.values())
|
||||
consistency_score = max(0.0, 1.0 - (variance / 0.25))
|
||||
else:
|
||||
avg_score = 0.0
|
||||
variance = 0.0
|
||||
score_range = 0.0
|
||||
consistency_score = 1.0
|
||||
|
||||
# Update or create aggregation
|
||||
stmt = select(CrossChainReputationAggregation).where(
|
||||
CrossChainReputationAggregation.agent_id == agent_id
|
||||
)
|
||||
|
||||
aggregation = self.session.exec(stmt).first()
|
||||
|
||||
if aggregation:
|
||||
aggregation.aggregated_score = avg_score
|
||||
aggregation.chain_scores = chain_scores
|
||||
aggregation.active_chains = list(chain_scores.keys())
|
||||
aggregation.score_variance = variance
|
||||
aggregation.score_range = score_range
|
||||
aggregation.consistency_score = consistency_score
|
||||
aggregation.last_updated = datetime.utcnow()
|
||||
else:
|
||||
aggregation = CrossChainReputationAggregation(
|
||||
agent_id=agent_id,
|
||||
aggregated_score=avg_score,
|
||||
chain_scores=chain_scores,
|
||||
active_chains=list(chain_scores.keys()),
|
||||
score_variance=variance,
|
||||
score_range=score_range,
|
||||
consistency_score=consistency_score,
|
||||
verification_status="pending",
|
||||
created_at=datetime.utcnow(),
|
||||
last_updated=datetime.utcnow()
|
||||
)
|
||||
|
||||
self.session.add(aggregation)
|
||||
|
||||
self.session.commit()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating cross-chain aggregation for agent {agent_id}: {e}")
|
||||
|
||||
def _determine_reputation_level(self, score: float) -> str:
|
||||
"""Determine reputation level based on score"""
|
||||
|
||||
# Map to existing reputation levels
|
||||
if score >= 0.9:
|
||||
return "master"
|
||||
elif score >= 0.8:
|
||||
return "expert"
|
||||
elif score >= 0.6:
|
||||
return "advanced"
|
||||
elif score >= 0.4:
|
||||
return "intermediate"
|
||||
elif score >= 0.2:
|
||||
return "beginner"
|
||||
else:
|
||||
return "beginner"
|
||||
476
apps/coordinator-api/src/app/reputation/engine.py
Normal file
476
apps/coordinator-api/src/app/reputation/engine.py
Normal file
@@ -0,0 +1,476 @@
|
||||
"""
|
||||
Cross-Chain Reputation Engine
|
||||
Core reputation calculation and aggregation engine for multi-chain agent reputation
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import math
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, List, Optional, Any, Tuple
|
||||
from uuid import uuid4
|
||||
import json
|
||||
from aitbc.logging import get_logger
|
||||
|
||||
from sqlmodel import Session, select, update, delete, func
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
from ..domain.reputation import AgentReputation, ReputationEvent, ReputationLevel
|
||||
from ..domain.cross_chain_reputation import (
|
||||
CrossChainReputationAggregation, CrossChainReputationEvent,
|
||||
CrossChainReputationConfig, ReputationMetrics
|
||||
)
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class CrossChainReputationEngine:
|
||||
"""Core reputation calculation and aggregation engine"""
|
||||
|
||||
def __init__(self, session: Session):
|
||||
self.session = session
|
||||
|
||||
async def calculate_reputation_score(
|
||||
self,
|
||||
agent_id: str,
|
||||
chain_id: int,
|
||||
transaction_data: Optional[Dict[str, Any]] = None
|
||||
) -> float:
|
||||
"""Calculate reputation score for an agent on a specific chain"""
|
||||
|
||||
try:
|
||||
# Get existing reputation
|
||||
stmt = select(AgentReputation).where(
|
||||
AgentReputation.agent_id == agent_id,
|
||||
AgentReputation.chain_id == chain_id if hasattr(AgentReputation, 'chain_id') else True
|
||||
)
|
||||
|
||||
# Handle case where existing reputation doesn't have chain_id
|
||||
if not hasattr(AgentReputation, 'chain_id'):
|
||||
stmt = select(AgentReputation).where(AgentReputation.agent_id == agent_id)
|
||||
|
||||
reputation = self.session.exec(stmt).first()
|
||||
|
||||
if reputation:
|
||||
# Update existing reputation based on transaction data
|
||||
score = await self._update_reputation_from_transaction(reputation, transaction_data)
|
||||
else:
|
||||
# Create new reputation with base score
|
||||
config = await self._get_chain_config(chain_id)
|
||||
base_score = config.base_reputation_bonus if config else 0.0
|
||||
score = max(0.0, min(1.0, base_score))
|
||||
|
||||
# Create new reputation record
|
||||
new_reputation = AgentReputation(
|
||||
agent_id=agent_id,
|
||||
trust_score=score * 1000, # Convert to 0-1000 scale
|
||||
reputation_level=self._determine_reputation_level(score),
|
||||
created_at=datetime.utcnow(),
|
||||
updated_at=datetime.utcnow()
|
||||
)
|
||||
|
||||
self.session.add(new_reputation)
|
||||
self.session.commit()
|
||||
|
||||
return score
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating reputation for agent {agent_id} on chain {chain_id}: {e}")
|
||||
return 0.0
|
||||
|
||||
async def aggregate_cross_chain_reputation(self, agent_id: str) -> Dict[int, float]:
|
||||
"""Aggregate reputation scores across all chains for an agent"""
|
||||
|
||||
try:
|
||||
# Get all reputation records for the agent
|
||||
stmt = select(AgentReputation).where(AgentReputation.agent_id == agent_id)
|
||||
reputations = self.session.exec(stmt).all()
|
||||
|
||||
if not reputations:
|
||||
return {}
|
||||
|
||||
# Get chain configurations
|
||||
chain_configs = {}
|
||||
for reputation in reputations:
|
||||
chain_id = getattr(reputation, 'chain_id', 1) # Default to chain 1 if not set
|
||||
config = await self._get_chain_config(chain_id)
|
||||
chain_configs[chain_id] = config
|
||||
|
||||
# Calculate weighted scores
|
||||
chain_scores = {}
|
||||
total_weight = 0.0
|
||||
weighted_sum = 0.0
|
||||
|
||||
for reputation in reputations:
|
||||
chain_id = getattr(reputation, 'chain_id', 1)
|
||||
config = chain_configs.get(chain_id)
|
||||
|
||||
if config and config.is_active:
|
||||
# Convert trust score to 0-1 scale
|
||||
score = min(1.0, reputation.trust_score / 1000.0)
|
||||
weight = config.chain_weight
|
||||
|
||||
chain_scores[chain_id] = score
|
||||
total_weight += weight
|
||||
weighted_sum += score * weight
|
||||
|
||||
# Normalize scores
|
||||
if total_weight > 0:
|
||||
normalized_scores = {
|
||||
chain_id: score * (total_weight / len(chain_scores))
|
||||
for chain_id, score in chain_scores.items()
|
||||
}
|
||||
else:
|
||||
normalized_scores = chain_scores
|
||||
|
||||
# Store aggregation
|
||||
await self._store_cross_chain_aggregation(agent_id, chain_scores, normalized_scores)
|
||||
|
||||
return chain_scores
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error aggregating cross-chain reputation for agent {agent_id}: {e}")
|
||||
return {}
|
||||
|
||||
async def update_reputation_from_event(self, event_data: Dict[str, Any]) -> bool:
|
||||
"""Update reputation from a reputation-affecting event"""
|
||||
|
||||
try:
|
||||
agent_id = event_data['agent_id']
|
||||
chain_id = event_data.get('chain_id', 1)
|
||||
event_type = event_data['event_type']
|
||||
impact_score = event_data['impact_score']
|
||||
|
||||
# Get existing reputation
|
||||
stmt = select(AgentReputation).where(
|
||||
AgentReputation.agent_id == agent_id,
|
||||
AgentReputation.chain_id == chain_id if hasattr(AgentReputation, 'chain_id') else True
|
||||
)
|
||||
|
||||
if not hasattr(AgentReputation, 'chain_id'):
|
||||
stmt = select(AgentReputation).where(AgentReputation.agent_id == agent_id)
|
||||
|
||||
reputation = self.session.exec(stmt).first()
|
||||
|
||||
if not reputation:
|
||||
# Create new reputation record
|
||||
config = await self._get_chain_config(chain_id)
|
||||
base_score = config.base_reputation_bonus if config else 0.0
|
||||
|
||||
reputation = AgentReputation(
|
||||
agent_id=agent_id,
|
||||
trust_score=max(0, min(1000, (base_score + impact_score) * 1000)),
|
||||
reputation_level=self._determine_reputation_level(base_score + impact_score),
|
||||
created_at=datetime.utcnow(),
|
||||
updated_at=datetime.utcnow()
|
||||
)
|
||||
|
||||
self.session.add(reputation)
|
||||
else:
|
||||
# Update existing reputation
|
||||
old_score = reputation.trust_score / 1000.0
|
||||
new_score = max(0.0, min(1.0, old_score + impact_score))
|
||||
|
||||
reputation.trust_score = new_score * 1000
|
||||
reputation.reputation_level = self._determine_reputation_level(new_score)
|
||||
reputation.updated_at = datetime.utcnow()
|
||||
|
||||
# Create reputation event record
|
||||
event = ReputationEvent(
|
||||
agent_id=agent_id,
|
||||
event_type=event_type,
|
||||
impact_score=impact_score,
|
||||
trust_score_before=reputation.trust_score - (impact_score * 1000),
|
||||
trust_score_after=reputation.trust_score,
|
||||
event_data=event_data,
|
||||
occurred_at=datetime.utcnow()
|
||||
)
|
||||
|
||||
self.session.add(event)
|
||||
self.session.commit()
|
||||
|
||||
# Update cross-chain aggregation
|
||||
await self.aggregate_cross_chain_reputation(agent_id)
|
||||
|
||||
logger.info(f"Updated reputation for agent {agent_id} from {event_type} event")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating reputation from event: {e}")
|
||||
return False
|
||||
|
||||
async def get_reputation_trend(self, agent_id: str, days: int = 30) -> List[float]:
|
||||
"""Get reputation trend for an agent over specified days"""
|
||||
|
||||
try:
|
||||
# Get reputation events for the period
|
||||
cutoff_date = datetime.utcnow() - timedelta(days=days)
|
||||
|
||||
stmt = select(ReputationEvent).where(
|
||||
ReputationEvent.agent_id == agent_id,
|
||||
ReputationEvent.occurred_at >= cutoff_date
|
||||
).order_by(ReputationEvent.occurred_at)
|
||||
|
||||
events = self.session.exec(stmt).all()
|
||||
|
||||
# Extract scores from events
|
||||
scores = []
|
||||
for event in events:
|
||||
if event.trust_score_after is not None:
|
||||
scores.append(event.trust_score_after / 1000.0) # Convert to 0-1 scale
|
||||
|
||||
return scores
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting reputation trend for agent {agent_id}: {e}")
|
||||
return []
|
||||
|
||||
async def detect_reputation_anomalies(self, agent_id: str) -> List[Dict[str, Any]]:
|
||||
"""Detect reputation anomalies for an agent"""
|
||||
|
||||
try:
|
||||
anomalies = []
|
||||
|
||||
# Get recent reputation events
|
||||
stmt = select(ReputationEvent).where(
|
||||
ReputationEvent.agent_id == agent_id
|
||||
).order_by(ReputationEvent.occurred_at.desc()).limit(10)
|
||||
|
||||
events = self.session.exec(stmt).all()
|
||||
|
||||
if len(events) < 2:
|
||||
return anomalies
|
||||
|
||||
# Check for sudden score changes
|
||||
for i in range(len(events) - 1):
|
||||
current_event = events[i]
|
||||
previous_event = events[i + 1]
|
||||
|
||||
if current_event.trust_score_after and previous_event.trust_score_after:
|
||||
score_change = abs(current_event.trust_score_after - previous_event.trust_score_after) / 1000.0
|
||||
|
||||
if score_change > 0.3: # 30% change threshold
|
||||
anomalies.append({
|
||||
'agent_id': agent_id,
|
||||
'chain_id': getattr(current_event, 'chain_id', 1),
|
||||
'anomaly_type': 'sudden_score_change',
|
||||
'detected_at': current_event.occurred_at,
|
||||
'description': f"Sudden reputation change of {score_change:.2f}",
|
||||
'severity': 'high' if score_change > 0.5 else 'medium',
|
||||
'previous_score': previous_event.trust_score_after / 1000.0,
|
||||
'current_score': current_event.trust_score_after / 1000.0,
|
||||
'score_change': score_change,
|
||||
'confidence': min(1.0, score_change / 0.3)
|
||||
})
|
||||
|
||||
return anomalies
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error detecting reputation anomalies for agent {agent_id}: {e}")
|
||||
return []
|
||||
|
||||
async def _update_reputation_from_transaction(
|
||||
self,
|
||||
reputation: AgentReputation,
|
||||
transaction_data: Optional[Dict[str, Any]]
|
||||
) -> float:
|
||||
"""Update reputation based on transaction data"""
|
||||
|
||||
if not transaction_data:
|
||||
return reputation.trust_score / 1000.0
|
||||
|
||||
# Extract transaction metrics
|
||||
success = transaction_data.get('success', True)
|
||||
gas_efficiency = transaction_data.get('gas_efficiency', 0.5)
|
||||
response_time = transaction_data.get('response_time', 1.0)
|
||||
|
||||
# Calculate impact based on transaction outcome
|
||||
config = await self._get_chain_config(getattr(reputation, 'chain_id', 1))
|
||||
|
||||
if success:
|
||||
impact = config.transaction_success_weight if config else 0.1
|
||||
impact *= gas_efficiency # Bonus for gas efficiency
|
||||
impact *= (2.0 - min(response_time, 2.0)) # Bonus for fast response
|
||||
else:
|
||||
impact = config.transaction_failure_weight if config else -0.2
|
||||
|
||||
# Update reputation
|
||||
old_score = reputation.trust_score / 1000.0
|
||||
new_score = max(0.0, min(1.0, old_score + impact))
|
||||
|
||||
reputation.trust_score = new_score * 1000
|
||||
reputation.reputation_level = self._determine_reputation_level(new_score)
|
||||
reputation.updated_at = datetime.utcnow()
|
||||
|
||||
# Update transaction metrics if available
|
||||
if 'transaction_count' in transaction_data:
|
||||
reputation.transaction_count = transaction_data['transaction_count']
|
||||
|
||||
self.session.commit()
|
||||
|
||||
return new_score
|
||||
|
||||
async def _get_chain_config(self, chain_id: int) -> Optional[CrossChainReputationConfig]:
|
||||
"""Get configuration for a specific chain"""
|
||||
|
||||
stmt = select(CrossChainReputationConfig).where(
|
||||
CrossChainReputationConfig.chain_id == chain_id,
|
||||
CrossChainReputationConfig.is_active == True
|
||||
)
|
||||
|
||||
config = self.session.exec(stmt).first()
|
||||
|
||||
if not config:
|
||||
# Create default config
|
||||
config = CrossChainReputationConfig(
|
||||
chain_id=chain_id,
|
||||
chain_weight=1.0,
|
||||
base_reputation_bonus=0.0,
|
||||
transaction_success_weight=0.1,
|
||||
transaction_failure_weight=-0.2,
|
||||
dispute_penalty_weight=-0.3,
|
||||
minimum_transactions_for_score=5,
|
||||
reputation_decay_rate=0.01,
|
||||
anomaly_detection_threshold=0.3
|
||||
)
|
||||
|
||||
self.session.add(config)
|
||||
self.session.commit()
|
||||
|
||||
return config
|
||||
|
||||
async def _store_cross_chain_aggregation(
|
||||
self,
|
||||
agent_id: str,
|
||||
chain_scores: Dict[int, float],
|
||||
normalized_scores: Dict[int, float]
|
||||
) -> None:
|
||||
"""Store cross-chain reputation aggregation"""
|
||||
|
||||
try:
|
||||
# Calculate aggregation metrics
|
||||
if chain_scores:
|
||||
avg_score = sum(chain_scores.values()) / len(chain_scores)
|
||||
variance = sum((score - avg_score) ** 2 for score in chain_scores.values()) / len(chain_scores)
|
||||
score_range = max(chain_scores.values()) - min(chain_scores.values())
|
||||
consistency_score = max(0.0, 1.0 - (variance / 0.25)) # Normalize variance
|
||||
else:
|
||||
avg_score = 0.0
|
||||
variance = 0.0
|
||||
score_range = 0.0
|
||||
consistency_score = 1.0
|
||||
|
||||
# Check if aggregation already exists
|
||||
stmt = select(CrossChainReputationAggregation).where(
|
||||
CrossChainReputationAggregation.agent_id == agent_id
|
||||
)
|
||||
|
||||
aggregation = self.session.exec(stmt).first()
|
||||
|
||||
if aggregation:
|
||||
# Update existing aggregation
|
||||
aggregation.aggregated_score = avg_score
|
||||
aggregation.chain_scores = chain_scores
|
||||
aggregation.active_chains = list(chain_scores.keys())
|
||||
aggregation.score_variance = variance
|
||||
aggregation.score_range = score_range
|
||||
aggregation.consistency_score = consistency_score
|
||||
aggregation.last_updated = datetime.utcnow()
|
||||
else:
|
||||
# Create new aggregation
|
||||
aggregation = CrossChainReputationAggregation(
|
||||
agent_id=agent_id,
|
||||
aggregated_score=avg_score,
|
||||
chain_scores=chain_scores,
|
||||
active_chains=list(chain_scores.keys()),
|
||||
score_variance=variance,
|
||||
score_range=score_range,
|
||||
consistency_score=consistency_score,
|
||||
verification_status="pending",
|
||||
created_at=datetime.utcnow(),
|
||||
last_updated=datetime.utcnow()
|
||||
)
|
||||
|
||||
self.session.add(aggregation)
|
||||
|
||||
self.session.commit()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error storing cross-chain aggregation for agent {agent_id}: {e}")
|
||||
|
||||
def _determine_reputation_level(self, score: float) -> ReputationLevel:
|
||||
"""Determine reputation level based on score"""
|
||||
|
||||
if score >= 0.9:
|
||||
return ReputationLevel.MASTER
|
||||
elif score >= 0.8:
|
||||
return ReputationLevel.EXPERT
|
||||
elif score >= 0.6:
|
||||
return ReputationLevel.ADVANCED
|
||||
elif score >= 0.4:
|
||||
return ReputationLevel.INTERMEDIATE
|
||||
elif score >= 0.2:
|
||||
return ReputationLevel.BEGINNER
|
||||
else:
|
||||
return ReputationLevel.BEGINNER # Map to existing levels
|
||||
|
||||
async def get_agent_reputation_summary(self, agent_id: str) -> Dict[str, Any]:
|
||||
"""Get comprehensive reputation summary for an agent"""
|
||||
|
||||
try:
|
||||
# Get basic reputation
|
||||
stmt = select(AgentReputation).where(AgentReputation.agent_id == agent_id)
|
||||
reputation = self.session.exec(stmt).first()
|
||||
|
||||
if not reputation:
|
||||
return {
|
||||
'agent_id': agent_id,
|
||||
'trust_score': 0.0,
|
||||
'reputation_level': ReputationLevel.BEGINNER,
|
||||
'total_transactions': 0,
|
||||
'success_rate': 0.0,
|
||||
'cross_chain': {
|
||||
'aggregated_score': 0.0,
|
||||
'chain_count': 0,
|
||||
'active_chains': [],
|
||||
'consistency_score': 1.0
|
||||
}
|
||||
}
|
||||
|
||||
# Get cross-chain aggregation
|
||||
stmt = select(CrossChainReputationAggregation).where(
|
||||
CrossChainReputationAggregation.agent_id == agent_id
|
||||
)
|
||||
aggregation = self.session.exec(stmt).first()
|
||||
|
||||
# Get reputation trend
|
||||
trend = await self.get_reputation_trend(agent_id, 30)
|
||||
|
||||
# Get anomalies
|
||||
anomalies = await self.detect_reputation_anomalies(agent_id)
|
||||
|
||||
return {
|
||||
'agent_id': agent_id,
|
||||
'trust_score': reputation.trust_score,
|
||||
'reputation_level': reputation.reputation_level,
|
||||
'performance_rating': getattr(reputation, 'performance_rating', 3.0),
|
||||
'reliability_score': getattr(reputation, 'reliability_score', 50.0),
|
||||
'total_transactions': getattr(reputation, 'transaction_count', 0),
|
||||
'success_rate': getattr(reputation, 'success_rate', 0.0),
|
||||
'dispute_count': getattr(reputation, 'dispute_count', 0),
|
||||
'last_activity': getattr(reputation, 'last_activity', datetime.utcnow()),
|
||||
'cross_chain': {
|
||||
'aggregated_score': aggregation.aggregated_score if aggregation else 0.0,
|
||||
'chain_count': aggregation.chain_count if aggregation else 0,
|
||||
'active_chains': aggregation.active_chains if aggregation else [],
|
||||
'consistency_score': aggregation.consistency_score if aggregation else 1.0,
|
||||
'chain_scores': aggregation.chain_scores if aggregation else {}
|
||||
},
|
||||
'trend': trend,
|
||||
'anomalies': anomalies,
|
||||
'created_at': reputation.created_at,
|
||||
'updated_at': reputation.updated_at
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting reputation summary for agent {agent_id}: {e}")
|
||||
return {'agent_id': agent_id, 'error': str(e)}
|
||||
Reference in New Issue
Block a user