Skip to main content

AG-UI protocol adapter for Dify — translates Dify API responses to AG-UI streaming events

Project description

ag-ui-dify-adapter

AG-UI protocol adapter for Dify — translates Dify API responses to AG-UI streaming events, enabling Dify-powered AI agents to integrate with any AG-UI-compatible frontend.

Features

  • All 4 Dify app types: Chat, Agent, Workflow, Completion
  • 24+ AG-UI event types: TEXT_MESSAGE, TOOL_CALL, TOOL_CALL_RESULT, REASONING, STATE_SNAPSHOT, MESSAGES_SNAPSHOT, STEP, CUSTOM, RAW, RUN
  • Tool call lifecycle: TOOL_CALL_STARTARGSENDRESULT for Agent ReAct loops
  • Reasoning events: <think> tag streaming detection → REASONING_START/MESSAGE_START/CONTENT/MESSAGE_END/END
  • Snapshots: MESSAGES_SNAPSHOT + STATE_SNAPSHOT emitted at start of every run
  • Streaming: Real-time text streaming with <think> block separation
  • Multi-turn conversation: thread_idconversation_id tracking
  • State & context: AG-UI state/context → Dify input variables
  • Single-port multi-agent: One server, multiple Dify apps routed by path
  • RAW passthrough: Unrecognized Dify events forwarded as RAW, never dropped
  • Async: Full async support with httpx

Installation

pip install ag-ui-dify-adapter

For the HTTP server:

pip install ag-ui-dify-adapter[server]

Quick Start

Library

import asyncio
from ag_ui_dify import DifyAgent, DifyConfig, DifyAppType
from ag_ui.core import RunAgentInput, UserMessage

async def main():
    agent = DifyAgent(DifyConfig(
        api_key="app-xxx",
        base_url="https://api.dify.ai/v1",
        app_type=DifyAppType.AGENT,
    ))

    input = RunAgentInput(
        thread_id="thread-1",
        run_id="run-1",
        state=None,
        messages=[UserMessage(id="u1", role="user", content="Hello!")],
        tools=[], context=[], forwarded_props={},
    )

    async for event in agent.run(input):
        print(event.model_dump_json(by_alias=True))

asyncio.run(main())

HTTP Server

# Single agent
DIFY_API_KEY=app-xxx DIFY_APP_TYPE=agent \
  uvicorn ag_ui_dify:create_app --port 8080

# Multi-agent (single port, routed by path)
DIFY_AGENTS='{
  "agent-a": {"key": "app-xxx", "type": "agent"},
  "wf-b":    {"key": "app-yyy", "type": "workflow"}
}' \
  uvicorn ag_ui_dify:create_app --port 8080

API keys are configured server-side via environment variables — never exposed to clients.

# Endpoints
curl -X POST http://localhost:8080/agent-a \
  -H "Content-Type: application/json" \
  -d '{"threadId":"t1","runId":"r1","messages":[{"id":"u1","role":"user","content":"Hello"}],"tools":[],"context":[]}'

curl http://localhost:8080/health   # → {"status":"ok"}
curl http://localhost:8080/info     # → agent discovery

Dify → AG-UI Event Mapping

Agent App

Dify SSE Event AG-UI Event(s)
agent_thought (with thought) STEP_STARTED + CUSTOM (thought)
agent_thought (with tool) TOOL_CALL_START + TOOL_CALL_ARGS + TOOL_CALL_END
agent_thought (with observation) TOOL_CALL_RESULT
agent_message (first) TEXT_MESSAGE_START
agent_message TEXT_MESSAGE_CONTENT (with <think>REASONING)
message_replace CUSTOM
message_file CUSTOM
message_end TEXT_MESSAGE_END + RUN_FINISHED

Workflow App

Dify SSE Event AG-UI Event(s)
workflow_started RUN_STARTED + TEXT_MESSAGE_START
node_started / node_retry STEP_STARTED
node_finished STEP_FINISHED
agent_log STEP_STARTED / STEP_FINISHED
iteration_started/completed STEP_STARTED / STEP_FINISHED
loop_started/completed STEP_STARTED / STEP_FINISHED
text_chunk TEXT_MESSAGE_CONTENT (with <think>REASONING)
text_replace CUSTOM
workflow_paused CUSTOM + RUN_FINISHED
human_input_* CUSTOM
workflow_finished TEXT_MESSAGE_END + RUN_FINISHED

