diff --git a/.env.template b/.env.template index faae36b..14fc995 100644 --- a/.env.template +++ b/.env.template @@ -29,6 +29,10 @@ QDRANT_URL="http://localhost:6333" ## Elasticsearch Settings ELASTICSEARCH_URL="http://localhost:9200" +## Dify Settings +DIFY_API_URL="https://api.dify.ai/v1" +DIFY_API_KEY="xxx" + # --------- # Utilities # --------- diff --git a/pyproject.toml b/pyproject.toml index 0b2fd9c..11cf874 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ readme = "README.md" requires-python = ">=3.10" dependencies = [ "elasticsearch>=9.1.0", + "httpx>=0.28.1", "langchain-community>=0.3.27", "langchain-openai>=0.3.28", "langchain-text-splitters>=0.3.9", diff --git a/scripts/dify_operator.py b/scripts/dify_operator.py new file mode 100644 index 0000000..1a2e61b --- /dev/null +++ b/scripts/dify_operator.py @@ -0,0 +1,65 @@ +import json +import logging + +import typer +from dotenv import load_dotenv + +from template_langgraph.loggers import get_logger +from template_langgraph.tools.dify_tool import DifyClientWrapper + +# Initialize the Typer application +app = typer.Typer( + add_completion=False, + help="Dify operator CLI", +) + +# Set up logging +logger = get_logger(__name__) + + +@app.command() +def run_workflow( + requirements: str = typer.Option( + "生成 AI のサービス概要を教えてください。日本語でお願いします", + "--requirements", + "-r", + help="Requirements for running the Dify workflow", + ), + verbose: bool = typer.Option( + False, + "--verbose", + "-v", + help="Enable verbose output", + ), +): + # Set up logging + if verbose: + logger.setLevel(logging.DEBUG) + + logger.info("Running Dify workflow...") + client = DifyClientWrapper() + response = client.run_workflow( + inputs={ + "inputs": { + "requirements": requirements, + }, + "response_mode": "blocking", + "user": "abc-123", + } + ) + logger.info( + json.dumps( + response, + indent=2, + ensure_ascii=False, + ) + ) + logger.info(f"Input: {response['data']['outputs']['requirements']}, Output: {response['data']['outputs']['text']}") + + +if __name__ == "__main__": + load_dotenv( + override=True, + verbose=True, + ) + app() diff --git a/template_langgraph/tools/dify_tool.py b/template_langgraph/tools/dify_tool.py new file mode 100644 index 0000000..e26936e --- /dev/null +++ b/template_langgraph/tools/dify_tool.py @@ -0,0 +1,50 @@ +from functools import lru_cache + +import httpx +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class Settings(BaseSettings): + dify_base_url: str = "https://api.dify.ai/v1" + dify_api_key: str = "" + + model_config = SettingsConfigDict( + env_file=".env", + env_ignore_empty=True, + extra="ignore", + ) + + +@lru_cache +def get_dify_settings() -> Settings: + """Get Dify settings.""" + return Settings() + + +class DifyClientWrapper: + def __init__( + self, + settings: Settings = None, + ): + if settings is None: + settings = get_dify_settings() + self.base_url = settings.dify_base_url + self.headers = { + "Authorization": f"Bearer {settings.dify_api_key}", + "Content-Type": "application/json", + } + + def run_workflow( + self, + inputs: dict, + ) -> dict: + """Run a Dify workflow.""" + with httpx.Client() as client: + response = client.post( + url=f"{self.base_url}/workflows/run", + json=inputs, + headers=self.headers, + timeout=60 * 5, # Set a timeout for the request + ) + response.raise_for_status() + return response.json() diff --git a/uv.lock b/uv.lock index 03ed925..646ba0f 100644 --- a/uv.lock +++ b/uv.lock @@ -3664,6 +3664,7 @@ version = "0.0.1" source = { editable = "." } dependencies = [ { name = "elasticsearch" }, + { name = "httpx" }, { name = "langchain-community" }, { name = "langchain-openai" }, { name = "langchain-text-splitters" }, @@ -3695,6 +3696,7 @@ docs = [ [package.metadata] requires-dist = [ { name = "elasticsearch", specifier = ">=9.1.0" }, + { name = "httpx", specifier = ">=0.28.1" }, { name = "langchain-community", specifier = ">=0.3.27" }, { name = "langchain-openai", specifier = ">=0.3.28" }, { name = "langchain-text-splitters", specifier = ">=0.3.9" },