mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-14 10:34:06 +02:00
Inform AI engine which endpoints are disabled on the backend (#6251)
# Description of Changes Have the Java send a list of enabled endpoints to the AI engine so it can intelligently respond to the user that the tool does exist but is disabled on the server so it can't acutally run the operation, instead of the current behaviour where it sends the API call back and then 503 errors because the execution fails when the URL is disabled. <img width="380" height="208" alt="image" src="https://github.com/user-attachments/assets/5842fb2e-2e55-45a5-8205-25515636daae" /> --------- Co-authored-by: EthanHealy01 <[email protected]>
This commit is contained in:
co-authored by
EthanHealy01
parent
5541dd666c
commit
51f5345151
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterable
|
||||
from dataclasses import dataclass
|
||||
|
||||
import pytest
|
||||
@@ -20,7 +21,7 @@ from stirling.contracts import (
|
||||
SupportedCapability,
|
||||
ToolOperationStep,
|
||||
)
|
||||
from stirling.models import FileId
|
||||
from stirling.models import OPERATIONS, FileId
|
||||
from stirling.models.tool_models import Angle, FlattenParams, RotatePdfParams, ToolEndpoint
|
||||
from stirling.services.runtime import AppRuntime
|
||||
|
||||
@@ -69,9 +70,19 @@ class StubPdfEditAgent(PdfEditAgent):
|
||||
if parameter_selector is not None:
|
||||
self.parameter_selector = parameter_selector
|
||||
|
||||
def _get_supported_operations(self, request: PdfEditRequest) -> Iterable[ToolEndpoint]:
|
||||
# Tests construct requests without `enabled_endpoints`; pretend everything is enabled
|
||||
# unless the test explicitly supplies an enabled set.
|
||||
if request.enabled_endpoints:
|
||||
return request.enabled_endpoints
|
||||
return OPERATIONS
|
||||
|
||||
async def _select_plan(
|
||||
self,
|
||||
request: PdfEditRequest,
|
||||
supported_operations: Iterable[ToolEndpoint],
|
||||
unavailable_operations: Iterable[ToolEndpoint],
|
||||
*,
|
||||
allow_need_content: bool = True,
|
||||
) -> PdfEditPlanOutput:
|
||||
return self.selection
|
||||
@@ -207,14 +218,20 @@ async def test_pdf_edit_agent_builds_selection_agent_matching_content_availabili
|
||||
agent = PdfEditAgent(runtime)
|
||||
captured: list[bool] = []
|
||||
|
||||
def record(*, allow_need_content: bool) -> PdfEditSelectionAgent:
|
||||
def record(
|
||||
supported_operations: Iterable[ToolEndpoint],
|
||||
unavailable_operations: Iterable[ToolEndpoint],
|
||||
*,
|
||||
allow_need_content: bool,
|
||||
) -> PdfEditSelectionAgent:
|
||||
captured.append(allow_need_content)
|
||||
raise _StopSelectionError()
|
||||
|
||||
agent._build_selection_agent = record
|
||||
|
||||
supported = list(OPERATIONS)
|
||||
with pytest.raises(_StopSelectionError):
|
||||
await agent._select_plan(PdfEditRequest(user_message="Rotate."))
|
||||
await agent._select_plan(PdfEditRequest(user_message="Rotate."), supported, [])
|
||||
with pytest.raises(_StopSelectionError):
|
||||
await agent._select_plan(
|
||||
PdfEditRequest(
|
||||
@@ -225,10 +242,12 @@ async def test_pdf_edit_agent_builds_selection_agent_matching_content_availabili
|
||||
pages=[PdfTextSelection(page_number=1, text="content")],
|
||||
)
|
||||
],
|
||||
)
|
||||
),
|
||||
supported,
|
||||
[],
|
||||
)
|
||||
with pytest.raises(_StopSelectionError):
|
||||
await agent._select_plan(PdfEditRequest(user_message="Rotate."), allow_need_content=False)
|
||||
await agent._select_plan(PdfEditRequest(user_message="Rotate."), supported, [], allow_need_content=False)
|
||||
|
||||
assert captured == [True, False, False]
|
||||
|
||||
@@ -282,3 +301,95 @@ async def test_pdf_edit_agent_passes_page_text_to_parameter_selector(runtime: Ap
|
||||
)
|
||||
|
||||
assert parameter_selector.calls[0].request.page_text == page_text
|
||||
|
||||
|
||||
def test_pdf_edit_request_drops_unknown_enabled_urls() -> None:
|
||||
request = PdfEditRequest.model_validate(
|
||||
{
|
||||
"user_message": "ignore me",
|
||||
"enabled_endpoints": [
|
||||
ToolEndpoint.COMPRESS_PDF.value,
|
||||
"/api/v1/not-a-real/endpoint",
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
assert request.enabled_endpoints == [ToolEndpoint.COMPRESS_PDF]
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_pdf_edit_agent_supported_operations_defaults_to_empty(
|
||||
runtime: AppRuntime,
|
||||
) -> None:
|
||||
agent = PdfEditAgent(runtime)
|
||||
supported = agent._get_supported_operations(PdfEditRequest(user_message="hi"))
|
||||
|
||||
assert list(supported) == []
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_pdf_edit_agent_supported_operations_uses_provided_list(
|
||||
runtime: AppRuntime,
|
||||
) -> None:
|
||||
agent = PdfEditAgent(runtime)
|
||||
request = PdfEditRequest(
|
||||
user_message="Compress this PDF.",
|
||||
enabled_endpoints=[ToolEndpoint.FLATTEN, ToolEndpoint.ROTATE_PDF],
|
||||
)
|
||||
|
||||
supported = agent._get_supported_operations(request)
|
||||
|
||||
assert list(supported) == [ToolEndpoint.FLATTEN, ToolEndpoint.ROTATE_PDF]
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_pdf_edit_agent_returns_cannot_do_when_no_operations_enabled(
|
||||
runtime: AppRuntime,
|
||||
) -> None:
|
||||
agent = PdfEditAgent(runtime)
|
||||
response = await agent.handle(PdfEditRequest(user_message="Do anything.", enabled_endpoints=[]))
|
||||
|
||||
assert isinstance(response, EditCannotDoResponse)
|
||||
|
||||
|
||||
def test_pdf_edit_selection_prompt_includes_unavailable_operations(runtime: AppRuntime) -> None:
|
||||
agent = PdfEditAgent(runtime)
|
||||
request = PdfEditRequest(
|
||||
user_message="Run OCR.",
|
||||
enabled_endpoints=[ToolEndpoint.FLATTEN],
|
||||
)
|
||||
supported = agent._get_supported_operations(request)
|
||||
unavailable = agent._get_unavailable_operations(supported)
|
||||
|
||||
prompt = agent._build_selection_prompt(request, supported, unavailable)
|
||||
|
||||
assert "Unavailable operations" in prompt
|
||||
assert "OCR_PDF" in prompt
|
||||
assert ToolEndpoint.OCR_PDF.value in prompt
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_pdf_edit_agent_rejects_plan_referencing_unavailable_operations(
|
||||
runtime: AppRuntime,
|
||||
) -> None:
|
||||
parameter_selector = RecordingParameterSelector()
|
||||
agent = StubPdfEditAgent(
|
||||
runtime,
|
||||
PdfEditPlanSelection(
|
||||
operations=[ToolEndpoint.COMPRESS_PDF],
|
||||
summary="Compress.",
|
||||
),
|
||||
parameter_selector=parameter_selector,
|
||||
)
|
||||
|
||||
response = await agent.handle(
|
||||
PdfEditRequest(
|
||||
user_message="Compress this PDF.",
|
||||
enabled_endpoints=[ToolEndpoint.FLATTEN],
|
||||
)
|
||||
)
|
||||
|
||||
assert isinstance(response, EditCannotDoResponse)
|
||||
assert "not available" in response.reason
|
||||
assert "COMPRESS_PDF" in response.reason
|
||||
assert parameter_selector.calls == []
|
||||
|
||||
Reference in New Issue
Block a user