import re from langchain_core.prompts import HumanMessagePromptTemplate from langflow.custom import Component from langflow.inputs import DefaultPromptField, SecretStrInput, StrInput from langflow.io import Output from langflow.schema.message import Message class LangChainHubPromptComponent(Component): display_name: str = "Prompt Hub" description: str = "Prompt Component that uses LangChain Hub prompts" beta = True icon = "LangChain" trace_type = "prompt" name = "LangChain Hub Prompt" inputs = [ SecretStrInput( name="langchain_api_key", display_name="Your LangChain API Key", info="The LangChain API Key to use.", required=True, ), StrInput( name="langchain_hub_prompt", display_name="LangChain Hub Prompt", info="The LangChain Hub prompt to use, i.e., 'efriis/my-first-prompt'", refresh_button=True, required=True, ), ] outputs = [ Output(display_name="Build Prompt", name="prompt", method="build_prompt"), ] def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None): # If the field is not langchain_hub_prompt or the value is empty, return the build config as is if field_name != "langchain_hub_prompt" or not field_value: return build_config # Fetch the template template = self._fetch_langchain_hub_template() # Get the template's messages if hasattr(template, "messages"): template_messages = template.messages else: template_messages = [HumanMessagePromptTemplate(prompt=template)] # Extract the messages from the prompt data prompt_template = [message_data.prompt for message_data in template_messages] # Regular expression to find all instances of {} pattern = r"\{(.*?)\}" # Get all the custom fields custom_fields: list[str] = [] full_template = "" for message in prompt_template: # Find all matches matches = re.findall(pattern, message.template) custom_fields += matches # Create a string version of the full template full_template = full_template + "\n" + message.template # No need to reprocess if we have them already if all("param_" + custom_field in build_config for custom_field in custom_fields): return build_config # Easter egg: Show template in info popup build_config["langchain_hub_prompt"]["info"] = full_template # Remove old parameter inputs if any for key in build_config.copy(): if key.startswith("param_"): del build_config[key] # Now create inputs for each for custom_field in custom_fields: new_parameter = DefaultPromptField( name=f"param_{custom_field}", display_name=custom_field, info="Fill in the value for {" + custom_field + "}", ).to_dict() # Add the new parameter to the build config build_config[f"param_{custom_field}"] = new_parameter return build_config async def build_prompt( self, ) -> Message: # Fetch the template template = self._fetch_langchain_hub_template() # Get the parameters from the attributes params_dict = {param: getattr(self, "param_" + param, f"{{{param}}}") for param in template.input_variables} original_params = {k: v.text if hasattr(v, "text") else v for k, v in params_dict.items() if v is not None} prompt_value = template.invoke(original_params) # Update the template with the new value original_params["template"] = prompt_value.to_string() # Now pass the filtered attributes to the function prompt = Message.from_template(**original_params) self.status = prompt.text return prompt def _fetch_langchain_hub_template(self): import langchain.hub # Check if the api key is provided if not self.langchain_api_key: msg = "Please provide a LangChain API Key" raise ValueError(msg) # Pull the prompt from LangChain Hub return langchain.hub.pull(self.langchain_hub_prompt, api_key=self.langchain_api_key)