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.2.tar.gz (47.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.2-py3-none-any.whl (43.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: ag_ui_dify_adapter-0.1.2.tar.gz
  • Upload date:
  • Size: 47.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.2.tar.gz
Algorithm Hash digest
SHA256 4a9b96360ec1cfd4a8084b71c9f2fea669f838284e5bee08de8518c761946c50
MD5 2ee7da37cda85800e9c0081565c4b18d
BLAKE2b-256 88dc329eb61079843cf0edde3b888053fbff201e1e2089c5fb32eec86980c154

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for ag_ui_dify_adapter-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 eebca67adcd6ca2b92f09bfb7edfa4cae98a2f6d5cf8d29579a9ff8fc33a89cf
MD5 6c167cd74d1a37093a9740116d607df7
BLAKE2b-256 d6287e6a45f66f6e69bcbcfaa687c727035949979766759ae7cb0171d1a552fe

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