aip-agents-binary 0.5.10__py3-none-macosx_13_0_arm64.whl → 0.5.12__py3-none-macosx_13_0_arm64.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.
@@ -255,6 +255,7 @@ class LangGraphA2AExecutor(BaseA2AExecutor, ABC):
255
255
  return []
256
256
 
257
257
  normalized_files: list[str | dict[str, Any]] = []
258
+ invalid_entry_logged = False
258
259
  for entry in raw_files:
259
260
  if isinstance(entry, str) and entry:
260
261
  normalized_files.append(entry)
@@ -262,6 +263,8 @@ class LangGraphA2AExecutor(BaseA2AExecutor, ABC):
262
263
  if isinstance(entry, dict):
263
264
  normalized_files.append(entry)
264
265
  continue
265
- logger.warning("Invalid file metadata entry received; expected string or dict.")
266
+ if not invalid_entry_logged:
267
+ logger.warning("Invalid file metadata entry received; expected string or dict.")
268
+ invalid_entry_logged = True
266
269
 
267
270
  return normalized_files
@@ -15,6 +15,7 @@ from typing import Any
15
15
 
16
16
  from aip_agents.agent.hitl.config import ToolApprovalConfig
17
17
  from aip_agents.agent.hitl.prompt import BasePromptHandler, DeferredPromptHandler
18
+ from aip_agents.agent.hitl.registry import hitl_registry
18
19
  from aip_agents.schema.hitl import ApprovalDecision, ApprovalDecisionType, ApprovalLogEntry, ApprovalRequest
19
20
 
20
21
  # Constants
@@ -197,6 +198,9 @@ class ApprovalManager:
197
198
 
198
199
  self._active_requests[request.request_id] = request
199
200
 
201
+ # Register ownership in global registry for hierarchical routing
202
+ hitl_registry.register(request.request_id, self)
203
+
200
204
  def get_pending_request(self, request_id: str) -> ApprovalRequest | None:
