Skip to content

Commit 9dda6bd

Browse files
feat: preserve content blocks for reasoning state
Store response.content (Pydantic models) instead of extracted text to preserve ThinkingBlock with reasoning state for step-to-step preservation. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
1 parent 5ffa9cf commit 9dda6bd

1 file changed

Lines changed: 70 additions & 14 deletions

File tree

amplifier_module_loop_basic/__init__.py

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -193,18 +193,43 @@ async def execute(
193193
if tool_calls:
194194
# Add assistant message with tool calls BEFORE executing them
195195
if hasattr(context, "add_message"):
196-
assistant_msg = {
197-
"role": "assistant",
198-
"content": content if content else "",
199-
"tool_calls": [
200-
{
201-
"id": getattr(tc, "id", None) or tc.get("id"),
202-
"tool": getattr(tc, "name", None) or tc.get("tool"),
203-
"arguments": getattr(tc, "arguments", None) or tc.get("arguments") or {},
204-
}
205-
for tc in tool_calls
206-
],
207-
}
196+
# Store structured content from response.content (our Pydantic models)
197+
response_content = getattr(response, "content", None)
198+
if response_content and isinstance(response_content, list):
199+
assistant_msg = {
200+
"role": "assistant",
201+
"content": [
202+
block.model_dump() if hasattr(block, "model_dump") else block
203+
for block in response_content
204+
],
205+
"tool_calls": [
206+
{
207+
"id": getattr(tc, "id", None) or tc.get("id"),
208+
"tool": getattr(tc, "name", None) or tc.get("tool"),
209+
"arguments": getattr(tc, "arguments", None) or tc.get("arguments") or {},
210+
}
211+
for tc in tool_calls
212+
],
213+
}
214+
else:
215+
assistant_msg = {
216+
"role": "assistant",
217+
"content": content if content else "",
218+
"tool_calls": [
219+
{
220+
"id": getattr(tc, "id", None) or tc.get("id"),
221+
"tool": getattr(tc, "name", None) or tc.get("tool"),
222+
"arguments": getattr(tc, "arguments", None) or tc.get("arguments") or {},
223+
}
224+
for tc in tool_calls
225+
],
226+
}
227+
228+
# Preserve provider metadata (provider-agnostic passthrough)
229+
# This enables providers to maintain state across steps (e.g., OpenAI reasoning items)
230+
if hasattr(response, "metadata") and response.metadata:
231+
assistant_msg["metadata"] = response.metadata
232+
208233
await context.add_message(assistant_msg)
209234

210235
# Execute tools in parallel (user guidance: assume parallel intent when multiple tool calls)
@@ -320,7 +345,22 @@ async def execute_single_tool(tc: Any, group_id: str) -> tuple[str, str]:
320345
if content:
321346
final_content = content
322347
if hasattr(context, "add_message"):
323-
await context.add_message({"role": "assistant", "content": content})
348+
# Store structured content from response.content (our Pydantic models)
349+
response_content = getattr(response, "content", None)
350+
if response_content and isinstance(response_content, list):
351+
assistant_msg = {
352+
"role": "assistant",
353+
"content": [
354+
block.model_dump() if hasattr(block, "model_dump") else block
355+
for block in response_content
356+
],
357+
}
358+
else:
359+
assistant_msg = {"role": "assistant", "content": content}
360+
# Preserve provider metadata (provider-agnostic passthrough)
361+
if hasattr(response, "metadata") and response.metadata:
362+
assistant_msg["metadata"] = response.metadata
363+
await context.add_message(assistant_msg)
324364
break
325365

326366
# No content and no tool calls - this shouldn't happen but handle it
@@ -387,11 +427,27 @@ async def execute_single_tool(tc: Any, group_id: str) -> tuple[str, str]:
387427

388428
response = await provider.complete(chat_request, **kwargs)
389429
content = getattr(response, "content", None)
430+
content_blocks = getattr(response, "content_blocks", None)
390431

391432
if content:
392433
final_content = content
393434
if hasattr(context, "add_message"):
394-
await context.add_message({"role": "assistant", "content": content})
435+
# Store structured content from response.content (our Pydantic models)
436+
response_content = getattr(response, "content", None)
437+
if response_content and isinstance(response_content, list):
438+
assistant_msg = {
439+
"role": "assistant",
440+
"content": [
441+
block.model_dump() if hasattr(block, "model_dump") else block
442+
for block in response_content
443+
],
444+
}
445+
else:
446+
assistant_msg = {"role": "assistant", "content": content}
447+
# Preserve provider metadata (provider-agnostic passthrough)
448+
if hasattr(response, "metadata") and response.metadata:
449+
assistant_msg["metadata"] = response.metadata
450+
await context.add_message(assistant_msg)
395451

396452
except Exception as e:
397453
logger.error(f"Error getting final response after max iterations: {e}")

0 commit comments

Comments
 (0)