Skip to content

Commit c94e3b3

Browse files
committed
fix on prophet forecast tool
1 parent 5987582 commit c94e3b3

File tree

3 files changed

+126
-2
lines changed

3 files changed

+126
-2
lines changed

parrot/handlers/chat_interaction.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,3 +277,61 @@ async def delete(self) -> web.Response:
277277
response={"message": f"Conversation {session_id} not found"},
278278
status=404,
279279
)
280+
281+
async def patch(self) -> web.Response:
282+
"""Delete a specific turn from a conversation."""
283+
storage = self._get_storage()
284+
if storage is None:
285+
self.error(
286+
response={"message": "Chat storage not available"},
287+
status=503,
288+
)
289+
290+
user_id = await self._get_user_id()
291+
if not user_id:
292+
self.error(
293+
response={"message": "User ID not found in session"},
294+
status=401,
295+
)
296+
297+
session_id = self.request.match_info.get("session_id")
298+
if not session_id:
299+
self.error(
300+
response={"message": "session_id is required in path"},
301+
status=400,
302+
)
303+
304+
try:
305+
body = await self.json_data()
306+
except Exception:
307+
body = {}
308+
309+
action = body.get("action")
310+
if action != "delete_turn":
311+
self.error(
312+
response={"message": f"Unknown action: {action}"},
313+
status=400,
314+
)
315+
316+
turn_id = body.get("turn_id")
317+
if not turn_id:
318+
self.error(
319+
response={"message": "turn_id is required"},
320+
status=400,
321+
)
322+
323+
deleted = await storage.delete_turn(
324+
session_id=session_id,
325+
turn_id=turn_id,
326+
)
327+
328+
if deleted:
329+
return self.json_response({
330+
"message": f"Turn {turn_id} deleted",
331+
"session_id": session_id,
332+
"turn_id": turn_id,
333+
})
334+
self.error(
335+
response={"message": f"Turn {turn_id} not found"},
336+
status=404,
337+
)

parrot/storage/chat.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,42 @@ async def delete_conversation(
603603

604604
return deleted
605605

606+
async def delete_turn(
607+
self,
608+
session_id: str,
609+
turn_id: str,
610+
) -> bool:
611+
"""Delete a single turn (user + assistant messages) by turn_id.
612+
613+
Returns:
614+
True if deletion succeeded.
615+
"""
616+
if not self._docdb:
617+
return False
618+
try:
619+
await self._docdb.delete_many(
620+
MESSAGES_COLLECTION,
621+
{"session_id": session_id, "turn_id": turn_id},
622+
)
623+
self.logger.debug(
624+
f"Deleted turn {turn_id} from session {session_id}"
625+
)
626+
# Decrement message count on conversation metadata
627+
try:
628+
await self._docdb.update_one(
629+
CONVERSATIONS_COLLECTION,
630+
{"session_id": session_id},
631+
{"$inc": {"message_count": -2}},
632+
)
633+
except Exception:
634+
pass # Non-critical
635+
return True
636+
except Exception as exc:
637+
self.logger.warning(
638+
f"delete_turn failed for {turn_id} in {session_id}: {exc}"
639+
)
640+
return False
641+
606642
async def get_context_for_agent(
607643
self,
608644
user_id: str,

parrot/tools/prophetforecast.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,38 @@ async def _execute(self, **kwargs: Any) -> ToolResult:
8989
return await loop.run_in_executor(None, self._run_forecast, args)
9090

9191
def _run_forecast(self, args: ProphetForecastArgs) -> ToolResult:
92-
dataframe = self._resolve_dataframe(args.dataframe)
93-
cleaned_df = self._prepare_dataframe(dataframe, args)
92+
# Resolve DataFrame with recoverable error handling
93+
try:
94+
dataframe = self._resolve_dataframe(args.dataframe)
95+
except ValueError as e:
96+
# Return recoverable error so LLM can retry with correct DataFrame
97+
return ToolResult(
98+
success=False,
99+
status="error",
100+
result=None,
101+
error=str(e),
102+
metadata={
103+
"requested_dataframe": args.dataframe,
104+
"available_dataframes": list(self.dataframes.keys()),
105+
"recoverable": True,
106+
},
107+
)
108+
109+
# Prepare DataFrame with recoverable error handling
110+
try:
111+
cleaned_df = self._prepare_dataframe(dataframe, args)
112+
except ValueError as e:
113+
return ToolResult(
114+
success=False,
115+
status="error",
116+
result=None,
117+
error=str(e),
118+
metadata={
119+
"dataframe": args.dataframe,
120+
"available_columns": list(dataframe.columns),
121+
"recoverable": True,
122+
},
123+
)
94124

95125
model = Prophet()
96126
model.fit(cleaned_df)

0 commit comments

Comments
 (0)