Resolution favours main for all conflicts (new orchestrator shape with artifacts/file_names/conversation_history/resume_with, WorkflowOutcome enum, RAG, ledger agent, deleted engine/config/.env.example). Re-wires our three form-fill agents (FormAnalyserAgent, FormFillerAgent, DocumentExtractorAgent) into the new app layout: - contracts/__init__.py: adds form-fill exports to __all__ - api/dependencies.py: adds get_form_analyser_agent / get_form_filler_agent / get_document_extractor_agent - api/app.py: instantiates the three agents in lifespan and registers form_fill_router - api/routes/__init__.py: exports form_fill_router - tests/test_stirling_api.py: imports and registers form-fill stubs - tests/test_stirling_contracts.py: adds back KnowledgeUpdateResponse discriminator test Form fill remains accessible via its own endpoints (/api/v1/form/ai/analyse, /fill-batch, /extract). Not wired as an orchestrator delegate — following main's pattern where orchestrator only routes pdf_edit/pdf_question/user_spec /math_auditor. Follow-ups still needed: - Thread conversation_history into form-fill agent prompts - Align form-fill response outcomes with WorkflowOutcome enum - Decide whether engine should return ToolOperationStep plans (main's new pattern per #6116) or keep returning fill values directly All 127 engine tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
109 lines
3.2 KiB
Python
109 lines
3.2 KiB
Python
from stirling.config import AppSettings
|
|
from stirling.contracts import (
|
|
AgentExecutionRequest,
|
|
AgentSpec,
|
|
AgentSpecStep,
|
|
EditPlanResponse,
|
|
ExecutionContext,
|
|
ExtractedFileText,
|
|
ExtractedTextArtifact,
|
|
KnowledgeEntry,
|
|
KnowledgeUpdateResponse,
|
|
OrchestratorRequest,
|
|
PdfQuestionAnswerResponse,
|
|
PdfTextSelection,
|
|
ToolOperationStep,
|
|
)
|
|
from stirling.models.tool_models import Angle, RotatePdfParams, ToolEndpoint
|
|
|
|
|
|
def test_orchestrator_request_accepts_user_message() -> None:
|
|
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:
|
|
steps: list[AgentSpecStep] = [
|
|
ToolOperationStep(
|
|
tool=ToolEndpoint.ROTATE_PDF,
|
|
parameters=RotatePdfParams(angle=Angle(90)),
|
|
)
|
|
]
|
|
request = AgentExecutionRequest(
|
|
agent_spec=AgentSpec(
|
|
name="Invoice cleanup",
|
|
description="Normalise inbound invoices",
|
|
objective="Prepare uploads for accounting review",
|
|
steps=steps,
|
|
),
|
|
current_step_index=0,
|
|
execution_context=ExecutionContext(input_files=["invoice.pdf"]),
|
|
)
|
|
|
|
assert request.agent_spec.steps[0].kind == "tool"
|
|
|
|
|
|
def test_edit_plan_response_has_typed_steps() -> None:
|
|
steps = [ToolOperationStep(tool=ToolEndpoint.ROTATE_PDF, parameters=RotatePdfParams(angle=Angle(90)))]
|
|
response = EditPlanResponse(
|
|
summary="Rotate the input PDF by 90 degrees.",
|
|
steps=steps,
|
|
)
|
|
|
|
assert response.steps[0].tool == ToolEndpoint.ROTATE_PDF
|
|
|
|
|
|
def test_pdf_question_answer_defaults_evidence_list() -> None:
|
|
response = PdfQuestionAnswerResponse(answer="The invoice total is 120.00")
|
|
|
|
assert response.evidence == []
|
|
|
|
|
|
def test_knowledge_update_response_discriminator() -> None:
|
|
update = KnowledgeUpdateResponse(
|
|
proposed_entries=[KnowledgeEntry(key="name", value="John", source="CV")],
|
|
message="Extracted.",
|
|
)
|
|
assert update.outcome == "knowledge_update"
|
|
|
|
|
|
def test_app_settings_accepts_model_configuration() -> None:
|
|
from pathlib import Path
|
|
|
|
from stirling.config import RagBackend
|
|
|
|
settings = AppSettings(
|
|
smart_model_name="claude-sonnet-4-5-20250929",
|
|
fast_model_name="claude-haiku-4-5-20251001",
|
|
smart_model_max_tokens=8192,
|
|
fast_model_max_tokens=2048,
|
|
rag_backend=RagBackend.SQLITE,
|
|
rag_embedding_model="voyageai:voyage-4",
|
|
rag_store_path=Path(":memory:"),
|
|
rag_pgvector_dsn="",
|
|
rag_chunk_size=512,
|
|
rag_chunk_overlap=64,
|
|
rag_default_top_k=5,
|
|
posthog_enabled=False,
|
|
posthog_api_key="",
|
|
posthog_host="https://eu.i.posthog.com",
|
|
)
|
|
|
|
assert settings.smart_model_name
|
|
assert settings.fast_model_max_tokens == 2048
|