Skip to content

Commit f76a710

Browse files
committed
fix(api): resolve F-04 sanitize backend failures
1 parent 4232891 commit f76a710

4 files changed

Lines changed: 66 additions & 11 deletions

File tree

src/cas_reference_product/app.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from .config import Settings, get_settings
88
from .models import PromptEnvelope, WorkflowResult
99
from .telemetry import configure_telemetry
10-
from .workflow import WorkflowOrchestrator, build_workflow_agent_service
10+
from .workflow import WorkflowAgentServiceError, WorkflowOrchestrator, build_workflow_agent_service
1111

1212

1313
def create_app(settings: Settings | None = None) -> FastAPI:
@@ -37,7 +37,10 @@ def execute(envelope: PromptEnvelope, request: Request) -> WorkflowResult:
3737
raise HTTPException(status_code=503, detail="Workflow backend is not ready")
3838
request.state.correlation_id = envelope.correlationId
3939
orchestrator = WorkflowOrchestrator(service, app_settings.repository)
40-
return orchestrator.execute(envelope)
40+
try:
41+
return orchestrator.execute(envelope)
42+
except WorkflowAgentServiceError:
43+
raise HTTPException(status_code=502, detail="Workflow backend request failed") from None
4144

4245
@app.get("/")
4346
def root() -> dict[str, Any]:

src/cas_reference_product/workflow.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ class WorkflowAgentService(Protocol):
2020
def run(self, envelope: PromptEnvelope) -> str: ...
2121

2222

23+
class WorkflowAgentServiceError(RuntimeError):
24+
"""Stable application error raised when an external workflow backend fails."""
25+
26+
2327
class LocalWorkflowAgentService:
2428
def run(self, envelope: PromptEnvelope) -> str:
2529
return (
@@ -42,15 +46,18 @@ def __init__(self, settings: Settings) -> None:
4246

4347
def run(self, envelope: PromptEnvelope) -> str:
4448
with tracer.start_as_current_span("foundry.responses.create"):
45-
response = self._client.responses.create(
46-
input=envelope.prompt,
47-
extra_body={
48-
"agent_reference": {
49-
"name": self._agent_name,
50-
"type": "agent_reference",
51-
}
52-
},
53-
)
49+
try:
50+
response = self._client.responses.create(
51+
input=envelope.prompt,
52+
extra_body={
53+
"agent_reference": {
54+
"name": self._agent_name,
55+
"type": "agent_reference",
56+
}
57+
},
58+
)
59+
except Exception:
60+
raise WorkflowAgentServiceError("Foundry workflow invocation failed") from None
5461
return response.output_text
5562

5663

tests/test_api.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1+
from unittest.mock import patch
2+
13
from fastapi.testclient import TestClient
24

35
from cas_reference_product.app import create_app
46
from cas_reference_product.config import Settings
7+
from cas_reference_product.workflow import WorkflowAgentServiceError
8+
9+
10+
class FailingExternalService:
11+
def run(self, envelope) -> str:
12+
raise WorkflowAgentServiceError("sensitive provider detail")
513

614

715
def test_workflow_api_emits_canonical_events(envelope) -> None:
@@ -31,3 +39,17 @@ def test_incomplete_foundry_configuration_is_not_ready() -> None:
3139
assert client.get("/health/live").status_code == 200
3240
assert client.get("/health/ready").status_code == 503
3341
assert client.post("/api/v1/workflows", json={}).status_code == 422
42+
43+
44+
def test_workflow_api_sanitizes_external_service_failures(envelope) -> None:
45+
with patch(
46+
"cas_reference_product.app.build_workflow_agent_service",
47+
return_value=FailingExternalService(),
48+
):
49+
client = TestClient(create_app(Settings()))
50+
51+
response = client.post("/api/v1/workflows", json=envelope.model_dump(mode="json"))
52+
53+
assert response.status_code == 502
54+
assert response.json() == {"detail": "Workflow backend request failed"}
55+
assert "sensitive" not in response.text

tests/test_service_factory.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
from unittest.mock import patch
22

3+
import pytest
4+
35
from cas_reference_product.config import Settings
46
from cas_reference_product.workflow import (
57
FoundryWorkflowAgentService,
68
LocalWorkflowAgentService,
9+
WorkflowAgentServiceError,
710
build_workflow_agent_service,
811
)
912

@@ -54,3 +57,23 @@ def test_foundry_service_uses_next_gen_agent_reference(envelope) -> None:
5457
},
5558
)
5659
assert result == "Foundry result"
60+
61+
62+
def test_foundry_service_sanitizes_sdk_failure(envelope) -> None:
63+
settings = Settings(
64+
environment="prod",
65+
workflow_backend="foundry",
66+
foundry_project_endpoint="https://example.services.ai.azure.com/api/projects/example",
67+
foundry_agent_name="cas-reference-agent",
68+
)
69+
with (
70+
patch("cas_reference_product.workflow.build_credential"),
71+
patch("cas_reference_product.workflow.AIProjectClient") as project_client,
72+
):
73+
project_client.return_value.get_openai_client.return_value.responses.create.side_effect = (
74+
RuntimeError("sensitive provider detail")
75+
)
76+
service = FoundryWorkflowAgentService(settings)
77+
78+
with pytest.raises(WorkflowAgentServiceError, match="Foundry workflow invocation failed"):
79+
service.run(envelope)

0 commit comments

Comments
 (0)