From 1670579a61bd9775b4c79143f28ad60aff64e7e5 Mon Sep 17 00:00:00 2001 From: Zamil Majdy Date: Thu, 9 Jan 2025 23:29:16 +0700 Subject: [PATCH] fix(block): Remove Python.format & Jinja templating format backward compatibility (#9229) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Python format uses `{Variable}` as the variable placeholder, while Jinja uses `{{Variable}}` as its default. Jinja is used as the main templating engine on the system, but the Python format version is still maintained for backward compatibility. However, the backward compatibility support can cause a side effect while passing JSON string value into the block that uses it: https://github.com/Significant-Gravitas/AutoGPT/issues/9194 ### Changes 🏗️ * Use `{{Variable}}` place holder format and removed `{Variable}` support in these blocks: - '363ae599-353e-4804-937e-b2ee3cef3da4', -- AgentOutputBlock - 'db7d8f02-2f44-4c55-ab7a-eae0941f0c30', -- FillTextTemplateBlock - '1f292d4a-41a4-4977-9684-7c8d560b9f91', -- AITextGeneratorBlock - 'ed55ac19-356e-4243-a6cb-bc599e9b716f' -- AIStructuredResponseGeneratorBlock * Add Jinja templating support on `AITextGeneratorBlock` & `AIStructuredResponseGeneratorBlock` * Migrated the existing database content to prevent breaking changes. ### Checklist 📋 #### For code changes: - [ ] I have clearly listed my changes in the PR description - [ ] I have made a test plan - [ ] I have tested my changes according to the test plan: - [ ] ...
Example test plan - [ ] Create from scratch and execute an agent with at least 3 blocks - [ ] Import an agent from file upload, and confirm it executes correctly - [ ] Upload agent to marketplace - [ ] Import an agent from marketplace and confirm it executes correctly - [ ] Edit an agent from monitor, and confirm it executes correctly
#### For configuration changes: - [ ] `.env.example` is updated or already compatible with my changes - [ ] `docker-compose.yml` is updated or already compatible with my changes - [ ] I have included a list of my configuration changes in the PR description (under **Changes**)
Examples of configuration changes - Changing ports - Adding new services that need to communicate with each other - Secrets or environment variable changes - New or infrastructure changes such as databases
--- .../backend/backend/blocks/basic.py | 2 +- .../backend/backend/blocks/llm.py | 14 +++- .../backend/backend/blocks/text.py | 6 +- .../backend/backend/usecases/sample.py | 2 +- autogpt_platform/backend/backend/util/text.py | 5 -- .../migration.sql | 78 +++++++++++++++++++ .../backend/test/executor/test_manager.py | 2 +- 7 files changed, 94 insertions(+), 15 deletions(-) create mode 100644 autogpt_platform/backend/migrations/20250105254106_migrate_brace_to_double_brace_string_format/migration.sql diff --git a/autogpt_platform/backend/backend/blocks/basic.py b/autogpt_platform/backend/backend/blocks/basic.py index 58a3ba241..b68c04bad 100644 --- a/autogpt_platform/backend/backend/blocks/basic.py +++ b/autogpt_platform/backend/backend/blocks/basic.py @@ -241,7 +241,7 @@ class AgentOutputBlock(Block): advanced=True, ) format: str = SchemaField( - description="The format string to be used to format the recorded_value.", + description="The format string to be used to format the recorded_value. Use Jinja2 syntax.", default="", advanced=True, ) diff --git a/autogpt_platform/backend/backend/blocks/llm.py b/autogpt_platform/backend/backend/blocks/llm.py index 968f7d28f..6f242a443 100644 --- a/autogpt_platform/backend/backend/blocks/llm.py +++ b/autogpt_platform/backend/backend/blocks/llm.py @@ -26,8 +26,10 @@ from backend.data.model import ( ) from backend.util import json from backend.util.settings import BehaveAs, Settings +from backend.util.text import TextFormatter logger = logging.getLogger(__name__) +fmt = TextFormatter() LLMProviderName = Literal[ ProviderName.ANTHROPIC, @@ -236,7 +238,9 @@ class AIStructuredResponseGeneratorBlock(Block): description="Number of times to retry the LLM call if the response does not match the expected format.", ) prompt_values: dict[str, str] = SchemaField( - advanced=False, default={}, description="Values used to fill in the prompt." + advanced=False, + default={}, + description="Values used to fill in the prompt. The values can be used in the prompt by putting them in a double curly braces, e.g. {{variable_name}}.", ) max_tokens: int | None = SchemaField( advanced=True, @@ -450,8 +454,8 @@ class AIStructuredResponseGeneratorBlock(Block): values = input_data.prompt_values if values: - input_data.prompt = input_data.prompt.format(**values) - input_data.sys_prompt = input_data.sys_prompt.format(**values) + input_data.prompt = fmt.format_string(input_data.prompt, values) + input_data.sys_prompt = fmt.format_string(input_data.sys_prompt, values) if input_data.sys_prompt: prompt.append({"role": "system", "content": input_data.sys_prompt}) @@ -578,7 +582,9 @@ class AITextGeneratorBlock(Block): description="Number of times to retry the LLM call if the response does not match the expected format.", ) prompt_values: dict[str, str] = SchemaField( - advanced=False, default={}, description="Values used to fill in the prompt." + advanced=False, + default={}, + description="Values used to fill in the prompt. The values can be used in the prompt by putting them in a double curly braces, e.g. {{variable_name}}.", ) ollama_host: str = SchemaField( advanced=True, diff --git a/autogpt_platform/backend/backend/blocks/text.py b/autogpt_platform/backend/backend/blocks/text.py index d1e59cb83..35d25660f 100644 --- a/autogpt_platform/backend/backend/blocks/text.py +++ b/autogpt_platform/backend/backend/blocks/text.py @@ -141,10 +141,10 @@ class ExtractTextInformationBlock(Block): class FillTextTemplateBlock(Block): class Input(BlockSchema): values: dict[str, Any] = SchemaField( - description="Values (dict) to be used in format" + description="Values (dict) to be used in format. These values can be used by putting them in double curly braces in the format template. e.g. {{value_name}}.", ) format: str = SchemaField( - description="Template to format the text using `values`" + description="Template to format the text using `values`. Use Jinja2 syntax." ) class Output(BlockSchema): @@ -160,7 +160,7 @@ class FillTextTemplateBlock(Block): test_input=[ { "values": {"name": "Alice", "hello": "Hello", "world": "World!"}, - "format": "{hello}, {world} {{name}}", + "format": "{{hello}}, {{ world }} {{name}}", }, { "values": {"list": ["Hello", " World!"]}, diff --git a/autogpt_platform/backend/backend/usecases/sample.py b/autogpt_platform/backend/backend/usecases/sample.py index eb6ab6211..672fd278a 100644 --- a/autogpt_platform/backend/backend/usecases/sample.py +++ b/autogpt_platform/backend/backend/usecases/sample.py @@ -38,7 +38,7 @@ def create_test_graph() -> graph.Graph: graph.Node( block_id=FillTextTemplateBlock().id, input_default={ - "format": "{a}, {b}{c}", + "format": "{{a}}, {{b}}{{c}}", "values_#_c": "!!!", }, ), diff --git a/autogpt_platform/backend/backend/util/text.py b/autogpt_platform/backend/backend/util/text.py index 951339b55..c867b7a40 100644 --- a/autogpt_platform/backend/backend/util/text.py +++ b/autogpt_platform/backend/backend/util/text.py @@ -1,5 +1,3 @@ -import re - from jinja2 import BaseLoader from jinja2.sandbox import SandboxedEnvironment @@ -15,8 +13,5 @@ class TextFormatter: self.env.globals.clear() def format_string(self, template_str: str, values=None, **kwargs) -> str: - # For python.format compatibility: replace all {...} with {{..}}. - # But avoid replacing {{...}} to {{{...}}}. - template_str = re.sub(r"(?}", template_str) template = self.env.from_string(template_str) return template.render(values or {}, **kwargs) diff --git a/autogpt_platform/backend/migrations/20250105254106_migrate_brace_to_double_brace_string_format/migration.sql b/autogpt_platform/backend/migrations/20250105254106_migrate_brace_to_double_brace_string_format/migration.sql new file mode 100644 index 000000000..23bdfda53 --- /dev/null +++ b/autogpt_platform/backend/migrations/20250105254106_migrate_brace_to_double_brace_string_format/migration.sql @@ -0,0 +1,78 @@ +/* + Warnings: + - You are about replace a single brace string input format for the following blocks: + - AgentOutputBlock + - FillTextTemplateBlock + - AITextGeneratorBlock + - AIStructuredResponseGeneratorBlock + with a double brace format. +*/ +WITH to_update AS ( + SELECT + "agentBlockId", + "constantInput"::jsonb AS j + FROM "AgentNode" + WHERE + "agentBlockId" IN ( + '363ae599-353e-4804-937e-b2ee3cef3da4', -- AgentOutputBlock + 'db7d8f02-2f44-4c55-ab7a-eae0941f0c30', -- FillTextTemplateBlock + '1f292d4a-41a4-4977-9684-7c8d560b9f91', -- AITextGeneratorBlock + 'ed55ac19-356e-4243-a6cb-bc599e9b716f' -- AIStructuredResponseGeneratorBlock + ) + AND ( + "constantInput"::jsonb->>'format' ~ '(?>'prompt' ~ '(?>'sys_prompt' ~ '(?>'format' ~ '(?>'format', + '(?>'prompt' ~ '(?>'prompt', + '(?>'sys_prompt' ~ '(?>'sys_prompt', + '(?