201
205
  """Get a pending approval request by ID.
202
206
 
@@ -312,6 +316,10 @@ class ApprovalManager:
312
316
  decision.latency_ms = int(latency.total_seconds() * 1000)
313
317
 
314
318
  self._active_requests.pop(request.request_id, None)
319
+
320
+ # Unregister from global registry when finalized
321
+ hitl_registry.unregister(request.request_id)
322
+
315
323
  return decision
316
324
 
317
325
  async def prompt_for_decision(
@@ -459,6 +467,8 @@ class ApprovalManager:
459
467
 
460
468
  for request_id in expired_ids:
461
469
  self._active_requests.pop(request_id, None)
470
+ # Unregister expired requests from global registry
471
+ hitl_registry.unregister(request_id)
462
472
 
463
473
  return len(expired_ids)
464
474
 
@@ -1,6 +1,7 @@
1
1
  from _typeshed import Incomplete
2
2
  from aip_agents.agent.hitl.config import ToolApprovalConfig as ToolApprovalConfig
3
3
  from aip_agents.agent.hitl.prompt import BasePromptHandler as BasePromptHandler, DeferredPromptHandler as DeferredPromptHandler
4
+ from aip_agents.agent.hitl.registry import hitl_registry as hitl_registry
4
5
  from aip_agents.schema.hitl import ApprovalDecision as ApprovalDecision, ApprovalDecisionType as ApprovalDecisionType, ApprovalLogEntry as ApprovalLogEntry, ApprovalRequest as ApprovalRequest
5
6
  from collections.abc import Callable as Callable, Iterable
6
7
  from typing import Any
@@ -0,0 +1,149 @@
1
+ """Registry for tracking HITL approval manager ownership across agent hierarchies.
2
+
3
+ This module provides a thread-safe singleton registry that maps request IDs to their
4
+ owning ApprovalManager instances. This enables proper decision routing in hierarchical
5
+ agent architectures where sub-agents have HITL enabled but parents do not.
6
+
7
+ Authors:
8
+ Raymond Christopher (raymond.christopher@gdplabs.id)
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import threading
14
+ from typing import TYPE_CHECKING
15
+ from weakref import WeakValueDictionary
16
+
17
+ if TYPE_CHECKING:
18
+ from aip_agents.agent.hitl.manager import ApprovalManager
19
+
20
+
21
+ class HITLManagerRegistry:
22
+ """Global registry mapping request_id → owning ApprovalManager.
23
+
24
+ Uses weak references to avoid preventing manager garbage collection.
25
+ Thread-safe for concurrent agent execution.
26
+
27
+ This singleton registry allows the HITL decision endpoint to route decisions
28
+ to the correct manager in hierarchical agent setups, where a sub-agent may
29
+ create a pending request but the parent agent receives the decision.
30
+
31
+ Example:
32
+ >>> from aip_agents.agent.hitl.registry import hitl_registry
33
+ >>> # Manager auto-registers when creating requests
34
+ >>> manager.create_approval_request(...)
35
+ >>> # Later, decision handler looks up the owning manager
36
+ >>> owning_manager = hitl_registry.get_manager(request_id)
37
+ >>> owning_manager.resolve_pending_request(request_id, "approved")
38
+ """
39
+
40
+ _instance: HITLManagerRegistry | None = None
41
+ _lock = threading.RLock()
42
+ _registry: WeakValueDictionary[str, ApprovalManager]
43
+ _registry_lock: threading.RLock
44
+
45
+ def __new__(cls) -> HITLManagerRegistry:
46
+ """Ensure only one instance exists (singleton pattern).
47
+
48
+ Returns:
49
+ The singleton HITLManagerRegistry instance.
50
+ """
51
+ if cls._instance is None:
52
+ with cls._lock:
53
+ if cls._instance is None:
54
+ instance = super().__new__(cls)
55
+ instance._registry = WeakValueDictionary()
56
+ instance._registry_lock = threading.RLock()
57
+ cls._instance = instance
58
+ return cls._instance
59
+
60
+ def register(self, request_id: str, manager: ApprovalManager) -> None:
61
+ """Register a pending request with its owning manager.
62
+
63
+ This method is typically called automatically by ApprovalManager when
64
+ creating a new pending request. It establishes the ownership mapping
65
+ needed for proper decision routing.
66
+
67
+ Args:
68
+ request_id: Unique identifier for the pending request
69
+ manager: ApprovalManager instance that owns this request
70
+
71
+ Example:
72
+ >>> hitl_registry.register("req_abc123", my_manager)
73
+ """
74
+ with self._registry_lock:
75
+ self._registry[request_id] = manager
76
+
77
+ def unregister(self, request_id: str) -> None:
78
+ """Remove a request from the registry.
79
+
80
+ This method is called when a request is resolved, times out, or expires.
81
+ It's important for cleanup to prevent the registry from growing unbounded.
82
+
83
+ Args:
84
+ request_id: Unique identifier to remove
85
+
86
+ Example:
87
+ >>> hitl_registry.unregister("req_abc123")
88
+ """
89
+ with self._registry_lock:
90
+ self._registry.pop(request_id, None)
91
+
92
+ def get_manager(self, request_id: str) -> ApprovalManager | None:
93
+ """Retrieve the manager owning a specific request.
94
+
95
+ This is the primary lookup method used by decision handlers to route
96
+ decisions to the correct manager in hierarchical agent setups.
97
+
98
+ Args:
99
+ request_id: Unique identifier to lookup
100
+
101
+ Returns:
102
+ ApprovalManager instance if found, None otherwise. None can indicate
103
+ the request was resolved, timed out, or the manager was garbage collected.
104
+
105
+ Example:
106
+ >>> manager = hitl_registry.get_manager("req_abc123")
107
+ >>> if manager:
108
+ ... manager.resolve_pending_request("req_abc123", "approved")
109
+ """
110
+ with self._registry_lock:
111
+ return self._registry.get(request_id)
112
+
113
+ def clear(self) -> None:
114
+ """Clear all registrations.
115
+
116
+ This method is primarily intended for testing to ensure a clean state
117
+ between test cases. It removes all registered mappings.
118
+
119
+ Warning:
120
+ This should not be called in production code as it will prevent
121
+ pending requests from being resolved.
122
+
123
+ Example:
124
+ >>> hitl_registry.clear() # For testing only
125
+ """
126
+ with self._registry_lock:
127
+ self._registry.clear()
128
+
129
+ def list_all(self) -> list[str]:
130
+ """List all currently registered request IDs.
131
+
132
+ Returns:
133
+ List of request IDs currently in the registry.
134
+
135
+ Note:
136
+ Due to weak references, managers may be garbage collected between
137
+ calling this method and accessing them, so the actual available
138
+ managers might be fewer than the returned list length.
139
+
140
+ Example:
141
+ >>> request_ids = hitl_registry.list_all()
142
+ >>> print(f"Pending requests: {request_ids}")
143
+ """
144
+ with self._registry_lock:
145
+ return list(self._registry.keys())
146
+
147
+
148
+ # Global singleton instance
149
+ hitl_registry = HITLManagerRegistry()
@@ -0,0 +1,101 @@
1
+ from _typeshed import Incomplete
2
+ from aip_agents.agent.hitl.manager import ApprovalManager as ApprovalManager
3
+
4
+ class HITLManagerRegistry:
5
+ '''Global registry mapping request_id → owning ApprovalManager.
6
+
7
+ Uses weak references to avoid preventing manager garbage collection.
8
+ Thread-safe for concurrent agent execution.
9
+
10
+ This singleton registry allows the HITL decision endpoint to route decisions
11
+ to the correct manager in hierarchical agent setups, where a sub-agent may
12
+ create a pending request but the parent agent receives the decision.
13
+
14
+ Example:
15
+ >>> from aip_agents.agent.hitl.registry import hitl_registry
16
+ >>> # Manager auto-registers when creating requests
17
+ >>> manager.create_approval_request(...)
18
+ >>> # Later, decision handler looks up the owning manager
19
+ >>> owning_manager = hitl_registry.get_manager(request_id)
20
+ >>> owning_manager.resolve_pending_request(request_id, "approved")
21
+ '''
22
+ def __new__(cls) -> HITLManagerRegistry:
23
+ """Ensure only one instance exists (singleton pattern).
24
+
25
+ Returns:
26
+ The singleton HITLManagerRegistry instance.
27
+ """
28
+ def register(self, request_id: str, manager: ApprovalManager) -> None:
29
+ '''Register a pending request with its owning manager.
30
+
31
+ This method is typically called automatically by ApprovalManager when
32
+ creating a new pending request. It establishes the ownership mapping
33
+ needed for proper decision routing.
34
+
35
+ Args:
36
+ request_id: Unique identifier for the pending request
37
+ manager: ApprovalManager instance that owns this request
38
+
39
+ Example:
40
+ >>> hitl_registry.register("req_abc123", my_manager)
41
+ '''
42
+ def unregister(self, request_id: str) -> None:
43
+ '''Remove a request from the registry.
44
+
45
+ This method is called when a request is resolved, times out, or expires.
46
+ It\'s important for cleanup to prevent the registry from growing unbounded.
47
+
48
+ Args:
49
+ request_id: Unique identifier to remove
50
+
51
+ Example:
52
+ >>> hitl_registry.unregister("req_abc123")
53
+ '''
54
+ def get_manager(self, request_id: str) -> ApprovalManager | None:
55
+ '''Retrieve the manager owning a specific request.
56
+
57
+ This is the primary lookup method used by decision handlers to route
58
+ decisions to the correct manager in hierarchical agent setups.
59
+
60
+ Args:
61
+ request_id: Unique identifier to lookup
62
+
63
+ Returns:
64
+ ApprovalManager instance if found, None otherwise. None can indicate
65
+ the request was resolved, timed out, or the manager was garbage collected.
66
+
67
+ Example:
68
+ >>> manager = hitl_registry.get_manager("req_abc123")
69
+ >>> if manager:
70
+ ... manager.resolve_pending_request("req_abc123", "approved")
71
+ '''
72
+ def clear(self) -> None:
73
+ """Clear all registrations.
74
+
75
+ This method is primarily intended for testing to ensure a clean state
76
+ between test cases. It removes all registered mappings.
77
+
78
+ Warning:
79
+ This should not be called in production code as it will prevent
80
+ pending requests from being resolved.
81
+
82
+ Example:
83
+ >>> hitl_registry.clear() # For testing only
84
+ """
85
+ def list_all(self) -> list[str]:
86
+ '''List all currently registered request IDs.
87
+
88
+ Returns:
89
+ List of request IDs currently in the registry.
90
+
91
+ Note:
92
+ Due to weak references, managers may be garbage collected between
93
+ calling this method and accessing them, so the actual available
94
+ managers might be fewer than the returned list length.
95
+
96
+ Example:
97
+ >>> request_ids = hitl_registry.list_all()
98
+ >>> print(f"Pending requests: {request_ids}")
99
+ '''
100
+
101
+ hitl_registry: Incomplete
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aip-agents-binary
3
- Version: 0.5.10
3
+ Version: 0.5.12
4
4
  Summary: A library for managing agents in Gen AI applications.
