agentfield 0.1.22rc2__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.
Files changed (42) hide show
  1. agentfield/__init__.py +66 -0
  2. agentfield/agent.py +3569 -0
  3. agentfield/agent_ai.py +1125 -0
  4. agentfield/agent_cli.py +386 -0
  5. agentfield/agent_field_handler.py +494 -0
  6. agentfield/agent_mcp.py +534 -0
  7. agentfield/agent_registry.py +29 -0
  8. agentfield/agent_server.py +1185 -0
  9. agentfield/agent_utils.py +269 -0
  10. agentfield/agent_workflow.py +323 -0
  11. agentfield/async_config.py +278 -0
  12. agentfield/async_execution_manager.py +1227 -0
  13. agentfield/client.py +1447 -0
  14. agentfield/connection_manager.py +280 -0
  15. agentfield/decorators.py +527 -0
  16. agentfield/did_manager.py +337 -0
  17. agentfield/dynamic_skills.py +304 -0
  18. agentfield/execution_context.py +255 -0
  19. agentfield/execution_state.py +453 -0
  20. agentfield/http_connection_manager.py +429 -0
  21. agentfield/litellm_adapters.py +140 -0
  22. agentfield/logger.py +249 -0
  23. agentfield/mcp_client.py +204 -0
  24. agentfield/mcp_manager.py +340 -0
  25. agentfield/mcp_stdio_bridge.py +550 -0
  26. agentfield/memory.py +723 -0
  27. agentfield/memory_events.py +489 -0
  28. agentfield/multimodal.py +173 -0
  29. agentfield/multimodal_response.py +403 -0
  30. agentfield/pydantic_utils.py +227 -0
  31. agentfield/rate_limiter.py +280 -0
  32. agentfield/result_cache.py +441 -0
  33. agentfield/router.py +190 -0
  34. agentfield/status.py +70 -0
  35. agentfield/types.py +710 -0
  36. agentfield/utils.py +26 -0
  37. agentfield/vc_generator.py +464 -0
  38. agentfield/vision.py +198 -0
  39. agentfield-0.1.22rc2.dist-info/METADATA +102 -0
  40. agentfield-0.1.22rc2.dist-info/RECORD +42 -0
  41. agentfield-0.1.22rc2.dist-info/WHEEL +5 -0
  42. agentfield-0.1.22rc2.dist-info/top_level.txt +1 -0
