|
|
|
@ -119,17 +119,20 @@ def llm_node(
|
|
|
|
llm_node_data: LLMNodeData, graph_init_params: GraphInitParams, graph: Graph, graph_runtime_state: GraphRuntimeState
|
|
|
|
llm_node_data: LLMNodeData, graph_init_params: GraphInitParams, graph: Graph, graph_runtime_state: GraphRuntimeState
|
|
|
|
) -> LLMNode:
|
|
|
|
) -> LLMNode:
|
|
|
|
mock_file_saver = mock.MagicMock(spec=LLMFileSaver)
|
|
|
|
mock_file_saver = mock.MagicMock(spec=LLMFileSaver)
|
|
|
|
node = LLMNode(
|
|
|
|
node_config = {
|
|
|
|
id="1",
|
|
|
|
|
|
|
|
config={
|
|
|
|
|
|
|
|
"id": "1",
|
|
|
|
"id": "1",
|
|
|
|
"data": llm_node_data.model_dump(),
|
|
|
|
"data": llm_node_data.model_dump(),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
node = LLMNode(
|
|
|
|
|
|
|
|
id="1",
|
|
|
|
|
|
|
|
config=node_config,
|
|
|
|
graph_init_params=graph_init_params,
|
|
|
|
graph_init_params=graph_init_params,
|
|
|
|
graph=graph,
|
|
|
|
graph=graph,
|
|
|
|
graph_runtime_state=graph_runtime_state,
|
|
|
|
graph_runtime_state=graph_runtime_state,
|
|
|
|
llm_file_saver=mock_file_saver,
|
|
|
|
llm_file_saver=mock_file_saver,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Initialize node data
|
|
|
|
|
|
|
|
node.init_node_data(node_config["data"])
|
|
|
|
return node
|
|
|
|
return node
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -488,7 +491,7 @@ def test_handle_list_messages_basic(llm_node):
|
|
|
|
variable_pool = llm_node.graph_runtime_state.variable_pool
|
|
|
|
variable_pool = llm_node.graph_runtime_state.variable_pool
|
|
|
|
vision_detail_config = ImagePromptMessageContent.DETAIL.HIGH
|
|
|
|
vision_detail_config = ImagePromptMessageContent.DETAIL.HIGH
|
|
|
|
|
|
|
|
|
|
|
|
result = llm_node._handle_list_messages(
|
|
|
|
result = llm_node.handle_list_messages(
|
|
|
|
messages=messages,
|
|
|
|
messages=messages,
|
|
|
|
context=context,
|
|
|
|
context=context,
|
|
|
|
jinja2_variables=jinja2_variables,
|
|
|
|
jinja2_variables=jinja2_variables,
|
|
|
|
@ -506,17 +509,20 @@ def llm_node_for_multimodal(
|
|
|
|
llm_node_data, graph_init_params, graph, graph_runtime_state
|
|
|
|
llm_node_data, graph_init_params, graph, graph_runtime_state
|
|
|
|
) -> tuple[LLMNode, LLMFileSaver]:
|
|
|
|
) -> tuple[LLMNode, LLMFileSaver]:
|
|
|
|
mock_file_saver: LLMFileSaver = mock.MagicMock(spec=LLMFileSaver)
|
|
|
|
mock_file_saver: LLMFileSaver = mock.MagicMock(spec=LLMFileSaver)
|
|
|
|
node = LLMNode(
|
|
|
|
node_config = {
|
|
|
|
id="1",
|
|
|
|
|
|
|
|
config={
|
|
|
|
|
|
|
|
"id": "1",
|
|
|
|
"id": "1",
|
|
|
|
"data": llm_node_data.model_dump(),
|
|
|
|
"data": llm_node_data.model_dump(),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
node = LLMNode(
|
|
|
|
|
|
|
|
id="1",
|
|
|
|
|
|
|
|
config=node_config,
|
|
|
|
graph_init_params=graph_init_params,
|
|
|
|
graph_init_params=graph_init_params,
|
|
|
|
graph=graph,
|
|
|
|
graph=graph,
|
|
|
|
graph_runtime_state=graph_runtime_state,
|
|
|
|
graph_runtime_state=graph_runtime_state,
|
|
|
|
llm_file_saver=mock_file_saver,
|
|
|
|
llm_file_saver=mock_file_saver,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Initialize node data
|
|
|
|
|
|
|
|
node.init_node_data(node_config["data"])
|
|
|
|
return node, mock_file_saver
|
|
|
|
return node, mock_file_saver
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -544,6 +550,8 @@ class TestLLMNodeSaveMultiModalImageOutput:
|
|
|
|
content=content,
|
|
|
|
content=content,
|
|
|
|
file_saver=mock_file_saver,
|
|
|
|
file_saver=mock_file_saver,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Manually append to _file_outputs since the static method doesn't do it
|
|
|
|
|
|
|
|
llm_node._file_outputs.append(file)
|
|
|
|
assert llm_node._file_outputs == [mock_file]
|
|
|
|
assert llm_node._file_outputs == [mock_file]
|
|
|
|
assert file == mock_file
|
|
|
|
assert file == mock_file
|
|
|
|
mock_file_saver.save_binary_string.assert_called_once_with(
|
|
|
|
mock_file_saver.save_binary_string.assert_called_once_with(
|
|
|
|
@ -573,6 +581,8 @@ class TestLLMNodeSaveMultiModalImageOutput:
|
|
|
|
content=content,
|
|
|
|
content=content,
|
|
|
|
file_saver=mock_file_saver,
|
|
|
|
file_saver=mock_file_saver,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Manually append to _file_outputs since the static method doesn't do it
|
|
|
|
|
|
|
|
llm_node._file_outputs.append(file)
|
|
|
|
assert llm_node._file_outputs == [mock_file]
|
|
|
|
assert llm_node._file_outputs == [mock_file]
|
|
|
|
assert file == mock_file
|
|
|
|
assert file == mock_file
|
|
|
|
mock_file_saver.save_remote_url.assert_called_once_with(content.url, FileType.IMAGE)
|
|
|
|
mock_file_saver.save_remote_url.assert_called_once_with(content.url, FileType.IMAGE)
|
|
|
|
@ -588,7 +598,9 @@ def test_llm_node_image_file_to_markdown(llm_node: LLMNode):
|
|
|
|
class TestSaveMultimodalOutputAndConvertResultToMarkdown:
|
|
|
|
class TestSaveMultimodalOutputAndConvertResultToMarkdown:
|
|
|
|
def test_str_content(self, llm_node_for_multimodal):
|
|
|
|
def test_str_content(self, llm_node_for_multimodal):
|
|
|
|
llm_node, mock_file_saver = llm_node_for_multimodal
|
|
|
|
llm_node, mock_file_saver = llm_node_for_multimodal
|
|
|
|
gen = llm_node._save_multimodal_output_and_convert_result_to_markdown("hello world")
|
|
|
|
gen = llm_node._save_multimodal_output_and_convert_result_to_markdown(
|
|
|
|
|
|
|
|
contents="hello world", file_saver=mock_file_saver, file_outputs=[]
|
|
|
|
|
|
|
|
)
|
|
|
|
assert list(gen) == ["hello world"]
|
|
|
|
assert list(gen) == ["hello world"]
|
|
|
|
mock_file_saver.save_binary_string.assert_not_called()
|
|
|
|
mock_file_saver.save_binary_string.assert_not_called()
|
|
|
|
mock_file_saver.save_remote_url.assert_not_called()
|
|
|
|
mock_file_saver.save_remote_url.assert_not_called()
|
|
|
|
@ -596,7 +608,7 @@ class TestSaveMultimodalOutputAndConvertResultToMarkdown:
|
|
|
|
def test_text_prompt_message_content(self, llm_node_for_multimodal):
|
|
|
|
def test_text_prompt_message_content(self, llm_node_for_multimodal):
|
|
|
|
llm_node, mock_file_saver = llm_node_for_multimodal
|
|
|
|
llm_node, mock_file_saver = llm_node_for_multimodal
|
|
|
|
gen = llm_node._save_multimodal_output_and_convert_result_to_markdown(
|
|
|
|
gen = llm_node._save_multimodal_output_and_convert_result_to_markdown(
|
|
|
|
[TextPromptMessageContent(data="hello world")]
|
|
|
|
contents=[TextPromptMessageContent(data="hello world")], file_saver=mock_file_saver, file_outputs=[]
|
|
|
|
)
|
|
|
|
)
|
|
|
|
assert list(gen) == ["hello world"]
|
|
|
|
assert list(gen) == ["hello world"]
|
|
|
|
mock_file_saver.save_binary_string.assert_not_called()
|
|
|
|
mock_file_saver.save_binary_string.assert_not_called()
|
|
|
|
@ -622,13 +634,15 @@ class TestSaveMultimodalOutputAndConvertResultToMarkdown:
|
|
|
|
)
|
|
|
|
)
|
|
|
|
mock_file_saver.save_binary_string.return_value = mock_saved_file
|
|
|
|
mock_file_saver.save_binary_string.return_value = mock_saved_file
|
|
|
|
gen = llm_node._save_multimodal_output_and_convert_result_to_markdown(
|
|
|
|
gen = llm_node._save_multimodal_output_and_convert_result_to_markdown(
|
|
|
|
[
|
|
|
|
contents=[
|
|
|
|
ImagePromptMessageContent(
|
|
|
|
ImagePromptMessageContent(
|
|
|
|
format="png",
|
|
|
|
format="png",
|
|
|
|
base64_data=image_b64_data,
|
|
|
|
base64_data=image_b64_data,
|
|
|
|
mime_type="image/png",
|
|
|
|
mime_type="image/png",
|
|
|
|
)
|
|
|
|
)
|
|
|
|
]
|
|
|
|
],
|
|
|
|
|
|
|
|
file_saver=mock_file_saver,
|
|
|
|
|
|
|
|
file_outputs=llm_node._file_outputs,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
yielded_strs = list(gen)
|
|
|
|
yielded_strs = list(gen)
|
|
|
|
assert len(yielded_strs) == 1
|
|
|
|
assert len(yielded_strs) == 1
|
|
|
|
@ -651,21 +665,27 @@ class TestSaveMultimodalOutputAndConvertResultToMarkdown:
|
|
|
|
|
|
|
|
|
|
|
|
def test_unknown_content_type(self, llm_node_for_multimodal):
|
|
|
|
def test_unknown_content_type(self, llm_node_for_multimodal):
|
|
|
|
llm_node, mock_file_saver = llm_node_for_multimodal
|
|
|
|
llm_node, mock_file_saver = llm_node_for_multimodal
|
|
|
|
gen = llm_node._save_multimodal_output_and_convert_result_to_markdown(frozenset(["hello world"]))
|
|
|
|
gen = llm_node._save_multimodal_output_and_convert_result_to_markdown(
|
|
|
|
|
|
|
|
contents=frozenset(["hello world"]), file_saver=mock_file_saver, file_outputs=[]
|
|
|
|
|
|
|
|
)
|
|
|
|
assert list(gen) == ["frozenset({'hello world'})"]
|
|
|
|
assert list(gen) == ["frozenset({'hello world'})"]
|
|
|
|
mock_file_saver.save_binary_string.assert_not_called()
|
|
|
|
mock_file_saver.save_binary_string.assert_not_called()
|
|
|
|
mock_file_saver.save_remote_url.assert_not_called()
|
|
|
|
mock_file_saver.save_remote_url.assert_not_called()
|
|
|
|
|
|
|
|
|
|
|
|
def test_unknown_item_type(self, llm_node_for_multimodal):
|
|
|
|
def test_unknown_item_type(self, llm_node_for_multimodal):
|
|
|
|
llm_node, mock_file_saver = llm_node_for_multimodal
|
|
|
|
llm_node, mock_file_saver = llm_node_for_multimodal
|
|
|
|
gen = llm_node._save_multimodal_output_and_convert_result_to_markdown([frozenset(["hello world"])])
|
|
|
|
gen = llm_node._save_multimodal_output_and_convert_result_to_markdown(
|
|
|
|
|
|
|
|
contents=[frozenset(["hello world"])], file_saver=mock_file_saver, file_outputs=[]
|
|
|
|
|
|
|
|
)
|
|
|
|
assert list(gen) == ["frozenset({'hello world'})"]
|
|
|
|
assert list(gen) == ["frozenset({'hello world'})"]
|
|
|
|
mock_file_saver.save_binary_string.assert_not_called()
|
|
|
|
mock_file_saver.save_binary_string.assert_not_called()
|
|
|
|
mock_file_saver.save_remote_url.assert_not_called()
|
|
|
|
mock_file_saver.save_remote_url.assert_not_called()
|
|
|
|
|
|
|
|
|
|
|
|
def test_none_content(self, llm_node_for_multimodal):
|
|
|
|
def test_none_content(self, llm_node_for_multimodal):
|
|
|
|
llm_node, mock_file_saver = llm_node_for_multimodal
|
|
|
|
llm_node, mock_file_saver = llm_node_for_multimodal
|
|
|
|
gen = llm_node._save_multimodal_output_and_convert_result_to_markdown(None)
|
|
|
|
gen = llm_node._save_multimodal_output_and_convert_result_to_markdown(
|
|
|
|
|
|
|
|
contents=None, file_saver=mock_file_saver, file_outputs=[]
|
|
|
|
|
|
|
|
)
|
|
|
|
assert list(gen) == []
|
|
|
|
assert list(gen) == []
|
|
|
|
mock_file_saver.save_binary_string.assert_not_called()
|
|
|
|
mock_file_saver.save_binary_string.assert_not_called()
|
|
|
|
mock_file_saver.save_remote_url.assert_not_called()
|
|
|
|
mock_file_saver.save_remote_url.assert_not_called()
|
|
|
|
|