5
5
  Author-email: Raymond Christopher <raymond.christopher@gdplabs.id>
6
6
  Requires-Python: <3.13,>=3.11
@@ -14,7 +14,7 @@ aip_agents/a2a/server/google_adk_executor.py,sha256=s6mX7E1od8bDaVrIocEbJAbDnZvm
14
14
  aip_agents/a2a/server/google_adk_executor.pyi,sha256=bMU9GxPIA1xkA-sbowbEeDBS4eBgx-bpS-ZD_WX-1Jk,2600
15
15
  aip_agents/a2a/server/langflow_executor.py,sha256=6bVnwqRnqbBi949JHHWcIB-ZVQAPGJ84DhAMXnB19co,6646
16
16
  aip_agents/a2a/server/langflow_executor.pyi,sha256=nlWUIOEGFcp4qtyrmmxax0TfzwIwOGkvFK-YmG0cGMg,1837
17
- aip_agents/a2a/server/langgraph_executor.py,sha256=Rmt6u2jyAlJKPWut-W_xZDNevUyFTDwFiKWM1hOcOs8,10639
17
+ aip_agents/a2a/server/langgraph_executor.py,sha256=45XRRwj7EQFBAkS69cZGtuL4ZtHZ0BDEyvDTp4Kw3yg,10765
18
18
  aip_agents/a2a/server/langgraph_executor.pyi,sha256=I6RX8_CjF0-gbJEYmNMO2P_i272k3R7X5iZNP5IfdTE,2287
