Update Python version requirements and fix compatibility issues
- Bump minimum Python version from 3.11 to 3.13 across all apps - Add Python 3.11-3.13 test matrix to CLI workflow - Document Python 3.11+ requirement in .env.example - Fix Starlette Broadcast removal with in-process fallback implementation - Add _InProcessBroadcast class for tests when Starlette Broadcast is unavailable - Refactor API key validators to read live settings instead of cached values - Update database models with explicit
This commit is contained in:
@@ -199,7 +199,7 @@ export class AitbcClient {
|
||||
await this.request<void>("POST", "/v1/users/logout", options);
|
||||
}
|
||||
|
||||
private async request<T>(method: string, path: string, options: RequestOptions = {}): Promise<T> {
|
||||
async request<T>(method: string, path: string, options: RequestOptions = {}): Promise<T> {
|
||||
const response = await this.rawRequest(method, path, options);
|
||||
const text = await response.text();
|
||||
if (!response.ok) {
|
||||
@@ -208,7 +208,7 @@ export class AitbcClient {
|
||||
return text ? (JSON.parse(text) as T) : ({} as T);
|
||||
}
|
||||
|
||||
private async rawRequest(method: string, path: string, options: RequestOptions = {}): Promise<Response> {
|
||||
async rawRequest(method: string, path: string, options: RequestOptions = {}): Promise<Response> {
|
||||
const url = this.buildUrl(path, options.query);
|
||||
const headers = this.buildHeaders(options.headers);
|
||||
|
||||
|
||||
77
packages/js/aitbc-sdk/src/receipts.test.ts
Normal file
77
packages/js/aitbc-sdk/src/receipts.test.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { generateKeyPairSync, sign } from "crypto";
|
||||
import { ReceiptService } from "./receipts";
|
||||
import type { ReceiptSummary } from "./types";
|
||||
|
||||
class ThrowingClient {
|
||||
async request() {
|
||||
throw new Error("offline");
|
||||
}
|
||||
}
|
||||
|
||||
describe("ReceiptService signature verification", () => {
|
||||
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
|
||||
const publicKeyPem = publicKey.export({ type: "spki", format: "pem" }).toString();
|
||||
|
||||
const baseReceipt: ReceiptSummary = {
|
||||
receiptId: "r1",
|
||||
jobId: "job-123",
|
||||
miner: "miner-1",
|
||||
coordinator: "coord-1",
|
||||
issuedAt: new Date().toISOString(),
|
||||
status: "completed",
|
||||
payload: {
|
||||
job_id: "job-123",
|
||||
provider: "miner-1",
|
||||
client: "client-1",
|
||||
},
|
||||
};
|
||||
|
||||
const signPayload = (payload: Record<string, unknown>): string => {
|
||||
const message = Buffer.from(JSON.stringify(payload));
|
||||
return sign(null, message, privateKey).toString("base64");
|
||||
};
|
||||
|
||||
it("validates with provided PEM keys", async () => {
|
||||
const sig = signPayload(baseReceipt.payload!);
|
||||
const receipt: ReceiptSummary = {
|
||||
...baseReceipt,
|
||||
payload: {
|
||||
...baseReceipt.payload,
|
||||
minerSignature: sig,
|
||||
coordinatorSignature: sig,
|
||||
},
|
||||
};
|
||||
|
||||
const svc = new ReceiptService({
|
||||
client: new ThrowingClient() as any,
|
||||
minerPublicKeyPem: publicKeyPem,
|
||||
coordinatorPublicKeyPem: publicKeyPem,
|
||||
signatureAlgorithm: "ed25519",
|
||||
});
|
||||
|
||||
const result = await svc.validateReceipt(receipt);
|
||||
expect(result.valid).toBe(true);
|
||||
});
|
||||
|
||||
it("fails with bad signature", async () => {
|
||||
const receipt: ReceiptSummary = {
|
||||
...baseReceipt,
|
||||
payload: {
|
||||
...baseReceipt.payload,
|
||||
minerSignature: "bad",
|
||||
coordinatorSignature: "bad",
|
||||
},
|
||||
};
|
||||
|
||||
const svc = new ReceiptService({
|
||||
client: new ThrowingClient() as any,
|
||||
minerPublicKeyPem: publicKeyPem,
|
||||
coordinatorPublicKeyPem: publicKeyPem,
|
||||
signatureAlgorithm: "ed25519",
|
||||
});
|
||||
|
||||
const result = await svc.validateReceipt(receipt);
|
||||
expect(result.valid).toBe(false);
|
||||
});
|
||||
});
|
||||
209
packages/js/aitbc-sdk/src/receipts.ts
Normal file
209
packages/js/aitbc-sdk/src/receipts.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
import { verify as verifySignature, createPublicKey } from "crypto";
|
||||
import type { ReceiptListResponse, ReceiptSummary, RequestOptions } from "./types";
|
||||
import { AitbcClient } from "./client";
|
||||
|
||||
export interface PaginatedReceipts {
|
||||
items: ReceiptSummary[];
|
||||
nextCursor?: string | null;
|
||||
}
|
||||
|
||||
export interface ReceiptValidationResult {
|
||||
valid: boolean;
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export interface ReceiptValidationOptions extends RequestOptions {
|
||||
minerVerifier?: (receipt: ReceiptSummary) => Promise<boolean> | boolean;
|
||||
coordinatorVerifier?: (receipt: ReceiptSummary) => Promise<boolean> | boolean;
|
||||
minerPublicKeyPem?: string;
|
||||
coordinatorPublicKeyPem?: string;
|
||||
signatureAlgorithm?: "ed25519" | "secp256k1" | "rsa" | string;
|
||||
}
|
||||
|
||||
export interface ReceiptServiceOptions {
|
||||
client: AitbcClient;
|
||||
maxRetries?: number;
|
||||
retryDelayMs?: number;
|
||||
minerPublicKeyPem?: string;
|
||||
coordinatorPublicKeyPem?: string;
|
||||
signatureAlgorithm?: "ed25519" | "secp256k1" | "rsa" | string;
|
||||
}
|
||||
|
||||
export class ReceiptService {
|
||||
private client: AitbcClient;
|
||||
private maxRetries: number;
|
||||
private retryDelayMs: number;
|
||||
private minerPublicKeyPem?: string;
|
||||
private coordinatorPublicKeyPem?: string;
|
||||
private signatureAlgorithm?: string;
|
||||
|
||||
constructor(opts: ReceiptServiceOptions) {
|
||||
this.client = opts.client;
|
||||
this.maxRetries = opts.maxRetries ?? 3;
|
||||
this.retryDelayMs = opts.retryDelayMs ?? 250;
|
||||
this.minerPublicKeyPem = opts.minerPublicKeyPem;
|
||||
this.coordinatorPublicKeyPem = opts.coordinatorPublicKeyPem;
|
||||
this.signatureAlgorithm = opts.signatureAlgorithm ?? "ed25519";
|
||||
}
|
||||
|
||||
async getJobReceipts(
|
||||
jobId: string,
|
||||
cursor?: string,
|
||||
limit: number = 50,
|
||||
options?: RequestOptions,
|
||||
): Promise<PaginatedReceipts> {
|
||||
let attempt = 0;
|
||||
let lastError: unknown;
|
||||
|
||||
while (attempt <= this.maxRetries) {
|
||||
try {
|
||||
const data = await this.client.request<ReceiptListResponse & { next_cursor?: string }>(
|
||||
"GET",
|
||||
`/v1/jobs/${jobId}/receipts`,
|
||||
{
|
||||
...options,
|
||||
query: {
|
||||
cursor,
|
||||
limit,
|
||||
},
|
||||
}
|
||||
);
|
||||
return {
|
||||
items: (data.items ?? []).filter((r) => !r.jobId || r.jobId === jobId),
|
||||
nextCursor: data.next_cursor ?? (data as any).nextCursor ?? null,
|
||||
};
|
||||
} catch (err) {
|
||||
lastError = err;
|
||||
attempt += 1;
|
||||
if (attempt > this.maxRetries) break;
|
||||
await this.delay(this.retryDelayMs * attempt);
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError ?? new Error("Failed to fetch receipts");
|
||||
}
|
||||
|
||||
async validateReceipt(receipt: ReceiptSummary, options?: ReceiptValidationOptions): Promise<ReceiptValidationResult> {
|
||||
// Placeholder for full cryptographic verification: delegate to coordinator API
|
||||
const {
|
||||
minerVerifier,
|
||||
coordinatorVerifier,
|
||||
minerPublicKeyPem,
|
||||
coordinatorPublicKeyPem,
|
||||
signatureAlgorithm,
|
||||
...requestOptions
|
||||
} = options ?? {};
|
||||
try {
|
||||
const data = await this.client.request<{ valid: boolean; reason?: string }>(
|
||||
"POST",
|
||||
"/v1/receipts/verify",
|
||||
{
|
||||
...requestOptions,
|
||||
body: JSON.stringify(this.buildVerificationPayload(receipt)),
|
||||
}
|
||||
);
|
||||
return { valid: !!data.valid, reason: data.reason };
|
||||
} catch (err) {
|
||||
// Fallback to local checks if API unavailable
|
||||
const local = await this.validateLocally(
|
||||
receipt,
|
||||
minerVerifier,
|
||||
coordinatorVerifier,
|
||||
minerPublicKeyPem ?? this.minerPublicKeyPem,
|
||||
coordinatorPublicKeyPem ?? this.coordinatorPublicKeyPem,
|
||||
signatureAlgorithm ?? this.signatureAlgorithm ?? "ed25519"
|
||||
);
|
||||
return local.valid ? local : { valid: false, reason: (err as Error).message };
|
||||
}
|
||||
}
|
||||
|
||||
buildVerificationPayload(receipt: ReceiptSummary) {
|
||||
return {
|
||||
receipt_id: receipt.receiptId,
|
||||
job_id: receipt.jobId,
|
||||
miner: receipt.miner,
|
||||
coordinator: receipt.coordinator,
|
||||
issued_at: receipt.issuedAt,
|
||||
status: receipt.status,
|
||||
payload: receipt.payload,
|
||||
};
|
||||
}
|
||||
|
||||
async validateLocally(
|
||||
receipt: ReceiptSummary,
|
||||
minerVerifier?: (receipt: ReceiptSummary) => Promise<boolean> | boolean,
|
||||
coordinatorVerifier?: (receipt: ReceiptSummary) => Promise<boolean> | boolean,
|
||||
minerPublicKeyPem?: string,
|
||||
coordinatorPublicKeyPem?: string,
|
||||
signatureAlgorithm: string = "ed25519",
|
||||
): Promise<ReceiptValidationResult> {
|
||||
const payload = receipt.payload ?? {};
|
||||
const sig = payload.signature ?? {};
|
||||
const minerSig = payload.minerSignature ?? sig.sig;
|
||||
const coordinatorSig = payload.coordinatorSignature ?? sig.sig;
|
||||
|
||||
if (!minerSig) {
|
||||
return { valid: false, reason: "missing miner signature" };
|
||||
}
|
||||
if (!coordinatorSig) {
|
||||
return { valid: false, reason: "missing coordinator signature" };
|
||||
}
|
||||
if (!payload.job_id && receipt.jobId) {
|
||||
return { valid: false, reason: "missing job_id in payload" };
|
||||
}
|
||||
|
||||
const payloadForSig = this.sanitizePayload(payload);
|
||||
|
||||
if (minerVerifier) {
|
||||
const ok = await minerVerifier(receipt);
|
||||
if (!ok) return { valid: false, reason: "miner signature invalid" };
|
||||
} else if (minerPublicKeyPem) {
|
||||
const ok = this.verifyWithCrypto(minerSig, minerPublicKeyPem, payloadForSig, signatureAlgorithm);
|
||||
if (!ok) return { valid: false, reason: "miner signature invalid" };
|
||||
}
|
||||
if (coordinatorVerifier) {
|
||||
const ok = await coordinatorVerifier(receipt);
|
||||
if (!ok) return { valid: false, reason: "coordinator signature invalid" };
|
||||
} else if (coordinatorPublicKeyPem) {
|
||||
const ok = this.verifyWithCrypto(coordinatorSig, coordinatorPublicKeyPem, payloadForSig, signatureAlgorithm);
|
||||
if (!ok) return { valid: false, reason: "coordinator signature invalid" };
|
||||
}
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
private sanitizePayload(payload: Record<string, unknown>): Record<string, unknown> {
|
||||
const { signature, minerSignature, coordinatorSignature, ...rest } = payload ?? {};
|
||||
return rest;
|
||||
}
|
||||
|
||||
private verifyWithCrypto(
|
||||
signatureBase64: string,
|
||||
publicKeyPem: string,
|
||||
payload: Record<string, unknown>,
|
||||
alg: string,
|
||||
): boolean {
|
||||
try {
|
||||
const key = createPublicKey(publicKeyPem);
|
||||
const message = Buffer.from(JSON.stringify(payload ?? {}));
|
||||
const sig = Buffer.from(signatureBase64, "base64");
|
||||
|
||||
if (alg.toLowerCase() === "ed25519") {
|
||||
return verifySignature(null, message, key, sig);
|
||||
}
|
||||
if (alg.toLowerCase() === "secp256k1") {
|
||||
return verifySignature("sha256", message, { key, dsaEncoding: "ieee-p1363" }, sig);
|
||||
}
|
||||
if (alg.toLowerCase().startsWith("rsa")) {
|
||||
return verifySignature("sha256", message, key, sig);
|
||||
}
|
||||
// Unknown alg: fail closed
|
||||
return false;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async delay(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
}
|
||||
18
packages/py/aitbc-agent-sdk/aitbc_agent/__init__.py
Normal file
18
packages/py/aitbc-agent-sdk/aitbc_agent/__init__.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""
|
||||
AITBC Agent SDK - Python SDK for AI agents to participate in the AITBC network
|
||||
"""
|
||||
|
||||
from .agent import Agent
|
||||
from .compute_provider import ComputeProvider
|
||||
from .compute_consumer import ComputeConsumer
|
||||
from .platform_builder import PlatformBuilder
|
||||
from .swarm_coordinator import SwarmCoordinator
|
||||
|
||||
__version__ = "1.0.0"
|
||||
__all__ = [
|
||||
"Agent",
|
||||
"ComputeProvider",
|
||||
"ComputeConsumer",
|
||||
"PlatformBuilder",
|
||||
"SwarmCoordinator"
|
||||
]
|
||||
233
packages/py/aitbc-agent-sdk/aitbc_agent/agent.py
Normal file
233
packages/py/aitbc-agent-sdk/aitbc_agent/agent.py
Normal file
@@ -0,0 +1,233 @@
|
||||
"""
|
||||
Core Agent class for AITBC network participation
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional, Any
|
||||
from dataclasses import dataclass
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
|
||||
@dataclass
|
||||
class AgentCapabilities:
|
||||
"""Agent capability specification"""
|
||||
compute_type: str # "inference", "training", "processing"
|
||||
gpu_memory: Optional[int] = None
|
||||
supported_models: List[str] = None
|
||||
performance_score: float = 0.0
|
||||
max_concurrent_jobs: int = 1
|
||||
specialization: Optional[str] = None
|
||||
|
||||
def __post_init__(self):
|
||||
if self.supported_models is None:
|
||||
self.supported_models = []
|
||||
|
||||
@dataclass
|
||||
class AgentIdentity:
|
||||
"""Agent identity and cryptographic keys"""
|
||||
id: str
|
||||
name: str
|
||||
address: str
|
||||
public_key: str
|
||||
private_key: str
|
||||
|
||||
def sign_message(self, message: Dict[str, Any]) -> str:
|
||||
"""Sign a message with agent's private key"""
|
||||
message_str = json.dumps(message, sort_keys=True)
|
||||
private_key = serialization.load_pem_private_key(
|
||||
self.private_key.encode(),
|
||||
password=None
|
||||
)
|
||||
|
||||
signature = private_key.sign(
|
||||
message_str.encode(),
|
||||
padding.PSS(
|
||||
mgf=padding.MGF1(hashes.SHA256()),
|
||||
salt_length=padding.PSS.MAX_LENGTH
|
||||
),
|
||||
hashes.SHA256()
|
||||
)
|
||||
|
||||
return signature.hex()
|
||||
|
||||
def verify_signature(self, message: Dict[str, Any], signature: str) -> bool:
|
||||
"""Verify a message signature"""
|
||||
message_str = json.dumps(message, sort_keys=True)
|
||||
public_key = serialization.load_pem_public_key(
|
||||
self.public_key.encode()
|
||||
)
|
||||
|
||||
try:
|
||||
public_key.verify(
|
||||
bytes.fromhex(signature),
|
||||
message_str.encode(),
|
||||
padding.PSS(
|
||||
mgf=padding.MGF1(hashes.SHA256()),
|
||||
salt_length=padding.PSS.MAX_LENGTH
|
||||
),
|
||||
hashes.SHA256()
|
||||
)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
class Agent:
|
||||
"""Core AITBC Agent class"""
|
||||
|
||||
def __init__(self, identity: AgentIdentity, capabilities: AgentCapabilities):
|
||||
self.identity = identity
|
||||
self.capabilities = capabilities
|
||||
self.registered = False
|
||||
self.reputation_score = 0.0
|
||||
self.earnings = 0.0
|
||||
|
||||
@classmethod
|
||||
def create(cls, name: str, agent_type: str, capabilities: Dict[str, Any]) -> 'Agent':
|
||||
"""Create a new agent with generated identity"""
|
||||
# Generate cryptographic keys
|
||||
private_key = rsa.generate_private_key(
|
||||
public_exponent=65537,
|
||||
key_size=2048
|
||||
)
|
||||
|
||||
public_key = private_key.public_key()
|
||||
private_pem = private_key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.PKCS8,
|
||||
encryption_algorithm=serialization.NoEncryption()
|
||||
)
|
||||
|
||||
public_pem = public_key.public_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
||||
)
|
||||
|
||||
# Generate agent identity
|
||||
agent_id = f"agent_{uuid.uuid4().hex[:8]}"
|
||||
address = f"0x{uuid.uuid4().hex[:40]}"
|
||||
|
||||
identity = AgentIdentity(
|
||||
id=agent_id,
|
||||
name=name,
|
||||
address=address,
|
||||
public_key=public_pem.decode(),
|
||||
private_key=private_pem.decode()
|
||||
)
|
||||
|
||||
# Create capabilities object
|
||||
agent_capabilities = AgentCapabilities(**capabilities)
|
||||
|
||||
return cls(identity, agent_capabilities)
|
||||
|
||||
async def register(self) -> bool:
|
||||
"""Register the agent on the AITBC network"""
|
||||
try:
|
||||
registration_data = {
|
||||
"agent_id": self.identity.id,
|
||||
"name": self.identity.name,
|
||||
"address": self.identity.address,
|
||||
"public_key": self.identity.public_key,
|
||||
"capabilities": {
|
||||
"compute_type": self.capabilities.compute_type,
|
||||
"gpu_memory": self.capabilities.gpu_memory,
|
||||
"supported_models": self.capabilities.supported_models,
|
||||
"performance_score": self.capabilities.performance_score,
|
||||
"max_concurrent_jobs": self.capabilities.max_concurrent_jobs,
|
||||
"specialization": self.capabilities.specialization
|
||||
},
|
||||
"timestamp": datetime.utcnow().isoformat()
|
||||
}
|
||||
|
||||
# Sign registration data
|
||||
signature = self.identity.sign_message(registration_data)
|
||||
registration_data["signature"] = signature
|
||||
|
||||
# TODO: Submit to AITBC network registration endpoint
|
||||
# For now, simulate successful registration
|
||||
await asyncio.sleep(1) # Simulate network call
|
||||
|
||||
self.registered = True
|
||||
print(f"Agent {self.identity.id} registered successfully")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Registration failed: {e}")
|
||||
return False
|
||||
|
||||
async def get_reputation(self) -> Dict[str, float]:
|
||||
"""Get agent reputation metrics"""
|
||||
# TODO: Fetch from reputation system
|
||||
return {
|
||||
"overall_score": self.reputation_score,
|
||||
"job_success_rate": 0.95,
|
||||
"avg_response_time": 30.5,
|
||||
"client_satisfaction": 4.7
|
||||
}
|
||||
|
||||
async def update_reputation(self, new_score: float) -> None:
|
||||
"""Update agent reputation score"""
|
||||
self.reputation_score = new_score
|
||||
print(f"Reputation updated to {new_score}")
|
||||
|
||||
async def get_earnings(self, period: str = "30d") -> Dict[str, Any]:
|
||||
"""Get agent earnings information"""
|
||||
# TODO: Fetch from blockchain/payment system
|
||||
return {
|
||||
"total": self.earnings,
|
||||
"daily_average": self.earnings / 30,
|
||||
"period": period,
|
||||
"currency": "AITBC"
|
||||
}
|
||||
|
||||
async def send_message(self, recipient_id: str, message_type: str, payload: Dict[str, Any]) -> bool:
|
||||
"""Send a message to another agent"""
|
||||
message = {
|
||||
"from": self.identity.id,
|
||||
"to": recipient_id,
|
||||
"type": message_type,
|
||||
"payload": payload,
|
||||
"timestamp": datetime.utcnow().isoformat()
|
||||
}
|
||||
|
||||
# Sign message
|
||||
signature = self.identity.sign_message(message)
|
||||
message["signature"] = signature
|
||||
|
||||
# TODO: Send through AITBC agent messaging protocol
|
||||
print(f"Message sent to {recipient_id}: {message_type}")
|
||||
return True
|
||||
|
||||
async def receive_message(self, message: Dict[str, Any]) -> bool:
|
||||
"""Process a received message from another agent"""
|
||||
# Verify signature
|
||||
if "signature" not in message:
|
||||
return False
|
||||
|
||||
# TODO: Verify sender's signature
|
||||
# For now, just process the message
|
||||
print(f"Received message from {message.get('from')}: {message.get('type')}")
|
||||
return True
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert agent to dictionary representation"""
|
||||
return {
|
||||
"id": self.identity.id,
|
||||
"name": self.identity.name,
|
||||
"address": self.identity.address,
|
||||
"capabilities": {
|
||||
"compute_type": self.capabilities.compute_type,
|
||||
"gpu_memory": self.capabilities.gpu_memory,
|
||||
"supported_models": self.capabilities.supported_models,
|
||||
"performance_score": self.capabilities.performance_score,
|
||||
"max_concurrent_jobs": self.capabilities.max_concurrent_jobs,
|
||||
"specialization": self.capabilities.specialization
|
||||
},
|
||||
"reputation_score": self.reputation_score,
|
||||
"registered": self.registered,
|
||||
"earnings": self.earnings
|
||||
}
|
||||
251
packages/py/aitbc-agent-sdk/aitbc_agent/compute_provider.py
Normal file
251
packages/py/aitbc-agent-sdk/aitbc_agent/compute_provider.py
Normal file
@@ -0,0 +1,251 @@
|
||||
"""
|
||||
Compute Provider Agent - for agents that provide computational resources
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import Dict, List, Optional, Any
|
||||
from datetime import datetime, timedelta
|
||||
from dataclasses import dataclass
|
||||
from .agent import Agent, AgentCapabilities
|
||||
|
||||
@dataclass
|
||||
class ResourceOffer:
|
||||
"""Resource offering specification"""
|
||||
provider_id: str
|
||||
compute_type: str
|
||||
gpu_memory: int
|
||||
supported_models: List[str]
|
||||
price_per_hour: float
|
||||
availability_schedule: Dict[str, Any]
|
||||
max_concurrent_jobs: int
|
||||
quality_guarantee: float = 0.95
|
||||
|
||||
@dataclass
|
||||
class JobExecution:
|
||||
"""Job execution tracking"""
|
||||
job_id: str
|
||||
consumer_id: str
|
||||
start_time: datetime
|
||||
expected_duration: timedelta
|
||||
actual_duration: Optional[timedelta] = None
|
||||
status: str = "running" # running, completed, failed
|
||||
quality_score: Optional[float] = None
|
||||
|
||||
class ComputeProvider(Agent):
|
||||
"""Agent that provides computational resources"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.current_offers: List[ResourceOffer] = []
|
||||
self.active_jobs: List[JobExecution] = []
|
||||
self.earnings = 0.0
|
||||
self.utilization_rate = 0.0
|
||||
|
||||
@classmethod
|
||||
def register(cls, name: str, capabilities: Dict[str, Any], pricing_model: Dict[str, Any]) -> 'ComputeProvider':
|
||||
"""Register as a compute provider"""
|
||||
agent = super().create(name, "compute_provider", capabilities)
|
||||
provider = cls(agent.identity, agent.capabilities)
|
||||
provider.pricing_model = pricing_model
|
||||
return provider
|
||||
|
||||
async def offer_resources(self, price_per_hour: float, availability_schedule: Dict[str, Any], max_concurrent_jobs: int = 3) -> bool:
|
||||
"""Offer computational resources on the marketplace"""
|
||||
try:
|
||||
offer = ResourceOffer(
|
||||
provider_id=self.identity.id,
|
||||
compute_type=self.capabilities.compute_type,
|
||||
gpu_memory=self.capabilities.gpu_memory or 0,
|
||||
supported_models=self.capabilities.supported_models,
|
||||
price_per_hour=price_per_hour,
|
||||
availability_schedule=availability_schedule,
|
||||
max_concurrent_jobs=max_concurrent_jobs
|
||||
)
|
||||
|
||||
# Submit to marketplace
|
||||
await self._submit_to_marketplace(offer)
|
||||
self.current_offers.append(offer)
|
||||
|
||||
print(f"Resource offer submitted: {price_per_hour} AITBC/hour")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to offer resources: {e}")
|
||||
return False
|
||||
|
||||
async def set_availability(self, schedule: Dict[str, Any]) -> bool:
|
||||
"""Set availability schedule for resource offerings"""
|
||||
try:
|
||||
# Update all current offers with new schedule
|
||||
for offer in self.current_offers:
|
||||
offer.availability_schedule = schedule
|
||||
await self._update_marketplace_offer(offer)
|
||||
|
||||
print("Availability schedule updated")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to update availability: {e}")
|
||||
return False
|
||||
|
||||
async def enable_dynamic_pricing(self, base_rate: float, demand_threshold: float = 0.8, max_multiplier: float = 2.0, adjustment_frequency: str = "15min") -> bool:
|
||||
"""Enable dynamic pricing based on market demand"""
|
||||
try:
|
||||
self.dynamic_pricing = {
|
||||
"base_rate": base_rate,
|
||||
"demand_threshold": demand_threshold,
|
||||
"max_multiplier": max_multiplier,
|
||||
"adjustment_frequency": adjustment_frequency,
|
||||
"enabled": True
|
||||
}
|
||||
|
||||
# Start dynamic pricing task
|
||||
asyncio.create_task(self._dynamic_pricing_loop())
|
||||
|
||||
print("Dynamic pricing enabled")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to enable dynamic pricing: {e}")
|
||||
return False
|
||||
|
||||
async def _dynamic_pricing_loop(self):
|
||||
"""Background task for dynamic price adjustments"""
|
||||
while getattr(self, 'dynamic_pricing', {}).get('enabled', False):
|
||||
try:
|
||||
# Get current utilization
|
||||
current_utilization = len(self.active_jobs) / self.capabilities.max_concurrent_jobs
|
||||
|
||||
# Adjust pricing based on demand
|
||||
if current_utilization > self.dynamic_pricing['demand_threshold']:
|
||||
# High demand - increase price
|
||||
multiplier = min(
|
||||
1.0 + (current_utilization - self.dynamic_pricing['demand_threshold']) * 2,
|
||||
self.dynamic_pricing['max_multiplier']
|
||||
)
|
||||
else:
|
||||
# Low demand - decrease price
|
||||
multiplier = max(0.5, current_utilization / self.dynamic_pricing['demand_threshold'])
|
||||
|
||||
new_price = self.dynamic_pricing['base_rate'] * multiplier
|
||||
|
||||
# Update marketplace offers
|
||||
for offer in self.current_offers:
|
||||
offer.price_per_hour = new_price
|
||||
await self._update_marketplace_offer(offer)
|
||||
|
||||
print(f"Dynamic pricing: utilization={current_utilization:.2f}, price={new_price:.3f} AITBC/h")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Dynamic pricing error: {e}")
|
||||
|
||||
# Wait for next adjustment
|
||||
await asyncio.sleep(900) # 15 minutes
|
||||
|
||||
async def accept_job(self, job_request: Dict[str, Any]) -> bool:
|
||||
"""Accept and execute a computational job"""
|
||||
try:
|
||||
# Check capacity
|
||||
if len(self.active_jobs) >= self.capabilities.max_concurrent_jobs:
|
||||
return False
|
||||
|
||||
# Create job execution record
|
||||
job = JobExecution(
|
||||
job_id=job_request["job_id"],
|
||||
consumer_id=job_request["consumer_id"],
|
||||
start_time=datetime.utcnow(),
|
||||
expected_duration=timedelta(hours=job_request["estimated_hours"])
|
||||
)
|
||||
|
||||
self.active_jobs.append(job)
|
||||
self._update_utilization()
|
||||
|
||||
# Execute job (simulate)
|
||||
asyncio.create_task(self._execute_job(job, job_request))
|
||||
|
||||
print(f"Job accepted: {job.job_id} from {job.consumer_id}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to accept job: {e}")
|
||||
return False
|
||||
|
||||
async def _execute_job(self, job: JobExecution, job_request: Dict[str, Any]):
|
||||
"""Execute a computational job"""
|
||||
try:
|
||||
# Simulate job execution
|
||||
execution_time = timedelta(hours=job_request["estimated_hours"])
|
||||
await asyncio.sleep(5) # Simulate processing time
|
||||
|
||||
# Update job completion
|
||||
job.actual_duration = execution_time
|
||||
job.status = "completed"
|
||||
job.quality_score = 0.95 # Simulate quality score
|
||||
|
||||
# Calculate earnings
|
||||
earnings = job_request["estimated_hours"] * job_request["agreed_price"]
|
||||
self.earnings += earnings
|
||||
|
||||
# Remove from active jobs
|
||||
self.active_jobs.remove(job)
|
||||
self._update_utilization()
|
||||
|
||||
# Notify consumer
|
||||
await self._notify_job_completion(job, earnings)
|
||||
|
||||
print(f"Job completed: {job.job_id}, earned {earnings} AITBC")
|
||||
|
||||
except Exception as e:
|
||||
job.status = "failed"
|
||||
print(f"Job execution failed: {job.job_id} - {e}")
|
||||
|
||||
async def _notify_job_completion(self, job: JobExecution, earnings: float):
|
||||
"""Notify consumer about job completion"""
|
||||
notification = {
|
||||
"job_id": job.job_id,
|
||||
"status": job.status,
|
||||
"completion_time": datetime.utcnow().isoformat(),
|
||||
"duration_hours": job.actual_duration.total_seconds() / 3600 if job.actual_duration else None,
|
||||
"quality_score": job.quality_score,
|
||||
"cost": earnings
|
||||
}
|
||||
|
||||
await self.send_message(job.consumer_id, "job_completion", notification)
|
||||
|
||||
def _update_utilization(self):
|
||||
"""Update current utilization rate"""
|
||||
self.utilization_rate = len(self.active_jobs) / self.capabilities.max_concurrent_jobs
|
||||
|
||||
async def get_performance_metrics(self) -> Dict[str, Any]:
|
||||
"""Get provider performance metrics"""
|
||||
completed_jobs = [j for j in self.active_jobs if j.status == "completed"]
|
||||
|
||||
return {
|
||||
"utilization_rate": self.utilization_rate,
|
||||
"active_jobs": len(self.active_jobs),
|
||||
"total_earnings": self.earnings,
|
||||
"average_job_duration": sum(j.actual_duration.total_seconds() for j in completed_jobs) / len(completed_jobs) if completed_jobs else 0,
|
||||
"quality_score": sum(j.quality_score for j in completed_jobs if j.quality_score) / len(completed_jobs) if completed_jobs else 0,
|
||||
"current_offers": len(self.current_offers)
|
||||
}
|
||||
|
||||
async def _submit_to_marketplace(self, offer: ResourceOffer):
|
||||
"""Submit resource offer to marketplace (placeholder)"""
|
||||
# TODO: Implement actual marketplace submission
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
async def _update_marketplace_offer(self, offer: ResourceOffer):
|
||||
"""Update existing marketplace offer (placeholder)"""
|
||||
# TODO: Implement actual marketplace update
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
@classmethod
|
||||
def assess_capabilities(cls) -> Dict[str, Any]:
|
||||
"""Assess available computational capabilities"""
|
||||
# TODO: Implement actual capability assessment
|
||||
return {
|
||||
"gpu_memory": 24,
|
||||
"supported_models": ["llama3.2", "mistral", "deepseek"],
|
||||
"performance_score": 0.95,
|
||||
"max_concurrent_jobs": 3
|
||||
}
|
||||
337
packages/py/aitbc-agent-sdk/aitbc_agent/swarm_coordinator.py
Normal file
337
packages/py/aitbc-agent-sdk/aitbc_agent/swarm_coordinator.py
Normal file
@@ -0,0 +1,337 @@
|
||||
"""
|
||||
Swarm Coordinator - for agents participating in collective intelligence
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from typing import Dict, List, Optional, Any
|
||||
from datetime import datetime
|
||||
from dataclasses import dataclass
|
||||
from .agent import Agent
|
||||
|
||||
@dataclass
|
||||
class SwarmMessage:
|
||||
"""Swarm communication message"""
|
||||
swarm_id: str
|
||||
sender_id: str
|
||||
message_type: str
|
||||
priority: str
|
||||
payload: Dict[str, Any]
|
||||
timestamp: str
|
||||
swarm_signature: str
|
||||
|
||||
@dataclass
|
||||
class SwarmDecision:
|
||||
"""Collective swarm decision"""
|
||||
swarm_id: str
|
||||
decision_type: str
|
||||
proposal: Dict[str, Any]
|
||||
votes: Dict[str, str] # agent_id -> vote
|
||||
consensus: bool
|
||||
implementation_plan: Dict[str, Any]
|
||||
timestamp: str
|
||||
|
||||
class SwarmCoordinator(Agent):
|
||||
"""Agent that participates in swarm intelligence"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.joined_swarms: Dict[str, Dict[str, Any]] = {}
|
||||
self.swarm_reputation: Dict[str, float] = {}
|
||||
self.contribution_score = 0.0
|
||||
|
||||
async def join_swarm(self, swarm_type: str, config: Dict[str, Any]) -> bool:
|
||||
"""Join a swarm for collective intelligence"""
|
||||
try:
|
||||
swarm_id = f"{swarm_type}-v1"
|
||||
|
||||
# Register with swarm
|
||||
registration = {
|
||||
"agent_id": self.identity.id,
|
||||
"swarm_id": swarm_id,
|
||||
"role": config.get("role", "participant"),
|
||||
"capabilities": {
|
||||
"compute_type": self.capabilities.compute_type,
|
||||
"performance_score": self.capabilities.performance_score,
|
||||
"specialization": self.capabilities.specialization
|
||||
},
|
||||
"contribution_level": config.get("contribution_level", "medium"),
|
||||
"data_sharing_consent": config.get("data_sharing_consent", True)
|
||||
}
|
||||
|
||||
# Sign swarm registration
|
||||
signature = self.identity.sign_message(registration)
|
||||
registration["signature"] = signature
|
||||
|
||||
# Submit to swarm coordinator
|
||||
await self._register_with_swarm(swarm_id, registration)
|
||||
|
||||
# Store swarm membership
|
||||
self.joined_swarms[swarm_id] = {
|
||||
"type": swarm_type,
|
||||
"role": config.get("role", "participant"),
|
||||
"joined_at": datetime.utcnow().isoformat(),
|
||||
"contribution_count": 0,
|
||||
"last_activity": datetime.utcnow().isoformat()
|
||||
}
|
||||
|
||||
# Initialize swarm reputation
|
||||
self.swarm_reputation[swarm_id] = 0.5 # Starting reputation
|
||||
|
||||
# Start swarm participation tasks
|
||||
asyncio.create_task(self._swarm_participation_loop(swarm_id))
|
||||
|
||||
print(f"Joined swarm: {swarm_id} as {config.get('role', 'participant')}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to join swarm {swarm_type}: {e}")
|
||||
return False
|
||||
|
||||
async def _swarm_participation_loop(self, swarm_id: str):
|
||||
"""Background task for active swarm participation"""
|
||||
while swarm_id in self.joined_swarms:
|
||||
try:
|
||||
# Listen for swarm messages
|
||||
await self._process_swarm_messages(swarm_id)
|
||||
|
||||
# Contribute data if enabled
|
||||
swarm_config = self.joined_swarms[swarm_id]
|
||||
if swarm_config.get("data_sharing", True):
|
||||
await self._contribute_swarm_data(swarm_id)
|
||||
|
||||
# Participate in collective decisions
|
||||
await self._participate_in_decisions(swarm_id)
|
||||
|
||||
# Update activity timestamp
|
||||
swarm_config["last_activity"] = datetime.utcnow().isoformat()
|
||||
|
||||
except Exception as e:
|
||||
print(f"Swarm participation error for {swarm_id}: {e}")
|
||||
|
||||
# Wait before next participation cycle
|
||||
await asyncio.sleep(60) # 1 minute
|
||||
|
||||
async def broadcast_to_swarm(self, message: SwarmMessage) -> bool:
|
||||
"""Broadcast a message to the swarm"""
|
||||
try:
|
||||
# Verify swarm membership
|
||||
if message.swarm_id not in self.joined_swarms:
|
||||
return False
|
||||
|
||||
# Sign swarm message
|
||||
swarm_signature = self.identity.sign_message({
|
||||
"swarm_id": message.swarm_id,
|
||||
"sender_id": message.sender_id,
|
||||
"message_type": message.message_type,
|
||||
"payload": message.payload,
|
||||
"timestamp": message.timestamp
|
||||
})
|
||||
message.swarm_signature = swarm_signature
|
||||
|
||||
# Broadcast to swarm network
|
||||
await self._broadcast_to_swarm_network(message)
|
||||
|
||||
# Update contribution count
|
||||
self.joined_swarms[message.swarm_id]["contribution_count"] += 1
|
||||
|
||||
print(f"Broadcasted to swarm {message.swarm_id}: {message.message_type}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to broadcast to swarm: {e}")
|
||||
return False
|
||||
|
||||
async def _contribute_swarm_data(self, swarm_id: str):
|
||||
"""Contribute data to swarm intelligence"""
|
||||
try:
|
||||
swarm_type = self.joined_swarms[swarm_id]["type"]
|
||||
|
||||
if swarm_type == "load_balancing":
|
||||
data = await self._get_load_balancing_data()
|
||||
elif swarm_type == "pricing":
|
||||
data = await self._get_pricing_data()
|
||||
elif swarm_type == "security":
|
||||
data = await self._get_security_data()
|
||||
else:
|
||||
data = await self._get_general_data()
|
||||
|
||||
message = SwarmMessage(
|
||||
swarm_id=swarm_id,
|
||||
sender_id=self.identity.id,
|
||||
message_type="data_contribution",
|
||||
priority="medium",
|
||||
payload=data,
|
||||
timestamp=datetime.utcnow().isoformat(),
|
||||
swarm_signature="" # Will be added in broadcast_to_swarm
|
||||
)
|
||||
|
||||
await self.broadcast_to_swarm(message)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to contribute swarm data: {e}")
|
||||
|
||||
async def _get_load_balancing_data(self) -> Dict[str, Any]:
|
||||
"""Get load balancing data for swarm contribution"""
|
||||
# TODO: Get actual load balancing metrics
|
||||
return {
|
||||
"resource_type": "gpu_memory",
|
||||
"availability": 0.75,
|
||||
"location": "us-west-2",
|
||||
"pricing_trend": "stable",
|
||||
"current_load": 0.6,
|
||||
"capacity_utilization": 0.8
|
||||
}
|
||||
|
||||
async def _get_pricing_data(self) -> Dict[str, Any]:
|
||||
"""Get pricing data for swarm contribution"""
|
||||
# TODO: Get actual pricing data
|
||||
return {
|
||||
"current_demand": "high",
|
||||
"price_trends": "increasing",
|
||||
"resource_constraints": "gpu_memory",
|
||||
"competitive_landscape": "moderate",
|
||||
"market_volatility": 0.15
|
||||
}
|
||||
|
||||
async def _get_security_data(self) -> Dict[str, Any]:
|
||||
"""Get security data for swarm contribution"""
|
||||
# TODO: Get actual security metrics
|
||||
return {
|
||||
"threat_level": "low",
|
||||
"anomaly_count": 2,
|
||||
"verification_success_rate": 0.98,
|
||||
"network_health": "good",
|
||||
"security_events": []
|
||||
}
|
||||
|
||||
async def _get_general_data(self) -> Dict[str, Any]:
|
||||
"""Get general performance data for swarm contribution"""
|
||||
return {
|
||||
"performance_metrics": {
|
||||
"response_time": 30.5,
|
||||
"success_rate": 0.95,
|
||||
"quality_score": 0.92
|
||||
},
|
||||
"network_status": "healthy",
|
||||
"agent_status": "active"
|
||||
}
|
||||
|
||||
async def coordinate_task(self, task: str, collaborators: int) -> Dict[str, Any]:
|
||||
"""Coordinate a collaborative task with other agents"""
|
||||
try:
|
||||
# Create coordination proposal
|
||||
proposal = {
|
||||
"task_id": f"task_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}",
|
||||
"task_type": task,
|
||||
"coordinator_id": self.identity.id,
|
||||
"required_collaborators": collaborators,
|
||||
"task_description": f"Collaborative {task} task",
|
||||
"estimated_duration": "2h",
|
||||
"resource_requirements": {
|
||||
"compute_type": "general",
|
||||
"min_performance": 0.8
|
||||
}
|
||||
}
|
||||
|
||||
# Submit to swarm for coordination
|
||||
coordination_result = await self._submit_coordination_proposal(proposal)
|
||||
|
||||
print(f"Task coordination initiated: {task} with {collaborators} collaborators")
|
||||
return coordination_result
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to coordinate task: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
async def get_market_intelligence(self) -> Dict[str, Any]:
|
||||
"""Get collective market intelligence from swarm"""
|
||||
try:
|
||||
# Request market intelligence from pricing swarm
|
||||
if "pricing-v1" in self.joined_swarms:
|
||||
intel_request = SwarmMessage(
|
||||
swarm_id="pricing-v1",
|
||||
sender_id=self.identity.id,
|
||||
message_type="intelligence_request",
|
||||
priority="high",
|
||||
payload={"request_type": "market_intelligence"},
|
||||
timestamp=datetime.utcnow().isoformat(),
|
||||
swarm_signature=""
|
||||
)
|
||||
|
||||
await self.broadcast_to_swarm(intel_request)
|
||||
|
||||
# Wait for intelligence response (simulate)
|
||||
await asyncio.sleep(2)
|
||||
|
||||
return {
|
||||
"demand_forecast": "increasing",
|
||||
"price_trends": "stable_to_rising",
|
||||
"competition_analysis": "moderate",
|
||||
"opportunity_areas": ["specialized_models", "batch_processing"],
|
||||
"risk_factors": ["gpu_shortages", "price_volatility"]
|
||||
}
|
||||
else:
|
||||
return {"error": "Not joined to pricing swarm"}
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to get market intelligence: {e}")
|
||||
return {"error": str(e)}
|
||||
|
||||
async def analyze_swarm_benefits(self) -> Dict[str, Any]:
|
||||
"""Analyze benefits of swarm participation"""
|
||||
try:
|
||||
# Calculate benefits based on swarm participation
|
||||
total_contributions = sum(
|
||||
swarm["contribution_count"]
|
||||
for swarm in self.joined_swarms.values()
|
||||
)
|
||||
|
||||
avg_reputation = sum(self.swarm_reputation.values()) / len(self.swarm_reputation) if self.swarm_reputation else 0
|
||||
|
||||
# Simulate benefit analysis
|
||||
earnings_boost = total_contributions * 0.15 # 15% boost per contribution
|
||||
utilization_improvement = avg_reputation * 0.25 # 25% utilization improvement
|
||||
|
||||
return {
|
||||
"earnings_boost": f"{earnings_boost:.1%}",
|
||||
"utilization_improvement": f"{utilization_improvement:.1%}",
|
||||
"total_contributions": total_contributions,
|
||||
"swarm_reputation": avg_reputation,
|
||||
"joined_swarms": len(self.joined_swarms)
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to analyze swarm benefits: {e}")
|
||||
return {"error": str(e)}
|
||||
|
||||
async def _register_with_swarm(self, swarm_id: str, registration: Dict[str, Any]):
|
||||
"""Register with swarm coordinator (placeholder)"""
|
||||
# TODO: Implement actual swarm registration
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
async def _broadcast_to_swarm_network(self, message: SwarmMessage):
|
||||
"""Broadcast message to swarm network (placeholder)"""
|
||||
# TODO: Implement actual swarm broadcasting
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
async def _process_swarm_messages(self, swarm_id: str):
|
||||
"""Process incoming swarm messages (placeholder)"""
|
||||
# TODO: Implement actual message processing
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
async def _participate_in_decisions(self, swarm_id: str):
|
||||
"""Participate in swarm decision making (placeholder)"""
|
||||
# TODO: Implement actual decision participation
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
async def _submit_coordination_proposal(self, proposal: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Submit coordination proposal to swarm (placeholder)"""
|
||||
# TODO: Implement actual proposal submission
|
||||
await asyncio.sleep(0.5)
|
||||
return {
|
||||
"success": True,
|
||||
"proposal_id": proposal["task_id"],
|
||||
"status": "coordinating",
|
||||
"expected_collaborators": 5
|
||||
}
|
||||
@@ -146,9 +146,29 @@ pytest tests/security/
|
||||
## Dependencies
|
||||
|
||||
- **pynacl**: Cryptographic primitives (Ed25519, X25519)
|
||||
- **pydantic**: Data validation and serialization
|
||||
- **Dependencies**: pynacl>=1.5.0, pydantic>=2.0.0
|
||||
- **Python 3.11+**: Modern Python features and performance
|
||||
|
||||
## Compatibility & Stability
|
||||
|
||||
### Python Version Support
|
||||
- **Minimum Version**: Python 3.11+
|
||||
- **Recommended**: Python 3.11 or 3.12
|
||||
- **Security Guarantee**: All cryptographic operations maintain security properties
|
||||
- **Performance**: Optimized for Python 3.11+ performance improvements
|
||||
|
||||
### Cryptographic Security
|
||||
- **Algorithm**: Ed25519 digital signatures (constant-time implementation)
|
||||
- **Key Security**: 256-bit keys with proper entropy requirements
|
||||
- **Signature Verification**: Timing-attack resistant verification
|
||||
- **Randomness**: Uses cryptographically secure random number generation
|
||||
|
||||
### API Stability
|
||||
- **Major Version**: 0.x (pre-1.0, APIs may evolve)
|
||||
- **Backward Compatibility**: Maintained within Python 3.11+ versions
|
||||
- **Security Updates**: Non-breaking security improvements may be added
|
||||
- **Deprecation Notice**: 2+ releases for deprecated cryptographic features
|
||||
|
||||
## License
|
||||
|
||||
MIT License - see LICENSE file for details.
|
||||
|
||||
@@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
description = "AITBC cryptographic utilities for zero-knowledge proofs and digital signatures"
|
||||
readme = "README.md"
|
||||
license = {text = "MIT"}
|
||||
requires-python = ">=3.11"
|
||||
requires-python = ">=3.13"
|
||||
authors = [
|
||||
{name = "AITBC Team", email = "team@aitbc.dev"}
|
||||
]
|
||||
@@ -16,6 +16,7 @@ classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Topic :: Security :: Cryptography",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules"
|
||||
]
|
||||
|
||||
@@ -15,9 +15,10 @@ Classifier: License :: OSI Approved :: MIT License
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.11
|
||||
Classifier: Programming Language :: Python :: 3.12
|
||||
Classifier: Programming Language :: Python :: 3.13
|
||||
Classifier: Topic :: Security :: Cryptography
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Requires-Python: >=3.11
|
||||
Requires-Python: >=3.13
|
||||
Description-Content-Type: text/markdown
|
||||
Requires-Dist: pydantic>=2.7.0
|
||||
Requires-Dist: pynacl>=1.5.0
|
||||
@@ -176,9 +177,29 @@ pytest tests/security/
|
||||
## Dependencies
|
||||
|
||||
- **pynacl**: Cryptographic primitives (Ed25519, X25519)
|
||||
- **pydantic**: Data validation and serialization
|
||||
- **Dependencies**: pynacl>=1.5.0, pydantic>=2.0.0
|
||||
- **Python 3.11+**: Modern Python features and performance
|
||||
|
||||
## Compatibility & Stability
|
||||
|
||||
### Python Version Support
|
||||
- **Minimum Version**: Python 3.11+
|
||||
- **Recommended**: Python 3.11 or 3.12
|
||||
- **Security Guarantee**: All cryptographic operations maintain security properties
|
||||
- **Performance**: Optimized for Python 3.11+ performance improvements
|
||||
|
||||
### Cryptographic Security
|
||||
- **Algorithm**: Ed25519 digital signatures (constant-time implementation)
|
||||
- **Key Security**: 256-bit keys with proper entropy requirements
|
||||
- **Signature Verification**: Timing-attack resistant verification
|
||||
- **Randomness**: Uses cryptographically secure random number generation
|
||||
|
||||
### API Stability
|
||||
- **Major Version**: 0.x (pre-1.0, APIs may evolve)
|
||||
- **Backward Compatibility**: Maintained within Python 3.11+ versions
|
||||
- **Security Updates**: Non-breaking security improvements may be added
|
||||
- **Deprecation Notice**: 2+ releases for deprecated cryptographic features
|
||||
|
||||
## License
|
||||
|
||||
MIT License - see LICENSE file for details.
|
||||
|
||||
@@ -8,6 +8,25 @@ Python client SDK for interacting with AITBC coordinator services, blockchain no
|
||||
pip install aitbc-sdk
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Python**: 3.11 or later
|
||||
- **Dependencies**: httpx, pydantic, aitbc-crypto
|
||||
|
||||
## Compatibility & Stability
|
||||
|
||||
### Python Version Support
|
||||
- **Minimum Version**: Python 3.11+
|
||||
- **Recommended**: Python 3.11 or 3.12
|
||||
- **Guarantee**: All APIs maintain backward compatibility within Python 3.11+
|
||||
- **Security**: Cryptographic operations maintain security properties across versions
|
||||
|
||||
### API Stability
|
||||
- **Major Version**: 0.x (pre-1.0, APIs may change with notice)
|
||||
- **Deprecation Policy**: Deprecated features marked with warnings for 2+ releases
|
||||
- **Breaking Changes**: Announced in release notes with migration guides
|
||||
- **Semantic Versioning**: Follows semver.org specifications
|
||||
|
||||
## Quick Start
|
||||
|
||||
```python
|
||||
|
||||
@@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
description = "AITBC client SDK for interacting with coordinator services"
|
||||
readme = "README.md"
|
||||
license = {text = "MIT"}
|
||||
requires-python = ">=3.11"
|
||||
requires-python = ">=3.13"
|
||||
authors = [
|
||||
{name = "AITBC Team", email = "team@aitbc.dev"}
|
||||
]
|
||||
@@ -16,6 +16,7 @@ classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"Topic :: Scientific/Engineering :: Artificial Intelligence"
|
||||
]
|
||||
|
||||
@@ -15,9 +15,10 @@ Classifier: License :: OSI Approved :: MIT License
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.11
|
||||
Classifier: Programming Language :: Python :: 3.12
|
||||
Classifier: Programming Language :: Python :: 3.13
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
||||
Requires-Python: >=3.11
|
||||
Requires-Python: >=3.13
|
||||
Description-Content-Type: text/markdown
|
||||
Requires-Dist: httpx>=0.27.0
|
||||
Requires-Dist: pydantic>=2.7.0
|
||||
@@ -39,6 +40,25 @@ Python client SDK for interacting with AITBC coordinator services, blockchain no
|
||||
pip install aitbc-sdk
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Python**: 3.11 or later
|
||||
- **Dependencies**: httpx, pydantic, aitbc-crypto
|
||||
|
||||
## Compatibility & Stability
|
||||
|
||||
### Python Version Support
|
||||
- **Minimum Version**: Python 3.11+
|
||||
- **Recommended**: Python 3.11 or 3.12
|
||||
- **Guarantee**: All APIs maintain backward compatibility within Python 3.11+
|
||||
- **Security**: Cryptographic operations maintain security properties across versions
|
||||
|
||||
### API Stability
|
||||
- **Major Version**: 0.x (pre-1.0, APIs may change with notice)
|
||||
- **Deprecation Policy**: Deprecated features marked with warnings for 2+ releases
|
||||
- **Breaking Changes**: Announced in release notes with migration guides
|
||||
- **Semantic Versioning**: Follows semver.org specifications
|
||||
|
||||
## Quick Start
|
||||
|
||||
```python
|
||||
|
||||
@@ -123,6 +123,42 @@
|
||||
"ERC20"
|
||||
]
|
||||
},
|
||||
"/home/oib/windsurf/aitbc/packages/solidity/aitbc-token/node_modules/@openzeppelin/contracts/utils/cryptography/ECDSA.sol": {
|
||||
"lastModificationDate": 1758948616491,
|
||||
"contentHash": "81de029d56aa803972be03c5d277cb6c",
|
||||
"sourceName": "@openzeppelin/contracts/utils/cryptography/ECDSA.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.24",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"evmVersion": "paris",
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers",
|
||||
"metadata"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imports": [],
|
||||
"versionPragmas": [
|
||||
"^0.8.20"
|
||||
],
|
||||
"artifacts": [
|
||||
"ECDSA"
|
||||
]
|
||||
},
|
||||
"/home/oib/windsurf/aitbc/packages/solidity/aitbc-token/node_modules/@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol": {
|
||||
"lastModificationDate": 1758948616595,
|
||||
"contentHash": "260f3968eefa3bbd30520cff5384cd93",
|
||||
@@ -161,42 +197,6 @@
|
||||
"MessageHashUtils"
|
||||
]
|
||||
},
|
||||
"/home/oib/windsurf/aitbc/packages/solidity/aitbc-token/node_modules/@openzeppelin/contracts/utils/cryptography/ECDSA.sol": {
|
||||
"lastModificationDate": 1758948616491,
|
||||
"contentHash": "81de029d56aa803972be03c5d277cb6c",
|
||||
"sourceName": "@openzeppelin/contracts/utils/cryptography/ECDSA.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.24",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"evmVersion": "paris",
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers",
|
||||
"metadata"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imports": [],
|
||||
"versionPragmas": [
|
||||
"^0.8.20"
|
||||
],
|
||||
"artifacts": [
|
||||
"ECDSA"
|
||||
]
|
||||
},
|
||||
"/home/oib/windsurf/aitbc/packages/solidity/aitbc-token/node_modules/@openzeppelin/contracts/access/IAccessControl.sol": {
|
||||
"lastModificationDate": 1758948616567,
|
||||
"contentHash": "def1e8f7b6cac577cf2600655bf3bdf8",
|
||||
|
||||
Reference in New Issue
Block a user