ag-ui-dify-adapter 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ag_ui_dify_adapter-0.1.0/LICENSE +21 -0
- ag_ui_dify_adapter-0.1.0/MANIFEST.in +8 -0
- ag_ui_dify_adapter-0.1.0/PKG-INFO +240 -0
- ag_ui_dify_adapter-0.1.0/README.md +208 -0
- ag_ui_dify_adapter-0.1.0/README_zh_CN.md +208 -0
- ag_ui_dify_adapter-0.1.0/ag_ui_dify/__init__.py +58 -0
- ag_ui_dify_adapter-0.1.0/ag_ui_dify/agent.py +163 -0
- ag_ui_dify_adapter-0.1.0/ag_ui_dify/dify_client.py +258 -0
- ag_ui_dify_adapter-0.1.0/ag_ui_dify/event_translator.py +785 -0
- ag_ui_dify_adapter-0.1.0/ag_ui_dify/server.py +266 -0
- ag_ui_dify_adapter-0.1.0/ag_ui_dify/types.py +142 -0
- ag_ui_dify_adapter-0.1.0/ag_ui_dify_adapter.egg-info/PKG-INFO +240 -0
- ag_ui_dify_adapter-0.1.0/ag_ui_dify_adapter.egg-info/SOURCES.txt +22 -0
- ag_ui_dify_adapter-0.1.0/ag_ui_dify_adapter.egg-info/dependency_links.txt +1 -0
- ag_ui_dify_adapter-0.1.0/ag_ui_dify_adapter.egg-info/requires.txt +12 -0
- ag_ui_dify_adapter-0.1.0/ag_ui_dify_adapter.egg-info/top_level.txt +1 -0
- ag_ui_dify_adapter-0.1.0/pyproject.toml +51 -0
- ag_ui_dify_adapter-0.1.0/setup.cfg +4 -0
- ag_ui_dify_adapter-0.1.0/tests/__init__.py +0 -0
- ag_ui_dify_adapter-0.1.0/tests/test_agent.py +159 -0
- ag_ui_dify_adapter-0.1.0/tests/test_client.py +112 -0
- ag_ui_dify_adapter-0.1.0/tests/test_integration.py +118 -0
- ag_ui_dify_adapter-0.1.0/tests/test_translator.py +342 -0
- ag_ui_dify_adapter-0.1.0/tests/test_types.py +186 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Chuanlong
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ag-ui-dify-adapter
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: AG-UI protocol adapter for Dify — translates Dify API responses to AG-UI streaming events
|
|
5
|
+
Author: Chuanlong
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://gitee.com/chuanlong/ag-ui-dify-adapter
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Requires-Python: >=3.9
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: ag-ui-protocol>=0.1.17
|
|
22
|
+
Requires-Dist: httpx>=0.27.0
|
|
23
|
+
Requires-Dist: pydantic>=2.11.0
|
|
24
|
+
Provides-Extra: server
|
|
25
|
+
Requires-Dist: starlette>=0.40.0; extra == "server"
|
|
26
|
+
Requires-Dist: uvicorn>=0.30.0; extra == "server"
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest-httpx>=0.30.0; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# ag-ui-dify-adapter
|
|
34
|
+
|
|
35
|
+
AG-UI protocol adapter for Dify — translates [Dify](https://dify.ai) API responses to [AG-UI](https://ag-ui.com) streaming events, enabling Dify-powered AI agents to integrate with any AG-UI-compatible frontend.
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- **All 4 Dify app types**: Chat, Agent, Workflow, Completion
|
|
40
|
+
- **Complete event mapping**: Maps all Dify SSE events to AG-UI's 17 standard event types
|
|
41
|
+
- **Tool call support**: Agent tool calls (ReAct loops) translated to `TOOL_CALL_START/ARGS/END`
|
|
42
|
+
- **Streaming**: Real-time text streaming via `TEXT_MESSAGE_START/CONTENT/END`
|
|
43
|
+
- **Multi-turn conversation**: `thread_id` ↔ `conversation_id` tracking
|
|
44
|
+
- **State & context**: AG-UI state/context/forwardedProps → Dify input variables
|
|
45
|
+
- **File support**: File attachments via Dify's file upload API
|
|
46
|
+
- **HTTP server**: Built-in Starlette endpoint with SSE streaming
|
|
47
|
+
- **Async**: Full async support with `httpx`
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install ag-ui-dify-adapter
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
For the HTTP server:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install ag-ui-dify-adapter[server]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
### Usage as a library
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
import asyncio
|
|
67
|
+
from ag_ui_dify import DifyAgent, DifyConfig, DifyAppType
|
|
68
|
+
from ag_ui.core import RunAgentInput, UserMessage
|
|
69
|
+
|
|
70
|
+
async def main():
|
|
71
|
+
agent = DifyAgent(DifyConfig(
|
|
72
|
+
api_key="app-xxx",
|
|
73
|
+
base_url="https://api.dify.ai/v1",
|
|
74
|
+
app_type=DifyAppType.AGENT, # auto-detected if omitted
|
|
75
|
+
))
|
|
76
|
+
|
|
77
|
+
input = RunAgentInput(
|
|
78
|
+
thread_id="thread-1",
|
|
79
|
+
run_id="run-1",
|
|
80
|
+
state=None,
|
|
81
|
+
messages=[UserMessage(id="u1", role="user", content="Hello!")],
|
|
82
|
+
tools=[],
|
|
83
|
+
context=[],
|
|
84
|
+
forwarded_props={},
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
async for event in agent.run(input):
|
|
88
|
+
print(event.model_dump_json(by_alias=True))
|
|
89
|
+
|
|
90
|
+
asyncio.run(main())
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Usage as an HTTP server
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from ag_ui_dify import create_app
|
|
97
|
+
import uvicorn
|
|
98
|
+
|
|
99
|
+
app = create_app()
|
|
100
|
+
uvicorn.run(app, host="0.0.0.0", port=8080)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Then send requests:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
curl -X POST http://localhost:8080/ \
|
|
107
|
+
-H "Content-Type: application/json" \
|
|
108
|
+
-d '{
|
|
109
|
+
"threadId": "t1",
|
|
110
|
+
"runId": "r1",
|
|
111
|
+
"messages": [{"id": "u1", "role": "user", "content": "Hello!"}],
|
|
112
|
+
"tools": [],
|
|
113
|
+
"context": [],
|
|
114
|
+
"forwardedProps": {
|
|
115
|
+
"apiKey": "app-xxx",
|
|
116
|
+
"baseUrl": "https://api.dify.ai/v1",
|
|
117
|
+
"appType": "agent"
|
|
118
|
+
}
|
|
119
|
+
}'
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Dify → AG-UI Event Mapping
|
|
123
|
+
|
|
124
|
+
### Agent App
|
|
125
|
+
|
|
126
|
+
| Dify SSE Event | AG-UI Event(s) |
|
|
127
|
+
|---|---|
|
|
128
|
+
| `agent_thought` (with thought) | `STEP_STARTED` + `CUSTOM` (thought) |
|
|
129
|
+
| `agent_thought` (with tool) | `TOOL_CALL_START` + `TOOL_CALL_ARGS` + `TOOL_CALL_END` |
|
|
130
|
+
| `agent_thought` (with observation) | `CUSTOM` (observation) + `STEP_FINISHED` |
|
|
131
|
+
| `agent_message` (first) | `TEXT_MESSAGE_START` |
|
|
132
|
+
| `agent_message` | `TEXT_MESSAGE_CONTENT` |
|
|
133
|
+
| `message_end` | `TEXT_MESSAGE_END` + `RUN_FINISHED` |
|
|
134
|
+
|
|
135
|
+
### Workflow App
|
|
136
|
+
|
|
137
|
+
| Dify SSE Event | AG-UI Event(s) |
|
|
138
|
+
|---|---|
|
|
139
|
+
| `workflow_started` | `RUN_STARTED` |
|
|
140
|
+
| `node_started` | `STEP_STARTED` |
|
|
141
|
+
| `agent_log` | `STEP_STARTED` / `STEP_FINISHED` |
|
|
142
|
+
| `text_chunk` | `TEXT_MESSAGE_CONTENT` |
|
|
143
|
+
| `node_finished` | `STEP_FINISHED` |
|
|
144
|
+
| `workflow_finished` | `TEXT_MESSAGE_END` + `RUN_FINISHED` |
|
|
145
|
+
|
|
146
|
+
### Chat App
|
|
147
|
+
|
|
148
|
+
| Dify SSE Event | AG-UI Event(s) |
|
|
149
|
+
|---|---|
|
|
150
|
+
| `message` (first) | `TEXT_MESSAGE_START` |
|
|
151
|
+
| `message` | `TEXT_MESSAGE_CONTENT` |
|
|
152
|
+
| `message_end` | `TEXT_MESSAGE_END` + `RUN_FINISHED` |
|
|
153
|
+
| `message_file` | `CUSTOM` |
|
|
154
|
+
|
|
155
|
+
### Completion App
|
|
156
|
+
|
|
157
|
+
| Dify SSE Event | AG-UI Event(s) |
|
|
158
|
+
|---|---|
|
|
159
|
+
| `message` (first) | `TEXT_MESSAGE_START` |
|
|
160
|
+
| `message` | `TEXT_MESSAGE_CONTENT` |
|
|
161
|
+
| `message_end` | `TEXT_MESSAGE_END` + `RUN_FINISHED` |
|
|
162
|
+
|
|
163
|
+
All app types: `RUN_STARTED` at the beginning, `RUN_ERROR` on error, `ping` ignored.
|
|
164
|
+
|
|
165
|
+
## API Reference
|
|
166
|
+
|
|
167
|
+
### DifyAgent
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
agent = DifyAgent(DifyConfig(
|
|
171
|
+
api_key="app-xxx", # Required: Dify API key
|
|
172
|
+
base_url="...", # Default: https://api.dify.ai/v1
|
|
173
|
+
app_type=DifyAppType.AGENT, # Auto-detected if omitted
|
|
174
|
+
user="ag-ui-user", # Default user identifier
|
|
175
|
+
timeout=120.0, # HTTP timeout in seconds
|
|
176
|
+
))
|
|
177
|
+
|
|
178
|
+
async for event in agent.run(run_input):
|
|
179
|
+
...
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### DifyClient
|
|
183
|
+
|
|
184
|
+
Low-level async client for all Dify API endpoints:
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
client = DifyClient(config)
|
|
188
|
+
async for evt in client.stream_chat(query="Hello", inputs={}):
|
|
189
|
+
...
|
|
190
|
+
async for evt in client.stream_workflow(inputs={"url": "..."}):
|
|
191
|
+
...
|
|
192
|
+
async for evt in client.stream_completion(inputs={}):
|
|
193
|
+
...
|
|
194
|
+
await client.stop_chat(task_id="...")
|
|
195
|
+
messages = await client.get_messages(conversation_id="...", user="...")
|
|
196
|
+
convs = await client.get_conversations(user="...")
|
|
197
|
+
upload_result = await client.upload_file(file_path="...", user="...")
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Project Structure
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
ag_ui_dify/
|
|
204
|
+
├── __init__.py # Package exports
|
|
205
|
+
├── types.py # Dify type definitions (Pydantic models)
|
|
206
|
+
├── dify_client.py # Async HTTP client for all Dify endpoints
|
|
207
|
+
├── event_translator.py # Event translators (Chat/Agent/Workflow/Completion)
|
|
208
|
+
├── agent.py # DifyAgent main adapter
|
|
209
|
+
└── server.py # Starlette HTTP SSE endpoint
|
|
210
|
+
tests/
|
|
211
|
+
├── test_types.py # Type model tests (19)
|
|
212
|
+
├── test_translator.py # Translator tests (14)
|
|
213
|
+
├── test_client.py # Client tests (5)
|
|
214
|
+
├── test_agent.py # Agent tests (10)
|
|
215
|
+
└── test_integration.py # Real-environment integration tests
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Verification Status
|
|
219
|
+
|
|
220
|
+
All 4 Dify app types have been verified against a real Dify instance:
|
|
221
|
+
|
|
222
|
+
| App Type | Real Dify Tested | Notes |
|
|
223
|
+
|---|---|---|
|
|
224
|
+
| Agent | ✓ | Tool calls, reasoning chain, multi-turn conversation |
|
|
225
|
+
| Workflow | ✓ | Node execution, agent_log sub-steps, text output |
|
|
226
|
+
| Chat | ✓ | Streaming text, message lifecycle |
|
|
227
|
+
| Completion | ✓ | Streaming text, input variables |
|
|
228
|
+
|
|
229
|
+
## Requirements
|
|
230
|
+
|
|
231
|
+
- Python >= 3.9
|
|
232
|
+
- ag-ui-protocol >= 0.1.17
|
|
233
|
+
- httpx >= 0.27.0
|
|
234
|
+
- pydantic >= 2.11.0
|
|
235
|
+
- starlette >= 0.40.0 (optional, for HTTP server)
|
|
236
|
+
- uvicorn (optional, for HTTP server)
|
|
237
|
+
|
|
238
|
+
## License
|
|
239
|
+
|
|
240
|
+
MIT
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# ag-ui-dify-adapter
|
|
2
|
+
|
|
3
|
+
AG-UI protocol adapter for Dify — translates [Dify](https://dify.ai) API responses to [AG-UI](https://ag-ui.com) streaming events, enabling Dify-powered AI agents to integrate with any AG-UI-compatible frontend.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **All 4 Dify app types**: Chat, Agent, Workflow, Completion
|
|
8
|
+
- **Complete event mapping**: Maps all Dify SSE events to AG-UI's 17 standard event types
|
|
9
|
+
- **Tool call support**: Agent tool calls (ReAct loops) translated to `TOOL_CALL_START/ARGS/END`
|
|
10
|
+
- **Streaming**: Real-time text streaming via `TEXT_MESSAGE_START/CONTENT/END`
|
|
11
|
+
- **Multi-turn conversation**: `thread_id` ↔ `conversation_id` tracking
|
|
12
|
+
- **State & context**: AG-UI state/context/forwardedProps → Dify input variables
|
|
13
|
+
- **File support**: File attachments via Dify's file upload API
|
|
14
|
+
- **HTTP server**: Built-in Starlette endpoint with SSE streaming
|
|
15
|
+
- **Async**: Full async support with `httpx`
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install ag-ui-dify-adapter
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
For the HTTP server:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install ag-ui-dify-adapter[server]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
### Usage as a library
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
import asyncio
|
|
35
|
+
from ag_ui_dify import DifyAgent, DifyConfig, DifyAppType
|
|
36
|
+
from ag_ui.core import RunAgentInput, UserMessage
|
|
37
|
+
|
|
38
|
+
async def main():
|
|
39
|
+
agent = DifyAgent(DifyConfig(
|
|
40
|
+
api_key="app-xxx",
|
|
41
|
+
base_url="https://api.dify.ai/v1",
|
|
42
|
+
app_type=DifyAppType.AGENT, # auto-detected if omitted
|
|
43
|
+
))
|
|
44
|
+
|
|
45
|
+
input = RunAgentInput(
|
|
46
|
+
thread_id="thread-1",
|
|
47
|
+
run_id="run-1",
|
|
48
|
+
state=None,
|
|
49
|
+
messages=[UserMessage(id="u1", role="user", content="Hello!")],
|
|
50
|
+
tools=[],
|
|
51
|
+
context=[],
|
|
52
|
+
forwarded_props={},
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
async for event in agent.run(input):
|
|
56
|
+
print(event.model_dump_json(by_alias=True))
|
|
57
|
+
|
|
58
|
+
asyncio.run(main())
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Usage as an HTTP server
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from ag_ui_dify import create_app
|
|
65
|
+
import uvicorn
|
|
66
|
+
|
|
67
|
+
app = create_app()
|
|
68
|
+
uvicorn.run(app, host="0.0.0.0", port=8080)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Then send requests:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
curl -X POST http://localhost:8080/ \
|
|
75
|
+
-H "Content-Type: application/json" \
|
|
76
|
+
-d '{
|
|
77
|
+
"threadId": "t1",
|
|
78
|
+
"runId": "r1",
|
|
79
|
+
"messages": [{"id": "u1", "role": "user", "content": "Hello!"}],
|
|
80
|
+
"tools": [],
|
|
81
|
+
"context": [],
|
|
82
|
+
"forwardedProps": {
|
|
83
|
+
"apiKey": "app-xxx",
|
|
84
|
+
"baseUrl": "https://api.dify.ai/v1",
|
|
85
|
+
"appType": "agent"
|
|
86
|
+
}
|
|
87
|
+
}'
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Dify → AG-UI Event Mapping
|
|
91
|
+
|
|
92
|
+
### Agent App
|
|
93
|
+
|
|
94
|
+
| Dify SSE Event | AG-UI Event(s) |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `agent_thought` (with thought) | `STEP_STARTED` + `CUSTOM` (thought) |
|
|
97
|
+
| `agent_thought` (with tool) | `TOOL_CALL_START` + `TOOL_CALL_ARGS` + `TOOL_CALL_END` |
|
|
98
|
+
| `agent_thought` (with observation) | `CUSTOM` (observation) + `STEP_FINISHED` |
|
|
99
|
+
| `agent_message` (first) | `TEXT_MESSAGE_START` |
|
|
100
|
+
| `agent_message` | `TEXT_MESSAGE_CONTENT` |
|
|
101
|
+
| `message_end` | `TEXT_MESSAGE_END` + `RUN_FINISHED` |
|
|
102
|
+
|
|
103
|
+
### Workflow App
|
|
104
|
+
|
|
105
|
+
| Dify SSE Event | AG-UI Event(s) |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `workflow_started` | `RUN_STARTED` |
|
|
108
|
+
| `node_started` | `STEP_STARTED` |
|
|
109
|
+
| `agent_log` | `STEP_STARTED` / `STEP_FINISHED` |
|
|
110
|
+
| `text_chunk` | `TEXT_MESSAGE_CONTENT` |
|
|
111
|
+
| `node_finished` | `STEP_FINISHED` |
|
|
112
|
+
| `workflow_finished` | `TEXT_MESSAGE_END` + `RUN_FINISHED` |
|
|
113
|
+
|
|
114
|
+
### Chat App
|
|
115
|
+
|
|
116
|
+
| Dify SSE Event | AG-UI Event(s) |
|
|
117
|
+
|---|---|
|
|
118
|
+
| `message` (first) | `TEXT_MESSAGE_START` |
|
|
119
|
+
| `message` | `TEXT_MESSAGE_CONTENT` |
|
|
120
|
+
| `message_end` | `TEXT_MESSAGE_END` + `RUN_FINISHED` |
|
|
121
|
+
| `message_file` | `CUSTOM` |
|
|
122
|
+
|
|
123
|
+
### Completion App
|
|
124
|
+
|
|
125
|
+
| Dify SSE Event | AG-UI Event(s) |
|
|
126
|
+
|---|---|
|
|
127
|
+
| `message` (first) | `TEXT_MESSAGE_START` |
|
|
128
|
+
| `message` | `TEXT_MESSAGE_CONTENT` |
|
|
129
|
+
| `message_end` | `TEXT_MESSAGE_END` + `RUN_FINISHED` |
|
|
130
|
+
|
|
131
|
+
All app types: `RUN_STARTED` at the beginning, `RUN_ERROR` on error, `ping` ignored.
|
|
132
|
+
|
|
133
|
+
## API Reference
|
|
134
|
+
|
|
135
|
+
### DifyAgent
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
agent = DifyAgent(DifyConfig(
|
|
139
|
+
api_key="app-xxx", # Required: Dify API key
|
|
140
|
+
base_url="...", # Default: https://api.dify.ai/v1
|
|
141
|
+
app_type=DifyAppType.AGENT, # Auto-detected if omitted
|
|
142
|
+
user="ag-ui-user", # Default user identifier
|
|
143
|
+
timeout=120.0, # HTTP timeout in seconds
|
|
144
|
+
))
|
|
145
|
+
|
|
146
|
+
async for event in agent.run(run_input):
|
|
147
|
+
...
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### DifyClient
|
|
151
|
+
|
|
152
|
+
Low-level async client for all Dify API endpoints:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
client = DifyClient(config)
|
|
156
|
+
async for evt in client.stream_chat(query="Hello", inputs={}):
|
|
157
|
+
...
|
|
158
|
+
async for evt in client.stream_workflow(inputs={"url": "..."}):
|
|
159
|
+
...
|
|
160
|
+
async for evt in client.stream_completion(inputs={}):
|
|
161
|
+
...
|
|
162
|
+
await client.stop_chat(task_id="...")
|
|
163
|
+
messages = await client.get_messages(conversation_id="...", user="...")
|
|
164
|
+
convs = await client.get_conversations(user="...")
|
|
165
|
+
upload_result = await client.upload_file(file_path="...", user="...")
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Project Structure
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
ag_ui_dify/
|
|
172
|
+
├── __init__.py # Package exports
|
|
173
|
+
├── types.py # Dify type definitions (Pydantic models)
|
|
174
|
+
├── dify_client.py # Async HTTP client for all Dify endpoints
|
|
175
|
+
├── event_translator.py # Event translators (Chat/Agent/Workflow/Completion)
|
|
176
|
+
├── agent.py # DifyAgent main adapter
|
|
177
|
+
└── server.py # Starlette HTTP SSE endpoint
|
|
178
|
+
tests/
|
|
179
|
+
├── test_types.py # Type model tests (19)
|
|
180
|
+
├── test_translator.py # Translator tests (14)
|
|
181
|
+
├── test_client.py # Client tests (5)
|
|
182
|
+
├── test_agent.py # Agent tests (10)
|
|
183
|
+
└── test_integration.py # Real-environment integration tests
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Verification Status
|
|
187
|
+
|
|
188
|
+
All 4 Dify app types have been verified against a real Dify instance:
|
|
189
|
+
|
|
190
|
+
| App Type | Real Dify Tested | Notes |
|
|
191
|
+
|---|---|---|
|
|
192
|
+
| Agent | ✓ | Tool calls, reasoning chain, multi-turn conversation |
|
|
193
|
+
| Workflow | ✓ | Node execution, agent_log sub-steps, text output |
|
|
194
|
+
| Chat | ✓ | Streaming text, message lifecycle |
|
|
195
|
+
| Completion | ✓ | Streaming text, input variables |
|
|
196
|
+
|
|
197
|
+
## Requirements
|
|
198
|
+
|
|
199
|
+
- Python >= 3.9
|
|
200
|
+
- ag-ui-protocol >= 0.1.17
|
|
201
|
+
- httpx >= 0.27.0
|
|
202
|
+
- pydantic >= 2.11.0
|
|
203
|
+
- starlette >= 0.40.0 (optional, for HTTP server)
|
|
204
|
+
- uvicorn (optional, for HTTP server)
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
MIT
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# ag-ui-dify-adapter
|
|
2
|
+
|
|
3
|
+
AG-UI 协议 Dify 适配器 — 将 [Dify](https://dify.ai) API 响应转换为 [AG-UI](https://ag-ui.com) 流式事件,使 Dify 驱动的 AI Agent 能够集成到任何兼容 AG-UI 的前端应用中。
|
|
4
|
+
|
|
5
|
+
## 特性
|
|
6
|
+
|
|
7
|
+
- **全部 4 种 Dify 应用类型**:Chat(对话)、Agent(智能体)、Workflow(工作流)、Completion(文本生成)
|
|
8
|
+
- **完整事件映射**:将全部 Dify SSE 事件映射为 AG-UI 的 17 种标准事件类型
|
|
9
|
+
- **工具调用支持**:Agent 工具调用(ReAct 循环)转换为 `TOOL_CALL_START/ARGS/END`
|
|
10
|
+
- **实时流式输出**:通过 `TEXT_MESSAGE_START/CONTENT/END` 实现逐 token 流式文本输出
|
|
11
|
+
- **多轮对话**:`thread_id` ↔ `conversation_id` 映射追踪
|
|
12
|
+
- **状态与上下文**:AG-UI 的 state/context/forwardedProps → Dify 的 input 变量
|
|
13
|
+
- **文件支持**:通过 Dify 文件上传 API 支持文件附件
|
|
14
|
+
- **HTTP 服务**:内置 Starlette SSE 流式端点
|
|
15
|
+
- **全异步**:基于 `httpx` 的完整异步支持
|
|
16
|
+
|
|
17
|
+
## 安装
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install ag-ui-dify-adapter
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
如需 HTTP 服务端:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install ag-ui-dify-adapter[server]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 快速开始
|
|
30
|
+
|
|
31
|
+
### 作为库使用
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
import asyncio
|
|
35
|
+
from ag_ui_dify import DifyAgent, DifyConfig, DifyAppType
|
|
36
|
+
from ag_ui.core import RunAgentInput, UserMessage
|
|
37
|
+
|
|
38
|
+
async def main():
|
|
39
|
+
agent = DifyAgent(DifyConfig(
|
|
40
|
+
api_key="app-xxx",
|
|
41
|
+
base_url="https://api.dify.ai/v1",
|
|
42
|
+
app_type=DifyAppType.AGENT, # 不指定则自动检测
|
|
43
|
+
))
|
|
44
|
+
|
|
45
|
+
input = RunAgentInput(
|
|
46
|
+
thread_id="thread-1",
|
|
47
|
+
run_id="run-1",
|
|
48
|
+
state=None,
|
|
49
|
+
messages=[UserMessage(id="u1", role="user", content="你好!")],
|
|
50
|
+
tools=[],
|
|
51
|
+
context=[],
|
|
52
|
+
forwarded_props={},
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
async for event in agent.run(input):
|
|
56
|
+
print(event.model_dump_json(by_alias=True))
|
|
57
|
+
|
|
58
|
+
asyncio.run(main())
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 作为 HTTP 服务运行
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from ag_ui_dify import create_app
|
|
65
|
+
import uvicorn
|
|
66
|
+
|
|
67
|
+
app = create_app()
|
|
68
|
+
uvicorn.run(app, host="0.0.0.0", port=8080)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
发送请求:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
curl -X POST http://localhost:8080/ \
|
|
75
|
+
-H "Content-Type: application/json" \
|
|
76
|
+
-d '{
|
|
77
|
+
"threadId": "t1",
|
|
78
|
+
"runId": "r1",
|
|
79
|
+
"messages": [{"id": "u1", "role": "user", "content": "你好!"}],
|
|
80
|
+
"tools": [],
|
|
81
|
+
"context": [],
|
|
82
|
+
"forwardedProps": {
|
|
83
|
+
"apiKey": "app-xxx",
|
|
84
|
+
"baseUrl": "https://api.dify.ai/v1",
|
|
85
|
+
"appType": "agent"
|
|
86
|
+
}
|
|
87
|
+
}'
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Dify → AG-UI 事件映射
|
|
91
|
+
|
|
92
|
+
### Agent(智能体)应用
|
|
93
|
+
|
|
94
|
+
| Dify SSE 事件 | AG-UI 事件 |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `agent_thought`(带思考内容) | `STEP_STARTED` + `CUSTOM`(思考) |
|
|
97
|
+
| `agent_thought`(带工具调用) | `TOOL_CALL_START` + `TOOL_CALL_ARGS` + `TOOL_CALL_END` |
|
|
98
|
+
| `agent_thought`(带观察结果) | `CUSTOM`(观察)+ `STEP_FINISHED` |
|
|
99
|
+
| `agent_message`(首条) | `TEXT_MESSAGE_START` |
|
|
100
|
+
| `agent_message` | `TEXT_MESSAGE_CONTENT` |
|
|
101
|
+
| `message_end` | `TEXT_MESSAGE_END` + `RUN_FINISHED` |
|
|
102
|
+
|
|
103
|
+
### Workflow(工作流)应用
|
|
104
|
+
|
|
105
|
+
| Dify SSE 事件 | AG-UI 事件 |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `workflow_started` | `RUN_STARTED` |
|
|
108
|
+
| `node_started` | `STEP_STARTED` |
|
|
109
|
+
| `agent_log` | `STEP_STARTED` / `STEP_FINISHED` |
|
|
110
|
+
| `text_chunk` | `TEXT_MESSAGE_CONTENT` |
|
|
111
|
+
| `node_finished` | `STEP_FINISHED` |
|
|
112
|
+
| `workflow_finished` | `TEXT_MESSAGE_END` + `RUN_FINISHED` |
|
|
113
|
+
|
|
114
|
+
### Chat(对话)应用
|
|
115
|
+
|
|
116
|
+
| Dify SSE 事件 | AG-UI 事件 |
|
|
117
|
+
|---|---|
|
|
118
|
+
| `message`(首条) | `TEXT_MESSAGE_START` |
|
|
119
|
+
| `message` | `TEXT_MESSAGE_CONTENT` |
|
|
120
|
+
| `message_end` | `TEXT_MESSAGE_END` + `RUN_FINISHED` |
|
|
121
|
+
| `message_file` | `CUSTOM` |
|
|
122
|
+
|
|
123
|
+
### Completion(文本生成)应用
|
|
124
|
+
|
|
125
|
+
| Dify SSE 事件 | AG-UI 事件 |
|
|
126
|
+
|---|---|
|
|
127
|
+
| `message`(首条) | `TEXT_MESSAGE_START` |
|
|
128
|
+
| `message` | `TEXT_MESSAGE_CONTENT` |
|
|
129
|
+
| `message_end` | `TEXT_MESSAGE_END` + `RUN_FINISHED` |
|
|
130
|
+
|
|
131
|
+
所有应用类型:开头均发送 `RUN_STARTED`,出错时发送 `RUN_ERROR`,`ping` 心跳事件被忽略。
|
|
132
|
+
|
|
133
|
+
## API 参考
|
|
134
|
+
|
|
135
|
+
### DifyAgent
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
agent = DifyAgent(DifyConfig(
|
|
139
|
+
api_key="app-xxx", # 必填:Dify API 密钥
|
|
140
|
+
base_url="...", # 默认:https://api.dify.ai/v1
|
|
141
|
+
app_type=DifyAppType.AGENT, # 不指定则自动检测
|
|
142
|
+
user="ag-ui-user", # 默认用户标识
|
|
143
|
+
timeout=120.0, # HTTP 超时时间(秒)
|
|
144
|
+
))
|
|
145
|
+
|
|
146
|
+
async for event in agent.run(run_input):
|
|
147
|
+
...
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### DifyClient
|
|
151
|
+
|
|
152
|
+
底层异步客户端,覆盖全部 Dify API 端点:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
client = DifyClient(config)
|
|
156
|
+
async for evt in client.stream_chat(query="你好", inputs={}):
|
|
157
|
+
...
|
|
158
|
+
async for evt in client.stream_workflow(inputs={"url": "..."}):
|
|
159
|
+
...
|
|
160
|
+
async for evt in client.stream_completion(inputs={}):
|
|
161
|
+
...
|
|
162
|
+
await client.stop_chat(task_id="...")
|
|
163
|
+
messages = await client.get_messages(conversation_id="...", user="...")
|
|
164
|
+
convs = await client.get_conversations(user="...")
|
|
165
|
+
upload_result = await client.upload_file(file_path="...", user="...")
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## 依赖
|
|
169
|
+
|
|
170
|
+
- Python >= 3.9
|
|
171
|
+
- ag-ui-protocol >= 0.1.17
|
|
172
|
+
- httpx >= 0.27.0
|
|
173
|
+
- pydantic >= 2.11.0
|
|
174
|
+
- starlette >= 0.40.0(可选,用于 HTTP 服务)
|
|
175
|
+
- uvicorn(可选,用于 HTTP 服务)
|
|
176
|
+
|
|
177
|
+
## 项目结构
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
ag_ui_dify/
|
|
181
|
+
├── __init__.py # 包导出
|
|
182
|
+
├── types.py # Dify 类型定义(Pydantic 模型)
|
|
183
|
+
├── dify_client.py # Dify API 异步 HTTP 客户端
|
|
184
|
+
├── event_translator.py # 事件转换器(Chat/Agent/Workflow/Completion)
|
|
185
|
+
├── agent.py # DifyAgent 主适配器
|
|
186
|
+
└── server.py # Starlette HTTP SSE 端点
|
|
187
|
+
tests/
|
|
188
|
+
├── test_types.py # 类型模型测试(19 个)
|
|
189
|
+
├── test_translator.py # 转换器测试(14 个)
|
|
190
|
+
├── test_client.py # 客户端测试(5 个)
|
|
191
|
+
├── test_agent.py # Agent 测试(10 个)
|
|
192
|
+
└── test_integration.py # 实际环境集成测试
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## 验证状态
|
|
196
|
+
|
|
197
|
+
全部 4 种 Dify 应用类型已在真实 Dify 实例上完成验证:
|
|
198
|
+
|
|
199
|
+
| App 类型 | 实际 Dify 环境 | 验证内容 |
|
|
200
|
+
|---|---|---|
|
|
201
|
+
| Agent(智能体) | ✅ | 工具调用、思考链、多轮对话 |
|
|
202
|
+
| Workflow(工作流) | ✅ | 节点执行、Agent 子步骤、文本输出 |
|
|
203
|
+
| Chat(对话) | ✅ | 流式文本、消息生命周期 |
|
|
204
|
+
| Completion(文本生成) | ✅ | 流式文本、输入变量 |
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
MIT
|