ジェネラティブエージェンツの大嶋です。
もくもくDifyで「Difyソースコードリーディング #10 - Dify v0.10.0のファイルアップロードを読む」という勉強会を開催しました。
https://dify-mokumoku.connpass.com/event/335249/dify-mokumoku.connpass.com
アーカイブ動画はこちらです。
Difyのソースコードはこちらです。
今回も 戸塚さん と一緒に話しながらコードを読んでいきました!
今回のポイント
今回はファイルアップロード機能など、Dify v0.10.0以降のリリースノートで気になった機能のソースコードを見ていきました。
Difyのリリースノートはこちらです。
ファイルアップロード
Difyではチャットフローやワークフローを作成する際、「開始ノード」の「入力フィールド」として、「単一ファイル」や「ファイルリスト」が選択できるようになりました。
開始ノードの実装はapi/core/workflow/nodes/start/start_node.pyにあります。
StartNodeクラス→StartNodeDataクラス→VariableEntityクラスとたどっていくと、VariableEntityクラスのtypeとして、「VariableEntityType.FILE」や「VariableEntityType.FILE_LIST」が追加されていることが分かります。
class VariableEntityType(str, Enum): TEXT_INPUT = "text-input" SELECT = "select" PARAGRAPH = "paragraph" NUMBER = "number" EXTERNAL_DATA_TOOL = "external_data_tool" FILE = "file" FILE_LIST = "file-list" class VariableEntity(BaseModel): """ Variable Entity. """ variable: str label: str description: str = "" type: VariableEntityType :
ソースコードの引用元:https://github.com/langgenius/dify/blob/53a7cb0e9ddb5a5b04d8c4f48033c67edae32be8/api/core/app/app_config/entities.py#L91-L109
External Data Tool
ここで、VariableEntityTypeに「EXTERNAL_DATA_TOOL」というものがあり、これが何なのか気になったので追いかけてみました。
Difyのソースコードを「EXTERNAL_DATA_TOOL」で検索すると、api/core/external_data_toolというディレクトリがあるのが見つかります。
ここに実装されている機能は、公式ドキュメントでは以下のページに書かれているようです。
https://docs.dify.ai/ja-jp/guides/knowledge-base/external-data-tool
簡単に言えば、Difyの「API-Based Extension」の一種として外部データとの連携ができる、というもののようです。
DifyのExtension
DifyのAPI-Based Extensionについて気になったので、さらに見ていきました。
DifyのExtensionについては、公式ドキュメントの以下のページに書かれています。
https://docs.dify.ai/ja-jp/guides/extension
DifyのExtension、つまり拡張方法として「API-Based Extension」と「Code-Based Extension」の2つが提供されているということです。
API-Based Extension
API-Based Extensionは、外部のAPIによるDifyの拡張です。 外部のAPIをModerationまたは上記のExternal Data Toolとして使うことができるようです。
https://docs.dify.ai/ja-jp/guides/extension/api-based-extension
API-Based ExtensionはDifyのクラウドサービス版でも利用可能です。
api/models/api_based_extension.pyを見ると、API-Based Extensionの設定がテナントごとにDBに保存されることが分かります。
class APIBasedExtension(db.Model): : id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()")) tenant_id = db.Column(StringUUID, nullable=False) name = db.Column(db.String(255), nullable=False) api_endpoint = db.Column(db.String(255), nullable=False) api_key = db.Column(db.Text, nullable=False) created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)"))
Code-Based Extension
Code-Based Extensionは、コードによるDifyの拡張です。 こちらでもModerationやExternal Data Toolを追加できるようです。
https://docs.dify.ai/ja-jp/guides/extension/code-based-extension
詳しくは公式ドキュメントに書かれていますが、特定のディレクトリに特定の形式でソースコードを配置することで、Difyを拡張できる、ということのようです。 ソースコードを配置する必要性から分かるように、コミュニティ版を自分でホスティングする場合のみ利用可能なようです。
Dify v0.10.0以降のアップデート関係を読み解くという本題からは外れましたが、Extension関係はなかなか面白かったです。
Document Extractor
話を元に戻して、Dify v0.10.0以降のアップデート関係を改めて見ていきました。
次に見たのは、ワークフローに追加されたDocument Extractorのノードについてです。
簡単に言えば、xlsxやpdfといった形式のファイルからテキストを抽出するノードです。
api/core/workflow/nodes/document_extractor/node.pyを参照すると、抽出処理の実装が分かります。
拡張子などをもとに処理が分岐され、たとえばxlsxであればpandasの機能で読み込み、pdfであればpypdfium2で読み込んでいるといったことが分かります。
Code Generator
そして、v0.10.1で追加されたCode Generatorについても見ていきました。
これは、Python/JavaScriptのコードを実行するノードを組み込む際に、実行するコードを生成できるという機能です。
この機能のプロンプトはapi/core/llm_generator/prompts.pyにありました。
以下のように、どのような形式のコードを出力するといったことや、どんなライブラリが使えるといったことが書かれています。
PYTHON_CODE_GENERATOR_PROMPT_TEMPLATE = ( "You are an expert programmer. Generate code based on the following instructions:\n\n" "Instructions: {{INSTRUCTION}}\n\n" "Write the code in {{CODE_LANGUAGE}}.\n\n" "Please ensure that you meet the following requirements:\n" "1. Define a function named 'main'.\n" "2. The 'main' function must return a dictionary (dict).\n" "3. You may modify the arguments of the 'main' function, but include appropriate type hints.\n" "4. The returned dictionary should contain at least one key-value pair.\n\n" "5. You may ONLY use the following libraries in your code: \n" "- json\n" "- datetime\n" "- math\n" "- random\n" "- re\n" "- string\n" "- sys\n" "- time\n" "- traceback\n" "- uuid\n" "- os\n" "- base64\n" "- hashlib\n" "- hmac\n" "- binascii\n" "- collections\n" "- functools\n" "- operator\n" "- itertools\n\n" "Example:\n" "def main(arg1: str, arg2: int) -> dict:\n" " return {\n" ' "result": arg1 * arg2,\n' " }\n\n" "IMPORTANT:\n" "- Provide ONLY the code without any additional explanations, comments, or markdown formatting.\n" "- DO NOT use markdown code blocks (``` or ``` python). Return the raw code directly.\n" "- The code should start immediately after this instruction, without any preceding newlines or spaces.\n" "- The code should be complete, functional, and follow best practices for {{CODE_LANGUAGE}}.\n\n" "- Always use the format return {'result': ...} for the output.\n\n" "Generated Code:\n" )
Markdown Buttons in Workflows
最後に、Dify v0.10.2で追加された「Markdown Buttons in Workflows」について確認しました。
以下のプルリクエストで説明されているように、<button data-message="Hello World" data-variant="primary">YOYOYO</button>
のように出力すると「YOYOYO」というボタンとして表示され、クリックされると「Hello World」というテキストを入力したのと同じ挙動になる、という機能です。
こちらはソースコードを読むことまでは実施しませんでしたが、単にフロントエンドだけで実装されているという可能性が高そうな気がします。(完全に想像です)
次回のご案内
以上、今回はDify v0.10.0以降のアップデートについてソースコードを見ていきました。
次回の開催日は未定ですが、またv0.11がリリースされたといったタイミングで新機能のソースコードリーディングなどをしていくつもりです!
また、水曜日にもDifyの活用についてのもくもく会があります。 ご興味ある方はこちらも気軽にご参加ください!
https://dify-mokumoku.connpass.com/event/335127/dify-mokumoku.connpass.com