mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-05-10 23:10:08 +02:00
# Description of Changes Redesign AI engine so that it autogenerates the `tool_models.py` file from the OpenAPI spec so the Python has access to the Java API parameters and the full list of Java tools that it can run. CI ensures that whenever someone modifies a tool endpoint that the AI enigne tool models get updated as well (the dev gets told to run `task engine:tool-models`). There's loads of advantages to having the Java be the one that actually executes the tools, rather than the frontend as it was previously set up to theoretically use: - The AI gets much better descriptions of the params from the API docs - It'll be usable headless in the future so a Java daemon could run to execute ops on files in a folder without the need for the UI to run - The Java already has all the logic it needs to execute the tools - We don't need to parse the TypeScript to find the API (which is hard because the TS wasn't designed to be computer-read to extract the API) I've also hooked up the prototype frontend to ensure it's working properly, and have built it in a way that all the tool names can be translated properly, which was always an issue with previous prototypes of this. --------- Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Co-authored-by: EthanHealy01 <80844253+EthanHealy01@users.noreply.github.com>
165 lines
5.3 KiB
Python
165 lines
5.3 KiB
Python
from conftest import build_app_settings
|
|
from fastapi.testclient import TestClient
|
|
|
|
from stirling.api import app
|
|
from stirling.api.dependencies import (
|
|
get_execution_planning_agent,
|
|
get_orchestrator_agent,
|
|
get_pdf_edit_agent,
|
|
get_pdf_question_agent,
|
|
get_user_spec_agent,
|
|
)
|
|
from stirling.config import load_settings
|
|
from stirling.contracts import (
|
|
AgentDraft,
|
|
AgentDraftRequest,
|
|
AgentDraftResponse,
|
|
AgentExecutionRequest,
|
|
AgentRevisionRequest,
|
|
AgentRevisionResponse,
|
|
CannotContinueExecutionAction,
|
|
EditCannotDoResponse,
|
|
OrchestratorRequest,
|
|
PdfEditRequest,
|
|
PdfQuestionNeedContentResponse,
|
|
PdfQuestionNotFoundResponse,
|
|
PdfQuestionRequest,
|
|
)
|
|
from stirling.models.tool_models import Angle, RotatePdfParams
|
|
|
|
|
|
class StubOrchestratorAgent:
|
|
async def handle(self, request: OrchestratorRequest) -> PdfQuestionNeedContentResponse:
|
|
return PdfQuestionNeedContentResponse(reason=request.user_message, files=[], max_pages=1, max_characters=1000)
|
|
|
|
|
|
class StubPdfEditAgent:
|
|
async def handle(self, request: PdfEditRequest) -> EditCannotDoResponse:
|
|
return EditCannotDoResponse(reason=request.user_message)
|
|
|
|
|
|
class StubPdfQuestionAgent:
|
|
async def handle(self, request: PdfQuestionRequest) -> PdfQuestionNotFoundResponse:
|
|
return PdfQuestionNotFoundResponse(reason=request.question)
|
|
|
|
|
|
class StubUserSpecAgent:
|
|
async def draft(self, request: AgentDraftRequest) -> AgentDraftResponse:
|
|
return AgentDraftResponse(
|
|
draft=AgentDraft(
|
|
name="Drafted",
|
|
description="Route wiring test",
|
|
objective=request.user_message,
|
|
steps=[],
|
|
)
|
|
)
|
|
|
|
async def revise(self, request: AgentRevisionRequest) -> AgentRevisionResponse:
|
|
return AgentRevisionResponse(draft=request.current_draft)
|
|
|
|
|
|
class StubExecutionPlanningAgent:
|
|
async def next_action(self, request: AgentExecutionRequest) -> CannotContinueExecutionAction:
|
|
return CannotContinueExecutionAction(reason=str(request.current_step_index))
|
|
|
|
|
|
app.dependency_overrides[load_settings] = build_app_settings
|
|
app.dependency_overrides[get_orchestrator_agent] = lambda: StubOrchestratorAgent()
|
|
app.dependency_overrides[get_pdf_edit_agent] = lambda: StubPdfEditAgent()
|
|
app.dependency_overrides[get_pdf_question_agent] = lambda: StubPdfQuestionAgent()
|
|
app.dependency_overrides[get_user_spec_agent] = lambda: StubUserSpecAgent()
|
|
app.dependency_overrides[get_execution_planning_agent] = lambda: StubExecutionPlanningAgent()
|
|
|
|
client: TestClient = TestClient(app)
|
|
|
|
|
|
def test_health_route() -> None:
|
|
response = client.get("/health")
|
|
|
|
assert response.status_code == 200
|
|
assert response.json()["status"] == "ok"
|
|
|
|
|
|
def test_orchestrator_route() -> None:
|
|
response = client.post("/api/v1/orchestrator", json={"userMessage": "route this", "fileNames": ["test.pdf"]})
|
|
|
|
assert response.status_code == 200
|
|
assert response.json()["outcome"] == "need_content"
|
|
|
|
|
|
def test_pdf_edit_route() -> None:
|
|
response = client.post("/api/v1/pdf/edit", json={"userMessage": "rotate this"})
|
|
|
|
assert response.status_code == 200
|
|
assert response.json()["outcome"] == "cannot_do"
|
|
|
|
|
|
def test_pdf_questions_route() -> None:
|
|
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"
|
|
|
|
|
|
def test_agent_draft_route() -> None:
|
|
response = client.post("/api/v1/agents/draft", json={"userMessage": "build me an agent"})
|
|
|
|
assert response.status_code == 200
|
|
assert response.json()["outcome"] == "draft"
|
|
|
|
|
|
def test_agent_revise_route() -> None:
|
|
response = client.post(
|
|
"/api/v1/agents/revise",
|
|
json={
|
|
"userMessage": "revise it",
|
|
"currentDraft": {
|
|
"name": "Drafted",
|
|
"description": "Route wiring test",
|
|
"objective": "build me an agent",
|
|
"steps": [
|
|
{
|
|
"kind": "tool",
|
|
"tool": "/api/v1/general/rotate-pdf",
|
|
"parameters": RotatePdfParams(angle=Angle(90)).model_dump(by_alias=True),
|
|
}
|
|
],
|
|
},
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert response.json()["outcome"] == "draft"
|
|
|
|
|
|
def test_next_action_route() -> None:
|
|
response = client.post(
|
|
"/api/v1/agents/next-action",
|
|
json={
|
|
"agentSpec": {
|
|
"name": "Drafted",
|
|
"description": "Route wiring test",
|
|
"objective": "build me an agent",
|
|
"steps": [
|
|
{
|
|
"kind": "tool",
|
|
"tool": "/api/v1/general/rotate-pdf",
|
|
"parameters": RotatePdfParams(angle=Angle(90)).model_dump(by_alias=True),
|
|
}
|
|
],
|
|
},
|
|
"currentStepIndex": 0,
|
|
"executionContext": {"inputFiles": ["input.pdf"], "metadata": {}},
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert response.json()["outcome"] == "cannot_continue"
|