Skip to content

Commit

Permalink
fix: Make from_template_and_variables async for backwards compatibili…
Browse files Browse the repository at this point in the history
…ty, add sync version, add tests and small fixes (#4500)

* Refactor `trace_name` property to use `_id` instead of `_vertex.id` for component identification

* Handle missing session_id attribute in component and agent classes

* Add SUPPORTED_VERSIONS constant for version tracking in tests

* Add utility to download components from GitHub in integration tests

* Rename TestComponent to ComponentForTesting to avoid conflict with pytest

* test: enhance PromptComponent tests for version support

Add parameterized testing for supported versions and a validation for the latest PromptComponent. This improves test coverage and ensures compatibility across different versions.

* refactor: move build_component_instance_for_tests utility to integration utils

* Make `from_template_and_variables` async for backwards compatibility and add sync version

* Refactor `PromptComponent` to use `Message.from_template` method across starter projects JSON files.

* add await to `from_template_and_variables` call

* Add async test for message prompt serialization and update cache directory handling

- Introduced `async` in `test_message_prompt_serialization` for asynchronous message creation.
- Added `test_message_sync_prompt_serialization` for synchronous message testing.
- Updated cache directory paths to "langflow_test" for test isolation.
- Utilized `monkeypatch` to set environment variable for cache directory in `langflow_cache_dir` fixture.

* Add fixture and existence check in test_schema_message.py

- Use `langflow_cache_dir` fixture in `test_message_with_single_image`.
- Add assertion to verify the existence of `second_image`.

* Add base test class to ensure file names are defined for all supported versions

* Remove default value for 'file_name' parameter in 'build_component_instance_for_tests' function

* Enhance `TestPromptComponent` with version-specific file name handling and base class integration

* Refactor test_prompt_component_versions to use FILE_NAMES_MAPPING directly

* Add component version tests and base classes for testing with/without client

* Simplify `build_component_instance_for_tests` by returning `cc_class` directly

* Refactor `TestPromptComponent` to use `ComponentTestBaseWithClient` and remove version tests

* Add assertion for LANGFLOW_CONFIG_DIR in test_message_with_multiple_images

* Refactor: update method call to `from_template` in `langchain_hub.py`

* Handle missing '_id' attribute in 'trace_name' method of custom_component.py

* Optimize `get_and_cache_all_types_dict` call by removing unnecessary thread usage in test.
  • Loading branch information
ogabrielluiz authored Nov 12, 2024
1 parent 8bb0f65 commit 768e6f0
Show file tree
Hide file tree
Showing 21 changed files with 1,013 additions and 32 deletions.
9 changes: 8 additions & 1 deletion src/backend/base/langflow/base/agents/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,19 @@ async def run_agent(
if self.chat_history:
input_dict["chat_history"] = data_to_messages(self.chat_history)

if hasattr(self, "graph"):
session_id = self.graph.session_id
elif hasattr(self, "_session_id"):
session_id = self._session_id
else:
session_id = None

agent_message = Message(
sender=MESSAGE_SENDER_AI,
sender_name=self.display_name or "Agent",
properties={"icon": "Bot", "state": "partial"},
content_blocks=[ContentBlock(title="Agent Steps", contents=[])],
session_id=self.graph.session_id,
session_id=session_id,
)
try:
result = await process_agent_events(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ async def build_prompt(
original_params["template"] = prompt_value.to_string()

# Now pass the filtered attributes to the function
prompt = Message.from_template_and_variables(**original_params)
prompt = Message.from_template(**original_params)

self.status = prompt.text

Expand Down
2 changes: 1 addition & 1 deletion src/backend/base/langflow/components/prompts/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class PromptComponent(Component):
]

async def build_prompt(self) -> Message:
prompt = Message.from_template_and_variables(**self._attributes)
prompt = Message.from_template(**self._attributes)
self.status = prompt.text
return prompt

Expand Down
10 changes: 8 additions & 2 deletions src/backend/base/langflow/custom/custom_component/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -792,22 +792,28 @@ async def _build_without_tracing(self):

async def build_results(self):
"""Build the results of the component."""
if hasattr(self, "graph"):
session_id = self.graph.session_id
elif hasattr(self, "_session_id"):
session_id = self._session_id
else:
session_id = None
try:
if self._tracing_service:
return await self._build_with_tracing()
return await self._build_without_tracing()
except StreamingError as e:
self.send_error(
exception=e.cause,
session_id=self.graph.session_id,
session_id=session_id,
trace_name=getattr(self, "trace_name", None),
source=e.source,
)
raise e.cause # noqa: B904
except Exception as e:
self.send_error(
exception=e,
session_id=self.graph.session_id,
session_id=session_id,
source=Source(id=self._id, display_name=self.display_name, source=self.display_name),
trace_name=getattr(self, "trace_name", None),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,12 @@ def set_parameters(self, parameters: dict) -> None:

@property
def trace_name(self) -> str:
if self._vertex is None:
msg = "Vertex is not set"
if hasattr(self, "_id") and self._id is None:
msg = "Component id is not set"
raise ValueError(msg)
return f"{self.display_name} ({self._vertex.id})"
if hasattr(self, "_id"):
return f"{self.display_name} ({self._id})"
return f"{self.display_name}"

def update_state(self, name: str, value: Any) -> None:
if not self._vertex:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@
"show": true,
"title_case": false,
"type": "code",
"value": "from langflow.base.prompts.api_utils import process_prompt_template\nfrom langflow.custom import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import Output, PromptInput\nfrom langflow.schema.message import Message\nfrom langflow.template.utils import update_template_values\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt Message\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(self) -> Message:\n prompt = Message.from_template_and_variables(**self._attributes)\n self.status = prompt.text\n return prompt\n\n def _update_template(self, frontend_node: dict):\n prompt_template = frontend_node[\"template\"][\"template\"][\"value\"]\n custom_fields = frontend_node[\"custom_fields\"]\n frontend_node_template = frontend_node[\"template\"]\n _ = process_prompt_template(\n template=prompt_template,\n name=\"template\",\n custom_fields=custom_fields,\n frontend_node_template=frontend_node_template,\n )\n return frontend_node\n\n def post_code_processing(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"This function is called after the code validation is done.\"\"\"\n frontend_node = super().post_code_processing(new_frontend_node, current_frontend_node)\n template = frontend_node[\"template\"][\"template\"][\"value\"]\n # Kept it duplicated for backwards compatibility\n _ = process_prompt_template(\n template=template,\n name=\"template\",\n custom_fields=frontend_node[\"custom_fields\"],\n frontend_node_template=frontend_node[\"template\"],\n )\n # Now that template is updated, we need to grab any values that were set in the current_frontend_node\n # and update the frontend_node with those values\n update_template_values(new_template=frontend_node, previous_template=current_frontend_node[\"template\"])\n return frontend_node\n\n def _get_fallback_input(self, **kwargs):\n return DefaultPromptField(**kwargs)\n"
"value": "from langflow.base.prompts.api_utils import process_prompt_template\nfrom langflow.custom import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import Output, PromptInput\nfrom langflow.schema.message import Message\nfrom langflow.template.utils import update_template_values\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt Message\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(self) -> Message:\n prompt = Message.from_template(**self._attributes)\n self.status = prompt.text\n return prompt\n\n def _update_template(self, frontend_node: dict):\n prompt_template = frontend_node[\"template\"][\"template\"][\"value\"]\n custom_fields = frontend_node[\"custom_fields\"]\n frontend_node_template = frontend_node[\"template\"]\n _ = process_prompt_template(\n template=prompt_template,\n name=\"template\",\n custom_fields=custom_fields,\n frontend_node_template=frontend_node_template,\n )\n return frontend_node\n\n def post_code_processing(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"This function is called after the code validation is done.\"\"\"\n frontend_node = super().post_code_processing(new_frontend_node, current_frontend_node)\n template = frontend_node[\"template\"][\"template\"][\"value\"]\n # Kept it duplicated for backwards compatibility\n _ = process_prompt_template(\n template=template,\n name=\"template\",\n custom_fields=frontend_node[\"custom_fields\"],\n frontend_node_template=frontend_node[\"template\"],\n )\n # Now that template is updated, we need to grab any values that were set in the current_frontend_node\n # and update the frontend_node with those values\n update_template_values(new_template=frontend_node, previous_template=current_frontend_node[\"template\"])\n return frontend_node\n\n def _get_fallback_input(self, **kwargs):\n return DefaultPromptField(**kwargs)\n"
},
"template": {
"advanced": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@
"show": true,
"title_case": false,
"type": "code",
"value": "from langflow.base.prompts.api_utils import process_prompt_template\nfrom langflow.custom import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import Output, PromptInput\nfrom langflow.schema.message import Message\nfrom langflow.template.utils import update_template_values\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt Message\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(self) -> Message:\n prompt = Message.from_template_and_variables(**self._attributes)\n self.status = prompt.text\n return prompt\n\n def _update_template(self, frontend_node: dict):\n prompt_template = frontend_node[\"template\"][\"template\"][\"value\"]\n custom_fields = frontend_node[\"custom_fields\"]\n frontend_node_template = frontend_node[\"template\"]\n _ = process_prompt_template(\n template=prompt_template,\n name=\"template\",\n custom_fields=custom_fields,\n frontend_node_template=frontend_node_template,\n )\n return frontend_node\n\n def post_code_processing(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"This function is called after the code validation is done.\"\"\"\n frontend_node = super().post_code_processing(new_frontend_node, current_frontend_node)\n template = frontend_node[\"template\"][\"template\"][\"value\"]\n # Kept it duplicated for backwards compatibility\n _ = process_prompt_template(\n template=template,\n name=\"template\",\n custom_fields=frontend_node[\"custom_fields\"],\n frontend_node_template=frontend_node[\"template\"],\n )\n # Now that template is updated, we need to grab any values that were set in the current_frontend_node\n # and update the frontend_node with those values\n update_template_values(new_template=frontend_node, previous_template=current_frontend_node[\"template\"])\n return frontend_node\n\n def _get_fallback_input(self, **kwargs):\n return DefaultPromptField(**kwargs)\n"
"value": "from langflow.base.prompts.api_utils import process_prompt_template\nfrom langflow.custom import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import Output, PromptInput\nfrom langflow.schema.message import Message\nfrom langflow.template.utils import update_template_values\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt Message\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(self) -> Message:\n prompt = Message.from_template(**self._attributes)\n self.status = prompt.text\n return prompt\n\n def _update_template(self, frontend_node: dict):\n prompt_template = frontend_node[\"template\"][\"template\"][\"value\"]\n custom_fields = frontend_node[\"custom_fields\"]\n frontend_node_template = frontend_node[\"template\"]\n _ = process_prompt_template(\n template=prompt_template,\n name=\"template\",\n custom_fields=custom_fields,\n frontend_node_template=frontend_node_template,\n )\n return frontend_node\n\n def post_code_processing(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"This function is called after the code validation is done.\"\"\"\n frontend_node = super().post_code_processing(new_frontend_node, current_frontend_node)\n template = frontend_node[\"template\"][\"template\"][\"value\"]\n # Kept it duplicated for backwards compatibility\n _ = process_prompt_template(\n template=template,\n name=\"template\",\n custom_fields=frontend_node[\"custom_fields\"],\n frontend_node_template=frontend_node[\"template\"],\n )\n # Now that template is updated, we need to grab any values that were set in the current_frontend_node\n # and update the frontend_node with those values\n update_template_values(new_template=frontend_node, previous_template=current_frontend_node[\"template\"])\n return frontend_node\n\n def _get_fallback_input(self, **kwargs):\n return DefaultPromptField(**kwargs)\n"
},
"instructions": {
"advanced": false,
Expand Down
Loading

0 comments on commit 768e6f0

Please sign in to comment.