mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-05-21 08:37:05 +08:00
Fix: add codeexec attachments output (#14787)
### What problem does this PR solve? add codeexec attachments output ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
@ -37,6 +37,7 @@ SYSTEM_OUTPUT_KEYS = frozenset(
|
||||
{
|
||||
"content",
|
||||
"actual_type",
|
||||
"attachments",
|
||||
"_ERROR",
|
||||
"_ARTIFACTS",
|
||||
"_ATTACHMENT_CONTENT",
|
||||
@ -312,7 +313,10 @@ module.exports = { main };
|
||||
self.lang = Language.PYTHON.value
|
||||
self.script = 'def main(arg1: str, arg2: str) -> dict: return {"result": arg1 + arg2}'
|
||||
self.arguments = {}
|
||||
self.outputs = {"result": {"value": "", "type": "object"}}
|
||||
self.outputs = {
|
||||
"result": {"value": "", "type": "object"},
|
||||
"attachments": {"value": [], "type": "Array<String>"},
|
||||
}
|
||||
|
||||
def check(self):
|
||||
self.check_valid_value(self.lang, "Support languages", ["python", "python3", "nodejs", "javascript"])
|
||||
@ -468,11 +472,13 @@ class CodeExec(ToolBase, ABC):
|
||||
self.set_output("_ARTIFACTS", artifact_urls or None)
|
||||
attachment_text = self._build_attachment_content(artifacts, artifact_urls)
|
||||
self.set_output("_ATTACHMENT_CONTENT", attachment_text)
|
||||
self.set_output("attachments", self._build_attachment_markdown_list(artifact_urls))
|
||||
if attachment_text:
|
||||
content_parts.append(attachment_text)
|
||||
else:
|
||||
self.set_output("_ARTIFACTS", None)
|
||||
self.set_output("_ATTACHMENT_CONTENT", "")
|
||||
self.set_output("attachments", [])
|
||||
|
||||
self.set_output("content", "\n\n".join([part for part in content_parts if part]).strip())
|
||||
|
||||
@ -641,6 +647,23 @@ class CodeExec(ToolBase, ABC):
|
||||
return f"attachment_count: {len(sections)}\n\n" + "\n\n".join(sections)
|
||||
return "attachment_count: 0"
|
||||
|
||||
def _build_attachment_markdown_list(self, artifact_urls: list[dict]) -> list[str]:
|
||||
markdown_items = []
|
||||
for art in artifact_urls:
|
||||
name = _art_field(art, "name")
|
||||
url = _art_field(art, "url")
|
||||
mime_type = str(_art_field(art, "mime_type") or "").strip().lower()
|
||||
if not name:
|
||||
continue
|
||||
|
||||
if mime_type.startswith("image/") and url:
|
||||
markdown_items.append(f"")
|
||||
elif url:
|
||||
markdown_items.append(f"[Download {name}]({url})")
|
||||
else:
|
||||
markdown_items.append(name)
|
||||
return markdown_items
|
||||
|
||||
def _normalize_attachment_type(self, name: str, mime_type: str) -> str:
|
||||
mime_type = str(mime_type or "").strip().lower()
|
||||
if mime_type.startswith("image/"):
|
||||
|
||||
@ -140,7 +140,7 @@ def test_select_business_output_ignores_system_outputs():
|
||||
"actual_type": {"value": "", "type": "string"},
|
||||
"_ERROR": {"value": "", "type": "string"},
|
||||
"_ARTIFACTS": {"value": [], "type": "Array<Object>"},
|
||||
"_ATTACHMENT_CONTENT": {"value": "", "type": "string"},
|
||||
"attachments": {"value": [], "type": "Array<String>"},
|
||||
"raw_result": {"value": None, "type": "Any"},
|
||||
"_created_time": {"value": 1.0, "type": "Number"},
|
||||
"_elapsed_time": {"value": 2.0, "type": "Number"},
|
||||
@ -297,7 +297,7 @@ def test_legacy_multi_output_schema_is_rejected():
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", ["content", "actual_type", "_ERROR", "_ARTIFACTS", "_ATTACHMENT_CONTENT", "raw_result"])
|
||||
@pytest.mark.parametrize("name", ["content", "actual_type", "attachments", "_ERROR", "_ARTIFACTS", "raw_result"])
|
||||
def test_reserved_business_output_names_are_rejected(name):
|
||||
module = _load_module()
|
||||
with pytest.raises(module.ContractError, match="reserved output name"):
|
||||
@ -387,7 +387,6 @@ def test_process_execution_result_returns_early_for_stderr_only_without_artifact
|
||||
def test_process_execution_result_appends_artifact_content_to_canonical_content():
|
||||
tool = _build_code_exec("Object")
|
||||
tool._upload_artifacts = lambda _artifacts: [{"name": "chart.png", "url": "/artifact/chart.png", "mime_type": "image/png", "size": 12}]
|
||||
tool._build_attachment_content = lambda _artifacts, _artifact_urls: "attachment_count: 1\n\nattachment1 (image): chart.png\nparsed artifact"
|
||||
|
||||
result = tool._process_execution_result(
|
||||
'{"foo": "bar"}',
|
||||
@ -400,8 +399,7 @@ def test_process_execution_result_appends_artifact_content_to_canonical_content(
|
||||
assert result["content"] == '{\n "foo": "bar"\n}\n\nattachment_count: 1\n\nattachment1 (image): chart.png\nparsed artifact'
|
||||
assert result["_ARTIFACTS"] == [{"name": "chart.png", "url": "/artifact/chart.png", "mime_type": "image/png", "size": 12}]
|
||||
assert result["_ARTIFACTS"][0]["mime_type"] == "image/png"
|
||||
assert result["_ATTACHMENT_CONTENT"] == "attachment_count: 1\n\nattachment1 (image): chart.png\nparsed artifact"
|
||||
assert "attachment1 (image): chart.png" in result["_ATTACHMENT_CONTENT"]
|
||||
assert result["attachments"] == [""]
|
||||
|
||||
|
||||
def test_process_execution_result_without_artifacts_clears_stale_artifacts_output():
|
||||
|
||||
@ -4,6 +4,7 @@ import { CodeOutputContract } from '../../form/code-form/utils';
|
||||
const SYSTEM_OUTPUT_NAMES = new Set([
|
||||
'_ERROR',
|
||||
'_ARTIFACTS',
|
||||
'attachments',
|
||||
'_ATTACHMENT_CONTENT',
|
||||
]);
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ const CodeExecReservedOutputKeys = [
|
||||
'content',
|
||||
'actual_type',
|
||||
'raw_result',
|
||||
'attachments',
|
||||
'_ERROR',
|
||||
'_ARTIFACTS',
|
||||
'_ATTACHMENT_CONTENT',
|
||||
@ -30,6 +31,10 @@ export const CodeExecPanelSystemOutputs: ICodeForm['outputs'] = {
|
||||
type: 'String',
|
||||
value: '',
|
||||
},
|
||||
attachments: {
|
||||
type: 'Array<String>',
|
||||
value: [],
|
||||
},
|
||||
};
|
||||
|
||||
const CodeExecReservedOutputKeySet = new Set<string>(
|
||||
|
||||
@ -73,6 +73,10 @@ function getNodeOutputs(x: BaseNode) {
|
||||
type: JsonSchemaDataType.String,
|
||||
value: '',
|
||||
},
|
||||
attachments: outputs.attachments ?? {
|
||||
type: 'Array<String>',
|
||||
value: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user