19
19
  aip_agents/agent/__init__.py,sha256=KBT-e5nEBMVJypC8OFulmErUK63gmQZus0UcBu6EqBo,892
20
20
  aip_agents/agent/__init__.pyi,sha256=MxIAeAv1pPCtqfAa3lvmeCAN-IT5p3v77IeKhfKYvKo,855
@@ -44,10 +44,12 @@ aip_agents/agent/hitl/config.py,sha256=Stzga68KWD5sGQG0-Qn4zS1zSiwdQaelIaHcq9YEU
44
44
  aip_agents/agent/hitl/config.pyi,sha256=onrJHVuirFbL5kHgCmHLzrGsHm7pFaCtIsnT9N2vvX8,495
45
45
  aip_agents/agent/hitl/langgraph_hitl_mixin.py,sha256=wNgZTJhMoQm8yQiVus1muXmVFBfzCCh6gFO25hCzoz8,19054
46
46
  aip_agents/agent/hitl/langgraph_hitl_mixin.pyi,sha256=Hz_3AqvrEjX0iZC7Xia93L4O0UbknvLmUwYA8nLBZ8c,1978
47
- aip_agents/agent/hitl/manager.py,sha256=YNc-FpPhDUCueaIqMJ582P4gqOTaCjfVemv4jycLlq4,19969
48
- aip_agents/agent/hitl/manager.pyi,sha256=HlsB_xOoJGXiFNC7rl2RWYv0bOaCHIYxyeI8HbZcHRc,8616
47
+ aip_agents/agent/hitl/manager.py,sha256=4dGu6QEHEzlVnF-bQdRScLdZ8WnGTJPZutz87pmzQtk,20381
48
+ aip_agents/agent/hitl/manager.pyi,sha256=5wiU0MU4Ylf-wJ6Bn1wBDJeiVLFk7i3jD-boIMruf98,8690
49
49
  aip_agents/agent/hitl/models.py,sha256=Dwvc_TmkeWse6IAKE38k3IspLXnLFqjerUve4n82KQI,369