agentfield/memory.py ADDED
@@ -0,0 +1,723 @@
1
+ """
2
+ Cross-Agent Persistent Memory Client for AgentField SDK.
3
+
4
+ This module provides the memory interface that enables seamless, automatic memory
5
+ sharing and synchronization across distributed agents.
6
+ """
7
+
8
+ import asyncio
9
+ import json
10
+ import sys
11
+ from functools import wraps
12
+ from typing import Any, Callable, Dict, List, Optional, Sequence, Union
13
+ from .client import AgentFieldClient
14
+ from .execution_context import ExecutionContext
15
+ from .memory_events import MemoryEventClient, ScopedMemoryEventClient
16
+
17
+
18
+ # Python 3.8 compatibility: asyncio.to_thread was added in Python 3.9
19
+ if sys.version_info >= (3, 9):
20
+ from asyncio import to_thread as _to_thread
21
+ else:
22
+ async def _to_thread(func, *args, **kwargs):
23
+ """Compatibility shim for asyncio.to_thread on Python 3.8."""
24
+ loop = asyncio.get_event_loop()
25
+ return await loop.run_in_executor(None, lambda: func(*args, **kwargs))
26
+
27
+
28
+ def _vector_to_list(values: Union[Sequence[float], Any]) -> List[float]:
29
+ """
30
+ Normalize numpy arrays, tuples, or other sequences to a plain float list.
31
+ """
32
+ if hasattr(values, "tolist"):
33
+ values = values.tolist()
34
+ return [float(x) for x in values] # type: ignore[arg-type]
35
+
36
+
37
+ class MemoryClient:
38
+ """
39
+ Core memory client that communicates with the AgentField server's memory API.
40
+
41
+ This client handles the low-level HTTP operations for memory management
42
+ and automatically includes execution context headers for proper scoping.
43
+ """
44
+
45
+ def __init__(
46
+ self,
47
+ agentfield_client: AgentFieldClient,
48
+ execution_context: ExecutionContext,
49
+ agent_node_id: Optional[str] = None,
50
+ ):
51
+ self.agentfield_client = agentfield_client
52
+ self.execution_context = execution_context
53
+ self.agent_node_id = agent_node_id
54
+
55
+ def _build_headers(
56
+ self, scope: Optional[str] = None, scope_id: Optional[str] = None
57
+ ) -> Dict[str, str]:
58
+ """Merge execution context headers with explicit scope overrides."""
59
+
60
+ headers = self.execution_context.to_headers()
61
+
62
+ if (not headers.get("X-Agent-Node-ID")) and self.agent_node_id:
63
+ headers["X-Agent-Node-ID"] = self.agent_node_id
64
+
65
+ if scope_id is not None:
66
+ header_name = {
67
+ "workflow": "X-Workflow-ID",
68
+ "session": "X-Session-ID",
69
+ "actor": "X-Actor-ID",
70
+ }.get(scope or "")
71
+
72
+ if header_name:
73
+ headers[header_name] = scope_id
74
+
75
+ return headers
76
+
77
+ async def _async_request(self, method: str, url: str, **kwargs):
78
+ """Internal helper to perform HTTP requests with graceful fallbacks."""
79
+ if hasattr(self.agentfield_client, "_async_request"):
80
+ return await self.agentfield_client._async_request(method, url, **kwargs)
81
+
82
+ try:
83
+ import httpx
84
+
85
+ async with httpx.AsyncClient() as client:
86
+ return await client.request(method, url, **kwargs)
87
+ except ImportError:
88
+ import requests
89
+
90
+ return await _to_thread(requests.request, method, url, **kwargs)
91
+
92
+ async def set(
93
+ self, key: str, data: Any, scope: Optional[str] = None, scope_id: Optional[str] = None
94
+ ) -> None:
95
+ """
96
+ Set a memory value with automatic scoping.
97
+
98
+ Args:
99
+ key: The memory key
100
+ data: The data to store (will be JSON serialized)
101
+ scope: Optional explicit scope override
102
+ """
103
+ from agentfield.logger import log_debug
104
+
105
+ headers = self._build_headers(scope, scope_id)
106
+
107
+ payload = {"key": key, "data": data}
108
+
109
+ if scope:
110
+ payload["scope"] = scope
111
+
112
+ # Test JSON serialization before sending
113
+ try:
114
+ json.dumps(payload)
115
+ log_debug(f"Memory set operation for key: {key}")
116
+ except Exception as json_error:
117
+ log_debug(
118
+ f"JSON serialization failed for memory key {key}: {type(json_error).__name__}: {json_error}"
119
+ )
120
+ raise
121
+
122
+ # Use synchronous requests to avoid event loop conflicts with AgentField SDK
123
+ url = f"{self.agentfield_client.api_base}/memory/set"
124
+
125
+ try:
126
+ if hasattr(self.agentfield_client, "_async_request"):
127
+ response = await self.agentfield_client._async_request(
128
+ "POST",
129
+ url,
130
+ json=payload,
131
+ headers=headers,
132
+ timeout=10.0,
133
+ )
134
+ else:
135
+ import requests
136
+
137
+ response = await _to_thread(
138
+ requests.post,
139
+ url,
140
+ json=payload,
141
+ headers=headers,
142
+ timeout=10.0,
143
+ )
144
+ response.raise_for_status()
145
+ log_debug(f"Memory set successful for key: {key}")
146
+ except Exception as e:
147
+ log_debug(f"Memory set failed for key {key}: {type(e).__name__}: {e}")
148
+ raise
149
+
150
+ async def set_vector(
151
+ self,
152
+ key: str,
153
+ embedding: Union[Sequence[float], Any],
154
+ metadata: Optional[Dict[str, Any]] = None,
155
+ scope: Optional[str] = None,
156
+ scope_id: Optional[str] = None,
157
+ ) -> None:
158
+ """
159
+ Store a vector embedding with optional metadata.
160
+ """
161
+ headers = self._build_headers(scope, scope_id)
162
+ payload: Dict[str, Any] = {
163
+ "key": key,
164
+ "embedding": _vector_to_list(embedding),
165
+ }
166
+ if metadata:
167
+ payload["metadata"] = metadata
168
+ if scope:
169
+ payload["scope"] = scope
170
+
171
+ response = await self._async_request(
172
+ "POST",
173
+ f"{self.agentfield_client.api_base}/memory/vector/set",
174
+ json=payload,
175
+ headers=headers,
176
+ timeout=15.0,
177
+ )
178
+ response.raise_for_status()
179
+
180
+ async def get(
181
+ self,
182
+ key: str,
183
+ default: Any = None,
184
+ scope: Optional[str] = None,
185
+ scope_id: Optional[str] = None,
186
+ ) -> Any:
187
+ """
188
+ Get a memory value with hierarchical lookup.
189
+
190
+ Args:
191
+ key: The memory key
192
+ default: Default value if key not found
193
+ scope: Optional explicit scope override
194
+
195
+ Returns:
196
+ The stored value or default if not found
197
+ """
198
+ headers = self._build_headers(scope, scope_id)
199
+
200
+ payload = {"key": key}
201
+
202
+ if scope:
203
+ payload["scope"] = scope
204
+
205
+ response = await self._async_request(
206
+ "POST",
207
+ f"{self.agentfield_client.api_base}/memory/get",
208
+ json=payload,
209
+ headers=headers,
210
+ timeout=10.0,
211
+ )
212
+
213
+ if response.status_code == 404:
214
+ return default
215
+
216
+ response.raise_for_status()
217
+ result = response.json()
218
+
219
+ # Extract the actual data from the memory response
220
+ if isinstance(result, dict) and "data" in result:
221
+ # The server returns JSON-encoded data, so we need to decode it
222
+ data = result["data"]
223
+ if isinstance(data, str):
224
+ try:
225
+ return json.loads(data)
226
+ except json.JSONDecodeError:
227
+ return data
228
+ return data
229
+
230
+ return result
231
+
232
+ async def exists(
233
+ self, key: str, scope: Optional[str] = None, scope_id: Optional[str] = None
234
+ ) -> bool:
235
+ """
236
+ Check if a memory key exists.
237
+
238
+ Args:
239
+ key: The memory key
240
+ scope: Optional explicit scope override
241
+
242
+ Returns:
243
+ True if key exists, False otherwise
244
+ """
245
+ try:
246
+ await self.get(key, scope=scope, scope_id=scope_id)
247
+ return True
248
+ except Exception:
249
+ return False
250
+
251
+ async def delete(
252
+ self, key: str, scope: Optional[str] = None, scope_id: Optional[str] = None
253
+ ) -> None:
254
+ """
255
+ Delete a memory value.
256
+
257
+ Args:
258
+ key: The memory key
259
+ scope: Optional explicit scope override
260
+ """
261
+ headers = self._build_headers(scope, scope_id)
262
+
263
+ payload = {"key": key}
264
+
265
+ if scope:
266
+ payload["scope"] = scope
267
+
268
+ response = await self._async_request(
269
+ "POST",
270
+ f"{self.agentfield_client.api_base}/memory/delete",
271
+ json=payload,
272
+ headers=headers,
273
+ timeout=10.0,
274
+ )
275
+ response.raise_for_status()
276
+
277
+ async def delete_vector(
278
+ self, key: str, scope: Optional[str] = None, scope_id: Optional[str] = None
279
+ ) -> None:
280
+ """
281
+ Delete a stored vector embedding.
282
+ """
283
+ headers = self._build_headers(scope, scope_id)
284
+ payload: Dict[str, Any] = {"key": key}
285
+ if scope:
286
+ payload["scope"] = scope
287
+ response = await self._async_request(
288
+ "POST",
289
+ f"{self.agentfield_client.api_base}/memory/vector/delete",
290
+ json=payload,
291
+ headers=headers,
292
+ timeout=10.0,
293
+ )
294
+ response.raise_for_status()
295
+
296
+ async def list_keys(
297
+ self, scope: str, scope_id: Optional[str] = None
298
+ ) -> List[str]:
299
+ """
300
+ List all keys in a specific scope.
301
+
302
+ Args:
303
+ scope: The scope to list keys from
304
+
305
+ Returns:
306
+ List of memory keys in the scope
307
+ """
308
+ headers = self._build_headers(scope, scope_id)
309
+
310
+ response = await self._async_request(
311
+ "GET",
312
+ f"{self.agentfield_client.api_base}/memory/list",
313
+ params={"scope": scope},
314
+ headers=headers,
315
+ timeout=10.0,
316
+ )
317
+ response.raise_for_status()
318
+ result = response.json()
319
+
320
+ # Extract keys from the memory list response
321
+ if isinstance(result, list):
322
+ return [item.get("key", "") for item in result if "key" in item]
323
+
324
+ return []
325
+
326
+ async def similarity_search(
327
+ self,
328
+ query_embedding: Union[Sequence[float], Any],
329
+ top_k: int = 10,
330
+ scope: Optional[str] = None,
331
+ scope_id: Optional[str] = None,
332
+ filters: Optional[Dict[str, Any]] = None,
333
+ ) -> List[Dict[str, Any]]:
334
+ """
335
+ Perform a similarity search against stored vectors.
336
+ """
337
+ headers = self._build_headers(scope, scope_id)
338
+ payload: Dict[str, Any] = {
339
+ "query_embedding": _vector_to_list(query_embedding),
340
+ "top_k": top_k,
341
+ "filters": filters or {},
342
+ }
343
+ if scope:
344
+ payload["scope"] = scope
345
+
346
+ response = await self._async_request(
347
+ "POST",
348
+ f"{self.agentfield_client.api_base}/memory/vector/search",
349
+ json=payload,
350
+ headers=headers,
351
+ timeout=15.0,
352
+ )
353
+ response.raise_for_status()
354
+ return response.json()
355
+
356
+
357
+ class ScopedMemoryClient:
358
+ """
359
+ Memory client that operates within a specific scope.
360
+
361
+ This provides a scoped view of memory operations, automatically
362
+ using the specified scope for all operations.
363
+ """
364
+
365
+ def __init__(
366
+ self,
367
+ memory_client: MemoryClient,
368
+ scope: str,
369
+ scope_id: str,
370
+ event_client: Optional[MemoryEventClient] = None,
371
+ ):
372
+ self.memory_client = memory_client
373
+ self.scope = scope
374
+ self.scope_id = scope_id
375
+ self.events = (
376
+ ScopedMemoryEventClient(event_client, scope, scope_id)
377
+ if event_client
378
+ else None
379
+ )
380
+
381
+ async def set(self, key: str, data: Any) -> None:
382
+ """Set a value in this specific scope."""
383
+ await self.memory_client.set(
384
+ key, data, scope=self.scope, scope_id=self.scope_id
385
+ )
386
+
387
+ async def get(self, key: str, default: Any = None) -> Any:
388
+ """Get a value from this specific scope."""
389
+ return await self.memory_client.get(
390
+ key, default=default, scope=self.scope, scope_id=self.scope_id
391
+ )
392
+
393
+ async def exists(self, key: str) -> bool:
394
+ """Check if a key exists in this specific scope."""
395
+ return await self.memory_client.exists(
396
+ key, scope=self.scope, scope_id=self.scope_id
397
+ )
398
+
399
+ async def delete(self, key: str) -> None:
400
+ """Delete a value from this specific scope."""
401
+ await self.memory_client.delete(
402
+ key, scope=self.scope, scope_id=self.scope_id
403
+ )
404
+
405
+ async def list_keys(self) -> List[str]:
406
+ """List all keys in this specific scope."""
407
+ return await self.memory_client.list_keys(self.scope, scope_id=self.scope_id)
408
+
409
+ async def set_vector(
410
+ self,
411
+ key: str,
412
+ embedding: Union[Sequence[float], Any],
413
+ metadata: Optional[Dict[str, Any]] = None,
414
+ ) -> None:
415
+ """Store a vector within this scope."""
416
+ await self.memory_client.set_vector(
417
+ key,
418
+ embedding,
419
+ metadata=metadata,
420
+ scope=self.scope,
421
+ scope_id=self.scope_id,
422
+ )
423
+
424
+ async def delete_vector(self, key: str) -> None:
425
+ """Delete a vector within this scope."""
426
+ await self.memory_client.delete_vector(
427
+ key, scope=self.scope, scope_id=self.scope_id
428
+ )
429
+
430
+ async def similarity_search(
431
+ self,
432
+ query_embedding: Union[Sequence[float], Any],
433
+ top_k: int = 10,
434
+ filters: Optional[Dict[str, Any]] = None,
435
+ ) -> List[Dict[str, Any]]:
436
+ """Search vectors within this scope."""
437
+ return await self.memory_client.similarity_search(
438
+ query_embedding,
439
+ top_k=top_k,
440
+ scope=self.scope,
441
+ scope_id=self.scope_id,
442
+ filters=filters,
443
+ )
444
+
445
+ def on_change(self, patterns: Union[str, List[str]]):
446
+ """
447
+ Decorator for subscribing to memory change events in this scope.
448
+
449
+ Args:
450
+ patterns: Pattern(s) to match against memory keys
451
+
452
+ Returns:
453
+ Decorator function
454
+ """
455
+ if self.events:
456
+ return self.events.on_change(patterns)
457
+ else:
458
+ # Return a no-op decorator if events are not available
459
+ def decorator(func):
460
+ return func
461
+
462
+ return decorator
463
+
464
+
465
+ class GlobalMemoryClient:
466
+ """
467
+ Memory client for global scope operations.
468
+
469
+ This provides access to the global memory scope that is shared
470
+ across all agents and sessions.
471
+ """
472
+
473
+ def __init__(
474
+ self,
475
+ memory_client: MemoryClient,
476
+ event_client: Optional[MemoryEventClient] = None,
477
+ ):
478
+ self.memory_client = memory_client
479
+ self.event_client = event_client
480
+
481
+ async def set(self, key: str, data: Any) -> None:
482
+ """Set a value in global scope."""
483
+ await self.memory_client.set(key, data, scope="global")
484
+
485
+ async def get(self, key: str, default: Any = None) -> Any:
486
+ """Get a value from global scope."""
487
+ return await self.memory_client.get(key, default=default, scope="global")
488
+
489
+ async def exists(self, key: str) -> bool:
490
+ """Check if a key exists in global scope."""
491
+ return await self.memory_client.exists(key, scope="global")
492
+
493
+ async def delete(self, key: str) -> None:
494
+ """Delete a value from global scope."""
495
+ await self.memory_client.delete(key, scope="global")
496
+
497
+ async def list_keys(self) -> List[str]:
498
+ """List all keys in global scope."""
499
+ return await self.memory_client.list_keys("global")
500
+
501
+ async def set_vector(
502
+ self,
503
+ key: str,
504
+ embedding: Union[Sequence[float], Any],
505
+ metadata: Optional[Dict[str, Any]] = None,
506
+ ) -> None:
507
+ """Store a vector in global scope."""
508
+ await self.memory_client.set_vector(
509
+ key, embedding, metadata=metadata, scope="global"
510
+ )
511
+
512
+ async def delete_vector(self, key: str) -> None:
513
+ """Delete a vector in global scope."""
514
+ await self.memory_client.delete_vector(key, scope="global")
515
+
516
+ async def similarity_search(
517
+ self,
518
+ query_embedding: Union[Sequence[float], Any],
519
+ top_k: int = 10,
520
+ filters: Optional[Dict[str, Any]] = None,
521
+ ) -> List[Dict[str, Any]]:
522
+ """Search vectors in global scope."""
523
+ return await self.memory_client.similarity_search(
524
+ query_embedding, top_k=top_k, scope="global", filters=filters
525
+ )
526
+
527
+ def on_change(self, patterns: Union[str, List[str]]) -> Callable:
528
+ """
529
+ Decorator for subscribing to global-scope memory change events.
530
+
531
+ Args:
532
+ patterns: Pattern(s) to match against memory keys
533
+
534
+ Returns:
535
+ Decorator function
536
+ """
537
+
538
+ if not self.event_client:
539
+ # No event client available (e.g., during unit tests) — return no-op decorator
540
+ def decorator(func: Callable) -> Callable:
541
+ return func
542
+
543
+ return decorator
544
+
545
+ def decorator(func: Callable) -> Callable:
546
+ @wraps(func)
547
+ async def wrapper(event):
548
+ return await func(event)
549
+
550
+ self.event_client.subscribe(
551
+ patterns,
552
+ wrapper,
553
+ scope="global",
554
+ scope_id=None,
555
+ )
556
+
557
+ setattr(wrapper, "_memory_event_listener", True)
558
+ setattr(
559
+ wrapper,
560
+ "_memory_event_patterns",
561
+ patterns if isinstance(patterns, list) else [patterns],
562
+ )
563
+ setattr(wrapper, "_memory_event_scope", "global")
564
+ setattr(wrapper, "_memory_event_scope_id", None)
565
+
566
+ return wrapper
567
+
568
+ return decorator
569
+
570
+
571
+ class MemoryInterface:
572
+ """
573
+ Developer-facing memory interface that provides the intuitive app.memory API.
574
+
575
+ This class provides the main interface that developers interact with,
576
+ offering automatic scoping, hierarchical lookup, and explicit scope access.
577
+ """
578
+
579
+ def __init__(self, memory_client: MemoryClient, event_client: MemoryEventClient):
580
+ self.memory_client = memory_client
581
+ self.events = event_client
582
+
583
+ async def set(self, key: str, data: Any) -> None:
584
+ """
585
+ Set a memory value with automatic scoping.
586
+
587
+ The value will be stored in the most specific available scope
588
+ based on the current execution context.
589
+
590
+ Args:
591
+ key: The memory key
592
+ data: The data to store
593
+ """
594
+ await self.memory_client.set(key, data)
595
+
596
+ async def set_vector(
597
+ self,
598
+ key: str,
599
+ embedding: Union[Sequence[float], Any],
600
+ metadata: Optional[Dict[str, Any]] = None,
601
+ ) -> None:
602
+ """
603
+ Store a vector embedding with automatic scoping.
604
+ """
605
+ await self.memory_client.set_vector(key, embedding, metadata=metadata)
606
+
607
+ async def get(self, key: str, default: Any = None) -> Any:
608
+ """
609
+ Get a memory value with hierarchical lookup.
610
+
611
+ This will search through scopes in order: workflow -> session -> actor -> global
612
+ and return the first match found.
613
+
614
+ Args:
615
+ key: The memory key
616
+ default: Default value if key not found in any scope
617
+
618
+ Returns:
619
+ The stored value or default if not found
620
+ """
621
+ return await self.memory_client.get(key, default=default)
622
+
623
+ async def exists(self, key: str) -> bool:
624
+ """
625
+ Check if a memory key exists in any scope.
626
+
627
+ Args:
628
+ key: The memory key
629
+
630
+ Returns:
631
+ True if key exists in any scope, False otherwise
632
+ """
633
+ return await self.memory_client.exists(key)
634
+
635
+ async def delete(self, key: str) -> None:
636
+ """
637
+ Delete a memory value from the current scope.
638
+
639
+ Args:
640
+ key: The memory key
641
+ """
642
+ await self.memory_client.delete(key)
643
+
644
+ async def delete_vector(self, key: str) -> None:
645
+ """
646
+ Delete a vector embedding from the current scope.
647
+ """
648
+ await self.memory_client.delete_vector(key)
649
+
650
+ async def similarity_search(
651
+ self,
652
+ query_embedding: Union[Sequence[float], Any],
653
+ top_k: int = 10,
654
+ filters: Optional[Dict[str, Any]] = None,
655
+ ) -> List[Dict[str, Any]]:
656
+ """
657
+ Search stored vectors using similarity matching.
658
+ """
659
+ return await self.memory_client.similarity_search(
660
+ query_embedding, top_k=top_k, filters=filters
661
+ )
662
+
663
+ def on_change(self, patterns: Union[str, List[str]]):
664
+ """
665
+ Decorator for subscribing to memory change events.
666
+
667
+ Args:
668
+ patterns: Pattern(s) to match against memory keys
669
+
670
+ Returns:
671
+ Decorator function
672
+ """
673
+ return self.events.on_change(patterns)
674
+
675
+ def session(self, session_id: str) -> ScopedMemoryClient:
676
+ """
677
+ Get a memory client scoped to a specific session.
678
+
679
+ Args:
680
+ session_id: The session ID to scope to
681
+
682
+ Returns:
683
+ ScopedMemoryClient for the specified session
684
+ """
685
+ return ScopedMemoryClient(
686
+ self.memory_client, "session", session_id, self.events
687
+ )
688
+
689
+ def actor(self, actor_id: str) -> ScopedMemoryClient:
690
+ """
691
+ Get a memory client scoped to a specific actor.
692
+
693
+ Args:
694
+ actor_id: The actor ID to scope to
695
+
696
+ Returns:
697
+ ScopedMemoryClient for the specified actor
698
+ """
699
+ return ScopedMemoryClient(self.memory_client, "actor", actor_id, self.events)
700
+
701
+ def workflow(self, workflow_id: str) -> ScopedMemoryClient:
702
+ """
703
+ Get a memory client scoped to a specific workflow.
704
+
705
+ Args:
706
+ workflow_id: The workflow ID to scope to
707
+
708
+ Returns:
709
+ ScopedMemoryClient for the specified workflow
710
+ """
711
+ return ScopedMemoryClient(
712
+ self.memory_client, "workflow", workflow_id, self.events
713
+ )
714
+
715
+ @property
716
+ def global_scope(self) -> GlobalMemoryClient:
717
+ """
718
+ Get a memory client for global scope operations.
719
+
720
+ Returns:
721
+ GlobalMemoryClient for global scope access
722
+ """
723
+ return GlobalMemoryClient(self.memory_client, self.events)