fix: replace datetime.UTC with timezone.utc for Python 3.12+ compatibility
Some checks failed
API Endpoint Tests / test-api-endpoints (push) Successful in 22s
Blockchain Synchronization Verification / sync-verification (push) Successful in 3s
CLI Tests / test-cli (push) Failing after 13s
Cross-Chain Functionality Tests / test-cross-chain-sync (push) Failing after 3s
Cross-Chain Functionality Tests / test-cross-chain-transactions (push) Successful in 3s
Cross-Chain Functionality Tests / test-cross-chain-bridge (push) Has been skipped
Cross-Chain Functionality Tests / test-multi-chain-consensus (push) Failing after 3s
Cross-Chain Functionality Tests / aggregate-results (push) Has been skipped
Cross-Node Transaction Testing / transaction-test (push) Successful in 2s
Deploy to Testnet / deploy-testnet (push) Successful in 1m34s
Documentation Validation / validate-docs (push) Failing after 10s
Documentation Validation / validate-policies-strict (push) Successful in 3s
Multi-Node Stress Testing / stress-test (push) Has been cancelled
Node Failover Simulation / failover-test (push) Has been cancelled
Python Tests / test-python (push) Has been cancelled
Integration Tests / test-service-integration (push) Successful in 2m42s
Multi-Chain Island Architecture Tests / test-multi-chain-island (push) Successful in 3s
Multi-Node Blockchain Health Monitoring / health-check (push) Successful in 5s
P2P Network Verification / p2p-verification (push) Successful in 3s
Package Tests / Python package - aitbc-agent-sdk (push) Failing after 33s
Package Tests / Python package - aitbc-core (push) Successful in 17s
Package Tests / Python package - aitbc-crypto (push) Successful in 11s
Security Scanning / security-scan (push) Has been cancelled
Package Tests / Python package - aitbc-sdk (push) Successful in 13s
Package Tests / JavaScript package - aitbc-sdk-js (push) Successful in 9s
Package Tests / JavaScript package - aitbc-token (push) Successful in 17s
Staking Tests / test-staking-service (push) Failing after 6s
Staking Tests / test-staking-integration (push) Has been skipped
Staking Tests / test-staking-contract (push) Has been skipped
Staking Tests / run-staking-test-runner (push) Has been skipped
Some checks failed
API Endpoint Tests / test-api-endpoints (push) Successful in 22s
Blockchain Synchronization Verification / sync-verification (push) Successful in 3s
CLI Tests / test-cli (push) Failing after 13s
Cross-Chain Functionality Tests / test-cross-chain-sync (push) Failing after 3s
Cross-Chain Functionality Tests / test-cross-chain-transactions (push) Successful in 3s
Cross-Chain Functionality Tests / test-cross-chain-bridge (push) Has been skipped
Cross-Chain Functionality Tests / test-multi-chain-consensus (push) Failing after 3s
Cross-Chain Functionality Tests / aggregate-results (push) Has been skipped
Cross-Node Transaction Testing / transaction-test (push) Successful in 2s
Deploy to Testnet / deploy-testnet (push) Successful in 1m34s
Documentation Validation / validate-docs (push) Failing after 10s
Documentation Validation / validate-policies-strict (push) Successful in 3s
Multi-Node Stress Testing / stress-test (push) Has been cancelled
Node Failover Simulation / failover-test (push) Has been cancelled
Python Tests / test-python (push) Has been cancelled
Integration Tests / test-service-integration (push) Successful in 2m42s
Multi-Chain Island Architecture Tests / test-multi-chain-island (push) Successful in 3s
Multi-Node Blockchain Health Monitoring / health-check (push) Successful in 5s
P2P Network Verification / p2p-verification (push) Successful in 3s
Package Tests / Python package - aitbc-agent-sdk (push) Failing after 33s
Package Tests / Python package - aitbc-core (push) Successful in 17s
Package Tests / Python package - aitbc-crypto (push) Successful in 11s
Security Scanning / security-scan (push) Has been cancelled
Package Tests / Python package - aitbc-sdk (push) Successful in 13s
Package Tests / JavaScript package - aitbc-sdk-js (push) Successful in 9s
Package Tests / JavaScript package - aitbc-token (push) Successful in 17s
Staking Tests / test-staking-service (push) Failing after 6s
Staking Tests / test-staking-integration (push) Has been skipped
Staking Tests / test-staking-contract (push) Has been skipped
Staking Tests / run-staking-test-runner (push) Has been skipped
This commit is contained in:
@@ -8,7 +8,7 @@ import asyncio
|
|||||||
import aiohttp
|
import aiohttp
|
||||||
import json
|
import json
|
||||||
from typing import Dict, Any, List, Optional
|
from typing import Dict, Any, List, Optional
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
class AITBCServiceIntegration:
|
class AITBCServiceIntegration:
|
||||||
"""Integration layer for AITBC services"""
|
"""Integration layer for AITBC services"""
|
||||||
@@ -110,7 +110,7 @@ class AgentServiceBridge:
|
|||||||
self.active_agents[agent_id] = {
|
self.active_agents[agent_id] = {
|
||||||
"config": agent_config,
|
"config": agent_config,
|
||||||
"registration": registration_result,
|
"registration": registration_result,
|
||||||
"started_at": datetime.now(datetime.UTC)
|
"started_at": datetime.now(timezone.utc)
|
||||||
}
|
}
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@@ -182,7 +182,7 @@ class AgentServiceBridge:
|
|||||||
"volatility": "medium",
|
"volatility": "medium",
|
||||||
"recommendation": "hold"
|
"recommendation": "hold"
|
||||||
},
|
},
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat()
|
"timestamp": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
return {"status": "success", "result": analysis_result}
|
return {"status": "success", "result": analysis_result}
|
||||||
@@ -221,7 +221,7 @@ class AgentServiceBridge:
|
|||||||
"check_type": task_data.get("check_type", "basic"),
|
"check_type": task_data.get("check_type", "basic"),
|
||||||
"status": "passed",
|
"status": "passed",
|
||||||
"checks_performed": ["kyc", "aml", "sanctions"],
|
"checks_performed": ["kyc", "aml", "sanctions"],
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat()
|
"timestamp": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
return {"status": "success", "result": compliance_result}
|
return {"status": "success", "result": compliance_result}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
from typing import Dict, Any, List
|
from typing import Dict, Any, List
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ class ComplianceAgent:
|
|||||||
"alert_type": "compliance_failure",
|
"alert_type": "compliance_failure",
|
||||||
"severity": "high",
|
"severity": "high",
|
||||||
"details": result,
|
"details": result,
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat()
|
"timestamp": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
# In a real implementation, this would send to alert system
|
# In a real implementation, this would send to alert system
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from typing import List, Optional, Dict, Any
|
|||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
import os
|
import os
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
@@ -247,7 +247,7 @@ async def get_task_status():
|
|||||||
},
|
},
|
||||||
"queue_sizes": queue_sizes
|
"queue_sizes": queue_sizes
|
||||||
},
|
},
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat()
|
"timestamp": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Agent Management Endpoints
|
# Agent Management Endpoints
|
||||||
@@ -272,7 +272,7 @@ async def register_agent(request: AgentRegistrationRequest):
|
|||||||
json.dumps(request.services),
|
json.dumps(request.services),
|
||||||
json.dumps(request.endpoints),
|
json.dumps(request.endpoints),
|
||||||
json.dumps(request.metadata),
|
json.dumps(request.metadata),
|
||||||
datetime.now(datetime.UTC),
|
datetime.now(timezone.utc),
|
||||||
1.0
|
1.0
|
||||||
))
|
))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
@@ -284,7 +284,7 @@ async def register_agent(request: AgentRegistrationRequest):
|
|||||||
"status": "success",
|
"status": "success",
|
||||||
"message": f"Agent {request.agent_id} registered successfully",
|
"message": f"Agent {request.agent_id} registered successfully",
|
||||||
"agent_id": request.agent_id,
|
"agent_id": request.agent_id,
|
||||||
"registered_at": datetime.now(datetime.UTC).isoformat()
|
"registered_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"ERROR: Failed to register agent: {str(e)}")
|
print(f"ERROR: Failed to register agent: {str(e)}")
|
||||||
@@ -345,7 +345,7 @@ async def discover_agents(query: Dict[str, Any]):
|
|||||||
for agent in agents
|
for agent in agents
|
||||||
],
|
],
|
||||||
"count": len(agents),
|
"count": len(agents),
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat()
|
"timestamp": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Error discovering agents: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"Error discovering agents: {str(e)}")
|
||||||
@@ -374,7 +374,7 @@ async def get_agent(agent_id: str):
|
|||||||
"registration_time": agent["registration_time"],
|
"registration_time": agent["registration_time"],
|
||||||
"health_score": agent["health_score"]
|
"health_score": agent["health_score"]
|
||||||
},
|
},
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat()
|
"timestamp": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
raise
|
raise
|
||||||
@@ -390,7 +390,7 @@ async def update_agent_status(agent_id: str, request: AgentStatusUpdate):
|
|||||||
conn.execute('''
|
conn.execute('''
|
||||||
UPDATE agents SET status = ?, last_heartbeat = ?
|
UPDATE agents SET status = ?, last_heartbeat = ?
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
''', (request.status, datetime.now(datetime.UTC), agent_id))
|
''', (request.status, datetime.now(timezone.utc), agent_id))
|
||||||
|
|
||||||
# Update load metrics if provided
|
# Update load metrics if provided
|
||||||
if request.load_metrics:
|
if request.load_metrics:
|
||||||
@@ -404,7 +404,7 @@ async def update_agent_status(agent_id: str, request: AgentStatusUpdate):
|
|||||||
"message": f"Agent {agent_id} status updated",
|
"message": f"Agent {agent_id} status updated",
|
||||||
"agent_id": agent_id,
|
"agent_id": agent_id,
|
||||||
"new_status": request.status,
|
"new_status": request.status,
|
||||||
"updated_at": datetime.now(datetime.UTC).isoformat()
|
"updated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Error updating agent status: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"Error updating agent status: {str(e)}")
|
||||||
@@ -417,12 +417,12 @@ async def agent_heartbeat(agent_id: str):
|
|||||||
conn.execute('''
|
conn.execute('''
|
||||||
UPDATE agents SET last_heartbeat = ?
|
UPDATE agents SET last_heartbeat = ?
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
''', (datetime.now(datetime.UTC), agent_id))
|
''', (datetime.now(timezone.utc), agent_id))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"message": f"Heartbeat received for agent {agent_id}",
|
"message": f"Heartbeat received for agent {agent_id}",
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat()
|
"timestamp": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Error updating heartbeat: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"Error updating heartbeat: {str(e)}")
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Handles message creation, routing, and delivery between agents
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from typing import Dict, Any, Optional, List
|
from typing import Dict, Any, Optional, List
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ class MessageProtocol:
|
|||||||
"receiver_id": receiver_id,
|
"receiver_id": receiver_id,
|
||||||
"message_type": message_type.value,
|
"message_type": message_type.value,
|
||||||
"content": content,
|
"content": content,
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"status": "pending"
|
"status": "pending"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ class MessageProtocol:
|
|||||||
"""Send a message to the receiver"""
|
"""Send a message to the receiver"""
|
||||||
try:
|
try:
|
||||||
message["status"] = "sent"
|
message["status"] = "sent"
|
||||||
message["sent_timestamp"] = datetime.now(datetime.UTC).isoformat()
|
message["sent_timestamp"] = datetime.now(timezone.utc).isoformat()
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
message["status"] = "failed"
|
message["status"] = "failed"
|
||||||
@@ -65,7 +65,7 @@ class MessageProtocol:
|
|||||||
for message in self.messages:
|
for message in self.messages:
|
||||||
if message["message_id"] == message_id:
|
if message["message_id"] == message_id:
|
||||||
message["status"] = "received"
|
message["status"] = "received"
|
||||||
message["received_timestamp"] = datetime.now(datetime.UTC).isoformat()
|
message["received_timestamp"] = datetime.now(timezone.utc).isoformat()
|
||||||
return message
|
return message
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Handles task creation, assignment, and tracking
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from typing import Dict, Any, Optional, List
|
from typing import Dict, Any, Optional, List
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
@@ -42,8 +42,8 @@ class Task:
|
|||||||
self.priority = priority
|
self.priority = priority
|
||||||
self.created_by = created_by or assigned_to
|
self.created_by = created_by or assigned_to
|
||||||
self.status = TaskStatus.PENDING
|
self.status = TaskStatus.PENDING
|
||||||
self.created_at = datetime.now(datetime.UTC)
|
self.created_at = datetime.now(timezone.utc)
|
||||||
self.updated_at = datetime.now(datetime.UTC)
|
self.updated_at = datetime.now(timezone.utc)
|
||||||
self.completed_at = None
|
self.completed_at = None
|
||||||
self.result = None
|
self.result = None
|
||||||
self.error = None
|
self.error = None
|
||||||
@@ -94,10 +94,10 @@ class TaskManager:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
task.status = status
|
task.status = status
|
||||||
task.updated_at = datetime.now(datetime.UTC)
|
task.updated_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
if status == TaskStatus.COMPLETED:
|
if status == TaskStatus.COMPLETED:
|
||||||
task.completed_at = datetime.now(datetime.UTC)
|
task.completed_at = datetime.now(timezone.utc)
|
||||||
task.result = result
|
task.result = result
|
||||||
elif status == TaskStatus.FAILED:
|
elif status == TaskStatus.FAILED:
|
||||||
task.error = error
|
task.error = error
|
||||||
@@ -120,7 +120,7 @@ class TaskManager:
|
|||||||
|
|
||||||
def get_overdue_tasks(self, hours: int = 24) -> List[Task]:
|
def get_overdue_tasks(self, hours: int = 24) -> List[Task]:
|
||||||
"""Get tasks that are overdue"""
|
"""Get tasks that are overdue"""
|
||||||
cutoff_time = datetime.now(datetime.UTC) - timedelta(hours=hours)
|
cutoff_time = datetime.now(timezone.utc) - timedelta(hours=hours)
|
||||||
return [
|
return [
|
||||||
task for task in self.tasks.values()
|
task for task in self.tasks.values()
|
||||||
if task.status in [TaskStatus.PENDING, TaskStatus.IN_PROGRESS] and
|
if task.status in [TaskStatus.PENDING, TaskStatus.IN_PROGRESS] and
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import json
|
|||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
import os
|
import os
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
@@ -149,7 +149,7 @@ async def list_agents(
|
|||||||
@app.get("/api/health")
|
@app.get("/api/health")
|
||||||
async def health_check():
|
async def health_check():
|
||||||
"""Health check endpoint"""
|
"""Health check endpoint"""
|
||||||
return {"status": "ok", "timestamp": datetime.now(datetime.UTC)}
|
return {"status": "ok", "timestamp": datetime.now(timezone.utc)}
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Basic AI-powered trading and analytics
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from typing import Dict, Any, List
|
from typing import Dict, Any, List
|
||||||
@@ -58,7 +58,7 @@ class SimpleAITradingEngine:
|
|||||||
'overall_sentiment': np.random.choice(['bullish', 'bearish', 'neutral'])
|
'overall_sentiment': np.random.choice(['bullish', 'bearish', 'neutral'])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'timestamp': datetime.now(datetime.UTC)
|
'timestamp': datetime.now(timezone.utc)
|
||||||
}
|
}
|
||||||
|
|
||||||
async def make_trading_decision(self, symbol: str) -> Dict[str, Any]:
|
async def make_trading_decision(self, symbol: str) -> Dict[str, Any]:
|
||||||
@@ -90,7 +90,7 @@ class SimpleAITradingEngine:
|
|||||||
'quantity': quantity,
|
'quantity': quantity,
|
||||||
'price': analysis['current_price'],
|
'price': analysis['current_price'],
|
||||||
'reasoning': f"Signal strength: {signal_strength:.3f}",
|
'reasoning': f"Signal strength: {signal_strength:.3f}",
|
||||||
'timestamp': datetime.now(datetime.UTC)
|
'timestamp': datetime.now(timezone.utc)
|
||||||
}
|
}
|
||||||
|
|
||||||
# Global AI engine
|
# Global AI engine
|
||||||
@@ -104,7 +104,7 @@ async def analyze_market(request: AnalysisRequest):
|
|||||||
return {
|
return {
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"analysis": analysis,
|
"analysis": analysis,
|
||||||
"timestamp": datetime.now(datetime.UTC)
|
"timestamp": datetime.now(timezone.utc)
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"status": "error", "message": "Analysis failed"}
|
return {"status": "error", "message": "Analysis failed"}
|
||||||
@@ -118,7 +118,7 @@ async def execute_ai_trade(request: TradingRequest):
|
|||||||
return {
|
return {
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"decision": decision,
|
"decision": decision,
|
||||||
"timestamp": datetime.now(datetime.UTC)
|
"timestamp": datetime.now(timezone.utc)
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"status": "error", "message": "Analysis failed"}
|
return {"status": "error", "message": "Analysis failed"}
|
||||||
@@ -136,7 +136,7 @@ async def predict_market(symbol: str):
|
|||||||
"risk": analysis['ai_predictions']['risk_assessment'],
|
"risk": analysis['ai_predictions']['risk_assessment'],
|
||||||
"sentiment": analysis['ai_predictions']['sentiment_analysis']
|
"sentiment": analysis['ai_predictions']['sentiment_analysis']
|
||||||
},
|
},
|
||||||
"timestamp": datetime.now(datetime.UTC)
|
"timestamp": datetime.now(timezone.utc)
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"status": "error", "message": "Analysis failed"}
|
return {"status": "error", "message": "Analysis failed"}
|
||||||
@@ -152,7 +152,7 @@ async def get_ai_dashboard():
|
|||||||
'total_volume': np.random.uniform(100000, 1000000),
|
'total_volume': np.random.uniform(100000, 1000000),
|
||||||
'active_symbols': len(symbols),
|
'active_symbols': len(symbols),
|
||||||
'ai_models_active': 3,
|
'ai_models_active': 3,
|
||||||
'last_update': datetime.now(datetime.UTC)
|
'last_update': datetime.now(timezone.utc)
|
||||||
},
|
},
|
||||||
'symbol_analysis': {}
|
'symbol_analysis': {}
|
||||||
}
|
}
|
||||||
@@ -169,7 +169,7 @@ async def get_ai_dashboard():
|
|||||||
return {
|
return {
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"dashboard": dashboard_data,
|
"dashboard": dashboard_data,
|
||||||
"timestamp": datetime.now(datetime.UTC)
|
"timestamp": datetime.now(timezone.utc)
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"status": "error", "message": "Analysis failed"}
|
return {"status": "error", "message": "Analysis failed"}
|
||||||
@@ -192,13 +192,13 @@ async def get_ai_status():
|
|||||||
"risk_assessment",
|
"risk_assessment",
|
||||||
"sentiment_analysis"
|
"sentiment_analysis"
|
||||||
],
|
],
|
||||||
"timestamp": datetime.now(datetime.UTC)
|
"timestamp": datetime.now(timezone.utc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/health")
|
@app.get("/api/health")
|
||||||
async def health_check():
|
async def health_check():
|
||||||
"""Health check endpoint"""
|
"""Health check endpoint"""
|
||||||
return {"status": "ok", "timestamp": datetime.now(datetime.UTC)}
|
return {"status": "ok", "timestamp": datetime.now(timezone.utc)}
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import sys
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import Mock, patch, MagicMock
|
from unittest.mock import Mock, patch, MagicMock
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
# Mock numpy before importing
|
# Mock numpy before importing
|
||||||
@@ -68,7 +68,7 @@ async def test_make_trading_decision_extreme_confidence():
|
|||||||
'risk_assessment': {'risk_score': 0.0, 'volatility': 0.01},
|
'risk_assessment': {'risk_score': 0.0, 'volatility': 0.01},
|
||||||
'sentiment_analysis': {'sentiment_score': 1.0, 'overall_sentiment': 'bullish'}
|
'sentiment_analysis': {'sentiment_score': 1.0, 'overall_sentiment': 'bullish'}
|
||||||
},
|
},
|
||||||
'timestamp': datetime.now(datetime.UTC)
|
'timestamp': datetime.now(timezone.utc)
|
||||||
}
|
}
|
||||||
|
|
||||||
result = await engine.make_trading_decision('AITBC/BTC')
|
result = await engine.make_trading_decision('AITBC/BTC')
|
||||||
@@ -155,7 +155,7 @@ async def test_signal_strength_boundary_buy():
|
|||||||
'risk_assessment': {'risk_score': 0.0, 'volatility': 0.01},
|
'risk_assessment': {'risk_score': 0.0, 'volatility': 0.01},
|
||||||
'sentiment_analysis': {'sentiment_score': 0.5, 'overall_sentiment': 'bullish'}
|
'sentiment_analysis': {'sentiment_score': 0.5, 'overall_sentiment': 'bullish'}
|
||||||
},
|
},
|
||||||
'timestamp': datetime.now(datetime.UTC)
|
'timestamp': datetime.now(timezone.utc)
|
||||||
}
|
}
|
||||||
|
|
||||||
result = await engine.make_trading_decision('AITBC/BTC')
|
result = await engine.make_trading_decision('AITBC/BTC')
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import pytest
|
|||||||
import sys
|
import sys
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from unittest.mock import Mock, patch, MagicMock
|
from unittest.mock import Mock, patch, MagicMock
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ def test_get_ai_dashboard_endpoint():
|
|||||||
'risk_assessment': {'risk_score': 0.5, 'volatility': 0.03},
|
'risk_assessment': {'risk_score': 0.5, 'volatility': 0.03},
|
||||||
'sentiment_analysis': {'sentiment_score': 0.5, 'overall_sentiment': 'bullish'}
|
'sentiment_analysis': {'sentiment_score': 0.5, 'overall_sentiment': 'bullish'}
|
||||||
},
|
},
|
||||||
'timestamp': datetime.now(datetime.UTC)
|
'timestamp': datetime.now(timezone.utc)
|
||||||
}
|
}
|
||||||
|
|
||||||
mock_decision.return_value = {
|
mock_decision.return_value = {
|
||||||
@@ -108,7 +108,7 @@ def test_get_ai_dashboard_endpoint():
|
|||||||
'quantity': 500,
|
'quantity': 500,
|
||||||
'price': 0.005,
|
'price': 0.005,
|
||||||
'reasoning': 'Test reasoning',
|
'reasoning': 'Test reasoning',
|
||||||
'timestamp': datetime.now(datetime.UTC)
|
'timestamp': datetime.now(timezone.utc)
|
||||||
}
|
}
|
||||||
|
|
||||||
response = client.get("/api/ai/dashboard")
|
response = client.get("/api/ai/dashboard")
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ sys.path.insert(0, 'src')
|
|||||||
|
|
||||||
from aitbc_chain.database import session_scope, init_db
|
from aitbc_chain.database import session_scope, init_db
|
||||||
from aitbc_chain.models import Block
|
from aitbc_chain.models import Block
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
def compute_block_hash(height: int, parent_hash: str, timestamp: datetime) -> str:
|
def compute_block_hash(height: int, parent_hash: str, timestamp: datetime) -> str:
|
||||||
@@ -31,7 +31,7 @@ def create_genesis():
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Create genesis block
|
# Create genesis block
|
||||||
timestamp = datetime.now(datetime.UTC)
|
timestamp = datetime.now(timezone.utc)
|
||||||
genesis_hash = compute_block_hash(0, "0x00", timestamp)
|
genesis_hash = compute_block_hash(0, "0x00", timestamp)
|
||||||
genesis = Block(
|
genesis = Block(
|
||||||
height=0,
|
height=0,
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
from aitbc_chain.database import session_scope, init_db
|
from aitbc_chain.database import session_scope, init_db
|
||||||
from aitbc_chain.models import Account
|
from aitbc_chain.models import Account
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
def fix():
|
def fix():
|
||||||
init_db()
|
init_db()
|
||||||
with session_scope() as session:
|
with session_scope() as session:
|
||||||
acc = Account(chain_id="ait-mainnet", address="aitbc1genesis", balance=10000000, nonce=0, updated_at=datetime.now(datetime.UTC), account_type="regular", metadata="{}")
|
acc = Account(chain_id="ait-mainnet", address="aitbc1genesis", balance=10000000, nonce=0, updated_at=datetime.now(timezone.utc), account_type="regular", metadata="{}")
|
||||||
session.merge(acc)
|
session.merge(acc)
|
||||||
session.commit()
|
session.commit()
|
||||||
print("Added aitbc1genesis to mainnet")
|
print("Added aitbc1genesis to mainnet")
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import base64
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from typing import Dict, List, Any, Optional
|
from typing import Dict, List, Any, Optional
|
||||||
|
|
||||||
from cryptography.hazmat.primitives.asymmetric import ed25519
|
from cryptography.hazmat.primitives.asymmetric import ed25519
|
||||||
@@ -233,7 +233,7 @@ def initialize_genesis_database(genesis_block: Dict, allocations: List[Dict], db
|
|||||||
alloc["address"],
|
alloc["address"],
|
||||||
alloc["balance"],
|
alloc["balance"],
|
||||||
alloc["nonce"],
|
alloc["nonce"],
|
||||||
datetime.now(datetime.UTC).isoformat()
|
datetime.now(timezone.utc).isoformat()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ of agent compromise.
|
|||||||
|
|
||||||
from typing import Dict, List, Optional, Tuple
|
from typing import Dict, List, Optional, Tuple
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
import json
|
import json
|
||||||
from eth_account import Account
|
from eth_account import Account
|
||||||
from eth_utils import to_checksum_address
|
from eth_utils import to_checksum_address
|
||||||
@@ -37,7 +37,7 @@ class AgentSecurityProfile:
|
|||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
if self.created_at is None:
|
if self.created_at is None:
|
||||||
self.created_at = datetime.now(datetime.UTC)
|
self.created_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
|
|
||||||
class AgentWalletSecurity:
|
class AgentWalletSecurity:
|
||||||
@@ -423,7 +423,7 @@ class AgentWalletSecurity:
|
|||||||
def _log_security_event(self, **kwargs):
|
def _log_security_event(self, **kwargs):
|
||||||
"""Log a security event"""
|
"""Log a security event"""
|
||||||
event = {
|
event = {
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
**kwargs
|
**kwargs
|
||||||
}
|
}
|
||||||
self.security_events.append(event)
|
self.security_events.append(event)
|
||||||
@@ -469,7 +469,7 @@ class AgentWalletSecurity:
|
|||||||
return {
|
return {
|
||||||
"status": "disabled",
|
"status": "disabled",
|
||||||
"agent_address": agent_address,
|
"agent_address": agent_address,
|
||||||
"disabled_at": datetime.now(datetime.UTC).isoformat(),
|
"disabled_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"guardian": guardian_address
|
"guardian": guardian_address
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,7 +527,7 @@ def generate_security_report() -> Dict:
|
|||||||
recent_events = agent_wallet_security.get_security_events(limit=20)
|
recent_events = agent_wallet_security.get_security_events(limit=20)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat(),
|
"generated_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"summary": {
|
"summary": {
|
||||||
"total_protected_agents": total_agents,
|
"total_protected_agents": total_agents,
|
||||||
"active_agents": active_agents,
|
"active_agents": active_agents,
|
||||||
@@ -580,5 +580,5 @@ def detect_suspicious_activity(agent_address: str, hours: int = 24) -> Dict:
|
|||||||
"suspicious_activity": len(suspicious_patterns) > 0,
|
"suspicious_activity": len(suspicious_patterns) > 0,
|
||||||
"suspicious_patterns": suspicious_patterns,
|
"suspicious_patterns": suspicious_patterns,
|
||||||
"analysis_period_hours": hours,
|
"analysis_period_hours": hours,
|
||||||
"analyzed_at": datetime.now(datetime.UTC).isoformat()
|
"analyzed_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ wallets from unlimited spending in case of compromise. It provides:
|
|||||||
|
|
||||||
from typing import Dict, List, Optional, Tuple
|
from typing import Dict, List, Optional, Tuple
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
@@ -248,7 +248,7 @@ class GuardianContract:
|
|||||||
def _get_spent_in_period(self, period: str, timestamp: datetime = None) -> int:
|
def _get_spent_in_period(self, period: str, timestamp: datetime = None) -> int:
|
||||||
"""Calculate total spent in given period"""
|
"""Calculate total spent in given period"""
|
||||||
if timestamp is None:
|
if timestamp is None:
|
||||||
timestamp = datetime.now(datetime.UTC)
|
timestamp = datetime.now(timezone.utc)
|
||||||
|
|
||||||
period_key = self._get_period_key(timestamp, period)
|
period_key = self._get_period_key(timestamp, period)
|
||||||
|
|
||||||
@@ -265,7 +265,7 @@ class GuardianContract:
|
|||||||
def _check_spending_limits(self, amount: int, timestamp: datetime = None) -> Tuple[bool, str]:
|
def _check_spending_limits(self, amount: int, timestamp: datetime = None) -> Tuple[bool, str]:
|
||||||
"""Check if amount exceeds spending limits"""
|
"""Check if amount exceeds spending limits"""
|
||||||
if timestamp is None:
|
if timestamp is None:
|
||||||
timestamp = datetime.now(datetime.UTC)
|
timestamp = datetime.now(timezone.utc)
|
||||||
|
|
||||||
# Check per-transaction limit
|
# Check per-transaction limit
|
||||||
if amount > self.config.limits.per_transaction:
|
if amount > self.config.limits.per_transaction:
|
||||||
@@ -350,7 +350,7 @@ class GuardianContract:
|
|||||||
"to": to_address,
|
"to": to_address,
|
||||||
"amount": amount,
|
"amount": amount,
|
||||||
"data": data,
|
"data": data,
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"nonce": self.nonce,
|
"nonce": self.nonce,
|
||||||
"status": "pending"
|
"status": "pending"
|
||||||
}
|
}
|
||||||
@@ -360,7 +360,7 @@ class GuardianContract:
|
|||||||
|
|
||||||
# Check if time lock is required
|
# Check if time lock is required
|
||||||
if self._requires_time_lock(amount):
|
if self._requires_time_lock(amount):
|
||||||
unlock_time = datetime.now(datetime.UTC) + timedelta(hours=self.config.time_lock.delay_hours)
|
unlock_time = datetime.now(timezone.utc) + timedelta(hours=self.config.time_lock.delay_hours)
|
||||||
operation["unlock_time"] = unlock_time.isoformat()
|
operation["unlock_time"] = unlock_time.isoformat()
|
||||||
operation["status"] = "time_locked"
|
operation["status"] = "time_locked"
|
||||||
|
|
||||||
@@ -406,7 +406,7 @@ class GuardianContract:
|
|||||||
# Check if operation is time locked
|
# Check if operation is time locked
|
||||||
if operation["status"] == "time_locked":
|
if operation["status"] == "time_locked":
|
||||||
unlock_time = datetime.fromisoformat(operation["unlock_time"])
|
unlock_time = datetime.fromisoformat(operation["unlock_time"])
|
||||||
if datetime.now(datetime.UTC) < unlock_time:
|
if datetime.now(timezone.utc) < unlock_time:
|
||||||
return {
|
return {
|
||||||
"status": "error",
|
"status": "error",
|
||||||
"reason": f"Operation locked until {unlock_time.isoformat()}"
|
"reason": f"Operation locked until {unlock_time.isoformat()}"
|
||||||
@@ -432,7 +432,7 @@ class GuardianContract:
|
|||||||
"amount": operation["amount"],
|
"amount": operation["amount"],
|
||||||
"data": operation.get("data", ""),
|
"data": operation.get("data", ""),
|
||||||
"timestamp": operation["timestamp"],
|
"timestamp": operation["timestamp"],
|
||||||
"executed_at": datetime.now(datetime.UTC).isoformat(),
|
"executed_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"nonce": operation["nonce"]
|
"nonce": operation["nonce"]
|
||||||
}
|
}
|
||||||
@@ -479,7 +479,7 @@ class GuardianContract:
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"status": "paused",
|
"status": "paused",
|
||||||
"paused_at": datetime.now(datetime.UTC).isoformat(),
|
"paused_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"guardian": guardian_address,
|
"guardian": guardian_address,
|
||||||
"message": "Emergency pause activated - all operations halted"
|
"message": "Emergency pause activated - all operations halted"
|
||||||
}
|
}
|
||||||
@@ -513,7 +513,7 @@ class GuardianContract:
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"status": "unpaused",
|
"status": "unpaused",
|
||||||
"unpaused_at": datetime.now(datetime.UTC).isoformat(),
|
"unpaused_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"message": "Emergency pause lifted - operations resumed"
|
"message": "Emergency pause lifted - operations resumed"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,13 +541,13 @@ class GuardianContract:
|
|||||||
"status": "updated",
|
"status": "updated",
|
||||||
"old_limits": old_limits,
|
"old_limits": old_limits,
|
||||||
"new_limits": new_limits,
|
"new_limits": new_limits,
|
||||||
"updated_at": datetime.now(datetime.UTC).isoformat(),
|
"updated_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"guardian": guardian_address
|
"guardian": guardian_address
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_spending_status(self) -> Dict:
|
def get_spending_status(self) -> Dict:
|
||||||
"""Get current spending status and limits"""
|
"""Get current spending status and limits"""
|
||||||
now = datetime.now(datetime.UTC)
|
now = datetime.now(timezone.utc)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"agent_address": self.agent_address,
|
"agent_address": self.agent_address,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Fixes the critical vulnerability where spending limits were lost on restart
|
|||||||
|
|
||||||
from typing import Dict, List, Optional, Tuple
|
from typing import Dict, List, Optional, Tuple
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from sqlalchemy import create_engine, Column, String, Integer, Float, DateTime, Index
|
from sqlalchemy import create_engine, Column, String, Integer, Float, DateTime, Index
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy.orm import sessionmaker, Session
|
from sqlalchemy.orm import sessionmaker, Session
|
||||||
@@ -25,7 +25,7 @@ class SpendingRecord(Base):
|
|||||||
period_key = Column(String, index=True)
|
period_key = Column(String, index=True)
|
||||||
amount = Column(Float)
|
amount = Column(Float)
|
||||||
transaction_hash = Column(String)
|
transaction_hash = Column(String)
|
||||||
timestamp = Column(DateTime, default=datetime.now(datetime.UTC))
|
timestamp = Column(DateTime, default=datetime.now(timezone.utc))
|
||||||
|
|
||||||
# Composite indexes for performance
|
# Composite indexes for performance
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
@@ -45,7 +45,7 @@ class SpendingLimit(Base):
|
|||||||
per_week = Column(Float)
|
per_week = Column(Float)
|
||||||
time_lock_threshold = Column(Float)
|
time_lock_threshold = Column(Float)
|
||||||
time_lock_delay_hours = Column(Integer)
|
time_lock_delay_hours = Column(Integer)
|
||||||
updated_at = Column(DateTime, default=datetime.now(datetime.UTC))
|
updated_at = Column(DateTime, default=datetime.now(timezone.utc))
|
||||||
updated_by = Column(String) # Guardian who updated
|
updated_by = Column(String) # Guardian who updated
|
||||||
|
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ class GuardianAuthorization(Base):
|
|||||||
agent_address = Column(String, index=True)
|
agent_address = Column(String, index=True)
|
||||||
guardian_address = Column(String, index=True)
|
guardian_address = Column(String, index=True)
|
||||||
is_active = Column(Boolean, default=True)
|
is_active = Column(Boolean, default=True)
|
||||||
added_at = Column(DateTime, default=datetime.now(datetime.UTC))
|
added_at = Column(DateTime, default=datetime.now(timezone.utc))
|
||||||
added_by = Column(String)
|
added_by = Column(String)
|
||||||
|
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ class PersistentSpendingTracker:
|
|||||||
Total amount spent in period
|
Total amount spent in period
|
||||||
"""
|
"""
|
||||||
if timestamp is None:
|
if timestamp is None:
|
||||||
timestamp = datetime.now(datetime.UTC)
|
timestamp = datetime.now(timezone.utc)
|
||||||
|
|
||||||
period_key = self._get_period_key(timestamp, period)
|
period_key = self._get_period_key(timestamp, period)
|
||||||
agent_address = to_checksum_address(agent_address)
|
agent_address = to_checksum_address(agent_address)
|
||||||
@@ -140,7 +140,7 @@ class PersistentSpendingTracker:
|
|||||||
True if recorded successfully
|
True if recorded successfully
|
||||||
"""
|
"""
|
||||||
if timestamp is None:
|
if timestamp is None:
|
||||||
timestamp = datetime.now(datetime.UTC)
|
timestamp = datetime.now(timezone.utc)
|
||||||
|
|
||||||
agent_address = to_checksum_address(agent_address)
|
agent_address = to_checksum_address(agent_address)
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ class PersistentSpendingTracker:
|
|||||||
Spending check result
|
Spending check result
|
||||||
"""
|
"""
|
||||||
if timestamp is None:
|
if timestamp is None:
|
||||||
timestamp = datetime.now(datetime.UTC)
|
timestamp = datetime.now(timezone.utc)
|
||||||
|
|
||||||
agent_address = to_checksum_address(agent_address)
|
agent_address = to_checksum_address(agent_address)
|
||||||
|
|
||||||
@@ -312,7 +312,7 @@ class PersistentSpendingTracker:
|
|||||||
limits.per_week = new_limits.get("per_week", limits.per_week)
|
limits.per_week = new_limits.get("per_week", limits.per_week)
|
||||||
limits.time_lock_threshold = new_limits.get("time_lock_threshold", limits.time_lock_threshold)
|
limits.time_lock_threshold = new_limits.get("time_lock_threshold", limits.time_lock_threshold)
|
||||||
limits.time_lock_delay_hours = new_limits.get("time_lock_delay_hours", limits.time_lock_delay_hours)
|
limits.time_lock_delay_hours = new_limits.get("time_lock_delay_hours", limits.time_lock_delay_hours)
|
||||||
limits.updated_at = datetime.now(datetime.UTC)
|
limits.updated_at = datetime.now(timezone.utc)
|
||||||
limits.updated_by = guardian_address
|
limits.updated_by = guardian_address
|
||||||
else:
|
else:
|
||||||
limits = SpendingLimit(
|
limits = SpendingLimit(
|
||||||
@@ -323,7 +323,7 @@ class PersistentSpendingTracker:
|
|||||||
per_week=new_limits.get("per_week", 100000.0),
|
per_week=new_limits.get("per_week", 100000.0),
|
||||||
time_lock_threshold=new_limits.get("time_lock_threshold", 5000.0),
|
time_lock_threshold=new_limits.get("time_lock_threshold", 5000.0),
|
||||||
time_lock_delay_hours=new_limits.get("time_lock_delay_hours", 24),
|
time_lock_delay_hours=new_limits.get("time_lock_delay_hours", 24),
|
||||||
updated_at=datetime.now(datetime.UTC),
|
updated_at=datetime.now(timezone.utc),
|
||||||
updated_by=guardian_address
|
updated_by=guardian_address
|
||||||
)
|
)
|
||||||
session.add(limits)
|
session.add(limits)
|
||||||
@@ -361,7 +361,7 @@ class PersistentSpendingTracker:
|
|||||||
|
|
||||||
if existing:
|
if existing:
|
||||||
existing.is_active = True
|
existing.is_active = True
|
||||||
existing.added_at = datetime.now(datetime.UTC)
|
existing.added_at = datetime.now(timezone.utc)
|
||||||
existing.added_by = added_by
|
existing.added_by = added_by
|
||||||
else:
|
else:
|
||||||
auth = GuardianAuthorization(
|
auth = GuardianAuthorization(
|
||||||
@@ -369,7 +369,7 @@ class PersistentSpendingTracker:
|
|||||||
agent_address=agent_address,
|
agent_address=agent_address,
|
||||||
guardian_address=guardian_address,
|
guardian_address=guardian_address,
|
||||||
is_active=True,
|
is_active=True,
|
||||||
added_at=datetime.now(datetime.UTC),
|
added_at=datetime.now(timezone.utc),
|
||||||
added_by=added_by
|
added_by=added_by
|
||||||
)
|
)
|
||||||
session.add(auth)
|
session.add(auth)
|
||||||
@@ -415,7 +415,7 @@ class PersistentSpendingTracker:
|
|||||||
Spending summary
|
Spending summary
|
||||||
"""
|
"""
|
||||||
agent_address = to_checksum_address(agent_address)
|
agent_address = to_checksum_address(agent_address)
|
||||||
now = datetime.now(datetime.UTC)
|
now = datetime.now(timezone.utc)
|
||||||
|
|
||||||
# Get current spending
|
# Get current spending
|
||||||
current_spent = {
|
current_spent = {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""Cross-chain synchronization for testing multi-chain scenarios."""
|
"""Cross-chain synchronization for testing multi-chain scenarios."""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import asyncio
|
|||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
from typing import Any, Dict, Optional, List
|
from typing import Any, Dict, Optional, List
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, status
|
from fastapi import APIRouter, HTTPException, status
|
||||||
from pydantic import BaseModel, Field, model_validator
|
from pydantic import BaseModel, Field, model_validator
|
||||||
@@ -478,12 +478,12 @@ async def submit_marketplace_transaction(tx_data: Dict[str, Any]) -> Dict[str, A
|
|||||||
sender=sender_addr,
|
sender=sender_addr,
|
||||||
recipient=recipient_addr,
|
recipient=recipient_addr,
|
||||||
payload=tx_data.get("payload", {}),
|
payload=tx_data.get("payload", {}),
|
||||||
created_at=datetime.now(datetime.UTC),
|
created_at=datetime.now(timezone.utc),
|
||||||
nonce=tx_nonce,
|
nonce=tx_nonce,
|
||||||
value=amount,
|
value=amount,
|
||||||
fee=fee,
|
fee=fee,
|
||||||
status="pending",
|
status="pending",
|
||||||
timestamp=datetime.now(datetime.UTC).isoformat()
|
timestamp=datetime.now(timezone.utc).isoformat()
|
||||||
)
|
)
|
||||||
session.add(transaction)
|
session.add(transaction)
|
||||||
|
|
||||||
@@ -798,9 +798,9 @@ async def import_block(block_data: dict) -> Dict[str, Any]:
|
|||||||
try:
|
try:
|
||||||
timestamp = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
|
timestamp = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
timestamp = datetime.now(datetime.UTC)
|
timestamp = datetime.now(timezone.utc)
|
||||||
elif timestamp is None:
|
elif timestamp is None:
|
||||||
timestamp = datetime.now(datetime.UTC)
|
timestamp = datetime.now(timezone.utc)
|
||||||
|
|
||||||
with session_scope(chain_id) as session:
|
with session_scope(chain_id) as session:
|
||||||
# Check for hash conflicts across chains
|
# Check for hash conflicts across chains
|
||||||
@@ -1046,7 +1046,7 @@ async def import_chain(import_data: dict) -> Dict[str, Any]:
|
|||||||
_logger.info(f"Importing {len(unique_blocks)} unique blocks (filtered from {len(blocks)} total)")
|
_logger.info(f"Importing {len(unique_blocks)} unique blocks (filtered from {len(blocks)} total)")
|
||||||
|
|
||||||
for block_data in unique_blocks:
|
for block_data in unique_blocks:
|
||||||
block_timestamp = _parse_datetime_value(block_data.get("timestamp"), "block timestamp") or datetime.now(datetime.UTC)
|
block_timestamp = _parse_datetime_value(block_data.get("timestamp"), "block timestamp") or datetime.now(timezone.utc)
|
||||||
block = Block(
|
block = Block(
|
||||||
chain_id=chain_id,
|
chain_id=chain_id,
|
||||||
height=block_data["height"],
|
height=block_data["height"],
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from typing import Dict, List, Optional, Tuple
|
|||||||
|
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
from sqlalchemy import select, text
|
from sqlalchemy import select, text
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
from ..models import Account, Transaction, Receipt
|
from ..models import Account, Transaction, Receipt
|
||||||
from ..logger import get_logger
|
from ..logger import get_logger
|
||||||
@@ -242,7 +242,7 @@ class StateTransition:
|
|||||||
|
|
||||||
# Update receipt status
|
# Update receipt status
|
||||||
receipt.status = "claimed"
|
receipt.status = "claimed"
|
||||||
receipt.claimed_at = datetime.now(datetime.UTC)
|
receipt.claimed_at = datetime.now(timezone.utc)
|
||||||
receipt.claimed_by = sender_addr
|
receipt.claimed_by = sender_addr
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import hmac
|
|||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
@@ -508,9 +508,9 @@ class ChainSync:
|
|||||||
block_hash = block_data["hash"]
|
block_hash = block_data["hash"]
|
||||||
timestamp_str = block_data.get("timestamp", "")
|
timestamp_str = block_data.get("timestamp", "")
|
||||||
try:
|
try:
|
||||||
timestamp = datetime.fromisoformat(timestamp_str) if timestamp_str else datetime.now(datetime.UTC)
|
timestamp = datetime.fromisoformat(timestamp_str) if timestamp_str else datetime.now(timezone.utc)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
timestamp = datetime.now(datetime.UTC)
|
timestamp = datetime.now(timezone.utc)
|
||||||
|
|
||||||
tx_count = block_data.get("tx_count", 0)
|
tx_count = block_data.get("tx_count", 0)
|
||||||
if transactions:
|
if transactions:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|||||||
import sys
|
import sys
|
||||||
import asyncio
|
import asyncio
|
||||||
import pytest
|
import pytest
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from unittest.mock import AsyncMock, Mock, patch
|
from unittest.mock import AsyncMock, Mock, patch
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
|
|
||||||
@@ -192,7 +192,7 @@ class TestPoAProposer:
|
|||||||
hash="0xparent",
|
hash="0xparent",
|
||||||
parent_hash="0x00",
|
parent_hash="0x00",
|
||||||
proposer="previous-proposer",
|
proposer="previous-proposer",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
tx_count=0,
|
tx_count=0,
|
||||||
)
|
)
|
||||||
test_db.add(parent)
|
test_db.add(parent)
|
||||||
@@ -231,16 +231,16 @@ class TestPoAProposer:
|
|||||||
hash="0xhead",
|
hash="0xhead",
|
||||||
parent_hash="0x00",
|
parent_hash="0x00",
|
||||||
proposer="test-proposer",
|
proposer="test-proposer",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
tx_count=0,
|
tx_count=0,
|
||||||
)
|
)
|
||||||
test_db.add(head)
|
test_db.add(head)
|
||||||
test_db.commit()
|
test_db.commit()
|
||||||
|
|
||||||
# Should wait for the configured interval
|
# Should wait for the configured interval
|
||||||
start_time = datetime.now(datetime.UTC)
|
start_time = datetime.now(timezone.utc)
|
||||||
await proposer._wait_until_next_slot()
|
await proposer._wait_until_next_slot()
|
||||||
elapsed = (datetime.now(datetime.UTC) - start_time).total_seconds()
|
elapsed = (datetime.now(timezone.utc) - start_time).total_seconds()
|
||||||
|
|
||||||
# Should wait at least some time (but less than full interval since block is recent)
|
# Should wait at least some time (but less than full interval since block is recent)
|
||||||
assert elapsed >= 0.1
|
assert elapsed >= 0.1
|
||||||
@@ -255,7 +255,7 @@ class TestPoAProposer:
|
|||||||
hash="0xhead",
|
hash="0xhead",
|
||||||
parent_hash="0x00",
|
parent_hash="0x00",
|
||||||
proposer="test-proposer",
|
proposer="test-proposer",
|
||||||
timestamp=datetime.now(datetime.UTC) - timedelta(seconds=10),
|
timestamp=datetime.now(timezone.utc) - timedelta(seconds=10),
|
||||||
tx_count=0,
|
tx_count=0,
|
||||||
)
|
)
|
||||||
test_db.add(head)
|
test_db.add(head)
|
||||||
@@ -263,9 +263,9 @@ class TestPoAProposer:
|
|||||||
|
|
||||||
# Set stop event and wait
|
# Set stop event and wait
|
||||||
proposer._stop_event.set()
|
proposer._stop_event.set()
|
||||||
start_time = datetime.now(datetime.UTC)
|
start_time = datetime.now(timezone.utc)
|
||||||
await proposer._wait_until_next_slot()
|
await proposer._wait_until_next_slot()
|
||||||
elapsed = (datetime.now(datetime.UTC) - start_time).total_seconds()
|
elapsed = (datetime.now(timezone.utc) - start_time).total_seconds()
|
||||||
|
|
||||||
# Should return immediately due to stop event
|
# Should return immediately due to stop event
|
||||||
assert elapsed < 0.1
|
assert elapsed < 0.1
|
||||||
@@ -290,7 +290,7 @@ class TestPoAProposer:
|
|||||||
"""Test block hash computation."""
|
"""Test block hash computation."""
|
||||||
height = 1
|
height = 1
|
||||||
parent_hash = "0xparent"
|
parent_hash = "0xparent"
|
||||||
timestamp = datetime.now(datetime.UTC)
|
timestamp = datetime.now(timezone.utc)
|
||||||
processed_txs = []
|
processed_txs = []
|
||||||
|
|
||||||
block_hash = proposer._compute_block_hash(height, parent_hash, timestamp, processed_txs)
|
block_hash = proposer._compute_block_hash(height, parent_hash, timestamp, processed_txs)
|
||||||
@@ -303,7 +303,7 @@ class TestPoAProposer:
|
|||||||
"""Test block hash computation with transactions."""
|
"""Test block hash computation with transactions."""
|
||||||
height = 1
|
height = 1
|
||||||
parent_hash = "0xparent"
|
parent_hash = "0xparent"
|
||||||
timestamp = datetime.now(datetime.UTC)
|
timestamp = datetime.now(timezone.utc)
|
||||||
|
|
||||||
mock_tx = Mock()
|
mock_tx = Mock()
|
||||||
mock_tx.tx_hash = "0xtx"
|
mock_tx.tx_hash = "0xtx"
|
||||||
@@ -324,7 +324,7 @@ class TestPoAProposer:
|
|||||||
hash="0xexisting",
|
hash="0xexisting",
|
||||||
parent_hash="0x00",
|
parent_hash="0x00",
|
||||||
proposer="test-proposer",
|
proposer="test-proposer",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
tx_count=0,
|
tx_count=0,
|
||||||
)
|
)
|
||||||
test_db.add(block)
|
test_db.add(block)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import sys
|
|||||||
import pytest
|
import pytest
|
||||||
import tempfile
|
import tempfile
|
||||||
import shutil
|
import shutil
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import patch, Mock
|
from unittest.mock import patch, Mock
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
@@ -113,7 +113,7 @@ class TestGuardianContract:
|
|||||||
def test_spending_limit_check_hourly(self, guardian_contract: GuardianContract) -> None:
|
def test_spending_limit_check_hourly(self, guardian_contract: GuardianContract) -> None:
|
||||||
"""Test hourly spending limit."""
|
"""Test hourly spending limit."""
|
||||||
# Add some spending history
|
# Add some spending history
|
||||||
base_time = datetime.now(datetime.UTC)
|
base_time = datetime.now(timezone.utc)
|
||||||
guardian_contract.spending_history = [
|
guardian_contract.spending_history = [
|
||||||
{
|
{
|
||||||
"operation_id": "op1",
|
"operation_id": "op1",
|
||||||
@@ -139,7 +139,7 @@ class TestGuardianContract:
|
|||||||
def test_spending_limit_check_daily(self, guardian_contract: GuardianContract) -> None:
|
def test_spending_limit_check_daily(self, guardian_contract: GuardianContract) -> None:
|
||||||
"""Test daily spending limit."""
|
"""Test daily spending limit."""
|
||||||
# Add spending history across the day
|
# Add spending history across the day
|
||||||
base_time = datetime.now(datetime.UTC)
|
base_time = datetime.now(timezone.utc)
|
||||||
guardian_contract.spending_history = [
|
guardian_contract.spending_history = [
|
||||||
{
|
{
|
||||||
"operation_id": "op1",
|
"operation_id": "op1",
|
||||||
@@ -165,7 +165,7 @@ class TestGuardianContract:
|
|||||||
def test_spending_limit_check_weekly(self, guardian_contract: GuardianContract) -> None:
|
def test_spending_limit_check_weekly(self, guardian_contract: GuardianContract) -> None:
|
||||||
"""Test weekly spending limit."""
|
"""Test weekly spending limit."""
|
||||||
# Add spending history across the week
|
# Add spending history across the week
|
||||||
base_time = datetime.now(datetime.UTC)
|
base_time = datetime.now(timezone.utc)
|
||||||
guardian_contract.spending_history = [
|
guardian_contract.spending_history = [
|
||||||
{
|
{
|
||||||
"operation_id": "op1",
|
"operation_id": "op1",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import hashlib
|
|||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
import pytest
|
import pytest
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from unittest.mock import AsyncMock, Mock
|
from unittest.mock import AsyncMock, Mock
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ class TestProposerSignatureValidator:
|
|||||||
|
|
||||||
def test_valid_block(self):
|
def test_valid_block(self):
|
||||||
v = ProposerSignatureValidator()
|
v = ProposerSignatureValidator()
|
||||||
ts = datetime.now(datetime.UTC)
|
ts = datetime.now(timezone.utc)
|
||||||
bh = _make_block_hash("test", 1, "0x00", ts)
|
bh = _make_block_hash("test", 1, "0x00", ts)
|
||||||
ok, reason = v.validate_block_signature({
|
ok, reason = v.validate_block_signature({
|
||||||
"height": 1, "hash": bh, "parent_hash": "0x00",
|
"height": 1, "hash": bh, "parent_hash": "0x00",
|
||||||
@@ -83,7 +83,7 @@ class TestProposerSignatureValidator:
|
|||||||
v = ProposerSignatureValidator()
|
v = ProposerSignatureValidator()
|
||||||
ok, reason = v.validate_block_signature({
|
ok, reason = v.validate_block_signature({
|
||||||
"height": 1, "hash": "0x" + "a" * 64, "parent_hash": "0x00",
|
"height": 1, "hash": "0x" + "a" * 64, "parent_hash": "0x00",
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
})
|
})
|
||||||
assert ok is False
|
assert ok is False
|
||||||
assert "Missing proposer" in reason
|
assert "Missing proposer" in reason
|
||||||
@@ -92,7 +92,7 @@ class TestProposerSignatureValidator:
|
|||||||
v = ProposerSignatureValidator()
|
v = ProposerSignatureValidator()
|
||||||
ok, reason = v.validate_block_signature({
|
ok, reason = v.validate_block_signature({
|
||||||
"height": 1, "hash": "badhash", "parent_hash": "0x00",
|
"height": 1, "hash": "badhash", "parent_hash": "0x00",
|
||||||
"proposer": "node-a", "timestamp": datetime.now(datetime.UTC).isoformat(),
|
"proposer": "node-a", "timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
})
|
})
|
||||||
assert ok is False
|
assert ok is False
|
||||||
assert "Invalid block hash" in reason
|
assert "Invalid block hash" in reason
|
||||||
@@ -101,14 +101,14 @@ class TestProposerSignatureValidator:
|
|||||||
v = ProposerSignatureValidator()
|
v = ProposerSignatureValidator()
|
||||||
ok, reason = v.validate_block_signature({
|
ok, reason = v.validate_block_signature({
|
||||||
"height": 1, "hash": "0xabc", "parent_hash": "0x00",
|
"height": 1, "hash": "0xabc", "parent_hash": "0x00",
|
||||||
"proposer": "node-a", "timestamp": datetime.now(datetime.UTC).isoformat(),
|
"proposer": "node-a", "timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
})
|
})
|
||||||
assert ok is False
|
assert ok is False
|
||||||
assert "Invalid hash length" in reason
|
assert "Invalid hash length" in reason
|
||||||
|
|
||||||
def test_untrusted_proposer_rejected(self):
|
def test_untrusted_proposer_rejected(self):
|
||||||
v = ProposerSignatureValidator(trusted_proposers=["node-a", "node-b"])
|
v = ProposerSignatureValidator(trusted_proposers=["node-a", "node-b"])
|
||||||
ts = datetime.now(datetime.UTC)
|
ts = datetime.now(timezone.utc)
|
||||||
bh = _make_block_hash("test", 1, "0x00", ts)
|
bh = _make_block_hash("test", 1, "0x00", ts)
|
||||||
ok, reason = v.validate_block_signature({
|
ok, reason = v.validate_block_signature({
|
||||||
"height": 1, "hash": bh, "parent_hash": "0x00",
|
"height": 1, "hash": bh, "parent_hash": "0x00",
|
||||||
@@ -119,7 +119,7 @@ class TestProposerSignatureValidator:
|
|||||||
|
|
||||||
def test_trusted_proposer_accepted(self):
|
def test_trusted_proposer_accepted(self):
|
||||||
v = ProposerSignatureValidator(trusted_proposers=["node-a"])
|
v = ProposerSignatureValidator(trusted_proposers=["node-a"])
|
||||||
ts = datetime.now(datetime.UTC)
|
ts = datetime.now(timezone.utc)
|
||||||
bh = _make_block_hash("test", 1, "0x00", ts)
|
bh = _make_block_hash("test", 1, "0x00", ts)
|
||||||
ok, reason = v.validate_block_signature({
|
ok, reason = v.validate_block_signature({
|
||||||
"height": 1, "hash": bh, "parent_hash": "0x00",
|
"height": 1, "hash": bh, "parent_hash": "0x00",
|
||||||
@@ -149,7 +149,7 @@ class TestChainSyncAppend:
|
|||||||
|
|
||||||
def test_append_to_empty_chain(self, session_factory):
|
def test_append_to_empty_chain(self, session_factory):
|
||||||
sync = ChainSync(session_factory, chain_id="test", validate_signatures=False)
|
sync = ChainSync(session_factory, chain_id="test", validate_signatures=False)
|
||||||
ts = datetime.now(datetime.UTC)
|
ts = datetime.now(timezone.utc)
|
||||||
bh = _make_block_hash("test", 0, "0x00", ts)
|
bh = _make_block_hash("test", 0, "0x00", ts)
|
||||||
result = sync.import_block({
|
result = sync.import_block({
|
||||||
"height": 0, "hash": bh, "parent_hash": "0x00",
|
"height": 0, "hash": bh, "parent_hash": "0x00",
|
||||||
@@ -316,7 +316,7 @@ class TestChainSyncSignatureValidation:
|
|||||||
def test_untrusted_proposer_rejected_on_import(self, session_factory):
|
def test_untrusted_proposer_rejected_on_import(self, session_factory):
|
||||||
validator = ProposerSignatureValidator(trusted_proposers=["node-a"])
|
validator = ProposerSignatureValidator(trusted_proposers=["node-a"])
|
||||||
sync = ChainSync(session_factory, chain_id="test", validator=validator, validate_signatures=True)
|
sync = ChainSync(session_factory, chain_id="test", validator=validator, validate_signatures=True)
|
||||||
ts = datetime.now(datetime.UTC)
|
ts = datetime.now(timezone.utc)
|
||||||
bh = _make_block_hash("test", 0, "0x00", ts)
|
bh = _make_block_hash("test", 0, "0x00", ts)
|
||||||
result = sync.import_block({
|
result = sync.import_block({
|
||||||
"height": 0, "hash": bh, "parent_hash": "0x00",
|
"height": 0, "hash": bh, "parent_hash": "0x00",
|
||||||
@@ -328,7 +328,7 @@ class TestChainSyncSignatureValidation:
|
|||||||
def test_trusted_proposer_accepted_on_import(self, session_factory):
|
def test_trusted_proposer_accepted_on_import(self, session_factory):
|
||||||
validator = ProposerSignatureValidator(trusted_proposers=["node-a"])
|
validator = ProposerSignatureValidator(trusted_proposers=["node-a"])
|
||||||
sync = ChainSync(session_factory, chain_id="test", validator=validator, validate_signatures=True)
|
sync = ChainSync(session_factory, chain_id="test", validator=validator, validate_signatures=True)
|
||||||
ts = datetime.now(datetime.UTC)
|
ts = datetime.now(timezone.utc)
|
||||||
bh = _make_block_hash("test", 0, "0x00", ts)
|
bh = _make_block_hash("test", 0, "0x00", ts)
|
||||||
result = sync.import_block({
|
result = sync.import_block({
|
||||||
"height": 0, "hash": bh, "parent_hash": "0x00",
|
"height": 0, "hash": bh, "parent_hash": "0x00",
|
||||||
@@ -339,7 +339,7 @@ class TestChainSyncSignatureValidation:
|
|||||||
def test_validation_disabled(self, session_factory):
|
def test_validation_disabled(self, session_factory):
|
||||||
validator = ProposerSignatureValidator(trusted_proposers=["node-a"])
|
validator = ProposerSignatureValidator(trusted_proposers=["node-a"])
|
||||||
sync = ChainSync(session_factory, chain_id="test", validator=validator, validate_signatures=False)
|
sync = ChainSync(session_factory, chain_id="test", validator=validator, validate_signatures=False)
|
||||||
ts = datetime.now(datetime.UTC)
|
ts = datetime.now(timezone.utc)
|
||||||
bh = _make_block_hash("test", 0, "0x00", ts)
|
bh = _make_block_hash("test", 0, "0x00", ts)
|
||||||
result = sync.import_block({
|
result = sync.import_block({
|
||||||
"height": 0, "hash": bh, "parent_hash": "0x00",
|
"height": 0, "hash": bh, "parent_hash": "0x00",
|
||||||
@@ -379,7 +379,7 @@ class TestSyncMetrics:
|
|||||||
|
|
||||||
def test_accepted_block_increments_metrics(self, session_factory):
|
def test_accepted_block_increments_metrics(self, session_factory):
|
||||||
sync = ChainSync(session_factory, chain_id="test", validate_signatures=False)
|
sync = ChainSync(session_factory, chain_id="test", validate_signatures=False)
|
||||||
ts = datetime.now(datetime.UTC)
|
ts = datetime.now(timezone.utc)
|
||||||
bh = _make_block_hash("test", 0, "0x00", ts)
|
bh = _make_block_hash("test", 0, "0x00", ts)
|
||||||
sync.import_block({
|
sync.import_block({
|
||||||
"height": 0, "hash": bh, "parent_hash": "0x00",
|
"height": 0, "hash": bh, "parent_hash": "0x00",
|
||||||
@@ -392,7 +392,7 @@ class TestSyncMetrics:
|
|||||||
def test_rejected_block_increments_metrics(self, session_factory):
|
def test_rejected_block_increments_metrics(self, session_factory):
|
||||||
validator = ProposerSignatureValidator(trusted_proposers=["node-a"])
|
validator = ProposerSignatureValidator(trusted_proposers=["node-a"])
|
||||||
sync = ChainSync(session_factory, chain_id="test", validator=validator, validate_signatures=True)
|
sync = ChainSync(session_factory, chain_id="test", validator=validator, validate_signatures=True)
|
||||||
ts = datetime.now(datetime.UTC)
|
ts = datetime.now(timezone.utc)
|
||||||
bh = _make_block_hash("test", 0, "0x00", ts)
|
bh = _make_block_hash("test", 0, "0x00", ts)
|
||||||
sync.import_block({
|
sync.import_block({
|
||||||
"height": 0, "hash": bh, "parent_hash": "0x00",
|
"height": 0, "hash": bh, "parent_hash": "0x00",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Handles KYC/AML, regulatory compliance, and monitoring
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any, List, Optional
|
from typing import Dict, Any, List, Optional
|
||||||
from fastapi import FastAPI, HTTPException
|
from fastapi import FastAPI, HTTPException
|
||||||
@@ -68,7 +68,7 @@ async def root():
|
|||||||
return {
|
return {
|
||||||
"service": "AITBC Compliance Service",
|
"service": "AITBC Compliance Service",
|
||||||
"status": "running",
|
"status": "running",
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ async def submit_kyc(kyc_request: KYCRequest):
|
|||||||
"document_number": kyc_request.document_number,
|
"document_number": kyc_request.document_number,
|
||||||
"address": kyc_request.address,
|
"address": kyc_request.address,
|
||||||
"status": "pending",
|
"status": "pending",
|
||||||
"submitted_at": datetime.now(datetime.UTC).isoformat(),
|
"submitted_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"reviewed_at": None,
|
"reviewed_at": None,
|
||||||
"approved_at": None,
|
"approved_at": None,
|
||||||
"risk_score": "medium",
|
"risk_score": "medium",
|
||||||
@@ -111,8 +111,8 @@ async def submit_kyc(kyc_request: KYCRequest):
|
|||||||
|
|
||||||
# Auto-approve for demo (in production, this would involve actual verification)
|
# Auto-approve for demo (in production, this would involve actual verification)
|
||||||
kyc_record["status"] = "approved"
|
kyc_record["status"] = "approved"
|
||||||
kyc_record["reviewed_at"] = datetime.now(datetime.UTC).isoformat()
|
kyc_record["reviewed_at"] = datetime.now(timezone.utc).isoformat()
|
||||||
kyc_record["approved_at"] = datetime.now(datetime.UTC).isoformat()
|
kyc_record["approved_at"] = datetime.now(timezone.utc).isoformat()
|
||||||
kyc_record["risk_score"] = "low"
|
kyc_record["risk_score"] = "low"
|
||||||
|
|
||||||
logger.info(f"KYC approved for user: {kyc_request.user_id}")
|
logger.info(f"KYC approved for user: {kyc_request.user_id}")
|
||||||
@@ -146,7 +146,7 @@ async def list_kyc_records():
|
|||||||
@app.post("/api/v1/compliance/report")
|
@app.post("/api/v1/compliance/report")
|
||||||
async def create_compliance_report(report: ComplianceReport):
|
async def create_compliance_report(report: ComplianceReport):
|
||||||
"""Create a compliance report"""
|
"""Create a compliance report"""
|
||||||
report_id = f"report_{int(datetime.now(datetime.UTC).timestamp())}"
|
report_id = f"report_{int(datetime.now(timezone.utc).timestamp())}"
|
||||||
|
|
||||||
compliance_record = {
|
compliance_record = {
|
||||||
"report_id": report_id,
|
"report_id": report_id,
|
||||||
@@ -155,7 +155,7 @@ async def create_compliance_report(report: ComplianceReport):
|
|||||||
"severity": report.severity,
|
"severity": report.severity,
|
||||||
"details": report.details,
|
"details": report.details,
|
||||||
"status": "open",
|
"status": "open",
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"assigned_to": None,
|
"assigned_to": None,
|
||||||
"resolved_at": None,
|
"resolved_at": None,
|
||||||
"resolution": None
|
"resolution": None
|
||||||
@@ -195,7 +195,7 @@ async def monitor_transaction(transaction: TransactionMonitoring):
|
|||||||
"currency": transaction.currency,
|
"currency": transaction.currency,
|
||||||
"counterparty": transaction.counterparty,
|
"counterparty": transaction.counterparty,
|
||||||
"timestamp": transaction.timestamp.isoformat(),
|
"timestamp": transaction.timestamp.isoformat(),
|
||||||
"monitored_at": datetime.now(datetime.UTC).isoformat(),
|
"monitored_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"risk_score": calculate_transaction_risk(transaction),
|
"risk_score": calculate_transaction_risk(transaction),
|
||||||
"flags": [],
|
"flags": [],
|
||||||
"status": "monitored"
|
"status": "monitored"
|
||||||
@@ -232,7 +232,7 @@ async def list_monitored_transactions():
|
|||||||
@app.post("/api/v1/rules/create")
|
@app.post("/api/v1/rules/create")
|
||||||
async def create_compliance_rule(rule_data: Dict[str, Any]):
|
async def create_compliance_rule(rule_data: Dict[str, Any]):
|
||||||
"""Create a new compliance rule"""
|
"""Create a new compliance rule"""
|
||||||
rule_id = f"rule_{int(datetime.now(datetime.UTC).timestamp())}"
|
rule_id = f"rule_{int(datetime.now(timezone.utc).timestamp())}"
|
||||||
|
|
||||||
rule = {
|
rule = {
|
||||||
"rule_id": rule_id,
|
"rule_id": rule_id,
|
||||||
@@ -243,7 +243,7 @@ async def create_compliance_rule(rule_data: Dict[str, Any]):
|
|||||||
"actions": rule_data.get("actions", []),
|
"actions": rule_data.get("actions", []),
|
||||||
"severity": rule_data.get("severity", "medium"),
|
"severity": rule_data.get("severity", "medium"),
|
||||||
"active": True,
|
"active": True,
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"trigger_count": 0
|
"trigger_count": 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +294,7 @@ async def compliance_dashboard():
|
|||||||
},
|
},
|
||||||
"risk_distribution": get_risk_distribution(),
|
"risk_distribution": get_risk_distribution(),
|
||||||
"recent_activity": get_recent_activity(),
|
"recent_activity": get_recent_activity(),
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Helper functions
|
# Helper functions
|
||||||
@@ -337,7 +337,7 @@ def check_suspicious_patterns(transaction: TransactionMonitoring) -> List[str]:
|
|||||||
|
|
||||||
recent_transactions = [t for t in user_transactions
|
recent_transactions = [t for t in user_transactions
|
||||||
if datetime.fromisoformat(t["monitored_at"]) >
|
if datetime.fromisoformat(t["monitored_at"]) >
|
||||||
datetime.now(datetime.UTC) - timedelta(hours=1)]
|
datetime.now(timezone.utc) - timedelta(hours=1)]
|
||||||
|
|
||||||
if len(recent_transactions) > 5:
|
if len(recent_transactions) > 5:
|
||||||
flags.append("rapid_transactions")
|
flags.append("rapid_transactions")
|
||||||
@@ -385,7 +385,7 @@ def get_recent_activity() -> List[Dict]:
|
|||||||
recent_kyc = [r for r in kyc_records.values()
|
recent_kyc = [r for r in kyc_records.values()
|
||||||
if r.get("approved_at") and
|
if r.get("approved_at") and
|
||||||
datetime.fromisoformat(r["approved_at"]) >
|
datetime.fromisoformat(r["approved_at"]) >
|
||||||
datetime.now(datetime.UTC) - timedelta(hours=24)]
|
datetime.now(timezone.utc) - timedelta(hours=24)]
|
||||||
|
|
||||||
for kyc in recent_kyc[:5]:
|
for kyc in recent_kyc[:5]:
|
||||||
activities.append({
|
activities.append({
|
||||||
@@ -397,7 +397,7 @@ def get_recent_activity() -> List[Dict]:
|
|||||||
# Recent compliance reports
|
# Recent compliance reports
|
||||||
recent_reports = [r for r in compliance_reports.values()
|
recent_reports = [r for r in compliance_reports.values()
|
||||||
if datetime.fromisoformat(r["created_at"]) >
|
if datetime.fromisoformat(r["created_at"]) >
|
||||||
datetime.now(datetime.UTC) - timedelta(hours=24)]
|
datetime.now(timezone.utc) - timedelta(hours=24)]
|
||||||
|
|
||||||
for report in recent_reports[:5]:
|
for report in recent_reports[:5]:
|
||||||
activities.append({
|
activities.append({
|
||||||
@@ -418,7 +418,7 @@ async def periodic_compliance_checks():
|
|||||||
await asyncio.sleep(300) # Check every 5 minutes
|
await asyncio.sleep(300) # Check every 5 minutes
|
||||||
|
|
||||||
# Check for expired KYC records
|
# Check for expired KYC records
|
||||||
current_time = datetime.now(datetime.UTC)
|
current_time = datetime.now(timezone.utc)
|
||||||
for user_id, kyc_record in kyc_records.items():
|
for user_id, kyc_record in kyc_records.items():
|
||||||
if kyc_record["status"] == "approved":
|
if kyc_record["status"] == "approved":
|
||||||
approved_time = datetime.fromisoformat(kyc_record["approved_at"])
|
approved_time = datetime.fromisoformat(kyc_record["approved_at"])
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
from main import app, KYCRequest, ComplianceReport, TransactionMonitoring, kyc_records, compliance_reports, suspicious_transactions, compliance_rules
|
from main import app, KYCRequest, ComplianceReport, TransactionMonitoring, kyc_records, compliance_reports, suspicious_transactions, compliance_rules
|
||||||
@@ -62,7 +62,7 @@ def test_transaction_monitoring_zero_amount():
|
|||||||
amount=0.0,
|
amount=0.0,
|
||||||
currency="BTC",
|
currency="BTC",
|
||||||
counterparty="counterparty1",
|
counterparty="counterparty1",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert tx.amount == 0.0
|
assert tx.amount == 0.0
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ def test_transaction_monitoring_negative_amount():
|
|||||||
amount=-1000.0,
|
amount=-1000.0,
|
||||||
currency="BTC",
|
currency="BTC",
|
||||||
counterparty="counterparty1",
|
counterparty="counterparty1",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert tx.amount == -1000.0
|
assert tx.amount == -1000.0
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
from main import app, KYCRequest, ComplianceReport, TransactionMonitoring, kyc_records, compliance_reports, suspicious_transactions, compliance_rules
|
from main import app, KYCRequest, ComplianceReport, TransactionMonitoring, kyc_records, compliance_reports, suspicious_transactions, compliance_rules
|
||||||
@@ -171,7 +171,7 @@ def test_monitor_transaction():
|
|||||||
amount=1000.0,
|
amount=1000.0,
|
||||||
currency="BTC",
|
currency="BTC",
|
||||||
counterparty="counterparty1",
|
counterparty="counterparty1",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
response = client.post("/api/v1/monitoring/transaction", json=tx.model_dump(mode='json'))
|
response = client.post("/api/v1/monitoring/transaction", json=tx.model_dump(mode='json'))
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
@@ -190,7 +190,7 @@ def test_monitor_suspicious_transaction():
|
|||||||
amount=100000.0,
|
amount=100000.0,
|
||||||
currency="BTC",
|
currency="BTC",
|
||||||
counterparty="high_risk_entity_1",
|
counterparty="high_risk_entity_1",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
response = client.post("/api/v1/monitoring/transaction", json=tx.model_dump(mode='json'))
|
response = client.post("/api/v1/monitoring/transaction", json=tx.model_dump(mode='json'))
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import sys
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
from main import app, KYCRequest, ComplianceReport, TransactionMonitoring, calculate_transaction_risk, check_suspicious_patterns
|
from main import app, KYCRequest, ComplianceReport, TransactionMonitoring, calculate_transaction_risk, check_suspicious_patterns
|
||||||
@@ -62,7 +62,7 @@ def test_transaction_monitoring_model():
|
|||||||
amount=1000.0,
|
amount=1000.0,
|
||||||
currency="BTC",
|
currency="BTC",
|
||||||
counterparty="counterparty1",
|
counterparty="counterparty1",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert tx.transaction_id == "tx123"
|
assert tx.transaction_id == "tx123"
|
||||||
assert tx.user_id == "user123"
|
assert tx.user_id == "user123"
|
||||||
@@ -125,7 +125,7 @@ def test_check_suspicious_patterns_high_value():
|
|||||||
amount=100000.0,
|
amount=100000.0,
|
||||||
currency="BTC",
|
currency="BTC",
|
||||||
counterparty="counterparty1",
|
counterparty="counterparty1",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
flags = check_suspicious_patterns(tx)
|
flags = check_suspicious_patterns(tx)
|
||||||
assert "high_value_transaction" in flags
|
assert "high_value_transaction" in flags
|
||||||
@@ -140,7 +140,7 @@ def test_check_suspicious_patterns_high_risk_counterparty():
|
|||||||
amount=1000.0,
|
amount=1000.0,
|
||||||
currency="BTC",
|
currency="BTC",
|
||||||
counterparty="high_risk_entity_1",
|
counterparty="high_risk_entity_1",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
flags = check_suspicious_patterns(tx)
|
flags = check_suspicious_patterns(tx)
|
||||||
assert "high_risk_counterparty" in flags
|
assert "high_risk_counterparty" in flags
|
||||||
@@ -155,7 +155,7 @@ def test_check_suspicious_patterns_none():
|
|||||||
amount=1000.0,
|
amount=1000.0,
|
||||||
currency="BTC",
|
currency="BTC",
|
||||||
counterparty="safe_counterparty",
|
counterparty="safe_counterparty",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
flags = check_suspicious_patterns(tx)
|
flags = check_suspicious_patterns(tx)
|
||||||
assert len(flags) == 0
|
assert len(flags) == 0
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Demonstrates basic usage of the Agent Identity SDK
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
|
|
||||||
# Import SDK components
|
# Import SDK components
|
||||||
@@ -94,7 +94,7 @@ async def basic_identity_example():
|
|||||||
"agent_id": identity.agent_id,
|
"agent_id": identity.agent_id,
|
||||||
"chain_id": mapping.chain_id,
|
"chain_id": mapping.chain_id,
|
||||||
"chain_address": mapping.chain_address,
|
"chain_address": mapping.chain_address,
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"verification_method": "demo"
|
"verification_method": "demo"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +234,7 @@ async def advanced_transaction_example():
|
|||||||
"description": "Updated description with new capabilities",
|
"description": "Updated description with new capabilities",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"last_updated": datetime.now(datetime.UTC).isoformat()
|
"last_updated": datetime.now(timezone.utc).isoformat()
|
||||||
},
|
},
|
||||||
"tags": ["demo", "ai", "updated"]
|
"tags": ["demo", "ai", "updated"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Complete deployment procedures for the agent orchestration system
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from aitbc.logging import get_logger
|
from aitbc.logging import get_logger
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from typing import Dict, List, Optional, Any
|
from typing import Dict, List, Optional, Any
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ class AgentOrchestrationDeployment:
|
|||||||
"""Deploy complete agent orchestration system to production"""
|
"""Deploy complete agent orchestration system to production"""
|
||||||
|
|
||||||
deployment_result = {
|
deployment_result = {
|
||||||
"deployment_id": f"prod_deploy_{datetime.now(datetime.UTC).strftime('%Y%m%d_%H%M%S')}",
|
"deployment_id": f"prod_deploy_{datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')}",
|
||||||
"status": "in_progress",
|
"status": "in_progress",
|
||||||
"steps_completed": [],
|
"steps_completed": [],
|
||||||
"steps_failed": [],
|
"steps_failed": [],
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Ongoing maintenance, monitoring, and enhancement of the complete system
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from aitbc.logging import get_logger
|
from aitbc.logging import get_logger
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from typing import Dict, List, Optional, Any
|
from typing import Dict, List, Optional, Any
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ class SystemMaintenanceManager:
|
|||||||
"""Perform comprehensive maintenance cycle"""
|
"""Perform comprehensive maintenance cycle"""
|
||||||
|
|
||||||
maintenance_result = {
|
maintenance_result = {
|
||||||
"maintenance_cycle": f"maintenance_{datetime.now(datetime.UTC).strftime('%Y%m%d_%H%M%S')}",
|
"maintenance_cycle": f"maintenance_{datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')}",
|
||||||
"status": "in_progress",
|
"status": "in_progress",
|
||||||
"categories_completed": [],
|
"categories_completed": [],
|
||||||
"enhancements_implemented": [],
|
"enhancements_implemented": [],
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Provides unified agent identification and cross-chain compatibility
|
|||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ class AgentIdentityCore:
|
|||||||
if hasattr(identity, field):
|
if hasattr(identity, field):
|
||||||
setattr(identity, field, value)
|
setattr(identity, field, value)
|
||||||
|
|
||||||
identity.updated_at = datetime.now(datetime.UTC)
|
identity.updated_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
self.session.refresh(identity)
|
self.session.refresh(identity)
|
||||||
@@ -133,7 +133,7 @@ class AgentIdentityCore:
|
|||||||
# Update identity's supported chains
|
# Update identity's supported chains
|
||||||
if chain_id not in identity.supported_chains:
|
if chain_id not in identity.supported_chains:
|
||||||
identity.supported_chains.append(str(chain_id))
|
identity.supported_chains.append(str(chain_id))
|
||||||
identity.updated_at = datetime.now(datetime.UTC)
|
identity.updated_at = datetime.now(timezone.utc)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
|
||||||
logger.info(f"Registered cross-chain identity: {identity_id} -> {chain_id}:{chain_address}")
|
logger.info(f"Registered cross-chain identity: {identity_id} -> {chain_id}:{chain_address}")
|
||||||
@@ -190,7 +190,7 @@ class AgentIdentityCore:
|
|||||||
|
|
||||||
# Update mapping verification status
|
# Update mapping verification status
|
||||||
mapping.is_verified = True
|
mapping.is_verified = True
|
||||||
mapping.verified_at = datetime.now(datetime.UTC)
|
mapping.verified_at = datetime.now(timezone.utc)
|
||||||
mapping.verification_proof = proof_data
|
mapping.verification_proof = proof_data
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
|
||||||
@@ -198,7 +198,7 @@ class AgentIdentityCore:
|
|||||||
identity = await self.get_identity(identity_id)
|
identity = await self.get_identity(identity_id)
|
||||||
if identity and chain_id == identity.primary_chain:
|
if identity and chain_id == identity.primary_chain:
|
||||||
identity.is_verified = True
|
identity.is_verified = True
|
||||||
identity.verified_at = datetime.now(datetime.UTC)
|
identity.verified_at = datetime.now(timezone.utc)
|
||||||
identity.verification_level = verification_type
|
identity.verification_level = verification_type
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
|
||||||
@@ -242,7 +242,7 @@ class AgentIdentityCore:
|
|||||||
else:
|
else:
|
||||||
setattr(mapping, field, value)
|
setattr(mapping, field, value)
|
||||||
|
|
||||||
mapping.updated_at = datetime.now(datetime.UTC)
|
mapping.updated_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
self.session.refresh(mapping)
|
self.session.refresh(mapping)
|
||||||
@@ -260,11 +260,11 @@ class AgentIdentityCore:
|
|||||||
# Update identity status
|
# Update identity status
|
||||||
identity.status = IdentityStatus.REVOKED
|
identity.status = IdentityStatus.REVOKED
|
||||||
identity.is_verified = False
|
identity.is_verified = False
|
||||||
identity.updated_at = datetime.now(datetime.UTC)
|
identity.updated_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
# Add revocation reason to identity_data
|
# Add revocation reason to identity_data
|
||||||
identity.identity_data["revocation_reason"] = reason
|
identity.identity_data["revocation_reason"] = reason
|
||||||
identity.identity_data["revoked_at"] = datetime.now(datetime.UTC).isoformat()
|
identity.identity_data["revoked_at"] = datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
|
||||||
@@ -280,11 +280,11 @@ class AgentIdentityCore:
|
|||||||
|
|
||||||
# Update identity status
|
# Update identity status
|
||||||
identity.status = IdentityStatus.SUSPENDED
|
identity.status = IdentityStatus.SUSPENDED
|
||||||
identity.updated_at = datetime.now(datetime.UTC)
|
identity.updated_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
# Add suspension reason to identity_data
|
# Add suspension reason to identity_data
|
||||||
identity.identity_data["suspension_reason"] = reason
|
identity.identity_data["suspension_reason"] = reason
|
||||||
identity.identity_data["suspended_at"] = datetime.now(datetime.UTC).isoformat()
|
identity.identity_data["suspended_at"] = datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
|
||||||
@@ -303,7 +303,7 @@ class AgentIdentityCore:
|
|||||||
|
|
||||||
# Update identity status
|
# Update identity status
|
||||||
identity.status = IdentityStatus.ACTIVE
|
identity.status = IdentityStatus.ACTIVE
|
||||||
identity.updated_at = datetime.now(datetime.UTC)
|
identity.updated_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
# Clear suspension identity_data
|
# Clear suspension identity_data
|
||||||
if "suspension_reason" in identity.identity_data:
|
if "suspension_reason" in identity.identity_data:
|
||||||
@@ -336,8 +336,8 @@ class AgentIdentityCore:
|
|||||||
volume_factor = min(amount / 1000.0, 1.0) # Cap at 1.0 for amounts > 1000
|
volume_factor = min(amount / 1000.0, 1.0) # Cap at 1.0 for amounts > 1000
|
||||||
identity.reputation_score = base_score * (0.7 + 0.3 * volume_factor)
|
identity.reputation_score = base_score * (0.7 + 0.3 * volume_factor)
|
||||||
|
|
||||||
identity.last_activity = datetime.now(datetime.UTC)
|
identity.last_activity = datetime.now(timezone.utc)
|
||||||
identity.updated_at = datetime.now(datetime.UTC)
|
identity.updated_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
self.session.refresh(identity)
|
self.session.refresh(identity)
|
||||||
@@ -452,7 +452,7 @@ class AgentIdentityCore:
|
|||||||
"owner_address": identity.owner_address,
|
"owner_address": identity.owner_address,
|
||||||
"chain_id": chain_id,
|
"chain_id": chain_id,
|
||||||
"chain_address": mapping.chain_address,
|
"chain_address": mapping.chain_address,
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"nonce": str(uuid4()),
|
"nonce": str(uuid4()),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,5 +463,5 @@ class AgentIdentityCore:
|
|||||||
return {
|
return {
|
||||||
"proof_data": proof_data,
|
"proof_data": proof_data,
|
||||||
"proof_hash": proof_hash,
|
"proof_hash": proof_hash,
|
||||||
"expires_at": (datetime.now(datetime.UTC) + timedelta(hours=24)).isoformat(),
|
"expires_at": (datetime.now(timezone.utc) + timedelta(hours=24)).isoformat(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Agent Identity Manager Implementation
|
|||||||
High-level manager for agent identity operations and cross-chain management
|
High-level manager for agent identity operations and cross-chain management
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
@@ -171,7 +171,7 @@ class AgentIdentityManager:
|
|||||||
# Update identity reputation
|
# Update identity reputation
|
||||||
await self.core.update_reputation(agent_id, True, 0) # This will recalculate based on new data
|
await self.core.update_reputation(agent_id, True, 0) # This will recalculate based on new data
|
||||||
identity.reputation_score = aggregated_score
|
identity.reputation_score = aggregated_score
|
||||||
identity.updated_at = datetime.now(datetime.UTC)
|
identity.updated_at = datetime.now(timezone.utc)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
else:
|
else:
|
||||||
aggregated_score = identity.reputation_score
|
aggregated_score = identity.reputation_score
|
||||||
@@ -181,7 +181,7 @@ class AgentIdentityManager:
|
|||||||
"aggregated_reputation": aggregated_score,
|
"aggregated_reputation": aggregated_score,
|
||||||
"chain_reputations": reputation_scores,
|
"chain_reputations": reputation_scores,
|
||||||
"verified_chains": list(verified_chains) if "verified_chains" in locals() else [],
|
"verified_chains": list(verified_chains) if "verified_chains" in locals() else [],
|
||||||
"sync_timestamp": datetime.now(datetime.UTC).isoformat(),
|
"sync_timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -449,12 +449,12 @@ class AgentIdentityManager:
|
|||||||
"supported_chains": supported_chains,
|
"supported_chains": supported_chains,
|
||||||
"cleaned_verifications": cleaned_count,
|
"cleaned_verifications": cleaned_count,
|
||||||
"issues": issues,
|
"issues": issues,
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to get registry health: {e}")
|
logger.error(f"Failed to get registry health: {e}")
|
||||||
return {"status": "error", "error": "Health check failed", "timestamp": datetime.now(datetime.UTC).isoformat()}
|
return {"status": "error", "error": "Health check failed", "timestamp": datetime.now(timezone.utc).isoformat()}
|
||||||
|
|
||||||
async def export_agent_identity(self, agent_id: str, format: str = "json") -> dict[str, Any]:
|
async def export_agent_identity(self, agent_id: str, format: str = "json") -> dict[str, Any]:
|
||||||
"""Export agent identity data for backup or migration"""
|
"""Export agent identity data for backup or migration"""
|
||||||
@@ -469,7 +469,7 @@ class AgentIdentityManager:
|
|||||||
# Prepare export data
|
# Prepare export data
|
||||||
export_data = {
|
export_data = {
|
||||||
"export_version": "1.0",
|
"export_version": "1.0",
|
||||||
"export_timestamp": datetime.now(datetime.UTC).isoformat(),
|
"export_timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"agent_id": agent_id,
|
"agent_id": agent_id,
|
||||||
"identity": summary["identity"],
|
"identity": summary["identity"],
|
||||||
"cross_chain_mappings": summary["cross_chain"]["mappings"],
|
"cross_chain_mappings": summary["cross_chain"]["mappings"],
|
||||||
@@ -541,7 +541,7 @@ class AgentIdentityManager:
|
|||||||
"identity_id": identity.id,
|
"identity_id": identity.id,
|
||||||
"import_successful": True,
|
"import_successful": True,
|
||||||
"restored_mappings": len(chain_mappings),
|
"restored_mappings": len(chain_mappings),
|
||||||
"import_timestamp": datetime.now(datetime.UTC).isoformat(),
|
"import_timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Registry for cross-chain agent identity mapping and synchronization
|
|||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ class CrossChainRegistry:
|
|||||||
registration_results.append({"chain_id": chain_id, "chain_address": chain_address, "error": str(e)})
|
registration_results.append({"chain_id": chain_id, "chain_address": chain_address, "error": str(e)})
|
||||||
|
|
||||||
# Update identity
|
# Update identity
|
||||||
identity.updated_at = datetime.now(datetime.UTC)
|
identity.updated_at = datetime.now(timezone.utc)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -143,7 +143,7 @@ class CrossChainRegistry:
|
|||||||
|
|
||||||
old_address = mapping.chain_address
|
old_address = mapping.chain_address
|
||||||
mapping.chain_address = new_address.lower()
|
mapping.chain_address = new_address.lower()
|
||||||
mapping.updated_at = datetime.now(datetime.UTC)
|
mapping.updated_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
# Reset verification status since address changed
|
# Reset verification status since address changed
|
||||||
mapping.is_verified = False
|
mapping.is_verified = False
|
||||||
@@ -195,7 +195,7 @@ class CrossChainRegistry:
|
|||||||
proof_hash=proof_hash,
|
proof_hash=proof_hash,
|
||||||
proof_data=proof_data,
|
proof_data=proof_data,
|
||||||
verification_result="approved",
|
verification_result="approved",
|
||||||
expires_at=datetime.now(datetime.UTC) + timedelta(days=30),
|
expires_at=datetime.now(timezone.utc) + timedelta(days=30),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.session.add(verification)
|
self.session.add(verification)
|
||||||
@@ -204,7 +204,7 @@ class CrossChainRegistry:
|
|||||||
|
|
||||||
# Update mapping verification status
|
# Update mapping verification status
|
||||||
mapping.is_verified = True
|
mapping.is_verified = True
|
||||||
mapping.verified_at = datetime.now(datetime.UTC)
|
mapping.verified_at = datetime.now(timezone.utc)
|
||||||
mapping.verification_proof = proof_data
|
mapping.verification_proof = proof_data
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
|
||||||
@@ -212,7 +212,7 @@ class CrossChainRegistry:
|
|||||||
if self._is_higher_verification_level(verification_type, identity.verification_level):
|
if self._is_higher_verification_level(verification_type, identity.verification_level):
|
||||||
identity.verification_level = verification_type
|
identity.verification_level = verification_type
|
||||||
identity.is_verified = True
|
identity.is_verified = True
|
||||||
identity.verified_at = datetime.now(datetime.UTC)
|
identity.verified_at = datetime.now(timezone.utc)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
|
||||||
logger.info(f"Verified cross-chain identity: {identity_id} on chain {chain_id}")
|
logger.info(f"Verified cross-chain identity: {identity_id} on chain {chain_id}")
|
||||||
@@ -229,14 +229,14 @@ class CrossChainRegistry:
|
|||||||
mapping.is_verified = False
|
mapping.is_verified = False
|
||||||
mapping.verified_at = None
|
mapping.verified_at = None
|
||||||
mapping.verification_proof = None
|
mapping.verification_proof = None
|
||||||
mapping.updated_at = datetime.now(datetime.UTC)
|
mapping.updated_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
# Add revocation to metadata
|
# Add revocation to metadata
|
||||||
if not mapping.chain_metadata:
|
if not mapping.chain_metadata:
|
||||||
mapping.chain_metadata = {}
|
mapping.chain_metadata = {}
|
||||||
mapping.chain_metadata["verification_revoked"] = True
|
mapping.chain_metadata["verification_revoked"] = True
|
||||||
mapping.chain_metadata["revocation_reason"] = reason
|
mapping.chain_metadata["revocation_reason"] = reason
|
||||||
mapping.chain_metadata["revoked_at"] = datetime.now(datetime.UTC).isoformat()
|
mapping.chain_metadata["revoked_at"] = datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
|
||||||
@@ -453,7 +453,7 @@ class CrossChainRegistry:
|
|||||||
async def cleanup_expired_verifications(self) -> int:
|
async def cleanup_expired_verifications(self) -> int:
|
||||||
"""Clean up expired verification records"""
|
"""Clean up expired verification records"""
|
||||||
|
|
||||||
current_time = datetime.now(datetime.UTC)
|
current_time = datetime.now(timezone.utc)
|
||||||
|
|
||||||
# Find expired verifications
|
# Find expired verifications
|
||||||
stmt = select(IdentityVerification).where(IdentityVerification.expires_at < current_time)
|
stmt = select(IdentityVerification).where(IdentityVerification.expires_at < current_time)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Provides blockchain-agnostic wallet interface for agents
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ class EthereumWalletAdapter(WalletAdapter):
|
|||||||
"wallet_address": f"0x{'0' * 40}", # Mock address
|
"wallet_address": f"0x{'0' * 40}", # Mock address
|
||||||
"contract_address": f"0x{'1' * 40}", # Mock contract
|
"contract_address": f"0x{'1' * 40}", # Mock contract
|
||||||
"transaction_hash": f"0x{'2' * 64}", # Mock tx hash
|
"transaction_hash": f"0x{'2' * 64}", # Mock tx hash
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def get_balance(self, wallet_address: str) -> Decimal:
|
async def get_balance(self, wallet_address: str) -> Decimal:
|
||||||
@@ -91,7 +91,7 @@ class EthereumWalletAdapter(WalletAdapter):
|
|||||||
"gas_price": "20000000000",
|
"gas_price": "20000000000",
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"block_number": 12345,
|
"block_number": 12345,
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def get_transaction_history(self, wallet_address: str, limit: int = 50, offset: int = 0) -> list[dict[str, Any]]:
|
async def get_transaction_history(self, wallet_address: str, limit: int = 50, offset: int = 0) -> list[dict[str, Any]]:
|
||||||
@@ -105,7 +105,7 @@ class EthereumWalletAdapter(WalletAdapter):
|
|||||||
"amount": "0.1",
|
"amount": "0.1",
|
||||||
"gas_used": "21000",
|
"gas_used": "21000",
|
||||||
"block_number": 12344,
|
"block_number": 12344,
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -269,7 +269,7 @@ class MultiChainWalletAdapter:
|
|||||||
|
|
||||||
# Update wallet in database
|
# Update wallet in database
|
||||||
wallet.total_spent += float(amount)
|
wallet.total_spent += float(amount)
|
||||||
wallet.last_transaction = datetime.now(datetime.UTC)
|
wallet.last_transaction = datetime.now(timezone.utc)
|
||||||
wallet.transaction_count += 1
|
wallet.transaction_count += 1
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
|
||||||
@@ -312,7 +312,7 @@ class MultiChainWalletAdapter:
|
|||||||
if hasattr(wallet, field):
|
if hasattr(wallet, field):
|
||||||
setattr(wallet, field, value)
|
setattr(wallet, field, value)
|
||||||
|
|
||||||
wallet.updated_at = datetime.now(datetime.UTC)
|
wallet.updated_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
self.session.refresh(wallet)
|
self.session.refresh(wallet)
|
||||||
@@ -338,7 +338,7 @@ class MultiChainWalletAdapter:
|
|||||||
|
|
||||||
# Deactivate wallet
|
# Deactivate wallet
|
||||||
wallet.is_active = False
|
wallet.is_active = False
|
||||||
wallet.updated_at = datetime.now(datetime.UTC)
|
wallet.updated_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import hashlib
|
|||||||
import json
|
import json
|
||||||
import secrets
|
import secrets
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from enum import StrEnum
|
from enum import StrEnum
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@@ -124,7 +124,7 @@ class EnhancedWalletAdapter(ABC):
|
|||||||
"""Securely sign a message"""
|
"""Securely sign a message"""
|
||||||
try:
|
try:
|
||||||
# Add timestamp and nonce for replay protection
|
# Add timestamp and nonce for replay protection
|
||||||
timestamp = str(int(datetime.now(datetime.UTC).timestamp()))
|
timestamp = str(int(datetime.now(timezone.utc).timestamp()))
|
||||||
nonce = secrets.token_hex(16)
|
nonce = secrets.token_hex(16)
|
||||||
|
|
||||||
message_to_sign = f"{message}:{timestamp}:{nonce}"
|
message_to_sign = f"{message}:{timestamp}:{nonce}"
|
||||||
@@ -194,7 +194,7 @@ class EthereumWalletAdapter(EnhancedWalletAdapter):
|
|||||||
"chain_type": self.chain_type.value,
|
"chain_type": self.chain_type.value,
|
||||||
"owner_address": owner_address,
|
"owner_address": owner_address,
|
||||||
"security_level": self.security_level.value,
|
"security_level": self.security_level.value,
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"status": WalletStatus.ACTIVE.value,
|
"status": WalletStatus.ACTIVE.value,
|
||||||
"security_config": security_config,
|
"security_config": security_config,
|
||||||
"nonce": 0,
|
"nonce": 0,
|
||||||
@@ -227,7 +227,7 @@ class EthereumWalletAdapter(EnhancedWalletAdapter):
|
|||||||
"chain_id": self.chain_id,
|
"chain_id": self.chain_id,
|
||||||
"eth_balance": eth_balance,
|
"eth_balance": eth_balance,
|
||||||
"token_balances": {},
|
"token_balances": {},
|
||||||
"last_updated": datetime.now(datetime.UTC).isoformat(),
|
"last_updated": datetime.now(timezone.utc).isoformat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get token balances if specified
|
# Get token balances if specified
|
||||||
@@ -304,7 +304,7 @@ class EthereumWalletAdapter(EnhancedWalletAdapter):
|
|||||||
"gas_limit": gas_limit,
|
"gas_limit": gas_limit,
|
||||||
"gas_price": gas_price,
|
"gas_price": gas_price,
|
||||||
"status": TransactionStatus.PENDING.value,
|
"status": TransactionStatus.PENDING.value,
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(f"Executed Ethereum transaction {tx_hash} from {from_address} to {to_address}")
|
logger.info(f"Executed Ethereum transaction {tx_hash} from {from_address} to {to_address}")
|
||||||
@@ -331,7 +331,7 @@ class EthereumWalletAdapter(EnhancedWalletAdapter):
|
|||||||
"gas_used": None,
|
"gas_used": None,
|
||||||
"effective_gas_price": None,
|
"effective_gas_price": None,
|
||||||
"logs": [],
|
"logs": [],
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get transaction details
|
# Get transaction details
|
||||||
@@ -348,7 +348,7 @@ class EthereumWalletAdapter(EnhancedWalletAdapter):
|
|||||||
"from": tx_data.get("from"),
|
"from": tx_data.get("from"),
|
||||||
"to": tx_data.get("to"),
|
"to": tx_data.get("to"),
|
||||||
"value": int(tx_data.get("value", "0x0"), 16),
|
"value": int(tx_data.get("value", "0x0"), 16),
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Exception classes and error response schemas for AITBC coordinator
|
|||||||
Provides structured error responses for consistent API error handling.
|
Provides structured error responses for consistent API error handling.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ class ServiceRegistry(BaseModel):
|
|||||||
"""Service registry containing all available services"""
|
"""Service registry containing all available services"""
|
||||||
|
|
||||||
version: str = Field("1.0.0", description="Registry version")
|
version: str = Field("1.0.0", description="Registry version")
|
||||||
last_updated: datetime = Field(default_factory=datetime.now(datetime.UTC), description="Last update time")
|
last_updated: datetime = Field(default_factory=datetime.now(timezone.utc), description="Last update time")
|
||||||
services: dict[str, ServiceDefinition] = Field(..., description="Service definitions by ID")
|
services: dict[str, ServiceDefinition] = Field(..., description="Service definitions by ID")
|
||||||
|
|
||||||
def get_service(self, service_id: str) -> ServiceDefinition | None:
|
def get_service(self, service_id: str) -> ServiceDefinition | None:
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Repository layer for confidential transactions
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
from sqlalchemy import and_, delete, select, update
|
from sqlalchemy import and_, delete, select, update
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
@@ -131,7 +131,7 @@ class ParticipantKeyRepository:
|
|||||||
stmt = (
|
stmt = (
|
||||||
update(ParticipantKeyDB)
|
update(ParticipantKeyDB)
|
||||||
.where(ParticipantKeyDB.participant_id == participant_id)
|
.where(ParticipantKeyDB.participant_id == participant_id)
|
||||||
.values(active=active, revoked_at=datetime.now(datetime.UTC) if not active else None, revoke_reason=reason)
|
.values(active=active, revoked_at=datetime.now(timezone.utc) if not active else None, revoke_reason=reason)
|
||||||
)
|
)
|
||||||
|
|
||||||
result = await session.execute(stmt)
|
result = await session.execute(stmt)
|
||||||
@@ -309,7 +309,7 @@ class AuditAuthorizationRepository:
|
|||||||
and_(
|
and_(
|
||||||
AuditAuthorizationDB.id == authorization_id,
|
AuditAuthorizationDB.id == authorization_id,
|
||||||
AuditAuthorizationDB.active,
|
AuditAuthorizationDB.active,
|
||||||
AuditAuthorizationDB.expires_at > datetime.now(datetime.UTC),
|
AuditAuthorizationDB.expires_at > datetime.now(timezone.utc),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -321,7 +321,7 @@ class AuditAuthorizationRepository:
|
|||||||
stmt = (
|
stmt = (
|
||||||
update(AuditAuthorizationDB)
|
update(AuditAuthorizationDB)
|
||||||
.where(AuditAuthorizationDB.id == authorization_id)
|
.where(AuditAuthorizationDB.id == authorization_id)
|
||||||
.values(active=False, revoked_at=datetime.now(datetime.UTC))
|
.values(active=False, revoked_at=datetime.now(timezone.utc))
|
||||||
)
|
)
|
||||||
|
|
||||||
result = await session.execute(stmt)
|
result = await session.execute(stmt)
|
||||||
@@ -331,7 +331,7 @@ class AuditAuthorizationRepository:
|
|||||||
|
|
||||||
async def cleanup_expired(self, session: AsyncSession) -> int:
|
async def cleanup_expired(self, session: AsyncSession) -> int:
|
||||||
"""Clean up expired authorizations"""
|
"""Clean up expired authorizations"""
|
||||||
stmt = update(AuditAuthorizationDB).where(AuditAuthorizationDB.expires_at < datetime.now(datetime.UTC)).values(active=False)
|
stmt = update(AuditAuthorizationDB).where(AuditAuthorizationDB.expires_at < datetime.now(timezone.utc)).values(active=False)
|
||||||
|
|
||||||
result = await session.execute(stmt)
|
result = await session.execute(stmt)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Cross-Chain Reputation Aggregator
|
|||||||
Aggregates reputation data from multiple blockchains and normalizes scores
|
Aggregates reputation data from multiple blockchains and normalizes scores
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from aitbc import get_logger
|
from aitbc import get_logger
|
||||||
@@ -147,7 +147,7 @@ class CrossChainReputationAggregator:
|
|||||||
{
|
{
|
||||||
"agent_id": agent_id,
|
"agent_id": agent_id,
|
||||||
"anomaly_type": "low_consistency",
|
"anomaly_type": "low_consistency",
|
||||||
"detected_at": datetime.now(datetime.UTC),
|
"detected_at": datetime.now(timezone.utc),
|
||||||
"description": f"Low consistency score: {aggregation.consistency_score:.2f}",
|
"description": f"Low consistency score: {aggregation.consistency_score:.2f}",
|
||||||
"severity": "high" if aggregation.consistency_score < 0.5 else "medium",
|
"severity": "high" if aggregation.consistency_score < 0.5 else "medium",
|
||||||
"consistency_score": aggregation.consistency_score,
|
"consistency_score": aggregation.consistency_score,
|
||||||
@@ -162,7 +162,7 @@ class CrossChainReputationAggregator:
|
|||||||
{
|
{
|
||||||
"agent_id": agent_id,
|
"agent_id": agent_id,
|
||||||
"anomaly_type": "high_variance",
|
"anomaly_type": "high_variance",
|
||||||
"detected_at": datetime.now(datetime.UTC),
|
"detected_at": datetime.now(timezone.utc),
|
||||||
"description": f"High score variance: {aggregation.score_variance:.2f}",
|
"description": f"High score variance: {aggregation.score_variance:.2f}",
|
||||||
"severity": "high" if aggregation.score_variance > 0.5 else "medium",
|
"severity": "high" if aggregation.score_variance > 0.5 else "medium",
|
||||||
"score_variance": aggregation.score_variance,
|
"score_variance": aggregation.score_variance,
|
||||||
@@ -180,7 +180,7 @@ class CrossChainReputationAggregator:
|
|||||||
{
|
{
|
||||||
"agent_id": agent_id,
|
"agent_id": agent_id,
|
||||||
"anomaly_type": "missing_chain_data",
|
"anomaly_type": "missing_chain_data",
|
||||||
"detected_at": datetime.now(datetime.UTC),
|
"detected_at": datetime.now(timezone.utc),
|
||||||
"description": f"Missing data for chains: {list(missing_chains)}",
|
"description": f"Missing data for chains: {list(missing_chains)}",
|
||||||
"severity": "medium",
|
"severity": "medium",
|
||||||
"missing_chains": list(missing_chains),
|
"missing_chains": list(missing_chains),
|
||||||
@@ -221,7 +221,7 @@ class CrossChainReputationAggregator:
|
|||||||
# Update reputation
|
# Update reputation
|
||||||
reputation.trust_score = new_score * 1000 # Convert to 0-1000 scale
|
reputation.trust_score = new_score * 1000 # Convert to 0-1000 scale
|
||||||
reputation.reputation_level = self._determine_reputation_level(new_score)
|
reputation.reputation_level = self._determine_reputation_level(new_score)
|
||||||
reputation.updated_at = datetime.now(datetime.UTC)
|
reputation.updated_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
# Create event record
|
# Create event record
|
||||||
event = ReputationEvent(
|
event = ReputationEvent(
|
||||||
@@ -231,7 +231,7 @@ class CrossChainReputationAggregator:
|
|||||||
trust_score_before=reputation.trust_score,
|
trust_score_before=reputation.trust_score,
|
||||||
trust_score_after=reputation.trust_score,
|
trust_score_after=reputation.trust_score,
|
||||||
event_data=update,
|
event_data=update,
|
||||||
occurred_at=datetime.now(datetime.UTC),
|
occurred_at=datetime.now(timezone.utc),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.session.add(event)
|
self.session.add(event)
|
||||||
@@ -242,8 +242,8 @@ class CrossChainReputationAggregator:
|
|||||||
agent_id=agent_id,
|
agent_id=agent_id,
|
||||||
trust_score=new_score * 1000,
|
trust_score=new_score * 1000,
|
||||||
reputation_level=self._determine_reputation_level(new_score),
|
reputation_level=self._determine_reputation_level(new_score),
|
||||||
created_at=datetime.now(datetime.UTC),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.now(datetime.UTC),
|
updated_at=datetime.now(timezone.utc),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.session.add(reputation)
|
self.session.add(reputation)
|
||||||
@@ -316,7 +316,7 @@ class CrossChainReputationAggregator:
|
|||||||
"reputation_distribution": distribution,
|
"reputation_distribution": distribution,
|
||||||
"total_transactions": total_transactions,
|
"total_transactions": total_transactions,
|
||||||
"success_rate": success_rate,
|
"success_rate": success_rate,
|
||||||
"last_updated": datetime.now(datetime.UTC),
|
"last_updated": datetime.now(timezone.utc),
|
||||||
}
|
}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -430,7 +430,7 @@ class CrossChainReputationAggregator:
|
|||||||
aggregation.score_variance = variance
|
aggregation.score_variance = variance
|
||||||
aggregation.score_range = score_range
|
aggregation.score_range = score_range
|
||||||
aggregation.consistency_score = consistency_score
|
aggregation.consistency_score = consistency_score
|
||||||
aggregation.last_updated = datetime.now(datetime.UTC)
|
aggregation.last_updated = datetime.now(timezone.utc)
|
||||||
else:
|
else:
|
||||||
aggregation = CrossChainReputationAggregation(
|
aggregation = CrossChainReputationAggregation(
|
||||||
agent_id=agent_id,
|
agent_id=agent_id,
|
||||||
@@ -441,8 +441,8 @@ class CrossChainReputationAggregator:
|
|||||||
score_range=score_range,
|
score_range=score_range,
|
||||||
consistency_score=consistency_score,
|
consistency_score=consistency_score,
|
||||||
verification_status="pending",
|
verification_status="pending",
|
||||||
created_at=datetime.now(datetime.UTC),
|
created_at=datetime.now(timezone.utc),
|
||||||
last_updated=datetime.now(datetime.UTC),
|
last_updated=datetime.now(timezone.utc),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.session.add(aggregation)
|
self.session.add(aggregation)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Cross-Chain Reputation Engine
|
|||||||
Core reputation calculation and aggregation engine for multi-chain agent reputation
|
Core reputation calculation and aggregation engine for multi-chain agent reputation
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from aitbc import get_logger
|
from aitbc import get_logger
|
||||||
@@ -57,8 +57,8 @@ class CrossChainReputationEngine:
|
|||||||
agent_id=agent_id,
|
agent_id=agent_id,
|
||||||
trust_score=score * 1000, # Convert to 0-1000 scale
|
trust_score=score * 1000, # Convert to 0-1000 scale
|
||||||
reputation_level=self._determine_reputation_level(score),
|
reputation_level=self._determine_reputation_level(score),
|
||||||
created_at=datetime.now(datetime.UTC),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.now(datetime.UTC),
|
updated_at=datetime.now(timezone.utc),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.session.add(new_reputation)
|
self.session.add(new_reputation)
|
||||||
@@ -152,8 +152,8 @@ class CrossChainReputationEngine:
|
|||||||
agent_id=agent_id,
|
agent_id=agent_id,
|
||||||
trust_score=max(0, min(1000, (base_score + impact_score) * 1000)),
|
trust_score=max(0, min(1000, (base_score + impact_score) * 1000)),
|
||||||
reputation_level=self._determine_reputation_level(base_score + impact_score),
|
reputation_level=self._determine_reputation_level(base_score + impact_score),
|
||||||
created_at=datetime.now(datetime.UTC),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.now(datetime.UTC),
|
updated_at=datetime.now(timezone.utc),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.session.add(reputation)
|
self.session.add(reputation)
|
||||||
@@ -164,7 +164,7 @@ class CrossChainReputationEngine:
|
|||||||
|
|
||||||
reputation.trust_score = new_score * 1000
|
reputation.trust_score = new_score * 1000
|
||||||
reputation.reputation_level = self._determine_reputation_level(new_score)
|
reputation.reputation_level = self._determine_reputation_level(new_score)
|
||||||
reputation.updated_at = datetime.now(datetime.UTC)
|
reputation.updated_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
# Create reputation event record
|
# Create reputation event record
|
||||||
event = ReputationEvent(
|
event = ReputationEvent(
|
||||||
@@ -174,7 +174,7 @@ class CrossChainReputationEngine:
|
|||||||
trust_score_before=reputation.trust_score - (impact_score * 1000),
|
trust_score_before=reputation.trust_score - (impact_score * 1000),
|
||||||
trust_score_after=reputation.trust_score,
|
trust_score_after=reputation.trust_score,
|
||||||
event_data=event_data,
|
event_data=event_data,
|
||||||
occurred_at=datetime.now(datetime.UTC),
|
occurred_at=datetime.now(timezone.utc),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.session.add(event)
|
self.session.add(event)
|
||||||
@@ -195,7 +195,7 @@ class CrossChainReputationEngine:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Get reputation events for the period
|
# Get reputation events for the period
|
||||||
cutoff_date = datetime.now(datetime.UTC) - timedelta(days=days)
|
cutoff_date = datetime.now(timezone.utc) - timedelta(days=days)
|
||||||
|
|
||||||
stmt = (
|
stmt = (
|
||||||
select(ReputationEvent)
|
select(ReputationEvent)
|
||||||
@@ -295,7 +295,7 @@ class CrossChainReputationEngine:
|
|||||||
|
|
||||||
reputation.trust_score = new_score * 1000
|
reputation.trust_score = new_score * 1000
|
||||||
reputation.reputation_level = self._determine_reputation_level(new_score)
|
reputation.reputation_level = self._determine_reputation_level(new_score)
|
||||||
reputation.updated_at = datetime.now(datetime.UTC)
|
reputation.updated_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
# Update transaction metrics if available
|
# Update transaction metrics if available
|
||||||
if "transaction_count" in transaction_data:
|
if "transaction_count" in transaction_data:
|
||||||
@@ -364,7 +364,7 @@ class CrossChainReputationEngine:
|
|||||||
aggregation.score_variance = variance
|
aggregation.score_variance = variance
|
||||||
aggregation.score_range = score_range
|
aggregation.score_range = score_range
|
||||||
aggregation.consistency_score = consistency_score
|
aggregation.consistency_score = consistency_score
|
||||||
aggregation.last_updated = datetime.now(datetime.UTC)
|
aggregation.last_updated = datetime.now(timezone.utc)
|
||||||
else:
|
else:
|
||||||
# Create new aggregation
|
# Create new aggregation
|
||||||
aggregation = CrossChainReputationAggregation(
|
aggregation = CrossChainReputationAggregation(
|
||||||
@@ -376,8 +376,8 @@ class CrossChainReputationEngine:
|
|||||||
score_range=score_range,
|
score_range=score_range,
|
||||||
consistency_score=consistency_score,
|
consistency_score=consistency_score,
|
||||||
verification_status="pending",
|
verification_status="pending",
|
||||||
created_at=datetime.now(datetime.UTC),
|
created_at=datetime.now(timezone.utc),
|
||||||
last_updated=datetime.now(datetime.UTC),
|
last_updated=datetime.now(timezone.utc),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.session.add(aggregation)
|
self.session.add(aggregation)
|
||||||
@@ -440,7 +440,7 @@ class CrossChainReputationEngine:
|
|||||||
"total_transactions": getattr(reputation, "transaction_count", 0),
|
"total_transactions": getattr(reputation, "transaction_count", 0),
|
||||||
"success_rate": getattr(reputation, "success_rate", 0.0),
|
"success_rate": getattr(reputation, "success_rate", 0.0),
|
||||||
"dispute_count": getattr(reputation, "dispute_count", 0),
|
"dispute_count": getattr(reputation, "dispute_count", 0),
|
||||||
"last_activity": getattr(reputation, "last_activity", datetime.now(datetime.UTC)),
|
"last_activity": getattr(reputation, "last_activity", datetime.now(timezone.utc)),
|
||||||
"cross_chain": {
|
"cross_chain": {
|
||||||
"aggregated_score": aggregation.aggregated_score if aggregation else 0.0,
|
"aggregated_score": aggregation.aggregated_score if aggregation else 0.0,
|
||||||
"chain_count": aggregation.chain_count if aggregation else 0,
|
"chain_count": aggregation.chain_count if aggregation else 0,
|
||||||
|
|||||||
@@ -358,7 +358,7 @@ class PricingError(BaseModel):
|
|||||||
error_code: str = Field(..., description="Error code")
|
error_code: str = Field(..., description="Error code")
|
||||||
message: str = Field(..., description="Error message")
|
message: str = Field(..., description="Error message")
|
||||||
details: dict[str, Any] | None = Field(None, description="Additional error details")
|
details: dict[str, Any] | None = Field(None, description="Additional error details")
|
||||||
timestamp: datetime = Field(default_factory=datetime.now(datetime.UTC), description="Error timestamp")
|
timestamp: datetime = Field(default_factory=datetime.now(timezone.utc), description="Error timestamp")
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
json_encoders = {datetime: lambda v: v.isoformat()}
|
json_encoders = {datetime: lambda v: v.isoformat()}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Python SDK for enterprise clients to integrate with AITBC platform
|
|||||||
import hashlib
|
import hashlib
|
||||||
import secrets
|
import secrets
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from enum import StrEnum
|
from enum import StrEnum
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ class EnterpriseClient:
|
|||||||
# Store tokens
|
# Store tokens
|
||||||
self.access_token = auth_data["access_token"]
|
self.access_token = auth_data["access_token"]
|
||||||
self.refresh_token = auth_data.get("refresh_token")
|
self.refresh_token = auth_data.get("refresh_token")
|
||||||
self.token_expires_at = datetime.now(datetime.UTC) + timedelta(seconds=auth_data["expires_in"])
|
self.token_expires_at = datetime.now(timezone.utc) + timedelta(seconds=auth_data["expires_in"])
|
||||||
|
|
||||||
# Update session headers
|
# Update session headers
|
||||||
self.session.headers["Authorization"] = f"Bearer {self.access_token}"
|
self.session.headers["Authorization"] = f"Bearer {self.access_token}"
|
||||||
@@ -175,7 +175,7 @@ class EnterpriseClient:
|
|||||||
async def _ensure_valid_token(self):
|
async def _ensure_valid_token(self):
|
||||||
"""Ensure we have a valid access token"""
|
"""Ensure we have a valid access token"""
|
||||||
|
|
||||||
if not self.access_token or (self.token_expires_at and datetime.now(datetime.UTC) >= self.token_expires_at):
|
if not self.access_token or (self.token_expires_at and datetime.now(timezone.utc) >= self.token_expires_at):
|
||||||
await self.authenticate()
|
await self.authenticate()
|
||||||
|
|
||||||
async def create_integration(self, integration_config: IntegrationConfig) -> APIResponse:
|
async def create_integration(self, integration_config: IntegrationConfig) -> APIResponse:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Base interfaces for cross-chain settlement bridges
|
|||||||
import json
|
import json
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ class SettlementMessage:
|
|||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
if self.created_at is None:
|
if self.created_at is None:
|
||||||
self.created_at = datetime.now(datetime.UTC)
|
self.created_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -69,7 +69,7 @@ class SettlementResult:
|
|||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
if self.created_at is None:
|
if self.created_at is None:
|
||||||
self.created_at = datetime.now(datetime.UTC)
|
self.created_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
|
|
||||||
class BridgeAdapter(ABC):
|
class BridgeAdapter(ABC):
|
||||||
@@ -220,7 +220,7 @@ class EthereumBridge(BridgeAdapter):
|
|||||||
transaction_hash="0x" + "0" * 64, # Mock hash
|
transaction_hash="0x" + "0" * 64, # Mock hash
|
||||||
gas_used=gas_estimate,
|
gas_used=gas_estimate,
|
||||||
fee_paid=int(self.config.default_fee),
|
fee_paid=int(self.config.default_fee),
|
||||||
completed_at=datetime.now(datetime.UTC)
|
completed_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Settlement hooks for coordinator API integration
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from aitbc import get_logger
|
from aitbc import get_logger
|
||||||
@@ -210,7 +210,7 @@ class SettlementHook:
|
|||||||
"""Generate a unique nonce for settlement"""
|
"""Generate a unique nonce for settlement"""
|
||||||
# This would generate a unique nonce
|
# This would generate a unique nonce
|
||||||
# For now, use timestamp
|
# For now, use timestamp
|
||||||
return int(datetime.now(datetime.UTC).timestamp())
|
return int(datetime.now(timezone.utc).timestamp())
|
||||||
|
|
||||||
async def _sign_settlement_message(self, job: Job) -> str:
|
async def _sign_settlement_message(self, job: Job) -> str:
|
||||||
"""Sign the settlement message"""
|
"""Sign the settlement message"""
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Bridge manager for cross-chain settlements
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from dataclasses import asdict
|
from dataclasses import asdict
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from .bridges.base import BridgeAdapter, BridgeConfig, BridgeError, BridgeStatus, SettlementMessage, SettlementResult
|
from .bridges.base import BridgeAdapter, BridgeConfig, BridgeError, BridgeStatus, SettlementMessage, SettlementResult
|
||||||
@@ -240,9 +240,9 @@ class BridgeManager:
|
|||||||
async def _monitor_settlement(self, message_id: str) -> None:
|
async def _monitor_settlement(self, message_id: str) -> None:
|
||||||
"""Monitor settlement until completion"""
|
"""Monitor settlement until completion"""
|
||||||
max_wait_time = timedelta(hours=1)
|
max_wait_time = timedelta(hours=1)
|
||||||
start_time = datetime.now(datetime.UTC)
|
start_time = datetime.now(timezone.utc)
|
||||||
|
|
||||||
while datetime.now(datetime.UTC) - start_time < max_wait_time:
|
while datetime.now(timezone.utc) - start_time < max_wait_time:
|
||||||
# Check status
|
# Check status
|
||||||
result = await self.get_settlement_status(message_id)
|
result = await self.get_settlement_status(message_id)
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Storage layer for cross-chain settlements
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from .bridges.base import BridgeStatus, SettlementMessage
|
from .bridges.base import BridgeStatus, SettlementMessage
|
||||||
@@ -49,7 +49,7 @@ class SettlementStorage:
|
|||||||
message.signature,
|
message.signature,
|
||||||
bridge_name,
|
bridge_name,
|
||||||
status.value,
|
status.value,
|
||||||
message.created_at or datetime.now(datetime.UTC),
|
message.created_at or datetime.now(timezone.utc),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ class SettlementStorage:
|
|||||||
return
|
return
|
||||||
|
|
||||||
updates.append(f"updated_at = ${param_count}")
|
updates.append(f"updated_at = ${param_count}")
|
||||||
params.append(datetime.now(datetime.UTC))
|
params.append(datetime.now(timezone.utc))
|
||||||
param_count += 1
|
param_count += 1
|
||||||
|
|
||||||
params.append(message_id)
|
params.append(message_id)
|
||||||
@@ -258,8 +258,8 @@ class InMemorySettlementStorage(SettlementStorage):
|
|||||||
"signature": message.signature,
|
"signature": message.signature,
|
||||||
"bridge_name": bridge_name,
|
"bridge_name": bridge_name,
|
||||||
"status": status.value,
|
"status": status.value,
|
||||||
"created_at": message.created_at or datetime.now(datetime.UTC),
|
"created_at": message.created_at or datetime.now(timezone.utc),
|
||||||
"updated_at": datetime.now(datetime.UTC),
|
"updated_at": datetime.now(timezone.utc),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def update_settlement(
|
async def update_settlement(
|
||||||
@@ -285,7 +285,7 @@ class InMemorySettlementStorage(SettlementStorage):
|
|||||||
if completed_at is not None:
|
if completed_at is not None:
|
||||||
settlement["completed_at"] = completed_at
|
settlement["completed_at"] = completed_at
|
||||||
|
|
||||||
settlement["updated_at"] = datetime.now(datetime.UTC)
|
settlement["updated_at"] = datetime.now(timezone.utc)
|
||||||
|
|
||||||
async def get_settlement(self, message_id: str) -> dict[str, Any] | None:
|
async def get_settlement(self, message_id: str) -> dict[str, Any] | None:
|
||||||
async with self._lock:
|
async with self._lock:
|
||||||
@@ -316,7 +316,7 @@ class InMemorySettlementStorage(SettlementStorage):
|
|||||||
|
|
||||||
# Time range filtering
|
# Time range filtering
|
||||||
if time_range is not None:
|
if time_range is not None:
|
||||||
cutoff = datetime.now(datetime.UTC) - timedelta(hours=time_range)
|
cutoff = datetime.now(timezone.utc) - timedelta(hours=time_range)
|
||||||
if settlement["created_at"] < cutoff:
|
if settlement["created_at"] < cutoff:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -345,7 +345,7 @@ class InMemorySettlementStorage(SettlementStorage):
|
|||||||
|
|
||||||
async def cleanup_old_settlements(self, days: int = 30) -> int:
|
async def cleanup_old_settlements(self, days: int = 30) -> int:
|
||||||
async with self._lock:
|
async with self._lock:
|
||||||
cutoff = datetime.now(datetime.UTC) - timedelta(days=days)
|
cutoff = datetime.now(timezone.utc) - timedelta(days=days)
|
||||||
|
|
||||||
to_delete = [
|
to_delete = [
|
||||||
msg_id
|
msg_id
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from sqlalchemy.orm import Session, sessionmaker
|
|||||||
from aitbc import get_logger
|
from aitbc import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
from .config_pg import settings
|
from .config_pg import settings
|
||||||
|
|
||||||
@@ -237,6 +237,6 @@ def check_db_health() -> dict[str, Any]:
|
|||||||
try:
|
try:
|
||||||
adapter = get_db_adapter()
|
adapter = get_db_adapter()
|
||||||
adapter.execute_query("SELECT 1 as health_check")
|
adapter.execute_query("SELECT 1 as health_check")
|
||||||
return {"status": "healthy", "database": "postgresql", "timestamp": datetime.now(datetime.UTC).isoformat()}
|
return {"status": "healthy", "database": "postgresql", "timestamp": datetime.now(timezone.utc).isoformat()}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"status": "unhealthy", "error": str(e), "timestamp": datetime.now(datetime.UTC).isoformat()}
|
return {"status": "unhealthy", "error": str(e), "timestamp": datetime.now(timezone.utc).isoformat()}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class GovernanceProposal(SQLModel, table=True):
|
|||||||
target: dict[str, Any] | None = Field(default_factory=dict, sa_column=Column(JSON))
|
target: dict[str, Any] | None = Field(default_factory=dict, sa_column=Column(JSON))
|
||||||
proposer: str = Field(max_length=255, index=True)
|
proposer: str = Field(max_length=255, index=True)
|
||||||
status: str = Field(default="active", max_length=20) # active, passed, rejected, executed, expired
|
status: str = Field(default="active", max_length=20) # active, passed, rejected, executed, expired
|
||||||
created_at: datetime = Field(default_factory=datetime.now(datetime.UTC))
|
created_at: datetime = Field(default_factory=datetime.now(timezone.utc))
|
||||||
voting_deadline: datetime
|
voting_deadline: datetime
|
||||||
quorum_threshold: float = Field(default=0.1) # Percentage of total voting power
|
quorum_threshold: float = Field(default=0.1) # Percentage of total voting power
|
||||||
approval_threshold: float = Field(default=0.5) # Percentage of votes in favor
|
approval_threshold: float = Field(default=0.5) # Percentage of votes in favor
|
||||||
@@ -40,7 +40,7 @@ class ProposalVote(SQLModel, table=True):
|
|||||||
vote: str = Field(max_length=10) # for, against, abstain
|
vote: str = Field(max_length=10) # for, against, abstain
|
||||||
voting_power: int = Field(default=0) # Amount of voting power at time of vote
|
voting_power: int = Field(default=0) # Amount of voting power at time of vote
|
||||||
reason: str | None = Field(max_length=500)
|
reason: str | None = Field(max_length=500)
|
||||||
voted_at: datetime = Field(default_factory=datetime.now(datetime.UTC))
|
voted_at: datetime = Field(default_factory=datetime.now(timezone.utc))
|
||||||
|
|
||||||
# Relationships
|
# Relationships
|
||||||
proposal: GovernanceProposal = Relationship(back_populates="votes")
|
proposal: GovernanceProposal = Relationship(back_populates="votes")
|
||||||
@@ -57,7 +57,7 @@ class TreasuryTransaction(SQLModel, table=True):
|
|||||||
token: str = Field(default="AITBC", max_length=20)
|
token: str = Field(default="AITBC", max_length=20)
|
||||||
transaction_hash: str | None = Field(max_length=255)
|
transaction_hash: str | None = Field(max_length=255)
|
||||||
status: str = Field(default="pending", max_length=20) # pending, confirmed, failed
|
status: str = Field(default="pending", max_length=20) # pending, confirmed, failed
|
||||||
created_at: datetime = Field(default_factory=datetime.now(datetime.UTC))
|
created_at: datetime = Field(default_factory=datetime.now(timezone.utc))
|
||||||
confirmed_at: datetime | None = None
|
confirmed_at: datetime | None = None
|
||||||
memo: str | None = Field(max_length=500)
|
memo: str | None = Field(max_length=500)
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ class GovernanceParameter(SQLModel, table=True):
|
|||||||
min_value: str | None = Field(max_length=100)
|
min_value: str | None = Field(max_length=100)
|
||||||
max_value: str | None = Field(max_length=100)
|
max_value: str | None = Field(max_length=100)
|
||||||
value_type: str = Field(max_length=20) # string, number, boolean, json
|
value_type: str = Field(max_length=20) # string, number, boolean, json
|
||||||
updated_at: datetime = Field(default_factory=datetime.now(datetime.UTC))
|
updated_at: datetime = Field(default_factory=datetime.now(timezone.utc))
|
||||||
updated_by_proposal: str | None = Field(foreign_key="governanceproposal.id")
|
updated_by_proposal: str | None = Field(foreign_key="governanceproposal.id")
|
||||||
|
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ class VotingPowerSnapshot(SQLModel, table=True):
|
|||||||
id: str = Field(default_factory=lambda: str(uuid4()), primary_key=True)
|
id: str = Field(default_factory=lambda: str(uuid4()), primary_key=True)
|
||||||
user_id: str = Field(max_length=255, index=True)
|
user_id: str = Field(max_length=255, index=True)
|
||||||
voting_power: int
|
voting_power: int
|
||||||
snapshot_time: datetime = Field(default_factory=datetime.now(datetime.UTC), index=True)
|
snapshot_time: datetime = Field(default_factory=datetime.now(timezone.utc), index=True)
|
||||||
block_number: int | None = Field(index=True)
|
block_number: int | None = Field(index=True)
|
||||||
|
|
||||||
model_config = ConfigDict(
|
model_config = ConfigDict(
|
||||||
@@ -101,7 +101,7 @@ class ProtocolUpgrade(SQLModel, table=True):
|
|||||||
upgrade_type: str = Field(max_length=50) # hard_fork, soft_fork, patch
|
upgrade_type: str = Field(max_length=50) # hard_fork, soft_fork, patch
|
||||||
activation_block: int | None
|
activation_block: int | None
|
||||||
status: str = Field(default="pending", max_length=20) # pending, active, failed
|
status: str = Field(default="pending", max_length=20) # pending, active, failed
|
||||||
created_at: datetime = Field(default_factory=datetime.now(datetime.UTC))
|
created_at: datetime = Field(default_factory=datetime.now(timezone.utc))
|
||||||
activated_at: datetime | None = None
|
activated_at: datetime | None = None
|
||||||
rollback_available: bool = Field(default=False)
|
rollback_available: bool = Field(default=False)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from urllib import error, request
|
from urllib import error, request
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ class AlertDispatcher:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self._deliver(name, alert)
|
self._deliver(name, alert)
|
||||||
self._last_sent[name] = datetime.now(datetime.UTC)
|
self._last_sent[name] = datetime.now(timezone.utc)
|
||||||
results["sent"].append(name)
|
results["sent"].append(name)
|
||||||
self._record_alert(name, alert, delivery_status="sent")
|
self._record_alert(name, alert, delivery_status="sent")
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@@ -62,7 +62,7 @@ class AlertDispatcher:
|
|||||||
last_sent = self._last_sent.get(name)
|
last_sent = self._last_sent.get(name)
|
||||||
if last_sent is None:
|
if last_sent is None:
|
||||||
return False
|
return False
|
||||||
return datetime.now(datetime.UTC) - last_sent < timedelta(seconds=self.cooldown_seconds)
|
return datetime.now(timezone.utc) - last_sent < timedelta(seconds=self.cooldown_seconds)
|
||||||
|
|
||||||
def _record_alert(
|
def _record_alert(
|
||||||
self,
|
self,
|
||||||
@@ -71,9 +71,9 @@ class AlertDispatcher:
|
|||||||
delivery_status: str,
|
delivery_status: str,
|
||||||
error_message: str | None = None,
|
error_message: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
timestamp = datetime.now(datetime.UTC).isoformat()
|
timestamp = datetime.now(timezone.utc).isoformat()
|
||||||
record = {
|
record = {
|
||||||
"id": f"metrics_alert_{name}_{int(datetime.now(datetime.UTC).timestamp() * 1000)}",
|
"id": f"metrics_alert_{name}_{int(datetime.now(timezone.utc).timestamp() * 1000)}",
|
||||||
"deployment_id": None,
|
"deployment_id": None,
|
||||||
"severity": alert.get("status", "critical"),
|
"severity": alert.get("status", "critical"),
|
||||||
"message": f"Threshold triggered for {name}",
|
"message": f"Threshold triggered for {name}",
|
||||||
@@ -96,7 +96,7 @@ class AlertDispatcher:
|
|||||||
"status": alert.get("status", "critical"),
|
"status": alert.get("status", "critical"),
|
||||||
"value": alert.get("value"),
|
"value": alert.get("value"),
|
||||||
"threshold": alert.get("threshold"),
|
"threshold": alert.get("threshold"),
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if webhook_url:
|
if webhook_url:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import sys
|
|||||||
import pytest
|
import pytest
|
||||||
import asyncio
|
import asyncio
|
||||||
from unittest.mock import AsyncMock, patch
|
from unittest.mock import AsyncMock, patch
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
from app.agent_identity.sdk.client import AgentIdentityClient
|
from app.agent_identity.sdk.client import AgentIdentityClient
|
||||||
from app.agent_identity.sdk.models import (
|
from app.agent_identity.sdk.models import (
|
||||||
@@ -328,16 +328,16 @@ class TestModels:
|
|||||||
status=IdentityStatus.ACTIVE,
|
status=IdentityStatus.ACTIVE,
|
||||||
verification_level=VerificationType.BASIC,
|
verification_level=VerificationType.BASIC,
|
||||||
is_verified=True,
|
is_verified=True,
|
||||||
verified_at=datetime.now(datetime.UTC),
|
verified_at=datetime.now(timezone.utc),
|
||||||
supported_chains=['1', '137'],
|
supported_chains=['1', '137'],
|
||||||
primary_chain=1,
|
primary_chain=1,
|
||||||
reputation_score=85.5,
|
reputation_score=85.5,
|
||||||
total_transactions=100,
|
total_transactions=100,
|
||||||
successful_transactions=95,
|
successful_transactions=95,
|
||||||
success_rate=0.95,
|
success_rate=0.95,
|
||||||
created_at=datetime.now(datetime.UTC),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.now(datetime.UTC),
|
updated_at=datetime.now(timezone.utc),
|
||||||
last_activity=datetime.now(datetime.UTC),
|
last_activity=datetime.now(timezone.utc),
|
||||||
metadata={'key': 'value'},
|
metadata={'key': 'value'},
|
||||||
tags=['test', 'agent']
|
tags=['test', 'agent']
|
||||||
)
|
)
|
||||||
@@ -358,14 +358,14 @@ class TestModels:
|
|||||||
chain_type=ChainType.ETHEREUM,
|
chain_type=ChainType.ETHEREUM,
|
||||||
chain_address='0x123...',
|
chain_address='0x123...',
|
||||||
is_verified=True,
|
is_verified=True,
|
||||||
verified_at=datetime.now(datetime.UTC),
|
verified_at=datetime.now(timezone.utc),
|
||||||
wallet_address='0x456...',
|
wallet_address='0x456...',
|
||||||
wallet_type='agent-wallet',
|
wallet_type='agent-wallet',
|
||||||
chain_metadata={'test': 'data'},
|
chain_metadata={'test': 'data'},
|
||||||
last_transaction=datetime.now(datetime.UTC),
|
last_transaction=datetime.now(timezone.utc),
|
||||||
transaction_count=10,
|
transaction_count=10,
|
||||||
created_at=datetime.now(datetime.UTC),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.now(datetime.UTC)
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
assert mapping.id == 'mapping_123'
|
assert mapping.id == 'mapping_123'
|
||||||
@@ -391,10 +391,10 @@ class TestModels:
|
|||||||
requires_multisig=False,
|
requires_multisig=False,
|
||||||
multisig_threshold=1,
|
multisig_threshold=1,
|
||||||
multisig_signers=['0x123...'],
|
multisig_signers=['0x123...'],
|
||||||
last_transaction=datetime.now(datetime.UTC),
|
last_transaction=datetime.now(timezone.utc),
|
||||||
transaction_count=5,
|
transaction_count=5,
|
||||||
created_at=datetime.now(datetime.UTC),
|
created_at=datetime.now(timezone.utc),
|
||||||
updated_at=datetime.now(datetime.UTC)
|
updated_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
assert wallet.id == 'wallet_123'
|
assert wallet.id == 'wallet_123'
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import sys
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from unittest.mock import MagicMock, AsyncMock, patch
|
from unittest.mock import MagicMock, AsyncMock, patch
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
@@ -51,9 +51,9 @@ class FakeQuota:
|
|||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
if self.period_start is None:
|
if self.period_start is None:
|
||||||
self.period_start = datetime.now(datetime.UTC) - timedelta(hours=1)
|
self.period_start = datetime.now(timezone.utc) - timedelta(hours=1)
|
||||||
if self.period_end is None:
|
if self.period_end is None:
|
||||||
self.period_end = datetime.now(datetime.UTC) + timedelta(hours=23)
|
self.period_end = datetime.now(timezone.utc) + timedelta(hours=23)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -93,7 +93,7 @@ class InMemoryBillingStore:
|
|||||||
return self.tenants.get(tenant_id)
|
return self.tenants.get(tenant_id)
|
||||||
|
|
||||||
def get_active_quota(self, tenant_id: str, resource_type: str):
|
def get_active_quota(self, tenant_id: str, resource_type: str):
|
||||||
now = datetime.now(datetime.UTC)
|
now = datetime.now(timezone.utc)
|
||||||
for q in self.quotas:
|
for q in self.quotas:
|
||||||
if (q.tenant_id == tenant_id
|
if (q.tenant_id == tenant_id
|
||||||
and q.resource_type == resource_type
|
and q.resource_type == resource_type
|
||||||
@@ -119,7 +119,7 @@ async def apply_credit(store: InMemoryBillingStore, tenant_id: str, amount: Deci
|
|||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"amount": amount,
|
"amount": amount,
|
||||||
"reason": reason,
|
"reason": reason,
|
||||||
"timestamp": datetime.now(datetime.UTC),
|
"timestamp": datetime.now(timezone.utc),
|
||||||
})
|
})
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ async def apply_charge(store: InMemoryBillingStore, tenant_id: str, amount: Deci
|
|||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"amount": amount,
|
"amount": amount,
|
||||||
"reason": reason,
|
"reason": reason,
|
||||||
"timestamp": datetime.now(datetime.UTC),
|
"timestamp": datetime.now(timezone.utc),
|
||||||
})
|
})
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@ async def adjust_quota(
|
|||||||
|
|
||||||
async def reset_daily_quotas(store: InMemoryBillingStore) -> int:
|
async def reset_daily_quotas(store: InMemoryBillingStore) -> int:
|
||||||
"""Reset used_value to 0 for all daily quotas whose period has ended."""
|
"""Reset used_value to 0 for all daily quotas whose period has ended."""
|
||||||
now = datetime.now(datetime.UTC)
|
now = datetime.now(timezone.utc)
|
||||||
count = 0
|
count = 0
|
||||||
for q in store.quotas:
|
for q in store.quotas:
|
||||||
if q.period_type == "daily" and q.is_active and q.period_end <= now:
|
if q.period_type == "daily" and q.is_active and q.period_end <= now:
|
||||||
@@ -197,7 +197,7 @@ async def generate_monthly_invoices(store: InMemoryBillingStore) -> list[str]:
|
|||||||
if not tenant_usage:
|
if not tenant_usage:
|
||||||
continue
|
continue
|
||||||
total = sum(r.total_cost for r in tenant_usage)
|
total = sum(r.total_cost for r in tenant_usage)
|
||||||
inv_id = f"INV-{tenant.slug}-{datetime.now(datetime.UTC).strftime('%Y%m')}-{len(generated)+1:04d}"
|
inv_id = f"INV-{tenant.slug}-{datetime.now(timezone.utc).strftime('%Y%m')}-{len(generated)+1:04d}"
|
||||||
store.invoices_generated.append(inv_id)
|
store.invoices_generated.append(inv_id)
|
||||||
generated.append(inv_id)
|
generated.append(inv_id)
|
||||||
return generated
|
return generated
|
||||||
@@ -254,8 +254,8 @@ def store():
|
|||||||
id="q2", tenant_id="t1", resource_type="api_calls",
|
id="q2", tenant_id="t1", resource_type="api_calls",
|
||||||
limit_value=Decimal("10000"), used_value=Decimal("5000"),
|
limit_value=Decimal("10000"), used_value=Decimal("5000"),
|
||||||
period_type="daily",
|
period_type="daily",
|
||||||
period_start=datetime.now(datetime.UTC) - timedelta(days=2),
|
period_start=datetime.now(timezone.utc) - timedelta(days=2),
|
||||||
period_end=datetime.now(datetime.UTC) - timedelta(hours=1), # expired
|
period_end=datetime.now(timezone.utc) - timedelta(hours=1), # expired
|
||||||
))
|
))
|
||||||
return s
|
return s
|
||||||
|
|
||||||
@@ -345,7 +345,7 @@ class TestResetDailyQuotas:
|
|||||||
assert count == 1 # q2 is expired daily
|
assert count == 1 # q2 is expired daily
|
||||||
q2 = store.quotas[1]
|
q2 = store.quotas[1]
|
||||||
assert q2.used_value == Decimal("0")
|
assert q2.used_value == Decimal("0")
|
||||||
assert q2.period_end > datetime.now(datetime.UTC)
|
assert q2.period_end > datetime.now(timezone.utc)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_does_not_reset_active_quotas(self, store):
|
async def test_does_not_reset_active_quotas(self, store):
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Handles real exchange connections and trading operations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any, List, Optional
|
from typing import Dict, Any, List, Optional
|
||||||
import aiohttp
|
import aiohttp
|
||||||
@@ -54,7 +54,7 @@ async def root():
|
|||||||
return {
|
return {
|
||||||
"service": "AITBC Exchange Integration",
|
"service": "AITBC Exchange Integration",
|
||||||
"status": "running",
|
"status": "running",
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ async def register_exchange(registration: ExchangeRegistration):
|
|||||||
"sandbox": registration.sandbox,
|
"sandbox": registration.sandbox,
|
||||||
"description": registration.description,
|
"description": registration.description,
|
||||||
"connected": False,
|
"connected": False,
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"last_sync": None,
|
"last_sync": None,
|
||||||
"trading_pairs": []
|
"trading_pairs": []
|
||||||
}
|
}
|
||||||
@@ -116,7 +116,7 @@ async def connect_exchange(exchange_id: str):
|
|||||||
await asyncio.sleep(1) # Simulate connection delay
|
await asyncio.sleep(1) # Simulate connection delay
|
||||||
|
|
||||||
exchange["connected"] = True
|
exchange["connected"] = True
|
||||||
exchange["last_sync"] = datetime.now(datetime.UTC).isoformat()
|
exchange["last_sync"] = datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
logger.info(f"Exchange connected: {exchange_id}")
|
logger.info(f"Exchange connected: {exchange_id}")
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ async def create_trading_pair(pair: TradingPair):
|
|||||||
"price_precision": pair.price_precision,
|
"price_precision": pair.price_precision,
|
||||||
"quantity_precision": pair.quantity_precision,
|
"quantity_precision": pair.quantity_precision,
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"current_price": None,
|
"current_price": None,
|
||||||
"volume_24h": 0.0,
|
"volume_24h": 0.0,
|
||||||
"orders": []
|
"orders": []
|
||||||
@@ -186,7 +186,7 @@ async def create_order(order: OrderRequest):
|
|||||||
raise HTTPException(status_code=404, detail="Trading pair not found")
|
raise HTTPException(status_code=404, detail="Trading pair not found")
|
||||||
|
|
||||||
# Generate order ID
|
# Generate order ID
|
||||||
order_id = f"order_{int(datetime.now(datetime.UTC).timestamp())}"
|
order_id = f"order_{int(datetime.now(timezone.utc).timestamp())}"
|
||||||
|
|
||||||
# Create order
|
# Create order
|
||||||
order_data = {
|
order_data = {
|
||||||
@@ -197,7 +197,7 @@ async def create_order(order: OrderRequest):
|
|||||||
"quantity": order.quantity,
|
"quantity": order.quantity,
|
||||||
"price": order.price,
|
"price": order.price,
|
||||||
"status": "submitted",
|
"status": "submitted",
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"filled_quantity": 0.0,
|
"filled_quantity": 0.0,
|
||||||
"remaining_quantity": order.quantity,
|
"remaining_quantity": order.quantity,
|
||||||
"average_price": None
|
"average_price": None
|
||||||
@@ -216,7 +216,7 @@ async def create_order(order: OrderRequest):
|
|||||||
order_data["filled_quantity"] = order.quantity
|
order_data["filled_quantity"] = order.quantity
|
||||||
order_data["remaining_quantity"] = 0.0
|
order_data["remaining_quantity"] = 0.0
|
||||||
order_data["average_price"] = order.price or 0.00001 # Default price for demo
|
order_data["average_price"] = order.price or 0.00001 # Default price for demo
|
||||||
order_data["filled_at"] = datetime.now(datetime.UTC).isoformat()
|
order_data["filled_at"] = datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
logger.info(f"Order created and filled: {order_id}")
|
logger.info(f"Order created and filled: {order_id}")
|
||||||
|
|
||||||
@@ -263,7 +263,7 @@ async def update_market_price(pair_id: str, price_data: Dict[str, Any]):
|
|||||||
pair = trading_pairs[pair_id]
|
pair = trading_pairs[pair_id]
|
||||||
pair["current_price"] = price_data.get("price")
|
pair["current_price"] = price_data.get("price")
|
||||||
pair["volume_24h"] = price_data.get("volume", pair["volume_24h"])
|
pair["volume_24h"] = price_data.get("volume", pair["volume_24h"])
|
||||||
pair["last_price_update"] = datetime.now(datetime.UTC).isoformat()
|
pair["last_price_update"] = datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"pair_id": pair_id,
|
"pair_id": pair_id,
|
||||||
@@ -286,7 +286,7 @@ async def get_market_data():
|
|||||||
return {
|
return {
|
||||||
"market_data": market_data,
|
"market_data": market_data,
|
||||||
"total_pairs": len(market_data),
|
"total_pairs": len(market_data),
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Background task for simulating market data
|
# Background task for simulating market data
|
||||||
@@ -305,7 +305,7 @@ async def simulate_market_data():
|
|||||||
|
|
||||||
pair["current_price"] = new_price
|
pair["current_price"] = new_price
|
||||||
pair["volume_24h"] += random.uniform(100, 1000)
|
pair["volume_24h"] += random.uniform(100, 1000)
|
||||||
pair["last_price_update"] = datetime.now(datetime.UTC).isoformat()
|
pair["last_price_update"] = datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
# Start background task on startup
|
# Start background task on startup
|
||||||
@app.on_event("startup")
|
@app.on_event("startup")
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
FastAPI backend for the AITBC Trade Exchange
|
FastAPI backend for the AITBC Trade Exchange
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from fastapi import FastAPI, Depends, HTTPException, status, Header
|
from fastapi import FastAPI, Depends, HTTPException, status, Header
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
@@ -123,7 +123,7 @@ def create_mock_trades(db: Session):
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
# Create mock trades over the last hour
|
# Create mock trades over the last hour
|
||||||
now = datetime.now(datetime.UTC)
|
now = datetime.now(timezone.utc)
|
||||||
trades = []
|
trades = []
|
||||||
|
|
||||||
for i in range(20):
|
for i in range(20):
|
||||||
@@ -278,7 +278,7 @@ def try_match_order(order: Order, db: Session):
|
|||||||
amount=trade_amount,
|
amount=trade_amount,
|
||||||
price=match.price,
|
price=match.price,
|
||||||
total=trade_total,
|
total=trade_total,
|
||||||
trade_hash=f"trade_{datetime.now(datetime.UTC).timestamp()}"
|
trade_hash=f"trade_{datetime.now(timezone.utc).timestamp()}"
|
||||||
)
|
)
|
||||||
|
|
||||||
db.add(trade)
|
db.add(trade)
|
||||||
@@ -344,7 +344,7 @@ def logout_user(token: str = Header(..., alias="Authorization")):
|
|||||||
@app.get("/api/health")
|
@app.get("/api/health")
|
||||||
def health_check():
|
def health_check():
|
||||||
"""Health check endpoint"""
|
"""Health check endpoint"""
|
||||||
return {"status": "ok", "timestamp": datetime.now(datetime.UTC)}
|
return {"status": "ok", "timestamp": datetime.now(timezone.utc)}
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class User(Base):
|
|||||||
password_hash = Column(String(255), nullable=False)
|
password_hash = Column(String(255), nullable=False)
|
||||||
bitcoin_address = Column(String(100), unique=True, nullable=False)
|
bitcoin_address = Column(String(100), unique=True, nullable=False)
|
||||||
aitbc_address = Column(String(100), unique=True, nullable=False)
|
aitbc_address = Column(String(100), unique=True, nullable=False)
|
||||||
created_at = Column(DateTime, default=datetime.now(datetime.UTC))
|
created_at = Column(DateTime, default=datetime.now(timezone.utc))
|
||||||
is_active = Column(Boolean, default=True)
|
is_active = Column(Boolean, default=True)
|
||||||
|
|
||||||
# Relationships
|
# Relationships
|
||||||
@@ -46,8 +46,8 @@ class Order(Base):
|
|||||||
filled = Column(Float, default=0.0) # Amount filled
|
filled = Column(Float, default=0.0) # Amount filled
|
||||||
remaining = Column(Float, nullable=False) # Amount remaining to fill
|
remaining = Column(Float, nullable=False) # Amount remaining to fill
|
||||||
status = Column(String(20), default='OPEN') # OPEN, PARTIALLY_FILLED, FILLED, CANCELLED
|
status = Column(String(20), default='OPEN') # OPEN, PARTIALLY_FILLED, FILLED, CANCELLED
|
||||||
created_at = Column(DateTime, default=datetime.now(datetime.UTC))
|
created_at = Column(DateTime, default=datetime.now(timezone.utc))
|
||||||
updated_at = Column(DateTime, default=datetime.now(datetime.UTC), onupdate=datetime.now(datetime.UTC))
|
updated_at = Column(DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc))
|
||||||
|
|
||||||
# Relationships
|
# Relationships
|
||||||
user = relationship("User", back_populates="orders")
|
user = relationship("User", back_populates="orders")
|
||||||
@@ -74,7 +74,7 @@ class Trade(Base):
|
|||||||
price = Column(Float, nullable=False) # Trade price in BTC
|
price = Column(Float, nullable=False) # Trade price in BTC
|
||||||
total = Column(Float, nullable=False) # Total value in BTC
|
total = Column(Float, nullable=False) # Total value in BTC
|
||||||
trade_hash = Column(String(100), unique=True, nullable=False) # Blockchain transaction hash
|
trade_hash = Column(String(100), unique=True, nullable=False) # Blockchain transaction hash
|
||||||
created_at = Column(DateTime, default=datetime.now(datetime.UTC))
|
created_at = Column(DateTime, default=datetime.now(timezone.utc))
|
||||||
|
|
||||||
# Relationships
|
# Relationships
|
||||||
buyer = relationship("User", back_populates="trades", foreign_keys=[buyer_id])
|
buyer = relationship("User", back_populates="trades", foreign_keys=[buyer_id])
|
||||||
@@ -100,7 +100,7 @@ class Balance(Base):
|
|||||||
aitbc_balance = Column(Float, default=0.0)
|
aitbc_balance = Column(Float, default=0.0)
|
||||||
btc_locked = Column(Float, default=0.0) # Locked in open orders
|
btc_locked = Column(Float, default=0.0) # Locked in open orders
|
||||||
aitbc_locked = Column(Float, default=0.0) # Locked in open orders
|
aitbc_locked = Column(Float, default=0.0) # Locked in open orders
|
||||||
updated_at = Column(DateTime, default=datetime.now(datetime.UTC), onupdate=datetime.now(datetime.UTC))
|
updated_at = Column(DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc))
|
||||||
|
|
||||||
# Relationship
|
# Relationship
|
||||||
user = relationship("User")
|
user = relationship("User")
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import asyncio
|
|||||||
import ccxt
|
import ccxt
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from typing import Dict, List, Optional, Any, Tuple
|
from typing import Dict, List, Optional, Any, Tuple
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
@@ -105,7 +105,7 @@ class RealExchangeManager:
|
|||||||
self.health_status[exchange_name] = ExchangeHealth(
|
self.health_status[exchange_name] = ExchangeHealth(
|
||||||
status=ExchangeStatus.CONNECTED,
|
status=ExchangeStatus.CONNECTED,
|
||||||
latency_ms=0.0,
|
latency_ms=0.0,
|
||||||
last_check=datetime.now(datetime.UTC)
|
last_check=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info(f"✅ Connected to {exchange_name}")
|
logger.info(f"✅ Connected to {exchange_name}")
|
||||||
@@ -116,7 +116,7 @@ class RealExchangeManager:
|
|||||||
self.health_status[exchange_name] = ExchangeHealth(
|
self.health_status[exchange_name] = ExchangeHealth(
|
||||||
status=ExchangeStatus.ERROR,
|
status=ExchangeStatus.ERROR,
|
||||||
latency_ms=0.0,
|
latency_ms=0.0,
|
||||||
last_check=datetime.now(datetime.UTC),
|
last_check=datetime.now(timezone.utc),
|
||||||
error_message=str(e)
|
error_message=str(e)
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"""Seed initial market price for the exchange"""
|
"""Seed initial market price for the exchange"""
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from aitbc.constants import DATA_DIR
|
from aitbc.constants import DATA_DIR
|
||||||
|
|
||||||
def seed_initial_price():
|
def seed_initial_price():
|
||||||
@@ -27,7 +27,7 @@ def seed_initial_price():
|
|||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
INSERT INTO trades (amount, price, total, created_at)
|
INSERT INTO trades (amount, price, total, created_at)
|
||||||
VALUES (?, ?, ?, ?)
|
VALUES (?, ?, ?, ?)
|
||||||
''', (amount, price, total, datetime.now(datetime.UTC)))
|
''', (amount, price, total, datetime.now(timezone.utc)))
|
||||||
|
|
||||||
# Also create some initial orders for liquidity
|
# Also create some initial orders for liquidity
|
||||||
initial_orders = [
|
initial_orders = [
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Simple FastAPI backend for the AITBC Trade Exchange (Python 3.13 compatible)
|
|||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import random
|
import random
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import json
|
|||||||
import urllib.request
|
import urllib.request
|
||||||
import psycopg2
|
import psycopg2
|
||||||
from psycopg2.extras import RealDictCursor
|
from psycopg2.extras import RealDictCursor
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import random
|
import random
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ class ExchangeAPIHandler(BaseHTTPRequestHandler):
|
|||||||
self.send_json_response({
|
self.send_json_response({
|
||||||
'status': 'ok',
|
'status': 'ok',
|
||||||
'database': 'postgresql',
|
'database': 'postgresql',
|
||||||
'timestamp': datetime.now(datetime.UTC).isoformat()
|
'timestamp': datetime.now(timezone.utc).isoformat()
|
||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.send_json_response({
|
self.send_json_response({
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
|
|
||||||
from exchange_api import OrderCreate, OrderResponse, TradeResponse, OrderBookResponse
|
from exchange_api import OrderCreate, OrderResponse, TradeResponse, OrderBookResponse
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -55,7 +55,7 @@ def test_order_response_zero_remaining():
|
|||||||
filled=100.0,
|
filled=100.0,
|
||||||
remaining=0.0,
|
remaining=0.0,
|
||||||
status="FILLED",
|
status="FILLED",
|
||||||
created_at=datetime.now(datetime.UTC)
|
created_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert order.remaining == 0.0
|
assert order.remaining == 0.0
|
||||||
assert order.status == "FILLED"
|
assert order.status == "FILLED"
|
||||||
@@ -73,7 +73,7 @@ def test_order_response_empty_status():
|
|||||||
filled=0.0,
|
filled=0.0,
|
||||||
remaining=100.0,
|
remaining=100.0,
|
||||||
status="",
|
status="",
|
||||||
created_at=datetime.now(datetime.UTC)
|
created_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert order.status == ""
|
assert order.status == ""
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ def test_trade_response_zero_amount():
|
|||||||
amount=0.0,
|
amount=0.0,
|
||||||
price=0.00001,
|
price=0.00001,
|
||||||
total=0.0,
|
total=0.0,
|
||||||
created_at=datetime.now(datetime.UTC)
|
created_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert trade.amount == 0.0
|
assert trade.amount == 0.0
|
||||||
assert trade.total == 0.0
|
assert trade.total == 0.0
|
||||||
@@ -103,7 +103,7 @@ def test_order_book_empty_buys():
|
|||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_order_book_empty_sells():
|
def test_order_book_empty_sells():
|
||||||
"""Test OrderBookResponse with empty sells"""
|
"""Test OrderBookResponse with empty sells"""
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
buy_order = OrderResponse(
|
buy_order = OrderResponse(
|
||||||
id=1,
|
id=1,
|
||||||
order_type="BUY",
|
order_type="BUY",
|
||||||
@@ -113,7 +113,7 @@ def test_order_book_empty_sells():
|
|||||||
filled=0.0,
|
filled=0.0,
|
||||||
remaining=100.0,
|
remaining=100.0,
|
||||||
status="OPEN",
|
status="OPEN",
|
||||||
created_at=datetime.now(datetime.UTC)
|
created_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
orderbook = OrderBookResponse(buys=[buy_order], sells=[])
|
orderbook = OrderBookResponse(buys=[buy_order], sells=[])
|
||||||
assert len(orderbook.buys) == 1
|
assert len(orderbook.buys) == 1
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ def test_order_create_model_sell():
|
|||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_order_response_model():
|
def test_order_response_model():
|
||||||
"""Test OrderResponse model"""
|
"""Test OrderResponse model"""
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
order = OrderResponse(
|
order = OrderResponse(
|
||||||
id=1,
|
id=1,
|
||||||
order_type="BUY",
|
order_type="BUY",
|
||||||
@@ -55,7 +55,7 @@ def test_order_response_model():
|
|||||||
filled=0.0,
|
filled=0.0,
|
||||||
remaining=100.0,
|
remaining=100.0,
|
||||||
status="OPEN",
|
status="OPEN",
|
||||||
created_at=datetime.now(datetime.UTC)
|
created_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert order.id == 1
|
assert order.id == 1
|
||||||
assert order.order_type == "BUY"
|
assert order.order_type == "BUY"
|
||||||
@@ -66,13 +66,13 @@ def test_order_response_model():
|
|||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_trade_response_model():
|
def test_trade_response_model():
|
||||||
"""Test TradeResponse model"""
|
"""Test TradeResponse model"""
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
trade = TradeResponse(
|
trade = TradeResponse(
|
||||||
id=1,
|
id=1,
|
||||||
amount=50.0,
|
amount=50.0,
|
||||||
price=0.00001,
|
price=0.00001,
|
||||||
total=0.0005,
|
total=0.0005,
|
||||||
created_at=datetime.now(datetime.UTC)
|
created_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert trade.id == 1
|
assert trade.id == 1
|
||||||
assert trade.amount == 50.0
|
assert trade.amount == 50.0
|
||||||
@@ -82,7 +82,7 @@ def test_trade_response_model():
|
|||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_order_book_response_model():
|
def test_order_book_response_model():
|
||||||
"""Test OrderBookResponse model"""
|
"""Test OrderBookResponse model"""
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
buy_order = OrderResponse(
|
buy_order = OrderResponse(
|
||||||
id=1,
|
id=1,
|
||||||
order_type="BUY",
|
order_type="BUY",
|
||||||
@@ -92,7 +92,7 @@ def test_order_book_response_model():
|
|||||||
filled=0.0,
|
filled=0.0,
|
||||||
remaining=100.0,
|
remaining=100.0,
|
||||||
status="OPEN",
|
status="OPEN",
|
||||||
created_at=datetime.now(datetime.UTC)
|
created_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
sell_order = OrderResponse(
|
sell_order = OrderResponse(
|
||||||
id=2,
|
id=2,
|
||||||
@@ -103,7 +103,7 @@ def test_order_book_response_model():
|
|||||||
filled=0.0,
|
filled=0.0,
|
||||||
remaining=50.0,
|
remaining=50.0,
|
||||||
status="OPEN",
|
status="OPEN",
|
||||||
created_at=datetime.now(datetime.UTC)
|
created_at=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
orderbook = OrderBookResponse(buys=[buy_order], sells=[sell_order])
|
orderbook = OrderBookResponse(buys=[buy_order], sells=[sell_order])
|
||||||
assert len(orderbook.buys) == 1
|
assert len(orderbook.buys) == 1
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Handles cross-chain and cross-region AI agent communication with global optimiza
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any, List, Optional
|
from typing import Dict, Any, List, Optional
|
||||||
from fastapi import FastAPI, HTTPException
|
from fastapi import FastAPI, HTTPException
|
||||||
@@ -74,7 +74,7 @@ async def root():
|
|||||||
return {
|
return {
|
||||||
"service": "AITBC Global AI Agent Communication Service",
|
"service": "AITBC Global AI Agent Communication Service",
|
||||||
"status": "running",
|
"status": "running",
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,8 +105,8 @@ async def register_agent(agent: Agent):
|
|||||||
"languages": agent.languages,
|
"languages": agent.languages,
|
||||||
"specialization": agent.specialization,
|
"specialization": agent.specialization,
|
||||||
"performance_score": agent.performance_score,
|
"performance_score": agent.performance_score,
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"last_active": datetime.now(datetime.UTC).isoformat(),
|
"last_active": datetime.now(timezone.utc).isoformat(),
|
||||||
"total_messages_sent": 0,
|
"total_messages_sent": 0,
|
||||||
"total_messages_received": 0,
|
"total_messages_received": 0,
|
||||||
"collaborations_participated": 0,
|
"collaborations_participated": 0,
|
||||||
@@ -188,7 +188,7 @@ async def send_message(message: AgentMessage):
|
|||||||
"timestamp": message.timestamp.isoformat(),
|
"timestamp": message.timestamp.isoformat(),
|
||||||
"encryption_key": message.encryption_key,
|
"encryption_key": message.encryption_key,
|
||||||
"status": "delivered",
|
"status": "delivered",
|
||||||
"delivered_at": datetime.now(datetime.UTC).isoformat(),
|
"delivered_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"read_at": None
|
"read_at": None
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,16 +285,16 @@ async def create_collaboration(session: CollaborationSession):
|
|||||||
|
|
||||||
for participant_id in session.participants:
|
for participant_id in session.participants:
|
||||||
message_record = {
|
message_record = {
|
||||||
"message_id": f"collab_{int(datetime.now(datetime.UTC).timestamp())}",
|
"message_id": f"collab_{int(datetime.now(timezone.utc).timestamp())}",
|
||||||
"sender_id": "system",
|
"sender_id": "system",
|
||||||
"recipient_id": participant_id,
|
"recipient_id": participant_id,
|
||||||
"message_type": "notification",
|
"message_type": "notification",
|
||||||
"content": notification,
|
"content": notification,
|
||||||
"priority": "medium",
|
"priority": "medium",
|
||||||
"language": "english",
|
"language": "english",
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"status": "delivered",
|
"status": "delivered",
|
||||||
"delivered_at": datetime.now(datetime.UTC).isoformat()
|
"delivered_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
if participant_id not in agent_messages:
|
if participant_id not in agent_messages:
|
||||||
@@ -330,11 +330,11 @@ async def send_collaboration_message(session_id: str, sender_id: str, content: D
|
|||||||
|
|
||||||
# Create collaboration message
|
# Create collaboration message
|
||||||
message_record = {
|
message_record = {
|
||||||
"message_id": f"collab_msg_{int(datetime.now(datetime.UTC).timestamp())}",
|
"message_id": f"collab_msg_{int(datetime.now(timezone.utc).timestamp())}",
|
||||||
"sender_id": sender_id,
|
"sender_id": sender_id,
|
||||||
"session_id": session_id,
|
"session_id": session_id,
|
||||||
"content": content,
|
"content": content,
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"type": "collaboration_message"
|
"type": "collaboration_message"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,16 +351,16 @@ async def send_collaboration_message(session_id: str, sender_id: str, content: D
|
|||||||
}
|
}
|
||||||
|
|
||||||
msg_record = {
|
msg_record = {
|
||||||
"message_id": f"notif_{int(datetime.now(datetime.UTC).timestamp())}",
|
"message_id": f"notif_{int(datetime.now(timezone.utc).timestamp())}",
|
||||||
"sender_id": "system",
|
"sender_id": "system",
|
||||||
"recipient_id": participant_id,
|
"recipient_id": participant_id,
|
||||||
"message_type": "notification",
|
"message_type": "notification",
|
||||||
"content": notification,
|
"content": notification,
|
||||||
"priority": "medium",
|
"priority": "medium",
|
||||||
"language": "english",
|
"language": "english",
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"status": "delivered",
|
"status": "delivered",
|
||||||
"delivered_at": datetime.now(datetime.UTC).isoformat()
|
"delivered_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
if participant_id not in agent_messages:
|
if participant_id not in agent_messages:
|
||||||
@@ -381,7 +381,7 @@ async def record_agent_performance(performance: AgentPerformance):
|
|||||||
|
|
||||||
# Create performance record
|
# Create performance record
|
||||||
performance_record = {
|
performance_record = {
|
||||||
"performance_id": f"perf_{int(datetime.now(datetime.UTC).timestamp())}",
|
"performance_id": f"perf_{int(datetime.now(timezone.utc).timestamp())}",
|
||||||
"agent_id": performance.agent_id,
|
"agent_id": performance.agent_id,
|
||||||
"timestamp": performance.timestamp.isoformat(),
|
"timestamp": performance.timestamp.isoformat(),
|
||||||
"tasks_completed": performance.tasks_completed,
|
"tasks_completed": performance.tasks_completed,
|
||||||
@@ -421,7 +421,7 @@ async def get_agent_performance(agent_id: str, hours: int = 24):
|
|||||||
if agent_id not in global_agents:
|
if agent_id not in global_agents:
|
||||||
raise HTTPException(status_code=404, detail="Agent not found")
|
raise HTTPException(status_code=404, detail="Agent not found")
|
||||||
|
|
||||||
cutoff_time = datetime.now(datetime.UTC) - timedelta(hours=hours)
|
cutoff_time = datetime.now(timezone.utc) - timedelta(hours=hours)
|
||||||
performance_records = agent_performance.get(agent_id, [])
|
performance_records = agent_performance.get(agent_id, [])
|
||||||
recent_performance = [
|
recent_performance = [
|
||||||
p for p in performance_records
|
p for p in performance_records
|
||||||
@@ -448,7 +448,7 @@ async def get_agent_performance(agent_id: str, hours: int = 24):
|
|||||||
"total_tasks_completed": int(total_tasks),
|
"total_tasks_completed": int(total_tasks),
|
||||||
"total_records": len(recent_performance)
|
"total_records": len(recent_performance)
|
||||||
},
|
},
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/network/dashboard")
|
@app.get("/api/v1/network/dashboard")
|
||||||
@@ -476,7 +476,7 @@ async def get_network_dashboard():
|
|||||||
|
|
||||||
# Recent activity
|
# Recent activity
|
||||||
recent_messages = 0
|
recent_messages = 0
|
||||||
cutoff_time = datetime.now(datetime.UTC) - timedelta(hours=1)
|
cutoff_time = datetime.now(timezone.utc) - timedelta(hours=1)
|
||||||
for messages in agent_messages.values():
|
for messages in agent_messages.values():
|
||||||
recent_messages += len([m for m in messages if datetime.fromisoformat(m["timestamp"]) > cutoff_time])
|
recent_messages += len([m for m in messages if datetime.fromisoformat(m["timestamp"]) > cutoff_time])
|
||||||
|
|
||||||
@@ -503,7 +503,7 @@ async def get_network_dashboard():
|
|||||||
"total_tasks_completed": sum(a["tasks_completed"] for a in global_agents.values())
|
"total_tasks_completed": sum(a["tasks_completed"] for a in global_agents.values())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/network/optimize")
|
@app.get("/api/v1/network/optimize")
|
||||||
@@ -554,7 +554,7 @@ async def optimize_network():
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"optimization_results": optimization_results,
|
"optimization_results": optimization_results,
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Background task for network monitoring
|
# Background task for network monitoring
|
||||||
@@ -564,12 +564,12 @@ async def network_monitoring_task():
|
|||||||
await asyncio.sleep(300) # Monitor every 5 minutes
|
await asyncio.sleep(300) # Monitor every 5 minutes
|
||||||
|
|
||||||
# Update network statistics
|
# Update network statistics
|
||||||
global_network_stats["last_update"] = datetime.now(datetime.UTC).isoformat()
|
global_network_stats["last_update"] = datetime.now(timezone.utc).isoformat()
|
||||||
global_network_stats["total_agents"] = len(global_agents)
|
global_network_stats["total_agents"] = len(global_agents)
|
||||||
global_network_stats["active_agents"] = len([a for a in global_agents.values() if a["status"] == "active"])
|
global_network_stats["active_agents"] = len([a for a in global_agents.values() if a["status"] == "active"])
|
||||||
|
|
||||||
# Check for expired collaboration sessions
|
# Check for expired collaboration sessions
|
||||||
current_time = datetime.now(datetime.UTC)
|
current_time = datetime.now(timezone.utc)
|
||||||
for session_id, session in collaboration_sessions.items():
|
for session_id, session in collaboration_sessions.items():
|
||||||
if datetime.fromisoformat(session["expires_at"]) < current_time and session["status"] == "active":
|
if datetime.fromisoformat(session["expires_at"]) < current_time and session["status"] == "active":
|
||||||
session["status"] = "expired"
|
session["status"] = "expired"
|
||||||
@@ -637,8 +637,8 @@ async def startup_event():
|
|||||||
"languages": agent.languages,
|
"languages": agent.languages,
|
||||||
"specialization": agent.specialization,
|
"specialization": agent.specialization,
|
||||||
"performance_score": agent.performance_score,
|
"performance_score": agent.performance_score,
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"last_active": datetime.now(datetime.UTC).isoformat(),
|
"last_active": datetime.now(timezone.utc).isoformat(),
|
||||||
"total_messages_sent": 0,
|
"total_messages_sent": 0,
|
||||||
"total_messages_received": 0,
|
"total_messages_received": 0,
|
||||||
"collaborations_participated": 0,
|
"collaborations_participated": 0,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import sys
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
|
|
||||||
|
|
||||||
from main import app, Agent, AgentMessage, CollaborationSession, AgentPerformance, global_agents, agent_messages, collaboration_sessions, agent_performance
|
from main import app, Agent, AgentMessage, CollaborationSession, AgentPerformance, global_agents, agent_messages, collaboration_sessions, agent_performance
|
||||||
@@ -64,7 +64,7 @@ def test_agent_performance_out_of_range_score():
|
|||||||
"""Test AgentPerformance with out of range scores"""
|
"""Test AgentPerformance with out of range scores"""
|
||||||
performance = AgentPerformance(
|
performance = AgentPerformance(
|
||||||
agent_id="agent_123",
|
agent_id="agent_123",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
tasks_completed=10,
|
tasks_completed=10,
|
||||||
response_time_ms=50.5,
|
response_time_ms=50.5,
|
||||||
accuracy_score=2.0,
|
accuracy_score=2.0,
|
||||||
@@ -86,7 +86,7 @@ def test_agent_message_empty_content():
|
|||||||
content={},
|
content={},
|
||||||
priority="high",
|
priority="high",
|
||||||
language="english",
|
language="english",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert message.content == {}
|
assert message.content == {}
|
||||||
|
|
||||||
@@ -148,8 +148,8 @@ def test_send_collaboration_message_sender_not_participant():
|
|||||||
participants=["agent_123"],
|
participants=["agent_123"],
|
||||||
session_type="research",
|
session_type="research",
|
||||||
objective="Research task",
|
objective="Research task",
|
||||||
created_at=datetime.now(datetime.UTC),
|
created_at=datetime.now(timezone.utc),
|
||||||
expires_at=datetime.now(datetime.UTC) + timedelta(hours=1),
|
expires_at=datetime.now(timezone.utc) + timedelta(hours=1),
|
||||||
status="active"
|
status="active"
|
||||||
)
|
)
|
||||||
client.post("/api/v1/collaborations/create", json=session.model_dump(mode='json'))
|
client.post("/api/v1/collaborations/create", json=session.model_dump(mode='json'))
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import sys
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
|
|
||||||
|
|
||||||
from main import app, Agent, AgentMessage, CollaborationSession, AgentPerformance, global_agents, agent_messages, collaboration_sessions, agent_performance
|
from main import app, Agent, AgentMessage, CollaborationSession, AgentPerformance, global_agents, agent_messages, collaboration_sessions, agent_performance
|
||||||
@@ -194,7 +194,7 @@ def test_send_direct_message():
|
|||||||
content={"data": "test"},
|
content={"data": "test"},
|
||||||
priority="high",
|
priority="high",
|
||||||
language="english",
|
language="english",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
response = client.post("/api/v1/messages/send", json=message.model_dump(mode='json'))
|
response = client.post("/api/v1/messages/send", json=message.model_dump(mode='json'))
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
@@ -241,7 +241,7 @@ def test_send_broadcast_message():
|
|||||||
content={"data": "test"},
|
content={"data": "test"},
|
||||||
priority="medium",
|
priority="medium",
|
||||||
language="english",
|
language="english",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
response = client.post("/api/v1/messages/send", json=message.model_dump(mode='json'))
|
response = client.post("/api/v1/messages/send", json=message.model_dump(mode='json'))
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
@@ -261,7 +261,7 @@ def test_send_message_sender_not_found():
|
|||||||
content={"data": "test"},
|
content={"data": "test"},
|
||||||
priority="high",
|
priority="high",
|
||||||
language="english",
|
language="english",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
response = client.post("/api/v1/messages/send", json=message.model_dump(mode='json'))
|
response = client.post("/api/v1/messages/send", json=message.model_dump(mode='json'))
|
||||||
assert response.status_code == 400
|
assert response.status_code == 400
|
||||||
@@ -292,7 +292,7 @@ def test_send_message_recipient_not_found():
|
|||||||
content={"data": "test"},
|
content={"data": "test"},
|
||||||
priority="high",
|
priority="high",
|
||||||
language="english",
|
language="english",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
response = client.post("/api/v1/messages/send", json=message.model_dump(mode='json'))
|
response = client.post("/api/v1/messages/send", json=message.model_dump(mode='json'))
|
||||||
assert response.status_code == 400
|
assert response.status_code == 400
|
||||||
@@ -377,8 +377,8 @@ def test_create_collaboration():
|
|||||||
participants=["agent_123", "agent_456"],
|
participants=["agent_123", "agent_456"],
|
||||||
session_type="task_force",
|
session_type="task_force",
|
||||||
objective="Complete task",
|
objective="Complete task",
|
||||||
created_at=datetime.now(datetime.UTC),
|
created_at=datetime.now(timezone.utc),
|
||||||
expires_at=datetime.now(datetime.UTC) + timedelta(hours=1),
|
expires_at=datetime.now(timezone.utc) + timedelta(hours=1),
|
||||||
status="active"
|
status="active"
|
||||||
)
|
)
|
||||||
response = client.post("/api/v1/collaborations/create", json=session.model_dump(mode='json'))
|
response = client.post("/api/v1/collaborations/create", json=session.model_dump(mode='json'))
|
||||||
@@ -396,8 +396,8 @@ def test_create_collaboration_participant_not_found():
|
|||||||
participants=["nonexistent"],
|
participants=["nonexistent"],
|
||||||
session_type="task_force",
|
session_type="task_force",
|
||||||
objective="Complete task",
|
objective="Complete task",
|
||||||
created_at=datetime.now(datetime.UTC),
|
created_at=datetime.now(timezone.utc),
|
||||||
expires_at=datetime.now(datetime.UTC) + timedelta(hours=1),
|
expires_at=datetime.now(timezone.utc) + timedelta(hours=1),
|
||||||
status="active"
|
status="active"
|
||||||
)
|
)
|
||||||
response = client.post("/api/v1/collaborations/create", json=session.model_dump(mode='json'))
|
response = client.post("/api/v1/collaborations/create", json=session.model_dump(mode='json'))
|
||||||
@@ -427,8 +427,8 @@ def test_get_collaboration():
|
|||||||
participants=["agent_123"],
|
participants=["agent_123"],
|
||||||
session_type="research",
|
session_type="research",
|
||||||
objective="Research task",
|
objective="Research task",
|
||||||
created_at=datetime.now(datetime.UTC),
|
created_at=datetime.now(timezone.utc),
|
||||||
expires_at=datetime.now(datetime.UTC) + timedelta(hours=1),
|
expires_at=datetime.now(timezone.utc) + timedelta(hours=1),
|
||||||
status="active"
|
status="active"
|
||||||
)
|
)
|
||||||
client.post("/api/v1/collaborations/create", json=session.model_dump(mode='json'))
|
client.post("/api/v1/collaborations/create", json=session.model_dump(mode='json'))
|
||||||
@@ -462,8 +462,8 @@ def test_send_collaboration_message():
|
|||||||
participants=["agent_123"],
|
participants=["agent_123"],
|
||||||
session_type="research",
|
session_type="research",
|
||||||
objective="Research task",
|
objective="Research task",
|
||||||
created_at=datetime.now(datetime.UTC),
|
created_at=datetime.now(timezone.utc),
|
||||||
expires_at=datetime.now(datetime.UTC) + timedelta(hours=1),
|
expires_at=datetime.now(timezone.utc) + timedelta(hours=1),
|
||||||
status="active"
|
status="active"
|
||||||
)
|
)
|
||||||
client.post("/api/v1/collaborations/create", json=session.model_dump(mode='json'))
|
client.post("/api/v1/collaborations/create", json=session.model_dump(mode='json'))
|
||||||
@@ -493,7 +493,7 @@ def test_record_agent_performance():
|
|||||||
|
|
||||||
performance = AgentPerformance(
|
performance = AgentPerformance(
|
||||||
agent_id="agent_123",
|
agent_id="agent_123",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
tasks_completed=10,
|
tasks_completed=10,
|
||||||
response_time_ms=50.5,
|
response_time_ms=50.5,
|
||||||
accuracy_score=0.95,
|
accuracy_score=0.95,
|
||||||
@@ -513,7 +513,7 @@ def test_record_performance_agent_not_found():
|
|||||||
client = TestClient(app)
|
client = TestClient(app)
|
||||||
performance = AgentPerformance(
|
performance = AgentPerformance(
|
||||||
agent_id="nonexistent",
|
agent_id="nonexistent",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
tasks_completed=10,
|
tasks_completed=10,
|
||||||
response_time_ms=50.5,
|
response_time_ms=50.5,
|
||||||
accuracy_score=0.95,
|
accuracy_score=0.95,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import pytest
|
|||||||
import sys
|
import sys
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
from main import app, Agent, AgentMessage, CollaborationSession, AgentPerformance
|
from main import app, Agent, AgentMessage, CollaborationSession, AgentPerformance
|
||||||
@@ -67,7 +67,7 @@ def test_agent_message_model():
|
|||||||
content={"data": "test"},
|
content={"data": "test"},
|
||||||
priority="high",
|
priority="high",
|
||||||
language="english",
|
language="english",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert message.message_id == "msg_123"
|
assert message.message_id == "msg_123"
|
||||||
assert message.sender_id == "agent_123"
|
assert message.sender_id == "agent_123"
|
||||||
@@ -87,7 +87,7 @@ def test_agent_message_broadcast():
|
|||||||
content={"data": "test"},
|
content={"data": "test"},
|
||||||
priority="medium",
|
priority="medium",
|
||||||
language="english",
|
language="english",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert message.recipient_id is None
|
assert message.recipient_id is None
|
||||||
|
|
||||||
@@ -100,8 +100,8 @@ def test_collaboration_session_model():
|
|||||||
participants=["agent_123", "agent_456"],
|
participants=["agent_123", "agent_456"],
|
||||||
session_type="task_force",
|
session_type="task_force",
|
||||||
objective="Complete trading task",
|
objective="Complete trading task",
|
||||||
created_at=datetime.now(datetime.UTC),
|
created_at=datetime.now(timezone.utc),
|
||||||
expires_at=datetime.now(datetime.UTC),
|
expires_at=datetime.now(timezone.utc),
|
||||||
status="active"
|
status="active"
|
||||||
)
|
)
|
||||||
assert session.session_id == "session_123"
|
assert session.session_id == "session_123"
|
||||||
@@ -117,8 +117,8 @@ def test_collaboration_session_empty_participants():
|
|||||||
participants=[],
|
participants=[],
|
||||||
session_type="research",
|
session_type="research",
|
||||||
objective="Research task",
|
objective="Research task",
|
||||||
created_at=datetime.now(datetime.UTC),
|
created_at=datetime.now(timezone.utc),
|
||||||
expires_at=datetime.now(datetime.UTC),
|
expires_at=datetime.now(timezone.utc),
|
||||||
status="active"
|
status="active"
|
||||||
)
|
)
|
||||||
assert session.participants == []
|
assert session.participants == []
|
||||||
@@ -129,7 +129,7 @@ def test_agent_performance_model():
|
|||||||
"""Test AgentPerformance model"""
|
"""Test AgentPerformance model"""
|
||||||
performance = AgentPerformance(
|
performance = AgentPerformance(
|
||||||
agent_id="agent_123",
|
agent_id="agent_123",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
tasks_completed=10,
|
tasks_completed=10,
|
||||||
response_time_ms=50.5,
|
response_time_ms=50.5,
|
||||||
accuracy_score=0.95,
|
accuracy_score=0.95,
|
||||||
@@ -147,7 +147,7 @@ def test_agent_performance_negative_values():
|
|||||||
"""Test AgentPerformance with negative values"""
|
"""Test AgentPerformance with negative values"""
|
||||||
performance = AgentPerformance(
|
performance = AgentPerformance(
|
||||||
agent_id="agent_123",
|
agent_id="agent_123",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
tasks_completed=-10,
|
tasks_completed=-10,
|
||||||
response_time_ms=-50.5,
|
response_time_ms=-50.5,
|
||||||
accuracy_score=-0.95,
|
accuracy_score=-0.95,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Handles multi-region deployment, load balancing, and global optimization
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any, List, Optional
|
from typing import Dict, Any, List, Optional
|
||||||
from fastapi import FastAPI, HTTPException
|
from fastapi import FastAPI, HTTPException
|
||||||
@@ -72,7 +72,7 @@ async def root():
|
|||||||
return {
|
return {
|
||||||
"service": "AITBC Global Infrastructure Service",
|
"service": "AITBC Global Infrastructure Service",
|
||||||
"status": "running",
|
"status": "running",
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ async def register_region(region: Region):
|
|||||||
"current_load": region.current_load,
|
"current_load": region.current_load,
|
||||||
"latency_ms": region.latency_ms,
|
"latency_ms": region.latency_ms,
|
||||||
"compliance_level": region.compliance_level,
|
"compliance_level": region.compliance_level,
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"last_health_check": None,
|
"last_health_check": None,
|
||||||
"services_deployed": [],
|
"services_deployed": [],
|
||||||
"performance_history": []
|
"performance_history": []
|
||||||
@@ -148,7 +148,7 @@ async def get_region(region_id: str):
|
|||||||
@app.post("/api/v1/deployments/create")
|
@app.post("/api/v1/deployments/create")
|
||||||
async def create_deployment(deployment: GlobalDeployment):
|
async def create_deployment(deployment: GlobalDeployment):
|
||||||
"""Create a new global deployment"""
|
"""Create a new global deployment"""
|
||||||
deployment_id = f"deploy_{int(datetime.now(datetime.UTC).timestamp())}"
|
deployment_id = f"deploy_{int(datetime.now(timezone.utc).timestamp())}"
|
||||||
|
|
||||||
# Validate target regions
|
# Validate target regions
|
||||||
for region_id in deployment.target_regions:
|
for region_id in deployment.target_regions:
|
||||||
@@ -164,7 +164,7 @@ async def create_deployment(deployment: GlobalDeployment):
|
|||||||
"deployment_strategy": deployment.deployment_strategy,
|
"deployment_strategy": deployment.deployment_strategy,
|
||||||
"health_checks": deployment.health_checks,
|
"health_checks": deployment.health_checks,
|
||||||
"status": "pending",
|
"status": "pending",
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"started_at": None,
|
"started_at": None,
|
||||||
"completed_at": None,
|
"completed_at": None,
|
||||||
"deployment_progress": {},
|
"deployment_progress": {},
|
||||||
@@ -214,7 +214,7 @@ async def list_deployments(status: Optional[str] = None):
|
|||||||
@app.post("/api/v1/load-balancers/create")
|
@app.post("/api/v1/load-balancers/create")
|
||||||
async def create_load_balancer(balancer: LoadBalancer):
|
async def create_load_balancer(balancer: LoadBalancer):
|
||||||
"""Create a new load balancer"""
|
"""Create a new load balancer"""
|
||||||
balancer_id = f"lb_{int(datetime.now(datetime.UTC).timestamp())}"
|
balancer_id = f"lb_{int(datetime.now(timezone.utc).timestamp())}"
|
||||||
|
|
||||||
# Validate target regions
|
# Validate target regions
|
||||||
for region_id in balancer.target_regions:
|
for region_id in balancer.target_regions:
|
||||||
@@ -230,7 +230,7 @@ async def create_load_balancer(balancer: LoadBalancer):
|
|||||||
"health_check_interval": balancer.health_check_interval,
|
"health_check_interval": balancer.health_check_interval,
|
||||||
"failover_threshold": balancer.failover_threshold,
|
"failover_threshold": balancer.failover_threshold,
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"current_weights": {region_id: 1.0 for region_id in balancer.target_regions},
|
"current_weights": {region_id: 1.0 for region_id in balancer.target_regions},
|
||||||
"health_status": {region_id: "healthy" for region_id in balancer.target_regions},
|
"health_status": {region_id: "healthy" for region_id in balancer.target_regions},
|
||||||
"total_requests": 0,
|
"total_requests": 0,
|
||||||
@@ -265,7 +265,7 @@ async def list_load_balancers():
|
|||||||
async def record_performance_metrics(metrics: PerformanceMetrics):
|
async def record_performance_metrics(metrics: PerformanceMetrics):
|
||||||
"""Record performance metrics for a region"""
|
"""Record performance metrics for a region"""
|
||||||
metrics_record = {
|
metrics_record = {
|
||||||
"metrics_id": f"metrics_{int(datetime.now(datetime.UTC).timestamp())}",
|
"metrics_id": f"metrics_{int(datetime.now(timezone.utc).timestamp())}",
|
||||||
"region_id": metrics.region_id,
|
"region_id": metrics.region_id,
|
||||||
"timestamp": metrics.timestamp.isoformat(),
|
"timestamp": metrics.timestamp.isoformat(),
|
||||||
"cpu_usage": metrics.cpu_usage,
|
"cpu_usage": metrics.cpu_usage,
|
||||||
@@ -310,7 +310,7 @@ async def get_region_performance(region_id: str, hours: int = 24):
|
|||||||
if region_id not in performance_metrics:
|
if region_id not in performance_metrics:
|
||||||
raise HTTPException(status_code=404, detail="No performance data for region")
|
raise HTTPException(status_code=404, detail="No performance data for region")
|
||||||
|
|
||||||
cutoff_time = datetime.now(datetime.UTC) - timedelta(hours=hours)
|
cutoff_time = datetime.now(timezone.utc) - timedelta(hours=hours)
|
||||||
recent_metrics = [
|
recent_metrics = [
|
||||||
m for m in performance_metrics[region_id]
|
m for m in performance_metrics[region_id]
|
||||||
if datetime.fromisoformat(m["timestamp"]) > cutoff_time
|
if datetime.fromisoformat(m["timestamp"]) > cutoff_time
|
||||||
@@ -334,7 +334,7 @@ async def get_region_performance(region_id: str, hours: int = 24):
|
|||||||
"average_response_time_ms": round(avg_response_time, 2),
|
"average_response_time_ms": round(avg_response_time, 2),
|
||||||
"total_samples": len(recent_metrics)
|
"total_samples": len(recent_metrics)
|
||||||
},
|
},
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/compliance/{region_id}")
|
@app.get("/api/v1/compliance/{region_id}")
|
||||||
@@ -350,8 +350,8 @@ async def get_region_compliance(region_id: str):
|
|||||||
"compliance_level": global_regions[region_id]["compliance_level"],
|
"compliance_level": global_regions[region_id]["compliance_level"],
|
||||||
"certifications": ["SOC2", "ISO27001", "GDPR"],
|
"certifications": ["SOC2", "ISO27001", "GDPR"],
|
||||||
"data_residency": "compliant",
|
"data_residency": "compliant",
|
||||||
"last_audit": (datetime.now(datetime.UTC) - timedelta(days=90)).isoformat(),
|
"last_audit": (datetime.now(timezone.utc) - timedelta(days=90)).isoformat(),
|
||||||
"next_audit": (datetime.now(datetime.UTC) + timedelta(days=275)).isoformat(),
|
"next_audit": (datetime.now(timezone.utc) + timedelta(days=275)).isoformat(),
|
||||||
"regulations": ["GDPR", "CCPA", "PDPA"],
|
"regulations": ["GDPR", "CCPA", "PDPA"],
|
||||||
"data_protection": "end-to-end-encryption",
|
"data_protection": "end-to-end-encryption",
|
||||||
"access_controls": "role-based-access",
|
"access_controls": "role-based-access",
|
||||||
@@ -410,7 +410,7 @@ async def get_global_dashboard():
|
|||||||
"partial_compliance": len([r for r in global_regions.values() if r["compliance_level"] == "partial"])
|
"partial_compliance": len([r for r in global_regions.values() if r["compliance_level"] == "partial"])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Core deployment and load balancing functions
|
# Core deployment and load balancing functions
|
||||||
@@ -418,13 +418,13 @@ async def execute_deployment(deployment_id: str):
|
|||||||
"""Execute a global deployment"""
|
"""Execute a global deployment"""
|
||||||
deployment = deployments[deployment_id]
|
deployment = deployments[deployment_id]
|
||||||
deployment["status"] = "in_progress"
|
deployment["status"] = "in_progress"
|
||||||
deployment["started_at"] = datetime.now(datetime.UTC).isoformat()
|
deployment["started_at"] = datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for region_id in deployment["target_regions"]:
|
for region_id in deployment["target_regions"]:
|
||||||
deployment["deployment_progress"][region_id] = {
|
deployment["deployment_progress"][region_id] = {
|
||||||
"status": "deploying",
|
"status": "deploying",
|
||||||
"started_at": datetime.now(datetime.UTC).isoformat(),
|
"started_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"progress": 0
|
"progress": 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,7 +433,7 @@ async def execute_deployment(deployment_id: str):
|
|||||||
|
|
||||||
deployment["deployment_progress"][region_id].update({
|
deployment["deployment_progress"][region_id].update({
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"completed_at": datetime.now(datetime.UTC).isoformat(),
|
"completed_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"progress": 100
|
"progress": 100
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -443,7 +443,7 @@ async def execute_deployment(deployment_id: str):
|
|||||||
global_regions[region_id]["services_deployed"].append(deployment["service_name"])
|
global_regions[region_id]["services_deployed"].append(deployment["service_name"])
|
||||||
|
|
||||||
deployment["status"] = "completed"
|
deployment["status"] = "completed"
|
||||||
deployment["completed_at"] = datetime.now(datetime.UTC).isoformat()
|
deployment["completed_at"] = datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
logger.info(f"Deployment completed: {deployment_id}")
|
logger.info(f"Deployment completed: {deployment_id}")
|
||||||
|
|
||||||
@@ -516,7 +516,7 @@ async def global_monitoring_task():
|
|||||||
await asyncio.sleep(60) # Monitor every minute
|
await asyncio.sleep(60) # Monitor every minute
|
||||||
|
|
||||||
# Update global monitoring data
|
# Update global monitoring data
|
||||||
global_monitoring["last_update"] = datetime.now(datetime.UTC).isoformat()
|
global_monitoring["last_update"] = datetime.now(timezone.utc).isoformat()
|
||||||
global_monitoring["total_requests"] = sum(lb.get("total_requests", 0) for lb in load_balancers.values())
|
global_monitoring["total_requests"] = sum(lb.get("total_requests", 0) for lb in load_balancers.values())
|
||||||
global_monitoring["failed_requests"] = sum(lb.get("failed_requests", 0) for lb in load_balancers.values())
|
global_monitoring["failed_requests"] = sum(lb.get("failed_requests", 0) for lb in load_balancers.values())
|
||||||
|
|
||||||
@@ -582,7 +582,7 @@ async def startup_event():
|
|||||||
"current_load": region.current_load,
|
"current_load": region.current_load,
|
||||||
"latency_ms": region.latency_ms,
|
"latency_ms": region.latency_ms,
|
||||||
"compliance_level": region.compliance_level,
|
"compliance_level": region.compliance_level,
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"last_health_check": None,
|
"last_health_check": None,
|
||||||
"services_deployed": [],
|
"services_deployed": [],
|
||||||
"performance_history": []
|
"performance_history": []
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import sys
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
from main import app, Region, GlobalDeployment, LoadBalancer, PerformanceMetrics, global_regions, deployments, load_balancers, performance_metrics
|
from main import app, Region, GlobalDeployment, LoadBalancer, PerformanceMetrics, global_regions, deployments, load_balancers, performance_metrics
|
||||||
@@ -93,7 +93,7 @@ def test_performance_metrics_negative_values():
|
|||||||
"""Test PerformanceMetrics with negative values"""
|
"""Test PerformanceMetrics with negative values"""
|
||||||
metrics = PerformanceMetrics(
|
metrics = PerformanceMetrics(
|
||||||
region_id="us-east-1",
|
region_id="us-east-1",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
cpu_usage=-50.5,
|
cpu_usage=-50.5,
|
||||||
memory_usage=-60.2,
|
memory_usage=-60.2,
|
||||||
network_io=-1000.5,
|
network_io=-1000.5,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import sys
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
from main import app, Region, GlobalDeployment, LoadBalancer, PerformanceMetrics, global_regions, deployments, load_balancers, performance_metrics
|
from main import app, Region, GlobalDeployment, LoadBalancer, PerformanceMetrics, global_regions, deployments, load_balancers, performance_metrics
|
||||||
@@ -278,7 +278,7 @@ def test_record_performance_metrics():
|
|||||||
client = TestClient(app)
|
client = TestClient(app)
|
||||||
metrics = PerformanceMetrics(
|
metrics = PerformanceMetrics(
|
||||||
region_id="us-west-1",
|
region_id="us-west-1",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
cpu_usage=50.5,
|
cpu_usage=50.5,
|
||||||
memory_usage=60.2,
|
memory_usage=60.2,
|
||||||
network_io=1000.5,
|
network_io=1000.5,
|
||||||
@@ -300,7 +300,7 @@ def test_get_region_performance():
|
|||||||
# Record metrics first
|
# Record metrics first
|
||||||
metrics = PerformanceMetrics(
|
metrics = PerformanceMetrics(
|
||||||
region_id="us-west-1",
|
region_id="us-west-1",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
cpu_usage=50.5,
|
cpu_usage=50.5,
|
||||||
memory_usage=60.2,
|
memory_usage=60.2,
|
||||||
network_io=1000.5,
|
network_io=1000.5,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import pytest
|
|||||||
import sys
|
import sys
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
from main import app, Region, GlobalDeployment, LoadBalancer, PerformanceMetrics
|
from main import app, Region, GlobalDeployment, LoadBalancer, PerformanceMetrics
|
||||||
@@ -78,7 +78,7 @@ def test_performance_metrics_model():
|
|||||||
"""Test PerformanceMetrics model"""
|
"""Test PerformanceMetrics model"""
|
||||||
metrics = PerformanceMetrics(
|
metrics = PerformanceMetrics(
|
||||||
region_id="us-east-1",
|
region_id="us-east-1",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
cpu_usage=50.5,
|
cpu_usage=50.5,
|
||||||
memory_usage=60.2,
|
memory_usage=60.2,
|
||||||
network_io=1000.5,
|
network_io=1000.5,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import time
|
|||||||
import sys
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
|
||||||
from aitbc import get_logger, AITBCHTTPClient, NetworkError
|
from aitbc import get_logger, AITBCHTTPClient, NetworkError
|
||||||
@@ -216,7 +216,7 @@ def send_heartbeat():
|
|||||||
heartbeat_data = {
|
heartbeat_data = {
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"current_jobs": 0,
|
"current_jobs": 0,
|
||||||
"last_seen": datetime.now(datetime.UTC).isoformat(),
|
"last_seen": datetime.now(timezone.utc).isoformat(),
|
||||||
"gpu_utilization": gpu_info["utilization"],
|
"gpu_utilization": gpu_info["utilization"],
|
||||||
"memory_used": gpu_info["memory_used"],
|
"memory_used": gpu_info["memory_used"],
|
||||||
"memory_total": gpu_info["memory_total"],
|
"memory_total": gpu_info["memory_total"],
|
||||||
@@ -228,7 +228,7 @@ def send_heartbeat():
|
|||||||
heartbeat_data = {
|
heartbeat_data = {
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"current_jobs": 0,
|
"current_jobs": 0,
|
||||||
"last_seen": datetime.now(datetime.UTC).isoformat(),
|
"last_seen": datetime.now(timezone.utc).isoformat(),
|
||||||
"gpu_utilization": 0,
|
"gpu_utilization": 0,
|
||||||
"memory_used": 0,
|
"memory_used": 0,
|
||||||
"memory_total": 0,
|
"memory_total": 0,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import time
|
|||||||
import sys
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
|
||||||
from aitbc import get_logger, AITBCHTTPClient, NetworkError, LOG_DIR
|
from aitbc import get_logger, AITBCHTTPClient, NetworkError, LOG_DIR
|
||||||
@@ -209,7 +209,7 @@ def send_heartbeat():
|
|||||||
heartbeat_data = {
|
heartbeat_data = {
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"current_jobs": 0,
|
"current_jobs": 0,
|
||||||
"last_seen": datetime.now(datetime.UTC).isoformat(),
|
"last_seen": datetime.now(timezone.utc).isoformat(),
|
||||||
"gpu_utilization": gpu_info["utilization"],
|
"gpu_utilization": gpu_info["utilization"],
|
||||||
"memory_used": gpu_info["memory_used"],
|
"memory_used": gpu_info["memory_used"],
|
||||||
"memory_total": gpu_info["memory_total"],
|
"memory_total": gpu_info["memory_total"],
|
||||||
@@ -221,7 +221,7 @@ def send_heartbeat():
|
|||||||
heartbeat_data = {
|
heartbeat_data = {
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"current_jobs": 0,
|
"current_jobs": 0,
|
||||||
"last_seen": datetime.now(datetime.UTC).isoformat(),
|
"last_seen": datetime.now(timezone.utc).isoformat(),
|
||||||
"gpu_utilization": 0,
|
"gpu_utilization": 0,
|
||||||
"memory_used": 0,
|
"memory_used": 0,
|
||||||
"memory_total": 0,
|
"memory_total": 0,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Handles intelligent load distribution across global regions
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any, List, Optional
|
from typing import Dict, Any, List, Optional
|
||||||
from fastapi import FastAPI, HTTPException
|
from fastapi import FastAPI, HTTPException
|
||||||
@@ -68,7 +68,7 @@ async def root():
|
|||||||
return {
|
return {
|
||||||
"service": "AITBC Multi-Region Load Balancer",
|
"service": "AITBC Multi-Region Load Balancer",
|
||||||
"status": "running",
|
"status": "running",
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,10 +99,10 @@ async def create_load_balancing_rule(rule: LoadBalancingRule):
|
|||||||
"failover_enabled": rule.failover_enabled,
|
"failover_enabled": rule.failover_enabled,
|
||||||
"session_affinity": rule.session_affinity,
|
"session_affinity": rule.session_affinity,
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"total_requests": 0,
|
"total_requests": 0,
|
||||||
"failed_requests": 0,
|
"failed_requests": 0,
|
||||||
"last_updated": datetime.now(datetime.UTC).isoformat()
|
"last_updated": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
load_balancing_rules[rule.rule_id] = rule_record
|
load_balancing_rules[rule.rule_id] = rule_record
|
||||||
@@ -167,7 +167,7 @@ async def update_rule_weights(rule_id: str, weights: Dict[str, float]):
|
|||||||
|
|
||||||
# Update rule weights
|
# Update rule weights
|
||||||
rule["weights"] = normalized_weights
|
rule["weights"] = normalized_weights
|
||||||
rule["last_updated"] = datetime.now(datetime.UTC).isoformat()
|
rule["last_updated"] = datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
logger.info(f"Weights updated for rule {rule_id}: {normalized_weights}")
|
logger.info(f"Weights updated for rule {rule_id}: {normalized_weights}")
|
||||||
|
|
||||||
@@ -193,7 +193,7 @@ async def register_region_health(health: RegionHealth):
|
|||||||
return {
|
return {
|
||||||
"region_id": health.region_id,
|
"region_id": health.region_id,
|
||||||
"status": health.status,
|
"status": health.status,
|
||||||
"registered_at": datetime.now(datetime.UTC).isoformat()
|
"registered_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/health")
|
@app.get("/api/v1/health")
|
||||||
@@ -224,7 +224,7 @@ async def create_geographic_rule(rule: GeographicRule):
|
|||||||
"priority": rule.priority,
|
"priority": rule.priority,
|
||||||
"latency_threshold_ms": rule.latency_threshold_ms,
|
"latency_threshold_ms": rule.latency_threshold_ms,
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"usage_count": 0
|
"usage_count": 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,14 +256,14 @@ async def get_optimal_region(client_region: str, rule_id: Optional[str] = None):
|
|||||||
"optimal_region": optimal_region,
|
"optimal_region": optimal_region,
|
||||||
"rule_id": rule_id,
|
"rule_id": rule_id,
|
||||||
"selection_reason": get_selection_reason(optimal_region, client_region, rule_id),
|
"selection_reason": get_selection_reason(optimal_region, client_region, rule_id),
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat()
|
"timestamp": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.post("/api/v1/metrics/record")
|
@app.post("/api/v1/metrics/record")
|
||||||
async def record_balancing_metrics(metrics: LoadBalancingMetrics):
|
async def record_balancing_metrics(metrics: LoadBalancingMetrics):
|
||||||
"""Record load balancing performance metrics"""
|
"""Record load balancing performance metrics"""
|
||||||
metrics_record = {
|
metrics_record = {
|
||||||
"metrics_id": f"metrics_{int(datetime.now(datetime.UTC).timestamp())}",
|
"metrics_id": f"metrics_{int(datetime.now(timezone.utc).timestamp())}",
|
||||||
"balancer_id": metrics.balancer_id,
|
"balancer_id": metrics.balancer_id,
|
||||||
"timestamp": metrics.timestamp.isoformat(),
|
"timestamp": metrics.timestamp.isoformat(),
|
||||||
"total_requests": metrics.total_requests,
|
"total_requests": metrics.total_requests,
|
||||||
@@ -294,7 +294,7 @@ async def get_balancing_metrics(rule_id: str, hours: int = 24):
|
|||||||
if rule_id not in load_balancing_rules:
|
if rule_id not in load_balancing_rules:
|
||||||
raise HTTPException(status_code=404, detail="Load balancing rule not found")
|
raise HTTPException(status_code=404, detail="Load balancing rule not found")
|
||||||
|
|
||||||
cutoff_time = datetime.now(datetime.UTC) - timedelta(hours=hours)
|
cutoff_time = datetime.now(timezone.utc) - timedelta(hours=hours)
|
||||||
recent_metrics = [
|
recent_metrics = [
|
||||||
m for m in balancing_metrics.get(rule_id, [])
|
m for m in balancing_metrics.get(rule_id, [])
|
||||||
if datetime.fromisoformat(m["timestamp"]) > cutoff_time
|
if datetime.fromisoformat(m["timestamp"]) > cutoff_time
|
||||||
@@ -320,7 +320,7 @@ async def get_balancing_metrics(rule_id: str, hours: int = 24):
|
|||||||
"total_requests": int(total_requests),
|
"total_requests": int(total_requests),
|
||||||
"total_samples": len(recent_metrics)
|
"total_samples": len(recent_metrics)
|
||||||
},
|
},
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/dashboard")
|
@app.get("/api/v1/dashboard")
|
||||||
@@ -368,7 +368,7 @@ async def get_load_balancing_dashboard():
|
|||||||
"performance": performance_summary,
|
"performance": performance_summary,
|
||||||
"recent_activity": get_recent_activity()
|
"recent_activity": get_recent_activity()
|
||||||
},
|
},
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Core load balancing functions
|
# Core load balancing functions
|
||||||
@@ -534,7 +534,7 @@ def get_recent_activity() -> List[Dict]:
|
|||||||
|
|
||||||
# Recent health changes
|
# Recent health changes
|
||||||
for region_id, health in region_health_status.items():
|
for region_id, health in region_health_status.items():
|
||||||
if (datetime.now(datetime.UTC) - health.last_check).total_seconds() < 3600: # Last hour
|
if (datetime.now(timezone.utc) - health.last_check).total_seconds() < 3600: # Last hour
|
||||||
activity.append({
|
activity.append({
|
||||||
"type": "health_check",
|
"type": "health_check",
|
||||||
"region": region_id,
|
"region": region_id,
|
||||||
@@ -544,7 +544,7 @@ def get_recent_activity() -> List[Dict]:
|
|||||||
|
|
||||||
# Recent rule updates
|
# Recent rule updates
|
||||||
for rule_id, rule in load_balancing_rules.items():
|
for rule_id, rule in load_balancing_rules.items():
|
||||||
if (datetime.now(datetime.UTC) - datetime.fromisoformat(rule["last_updated"])).total_seconds() < 3600:
|
if (datetime.now(timezone.utc) - datetime.fromisoformat(rule["last_updated"])).total_seconds() < 3600:
|
||||||
activity.append({
|
activity.append({
|
||||||
"type": "rule_update",
|
"type": "rule_update",
|
||||||
"rule_id": rule_id,
|
"rule_id": rule_id,
|
||||||
@@ -598,7 +598,7 @@ async def check_region_health(region_id: str):
|
|||||||
response_time_ms=response_time,
|
response_time_ms=response_time,
|
||||||
success_rate=success_rate,
|
success_rate=success_rate,
|
||||||
active_connections=active_connections,
|
active_connections=active_connections,
|
||||||
last_check=datetime.now(datetime.UTC)
|
last_check=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
region_health_status[region_id] = health
|
region_health_status[region_id] = health
|
||||||
@@ -644,10 +644,10 @@ async def startup_event():
|
|||||||
"failover_enabled": rule.failover_enabled,
|
"failover_enabled": rule.failover_enabled,
|
||||||
"session_affinity": rule.session_affinity,
|
"session_affinity": rule.session_affinity,
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"total_requests": 0,
|
"total_requests": 0,
|
||||||
"failed_requests": 0,
|
"failed_requests": 0,
|
||||||
"last_updated": datetime.now(datetime.UTC).isoformat()
|
"last_updated": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
load_balancing_rules[rule.rule_id] = rule_record
|
load_balancing_rules[rule.rule_id] = rule_record
|
||||||
|
|
||||||
@@ -681,7 +681,7 @@ async def startup_event():
|
|||||||
"priority": geo_rule.priority,
|
"priority": geo_rule.priority,
|
||||||
"latency_threshold_ms": geo_rule.latency_threshold_ms,
|
"latency_threshold_ms": geo_rule.latency_threshold_ms,
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"usage_count": 0
|
"usage_count": 0
|
||||||
}
|
}
|
||||||
geographic_rules[geo_rule.rule_id] = geo_rule_record
|
geographic_rules[geo_rule.rule_id] = geo_rule_record
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import sys
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
from main import app, LoadBalancingRule, RegionHealth, LoadBalancingMetrics, GeographicRule, load_balancing_rules, region_health_status, balancing_metrics, geographic_rules
|
from main import app, LoadBalancingRule, RegionHealth, LoadBalancingMetrics, GeographicRule, load_balancing_rules, region_health_status, balancing_metrics, geographic_rules
|
||||||
@@ -50,7 +50,7 @@ def test_region_health_negative_success_rate():
|
|||||||
response_time_ms=45.5,
|
response_time_ms=45.5,
|
||||||
success_rate=-0.5,
|
success_rate=-0.5,
|
||||||
active_connections=100,
|
active_connections=100,
|
||||||
last_check=datetime.now(datetime.UTC)
|
last_check=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert health.success_rate == -0.5
|
assert health.success_rate == -0.5
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ def test_region_health_negative_connections():
|
|||||||
response_time_ms=45.5,
|
response_time_ms=45.5,
|
||||||
success_rate=0.99,
|
success_rate=0.99,
|
||||||
active_connections=-100,
|
active_connections=-100,
|
||||||
last_check=datetime.now(datetime.UTC)
|
last_check=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert health.active_connections == -100
|
assert health.active_connections == -100
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ def test_load_balancing_metrics_negative_requests():
|
|||||||
"""Test LoadBalancingMetrics with negative requests"""
|
"""Test LoadBalancingMetrics with negative requests"""
|
||||||
metrics = LoadBalancingMetrics(
|
metrics = LoadBalancingMetrics(
|
||||||
balancer_id="lb_123",
|
balancer_id="lb_123",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
total_requests=-1000,
|
total_requests=-1000,
|
||||||
requests_per_region={},
|
requests_per_region={},
|
||||||
average_response_time=50.5,
|
average_response_time=50.5,
|
||||||
@@ -89,7 +89,7 @@ def test_load_balancing_metrics_negative_response_time():
|
|||||||
"""Test LoadBalancingMetrics with negative response time"""
|
"""Test LoadBalancingMetrics with negative response time"""
|
||||||
metrics = LoadBalancingMetrics(
|
metrics = LoadBalancingMetrics(
|
||||||
balancer_id="lb_123",
|
balancer_id="lb_123",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
total_requests=1000,
|
total_requests=1000,
|
||||||
requests_per_region={},
|
requests_per_region={},
|
||||||
average_response_time=-50.5,
|
average_response_time=-50.5,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import sys
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
from main import app, LoadBalancingRule, RegionHealth, LoadBalancingMetrics, GeographicRule, load_balancing_rules, region_health_status, balancing_metrics, geographic_rules
|
from main import app, LoadBalancingRule, RegionHealth, LoadBalancingMetrics, GeographicRule, load_balancing_rules, region_health_status, balancing_metrics, geographic_rules
|
||||||
@@ -193,7 +193,7 @@ def test_register_region_health():
|
|||||||
response_time_ms=45.5,
|
response_time_ms=45.5,
|
||||||
success_rate=0.99,
|
success_rate=0.99,
|
||||||
active_connections=100,
|
active_connections=100,
|
||||||
last_check=datetime.now(datetime.UTC)
|
last_check=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
response = client.post("/api/v1/health/register", json=health.model_dump(mode='json'))
|
response = client.post("/api/v1/health/register", json=health.model_dump(mode='json'))
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
@@ -286,7 +286,7 @@ def test_record_balancing_metrics():
|
|||||||
client = TestClient(app)
|
client = TestClient(app)
|
||||||
metrics = LoadBalancingMetrics(
|
metrics = LoadBalancingMetrics(
|
||||||
balancer_id="lb_123",
|
balancer_id="lb_123",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
total_requests=1000,
|
total_requests=1000,
|
||||||
requests_per_region={"us-east-1": 500},
|
requests_per_region={"us-east-1": 500},
|
||||||
average_response_time=50.5,
|
average_response_time=50.5,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import pytest
|
|||||||
import sys
|
import sys
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
from main import app, LoadBalancingRule, RegionHealth, LoadBalancingMetrics, GeographicRule
|
from main import app, LoadBalancingRule, RegionHealth, LoadBalancingMetrics, GeographicRule
|
||||||
@@ -47,7 +47,7 @@ def test_region_health_model():
|
|||||||
response_time_ms=45.5,
|
response_time_ms=45.5,
|
||||||
success_rate=0.99,
|
success_rate=0.99,
|
||||||
active_connections=100,
|
active_connections=100,
|
||||||
last_check=datetime.now(datetime.UTC)
|
last_check=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert health.region_id == "us-east-1"
|
assert health.region_id == "us-east-1"
|
||||||
assert health.status == "healthy"
|
assert health.status == "healthy"
|
||||||
@@ -61,7 +61,7 @@ def test_load_balancing_metrics_model():
|
|||||||
"""Test LoadBalancingMetrics model"""
|
"""Test LoadBalancingMetrics model"""
|
||||||
metrics = LoadBalancingMetrics(
|
metrics = LoadBalancingMetrics(
|
||||||
balancer_id="lb_123",
|
balancer_id="lb_123",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
total_requests=1000,
|
total_requests=1000,
|
||||||
requests_per_region={"us-east-1": 500, "eu-west-1": 500},
|
requests_per_region={"us-east-1": 500, "eu-west-1": 500},
|
||||||
average_response_time=50.5,
|
average_response_time=50.5,
|
||||||
@@ -115,6 +115,6 @@ def test_region_health_negative_response_time():
|
|||||||
response_time_ms=-45.5,
|
response_time_ms=-45.5,
|
||||||
success_rate=0.99,
|
success_rate=0.99,
|
||||||
active_connections=100,
|
active_connections=100,
|
||||||
last_check=datetime.now(datetime.UTC)
|
last_check=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert health.response_time_ms == -45.5
|
assert health.response_time_ms == -45.5
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Handles plugin analytics, usage tracking, and performance monitoring
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any, List, Optional
|
from typing import Dict, Any, List, Optional
|
||||||
from fastapi import FastAPI, HTTPException
|
from fastapi import FastAPI, HTTPException
|
||||||
@@ -66,7 +66,7 @@ async def root():
|
|||||||
return {
|
return {
|
||||||
"service": "AITBC Plugin Analytics Service",
|
"service": "AITBC Plugin Analytics Service",
|
||||||
"status": "running",
|
"status": "running",
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ async def health_check():
|
|||||||
async def record_plugin_usage(usage: PluginUsage):
|
async def record_plugin_usage(usage: PluginUsage):
|
||||||
"""Record plugin usage event"""
|
"""Record plugin usage event"""
|
||||||
usage_record = {
|
usage_record = {
|
||||||
"usage_id": f"usage_{int(datetime.now(datetime.UTC).timestamp())}",
|
"usage_id": f"usage_{int(datetime.now(timezone.utc).timestamp())}",
|
||||||
"plugin_id": usage.plugin_id,
|
"plugin_id": usage.plugin_id,
|
||||||
"user_id": usage.user_id,
|
"user_id": usage.user_id,
|
||||||
"action": usage.action,
|
"action": usage.action,
|
||||||
@@ -113,7 +113,7 @@ async def record_plugin_usage(usage: PluginUsage):
|
|||||||
async def record_plugin_performance(performance: PluginPerformance):
|
async def record_plugin_performance(performance: PluginPerformance):
|
||||||
"""Record plugin performance metrics"""
|
"""Record plugin performance metrics"""
|
||||||
performance_record = {
|
performance_record = {
|
||||||
"performance_id": f"perf_{int(datetime.now(datetime.UTC).timestamp())}",
|
"performance_id": f"perf_{int(datetime.now(timezone.utc).timestamp())}",
|
||||||
"plugin_id": performance.plugin_id,
|
"plugin_id": performance.plugin_id,
|
||||||
"version": performance.version,
|
"version": performance.version,
|
||||||
"cpu_usage": performance.cpu_usage,
|
"cpu_usage": performance.cpu_usage,
|
||||||
@@ -141,7 +141,7 @@ async def record_plugin_performance(performance: PluginPerformance):
|
|||||||
async def record_plugin_rating(rating: PluginRating):
|
async def record_plugin_rating(rating: PluginRating):
|
||||||
"""Record plugin rating and review"""
|
"""Record plugin rating and review"""
|
||||||
rating_record = {
|
rating_record = {
|
||||||
"rating_id": f"rating_{int(datetime.now(datetime.UTC).timestamp())}",
|
"rating_id": f"rating_{int(datetime.now(timezone.utc).timestamp())}",
|
||||||
"plugin_id": rating.plugin_id,
|
"plugin_id": rating.plugin_id,
|
||||||
"user_id": rating.user_id,
|
"user_id": rating.user_id,
|
||||||
"rating": rating.rating,
|
"rating": rating.rating,
|
||||||
@@ -166,7 +166,7 @@ async def record_plugin_rating(rating: PluginRating):
|
|||||||
async def record_plugin_event(event: PluginEvent):
|
async def record_plugin_event(event: PluginEvent):
|
||||||
"""Record generic plugin event"""
|
"""Record generic plugin event"""
|
||||||
event_record = {
|
event_record = {
|
||||||
"event_id": f"event_{int(datetime.now(datetime.UTC).timestamp())}",
|
"event_id": f"event_{int(datetime.now(timezone.utc).timestamp())}",
|
||||||
"event_type": event.event_type,
|
"event_type": event.event_type,
|
||||||
"plugin_id": event.plugin_id,
|
"plugin_id": event.plugin_id,
|
||||||
"user_id": event.user_id,
|
"user_id": event.user_id,
|
||||||
@@ -190,7 +190,7 @@ async def record_plugin_event(event: PluginEvent):
|
|||||||
@app.get("/api/v1/analytics/usage/{plugin_id}")
|
@app.get("/api/v1/analytics/usage/{plugin_id}")
|
||||||
async def get_plugin_usage(plugin_id: str, days: int = 30):
|
async def get_plugin_usage(plugin_id: str, days: int = 30):
|
||||||
"""Get usage analytics for a specific plugin"""
|
"""Get usage analytics for a specific plugin"""
|
||||||
cutoff_date = datetime.now(datetime.UTC) - timedelta(days=days)
|
cutoff_date = datetime.now(timezone.utc) - timedelta(days=days)
|
||||||
|
|
||||||
usage_records = plugin_usage_data.get(plugin_id, [])
|
usage_records = plugin_usage_data.get(plugin_id, [])
|
||||||
recent_usage = [r for r in usage_records
|
recent_usage = [r for r in usage_records
|
||||||
@@ -204,13 +204,13 @@ async def get_plugin_usage(plugin_id: str, days: int = 30):
|
|||||||
"period_days": days,
|
"period_days": days,
|
||||||
"usage_statistics": usage_stats,
|
"usage_statistics": usage_stats,
|
||||||
"total_records": len(recent_usage),
|
"total_records": len(recent_usage),
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/analytics/performance/{plugin_id}")
|
@app.get("/api/v1/analytics/performance/{plugin_id}")
|
||||||
async def get_plugin_performance(plugin_id: str, hours: int = 24):
|
async def get_plugin_performance(plugin_id: str, hours: int = 24):
|
||||||
"""Get performance analytics for a specific plugin"""
|
"""Get performance analytics for a specific plugin"""
|
||||||
cutoff_time = datetime.now(datetime.UTC) - timedelta(hours=hours)
|
cutoff_time = datetime.now(timezone.utc) - timedelta(hours=hours)
|
||||||
|
|
||||||
performance_records = plugin_performance_data.get(plugin_id, [])
|
performance_records = plugin_performance_data.get(plugin_id, [])
|
||||||
recent_performance = [r for r in performance_records
|
recent_performance = [r for r in performance_records
|
||||||
@@ -224,7 +224,7 @@ async def get_plugin_performance(plugin_id: str, hours: int = 24):
|
|||||||
"period_hours": hours,
|
"period_hours": hours,
|
||||||
"performance_statistics": performance_stats,
|
"performance_statistics": performance_stats,
|
||||||
"total_records": len(recent_performance),
|
"total_records": len(recent_performance),
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/analytics/ratings/{plugin_id}")
|
@app.get("/api/v1/analytics/ratings/{plugin_id}")
|
||||||
@@ -240,7 +240,7 @@ async def get_plugin_ratings(plugin_id: str):
|
|||||||
"rating_statistics": rating_stats,
|
"rating_statistics": rating_stats,
|
||||||
"total_ratings": len(rating_records),
|
"total_ratings": len(rating_records),
|
||||||
"recent_ratings": rating_records[-10:], # Last 10 ratings
|
"recent_ratings": rating_records[-10:], # Last 10 ratings
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/analytics/dashboard")
|
@app.get("/api/v1/analytics/dashboard")
|
||||||
@@ -257,7 +257,7 @@ async def get_analytics_dashboard():
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"dashboard": dashboard_data,
|
"dashboard": dashboard_data,
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/analytics/trends")
|
@app.get("/api/v1/analytics/trends")
|
||||||
@@ -398,7 +398,7 @@ def get_overview_statistics() -> Dict[str, Any]:
|
|||||||
total_ratings = sum(len(data) for data in plugin_ratings.values())
|
total_ratings = sum(len(data) for data in plugin_ratings.values())
|
||||||
|
|
||||||
# Calculate active plugins (plugins with usage in last 7 days)
|
# Calculate active plugins (plugins with usage in last 7 days)
|
||||||
cutoff_date = datetime.now(datetime.UTC) - timedelta(days=7)
|
cutoff_date = datetime.now(timezone.utc) - timedelta(days=7)
|
||||||
active_plugins = 0
|
active_plugins = 0
|
||||||
|
|
||||||
for plugin_id, usage_records in plugin_usage_data.items():
|
for plugin_id, usage_records in plugin_usage_data.items():
|
||||||
@@ -417,7 +417,7 @@ def get_overview_statistics() -> Dict[str, Any]:
|
|||||||
|
|
||||||
def get_trending_plugins(limit: int = 10) -> List[Dict]:
|
def get_trending_plugins(limit: int = 10) -> List[Dict]:
|
||||||
"""Get trending plugins based on recent usage"""
|
"""Get trending plugins based on recent usage"""
|
||||||
cutoff_date = datetime.now(datetime.UTC) - timedelta(days=7)
|
cutoff_date = datetime.now(timezone.utc) - timedelta(days=7)
|
||||||
|
|
||||||
plugin_scores = []
|
plugin_scores = []
|
||||||
|
|
||||||
@@ -443,7 +443,7 @@ def get_trending_plugins(limit: int = 10) -> List[Dict]:
|
|||||||
|
|
||||||
def get_global_usage_trends(days: int = 30) -> Dict[str, Any]:
|
def get_global_usage_trends(days: int = 30) -> Dict[str, Any]:
|
||||||
"""Get global usage trends"""
|
"""Get global usage trends"""
|
||||||
cutoff_date = datetime.now(datetime.UTC) - timedelta(days=days)
|
cutoff_date = datetime.now(timezone.utc) - timedelta(days=days)
|
||||||
global_trends = {}
|
global_trends = {}
|
||||||
|
|
||||||
for plugin_id, usage_records in plugin_usage_data.items():
|
for plugin_id, usage_records in plugin_usage_data.items():
|
||||||
@@ -541,14 +541,14 @@ def get_plugin_trends(plugin_id: str, days: int) -> Dict[str, Any]:
|
|||||||
"""Get trends for a specific plugin"""
|
"""Get trends for a specific plugin"""
|
||||||
plugin_trends = usage_trends.get(plugin_id, {})
|
plugin_trends = usage_trends.get(plugin_id, {})
|
||||||
|
|
||||||
cutoff_date = datetime.now(datetime.UTC) - timedelta(days=days)
|
cutoff_date = datetime.now(timezone.utc) - timedelta(days=days)
|
||||||
date_key = cutoff_date.date().isoformat()
|
date_key = cutoff_date.date().isoformat()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"plugin_id": plugin_id,
|
"plugin_id": plugin_id,
|
||||||
"trends": plugin_trends,
|
"trends": plugin_trends,
|
||||||
"period_days": days,
|
"period_days": days,
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Report generation functions
|
# Report generation functions
|
||||||
@@ -581,7 +581,7 @@ def generate_summary_report(plugin_id: Optional[str] = None) -> Dict[str, Any]:
|
|||||||
"usage": get_plugin_usage(plugin_id, days=30),
|
"usage": get_plugin_usage(plugin_id, days=30),
|
||||||
"performance": get_plugin_performance(plugin_id, hours=24),
|
"performance": get_plugin_performance(plugin_id, hours=24),
|
||||||
"ratings": get_plugin_ratings(plugin_id),
|
"ratings": get_plugin_ratings(plugin_id),
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
return get_analytics_dashboard()
|
return get_analytics_dashboard()
|
||||||
@@ -613,7 +613,7 @@ def update_analytics_cache():
|
|||||||
|
|
||||||
def cleanup_old_data():
|
def cleanup_old_data():
|
||||||
"""Clean up old analytics data"""
|
"""Clean up old analytics data"""
|
||||||
cutoff_date = datetime.now(datetime.UTC) - timedelta(days=90)
|
cutoff_date = datetime.now(timezone.utc) - timedelta(days=90)
|
||||||
cutoff_iso = cutoff_date.isoformat()
|
cutoff_iso = cutoff_date.isoformat()
|
||||||
|
|
||||||
# Clean usage data
|
# Clean usage data
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import sys
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
from main import app, PluginUsage, PluginPerformance, PluginRating, PluginEvent, plugin_usage_data, plugin_performance_data, plugin_ratings, plugin_events
|
from main import app, PluginUsage, PluginPerformance, PluginRating, PluginEvent, plugin_usage_data, plugin_performance_data, plugin_ratings, plugin_events
|
||||||
@@ -32,7 +32,7 @@ def test_plugin_usage_empty_plugin_id():
|
|||||||
plugin_id="",
|
plugin_id="",
|
||||||
user_id="user_123",
|
user_id="user_123",
|
||||||
action="install",
|
action="install",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert usage.plugin_id == ""
|
assert usage.plugin_id == ""
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ def test_plugin_performance_negative_values():
|
|||||||
response_time=-0.1,
|
response_time=-0.1,
|
||||||
error_rate=-0.01,
|
error_rate=-0.01,
|
||||||
uptime=-50.0,
|
uptime=-50.0,
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert perf.cpu_usage == -10.0
|
assert perf.cpu_usage == -10.0
|
||||||
assert perf.memory_usage == -5.0
|
assert perf.memory_usage == -5.0
|
||||||
@@ -61,7 +61,7 @@ def test_plugin_rating_out_of_range():
|
|||||||
plugin_id="plugin_123",
|
plugin_id="plugin_123",
|
||||||
user_id="user_123",
|
user_id="user_123",
|
||||||
rating=10,
|
rating=10,
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert rating.rating == 10
|
assert rating.rating == 10
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ def test_plugin_rating_zero():
|
|||||||
plugin_id="plugin_123",
|
plugin_id="plugin_123",
|
||||||
user_id="user_123",
|
user_id="user_123",
|
||||||
rating=0,
|
rating=0,
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert rating.rating == 0
|
assert rating.rating == 0
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ def test_record_multiple_usage_events():
|
|||||||
plugin_id="plugin_123",
|
plugin_id="plugin_123",
|
||||||
user_id=f"user_{i}",
|
user_id=f"user_{i}",
|
||||||
action="use",
|
action="use",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
client.post("/api/v1/analytics/usage", json=usage.model_dump(mode='json'))
|
client.post("/api/v1/analytics/usage", json=usage.model_dump(mode='json'))
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import sys
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
from main import app, PluginUsage, PluginPerformance, PluginRating, PluginEvent, plugin_usage_data, plugin_performance_data, plugin_ratings, plugin_events
|
from main import app, PluginUsage, PluginPerformance, PluginRating, PluginEvent, plugin_usage_data, plugin_performance_data, plugin_ratings, plugin_events
|
||||||
@@ -56,7 +56,7 @@ def test_record_plugin_usage():
|
|||||||
plugin_id="plugin_123",
|
plugin_id="plugin_123",
|
||||||
user_id="user_123",
|
user_id="user_123",
|
||||||
action="install",
|
action="install",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
response = client.post("/api/v1/analytics/usage", json=usage.model_dump(mode='json'))
|
response = client.post("/api/v1/analytics/usage", json=usage.model_dump(mode='json'))
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
@@ -77,7 +77,7 @@ def test_record_plugin_performance():
|
|||||||
response_time=0.123,
|
response_time=0.123,
|
||||||
error_rate=0.001,
|
error_rate=0.001,
|
||||||
uptime=99.9,
|
uptime=99.9,
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
response = client.post("/api/v1/analytics/performance", json=perf.model_dump(mode='json'))
|
response = client.post("/api/v1/analytics/performance", json=perf.model_dump(mode='json'))
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
@@ -95,7 +95,7 @@ def test_record_plugin_rating():
|
|||||||
user_id="user_123",
|
user_id="user_123",
|
||||||
rating=5,
|
rating=5,
|
||||||
review="Great plugin!",
|
review="Great plugin!",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
response = client.post("/api/v1/analytics/rating", json=rating.model_dump(mode='json'))
|
response = client.post("/api/v1/analytics/rating", json=rating.model_dump(mode='json'))
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
@@ -113,7 +113,7 @@ def test_record_plugin_event():
|
|||||||
plugin_id="plugin_123",
|
plugin_id="plugin_123",
|
||||||
user_id="user_123",
|
user_id="user_123",
|
||||||
data={"error": "timeout"},
|
data={"error": "timeout"},
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
response = client.post("/api/v1/analytics/event", json=event.model_dump(mode='json'))
|
response = client.post("/api/v1/analytics/event", json=event.model_dump(mode='json'))
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
@@ -131,7 +131,7 @@ def test_get_plugin_usage():
|
|||||||
plugin_id="plugin_123",
|
plugin_id="plugin_123",
|
||||||
user_id="user_123",
|
user_id="user_123",
|
||||||
action="install",
|
action="install",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
client.post("/api/v1/analytics/usage", json=usage.model_dump(mode='json'))
|
client.post("/api/v1/analytics/usage", json=usage.model_dump(mode='json'))
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@ def test_get_plugin_performance():
|
|||||||
response_time=0.123,
|
response_time=0.123,
|
||||||
error_rate=0.001,
|
error_rate=0.001,
|
||||||
uptime=99.9,
|
uptime=99.9,
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
client.post("/api/v1/analytics/performance", json=perf.model_dump(mode='json'))
|
client.post("/api/v1/analytics/performance", json=perf.model_dump(mode='json'))
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ def test_get_plugin_ratings():
|
|||||||
plugin_id="plugin_123",
|
plugin_id="plugin_123",
|
||||||
user_id="user_123",
|
user_id="user_123",
|
||||||
rating=5,
|
rating=5,
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
client.post("/api/v1/analytics/rating", json=rating.model_dump(mode='json'))
|
client.post("/api/v1/analytics/rating", json=rating.model_dump(mode='json'))
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import pytest
|
|||||||
import sys
|
import sys
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
from main import app, PluginUsage, PluginPerformance, PluginRating, PluginEvent
|
from main import app, PluginUsage, PluginPerformance, PluginRating, PluginEvent
|
||||||
@@ -25,7 +25,7 @@ def test_plugin_usage_model():
|
|||||||
plugin_id="plugin_123",
|
plugin_id="plugin_123",
|
||||||
user_id="user_123",
|
user_id="user_123",
|
||||||
action="install",
|
action="install",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
metadata={"source": "marketplace"}
|
metadata={"source": "marketplace"}
|
||||||
)
|
)
|
||||||
assert usage.plugin_id == "plugin_123"
|
assert usage.plugin_id == "plugin_123"
|
||||||
@@ -41,7 +41,7 @@ def test_plugin_usage_defaults():
|
|||||||
plugin_id="plugin_123",
|
plugin_id="plugin_123",
|
||||||
user_id="user_123",
|
user_id="user_123",
|
||||||
action="use",
|
action="use",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert usage.metadata == {}
|
assert usage.metadata == {}
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ def test_plugin_performance_model():
|
|||||||
response_time=0.123,
|
response_time=0.123,
|
||||||
error_rate=0.001,
|
error_rate=0.001,
|
||||||
uptime=99.9,
|
uptime=99.9,
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert perf.plugin_id == "plugin_123"
|
assert perf.plugin_id == "plugin_123"
|
||||||
assert perf.version == "1.0.0"
|
assert perf.version == "1.0.0"
|
||||||
@@ -76,7 +76,7 @@ def test_plugin_rating_model():
|
|||||||
user_id="user_123",
|
user_id="user_123",
|
||||||
rating=5,
|
rating=5,
|
||||||
review="Great plugin!",
|
review="Great plugin!",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert rating.plugin_id == "plugin_123"
|
assert rating.plugin_id == "plugin_123"
|
||||||
assert rating.rating == 5
|
assert rating.rating == 5
|
||||||
@@ -90,7 +90,7 @@ def test_plugin_rating_defaults():
|
|||||||
plugin_id="plugin_123",
|
plugin_id="plugin_123",
|
||||||
user_id="user_123",
|
user_id="user_123",
|
||||||
rating=4,
|
rating=4,
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert rating.review is None
|
assert rating.review is None
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ def test_plugin_event_model():
|
|||||||
plugin_id="plugin_123",
|
plugin_id="plugin_123",
|
||||||
user_id="user_123",
|
user_id="user_123",
|
||||||
data={"error": "timeout"},
|
data={"error": "timeout"},
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert event.event_type == "error"
|
assert event.event_type == "error"
|
||||||
assert event.plugin_id == "plugin_123"
|
assert event.plugin_id == "plugin_123"
|
||||||
@@ -117,7 +117,7 @@ def test_plugin_event_defaults():
|
|||||||
event = PluginEvent(
|
event = PluginEvent(
|
||||||
event_type="info",
|
event_type="info",
|
||||||
plugin_id="plugin_123",
|
plugin_id="plugin_123",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert event.user_id is None
|
assert event.user_id is None
|
||||||
assert event.data == {}
|
assert event.data == {}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Provides web interface and marketplace functionality for plugins
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any, List, Optional
|
from typing import Dict, Any, List, Optional
|
||||||
from fastapi import FastAPI, HTTPException, Request
|
from fastapi import FastAPI, HTTPException, Request
|
||||||
@@ -120,7 +120,7 @@ async def get_featured_plugins_api():
|
|||||||
"""Get featured plugins for marketplace"""
|
"""Get featured plugins for marketplace"""
|
||||||
return {
|
return {
|
||||||
"featured_plugins": get_featured_plugins(),
|
"featured_plugins": get_featured_plugins(),
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/marketplace/popular")
|
@app.get("/api/v1/marketplace/popular")
|
||||||
@@ -128,7 +128,7 @@ async def get_popular_plugins_api(limit: int = 12):
|
|||||||
"""Get popular plugins"""
|
"""Get popular plugins"""
|
||||||
return {
|
return {
|
||||||
"popular_plugins": get_popular_plugins(limit),
|
"popular_plugins": get_popular_plugins(limit),
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/marketplace/recent")
|
@app.get("/api/v1/marketplace/recent")
|
||||||
@@ -136,7 +136,7 @@ async def get_recent_plugins_api(limit: int = 12):
|
|||||||
"""Get recently added plugins"""
|
"""Get recently added plugins"""
|
||||||
return {
|
return {
|
||||||
"recent_plugins": get_recent_plugins(limit),
|
"recent_plugins": get_recent_plugins(limit),
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/marketplace/stats")
|
@app.get("/api/v1/marketplace/stats")
|
||||||
@@ -144,13 +144,13 @@ async def get_marketplace_stats_api():
|
|||||||
"""Get marketplace statistics"""
|
"""Get marketplace statistics"""
|
||||||
return {
|
return {
|
||||||
"stats": get_marketplace_stats(),
|
"stats": get_marketplace_stats(),
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.post("/api/v1/reviews")
|
@app.post("/api/v1/reviews")
|
||||||
async def create_review(review: MarketplaceReview):
|
async def create_review(review: MarketplaceReview):
|
||||||
"""Create a plugin review"""
|
"""Create a plugin review"""
|
||||||
review_id = f"review_{int(datetime.now(datetime.UTC).timestamp())}"
|
review_id = f"review_{int(datetime.now(timezone.utc).timestamp())}"
|
||||||
|
|
||||||
review_record = {
|
review_record = {
|
||||||
"review_id": review_id,
|
"review_id": review_id,
|
||||||
@@ -162,7 +162,7 @@ async def create_review(review: MarketplaceReview):
|
|||||||
"pros": review.pros,
|
"pros": review.pros,
|
||||||
"cons": review.cons,
|
"cons": review.cons,
|
||||||
"helpful_votes": 0,
|
"helpful_votes": 0,
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"verified_purchase": False
|
"verified_purchase": False
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@ async def get_plugin_reviews_api(plugin_id: str):
|
|||||||
@app.post("/api/v1/purchases")
|
@app.post("/api/v1/purchases")
|
||||||
async def create_purchase(purchase: PluginPurchase):
|
async def create_purchase(purchase: PluginPurchase):
|
||||||
"""Create a plugin purchase"""
|
"""Create a plugin purchase"""
|
||||||
purchase_id = f"purchase_{int(datetime.now(datetime.UTC).timestamp())}"
|
purchase_id = f"purchase_{int(datetime.now(timezone.utc).timestamp())}"
|
||||||
|
|
||||||
purchase_record = {
|
purchase_record = {
|
||||||
"purchase_id": purchase_id,
|
"purchase_id": purchase_id,
|
||||||
@@ -211,8 +211,8 @@ async def create_purchase(purchase: PluginPurchase):
|
|||||||
"price": purchase.price,
|
"price": purchase.price,
|
||||||
"payment_method": purchase.payment_method,
|
"payment_method": purchase.payment_method,
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"refund_deadline": (datetime.now(datetime.UTC) + timedelta(days=30)).isoformat()
|
"refund_deadline": (datetime.now(timezone.utc) + timedelta(days=30)).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
if purchase.plugin_id not in purchases:
|
if purchase.plugin_id not in purchases:
|
||||||
@@ -235,7 +235,7 @@ async def create_purchase(purchase: PluginPurchase):
|
|||||||
@app.post("/api/v1/developers/apply")
|
@app.post("/api/v1/developers/apply")
|
||||||
async def apply_developer(application: DeveloperApplication):
|
async def apply_developer(application: DeveloperApplication):
|
||||||
"""Apply to become a verified developer"""
|
"""Apply to become a verified developer"""
|
||||||
application_id = f"dev_app_{int(datetime.now(datetime.UTC).timestamp())}"
|
application_id = f"dev_app_{int(datetime.now(timezone.utc).timestamp())}"
|
||||||
|
|
||||||
application_record = {
|
application_record = {
|
||||||
"application_id": application_id,
|
"application_id": application_id,
|
||||||
@@ -247,7 +247,7 @@ async def apply_developer(application: DeveloperApplication):
|
|||||||
"github_username": application.github_username,
|
"github_username": application.github_username,
|
||||||
"description": application.description,
|
"description": application.description,
|
||||||
"status": "pending",
|
"status": "pending",
|
||||||
"submitted_at": datetime.now(datetime.UTC).isoformat(),
|
"submitted_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"reviewed_at": None,
|
"reviewed_at": None,
|
||||||
"reviewer_notes": None
|
"reviewer_notes": None
|
||||||
}
|
}
|
||||||
@@ -268,7 +268,7 @@ async def get_verified_developers_api():
|
|||||||
return {
|
return {
|
||||||
"verified_developers": get_verified_developers(),
|
"verified_developers": get_verified_developers(),
|
||||||
"total_developers": len(verified_developers),
|
"total_developers": len(verified_developers),
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/revenue/{developer_id}")
|
@app.get("/api/v1/revenue/{developer_id}")
|
||||||
@@ -278,7 +278,7 @@ async def get_developer_revenue(developer_id: str):
|
|||||||
"total_revenue": 0.0,
|
"total_revenue": 0.0,
|
||||||
"plugin_revenue": {},
|
"plugin_revenue": {},
|
||||||
"monthly_revenue": {},
|
"monthly_revenue": {},
|
||||||
"last_updated": datetime.now(datetime.UTC).isoformat()
|
"last_updated": datetime.now(timezone.utc).isoformat()
|
||||||
})
|
})
|
||||||
|
|
||||||
return developer_revenue
|
return developer_revenue
|
||||||
@@ -358,7 +358,7 @@ def get_recent_plugins(limit: int = 12) -> List[Dict]:
|
|||||||
"rating": 4.9,
|
"rating": 4.9,
|
||||||
"downloads": 2340,
|
"downloads": 2340,
|
||||||
"price": 199.99,
|
"price": 199.99,
|
||||||
"created_at": (datetime.now(datetime.UTC) - timedelta(days=3)).isoformat()
|
"created_at": (datetime.now(timezone.utc) - timedelta(days=3)).isoformat()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"plugin_id": "performance_monitor",
|
"plugin_id": "performance_monitor",
|
||||||
@@ -369,7 +369,7 @@ def get_recent_plugins(limit: int = 12) -> List[Dict]:
|
|||||||
"rating": 4.4,
|
"rating": 4.4,
|
||||||
"downloads": 1890,
|
"downloads": 1890,
|
||||||
"price": 59.99,
|
"price": 59.99,
|
||||||
"created_at": (datetime.now(datetime.UTC) - timedelta(days=7)).isoformat()
|
"created_at": (datetime.now(timezone.utc) - timedelta(days=7)).isoformat()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -414,7 +414,7 @@ def get_plugin_details(plugin_id: str) -> Optional[Dict]:
|
|||||||
"downloads": 15420,
|
"downloads": 15420,
|
||||||
"price": 99.99,
|
"price": 99.99,
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"last_updated": (datetime.now(datetime.UTC) - timedelta(days=15)).isoformat(),
|
"last_updated": (datetime.now(timezone.utc) - timedelta(days=15)).isoformat(),
|
||||||
"repository_url": "https://github.com/aitbc-labs/ai-trading-bot",
|
"repository_url": "https://github.com/aitbc-labs/ai-trading-bot",
|
||||||
"homepage_url": "https://aitbc-trading-bot.com",
|
"homepage_url": "https://aitbc-trading-bot.com",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -442,7 +442,7 @@ def get_plugin_reviews(plugin_id: str) -> List[Dict]:
|
|||||||
"pros": ["Easy to use", "Great performance", "Good documentation"],
|
"pros": ["Easy to use", "Great performance", "Good documentation"],
|
||||||
"cons": ["Initial setup complexity"],
|
"cons": ["Initial setup complexity"],
|
||||||
"helpful_votes": 23,
|
"helpful_votes": 23,
|
||||||
"created_at": (datetime.now(datetime.UTC) - timedelta(days=10)).isoformat()
|
"created_at": (datetime.now(timezone.utc) - timedelta(days=10)).isoformat()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"review_id": "review_2",
|
"review_id": "review_2",
|
||||||
@@ -453,7 +453,7 @@ def get_plugin_reviews(plugin_id: str) -> List[Dict]:
|
|||||||
"pros": ["Powerful features", "Good support"],
|
"pros": ["Powerful features", "Good support"],
|
||||||
"cons": ["UI could be better", "Learning curve"],
|
"cons": ["UI could be better", "Learning curve"],
|
||||||
"helpful_votes": 15,
|
"helpful_votes": 15,
|
||||||
"created_at": (datetime.now(datetime.UTC) - timedelta(days=25)).isoformat()
|
"created_at": (datetime.now(timezone.utc) - timedelta(days=25)).isoformat()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Handles plugin registration, discovery, versioning, and security validation
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import hashlib
|
import hashlib
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any, List, Optional
|
from typing import Dict, Any, List, Optional
|
||||||
from fastapi import FastAPI, HTTPException, UploadFile, File
|
from fastapi import FastAPI, HTTPException, UploadFile, File
|
||||||
@@ -66,7 +66,7 @@ async def root():
|
|||||||
return {
|
return {
|
||||||
"service": "AITBC Plugin Registry",
|
"service": "AITBC Plugin Registry",
|
||||||
"status": "running",
|
"status": "running",
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ async def health_check():
|
|||||||
"security_scans": len(security_scans),
|
"security_scans": len(security_scans),
|
||||||
"downloads_today": len([d for downloads_list in downloads.values()
|
"downloads_today": len([d for downloads_list in downloads.values()
|
||||||
for d in downloads_list
|
for d in downloads_list
|
||||||
if datetime.fromisoformat(d["timestamp"]).date() == datetime.now(datetime.UTC).date()])
|
if datetime.fromisoformat(d["timestamp"]).date() == datetime.now(timezone.utc).date()])
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.post("/api/v1/plugins/register")
|
@app.post("/api/v1/plugins/register")
|
||||||
@@ -105,8 +105,8 @@ async def register_plugin(plugin: PluginRegistration):
|
|||||||
"aitbc_version": plugin.aitbc_version,
|
"aitbc_version": plugin.aitbc_version,
|
||||||
"plugin_type": plugin.plugin_type,
|
"plugin_type": plugin.plugin_type,
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"updated_at": datetime.now(datetime.UTC).isoformat(),
|
"updated_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"verified": False,
|
"verified": False,
|
||||||
"featured": False,
|
"featured": False,
|
||||||
"download_count": 0,
|
"download_count": 0,
|
||||||
@@ -158,14 +158,14 @@ async def add_plugin_version(plugin_id: str, version: PluginVersion):
|
|||||||
"release_date": version.release_date.isoformat(),
|
"release_date": version.release_date.isoformat(),
|
||||||
"downloads": 0,
|
"downloads": 0,
|
||||||
"security_scan_passed": False,
|
"security_scan_passed": False,
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat()
|
"created_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
plugin_versions[plugin_id].append(version_record)
|
plugin_versions[plugin_id].append(version_record)
|
||||||
|
|
||||||
# Update plugin's latest version
|
# Update plugin's latest version
|
||||||
plugins[plugin_id]["latest_version"] = version.version
|
plugins[plugin_id]["latest_version"] = version.version
|
||||||
plugins[plugin_id]["updated_at"] = datetime.now(datetime.UTC).isoformat()
|
plugins[plugin_id]["updated_at"] = datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
# Sort versions by version number (semantic versioning)
|
# Sort versions by version number (semantic versioning)
|
||||||
plugin_versions[plugin_id].sort(key=lambda x: x["version"], reverse=True)
|
plugin_versions[plugin_id].sort(key=lambda x: x["version"], reverse=True)
|
||||||
@@ -272,7 +272,7 @@ async def download_plugin(plugin_id: str, version: str):
|
|||||||
# Record download
|
# Record download
|
||||||
download_record = {
|
download_record = {
|
||||||
"version": version,
|
"version": version,
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"ip_address": "client_ip", # In production, get actual IP
|
"ip_address": "client_ip", # In production, get actual IP
|
||||||
"user_agent": "user_agent" # In production, get actual user agent
|
"user_agent": "user_agent" # In production, get actual user agent
|
||||||
}
|
}
|
||||||
@@ -284,7 +284,7 @@ async def download_plugin(plugin_id: str, version: str):
|
|||||||
# Update analytics
|
# Update analytics
|
||||||
if plugin_id not in analytics:
|
if plugin_id not in analytics:
|
||||||
analytics[plugin_id] = {"downloads": [], "views": [], "ratings": []}
|
analytics[plugin_id] = {"downloads": [], "views": [], "ratings": []}
|
||||||
analytics[plugin_id]["downloads"].append(datetime.now(datetime.UTC).timestamp())
|
analytics[plugin_id]["downloads"].append(datetime.now(timezone.utc).timestamp())
|
||||||
|
|
||||||
# Update plugin download count
|
# Update plugin download count
|
||||||
plugins[plugin_id]["download_count"] += 1
|
plugins[plugin_id]["download_count"] += 1
|
||||||
@@ -319,7 +319,7 @@ async def create_security_scan(plugin_id: str, scan: SecurityScan):
|
|||||||
"vulnerabilities": scan.vulnerabilities,
|
"vulnerabilities": scan.vulnerabilities,
|
||||||
"risk_score": scan.risk_score,
|
"risk_score": scan.risk_score,
|
||||||
"passed": scan.passed,
|
"passed": scan.passed,
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat()
|
"created_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Update version security status
|
# Update version security status
|
||||||
@@ -400,7 +400,7 @@ async def get_popular_plugins(limit: int = 10):
|
|||||||
return {
|
return {
|
||||||
"popular_plugins": popular_plugins,
|
"popular_plugins": popular_plugins,
|
||||||
"limit": limit,
|
"limit": limit,
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/analytics/recent")
|
@app.get("/api/v1/analytics/recent")
|
||||||
@@ -411,7 +411,7 @@ async def get_recent_plugins(limit: int = 10):
|
|||||||
return {
|
return {
|
||||||
"recent_plugins": recent_plugins,
|
"recent_plugins": recent_plugins,
|
||||||
"limit": limit,
|
"limit": limit,
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/analytics/dashboard")
|
@app.get("/api/v1/analytics/dashboard")
|
||||||
@@ -429,7 +429,7 @@ async def get_analytics_dashboard():
|
|||||||
|
|
||||||
# Recent activity
|
# Recent activity
|
||||||
recent_downloads = 0
|
recent_downloads = 0
|
||||||
today = datetime.now(datetime.UTC).date()
|
today = datetime.now(timezone.utc).date()
|
||||||
for download_list in downloads.values():
|
for download_list in downloads.values():
|
||||||
recent_downloads += len([d for d in download_list
|
recent_downloads += len([d for d in download_list
|
||||||
if datetime.fromisoformat(d["timestamp"]).date() == today])
|
if datetime.fromisoformat(d["timestamp"]).date() == today])
|
||||||
@@ -444,7 +444,7 @@ async def get_analytics_dashboard():
|
|||||||
"security_scans": len(security_scans),
|
"security_scans": len(security_scans),
|
||||||
"passed_scans": len([s for s in security_scans.values() if s["passed"]])
|
"passed_scans": len([s for s in security_scans.values() if s["passed"]])
|
||||||
},
|
},
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Background task for analytics processing
|
# Background task for analytics processing
|
||||||
@@ -454,7 +454,7 @@ async def process_analytics():
|
|||||||
await asyncio.sleep(3600) # Process every hour
|
await asyncio.sleep(3600) # Process every hour
|
||||||
|
|
||||||
# Update daily statistics
|
# Update daily statistics
|
||||||
current_date = datetime.now(datetime.UTC).date()
|
current_date = datetime.now(timezone.utc).date()
|
||||||
|
|
||||||
for plugin_id, plugin_analytics in analytics.items():
|
for plugin_id, plugin_analytics in analytics.items():
|
||||||
daily_key = current_date.isoformat()
|
daily_key = current_date.isoformat()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import sys
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
from main import app, PluginRegistration, PluginVersion, SecurityScan, plugins, plugin_versions, security_scans, analytics, downloads
|
from main import app, PluginRegistration, PluginVersion, SecurityScan, plugins, plugin_versions, security_scans, analytics, downloads
|
||||||
@@ -74,7 +74,7 @@ def test_plugin_version_empty_changelog():
|
|||||||
download_url="https://github.com/test/plugin/archive/v1.0.0.tar.gz",
|
download_url="https://github.com/test/plugin/archive/v1.0.0.tar.gz",
|
||||||
checksum="abc123",
|
checksum="abc123",
|
||||||
aitbc_compatibility=["1.0.0"],
|
aitbc_compatibility=["1.0.0"],
|
||||||
release_date=datetime.now(datetime.UTC)
|
release_date=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert version.changelog == ""
|
assert version.changelog == ""
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ def test_security_scan_empty_vulnerabilities():
|
|||||||
scan_id="scan_123",
|
scan_id="scan_123",
|
||||||
plugin_id="test_plugin",
|
plugin_id="test_plugin",
|
||||||
version="1.0.0",
|
version="1.0.0",
|
||||||
scan_date=datetime.now(datetime.UTC),
|
scan_date=datetime.now(timezone.utc),
|
||||||
vulnerabilities=[],
|
vulnerabilities=[],
|
||||||
risk_score="low",
|
risk_score="low",
|
||||||
passed=True
|
passed=True
|
||||||
@@ -104,7 +104,7 @@ def test_add_version_nonexistent_plugin():
|
|||||||
download_url="https://github.com/test/plugin/archive/v1.0.0.tar.gz",
|
download_url="https://github.com/test/plugin/archive/v1.0.0.tar.gz",
|
||||||
checksum="abc123",
|
checksum="abc123",
|
||||||
aitbc_compatibility=["1.0.0"],
|
aitbc_compatibility=["1.0.0"],
|
||||||
release_date=datetime.now(datetime.UTC)
|
release_date=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
response = client.post("/api/v1/plugins/nonexistent/versions", json=version.model_dump(mode='json'))
|
response = client.post("/api/v1/plugins/nonexistent/versions", json=version.model_dump(mode='json'))
|
||||||
assert response.status_code == 404
|
assert response.status_code == 404
|
||||||
@@ -152,7 +152,7 @@ def test_security_scan_nonexistent_plugin():
|
|||||||
scan_id="scan_123",
|
scan_id="scan_123",
|
||||||
plugin_id="nonexistent",
|
plugin_id="nonexistent",
|
||||||
version="1.0.0",
|
version="1.0.0",
|
||||||
scan_date=datetime.now(datetime.UTC),
|
scan_date=datetime.now(timezone.utc),
|
||||||
vulnerabilities=[],
|
vulnerabilities=[],
|
||||||
risk_score="low",
|
risk_score="low",
|
||||||
passed=True
|
passed=True
|
||||||
@@ -187,7 +187,7 @@ def test_security_scan_nonexistent_version():
|
|||||||
scan_id="scan_123",
|
scan_id="scan_123",
|
||||||
plugin_id="test_plugin",
|
plugin_id="test_plugin",
|
||||||
version="2.0.0",
|
version="2.0.0",
|
||||||
scan_date=datetime.now(datetime.UTC),
|
scan_date=datetime.now(timezone.utc),
|
||||||
vulnerabilities=[],
|
vulnerabilities=[],
|
||||||
risk_score="low",
|
risk_score="low",
|
||||||
passed=True
|
passed=True
|
||||||
@@ -296,7 +296,7 @@ def test_security_scan_failed():
|
|||||||
download_url="https://github.com/test/plugin/archive/v1.0.0.tar.gz",
|
download_url="https://github.com/test/plugin/archive/v1.0.0.tar.gz",
|
||||||
checksum="abc123",
|
checksum="abc123",
|
||||||
aitbc_compatibility=["1.0.0"],
|
aitbc_compatibility=["1.0.0"],
|
||||||
release_date=datetime.now(datetime.UTC)
|
release_date=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
client.post("/api/v1/plugins/test_plugin/versions", json=version.model_dump(mode='json'))
|
client.post("/api/v1/plugins/test_plugin/versions", json=version.model_dump(mode='json'))
|
||||||
|
|
||||||
@@ -305,7 +305,7 @@ def test_security_scan_failed():
|
|||||||
scan_id="scan_123",
|
scan_id="scan_123",
|
||||||
plugin_id="test_plugin",
|
plugin_id="test_plugin",
|
||||||
version="1.0.0",
|
version="1.0.0",
|
||||||
scan_date=datetime.now(datetime.UTC),
|
scan_date=datetime.now(timezone.utc),
|
||||||
vulnerabilities=[{"severity": "high", "description": "Critical issue"}],
|
vulnerabilities=[{"severity": "high", "description": "Critical issue"}],
|
||||||
risk_score="high",
|
risk_score="high",
|
||||||
passed=False
|
passed=False
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import sys
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
from main import app, PluginRegistration, PluginVersion, SecurityScan, plugins, plugin_versions, security_scans, analytics, downloads
|
from main import app, PluginRegistration, PluginVersion, SecurityScan, plugins, plugin_versions, security_scans, analytics, downloads
|
||||||
@@ -129,7 +129,7 @@ def test_add_plugin_version():
|
|||||||
download_url="https://github.com/test/plugin/archive/v1.1.0.tar.gz",
|
download_url="https://github.com/test/plugin/archive/v1.1.0.tar.gz",
|
||||||
checksum="def456",
|
checksum="def456",
|
||||||
aitbc_compatibility=["1.0.0"],
|
aitbc_compatibility=["1.0.0"],
|
||||||
release_date=datetime.now(datetime.UTC)
|
release_date=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
response = client.post("/api/v1/plugins/test_plugin/versions", json=version.model_dump(mode='json'))
|
response = client.post("/api/v1/plugins/test_plugin/versions", json=version.model_dump(mode='json'))
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
@@ -166,7 +166,7 @@ def test_add_duplicate_version():
|
|||||||
download_url="https://github.com/test/plugin/archive/v1.1.0.tar.gz",
|
download_url="https://github.com/test/plugin/archive/v1.1.0.tar.gz",
|
||||||
checksum="def456",
|
checksum="def456",
|
||||||
aitbc_compatibility=["1.0.0"],
|
aitbc_compatibility=["1.0.0"],
|
||||||
release_date=datetime.now(datetime.UTC)
|
release_date=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
client.post("/api/v1/plugins/test_plugin/versions", json=version.model_dump(mode='json'))
|
client.post("/api/v1/plugins/test_plugin/versions", json=version.model_dump(mode='json'))
|
||||||
|
|
||||||
@@ -280,7 +280,7 @@ def test_download_plugin():
|
|||||||
download_url="https://github.com/test/plugin/archive/v1.0.0.tar.gz",
|
download_url="https://github.com/test/plugin/archive/v1.0.0.tar.gz",
|
||||||
checksum="abc123",
|
checksum="abc123",
|
||||||
aitbc_compatibility=["1.0.0"],
|
aitbc_compatibility=["1.0.0"],
|
||||||
release_date=datetime.now(datetime.UTC)
|
release_date=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
client.post("/api/v1/plugins/test_plugin/versions", json=version.model_dump(mode='json'))
|
client.post("/api/v1/plugins/test_plugin/versions", json=version.model_dump(mode='json'))
|
||||||
|
|
||||||
@@ -320,7 +320,7 @@ def test_create_security_scan():
|
|||||||
download_url="https://github.com/test/plugin/archive/v1.0.0.tar.gz",
|
download_url="https://github.com/test/plugin/archive/v1.0.0.tar.gz",
|
||||||
checksum="abc123",
|
checksum="abc123",
|
||||||
aitbc_compatibility=["1.0.0"],
|
aitbc_compatibility=["1.0.0"],
|
||||||
release_date=datetime.now(datetime.UTC)
|
release_date=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
client.post("/api/v1/plugins/test_plugin/versions", json=version.model_dump(mode='json'))
|
client.post("/api/v1/plugins/test_plugin/versions", json=version.model_dump(mode='json'))
|
||||||
|
|
||||||
@@ -329,7 +329,7 @@ def test_create_security_scan():
|
|||||||
scan_id="scan_123",
|
scan_id="scan_123",
|
||||||
plugin_id="test_plugin",
|
plugin_id="test_plugin",
|
||||||
version="1.0.0",
|
version="1.0.0",
|
||||||
scan_date=datetime.now(datetime.UTC),
|
scan_date=datetime.now(timezone.utc),
|
||||||
vulnerabilities=[],
|
vulnerabilities=[],
|
||||||
risk_score="low",
|
risk_score="low",
|
||||||
passed=True
|
passed=True
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import pytest
|
|||||||
import sys
|
import sys
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
from main import app, PluginRegistration, PluginVersion, SecurityScan
|
from main import app, PluginRegistration, PluginVersion, SecurityScan
|
||||||
@@ -72,7 +72,7 @@ def test_plugin_version_model():
|
|||||||
download_url="https://github.com/test/plugin/archive/v1.0.0.tar.gz",
|
download_url="https://github.com/test/plugin/archive/v1.0.0.tar.gz",
|
||||||
checksum="abc123",
|
checksum="abc123",
|
||||||
aitbc_compatibility=["1.0.0", "1.1.0"],
|
aitbc_compatibility=["1.0.0", "1.1.0"],
|
||||||
release_date=datetime.now(datetime.UTC)
|
release_date=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
assert version.version == "1.0.0"
|
assert version.version == "1.0.0"
|
||||||
assert version.changelog == "Initial release"
|
assert version.changelog == "Initial release"
|
||||||
@@ -88,7 +88,7 @@ def test_security_scan_model():
|
|||||||
scan_id="scan_123",
|
scan_id="scan_123",
|
||||||
plugin_id="test_plugin",
|
plugin_id="test_plugin",
|
||||||
version="1.0.0",
|
version="1.0.0",
|
||||||
scan_date=datetime.now(datetime.UTC),
|
scan_date=datetime.now(timezone.utc),
|
||||||
vulnerabilities=[{"severity": "low", "description": "Test"}],
|
vulnerabilities=[{"severity": "low", "description": "Test"}],
|
||||||
risk_score="low",
|
risk_score="low",
|
||||||
passed=True
|
passed=True
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import json
|
|||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
import os
|
import os
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any, List, Optional
|
from typing import Dict, Any, List, Optional
|
||||||
from fastapi import FastAPI, HTTPException, UploadFile, File
|
from fastapi import FastAPI, HTTPException, UploadFile, File
|
||||||
@@ -63,7 +63,7 @@ async def root():
|
|||||||
return {
|
return {
|
||||||
"service": "AITBC Plugin Security Service",
|
"service": "AITBC Plugin Security Service",
|
||||||
"status": "running",
|
"status": "running",
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ async def health_check():
|
|||||||
@app.post("/api/v1/security/scan")
|
@app.post("/api/v1/security/scan")
|
||||||
async def initiate_security_scan(scan: SecurityScan):
|
async def initiate_security_scan(scan: SecurityScan):
|
||||||
"""Initiate a security scan for a plugin"""
|
"""Initiate a security scan for a plugin"""
|
||||||
scan_id = f"scan_{int(datetime.now(datetime.UTC).timestamp())}"
|
scan_id = f"scan_{int(datetime.now(timezone.utc).timestamp())}"
|
||||||
|
|
||||||
# Create scan record
|
# Create scan record
|
||||||
scan_record = {
|
scan_record = {
|
||||||
@@ -91,7 +91,7 @@ async def initiate_security_scan(scan: SecurityScan):
|
|||||||
"scan_type": scan.scan_type,
|
"scan_type": scan.scan_type,
|
||||||
"priority": scan.priority,
|
"priority": scan.priority,
|
||||||
"status": "queued",
|
"status": "queued",
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"started_at": None,
|
"started_at": None,
|
||||||
"completed_at": None,
|
"completed_at": None,
|
||||||
"duration": None,
|
"duration": None,
|
||||||
@@ -182,7 +182,7 @@ async def list_vulnerabilities(severity: Optional[str] = None,
|
|||||||
@app.post("/api/v1/security/policies")
|
@app.post("/api/v1/security/policies")
|
||||||
async def create_security_policy(policy: Dict[str, Any]):
|
async def create_security_policy(policy: Dict[str, Any]):
|
||||||
"""Create a new security policy"""
|
"""Create a new security policy"""
|
||||||
policy_id = f"policy_{int(datetime.now(datetime.UTC).timestamp())}"
|
policy_id = f"policy_{int(datetime.now(timezone.utc).timestamp())}"
|
||||||
|
|
||||||
policy_record = {
|
policy_record = {
|
||||||
"policy_id": policy_id,
|
"policy_id": policy_id,
|
||||||
@@ -197,8 +197,8 @@ async def create_security_policy(policy: Dict[str, Any]):
|
|||||||
}),
|
}),
|
||||||
"plugin_types": policy.get("plugin_types", []),
|
"plugin_types": policy.get("plugin_types", []),
|
||||||
"active": True,
|
"active": True,
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat(),
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
||||||
"updated_at": datetime.now(datetime.UTC).isoformat()
|
"updated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
security_policies[policy_id] = policy_record
|
security_policies[policy_id] = policy_record
|
||||||
@@ -261,7 +261,7 @@ async def get_security_dashboard():
|
|||||||
"""Get security dashboard data"""
|
"""Get security dashboard data"""
|
||||||
total_scans = len(scan_reports)
|
total_scans = len(scan_reports)
|
||||||
recent_scans = [r for r in scan_reports.values()
|
recent_scans = [r for r in scan_reports.values()
|
||||||
if datetime.fromisoformat(r["scan_date"]) > datetime.now(datetime.UTC) - timedelta(days=7)]
|
if datetime.fromisoformat(r["scan_date"]) > datetime.now(timezone.utc) - timedelta(days=7)]
|
||||||
|
|
||||||
# Calculate statistics
|
# Calculate statistics
|
||||||
scan_results = list(scan_reports.values())
|
scan_results = list(scan_reports.values())
|
||||||
@@ -294,7 +294,7 @@ async def get_security_dashboard():
|
|||||||
"queue_size": len(scan_queue),
|
"queue_size": len(scan_queue),
|
||||||
"active_policies": len([p for p in security_policies.values() if p["active"]])
|
"active_policies": len([p for p in security_policies.values() if p["active"]])
|
||||||
},
|
},
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Core security scanning functions
|
# Core security scanning functions
|
||||||
@@ -305,15 +305,15 @@ async def process_scan_file(scan_id: str, file_path: str, filename: str):
|
|||||||
for scan_record in scan_queue:
|
for scan_record in scan_queue:
|
||||||
if scan_record["scan_id"] == scan_id:
|
if scan_record["scan_id"] == scan_id:
|
||||||
scan_record["status"] = "running"
|
scan_record["status"] = "running"
|
||||||
scan_record["started_at"] = datetime.now(datetime.UTC).isoformat()
|
scan_record["started_at"] = datetime.now(timezone.utc).isoformat()
|
||||||
break
|
break
|
||||||
|
|
||||||
start_time = datetime.now(datetime.UTC)
|
start_time = datetime.now(timezone.utc)
|
||||||
|
|
||||||
# Perform security scan
|
# Perform security scan
|
||||||
scan_result = await perform_security_scan(file_path, filename)
|
scan_result = await perform_security_scan(file_path, filename)
|
||||||
|
|
||||||
end_time = datetime.now(datetime.UTC)
|
end_time = datetime.now(timezone.utc)
|
||||||
duration = (end_time - start_time).total_seconds()
|
duration = (end_time - start_time).total_seconds()
|
||||||
|
|
||||||
# Create security report
|
# Create security report
|
||||||
@@ -360,7 +360,7 @@ async def process_scan_file(scan_id: str, file_path: str, filename: str):
|
|||||||
for scan_record in scan_queue:
|
for scan_record in scan_queue:
|
||||||
if scan_record["scan_id"] == scan_id:
|
if scan_record["scan_id"] == scan_id:
|
||||||
scan_record["status"] = "failed"
|
scan_record["status"] = "failed"
|
||||||
scan_record["completed_at"] = datetime.now(datetime.UTC).isoformat()
|
scan_record["completed_at"] = datetime.now(timezone.utc).isoformat()
|
||||||
break
|
break
|
||||||
|
|
||||||
async def perform_security_scan(file_path: str, filename: str) -> Dict[str, Any]:
|
async def perform_security_scan(file_path: str, filename: str) -> Dict[str, Any]:
|
||||||
@@ -392,7 +392,7 @@ async def perform_security_scan(file_path: str, filename: str) -> Dict[str, Any]
|
|||||||
"vulnerability_count": len(vulnerabilities),
|
"vulnerability_count": len(vulnerabilities),
|
||||||
"severity_distribution": get_severity_distribution(vulnerabilities),
|
"severity_distribution": get_severity_distribution(vulnerabilities),
|
||||||
"file_type": filename.split('.')[-1],
|
"file_type": filename.split('.')[-1],
|
||||||
"scan_timestamp": datetime.now(datetime.UTC).isoformat()
|
"scan_timestamp": datetime.now(timezone.utc).isoformat()
|
||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import pytest
|
|||||||
import sys
|
import sys
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
|
||||||
from main import app, SecurityScan, Vulnerability, SecurityReport, calculate_overall_score, generate_recommendations, get_severity_distribution, estimate_scan_time
|
from main import app, SecurityScan, Vulnerability, SecurityReport, calculate_overall_score, generate_recommendations, get_severity_distribution, estimate_scan_time
|
||||||
@@ -76,7 +76,7 @@ def test_security_report_model():
|
|||||||
scan_id="scan_123",
|
scan_id="scan_123",
|
||||||
plugin_id="plugin_123",
|
plugin_id="plugin_123",
|
||||||
version="1.0.0",
|
version="1.0.0",
|
||||||
scan_date=datetime.now(datetime.UTC),
|
scan_date=datetime.now(timezone.utc),
|
||||||
scan_duration=120.5,
|
scan_duration=120.5,
|
||||||
overall_score="passed",
|
overall_score="passed",
|
||||||
vulnerabilities=[],
|
vulnerabilities=[],
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""Miner Registry Implementation"""
|
"""Miner Registry Implementation"""
|
||||||
|
|
||||||
from typing import List, Optional, Dict, Any
|
from typing import List, Optional, Dict, Any
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
@@ -22,8 +22,8 @@ class MinerInfo:
|
|||||||
jobs_completed: int = 0
|
jobs_completed: int = 0
|
||||||
jobs_failed: int = 0
|
jobs_failed: int = 0
|
||||||
uptime_percent: float = 100.0
|
uptime_percent: float = 100.0
|
||||||
registered_at: datetime = field(default_factory=datetime.now(datetime.UTC))
|
registered_at: datetime = field(default_factory=datetime.now(timezone.utc))
|
||||||
last_heartbeat: datetime = field(default_factory=datetime.now(datetime.UTC))
|
last_heartbeat: datetime = field(default_factory=datetime.now(timezone.utc))
|
||||||
gpu_utilization: float = 0.0
|
gpu_utilization: float = 0.0
|
||||||
memory_used_gb: float = 0.0
|
memory_used_gb: float = 0.0
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ class PoolInfo:
|
|||||||
total_hashrate: float = 0.0
|
total_hashrate: float = 0.0
|
||||||
jobs_completed_24h: int = 0
|
jobs_completed_24h: int = 0
|
||||||
earnings_24h: float = 0.0
|
earnings_24h: float = 0.0
|
||||||
created_at: datetime = field(default_factory=datetime.now(datetime.UTC))
|
created_at: datetime = field(default_factory=datetime.now(timezone.utc))
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -55,7 +55,7 @@ class JobAssignment:
|
|||||||
pool_id: str
|
pool_id: str
|
||||||
model: str
|
model: str
|
||||||
status: str = "assigned"
|
status: str = "assigned"
|
||||||
assigned_at: datetime = field(default_factory=datetime.now(datetime.UTC))
|
assigned_at: datetime = field(default_factory=datetime.now(timezone.utc))
|
||||||
deadline: Optional[datetime] = None
|
deadline: Optional[datetime] = None
|
||||||
completed_at: Optional[datetime] = None
|
completed_at: Optional[datetime] = None
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ class MinerRegistry:
|
|||||||
miner.current_jobs = current_jobs
|
miner.current_jobs = current_jobs
|
||||||
miner.gpu_utilization = gpu_utilization
|
miner.gpu_utilization = gpu_utilization
|
||||||
miner.memory_used_gb = memory_used_gb
|
miner.memory_used_gb = memory_used_gb
|
||||||
miner.last_heartbeat = datetime.now(datetime.UTC)
|
miner.last_heartbeat = datetime.now(timezone.utc)
|
||||||
|
|
||||||
async def update_capabilities(self, miner_id: str, capabilities: List[str]):
|
async def update_capabilities(self, miner_id: str, capabilities: List[str]):
|
||||||
"""Update miner capabilities."""
|
"""Update miner capabilities."""
|
||||||
@@ -271,7 +271,7 @@ class MinerRegistry:
|
|||||||
if job_id in self._jobs:
|
if job_id in self._jobs:
|
||||||
job = self._jobs[job_id]
|
job = self._jobs[job_id]
|
||||||
job.status = status
|
job.status = status
|
||||||
job.completed_at = datetime.now(datetime.UTC)
|
job.completed_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
if miner_id in self._miners:
|
if miner_id in self._miners:
|
||||||
miner = self._miners[miner_id]
|
miner = self._miners[miner_id]
|
||||||
@@ -314,7 +314,7 @@ class MinerRegistry:
|
|||||||
# Update job
|
# Update job
|
||||||
job.miner_id = new_miner_id
|
job.miner_id = new_miner_id
|
||||||
job.status = "assigned"
|
job.status = "assigned"
|
||||||
job.assigned_at = datetime.now(datetime.UTC)
|
job.assigned_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
# Update new miner
|
# Update new miner
|
||||||
if new_miner_id in self._miners:
|
if new_miner_id in self._miners:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""Health check routes for Pool Hub"""
|
"""Health check routes for Pool Hub"""
|
||||||
|
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
|
|
||||||
router = APIRouter(tags=["health"])
|
router = APIRouter(tags=["health"])
|
||||||
@@ -13,7 +13,7 @@ async def health_check():
|
|||||||
return {
|
return {
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"service": "pool-hub",
|
"service": "pool-hub",
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ async def readiness_check():
|
|||||||
return {
|
return {
|
||||||
"ready": all_ready,
|
"ready": all_ready,
|
||||||
"checks": checks,
|
"checks": checks,
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, Depends, Query
|
from fastapi import APIRouter, HTTPException, Depends, Query
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from ..registry import MinerRegistry
|
from ..registry import MinerRegistry
|
||||||
@@ -86,7 +86,7 @@ async def assign_job(
|
|||||||
job_id=job.job_id,
|
job_id=job.job_id,
|
||||||
miner_id=best_miner.miner_id,
|
miner_id=best_miner.miner_id,
|
||||||
pool_id=best_miner.pool_id,
|
pool_id=best_miner.pool_id,
|
||||||
assigned_at=datetime.now(datetime.UTC),
|
assigned_at=datetime.now(timezone.utc),
|
||||||
deadline=job.deadline
|
deadline=job.deadline
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from typing import List, Dict, Any, Optional
|
from typing import List, Dict, Any, Optional
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
import math
|
import math
|
||||||
|
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ class ScoringEngine:
|
|||||||
success_rate = 100.0 # New miners start with perfect score
|
success_rate = 100.0 # New miners start with perfect score
|
||||||
|
|
||||||
# Heartbeat freshness penalty
|
# Heartbeat freshness penalty
|
||||||
heartbeat_age = (datetime.now(datetime.UTC) - miner.last_heartbeat).total_seconds()
|
heartbeat_age = (datetime.now(timezone.utc) - miner.last_heartbeat).total_seconds()
|
||||||
if heartbeat_age > 300: # 5 minutes
|
if heartbeat_age > 300: # 5 minutes
|
||||||
freshness_penalty = min(20, heartbeat_age / 60)
|
freshness_penalty = min(20, heartbeat_age / 60)
|
||||||
else:
|
else:
|
||||||
@@ -137,7 +137,7 @@ class ScoringEngine:
|
|||||||
weight_total = 0
|
weight_total = 0
|
||||||
|
|
||||||
for record in history:
|
for record in history:
|
||||||
age_days = (datetime.now(datetime.UTC) - record["timestamp"]).days
|
age_days = (datetime.now(timezone.utc) - record["timestamp"]).days
|
||||||
weight = math.exp(-age_days / self.DECAY_HALF_LIFE_DAYS)
|
weight = math.exp(-age_days / self.DECAY_HALF_LIFE_DAYS)
|
||||||
|
|
||||||
if record["success"]:
|
if record["success"]:
|
||||||
@@ -153,7 +153,7 @@ class ScoringEngine:
|
|||||||
|
|
||||||
def _get_hours_active(self, miner) -> float:
|
def _get_hours_active(self, miner) -> float:
|
||||||
"""Get hours since miner registered."""
|
"""Get hours since miner registered."""
|
||||||
delta = datetime.now(datetime.UTC) - miner.registered_at
|
delta = datetime.now(timezone.utc) - miner.registered_at
|
||||||
return max(1, delta.total_seconds() / 3600)
|
return max(1, delta.total_seconds() / 3600)
|
||||||
|
|
||||||
def _parse_memory(self, memory_str: str) -> float:
|
def _parse_memory(self, memory_str: str) -> float:
|
||||||
@@ -204,7 +204,7 @@ class ScoringEngine:
|
|||||||
self._history[miner_id] = []
|
self._history[miner_id] = []
|
||||||
|
|
||||||
self._history[miner_id].append({
|
self._history[miner_id].append({
|
||||||
"timestamp": datetime.now(datetime.UTC),
|
"timestamp": datetime.now(timezone.utc),
|
||||||
"success": True,
|
"success": True,
|
||||||
"metrics": metrics or {}
|
"metrics": metrics or {}
|
||||||
})
|
})
|
||||||
@@ -219,7 +219,7 @@ class ScoringEngine:
|
|||||||
self._history[miner_id] = []
|
self._history[miner_id] = []
|
||||||
|
|
||||||
self._history[miner_id].append({
|
self._history[miner_id].append({
|
||||||
"timestamp": datetime.now(datetime.UTC),
|
"timestamp": datetime.now(timezone.utc),
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": error
|
"error": error
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ SLA and Billing API Endpoints for Pool-Hub
|
|||||||
Provides endpoints for SLA metrics, capacity planning, and billing integration.
|
Provides endpoints for SLA metrics, capacity planning, and billing integration.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from typing import Dict, List, Optional, Any
|
from typing import Dict, List, Optional, Any
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ async def get_capacity_snapshots(
|
|||||||
):
|
):
|
||||||
"""Get capacity planning snapshots"""
|
"""Get capacity planning snapshots"""
|
||||||
try:
|
try:
|
||||||
cutoff = datetime.now(datetime.UTC) - timedelta(hours=hours)
|
cutoff = datetime.now(timezone.utc) - timedelta(hours=hours)
|
||||||
stmt = (
|
stmt = (
|
||||||
db.query(CapacitySnapshot)
|
db.query(CapacitySnapshot)
|
||||||
.filter(CapacitySnapshot.timestamp >= cutoff)
|
.filter(CapacitySnapshot.timestamp >= cutoff)
|
||||||
@@ -236,7 +236,7 @@ async def configure_capacity_alerts(
|
|||||||
return {
|
return {
|
||||||
"status": "configured",
|
"status": "configured",
|
||||||
"alert_config": alert_config,
|
"alert_config": alert_config,
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error configuring capacity alerts: {e}")
|
logger.error(f"Error configuring capacity alerts: {e}")
|
||||||
@@ -270,7 +270,7 @@ async def sync_billing_usage(
|
|||||||
try:
|
try:
|
||||||
if request.miner_id:
|
if request.miner_id:
|
||||||
# Sync specific miner
|
# Sync specific miner
|
||||||
end_date = datetime.now(datetime.UTC)
|
end_date = datetime.now(timezone.utc)
|
||||||
start_date = end_date - timedelta(hours=request.hours_back)
|
start_date = end_date - timedelta(hours=request.hours_back)
|
||||||
result = await billing_integration.sync_miner_usage(
|
result = await billing_integration.sync_miner_usage(
|
||||||
miner_id=request.miner_id, start_date=start_date, end_date=end_date
|
miner_id=request.miner_id, start_date=start_date, end_date=end_date
|
||||||
@@ -350,7 +350,7 @@ async def get_sla_status(db: Session = Depends(get_db)):
|
|||||||
"status": status,
|
"status": status,
|
||||||
"active_violations": len(active_violations),
|
"active_violations": len(active_violations),
|
||||||
"recent_metrics_count": len(recent_metrics),
|
"recent_metrics_count": len(recent_metrics),
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting SLA status: {e}")
|
logger.error(f"Error getting SLA status: {e}")
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class Miner(Base):
|
|||||||
miner_id: Mapped[str] = mapped_column(String(64), primary_key=True)
|
miner_id: Mapped[str] = mapped_column(String(64), primary_key=True)
|
||||||
api_key_hash: Mapped[str] = mapped_column(String(128), nullable=False)
|
api_key_hash: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||||
created_at: Mapped[dt.datetime] = mapped_column(
|
created_at: Mapped[dt.datetime] = mapped_column(
|
||||||
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC)
|
DateTime(timezone=True), default=dt.datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
last_seen_at: Mapped[Optional[dt.datetime]] = mapped_column(DateTime(timezone=True))
|
last_seen_at: Mapped[Optional[dt.datetime]] = mapped_column(DateTime(timezone=True))
|
||||||
addr: Mapped[str] = mapped_column(String(256))
|
addr: Mapped[str] = mapped_column(String(256))
|
||||||
@@ -78,7 +78,7 @@ class MinerStatus(Base):
|
|||||||
uptime_pct: Mapped[Optional[float]] = mapped_column(Float) # SLA metric
|
uptime_pct: Mapped[Optional[float]] = mapped_column(Float) # SLA metric
|
||||||
last_heartbeat_at: Mapped[Optional[dt.datetime]] = mapped_column(DateTime(timezone=True))
|
last_heartbeat_at: Mapped[Optional[dt.datetime]] = mapped_column(DateTime(timezone=True))
|
||||||
updated_at: Mapped[dt.datetime] = mapped_column(
|
updated_at: Mapped[dt.datetime] = mapped_column(
|
||||||
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC), onupdate=dt.datetime.now(datetime.UTC)
|
DateTime(timezone=True), default=dt.datetime.now(timezone.utc), onupdate=dt.datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
miner: Mapped[Miner] = relationship(back_populates="status")
|
miner: Mapped[Miner] = relationship(back_populates="status")
|
||||||
@@ -95,7 +95,7 @@ class MatchRequest(Base):
|
|||||||
hints: Mapped[Dict[str, object]] = mapped_column(JSON, default=dict)
|
hints: Mapped[Dict[str, object]] = mapped_column(JSON, default=dict)
|
||||||
top_k: Mapped[int] = mapped_column(Integer, default=1)
|
top_k: Mapped[int] = mapped_column(Integer, default=1)
|
||||||
created_at: Mapped[dt.datetime] = mapped_column(
|
created_at: Mapped[dt.datetime] = mapped_column(
|
||||||
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC)
|
DateTime(timezone=True), default=dt.datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
results: Mapped[List["MatchResult"]] = relationship(
|
results: Mapped[List["MatchResult"]] = relationship(
|
||||||
@@ -119,7 +119,7 @@ class MatchResult(Base):
|
|||||||
price: Mapped[Optional[float]] = mapped_column(Float)
|
price: Mapped[Optional[float]] = mapped_column(Float)
|
||||||
|
|
||||||
created_at: Mapped[dt.datetime] = mapped_column(
|
created_at: Mapped[dt.datetime] = mapped_column(
|
||||||
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC)
|
DateTime(timezone=True), default=dt.datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
request: Mapped[MatchRequest] = relationship(back_populates="results")
|
request: Mapped[MatchRequest] = relationship(back_populates="results")
|
||||||
@@ -140,7 +140,7 @@ class Feedback(Base):
|
|||||||
fail_code: Mapped[Optional[str]] = mapped_column(String(64))
|
fail_code: Mapped[Optional[str]] = mapped_column(String(64))
|
||||||
tokens_spent: Mapped[Optional[float]] = mapped_column(Float)
|
tokens_spent: Mapped[Optional[float]] = mapped_column(Float)
|
||||||
created_at: Mapped[dt.datetime] = mapped_column(
|
created_at: Mapped[dt.datetime] = mapped_column(
|
||||||
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC)
|
DateTime(timezone=True), default=dt.datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
miner: Mapped[Miner] = relationship(back_populates="feedback")
|
miner: Mapped[Miner] = relationship(back_populates="feedback")
|
||||||
@@ -164,10 +164,10 @@ class ServiceConfig(Base):
|
|||||||
capabilities: Mapped[List[str]] = mapped_column(JSON, default=list)
|
capabilities: Mapped[List[str]] = mapped_column(JSON, default=list)
|
||||||
max_concurrent: Mapped[int] = mapped_column(Integer, default=1)
|
max_concurrent: Mapped[int] = mapped_column(Integer, default=1)
|
||||||
created_at: Mapped[dt.datetime] = mapped_column(
|
created_at: Mapped[dt.datetime] = mapped_column(
|
||||||
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC)
|
DateTime(timezone=True), default=dt.datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
updated_at: Mapped[dt.datetime] = mapped_column(
|
updated_at: Mapped[dt.datetime] = mapped_column(
|
||||||
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC), onupdate=dt.datetime.now(datetime.UTC)
|
DateTime(timezone=True), default=dt.datetime.now(timezone.utc), onupdate=dt.datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add unique constraint for miner_id + service_type
|
# Add unique constraint for miner_id + service_type
|
||||||
@@ -192,7 +192,7 @@ class SLAMetric(Base):
|
|||||||
threshold: Mapped[float] = mapped_column(Float, nullable=False)
|
threshold: Mapped[float] = mapped_column(Float, nullable=False)
|
||||||
is_violation: Mapped[bool] = mapped_column(Boolean, default=False)
|
is_violation: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||||
timestamp: Mapped[dt.datetime] = mapped_column(
|
timestamp: Mapped[dt.datetime] = mapped_column(
|
||||||
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC)
|
DateTime(timezone=True), default=dt.datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
meta_data: Mapped[Dict[str, str]] = mapped_column(JSON, default=dict)
|
meta_data: Mapped[Dict[str, str]] = mapped_column(JSON, default=dict)
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ class SLAViolation(Base):
|
|||||||
violation_duration_ms: Mapped[Optional[int]] = mapped_column(Integer)
|
violation_duration_ms: Mapped[Optional[int]] = mapped_column(Integer)
|
||||||
resolved_at: Mapped[Optional[dt.datetime]] = mapped_column(DateTime(timezone=True))
|
resolved_at: Mapped[Optional[dt.datetime]] = mapped_column(DateTime(timezone=True))
|
||||||
created_at: Mapped[dt.datetime] = mapped_column(
|
created_at: Mapped[dt.datetime] = mapped_column(
|
||||||
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC)
|
DateTime(timezone=True), default=dt.datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
meta_data: Mapped[Dict[str, str]] = mapped_column(JSON, default=dict)
|
meta_data: Mapped[Dict[str, str]] = mapped_column(JSON, default=dict)
|
||||||
|
|
||||||
@@ -241,6 +241,6 @@ class CapacitySnapshot(Base):
|
|||||||
recommended_scaling: Mapped[str] = mapped_column(String(32), nullable=False)
|
recommended_scaling: Mapped[str] = mapped_column(String(32), nullable=False)
|
||||||
scaling_reason: Mapped[str] = mapped_column(Text)
|
scaling_reason: Mapped[str] = mapped_column(Text)
|
||||||
timestamp: Mapped[dt.datetime] = mapped_column(
|
timestamp: Mapped[dt.datetime] = mapped_column(
|
||||||
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC)
|
DateTime(timezone=True), default=dt.datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
meta_data: Mapped[Dict[str, Any]] = mapped_column(JSON, default=dict)
|
meta_data: Mapped[Dict[str, Any]] = mapped_column(JSON, default=dict)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class FeedbackRepository:
|
|||||||
latency_ms=latency_ms,
|
latency_ms=latency_ms,
|
||||||
fail_code=fail_code,
|
fail_code=fail_code,
|
||||||
tokens_spent=tokens_spent,
|
tokens_spent=tokens_spent,
|
||||||
created_at=dt.datetime.now(datetime.UTC),
|
created_at=dt.datetime.now(timezone.utc),
|
||||||
)
|
)
|
||||||
self._session.add(feedback)
|
self._session.add(feedback)
|
||||||
await self._session.flush()
|
await self._session.flush()
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class MatchRepository:
|
|||||||
requirements=requirements,
|
requirements=requirements,
|
||||||
hints=hints or {},
|
hints=hints or {},
|
||||||
top_k=top_k,
|
top_k=top_k,
|
||||||
created_at=dt.datetime.now(datetime.UTC),
|
created_at=dt.datetime.now(timezone.utc),
|
||||||
)
|
)
|
||||||
self._session.add(request)
|
self._session.add(request)
|
||||||
await self._session.flush()
|
await self._session.flush()
|
||||||
@@ -58,7 +58,7 @@ class MatchRepository:
|
|||||||
publish: bool = True,
|
publish: bool = True,
|
||||||
) -> List[MatchResult]:
|
) -> List[MatchResult]:
|
||||||
results: List[MatchResult] = []
|
results: List[MatchResult] = []
|
||||||
created_at = dt.datetime.now(datetime.UTC)
|
created_at = dt.datetime.now(timezone.utc)
|
||||||
for candidate in candidates:
|
for candidate in candidates:
|
||||||
result = MatchResult(
|
result = MatchResult(
|
||||||
request_id=request_id,
|
request_id=request_id,
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class MinerRepository:
|
|||||||
miner.capabilities = capabilities
|
miner.capabilities = capabilities
|
||||||
miner.region = region
|
miner.region = region
|
||||||
|
|
||||||
miner.last_seen_at = dt.datetime.now(datetime.UTC)
|
miner.last_seen_at = dt.datetime.now(timezone.utc)
|
||||||
|
|
||||||
await self._session.flush()
|
await self._session.flush()
|
||||||
await self._sync_miner_to_redis(miner_id)
|
await self._sync_miner_to_redis(miner_id)
|
||||||
@@ -97,7 +97,7 @@ class MinerRepository:
|
|||||||
"avg_latency_ms": avg_latency_ms,
|
"avg_latency_ms": avg_latency_ms,
|
||||||
"temp_c": temp_c,
|
"temp_c": temp_c,
|
||||||
"mem_free_gb": mem_free_gb,
|
"mem_free_gb": mem_free_gb,
|
||||||
"updated_at": dt.datetime.now(datetime.UTC),
|
"updated_at": dt.datetime.now(timezone.utc),
|
||||||
}.items()
|
}.items()
|
||||||
if v is not None
|
if v is not None
|
||||||
}
|
}
|
||||||
@@ -107,7 +107,7 @@ class MinerRepository:
|
|||||||
|
|
||||||
miner = await self._session.get(Miner, miner_id)
|
miner = await self._session.get(Miner, miner_id)
|
||||||
if miner:
|
if miner:
|
||||||
miner.last_seen_at = dt.datetime.now(datetime.UTC)
|
miner.last_seen_at = dt.datetime.now(timezone.utc)
|
||||||
await self._session.flush()
|
await self._session.flush()
|
||||||
await self._sync_miner_to_redis(miner_id)
|
await self._sync_miner_to_redis(miner_id)
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ class MinerRepository:
|
|||||||
miner = await self._session.get(Miner, miner_id)
|
miner = await self._session.get(Miner, miner_id)
|
||||||
if miner is None:
|
if miner is None:
|
||||||
return
|
return
|
||||||
miner.last_seen_at = dt.datetime.now(datetime.UTC)
|
miner.last_seen_at = dt.datetime.now(timezone.utc)
|
||||||
await self._session.flush()
|
await self._session.flush()
|
||||||
await self._sync_miner_to_redis(miner_id)
|
await self._sync_miner_to_redis(miner_id)
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Integrates pool-hub usage data with coordinator-api's billing system.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Dict, List, Optional, Any
|
from typing import Dict, List, Optional, Any
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ class BillingIntegration:
|
|||||||
"unit_price": float(unit_price),
|
"unit_price": float(unit_price),
|
||||||
"total_amount": float(total_cost),
|
"total_amount": float(total_cost),
|
||||||
"currency": "USD",
|
"currency": "USD",
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"metadata": metadata or {},
|
"metadata": metadata or {},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ class BillingIntegration:
|
|||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Sync usage data for all miners to coordinator-api billing"""
|
"""Sync usage data for all miners to coordinator-api billing"""
|
||||||
|
|
||||||
end_date = datetime.now(datetime.UTC)
|
end_date = datetime.now(timezone.utc)
|
||||||
start_date = end_date - timedelta(hours=hours_back)
|
start_date = end_date - timedelta(hours=hours_back)
|
||||||
|
|
||||||
# Get all miners
|
# Get all miners
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Collects and tracks SLA metrics for miners including uptime, response time, job
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Dict, List, Optional, Any
|
from typing import Dict, List, Optional, Any
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ class SLACollector:
|
|||||||
metric_value=metric_value,
|
metric_value=metric_value,
|
||||||
threshold=threshold,
|
threshold=threshold,
|
||||||
is_violation=is_violation,
|
is_violation=is_violation,
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
meta_data=metadata or {},
|
meta_data=metadata or {},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ class SLACollector:
|
|||||||
# Calculate uptime based on last heartbeat
|
# Calculate uptime based on last heartbeat
|
||||||
if miner_status.last_heartbeat_at:
|
if miner_status.last_heartbeat_at:
|
||||||
time_since_heartbeat = (
|
time_since_heartbeat = (
|
||||||
datetime.now(datetime.UTC) - miner_status.last_heartbeat_at
|
datetime.now(timezone.utc) - miner_status.last_heartbeat_at
|
||||||
).total_seconds()
|
).total_seconds()
|
||||||
|
|
||||||
# Consider miner down if no heartbeat for 5 minutes
|
# Consider miner down if no heartbeat for 5 minutes
|
||||||
@@ -153,7 +153,7 @@ class SLACollector:
|
|||||||
stmt = (
|
stmt = (
|
||||||
select(Feedback)
|
select(Feedback)
|
||||||
.where(Feedback.miner_id == miner_id)
|
.where(Feedback.miner_id == miner_id)
|
||||||
.where(Feedback.created_at >= datetime.now(datetime.UTC) - timedelta(days=7))
|
.where(Feedback.created_at >= datetime.now(timezone.utc) - timedelta(days=7))
|
||||||
.order_by(Feedback.created_at.desc())
|
.order_by(Feedback.created_at.desc())
|
||||||
.limit(100)
|
.limit(100)
|
||||||
)
|
)
|
||||||
@@ -206,7 +206,7 @@ class SLACollector:
|
|||||||
forecast_capacity=total_miners, # Would be calculated from forecasting
|
forecast_capacity=total_miners, # Would be calculated from forecasting
|
||||||
recommended_scaling="stable",
|
recommended_scaling="stable",
|
||||||
scaling_reason="Capacity within normal range",
|
scaling_reason="Capacity within normal range",
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
meta_data={"method": "real_time_collection"},
|
meta_data={"method": "real_time_collection"},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -266,7 +266,7 @@ class SLACollector:
|
|||||||
stmt = (
|
stmt = (
|
||||||
select(func.count(SLAViolation.id))
|
select(func.count(SLAViolation.id))
|
||||||
.where(SLAViolation.resolved_at.is_(None))
|
.where(SLAViolation.resolved_at.is_(None))
|
||||||
.where(SLAViolation.created_at >= datetime.now(datetime.UTC) - timedelta(hours=1))
|
.where(SLAViolation.created_at >= datetime.now(timezone.utc) - timedelta(hours=1))
|
||||||
)
|
)
|
||||||
results["violations_detected"] = self.db.execute(stmt).scalar() or 0
|
results["violations_detected"] = self.db.execute(stmt).scalar() or 0
|
||||||
|
|
||||||
@@ -282,7 +282,7 @@ class SLACollector:
|
|||||||
) -> List[SLAMetric]:
|
) -> List[SLAMetric]:
|
||||||
"""Get SLA metrics for a miner or all miners"""
|
"""Get SLA metrics for a miner or all miners"""
|
||||||
|
|
||||||
cutoff = datetime.now(datetime.UTC) - timedelta(hours=hours)
|
cutoff = datetime.now(timezone.utc) - timedelta(hours=hours)
|
||||||
|
|
||||||
stmt = select(SLAMetric).where(SLAMetric.timestamp >= cutoff)
|
stmt = select(SLAMetric).where(SLAMetric.timestamp >= cutoff)
|
||||||
|
|
||||||
@@ -349,7 +349,7 @@ class SLACollector:
|
|||||||
metric_value=metric_value,
|
metric_value=metric_value,
|
||||||
threshold=threshold,
|
threshold=threshold,
|
||||||
violation_duration_ms=None, # Will be updated when resolved
|
violation_duration_ms=None, # Will be updated when resolved
|
||||||
created_at=datetime.now(datetime.UTC),
|
created_at=datetime.now(timezone.utc),
|
||||||
meta_data=metadata or {},
|
meta_data=metadata or {},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Tests for Billing Integration Service
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import pytest
|
import pytest
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from unittest.mock import AsyncMock, patch
|
from unittest.mock import AsyncMock, patch
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
@@ -84,7 +84,7 @@ async def test_record_usage_with_fallback_pricing(billing_integration: BillingIn
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_sync_miner_usage(billing_integration: BillingIntegration, sample_miner: Miner):
|
async def test_sync_miner_usage(billing_integration: BillingIntegration, sample_miner: Miner):
|
||||||
"""Test syncing usage for a specific miner"""
|
"""Test syncing usage for a specific miner"""
|
||||||
end_date = datetime.now(datetime.UTC)
|
end_date = datetime.now(timezone.utc)
|
||||||
start_date = end_date - timedelta(hours=24)
|
start_date = end_date - timedelta(hours=24)
|
||||||
|
|
||||||
with patch("poolhub.services.billing_integration.httpx.AsyncClient") as mock_client:
|
with patch("poolhub.services.billing_integration.httpx.AsyncClient") as mock_client:
|
||||||
@@ -123,7 +123,7 @@ async def test_sync_all_miners_usage(billing_integration: BillingIntegration, sa
|
|||||||
|
|
||||||
def test_collect_miner_usage(billing_integration: BillingIntegration, sample_miner: Miner):
|
def test_collect_miner_usage(billing_integration: BillingIntegration, sample_miner: Miner):
|
||||||
"""Test collecting usage data for a miner"""
|
"""Test collecting usage data for a miner"""
|
||||||
end_date = datetime.now(datetime.UTC)
|
end_date = datetime.now(timezone.utc)
|
||||||
start_date = end_date - timedelta(hours=24)
|
start_date = end_date - timedelta(hours=24)
|
||||||
|
|
||||||
usage_data = billing_integration.db.run_sync(
|
usage_data = billing_integration.db.run_sync(
|
||||||
@@ -169,7 +169,7 @@ async def test_trigger_invoice_generation(billing_integration: BillingIntegratio
|
|||||||
|
|
||||||
mock_client.return_value.__aenter__.return_value.post = AsyncMock(return_value=mock_response)
|
mock_client.return_value.__aenter__.return_value.post = AsyncMock(return_value=mock_response)
|
||||||
|
|
||||||
end_date = datetime.now(datetime.UTC)
|
end_date = datetime.now(timezone.utc)
|
||||||
start_date = end_date - timedelta(days=30)
|
start_date = end_date - timedelta(days=30)
|
||||||
|
|
||||||
result = await billing_integration.trigger_invoice_generation(
|
result = await billing_integration.trigger_invoice_generation(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Tests the integration between pool-hub and coordinator-api's billing system.
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ def test_end_to_end_sla_to_billing_workflow(
|
|||||||
assert len(metrics) > 0
|
assert len(metrics) > 0
|
||||||
|
|
||||||
# Step 3: Collect usage data for billing
|
# Step 3: Collect usage data for billing
|
||||||
end_date = datetime.now(datetime.UTC)
|
end_date = datetime.now(timezone.utc)
|
||||||
start_date = end_date - timedelta(hours=1)
|
start_date = end_date - timedelta(hours=1)
|
||||||
usage_data = sla_collector.db.run_sync(
|
usage_data = sla_collector.db.run_sync(
|
||||||
lambda sess: billing_integration._collect_miner_usage(
|
lambda sess: billing_integration._collect_miner_usage(
|
||||||
@@ -126,7 +126,7 @@ def test_sla_violation_billing_correlation(
|
|||||||
assert len(violations) > 0
|
assert len(violations) > 0
|
||||||
|
|
||||||
# Usage should still be recorded even with violations
|
# Usage should still be recorded even with violations
|
||||||
end_date = datetime.now(datetime.UTC)
|
end_date = datetime.now(timezone.utc)
|
||||||
start_date = end_date - timedelta(hours=1)
|
start_date = end_date - timedelta(hours=1)
|
||||||
usage_data = sla_collector.db.run_sync(
|
usage_data = sla_collector.db.run_sync(
|
||||||
lambda sess: billing_integration._collect_miner_usage(
|
lambda sess: billing_integration._collect_miner_usage(
|
||||||
@@ -172,7 +172,7 @@ def test_billing_sync_with_coordinator_api(
|
|||||||
"""Test billing sync with coordinator-api (mocked)"""
|
"""Test billing sync with coordinator-api (mocked)"""
|
||||||
from unittest.mock import AsyncMock, patch
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
end_date = datetime.now(datetime.UTC)
|
end_date = datetime.now(timezone.utc)
|
||||||
start_date = end_date - timedelta(hours=1)
|
start_date = end_date - timedelta(hours=1)
|
||||||
|
|
||||||
with patch("poolhub.services.billing_integration.httpx.AsyncClient") as mock_client:
|
with patch("poolhub.services.billing_integration.httpx.AsyncClient") as mock_client:
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Tests for SLA Collector Service
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import pytest
|
import pytest
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ def sample_miner_status(db_session: Session, sample_miner: Miner) -> MinerStatus
|
|||||||
avg_latency_ms=150,
|
avg_latency_ms=150,
|
||||||
temp_c=65,
|
temp_c=65,
|
||||||
mem_free_gb=32.0,
|
mem_free_gb=32.0,
|
||||||
last_heartbeat_at=datetime.now(datetime.UTC),
|
last_heartbeat_at=datetime.now(timezone.utc),
|
||||||
)
|
)
|
||||||
db_session.add(status)
|
db_session.add(status)
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Tests for SLA API Endpoints
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import pytest
|
import pytest
|
||||||
from datetime import datetime, UTC, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
@@ -65,7 +65,7 @@ def sample_sla_metric(db_session: Session, sample_miner: Miner) -> SLAMetric:
|
|||||||
metric_value=98.5,
|
metric_value=98.5,
|
||||||
threshold=95.0,
|
threshold=95.0,
|
||||||
is_violation=False,
|
is_violation=False,
|
||||||
timestamp=datetime.now(datetime.UTC),
|
timestamp=datetime.now(timezone.utc),
|
||||||
metadata={"test": "true"},
|
metadata={"test": "true"},
|
||||||
)
|
)
|
||||||
db_session.add(metric)
|
db_session.add(metric)
|
||||||
@@ -191,7 +191,7 @@ def test_record_usage(test_client: TestClient):
|
|||||||
|
|
||||||
def test_generate_invoice(test_client: TestClient):
|
def test_generate_invoice(test_client: TestClient):
|
||||||
"""Test triggering invoice generation"""
|
"""Test triggering invoice generation"""
|
||||||
end_date = datetime.now(datetime.UTC)
|
end_date = datetime.now(timezone.utc)
|
||||||
start_date = end_date - timedelta(days=30)
|
start_date = end_date - timedelta(days=30)
|
||||||
|
|
||||||
request_data = {
|
request_data = {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Handles order matching, trade execution, and settlement
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
from collections import defaultdict, deque
|
from collections import defaultdict, deque
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any, List, Optional, Tuple
|
from typing import Dict, Any, List, Optional, Tuple
|
||||||
from fastapi import FastAPI, HTTPException
|
from fastapi import FastAPI, HTTPException
|
||||||
@@ -70,7 +70,7 @@ async def root():
|
|||||||
return {
|
return {
|
||||||
"service": "AITBC Trading Engine",
|
"service": "AITBC Trading Engine",
|
||||||
"status": "running",
|
"status": "running",
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ async def submit_order(order: Order):
|
|||||||
"volume_24h": 0.0,
|
"volume_24h": 0.0,
|
||||||
"high_24h": None,
|
"high_24h": None,
|
||||||
"low_24h": None,
|
"low_24h": None,
|
||||||
"created_at": datetime.now(datetime.UTC).isoformat()
|
"created_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Store order
|
# Store order
|
||||||
@@ -185,7 +185,7 @@ async def get_order_book(symbol: str, depth: int = 10):
|
|||||||
"volume_24h": book["volume_24h"],
|
"volume_24h": book["volume_24h"],
|
||||||
"high_24h": book["high_24h"],
|
"high_24h": book["high_24h"],
|
||||||
"low_24h": book["low_24h"],
|
"low_24h": book["low_24h"],
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat()
|
"timestamp": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/trades")
|
@app.get("/api/v1/trades")
|
||||||
@@ -216,7 +216,7 @@ async def get_ticker(symbol: str):
|
|||||||
trades_24h = [t for t in trades.values()
|
trades_24h = [t for t in trades.values()
|
||||||
if t["symbol"] == symbol and
|
if t["symbol"] == symbol and
|
||||||
datetime.fromisoformat(t["timestamp"]) >
|
datetime.fromisoformat(t["timestamp"]) >
|
||||||
datetime.now(datetime.UTC) - timedelta(hours=24)]
|
datetime.now(timezone.utc) - timedelta(hours=24)]
|
||||||
|
|
||||||
if trades_24h:
|
if trades_24h:
|
||||||
prices = [t["price"] for t in trades_24h]
|
prices = [t["price"] for t in trades_24h]
|
||||||
@@ -276,7 +276,7 @@ async def cancel_order(order_id: str):
|
|||||||
|
|
||||||
# Update order status
|
# Update order status
|
||||||
order["status"] = "cancelled"
|
order["status"] = "cancelled"
|
||||||
order["cancelled_at"] = datetime.now(datetime.UTC).isoformat()
|
order["cancelled_at"] = datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
logger.info(f"Order cancelled: {order_id}")
|
logger.info(f"Order cancelled: {order_id}")
|
||||||
|
|
||||||
@@ -295,7 +295,7 @@ async def get_market_data():
|
|||||||
trades_24h = [t for t in trades.values()
|
trades_24h = [t for t in trades.values()
|
||||||
if t["symbol"] == symbol and
|
if t["symbol"] == symbol and
|
||||||
datetime.fromisoformat(t["timestamp"]) >
|
datetime.fromisoformat(t["timestamp"]) >
|
||||||
datetime.now(datetime.UTC) - timedelta(hours=24)]
|
datetime.now(timezone.utc) - timedelta(hours=24)]
|
||||||
|
|
||||||
market_summary[symbol] = {
|
market_summary[symbol] = {
|
||||||
"last_price": book["last_price"],
|
"last_price": book["last_price"],
|
||||||
@@ -310,7 +310,7 @@ async def get_market_data():
|
|||||||
return {
|
return {
|
||||||
"market_data": market_summary,
|
"market_data": market_summary,
|
||||||
"total_symbols": len(market_summary),
|
"total_symbols": len(market_summary),
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/engine/stats")
|
@app.get("/api/v1/engine/stats")
|
||||||
@@ -338,7 +338,7 @@ async def get_engine_stats():
|
|||||||
"active_order_books": len(order_books),
|
"active_order_books": len(order_books),
|
||||||
"uptime": "running"
|
"uptime": "running"
|
||||||
},
|
},
|
||||||
"generated_at": datetime.now(datetime.UTC).isoformat()
|
"generated_at": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Core trading engine logic
|
# Core trading engine logic
|
||||||
@@ -457,7 +457,7 @@ async def execute_trade(order1: Dict, order2: Dict, price: float) -> Optional[Di
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
# Create trade record
|
# Create trade record
|
||||||
trade_id = f"trade_{int(datetime.now(datetime.UTC).timestamp())}_{len(trades)}"
|
trade_id = f"trade_{int(datetime.now(timezone.utc).timestamp())}_{len(trades)}"
|
||||||
|
|
||||||
trade = {
|
trade = {
|
||||||
"trade_id": trade_id,
|
"trade_id": trade_id,
|
||||||
@@ -466,7 +466,7 @@ async def execute_trade(order1: Dict, order2: Dict, price: float) -> Optional[Di
|
|||||||
"sell_order_id": order2["order_id"] if order2["side"] == "sell" else order1["order_id"],
|
"sell_order_id": order2["order_id"] if order2["side"] == "sell" else order1["order_id"],
|
||||||
"quantity": trade_quantity,
|
"quantity": trade_quantity,
|
||||||
"price": price,
|
"price": price,
|
||||||
"timestamp": datetime.now(datetime.UTC).isoformat()
|
"timestamp": datetime.now(timezone.utc).isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
trades[trade_id] = trade
|
trades[trade_id] = trade
|
||||||
@@ -524,7 +524,7 @@ def update_market_data(symbol: str, trades_executed: List[Dict]):
|
|||||||
trades_24h = [t for t in trades.values()
|
trades_24h = [t for t in trades.values()
|
||||||
if t["symbol"] == symbol and
|
if t["symbol"] == symbol and
|
||||||
datetime.fromisoformat(t["timestamp"]) >
|
datetime.fromisoformat(t["timestamp"]) >
|
||||||
datetime.now(datetime.UTC) - timedelta(hours=24)]
|
datetime.now(timezone.utc) - timedelta(hours=24)]
|
||||||
|
|
||||||
if trades_24h:
|
if trades_24h:
|
||||||
prices = [t["price"] for t in trades_24h]
|
prices = [t["price"] for t in trades_24h]
|
||||||
@@ -548,7 +548,7 @@ async def simulate_market_activity():
|
|||||||
side = random.choice(["buy", "sell"])
|
side = random.choice(["buy", "sell"])
|
||||||
quantity = random.uniform(10, 1000)
|
quantity = random.uniform(10, 1000)
|
||||||
|
|
||||||
order_id = f"sim_order_{int(datetime.now(datetime.UTC).timestamp())}"
|
order_id = f"sim_order_{int(datetime.now(timezone.utc).timestamp())}"
|
||||||
order = Order(
|
order = Order(
|
||||||
order_id=order_id,
|
order_id=order_id,
|
||||||
symbol=symbol,
|
symbol=symbol,
|
||||||
@@ -556,7 +556,7 @@ async def simulate_market_activity():
|
|||||||
type="market",
|
type="market",
|
||||||
quantity=quantity,
|
quantity=quantity,
|
||||||
user_id="sim_user",
|
user_id="sim_user",
|
||||||
timestamp=datetime.now(datetime.UTC)
|
timestamp=datetime.now(timezone.utc)
|
||||||
)
|
)
|
||||||
|
|
||||||
order_data = {
|
order_data = {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user