50
50
  aip_agents/agent/hitl/models.pyi,sha256=1iAKXmv17QDYNiVGbcAlr3fa1g_yvHTzkUbsxDKyy2k,287
51
+ aip_agents/agent/hitl/registry.py,sha256=rDKw_5YbIic361RaxcwGRj9NP7y5KBkLjo2NkQDBK4Y,5390
52
+ aip_agents/agent/hitl/registry.pyi,sha256=d0ylLEf-tPsA08_VfaHjHAhCzDLBMyevgu1u05Hb4qE,3913
51
53
  aip_agents/agent/hitl/prompt/__init__.py,sha256=HIF1MpWbtSwgSlqoVdDKEJTfhPq8YDXSIqGE1johshI,263
52
54
  aip_agents/agent/hitl/prompt/__init__.pyi,sha256=0ZULcLlR3WZPl7EivrYTVS4f0FaX2LIZeO8jaTX1yyk,240
53
55
  aip_agents/agent/hitl/prompt/base.py,sha256=NWMmLsFq_sasMv2WDlANUVWTMU0x4_D_mD2L6CDgn2w,1470
@@ -536,7 +538,7 @@ aip_agents/utils/pii/pii_helper.py,sha256=g0yRzakfA2AA6vjUNLWHqFlcxyLql6MXQ90NN3
536
538
  aip_agents/utils/pii/pii_helper.pyi,sha256=dulZs150ikbAL3Bw2YLcz3_g4DsGmL3lciwf8mKxEjI,2939
537
539
  aip_agents/utils/pii/uuid_deanonymizer_mapping.py,sha256=Gks8l8t0cuS9pzoQnrpiK1CaLmWYksjOnTeiHh3_7EE,7348
538
540
  aip_agents/utils/pii/uuid_deanonymizer_mapping.pyi,sha256=gnWfD1rWZh_tloJjgKiZ6f6iNUuBaHpKqCSiP0d-9bs,3084
539
- aip_agents_binary-0.5.10.dist-info/METADATA,sha256=EafPczm70GgmRRBtOT6HgTXt_jYdJ3FbiHalAK_Wj8Q,22617
540
- aip_agents_binary-0.5.10.dist-info/WHEEL,sha256=PaP4PvkDyiSc4C6Dhw6ccQmfxsWFrSv-lJQjBshu0hw,105
541
- aip_agents_binary-0.5.10.dist-info/top_level.txt,sha256=PEz8vcwC1bH4UrkhF0LkIYCNfXGWZUHdSklbvkBe25E,11
542
- aip_agents_binary-0.5.10.dist-info/RECORD,,
541
+ aip_agents_binary-0.5.12.dist-info/METADATA,sha256=Q_EJoAONNrRwDF8M8KauU0z2JLMabnKX6dhiSoRD6rY,22617
542
+ aip_agents_binary-0.5.12.dist-info/WHEEL,sha256=PaP4PvkDyiSc4C6Dhw6ccQmfxsWFrSv-lJQjBshu0hw,105
543
+ aip_agents_binary-0.5.12.dist-info/top_level.txt,sha256=PEz8vcwC1bH4UrkhF0LkIYCNfXGWZUHdSklbvkBe25E,11
544
+ aip_agents_binary-0.5.12.dist-info/RECORD,,