Skip to content

Commit

Permalink
ci: script and workflow to update starter projects (langflow-ai#5195)
Browse files Browse the repository at this point in the history
  • Loading branch information
jordanrfrazier authored Dec 12, 2024
1 parent 9ead148 commit ba31d43
Show file tree
Hide file tree
Showing 23 changed files with 1,350 additions and 2,112 deletions.
22 changes: 22 additions & 0 deletions .github/workflows/py_autofix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,25 @@ jobs:
- uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c
- name: Minimize uv cache
run: uv cache prune --ci

update-starter-projects:
name: Update Starter Projects
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "Setup Environment"
uses: ./.github/actions/setup-uv

- name: "Install dependencies"
run: |
uv sync --frozen
uv pip install -e .
- name: Run starter projects update
run: uv run python scripts/ci/update_starter_projects.py

- uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c

- name: Minimize uv cache
run: uv cache prune --ci

4 changes: 0 additions & 4 deletions .github/workflows/style-check-py.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ on:
paths:
- "**/*.py"





jobs:
lint:
name: Ruff Style Check
Expand Down
43 changes: 43 additions & 0 deletions scripts/ci/update_starter_projects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Script to update Langflow starter projects with the latest component versions."""

import asyncio
import os

import langflow.main # noqa: F401
from langflow.initial_setup.setup import (
get_project_data,
load_starter_projects,
update_edges_with_latest_component_versions,
update_project_file,
update_projects_components_with_latest_component_versions,
)
from langflow.interface.types import get_and_cache_all_types_dict
from langflow.services.deps import get_settings_service
from langflow.services.utils import initialize_services


async def main():
"""Updates the starter projects with the latest component versions.
Copies the code from langflow/initial_setup/setup.py. Doesn't use the
create_or_update_starter_projects function directly to avoid sql interactions.
"""
await initialize_services(fix_migration=False)
all_types_dict = await get_and_cache_all_types_dict(get_settings_service())

starter_projects = await load_starter_projects()
for project_path, project in starter_projects:
_, _, _, _, project_data, _, _, _, _ = get_project_data(project)
do_update_starter_projects = os.environ.get("LANGFLOW_UPDATE_STARTER_PROJECTS", "true").lower() == "true"
if do_update_starter_projects:
updated_project_data = update_projects_components_with_latest_component_versions(
project_data.copy(), all_types_dict
)
updated_project_data = update_edges_with_latest_component_versions(updated_project_data)
if updated_project_data != project_data:
project_data = updated_project_data
await update_project_file(project_path, project, updated_project_data)


if __name__ == "__main__":
asyncio.run(main())
10 changes: 8 additions & 2 deletions src/backend/base/langflow/initial_setup/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,13 @@ async def find_existing_flow(session, flow_id, flow_endpoint_name):
return None


async def create_or_update_starter_projects(all_types_dict: dict) -> None:
async def create_or_update_starter_projects(all_types_dict: dict, *, do_create: bool = True) -> None:
"""Create or update starter projects.
Args:
all_types_dict (dict): Dictionary containing all component types and their templates
do_create (bool, optional): Whether to create new projects. Defaults to True.
"""
async with async_session_scope() as session:
new_folder = await create_starter_folder(session)
starter_projects = await load_starter_projects()
Expand Down Expand Up @@ -639,7 +645,7 @@ async def create_or_update_starter_projects(all_types_dict: dict) -> None:
project_data = updated_project_data
# We also need to update the project data in the file
await update_project_file(project_path, project, updated_project_data)
if project_name and project_data:
if do_create and project_name and project_data:
for existing_project in await get_all_flows_similar_to_project(session, new_folder.id):
await session.delete(existing_project)

Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,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 MessageTextInput, 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 MessageTextInput(\n name=\"tool_placeholder\",\n display_name=\"Tool Placeholder\",\n tool_mode=True,\n advanced=True,\n info=\"A placeholder input for tool mode.\",\n ),\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"
"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 MessageTextInput, 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 MessageTextInput(\n name=\"tool_placeholder\",\n display_name=\"Tool Placeholder\",\n tool_mode=True,\n advanced=True,\n info=\"A placeholder input for tool mode.\",\n ),\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 async 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 = await 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": {
"_input_type": "PromptInput",
Expand Down
Loading

0 comments on commit ba31d43

Please sign in to comment.