Resolve artifacts.artifact(id) for classic agents via the conversation parent

The classic prompt renderer now passes artifact_parent={conversation_id} so a
normal agent's prompt can resolve a prior-turn artifact with
{{ artifacts.artifact(id) }}, scoped to its own conversation (parent-derived
authz; a missing conversation_id safely yields an empty lookup). This makes the
artifacts variable surfaced in the agent builder functional, and is the parent
wiring an agent-as-tool / subagent feature would also rely on.
This commit is contained in:
Alex committed 2026-06-25 09:35:34 +01:00
1 parent 0d6c873358
commit e0915bddd2
3 files changed
+40

No files matched your search

+1
View File
@@ -117,6 +117,7 @@ def run_agent_headless(
user_id=owner,
docs=retrieved_docs or None,
docs_together=format_docs_for_prompt(retrieved_docs),
artifact_parent={"conversation_id": conversation_id},
)
except Exception as exc:
logger.warning("Headless prompt rendering failed; using raw prompt: %s", exc)
@@ -1517,6 +1517,7 @@ class StreamProcessor:
docs=docs,
docs_together=docs_together,
tools_data=tools_data,
artifact_parent={"conversation_id": self.conversation_id},
)
# Use the user_id that resolved the model so owner-scoped BYOM
@@ -379,6 +379,44 @@ class TestNamespaceManagerWithTools:
assert "system" in context
assert context["system"]["request_id"] == "req_123"
class TestPromptRendererArtifactParent:
"""render_prompt forwards artifact_parent so artifacts.artifact(id) resolves for classic agents."""
def test_render_resolves_artifact_by_id_with_conversation_parent(self):
from unittest.mock import patch
from application.api.answer.services.prompt_renderer import PromptRenderer
row = {"id": "art-1", "current_version": 1, "kind": "document", "title": "Report"}
version = {"mime_type": "text/html", "filename": "report.html", "size": 12}
with patch(
"application.storage.db.repositories.artifacts.ArtifactsRepository"
) as repo_cls, patch("application.storage.db.session.db_readonly"):
repo = repo_cls.return_value
repo.get_artifact_in_parent.return_value = row
repo.get_version.return_value = version
rendered = PromptRenderer().render_prompt(
"file={{ artifacts.artifact('art-1').filename }}",
artifact_parent={"conversation_id": "conv-1"},
)
assert rendered == "file=report.html"
_, kwargs = repo.get_artifact_in_parent.call_args
assert kwargs.get("conversation_id") == "conv-1"
def test_render_without_parent_yields_empty_lookup(self):
from application.api.answer.services.prompt_renderer import PromptRenderer
# No artifact_parent (e.g. a first-turn conversation_id is None) must not error;
# the lookup returns an empty mapping and the attribute renders blank.
rendered = PromptRenderer().render_prompt(
"[{{ artifacts.artifact('art-1').filename }}]"
)
assert rendered == "[]"
def test_namespace_manager_get_builder(self):
from application.templates.namespaces import NamespaceManager, SystemNamespace