agentmesh-platform 1.0.0a1__py3-none-any.whl → 1.0.0a2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,231 @@
1
+ """
2
+ Abstract Storage Provider Interface.
3
+
4
+ Defines the contract that all storage backends must implement.
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+ from typing import Any, Optional
9
+ from pydantic import BaseModel, Field
10
+
11
+
12
+ class StorageConfig(BaseModel):
13
+ """Configuration for storage provider."""
14
+
15
+ backend: str = Field(default="memory", description="Storage backend type")
16
+ connection_string: Optional[str] = Field(default=None, description="Connection string")
17
+ pool_size: int = Field(default=10, ge=1, le=100, description="Connection pool size")
18
+ timeout_seconds: int = Field(default=30, ge=1, le=300, description="Operation timeout")
19
+
20
+ # Redis-specific
21
+ redis_host: Optional[str] = Field(default="localhost")
22
+ redis_port: int = Field(default=6379, ge=1, le=65535)
23
+ redis_db: int = Field(default=0, ge=0)
24
+ redis_password: Optional[str] = None
25
+ redis_ssl: bool = False
26
+
27
+ # PostgreSQL-specific
28
+ postgres_host: Optional[str] = Field(default="localhost")
29
+ postgres_port: int = Field(default=5432, ge=1, le=65535)
30
+ postgres_database: Optional[str] = Field(default="agentmesh")
31
+ postgres_user: Optional[str] = None
32
+ postgres_password: Optional[str] = None
33
+ postgres_ssl_mode: str = Field(default="prefer")
34
+
35
+ # Cache settings
36
+ cache_ttl_seconds: int = Field(default=300, ge=0)
37
+ enable_cache: bool = True
38
+
39
+
40
+ class AbstractStorageProvider(ABC):
41
+ """
42
+ Abstract storage provider.
43
+
44
+ All storage backends (Redis, Postgres, etc.) must implement this interface.
45
+ Supports:
46
+ - Key-value operations
47
+ - Hash operations (for structured data)
48
+ - List operations (for audit logs, events)
49
+ - TTL support
50
+ - Async operations
51
+ """
52
+
53
+ def __init__(self, config: StorageConfig):
54
+ """Initialize storage provider with configuration."""
55
+ self.config = config
56
+
57
+ @abstractmethod
58
+ async def connect(self) -> None:
59
+ """Establish connection to storage backend."""
60
+ pass
61
+
62
+ @abstractmethod
63
+ async def disconnect(self) -> None:
64
+ """Close connection to storage backend."""
65
+ pass
66
+
67
+ @abstractmethod
68
+ async def health_check(self) -> bool:
69
+ """Check if storage backend is healthy."""
70
+ pass
71
+
72
+ # Key-Value Operations
73
+
74
+ @abstractmethod
75
+ async def get(self, key: str) -> Optional[str]:
76
+ """Get value by key."""
77
+ pass
78
+
79
+ @abstractmethod
80
+ async def set(
81
+ self,
82
+ key: str,
83
+ value: str,
84
+ ttl_seconds: Optional[int] = None,
85
+ ) -> bool:
86
+ """Set value with optional TTL."""
87
+ pass
88
+
89
+ @abstractmethod
90
+ async def delete(self, key: str) -> bool:
91
+ """Delete key."""
92
+ pass
93
+
94
+ @abstractmethod
95
+ async def exists(self, key: str) -> bool:
96
+ """Check if key exists."""
97
+ pass
98
+
99
+ # Hash Operations (for structured data)
100
+
101
+ @abstractmethod
102
+ async def hget(self, key: str, field: str) -> Optional[str]:
103
+ """Get hash field value."""
104
+ pass
105
+
106
+ @abstractmethod
107
+ async def hset(self, key: str, field: str, value: str) -> bool:
108
+ """Set hash field value."""
109
+ pass
110
+
111
+ @abstractmethod
112
+ async def hgetall(self, key: str) -> dict[str, str]:
113
+ """Get all hash fields."""
114
+ pass
115
+
116
+ @abstractmethod
117
+ async def hdel(self, key: str, field: str) -> bool:
118
+ """Delete hash field."""
119
+ pass
120
+
121
+ @abstractmethod
122
+ async def hkeys(self, key: str) -> list[str]:
123
+ """Get all hash field names."""
124
+ pass
125
+
126
+ # List Operations (for audit logs, events)
127
+
128
+ @abstractmethod
129
+ async def lpush(self, key: str, value: str) -> int:
130
+ """Push value to head of list. Returns new list length."""
131
+ pass
132
+
133
+ @abstractmethod
134
+ async def rpush(self, key: str, value: str) -> int:
135
+ """Push value to tail of list. Returns new list length."""
136
+ pass
137
+
138
+ @abstractmethod
139
+ async def lrange(self, key: str, start: int, stop: int) -> list[str]:
140
+ """Get list range [start, stop]."""
141
+ pass
142
+
143
+ @abstractmethod
144
+ async def llen(self, key: str) -> int:
145
+ """Get list length."""
146
+ pass
147
+
148
+ # Sorted Set Operations (for trust scores)
149
+
150
+ @abstractmethod
151
+ async def zadd(
152
+ self,
153
+ key: str,
154
+ score: float,
155
+ member: str,
156
+ ) -> bool:
157
+ """Add member to sorted set with score."""
158
+ pass
159
+
160
+ @abstractmethod
161
+ async def zscore(self, key: str, member: str) -> Optional[float]:
162
+ """Get score of member in sorted set."""
163
+ pass
164
+
165
+ @abstractmethod
166
+ async def zrange(
167
+ self,
168
+ key: str,
169
+ start: int,
170
+ stop: int,
171
+ with_scores: bool = False,
172
+ ) -> list[str] | list[tuple[str, float]]:
173
+ """Get sorted set range."""
174
+ pass
175
+
176
+ @abstractmethod
177
+ async def zrangebyscore(
178
+ self,
179
+ key: str,
180
+ min_score: float,
181
+ max_score: float,
182
+ with_scores: bool = False,
183
+ ) -> list[str] | list[tuple[str, float]]:
184
+ """Get sorted set range by score."""
185
+ pass
186
+
187
+ # Atomic Operations
188
+
189
+ @abstractmethod
190
+ async def incr(self, key: str) -> int:
191
+ """Increment value atomically. Returns new value."""
192
+ pass
193
+
194
+ @abstractmethod
195
+ async def decr(self, key: str) -> int:
196
+ """Decrement value atomically. Returns new value."""
197
+ pass
198
+
199
+ @abstractmethod
200
+ async def incrby(self, key: str, amount: int) -> int:
201
+ """Increment value by amount. Returns new value."""
202
+ pass
203
+
204
+ # Batch Operations
205
+
206
+ @abstractmethod
207
+ async def mget(self, keys: list[str]) -> list[Optional[str]]:
208
+ """Get multiple values."""
209
+ pass
210
+
211
+ @abstractmethod
212
+ async def mset(self, mapping: dict[str, str]) -> bool:
213
+ """Set multiple key-value pairs."""
214
+ pass
215
+
216
+ # Pattern Operations
217
+
218
+ @abstractmethod
219
+ async def keys(self, pattern: str) -> list[str]:
220
+ """Get keys matching pattern."""
221
+ pass
222
+
223
+ @abstractmethod
224
+ async def scan(
225
+ self,
226
+ cursor: int = 0,
227
+ match: Optional[str] = None,
228
+ count: int = 100,
229
+ ) -> tuple[int, list[str]]:
230
+ """Scan keys with cursor. Returns (new_cursor, keys)."""
231
+ pass
@@ -0,0 +1,223 @@
1
+ """
2
+ Redis Storage Provider.
3
+
4
+ Production-ready Redis backend with connection pooling and error handling.
5
+ """
6
+
7
+ from typing import Optional
8
+ import json
9
+
10
+ from .provider import AbstractStorageProvider, StorageConfig
11
+
12
+
13
+ class RedisStorageProvider(AbstractStorageProvider):
14
+ """
15
+ Redis storage provider.
16
+
17
+ Features:
18
+ - Connection pooling
19
+ - Automatic reconnection
20
+ - TTL support
21
+ - High-performance caching
22
+
23
+ Requires: redis[asyncio] package
24
+ """
25
+
26
+ def __init__(self, config: StorageConfig):
27
+ """Initialize Redis storage."""
28
+ super().__init__(config)
29
+ self._client = None
30
+ self._pool = None
31
+
32
+ async def connect(self) -> None:
33
+ """Establish connection to Redis."""
34
+ try:
35
+ import redis.asyncio as aioredis
36
+ except ImportError:
37
+ raise ImportError(
38
+ "redis package is required for RedisStorageProvider. "
39
+ "Install with: pip install redis[asyncio]"
40
+ )
41
+
42
+ # Create connection pool
43
+ self._pool = aioredis.ConnectionPool(
44
+ host=self.config.redis_host,
45
+ port=self.config.redis_port,
46
+ db=self.config.redis_db,
47
+ password=self.config.redis_password,
48
+ ssl=self.config.redis_ssl,
49
+ max_connections=self.config.pool_size,
50
+ socket_timeout=self.config.timeout_seconds,
51
+ socket_connect_timeout=self.config.timeout_seconds,
52
+ decode_responses=True,
53
+ )
54
+
55
+ self._client = aioredis.Redis(connection_pool=self._pool)
56
+
57
+ # Test connection
58
+ await self._client.ping()
59
+
60
+ async def disconnect(self) -> None:
61
+ """Close connection to Redis."""
62
+ if self._client:
63
+ await self._client.close()
64
+ if self._pool:
65
+ await self._pool.disconnect()
66
+
67
+ async def health_check(self) -> bool:
68
+ """Check if Redis is healthy."""
69
+ try:
70
+ if self._client:
71
+ await self._client.ping()
72
+ return True
73
+ except Exception:
74
+ pass
75
+ return False
76
+
77
+ # Key-Value Operations
78
+
79
+ async def get(self, key: str) -> Optional[str]:
80
+ """Get value by key."""
81
+ return await self._client.get(key)
82
+
83
+ async def set(
84
+ self,
85
+ key: str,
86
+ value: str,
87
+ ttl_seconds: Optional[int] = None,
88
+ ) -> bool:
89
+ """Set value with optional TTL."""
90
+ if ttl_seconds is not None:
91
+ return await self._client.setex(key, ttl_seconds, value)
92
+ return await self._client.set(key, value)
93
+
94
+ async def delete(self, key: str) -> bool:
95
+ """Delete key."""
96
+ result = await self._client.delete(key)
97
+ return result > 0
98
+
99
+ async def exists(self, key: str) -> bool:
100
+ """Check if key exists."""
101
+ result = await self._client.exists(key)
102
+ return result > 0
103
+
104
+ # Hash Operations
105
+
106
+ async def hget(self, key: str, field: str) -> Optional[str]:
107
+ """Get hash field value."""
108
+ return await self._client.hget(key, field)
109
+
110
+ async def hset(self, key: str, field: str, value: str) -> bool:
111
+ """Set hash field value."""
112
+ result = await self._client.hset(key, field, value)
113
+ return result >= 0
114
+
115
+ async def hgetall(self, key: str) -> dict[str, str]:
116
+ """Get all hash fields."""
117
+ return await self._client.hgetall(key)
118
+
119
+ async def hdel(self, key: str, field: str) -> bool:
120
+ """Delete hash field."""
121
+ result = await self._client.hdel(key, field)
122
+ return result > 0
123
+
124
+ async def hkeys(self, key: str) -> list[str]:
125
+ """Get all hash field names."""
126
+ return await self._client.hkeys(key)
127
+
128
+ # List Operations
129
+
130
+ async def lpush(self, key: str, value: str) -> int:
131
+ """Push value to head of list."""
132
+ return await self._client.lpush(key, value)
133
+
134
+ async def rpush(self, key: str, value: str) -> int:
135
+ """Push value to tail of list."""
136
+ return await self._client.rpush(key, value)
137
+
138
+ async def lrange(self, key: str, start: int, stop: int) -> list[str]:
139
+ """Get list range [start, stop]."""
140
+ return await self._client.lrange(key, start, stop)
141
+
142
+ async def llen(self, key: str) -> int:
143
+ """Get list length."""
144
+ return await self._client.llen(key)
145
+
146
+ # Sorted Set Operations
147
+
148
+ async def zadd(self, key: str, score: float, member: str) -> bool:
149
+ """Add member to sorted set with score."""
150
+ result = await self._client.zadd(key, {member: score})
151
+ return result >= 0
152
+
153
+ async def zscore(self, key: str, member: str) -> Optional[float]:
154
+ """Get score of member in sorted set."""
155
+ return await self._client.zscore(key, member)
156
+
157
+ async def zrange(
158
+ self,
159
+ key: str,
160
+ start: int,
161
+ stop: int,
162
+ with_scores: bool = False,
163
+ ) -> list[str] | list[tuple[str, float]]:
164
+ """Get sorted set range."""
165
+ return await self._client.zrange(
166
+ key, start, stop, withscores=with_scores
167
+ )
168
+
169
+ async def zrangebyscore(
170
+ self,
171
+ key: str,
172
+ min_score: float,
173
+ max_score: float,
174
+ with_scores: bool = False,
175
+ ) -> list[str] | list[tuple[str, float]]:
176
+ """Get sorted set range by score."""
177
+ return await self._client.zrangebyscore(
178
+ key, min_score, max_score, withscores=with_scores
179
+ )
180
+
181
+ # Atomic Operations
182
+
183
+ async def incr(self, key: str) -> int:
184
+ """Increment value atomically."""
185
+ return await self._client.incr(key)
186
+
187
+ async def decr(self, key: str) -> int:
188
+ """Decrement value atomically."""
189
+ return await self._client.decr(key)
190
+
191
+ async def incrby(self, key: str, amount: int) -> int:
192
+ """Increment value by amount."""
193
+ return await self._client.incrby(key, amount)
194
+
195
+ # Batch Operations
196
+
197
+ async def mget(self, keys: list[str]) -> list[Optional[str]]:
198
+ """Get multiple values."""
199
+ return await self._client.mget(keys)
200
+
201
+ async def mset(self, mapping: dict[str, str]) -> bool:
202
+ """Set multiple key-value pairs."""
203
+ return await self._client.mset(mapping)
204
+
205
+ # Pattern Operations
206
+
207
+ async def keys(self, pattern: str) -> list[str]:
208
+ """Get keys matching pattern."""
209
+ return await self._client.keys(pattern)
210
+
211
+ async def scan(
212
+ self,
213
+ cursor: int = 0,
214
+ match: Optional[str] = None,
215
+ count: int = 100,
216
+ ) -> tuple[int, list[str]]:
217
+ """Scan keys with cursor."""
218
+ new_cursor, keys = await self._client.scan(
219
+ cursor=cursor,
220
+ match=match,
221
+ count=count,
222
+ )
223
+ return new_cursor, keys
@@ -7,7 +7,7 @@ Native A2A and MCP support with transparent protocol translation.
7
7
 
8
8
  from .bridge import TrustBridge, ProtocolBridge
9
9
  from .handshake import TrustHandshake, HandshakeResult
10
- from .capability import CapabilityScope, CapabilityGrant
10
+ from .capability import CapabilityScope, CapabilityGrant, CapabilityRegistry
11
11
 
12
12
  __all__ = [
13
13
  "TrustBridge",
@@ -16,4 +16,5 @@ __all__ = [
16
16
  "HandshakeResult",
17
17
  "CapabilityScope",
18
18
  "CapabilityGrant",
19
+ "CapabilityRegistry",
19
20
  ]
agentmesh/trust/bridge.py CHANGED
@@ -246,6 +246,43 @@ class ProtocolBridge(BaseModel):
246
246
  "parameters": params.get("arguments", {}),
247
247
  }
248
248
 
249
+ def add_verification_footer(
250
+ self,
251
+ content: str,
252
+ trust_score: int,
253
+ agent_did: str,
254
+ metadata: Optional[dict] = None
255
+ ) -> str:
256
+ """
257
+ Add AgentMesh verification footer to content.
258
+
259
+ This creates the "viral" verification message that shows
260
+ AgentMesh is securing the interaction.
261
+
262
+ Args:
263
+ content: Original content
264
+ trust_score: Current trust score
265
+ agent_did: Agent DID
266
+ metadata: Optional metadata to include
267
+
268
+ Returns:
269
+ Content with verification footer appended
270
+ """
271
+ footer = (
272
+ f"\n\n> 🔒 Verified by AgentMesh (Trust Score: {trust_score}/1000)\n"
273
+ f"> Agent: {agent_did[:40]}...\n"
274
+ )
275
+
276
+ if metadata:
277
+ if "policy" in metadata:
278
+ footer += f"> Policy: {metadata['policy']}\n"
279
+ if "audit" in metadata:
280
+ footer += f"> Audit: {metadata['audit']}\n"
281
+ if "view_log" in metadata:
282
+ footer += f"> [View Audit Log]({metadata['view_log']})\n"
283
+
284
+ return content + footer
285
+
249
286
  async def _send(self, peer_did: str, message: Any, protocol: str) -> Any:
250
287
  """Send message via protocol handler."""
251
288
  # In production, would dispatch to actual protocol handlers