agnt5 0.2.8a3__cp310-abi3-manylinux_2_34_x86_64.whl → 0.2.8a5__cp310-abi3-manylinux_2_34_x86_64.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.
Potentially problematic release.
This version of agnt5 might be problematic. Click here for more details.
- agnt5/__init__.py +7 -5
- agnt5/_core.abi3.so +0 -0
- agnt5/agent.py +179 -108
- agnt5/entity.py +232 -153
- agnt5/exceptions.py +43 -0
- agnt5/tool.py +149 -0
- agnt5/worker.py +125 -62
- agnt5/workflow.py +112 -6
- {agnt5-0.2.8a3.dist-info → agnt5-0.2.8a5.dist-info}/METADATA +1 -1
- agnt5-0.2.8a5.dist-info/RECORD +22 -0
- agnt5/agent_session.py +0 -110
- agnt5-0.2.8a3.dist-info/RECORD +0 -23
- {agnt5-0.2.8a3.dist-info → agnt5-0.2.8a5.dist-info}/WHEEL +0 -0
agnt5/__init__.py
CHANGED
|
@@ -7,7 +7,6 @@ with built-in durability guarantees and state management.
|
|
|
7
7
|
|
|
8
8
|
from ._compat import _import_error, _rust_available
|
|
9
9
|
from .agent import Agent, AgentContext, AgentRegistry, AgentResult, Handoff, agent, handoff
|
|
10
|
-
from .agent_session import AgentSession
|
|
11
10
|
from .client import Client, RunError
|
|
12
11
|
from .context import Context
|
|
13
12
|
from .function import FunctionContext
|
|
@@ -15,7 +14,7 @@ from .workflow import WorkflowContext
|
|
|
15
14
|
from .entity import (
|
|
16
15
|
Entity,
|
|
17
16
|
EntityRegistry,
|
|
18
|
-
|
|
17
|
+
EntityStateAdapter,
|
|
19
18
|
EntityType,
|
|
20
19
|
create_entity_context,
|
|
21
20
|
with_entity_context,
|
|
@@ -27,9 +26,10 @@ from .exceptions import (
|
|
|
27
26
|
ExecutionError,
|
|
28
27
|
RetryError,
|
|
29
28
|
StateError,
|
|
29
|
+
WaitingForUserInputException,
|
|
30
30
|
)
|
|
31
31
|
from .function import FunctionRegistry, function
|
|
32
|
-
from .tool import Tool, ToolRegistry, tool
|
|
32
|
+
from .tool import AskUserTool, RequestApprovalTool, Tool, ToolRegistry, tool
|
|
33
33
|
from .types import BackoffPolicy, BackoffType, FunctionConfig, RetryPolicy, WorkflowConfig
|
|
34
34
|
from .version import _get_version
|
|
35
35
|
from .worker import Worker
|
|
@@ -55,7 +55,7 @@ __all__ = [
|
|
|
55
55
|
"Entity",
|
|
56
56
|
"EntityType",
|
|
57
57
|
"EntityRegistry",
|
|
58
|
-
"
|
|
58
|
+
"EntityStateAdapter",
|
|
59
59
|
"with_entity_context",
|
|
60
60
|
"create_entity_context",
|
|
61
61
|
"workflow",
|
|
@@ -63,11 +63,12 @@ __all__ = [
|
|
|
63
63
|
"tool",
|
|
64
64
|
"Tool",
|
|
65
65
|
"ToolRegistry",
|
|
66
|
+
"AskUserTool",
|
|
67
|
+
"RequestApprovalTool",
|
|
66
68
|
"agent",
|
|
67
69
|
"Agent",
|
|
68
70
|
"AgentRegistry",
|
|
69
71
|
"AgentResult",
|
|
70
|
-
"AgentSession",
|
|
71
72
|
"Handoff",
|
|
72
73
|
"handoff",
|
|
73
74
|
# Types
|
|
@@ -83,6 +84,7 @@ __all__ = [
|
|
|
83
84
|
"RetryError",
|
|
84
85
|
"StateError",
|
|
85
86
|
"CheckpointError",
|
|
87
|
+
"WaitingForUserInputException",
|
|
86
88
|
"RunError",
|
|
87
89
|
# Language Model (Simplified API)
|
|
88
90
|
"lm",
|
agnt5/_core.abi3.so
CHANGED
|
Binary file
|
agnt5/agent.py
CHANGED
|
@@ -9,6 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
import functools
|
|
10
10
|
import json
|
|
11
11
|
import logging
|
|
12
|
+
import time
|
|
12
13
|
from typing import Any, Callable, Dict, List, Optional
|
|
13
14
|
|
|
14
15
|
from .context import Context
|
|
@@ -79,42 +80,46 @@ class AgentContext(Context):
|
|
|
79
80
|
self._agent_name = agent_name
|
|
80
81
|
self._session_id = session_id or run_id
|
|
81
82
|
|
|
82
|
-
# Determine state
|
|
83
|
-
from .entity import
|
|
83
|
+
# Determine state adapter based on parent context
|
|
84
|
+
from .entity import EntityStateAdapter, _get_state_adapter
|
|
84
85
|
|
|
85
86
|
if state_manager:
|
|
86
|
-
# Explicit state
|
|
87
|
-
self.
|
|
88
|
-
logger.debug(f"AgentContext using provided state
|
|
87
|
+
# Explicit state adapter provided (parameter name kept for backward compat)
|
|
88
|
+
self._state_adapter = state_manager
|
|
89
|
+
logger.debug(f"AgentContext using provided state adapter")
|
|
89
90
|
elif parent_context:
|
|
90
|
-
# Try to inherit state
|
|
91
|
+
# Try to inherit state adapter from parent
|
|
91
92
|
try:
|
|
92
93
|
# Check if parent is WorkflowContext or AgentContext
|
|
93
94
|
if hasattr(parent_context, '_workflow_entity'):
|
|
94
|
-
# WorkflowContext - get state
|
|
95
|
-
self.
|
|
95
|
+
# WorkflowContext - get state adapter from worker context
|
|
96
|
+
self._state_adapter = _get_state_adapter()
|
|
96
97
|
logger.debug(f"AgentContext inheriting state from WorkflowContext")
|
|
97
|
-
elif hasattr(parent_context, '
|
|
98
|
-
# Parent AgentContext - share state
|
|
99
|
-
self.
|
|
98
|
+
elif hasattr(parent_context, '_state_adapter'):
|
|
99
|
+
# Parent AgentContext - share state adapter
|
|
100
|
+
self._state_adapter = parent_context._state_adapter
|
|
100
101
|
logger.debug(f"AgentContext inheriting state from parent AgentContext")
|
|
102
|
+
elif hasattr(parent_context, '_state_manager'):
|
|
103
|
+
# Backward compatibility: parent has old _state_manager
|
|
104
|
+
self._state_adapter = parent_context._state_manager
|
|
105
|
+
logger.debug(f"AgentContext inheriting state from parent (legacy)")
|
|
101
106
|
else:
|
|
102
|
-
# FunctionContext or base Context - create new state
|
|
103
|
-
self.
|
|
104
|
-
logger.debug(f"AgentContext created new state
|
|
107
|
+
# FunctionContext or base Context - create new state adapter
|
|
108
|
+
self._state_adapter = EntityStateAdapter()
|
|
109
|
+
logger.debug(f"AgentContext created new state adapter (parent has no state)")
|
|
105
110
|
except RuntimeError as e:
|
|
106
|
-
#
|
|
107
|
-
self.
|
|
108
|
-
logger.debug(f"AgentContext created standalone state
|
|
111
|
+
# _get_state_adapter() failed (not in worker context) - create standalone
|
|
112
|
+
self._state_adapter = EntityStateAdapter()
|
|
113
|
+
logger.debug(f"AgentContext created standalone state adapter (not in worker context)")
|
|
109
114
|
else:
|
|
110
115
|
# Try to get from worker context first
|
|
111
116
|
try:
|
|
112
|
-
self.
|
|
113
|
-
logger.debug(f"AgentContext got state
|
|
117
|
+
self._state_adapter = _get_state_adapter()
|
|
118
|
+
logger.debug(f"AgentContext got state adapter from worker context")
|
|
114
119
|
except RuntimeError as e:
|
|
115
|
-
# Standalone - create new state
|
|
116
|
-
self.
|
|
117
|
-
logger.debug(f"AgentContext created standalone state
|
|
120
|
+
# Standalone - create new state adapter
|
|
121
|
+
self._state_adapter = EntityStateAdapter()
|
|
122
|
+
logger.debug(f"AgentContext created standalone state adapter")
|
|
118
123
|
|
|
119
124
|
# Conversation key for state storage (used for in-memory state)
|
|
120
125
|
self._conversation_key = f"agent:{agent_name}:{self._session_id}:messages"
|
|
@@ -127,24 +132,23 @@ class AgentContext(Context):
|
|
|
127
132
|
"""
|
|
128
133
|
Get state interface for agent state management.
|
|
129
134
|
|
|
135
|
+
Note: This is a simplified in-memory state interface for agent-specific data.
|
|
136
|
+
Conversation history is managed separately via get_conversation_history() and
|
|
137
|
+
save_conversation_history() which use the Rust-backed persistence layer.
|
|
138
|
+
|
|
130
139
|
Returns:
|
|
131
|
-
|
|
140
|
+
Dict-like object for state operations
|
|
132
141
|
|
|
133
142
|
Example:
|
|
134
|
-
# Store
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
ctx.state.set(f"agent:{agent_name}:{session_id}:messages", messages)
|
|
138
|
-
|
|
139
|
-
# Store agent-specific data
|
|
140
|
-
ctx.state.set("research_results", data)
|
|
143
|
+
# Store agent-specific data (in-memory only)
|
|
144
|
+
ctx.state["research_results"] = data
|
|
145
|
+
ctx.state["iteration_count"] = 5
|
|
141
146
|
"""
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
return EntityState(state_dict)
|
|
147
|
+
# Simple dict-based state for agent-specific data
|
|
148
|
+
# This is in-memory only and not persisted to platform
|
|
149
|
+
if not hasattr(self, '_agent_state'):
|
|
150
|
+
self._agent_state = {}
|
|
151
|
+
return self._agent_state
|
|
148
152
|
|
|
149
153
|
@property
|
|
150
154
|
def session_id(self) -> str:
|
|
@@ -155,45 +159,30 @@ class AgentContext(Context):
|
|
|
155
159
|
"""
|
|
156
160
|
Retrieve conversation history from state, loading from database if needed.
|
|
157
161
|
|
|
162
|
+
Uses the EntityStateAdapter which delegates to Rust core for cache-first loading.
|
|
163
|
+
|
|
158
164
|
Returns:
|
|
159
165
|
List of Message objects from conversation history
|
|
160
166
|
"""
|
|
161
|
-
# Try to load from database first if not in memory
|
|
162
167
|
entity_type = "AgentSession"
|
|
163
|
-
entity_key = self._entity_key
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
messages_data = session_data["messages"]
|
|
182
|
-
elif isinstance(session_data, list):
|
|
183
|
-
# Old format - just messages array
|
|
184
|
-
messages_data = session_data
|
|
185
|
-
else:
|
|
186
|
-
messages_data = []
|
|
187
|
-
|
|
188
|
-
# Load messages into in-memory state (store just messages, not full session object)
|
|
189
|
-
self._state_manager.load_state_from_platform(state_key, json.dumps(messages_data), version)
|
|
190
|
-
logger.info(f"Loaded conversation history from database: {entity_key} (version {version})")
|
|
191
|
-
except Exception as e:
|
|
192
|
-
logger.warning(f"Failed to load conversation history from database: {e}")
|
|
193
|
-
|
|
194
|
-
# Get from in-memory state
|
|
195
|
-
messages_data = self.state.get(self._conversation_key, [])
|
|
196
|
-
logger.debug(f"Loaded {len(messages_data)} messages from conversation history")
|
|
168
|
+
entity_key = self._entity_key
|
|
169
|
+
|
|
170
|
+
# Load session data via adapter (Rust handles cache + platform load)
|
|
171
|
+
session_data = await self._state_adapter.load_state(entity_type, entity_key)
|
|
172
|
+
|
|
173
|
+
# Extract messages from session object
|
|
174
|
+
if isinstance(session_data, dict) and "messages" in session_data:
|
|
175
|
+
# New format with session metadata
|
|
176
|
+
messages_data = session_data["messages"]
|
|
177
|
+
logger.debug(f"Loaded {len(messages_data)} messages from session {entity_key}")
|
|
178
|
+
elif isinstance(session_data, list):
|
|
179
|
+
# Old format - just messages array
|
|
180
|
+
messages_data = session_data
|
|
181
|
+
logger.debug(f"Loaded {len(messages_data)} messages (legacy format)")
|
|
182
|
+
else:
|
|
183
|
+
# No messages found
|
|
184
|
+
messages_data = []
|
|
185
|
+
logger.debug(f"No conversation history found for {entity_key}")
|
|
197
186
|
|
|
198
187
|
# Convert dict representations back to Message objects
|
|
199
188
|
messages = []
|
|
@@ -221,6 +210,8 @@ class AgentContext(Context):
|
|
|
221
210
|
"""
|
|
222
211
|
Save conversation history to state and persist to database.
|
|
223
212
|
|
|
213
|
+
Uses the EntityStateAdapter which delegates to Rust core for version-checked saves.
|
|
214
|
+
|
|
224
215
|
Args:
|
|
225
216
|
messages: List of Message objects to persist
|
|
226
217
|
"""
|
|
@@ -231,49 +222,129 @@ class AgentContext(Context):
|
|
|
231
222
|
for msg in messages:
|
|
232
223
|
messages_data.append({
|
|
233
224
|
"role": msg.role.value if hasattr(msg.role, 'value') else str(msg.role),
|
|
234
|
-
"content": msg.content
|
|
225
|
+
"content": msg.content,
|
|
226
|
+
"timestamp": time.time() # Add timestamp for each message
|
|
235
227
|
})
|
|
236
228
|
|
|
237
|
-
|
|
238
|
-
self.
|
|
229
|
+
entity_type = "AgentSession"
|
|
230
|
+
entity_key = self._entity_key
|
|
239
231
|
|
|
240
|
-
#
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
import time
|
|
245
|
-
entity_type = "AgentSession"
|
|
246
|
-
entity_key = self._entity_key # Use entity_key without :messages suffix
|
|
247
|
-
state_key = (entity_type, entity_key)
|
|
248
|
-
|
|
249
|
-
# Get current version
|
|
250
|
-
expected_version = self._state_manager._versions.get(state_key, 0)
|
|
251
|
-
|
|
252
|
-
# Build session object with metadata for the sessions API
|
|
253
|
-
now = time.time()
|
|
254
|
-
session_data = {
|
|
255
|
-
"session_id": self._session_id,
|
|
256
|
-
"agent_name": self._agent_name,
|
|
257
|
-
"created_at": now if expected_version == 0 else None, # Only set on first save
|
|
258
|
-
"last_message_time": now,
|
|
259
|
-
"message_count": len(messages_data),
|
|
260
|
-
"messages": messages_data,
|
|
261
|
-
"metadata": {}
|
|
262
|
-
}
|
|
232
|
+
# Load current state with version for optimistic locking
|
|
233
|
+
current_state, current_version = await self._state_adapter.load_with_version(
|
|
234
|
+
entity_type, entity_key
|
|
235
|
+
)
|
|
263
236
|
|
|
264
|
-
|
|
265
|
-
|
|
237
|
+
# Build session object with metadata
|
|
238
|
+
now = time.time()
|
|
266
239
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
240
|
+
# Get custom metadata from instance variable or preserve from loaded state
|
|
241
|
+
custom_metadata = getattr(self, '_custom_metadata', current_state.get("metadata", {}))
|
|
242
|
+
|
|
243
|
+
session_data = {
|
|
244
|
+
"session_id": self._session_id,
|
|
245
|
+
"agent_name": self._agent_name,
|
|
246
|
+
"created_at": current_state.get("created_at", now), # Preserve existing or set new
|
|
247
|
+
"last_message_time": now,
|
|
248
|
+
"message_count": len(messages_data),
|
|
249
|
+
"messages": messages_data,
|
|
250
|
+
"metadata": custom_metadata # Save custom metadata
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
# Save to platform via adapter (Rust handles optimistic locking)
|
|
254
|
+
try:
|
|
255
|
+
new_version = await self._state_adapter.save_state(
|
|
256
|
+
entity_type,
|
|
257
|
+
entity_key,
|
|
258
|
+
session_data,
|
|
259
|
+
current_version
|
|
260
|
+
)
|
|
261
|
+
logger.info(
|
|
262
|
+
f"Persisted conversation history: {entity_key} (version {current_version} -> {new_version})"
|
|
263
|
+
)
|
|
264
|
+
except Exception as e:
|
|
265
|
+
logger.error(f"Failed to persist conversation history to database: {e}")
|
|
266
|
+
# Don't fail - conversation is still in memory for this execution
|
|
267
|
+
|
|
268
|
+
async def get_metadata(self) -> Dict[str, Any]:
|
|
269
|
+
"""
|
|
270
|
+
Get conversation session metadata.
|
|
271
|
+
|
|
272
|
+
Returns session metadata including:
|
|
273
|
+
- created_at: Timestamp of first message (float, Unix timestamp)
|
|
274
|
+
- last_activity: Timestamp of last message (float, Unix timestamp)
|
|
275
|
+
- message_count: Number of messages in conversation (int)
|
|
276
|
+
- custom: Dict of user-provided custom metadata
|
|
271
277
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
278
|
+
Returns:
|
|
279
|
+
Dictionary with metadata. If no conversation exists yet, returns defaults.
|
|
280
|
+
|
|
281
|
+
Example:
|
|
282
|
+
```python
|
|
283
|
+
metadata = await context.get_metadata()
|
|
284
|
+
print(f"Session created: {metadata['created_at']}")
|
|
285
|
+
print(f"User ID: {metadata['custom'].get('user_id')}")
|
|
286
|
+
```
|
|
287
|
+
"""
|
|
288
|
+
entity_type = "AgentSession"
|
|
289
|
+
entity_key = self._entity_key
|
|
290
|
+
|
|
291
|
+
# Load session data
|
|
292
|
+
session_data = await self._state_adapter.load_state(entity_type, entity_key)
|
|
293
|
+
|
|
294
|
+
if not session_data:
|
|
295
|
+
# No conversation exists yet - return defaults
|
|
296
|
+
return {
|
|
297
|
+
"created_at": None,
|
|
298
|
+
"last_activity": None,
|
|
299
|
+
"message_count": 0,
|
|
300
|
+
"custom": getattr(self, '_custom_metadata', {})
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
messages = session_data.get("messages", [])
|
|
304
|
+
|
|
305
|
+
# Derive timestamps from messages if available
|
|
306
|
+
created_at = session_data.get("created_at")
|
|
307
|
+
last_activity = session_data.get("last_message_time")
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
"created_at": created_at,
|
|
311
|
+
"last_activity": last_activity,
|
|
312
|
+
"message_count": len(messages),
|
|
313
|
+
"custom": session_data.get("metadata", {})
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
def update_metadata(self, **kwargs) -> None:
|
|
317
|
+
"""
|
|
318
|
+
Update custom session metadata.
|
|
319
|
+
|
|
320
|
+
Metadata will be persisted alongside conversation history on next save.
|
|
321
|
+
Use this to store application-specific data like user_id, preferences, etc.
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
**kwargs: Key-value pairs to store as metadata
|
|
325
|
+
|
|
326
|
+
Example:
|
|
327
|
+
```python
|
|
328
|
+
# Store user identification and preferences
|
|
329
|
+
context.update_metadata(
|
|
330
|
+
user_id="user-123",
|
|
331
|
+
subscription_tier="premium",
|
|
332
|
+
preferences={"theme": "dark", "language": "en"}
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# Later retrieve it
|
|
336
|
+
metadata = await context.get_metadata()
|
|
337
|
+
user_id = metadata["custom"]["user_id"]
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Note:
|
|
341
|
+
- Metadata is merged with existing metadata (doesn't replace)
|
|
342
|
+
- Changes persist on next save_conversation_history() call
|
|
343
|
+
- Use simple JSON-serializable types (str, int, float, dict, list)
|
|
344
|
+
"""
|
|
345
|
+
if not hasattr(self, '_custom_metadata'):
|
|
346
|
+
self._custom_metadata = {}
|
|
347
|
+
self._custom_metadata.update(kwargs)
|
|
277
348
|
|
|
278
349
|
|
|
279
350
|
class Handoff:
|