Chat / Completion App

Dify SSE Event AG-UI Event(s)
message (first) TEXT_MESSAGE_START
message TEXT_MESSAGE_CONTENT (with <think>REASONING)
message_replace CUSTOM
message_file CUSTOM
tts_message / tts_message_end CUSTOM
message_end TEXT_MESSAGE_END + RUN_FINISHED

All app types: MESSAGES_SNAPSHOT + STATE_SNAPSHOT at start, RUN_STARTED, RUN_ERROR on error, ping ignored, unknown events → RAW.

API Reference

DifyAgent

agent = DifyAgent(DifyConfig(
    api_key="app-xxx",          # Required: Dify API key
    base_url="...",             # Default: https://api.dify.ai/v1
    app_type=DifyAppType.AGENT, # Auto-detected if omitted
    user="ag-ui-user",          # Default user identifier
    timeout=120.0,              # HTTP timeout in seconds
))
async for event in agent.run(run_input):
    ...

HTTP Server

from ag_ui_dify import create_app, load_agents
import uvicorn

# Programmatic
agents = load_agents()  # reads DIFY_AGENTS / DIFY_API_KEY from env
app = create_app()      # Starlette app with /info, /health, /<agent>
uvicorn.run(app, port=8080)
Routes:
  POST /<agent-name>   AG-UI RunAgentInput → SSE stream
  GET  /info           Agent discovery
  GET  /health         Health check

DifyClient (low-level)

client = DifyClient(config)
async for evt in client.stream_chat(query="Hello", inputs={}): ...
async for evt in client.stream_workflow(inputs={"url": "..."}): ...
async for evt in client.stream_completion(inputs={}): ...
await client.stop_chat(task_id="...")

Project Structure

ag_ui_dify/
├── __init__.py           # Package exports
├── types.py              # Dify type definitions (Pydantic models)
├── dify_client.py        # Async HTTP client for all Dify endpoints
├── event_translator.py   # Event translators (Chat/Agent/Workflow/Completion)
├── agent.py              # DifyAgent main adapter
└── server.py             # Starlette single-port multi-agent server

Verification Status

All 4 Dify app types verified against a real Dify instance:

App Type Status Coverage
Agent Tool calls, reasoning chain, multi-turn conversation
Workflow Node execution, agent_log sub-steps, text output
Chat Streaming text, message lifecycle
Completion Streaming text, <think> → REASONING events

Requirements

  • Python >= 3.9
  • ag-ui-protocol >= 0.1.17
  • httpx >= 0.27.0
  • pydantic >= 2.11.0
  • starlette >= 0.40.0 (optional, for HTTP server)
  • uvicorn (optional, for HTTP server)

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ag_ui_dify_adapter-0.1.1.tar.gz (27.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ag_ui_dify_adapter-0.1.1-py3-none-any.whl (19.5 kB view details)

Uploaded Python 3

File details

Details for the file ag_ui_dify_adapter-0.1.1.tar.gz.

File metadata

  • Download URL: ag_ui_dify_adapter-0.1.1.tar.gz
  • Upload date:
  • Size: 27.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.7

File hashes

Hashes for ag_ui_dify_adapter-0.1.1.tar.gz
Algorithm Hash digest
SHA256 d6312509c864804bd24b550c42c65d3bbd6fcc248768c88f25503387239892cb
MD5 c5d6f125a45a853c1c2fb9c5029d7d9d
BLAKE2b-256 744cc5ac755498b062ca0b2884763b3ed1bd38701a26f757be5055e9575fe0b1

See more details on using hashes here.

File details

Details for the file ag_ui_dify_adapter-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for ag_ui_dify_adapter-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 91397a9a48e0885a7db5737f1902682990801d00647b55a0444482244fde1917
MD5 7e9f4e2a74a4311aab769f14d9ec8d41
BLAKE2b-256 791ea52943f4e31927ca639e56bbcf2aa51b4d75d2bc824d7ee8107cac89ab09

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page