jaf-py 2.5.10__py3-none-any.whl → 2.5.12__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.
- jaf/__init__.py +154 -57
- jaf/a2a/__init__.py +42 -21
- jaf/a2a/agent.py +79 -126
- jaf/a2a/agent_card.py +87 -78
- jaf/a2a/client.py +30 -66
- jaf/a2a/examples/client_example.py +12 -12
- jaf/a2a/examples/integration_example.py +38 -47
- jaf/a2a/examples/server_example.py +56 -53
- jaf/a2a/memory/__init__.py +0 -4
- jaf/a2a/memory/cleanup.py +28 -21
- jaf/a2a/memory/factory.py +155 -133
- jaf/a2a/memory/providers/composite.py +21 -26
- jaf/a2a/memory/providers/in_memory.py +89 -83
- jaf/a2a/memory/providers/postgres.py +117 -115
- jaf/a2a/memory/providers/redis.py +128 -121
- jaf/a2a/memory/serialization.py +77 -87
- jaf/a2a/memory/tests/run_comprehensive_tests.py +112 -83
- jaf/a2a/memory/tests/test_cleanup.py +211 -94
- jaf/a2a/memory/tests/test_serialization.py +73 -68
- jaf/a2a/memory/tests/test_stress_concurrency.py +186 -133
- jaf/a2a/memory/tests/test_task_lifecycle.py +138 -120
- jaf/a2a/memory/types.py +91 -53
- jaf/a2a/protocol.py +95 -125
- jaf/a2a/server.py +90 -118
- jaf/a2a/standalone_client.py +30 -43
- jaf/a2a/tests/__init__.py +16 -33
- jaf/a2a/tests/run_tests.py +17 -53
- jaf/a2a/tests/test_agent.py +40 -140
- jaf/a2a/tests/test_client.py +54 -117
- jaf/a2a/tests/test_integration.py +28 -82
- jaf/a2a/tests/test_protocol.py +54 -139
- jaf/a2a/tests/test_types.py +50 -136
- jaf/a2a/types.py +58 -34
- jaf/cli.py +21 -41
- jaf/core/__init__.py +7 -1
- jaf/core/agent_tool.py +93 -72
- jaf/core/analytics.py +257 -207
- jaf/core/checkpoint.py +223 -0
- jaf/core/composition.py +249 -235
- jaf/core/engine.py +817 -519
- jaf/core/errors.py +55 -42
- jaf/core/guardrails.py +276 -202
- jaf/core/handoff.py +47 -31
- jaf/core/parallel_agents.py +69 -75
- jaf/core/performance.py +75 -73
- jaf/core/proxy.py +43 -44
- jaf/core/proxy_helpers.py +24 -27
- jaf/core/regeneration.py +220 -129
- jaf/core/state.py +68 -66
- jaf/core/streaming.py +115 -108
- jaf/core/tool_results.py +111 -101
- jaf/core/tools.py +114 -116
- jaf/core/tracing.py +310 -210
- jaf/core/types.py +403 -151
- jaf/core/workflows.py +209 -168
- jaf/exceptions.py +46 -38
- jaf/memory/__init__.py +1 -6
- jaf/memory/approval_storage.py +54 -77
- jaf/memory/factory.py +4 -4
- jaf/memory/providers/in_memory.py +216 -180
- jaf/memory/providers/postgres.py +216 -146
- jaf/memory/providers/redis.py +173 -116
- jaf/memory/types.py +70 -51
- jaf/memory/utils.py +36 -34
- jaf/plugins/__init__.py +12 -12
- jaf/plugins/base.py +105 -96
- jaf/policies/__init__.py +0 -1
- jaf/policies/handoff.py +37 -46
- jaf/policies/validation.py +76 -52
- jaf/providers/__init__.py +6 -3
- jaf/providers/mcp.py +97 -51
- jaf/providers/model.py +475 -283
- jaf/server/__init__.py +1 -1
- jaf/server/main.py +7 -11
- jaf/server/server.py +514 -359
- jaf/server/types.py +208 -52
- jaf/utils/__init__.py +17 -18
- jaf/utils/attachments.py +111 -116
- jaf/utils/document_processor.py +175 -174
- jaf/visualization/__init__.py +1 -1
- jaf/visualization/example.py +111 -110
- jaf/visualization/functional_core.py +46 -71
- jaf/visualization/graphviz.py +154 -189
- jaf/visualization/imperative_shell.py +7 -16
- jaf/visualization/types.py +8 -4
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.12.dist-info}/METADATA +2 -2
- jaf_py-2.5.12.dist-info/RECORD +97 -0
- jaf_py-2.5.10.dist-info/RECORD +0 -96
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.12.dist-info}/WHEEL +0 -0
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.12.dist-info}/entry_points.txt +0 -0
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.12.dist-info}/licenses/LICENSE +0 -0
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.12.dist-info}/top_level.txt +0 -0
jaf/core/regeneration.py
CHANGED
|
@@ -10,27 +10,34 @@ from dataclasses import replace
|
|
|
10
10
|
from typing import Any, TypeVar, Optional
|
|
11
11
|
|
|
12
12
|
from .types import (
|
|
13
|
-
RunState,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
RunState,
|
|
14
|
+
RunConfig,
|
|
15
|
+
RunResult,
|
|
16
|
+
RegenerationRequest,
|
|
17
|
+
RegenerationContext,
|
|
18
|
+
MessageId,
|
|
19
|
+
Message,
|
|
20
|
+
ErrorOutcome,
|
|
21
|
+
ModelBehaviorError,
|
|
22
|
+
find_message_index,
|
|
23
|
+
truncate_messages_after,
|
|
24
|
+
get_message_by_id,
|
|
25
|
+
generate_run_id,
|
|
26
|
+
generate_trace_id,
|
|
18
27
|
)
|
|
19
28
|
from .engine import run as engine_run
|
|
20
29
|
from ..memory.types import Success, Failure
|
|
21
30
|
|
|
22
|
-
Ctx = TypeVar(
|
|
23
|
-
Out = TypeVar(
|
|
31
|
+
Ctx = TypeVar("Ctx")
|
|
32
|
+
Out = TypeVar("Out")
|
|
33
|
+
|
|
24
34
|
|
|
25
35
|
async def regenerate_conversation(
|
|
26
|
-
regeneration_request: RegenerationRequest,
|
|
27
|
-
config: RunConfig[Ctx],
|
|
28
|
-
context: Ctx,
|
|
29
|
-
agent_name: str
|
|
36
|
+
regeneration_request: RegenerationRequest, config: RunConfig[Ctx], context: Ctx, agent_name: str
|
|
30
37
|
) -> RunResult[Out]:
|
|
31
38
|
"""
|
|
32
39
|
Regenerate a conversation from a specific message ID.
|
|
33
|
-
|
|
40
|
+
|
|
34
41
|
This function:
|
|
35
42
|
1. Loads the full conversation from memory
|
|
36
43
|
2. Finds the message to regenerate from
|
|
@@ -38,13 +45,13 @@ async def regenerate_conversation(
|
|
|
38
45
|
4. Creates a new RunState with truncated conversation
|
|
39
46
|
5. Executes the regeneration through the normal engine flow
|
|
40
47
|
6. Updates memory with the new conversation path
|
|
41
|
-
|
|
48
|
+
|
|
42
49
|
Args:
|
|
43
50
|
regeneration_request: The regeneration request containing conversation_id and message_id
|
|
44
51
|
config: The run configuration
|
|
45
52
|
context: The context for the regeneration
|
|
46
53
|
agent_name: The name of the agent to use for regeneration
|
|
47
|
-
|
|
54
|
+
|
|
48
55
|
Returns:
|
|
49
56
|
RunResult with the regenerated conversation outcome
|
|
50
57
|
"""
|
|
@@ -56,15 +63,19 @@ async def regenerate_conversation(
|
|
|
56
63
|
messages=[],
|
|
57
64
|
current_agent_name=agent_name,
|
|
58
65
|
context=context,
|
|
59
|
-
turn_count=0
|
|
66
|
+
turn_count=0,
|
|
67
|
+
),
|
|
68
|
+
outcome=ErrorOutcome(
|
|
69
|
+
error=ModelBehaviorError(
|
|
70
|
+
detail="Regeneration requires memory provider and conversation_id to be configured"
|
|
71
|
+
)
|
|
60
72
|
),
|
|
61
|
-
outcome=ErrorOutcome(error=ModelBehaviorError(
|
|
62
|
-
detail="Regeneration requires memory provider and conversation_id to be configured"
|
|
63
|
-
))
|
|
64
73
|
)
|
|
65
74
|
|
|
66
75
|
# Load the conversation from memory
|
|
67
|
-
conversation_result = await config.memory.provider.get_conversation(
|
|
76
|
+
conversation_result = await config.memory.provider.get_conversation(
|
|
77
|
+
regeneration_request.conversation_id
|
|
78
|
+
)
|
|
68
79
|
if isinstance(conversation_result, Failure):
|
|
69
80
|
return RunResult(
|
|
70
81
|
final_state=RunState(
|
|
@@ -73,11 +84,13 @@ async def regenerate_conversation(
|
|
|
73
84
|
messages=[],
|
|
74
85
|
current_agent_name=agent_name,
|
|
75
86
|
context=context,
|
|
76
|
-
turn_count=0
|
|
87
|
+
turn_count=0,
|
|
88
|
+
),
|
|
89
|
+
outcome=ErrorOutcome(
|
|
90
|
+
error=ModelBehaviorError(
|
|
91
|
+
detail=f"Failed to load conversation: {conversation_result.error}"
|
|
92
|
+
)
|
|
77
93
|
),
|
|
78
|
-
outcome=ErrorOutcome(error=ModelBehaviorError(
|
|
79
|
-
detail=f"Failed to load conversation: {conversation_result.error}"
|
|
80
|
-
))
|
|
81
94
|
)
|
|
82
95
|
|
|
83
96
|
conversation_memory = conversation_result.data
|
|
@@ -89,16 +102,18 @@ async def regenerate_conversation(
|
|
|
89
102
|
messages=[],
|
|
90
103
|
current_agent_name=agent_name,
|
|
91
104
|
context=context,
|
|
92
|
-
turn_count=0
|
|
105
|
+
turn_count=0,
|
|
106
|
+
),
|
|
107
|
+
outcome=ErrorOutcome(
|
|
108
|
+
error=ModelBehaviorError(
|
|
109
|
+
detail=f"Conversation {regeneration_request.conversation_id} not found"
|
|
110
|
+
)
|
|
93
111
|
),
|
|
94
|
-
outcome=ErrorOutcome(error=ModelBehaviorError(
|
|
95
|
-
detail=f"Conversation {regeneration_request.conversation_id} not found"
|
|
96
|
-
))
|
|
97
112
|
)
|
|
98
113
|
|
|
99
114
|
# Convert tuple back to list for processing
|
|
100
115
|
original_messages = list(conversation_memory.messages)
|
|
101
|
-
|
|
116
|
+
|
|
102
117
|
# Find the message to regenerate from
|
|
103
118
|
regenerate_message = get_message_by_id(original_messages, regeneration_request.message_id)
|
|
104
119
|
if not regenerate_message:
|
|
@@ -109,11 +124,19 @@ async def regenerate_conversation(
|
|
|
109
124
|
messages=original_messages,
|
|
110
125
|
current_agent_name=agent_name,
|
|
111
126
|
context=context,
|
|
112
|
-
turn_count=len(
|
|
127
|
+
turn_count=len(
|
|
128
|
+
[
|
|
129
|
+
m
|
|
130
|
+
for m in original_messages
|
|
131
|
+
if (m.role.value if hasattr(m.role, "value") else m.role) == "assistant"
|
|
132
|
+
]
|
|
133
|
+
),
|
|
134
|
+
),
|
|
135
|
+
outcome=ErrorOutcome(
|
|
136
|
+
error=ModelBehaviorError(
|
|
137
|
+
detail=f"Message {regeneration_request.message_id} not found in conversation"
|
|
138
|
+
)
|
|
113
139
|
),
|
|
114
|
-
outcome=ErrorOutcome(error=ModelBehaviorError(
|
|
115
|
-
detail=f"Message {regeneration_request.message_id} not found in conversation"
|
|
116
|
-
))
|
|
117
140
|
)
|
|
118
141
|
|
|
119
142
|
# Get the index of the message to regenerate
|
|
@@ -126,77 +149,93 @@ async def regenerate_conversation(
|
|
|
126
149
|
messages=original_messages,
|
|
127
150
|
current_agent_name=agent_name,
|
|
128
151
|
context=context,
|
|
129
|
-
turn_count=len(
|
|
152
|
+
turn_count=len(
|
|
153
|
+
[
|
|
154
|
+
m
|
|
155
|
+
for m in original_messages
|
|
156
|
+
if (m.role.value if hasattr(m.role, "value") else m.role) == "assistant"
|
|
157
|
+
]
|
|
158
|
+
),
|
|
159
|
+
),
|
|
160
|
+
outcome=ErrorOutcome(
|
|
161
|
+
error=ModelBehaviorError(
|
|
162
|
+
detail=f"Failed to find index for message {regeneration_request.message_id}"
|
|
163
|
+
)
|
|
130
164
|
),
|
|
131
|
-
outcome=ErrorOutcome(error=ModelBehaviorError(
|
|
132
|
-
detail=f"Failed to find index for message {regeneration_request.message_id}"
|
|
133
|
-
))
|
|
134
165
|
)
|
|
135
166
|
|
|
136
167
|
def determine_regeneration_type(messages, regenerate_index, context):
|
|
137
168
|
"""Determine if this is pure regeneration or edit scenario."""
|
|
138
169
|
if context and context.get("replace_user_message"):
|
|
139
170
|
return "edit"
|
|
140
|
-
|
|
171
|
+
|
|
141
172
|
regenerate_message = messages[regenerate_index]
|
|
142
|
-
if regenerate_message.role in [
|
|
173
|
+
if regenerate_message.role in ["assistant", "ASSISTANT"]:
|
|
143
174
|
for i in range(regenerate_index - 1, -1, -1):
|
|
144
|
-
if messages[i].role in [
|
|
175
|
+
if messages[i].role in ["user", "USER"]:
|
|
145
176
|
return "pure"
|
|
146
177
|
return "edit"
|
|
147
178
|
|
|
148
179
|
# Determine regeneration type
|
|
149
|
-
regen_type = determine_regeneration_type(
|
|
180
|
+
regen_type = determine_regeneration_type(
|
|
181
|
+
original_messages, regenerate_index, regeneration_request.context or {}
|
|
182
|
+
)
|
|
150
183
|
print(f"[JAF:REGENERATION] Detected regeneration type: {regen_type}")
|
|
151
184
|
|
|
152
185
|
if regen_type == "pure":
|
|
153
186
|
# For pure regeneration, find the user message that started this conversation turn
|
|
154
187
|
user_message_index = None
|
|
155
188
|
for i in range(regenerate_index - 1, -1, -1):
|
|
156
|
-
if original_messages[i].role in [
|
|
189
|
+
if original_messages[i].role in ["user", "USER"]:
|
|
157
190
|
user_message_index = i
|
|
158
191
|
break
|
|
159
|
-
|
|
192
|
+
|
|
160
193
|
if user_message_index is not None:
|
|
161
194
|
# Truncate AFTER the user message (keeps user message, removes tool calls/outputs)
|
|
162
|
-
truncated_messages = original_messages[:user_message_index + 1]
|
|
163
|
-
print(
|
|
195
|
+
truncated_messages = original_messages[: user_message_index + 1]
|
|
196
|
+
print(
|
|
197
|
+
f"[JAF:REGENERATION] Pure regeneration: truncated to user message at index {user_message_index}"
|
|
198
|
+
)
|
|
164
199
|
else:
|
|
165
200
|
truncated_messages = original_messages[:regenerate_index]
|
|
166
201
|
print(f"[JAF:REGENERATION] Pure regeneration fallback: no user message found")
|
|
167
202
|
else:
|
|
168
203
|
# Edit regeneration: truncate at the specified point and add replacement query
|
|
169
204
|
truncated_messages = original_messages[:regenerate_index]
|
|
170
|
-
|
|
171
|
-
if
|
|
172
|
-
|
|
173
|
-
|
|
205
|
+
|
|
206
|
+
if regeneration_request.context and regeneration_request.context.get(
|
|
207
|
+
"replace_user_message"
|
|
208
|
+
):
|
|
174
209
|
from .types import ContentRole, Message
|
|
210
|
+
|
|
175
211
|
replacement_user_message = Message(
|
|
176
212
|
role=ContentRole.USER,
|
|
177
|
-
content=regeneration_request.context.get("replace_user_message")
|
|
213
|
+
content=regeneration_request.context.get("replace_user_message"),
|
|
178
214
|
)
|
|
179
215
|
truncated_messages.append(replacement_user_message)
|
|
180
|
-
print(
|
|
181
|
-
|
|
216
|
+
print(
|
|
217
|
+
f"[JAF:REGENERATION] Edit regeneration: replaced user query with: {regeneration_request.context.get('replace_user_message')}"
|
|
218
|
+
)
|
|
219
|
+
|
|
182
220
|
print(f"[JAF:REGENERATION] Truncated conversation to {len(truncated_messages)} messages")
|
|
183
|
-
|
|
184
221
|
|
|
185
|
-
print(
|
|
186
|
-
|
|
222
|
+
print(
|
|
223
|
+
f"[JAF:REGENERATION] About to store {len(truncated_messages)} truncated messages to memory"
|
|
224
|
+
)
|
|
225
|
+
|
|
187
226
|
def serialize_metadata(metadata):
|
|
188
227
|
import json
|
|
189
228
|
import datetime
|
|
190
|
-
|
|
229
|
+
|
|
191
230
|
def json_serializer(obj):
|
|
192
231
|
if isinstance(obj, datetime.datetime):
|
|
193
232
|
return obj.isoformat()
|
|
194
233
|
elif isinstance(obj, datetime.date):
|
|
195
234
|
return obj.isoformat()
|
|
196
|
-
elif hasattr(obj,
|
|
235
|
+
elif hasattr(obj, "__dict__"):
|
|
197
236
|
return obj.__dict__
|
|
198
237
|
return str(obj)
|
|
199
|
-
|
|
238
|
+
|
|
200
239
|
try:
|
|
201
240
|
json_str = json.dumps(metadata, default=json_serializer)
|
|
202
241
|
return json.loads(json_str)
|
|
@@ -207,24 +246,36 @@ async def regenerate_conversation(
|
|
|
207
246
|
"regeneration_point": str(regeneration_request.message_id),
|
|
208
247
|
"original_message_count": len(original_messages),
|
|
209
248
|
"truncated_at_index": regenerate_index,
|
|
210
|
-
"turn_count": len(
|
|
249
|
+
"turn_count": len(
|
|
250
|
+
[
|
|
251
|
+
m
|
|
252
|
+
for m in truncated_messages
|
|
253
|
+
if (m.role.value if hasattr(m.role, "value") else m.role) == "assistant"
|
|
254
|
+
]
|
|
255
|
+
),
|
|
211
256
|
}
|
|
212
|
-
|
|
213
|
-
metadata = serialize_metadata(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
257
|
+
|
|
258
|
+
metadata = serialize_metadata(
|
|
259
|
+
{
|
|
260
|
+
**conversation_memory.metadata,
|
|
261
|
+
"regeneration_truncated": True,
|
|
262
|
+
"regeneration_point": str(regeneration_request.message_id),
|
|
263
|
+
"original_message_count": len(original_messages),
|
|
264
|
+
"truncated_at_index": regenerate_index,
|
|
265
|
+
"turn_count": len(
|
|
266
|
+
[
|
|
267
|
+
m
|
|
268
|
+
for m in truncated_messages
|
|
269
|
+
if (m.role.value if hasattr(m.role, "value") else m.role) == "assistant"
|
|
270
|
+
]
|
|
271
|
+
),
|
|
272
|
+
}
|
|
273
|
+
)
|
|
274
|
+
|
|
222
275
|
store_result = await config.memory.provider.store_messages(
|
|
223
|
-
regeneration_request.conversation_id,
|
|
224
|
-
truncated_messages,
|
|
225
|
-
metadata
|
|
276
|
+
regeneration_request.conversation_id, truncated_messages, metadata
|
|
226
277
|
)
|
|
227
|
-
|
|
278
|
+
|
|
228
279
|
print(f"[JAF:REGENERATION] Store result type: {type(store_result)}")
|
|
229
280
|
if isinstance(store_result, Failure):
|
|
230
281
|
print(f"[JAF:REGENERATION] Store failed with error: {store_result.error}")
|
|
@@ -235,11 +286,19 @@ async def regenerate_conversation(
|
|
|
235
286
|
messages=original_messages,
|
|
236
287
|
current_agent_name=agent_name,
|
|
237
288
|
context=context,
|
|
238
|
-
turn_count=len(
|
|
289
|
+
turn_count=len(
|
|
290
|
+
[
|
|
291
|
+
m
|
|
292
|
+
for m in original_messages
|
|
293
|
+
if (m.role.value if hasattr(m.role, "value") else m.role) == "assistant"
|
|
294
|
+
]
|
|
295
|
+
),
|
|
296
|
+
),
|
|
297
|
+
outcome=ErrorOutcome(
|
|
298
|
+
error=ModelBehaviorError(
|
|
299
|
+
detail=f"Failed to store truncated conversation: {store_result.error}"
|
|
300
|
+
)
|
|
239
301
|
),
|
|
240
|
-
outcome=ErrorOutcome(error=ModelBehaviorError(
|
|
241
|
-
detail=f"Failed to store truncated conversation: {store_result.error}"
|
|
242
|
-
))
|
|
243
302
|
)
|
|
244
303
|
else:
|
|
245
304
|
print(f"[JAF:REGENERATION] Store successful, proceeding to engine execution")
|
|
@@ -250,12 +309,18 @@ async def regenerate_conversation(
|
|
|
250
309
|
truncated_at_index=regenerate_index,
|
|
251
310
|
regenerated_message_id=regeneration_request.message_id,
|
|
252
311
|
regeneration_id=f"regen_{int(time.time() * 1000)}_{regeneration_request.message_id}",
|
|
253
|
-
timestamp=int(time.time() * 1000)
|
|
312
|
+
timestamp=int(time.time() * 1000),
|
|
254
313
|
)
|
|
255
314
|
|
|
256
315
|
# Calculate turn count from truncated messages
|
|
257
|
-
truncated_turn_count = len(
|
|
258
|
-
|
|
316
|
+
truncated_turn_count = len(
|
|
317
|
+
[
|
|
318
|
+
m
|
|
319
|
+
for m in truncated_messages
|
|
320
|
+
if (m.role.value if hasattr(m.role, "value") else m.role) == "assistant"
|
|
321
|
+
]
|
|
322
|
+
)
|
|
323
|
+
|
|
259
324
|
final_context = context
|
|
260
325
|
print(f"[JAF:REGENERATION] Using provided context: {type(context).__name__}")
|
|
261
326
|
|
|
@@ -263,50 +328,66 @@ async def regenerate_conversation(
|
|
|
263
328
|
initial_state = RunState(
|
|
264
329
|
run_id=generate_run_id(),
|
|
265
330
|
trace_id=generate_trace_id(),
|
|
266
|
-
messages=[],
|
|
331
|
+
messages=[],
|
|
267
332
|
current_agent_name=agent_name,
|
|
268
333
|
context=final_context,
|
|
269
334
|
turn_count=truncated_turn_count,
|
|
270
|
-
approvals={} # Reset approvals for regeneration
|
|
335
|
+
approvals={}, # Reset approvals for regeneration
|
|
271
336
|
)
|
|
272
337
|
|
|
273
|
-
print(
|
|
274
|
-
|
|
338
|
+
print(
|
|
339
|
+
f"[JAF:REGENERATION] Starting regeneration from message {regeneration_request.message_id}"
|
|
340
|
+
)
|
|
341
|
+
print(
|
|
342
|
+
f"[JAF:REGENERATION] Original messages: {len(original_messages)}, Truncated to: {len(truncated_messages)}"
|
|
343
|
+
)
|
|
275
344
|
print(f"[JAF:REGENERATION] Regeneration context: {regeneration_context}")
|
|
276
345
|
|
|
277
346
|
# Create a modified config for regeneration that ensures memory storage
|
|
278
347
|
regeneration_config = replace(
|
|
279
348
|
config,
|
|
280
349
|
conversation_id=regeneration_request.conversation_id,
|
|
281
|
-
memory=replace(config.memory, auto_store=True, store_on_completion=True)
|
|
350
|
+
memory=replace(config.memory, auto_store=True, store_on_completion=True)
|
|
351
|
+
if config.memory
|
|
352
|
+
else None,
|
|
282
353
|
)
|
|
283
354
|
|
|
284
355
|
# Execute the regeneration through the normal engine flow
|
|
285
356
|
print(f"[JAF:REGENERATION] About to execute engine with {len(truncated_messages)} messages")
|
|
286
|
-
print(
|
|
287
|
-
|
|
357
|
+
print(
|
|
358
|
+
f"[JAF:REGENERATION] Final message: {truncated_messages[-1] if truncated_messages else 'None'}"
|
|
359
|
+
)
|
|
360
|
+
|
|
288
361
|
result = await engine_run(initial_state, regeneration_config)
|
|
289
362
|
|
|
290
363
|
print(f"[JAF:REGENERATION] Regeneration completed with status: {result.outcome.status}")
|
|
291
|
-
if hasattr(result,
|
|
364
|
+
if hasattr(result, "final_state") and hasattr(result.final_state, "messages"):
|
|
292
365
|
print(f"[JAF:REGENERATION] Final state has {len(result.final_state.messages)} messages")
|
|
293
|
-
assistant_msgs = [
|
|
366
|
+
assistant_msgs = [
|
|
367
|
+
m for m in result.final_state.messages if m.role in ["assistant", "ASSISTANT"]
|
|
368
|
+
]
|
|
294
369
|
print(f"[JAF:REGENERATION] Found {len(assistant_msgs)} assistant messages in result")
|
|
295
|
-
|
|
370
|
+
|
|
296
371
|
# After successful regeneration, mark the regeneration point and preserve metadata
|
|
297
|
-
if result.outcome.status ==
|
|
372
|
+
if result.outcome.status == "completed" and config.memory and config.memory.provider:
|
|
298
373
|
try:
|
|
299
374
|
print(f"[JAF:REGENERATION] Marking regeneration point after successful regeneration")
|
|
300
|
-
|
|
375
|
+
|
|
301
376
|
# Get the current conversation to preserve regeneration metadata
|
|
302
|
-
current_conv_result = await config.memory.provider.get_conversation(
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
377
|
+
current_conv_result = await config.memory.provider.get_conversation(
|
|
378
|
+
regeneration_request.conversation_id
|
|
379
|
+
)
|
|
380
|
+
print(
|
|
381
|
+
f"[JAF:REGENERATION] Retrieved conversation for preservation: {hasattr(current_conv_result, 'data') and current_conv_result.data is not None}"
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
if hasattr(current_conv_result, "data") and current_conv_result.data:
|
|
306
385
|
current_metadata = current_conv_result.data.metadata
|
|
307
|
-
regeneration_points = current_metadata.get(
|
|
308
|
-
print(
|
|
309
|
-
|
|
386
|
+
regeneration_points = current_metadata.get("regeneration_points", [])
|
|
387
|
+
print(
|
|
388
|
+
f"[JAF:REGENERATION] Found {len(regeneration_points)} regeneration points in metadata before marking"
|
|
389
|
+
)
|
|
390
|
+
|
|
310
391
|
# Mark the regeneration point by calling the provider method directly
|
|
311
392
|
mark_result = await config.memory.provider.mark_regeneration_point(
|
|
312
393
|
regeneration_request.conversation_id,
|
|
@@ -315,78 +396,88 @@ async def regenerate_conversation(
|
|
|
315
396
|
"regeneration_id": regeneration_context.regeneration_id,
|
|
316
397
|
"original_message_count": len(original_messages),
|
|
317
398
|
"truncated_at_index": regenerate_index,
|
|
318
|
-
"timestamp": regeneration_context.timestamp
|
|
319
|
-
}
|
|
399
|
+
"timestamp": regeneration_context.timestamp,
|
|
400
|
+
},
|
|
320
401
|
)
|
|
321
|
-
|
|
402
|
+
|
|
322
403
|
if isinstance(mark_result, Failure):
|
|
323
|
-
print(
|
|
404
|
+
print(
|
|
405
|
+
f"[JAF:REGENERATION] Warning: Failed to mark regeneration point: {mark_result.error}"
|
|
406
|
+
)
|
|
324
407
|
else:
|
|
325
408
|
print(f"[JAF:REGENERATION] Successfully marked regeneration point")
|
|
326
|
-
|
|
409
|
+
|
|
327
410
|
# Get the updated conversation with the new regeneration point
|
|
328
|
-
updated_conv_result = await config.memory.provider.get_conversation(
|
|
329
|
-
|
|
411
|
+
updated_conv_result = await config.memory.provider.get_conversation(
|
|
412
|
+
regeneration_request.conversation_id
|
|
413
|
+
)
|
|
414
|
+
if hasattr(updated_conv_result, "data") and updated_conv_result.data:
|
|
330
415
|
updated_metadata = updated_conv_result.data.metadata
|
|
331
|
-
updated_regeneration_points = updated_metadata.get(
|
|
332
|
-
print(
|
|
333
|
-
|
|
416
|
+
updated_regeneration_points = updated_metadata.get("regeneration_points", [])
|
|
417
|
+
print(
|
|
418
|
+
f"[JAF:REGENERATION] Found {len(updated_regeneration_points)} regeneration points after marking"
|
|
419
|
+
)
|
|
420
|
+
|
|
334
421
|
# Ensure final metadata includes the regeneration points
|
|
335
422
|
final_metadata = {
|
|
336
423
|
**updated_metadata,
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
424
|
+
"regeneration_points": updated_regeneration_points,
|
|
425
|
+
"regeneration_count": len(updated_regeneration_points),
|
|
426
|
+
"last_regeneration": updated_regeneration_points[-1]
|
|
427
|
+
if updated_regeneration_points
|
|
428
|
+
else None,
|
|
429
|
+
"regeneration_preserved": True,
|
|
430
|
+
"final_preservation_timestamp": int(time.time() * 1000),
|
|
342
431
|
}
|
|
343
|
-
|
|
432
|
+
|
|
344
433
|
# Store the final conversation with preserved regeneration metadata
|
|
345
434
|
await config.memory.provider.store_messages(
|
|
346
435
|
regeneration_request.conversation_id,
|
|
347
436
|
result.final_state.messages,
|
|
348
|
-
final_metadata
|
|
437
|
+
final_metadata,
|
|
438
|
+
)
|
|
439
|
+
print(
|
|
440
|
+
f"[JAF:REGENERATION] Final preservation completed with {len(updated_regeneration_points)} regeneration points"
|
|
349
441
|
)
|
|
350
|
-
print(f"[JAF:REGENERATION] Final preservation completed with {len(updated_regeneration_points)} regeneration points")
|
|
351
442
|
else:
|
|
352
443
|
print(f"[JAF:REGENERATION] No conversation data found for preservation")
|
|
353
|
-
|
|
444
|
+
|
|
354
445
|
except Exception as e:
|
|
355
446
|
print(f"[JAF:REGENERATION] Warning: Failed to preserve regeneration points: {e}")
|
|
356
447
|
import traceback
|
|
448
|
+
|
|
357
449
|
traceback.print_exc()
|
|
358
|
-
|
|
450
|
+
|
|
359
451
|
return result
|
|
360
452
|
|
|
361
453
|
|
|
362
|
-
async def get_regeneration_points(
|
|
363
|
-
conversation_id: str,
|
|
364
|
-
config: RunConfig[Ctx]
|
|
365
|
-
) -> Optional[list]:
|
|
454
|
+
async def get_regeneration_points(conversation_id: str, config: RunConfig[Ctx]) -> Optional[list]:
|
|
366
455
|
"""
|
|
367
456
|
Get all regeneration points for a conversation.
|
|
368
|
-
|
|
457
|
+
|
|
369
458
|
Args:
|
|
370
459
|
conversation_id: The conversation ID
|
|
371
460
|
config: The run configuration
|
|
372
|
-
|
|
461
|
+
|
|
373
462
|
Returns:
|
|
374
463
|
List of regeneration points or None if not available
|
|
375
464
|
"""
|
|
376
465
|
if not config.memory or not config.memory.provider:
|
|
377
466
|
return None
|
|
378
|
-
|
|
467
|
+
|
|
379
468
|
try:
|
|
380
469
|
conversation_result = await config.memory.provider.get_conversation(conversation_id)
|
|
381
|
-
if hasattr(conversation_result,
|
|
470
|
+
if hasattr(conversation_result, "data") and conversation_result.data:
|
|
382
471
|
metadata = conversation_result.data.metadata
|
|
383
|
-
regeneration_points = metadata.get(
|
|
384
|
-
print(
|
|
472
|
+
regeneration_points = metadata.get("regeneration_points", [])
|
|
473
|
+
print(
|
|
474
|
+
f"[JAF:REGENERATION] Retrieved {len(regeneration_points)} regeneration points for {conversation_id}"
|
|
475
|
+
)
|
|
385
476
|
return regeneration_points
|
|
386
477
|
else:
|
|
387
478
|
print(f"[JAF:REGENERATION] No conversation data found for {conversation_id}")
|
|
388
479
|
return []
|
|
389
480
|
except Exception as e:
|
|
390
481
|
print(f"[JAF:REGENERATION] Failed to get regeneration points: {e}")
|
|
391
|
-
|
|
482
|
+
|
|
392
483
|
return []
|