Add document I/O to workflows and surface run artifacts

Let workflow runs consume and produce documents end to end: bridge uploaded
attachments into run-scoped artifacts so nodes receive the input documents
(with a per-run cap and server-computed size/sha256, and the run row pre-created
so produced artifacts are authorized during the run); emit the run id to the
client and add a builder panel that lists, previews, and downloads a run's
artifacts; and allow attaching documents to a Preview run via the existing
upload flow.

Also fixes issues a compliance workflow surfaced: attachment ownership now keys
on the raw identity instead of a sanitized one (the sanitized form could not be
read back and could collide across users); workflow code nodes read prior state
from a state.json data file instead of templating it into the program, so
untrusted document content can never be interpolated into executed code;
structured node output wrapped in code fences is recovered; and the live
speech-to-text ownership check compares the raw identity.
This commit is contained in:
Alex committed 2026-06-24 22:50:37 +01:00
1 parent 396eb92ab0
commit 5a87fa6258
20 files changed
+1186 -47

No files matched your search

@@ -108,6 +108,41 @@ class TestStreamProcessorAgentConfiguration:
assert isinstance(processor.agent_config, dict)
assert processor.agent_id is None
def test_embedded_workflow_without_saved_id(self):
"""A preview run with no saved workflow id carries no ``workflow_id``."""
from application.api.answer.services.stream_processor import StreamProcessor
request_data = {
"question": "Test",
"workflow": {"nodes": [], "edges": []},
}
processor = StreamProcessor(request_data, {"sub": "user_123"})
processor._configure_agent()
assert processor.agent_config["agent_type"] == "workflow"
assert processor.agent_config["workflow"] == {"nodes": [], "edges": []}
assert "workflow_id" not in processor.agent_config
def test_embedded_workflow_with_saved_id_persists_run(self):
"""A saved workflow id alongside the embedded graph is captured so the
run can persist a ``workflow_runs`` row for artifact listing."""
from application.api.answer.services.stream_processor import StreamProcessor
request_data = {
"question": "Test",
"workflow": {"nodes": [], "edges": []},
"workflow_id": "11111111-1111-1111-1111-111111111111",
}
processor = StreamProcessor(request_data, {"sub": "user_123"})
processor._configure_agent()
assert processor.agent_config["agent_type"] == "workflow"
assert (
processor.agent_config["workflow_id"]
== "11111111-1111-1111-1111-111111111111"
)
assert processor.agent_config["workflow_owner"] == "user_123"
@pytest.mark.unit