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.3.tar.gz (59.4 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.3-py3-none-any.whl (44.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: ag_ui_dify_adapter-0.1.3.tar.gz
  • Upload date:
  • Size: 59.4 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.3.tar.gz
Algorithm Hash digest
SHA256 326f7c3d9ea33af44823e514764e1cef80f620265f5447e97bc71e5fc03fac95
MD5 e0ea476af50ab9dbb5c3d48341cef015
BLAKE2b-256 68c03501d1d307185a5133c5cac95680121c0d7c7a01c33d3fe2e0cf00e6ab0b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for ag_ui_dify_adapter-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 bcc54369b704a624498b38078df657451b2720c2d13619d5173ed630c1e5f16f
MD5 5c528e6a283f2e1610a0026ca25161ad
BLAKE2b-256 f0ef097e067348f625301ec32881a72b923c62c08ab8971129e062f9628a44dd

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