From 4ec1bbe895febd8942a2b1c3b068f3dc3ee545a8 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Wed, 2 Sep 2026 10:03:44 +0100 Subject: [PATCH] fix(engine): put the user request last in the planner prompt --- engine/src/stirling/agents/pdf_edit.py | 35 +++++++------------------- engine/tests/test_pdf_edit_agent.py | 11 ++++---- 2 files changed, 15 insertions(+), 31 deletions(-) diff --git a/engine/src/stirling/agents/pdf_edit.py b/engine/src/stirling/agents/pdf_edit.py index c46fdb9f24..f9206a5517 100644 --- a/engine/src/stirling/agents/pdf_edit.py +++ b/engine/src/stirling/agents/pdf_edit.py @@ -283,7 +283,7 @@ class PdfEditAgent: allow_need_content=can_request_content, ) return await agent.select( - self._build_selection_prompt(request, supported_operations, unavailable_operations, repair_note) + self._build_selection_prompt(request, supported_operations, repair_note) ) def _build_selection_agent( @@ -330,7 +330,6 @@ class PdfEditAgent: self, request: PdfEditRequest, supported_operations: Iterable[ToolEndpoint], - unavailable_operations: Iterable[ToolEndpoint], repair_note: str = "", ) -> str: repair_line = ( @@ -343,35 +342,19 @@ class PdfEditAgent: if repair_note else "" ) - unavailable_line = ( - "Unavailable operations (exist but not currently usable): " - f"{self._get_operations_prompt(unavailable_operations)}\n" - if unavailable_operations - else "" - ) return ( - f"Conversation history:\n{format_conversation_history(request.conversation_history)}\n" - f"User request: {request.user_message}\n" - f"Files: {format_file_names(request.files)}\n" f"Supported operations:\n{self._get_supported_operations_prompt(supported_operations)}\n" - f"{unavailable_line}" f"{repair_line}" - f"Extracted page text:\n{format_page_text(request.page_text)}" + f"Conversation history:\n{format_conversation_history(request.conversation_history)}\n" + f"Files: {format_file_names(request.files)}\n" + f"Extracted page text:\n{format_page_text(request.page_text)}\n" + f"User request: {request.user_message}" ) - # Endpoints that exist on the server and are callable via the direct API or the manual UI, - # but are never offered to the AI agent as a routing option. - # - # Why: REDACT_EXECUTE is the preferred AI-driven redaction route. AUTO_REDACT and REDACT are - # legacy endpoints that remain fully functional for human callers (the manual redact UI, direct - # API consumers, pipelines) but would produce a worse experience if the AI routed to them — - # they accept a simpler, less expressive schema and pre-date the unified operation model. - # Hiding them here channels all AI redaction traffic through REDACT_EXECUTE without disabling - # the legacy endpoints for anyone else. - # - # How to reuse: add an endpoint here whenever a legacy endpoint has a preferred replacement - # that the AI should use exclusively. The endpoint remains live on the server; only the AI - # planner is prevented from selecting it. + # Hidden from the AI planner only; still live for the manual UI, direct API and pipelines. + # AUTO_REDACT and REDACT pre-date the unified operation model and take a less expressive + # schema, so AI redaction is channelled through REDACT_EXECUTE. Add an endpoint here when a + # legacy one has a preferred replacement the AI should use exclusively. _AGENT_HIDDEN_ENDPOINTS: frozenset[ToolEndpoint] = frozenset({ToolEndpoint.AUTO_REDACT, ToolEndpoint.REDACT}) def _classify_operations(self, request: PdfEditRequest) -> tuple[list[ToolEndpoint], list[ToolEndpoint]]: diff --git a/engine/tests/test_pdf_edit_agent.py b/engine/tests/test_pdf_edit_agent.py index 15ca39369c..e55934ccd5 100644 --- a/engine/tests/test_pdf_edit_agent.py +++ b/engine/tests/test_pdf_edit_agent.py @@ -156,7 +156,6 @@ def test_repair_prompt_offers_reorder_or_telling_the_user(runtime: AppRuntime) - prompt = agent._build_selection_prompt( PdfEditRequest(user_message="anything", files=[]), list(OPERATIONS), - [], "step 3 (SANITIZE_PDF) accepts PDF but the previous step produces IMAGE.", ) assert "SANITIZE_PDF" in prompt @@ -494,11 +493,13 @@ def test_pdf_edit_selection_prompt_includes_unavailable_operations(runtime: AppR ) supported, unavailable = agent._classify_operations(request) - prompt = agent._build_selection_prompt(request, supported, unavailable) + selection_agent = agent._build_selection_agent(supported, unavailable, allow_need_content=False) + system_prompt = "".join(selection_agent.agent._system_prompts) + prompt = agent._build_selection_prompt(request, supported) - assert "Unavailable operations" in prompt - assert "OCR_PDF" in prompt - assert ToolEndpoint.OCR_PDF.value in prompt + assert "NOT currently available" in system_prompt + assert "OCR_PDF" in system_prompt + assert "OCR_PDF" not in prompt @pytest.mark.anyio