mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 11:23:10 +02:00
Add Java orchestrator to connect to the AI engine (#6003)
# Description of Changes Add Java orchestration layer which can connect and go back and forth with the AI engine to get results for the user. It's expected that the AI engine will not be publicly available and this Java layer will always be in front of it, to manage sessions and auth etc.
This commit is contained in:
@@ -5,10 +5,12 @@ import pytest
|
||||
from stirling.agents import PdfQuestionAgent
|
||||
from stirling.config import AppSettings
|
||||
from stirling.contracts import (
|
||||
ExtractedFileText,
|
||||
PdfQuestionAnswerResponse,
|
||||
PdfQuestionNeedTextResponse,
|
||||
PdfQuestionNeedContentResponse,
|
||||
PdfQuestionNotFoundResponse,
|
||||
PdfQuestionRequest,
|
||||
PdfTextSelection,
|
||||
)
|
||||
from stirling.services import build_runtime
|
||||
|
||||
@@ -34,13 +36,22 @@ def build_test_settings() -> AppSettings:
|
||||
)
|
||||
|
||||
|
||||
def invoice_page() -> ExtractedFileText:
|
||||
return ExtractedFileText(
|
||||
file_name="invoice.pdf",
|
||||
pages=[PdfTextSelection(page_number=1, text="Invoice total: 120.00")],
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_pdf_question_agent_requires_extracted_text() -> None:
|
||||
agent = PdfQuestionAgent(build_runtime(build_test_settings()))
|
||||
|
||||
response = await agent.handle(PdfQuestionRequest(question="What is the total?", extracted_text=""))
|
||||
response = await agent.handle(
|
||||
PdfQuestionRequest(question="What is the total?", page_text=[], file_names=["test.pdf"])
|
||||
)
|
||||
|
||||
assert isinstance(response, PdfQuestionNeedTextResponse)
|
||||
assert isinstance(response, PdfQuestionNeedContentResponse)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
@@ -48,15 +59,15 @@ async def test_pdf_question_agent_returns_grounded_answer() -> None:
|
||||
agent = StubPdfQuestionAgent(
|
||||
PdfQuestionAnswerResponse(
|
||||
answer="The invoice total is 120.00.",
|
||||
evidence=["Invoice total: 120.00"],
|
||||
evidence=[invoice_page()],
|
||||
)
|
||||
)
|
||||
|
||||
response = await agent.handle(
|
||||
PdfQuestionRequest(
|
||||
question="What is the total?",
|
||||
extracted_text="Invoice total: 120.00",
|
||||
file_name="invoice.pdf",
|
||||
page_text=[invoice_page()],
|
||||
file_names=["invoice.pdf"],
|
||||
)
|
||||
)
|
||||
|
||||
@@ -71,8 +82,13 @@ async def test_pdf_question_agent_returns_not_found_when_text_is_insufficient()
|
||||
response = await agent.handle(
|
||||
PdfQuestionRequest(
|
||||
question="What is the total?",
|
||||
extracted_text="This page contains only a shipping address.",
|
||||
file_name="invoice.pdf",
|
||||
page_text=[
|
||||
ExtractedFileText(
|
||||
file_name="invoice.pdf",
|
||||
pages=[PdfTextSelection(page_number=1, text="This page contains only a shipping address.")],
|
||||
)
|
||||
],
|
||||
file_names=["invoice.pdf"],
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@ from stirling.contracts import (
|
||||
EditCannotDoResponse,
|
||||
OrchestratorRequest,
|
||||
PdfEditRequest,
|
||||
PdfQuestionNeedContentResponse,
|
||||
PdfQuestionNotFoundResponse,
|
||||
PdfQuestionRequest,
|
||||
UnsupportedCapabilityResponse,
|
||||
)
|
||||
from stirling.models.tool_models import RotateParams
|
||||
|
||||
@@ -38,8 +38,8 @@ class StubSettingsProvider:
|
||||
|
||||
|
||||
class StubOrchestratorAgent:
|
||||
async def handle(self, request: OrchestratorRequest) -> UnsupportedCapabilityResponse:
|
||||
return UnsupportedCapabilityResponse(capability="pdf_edit", message=request.user_message)
|
||||
async def handle(self, request: OrchestratorRequest) -> PdfQuestionNeedContentResponse:
|
||||
return PdfQuestionNeedContentResponse(reason=request.user_message, files=[], max_pages=1, max_characters=1000)
|
||||
|
||||
|
||||
class StubPdfEditAgent:
|
||||
@@ -115,10 +115,10 @@ def test_health_route() -> None:
|
||||
|
||||
|
||||
def test_orchestrator_route() -> None:
|
||||
response = client.post("/api/v1/orchestrator", json={"userMessage": "route this"})
|
||||
response = client.post("/api/v1/orchestrator", json={"userMessage": "route this", "fileNames": ["test.pdf"]})
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["outcome"] == "unsupported_capability"
|
||||
assert response.json()["outcome"] == "need_content"
|
||||
|
||||
|
||||
def test_pdf_edit_route() -> None:
|
||||
@@ -129,7 +129,14 @@ def test_pdf_edit_route() -> None:
|
||||
|
||||
|
||||
def test_pdf_questions_route() -> None:
|
||||
response = client.post("/api/v1/pdf/questions", json={"question": "what is this?"})
|
||||
response = client.post(
|
||||
"/api/v1/pdf/questions",
|
||||
json={
|
||||
"question": "what is this?",
|
||||
"fileNames": ["test.pdf"],
|
||||
"pageText": [{"fileName": "test.pdf", "pages": [{"pageNumber": 1, "text": "Example"}]}],
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["outcome"] == "not_found"
|
||||
|
||||
@@ -9,17 +9,34 @@ from stirling.contracts import (
|
||||
AgentSpecStep,
|
||||
EditPlanResponse,
|
||||
ExecutionContext,
|
||||
ExtractedFileText,
|
||||
ExtractedTextArtifact,
|
||||
OrchestratorRequest,
|
||||
PdfQuestionAnswerResponse,
|
||||
PdfTextSelection,
|
||||
ToolOperationStep,
|
||||
)
|
||||
from stirling.models.tool_models import OperationId, RotateParams
|
||||
|
||||
|
||||
def test_orchestrator_request_accepts_user_message() -> None:
|
||||
request = OrchestratorRequest(user_message="Rotate the PDF")
|
||||
request = OrchestratorRequest(
|
||||
user_message="Rotate the PDF",
|
||||
file_names=["test.pdf"],
|
||||
artifacts=[
|
||||
ExtractedTextArtifact(
|
||||
files=[
|
||||
ExtractedFileText(
|
||||
file_name="test.pdf",
|
||||
pages=[PdfTextSelection(page_number=1, text="Hello")],
|
||||
)
|
||||
]
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
assert request.user_message == "Rotate the PDF"
|
||||
assert len(request.artifacts) == 1
|
||||
|
||||
|
||||
def test_agent_execution_request_uses_typed_agent_spec() -> None:
|
||||
|
||||
Reference in New Issue
Block a user