CatPtain commited on
Commit
5ec6b0e
·
verified ·
1 Parent(s): eb28c78

Upload 23 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ images/demo.png filter=lfs diff=lfs merge=lfs -text
37
+ images/GitHub_README_cover.png filter=lfs diff=lfs merge=lfs -text
38
+ images/wechat.png filter=lfs diff=lfs merge=lfs -text
images/GitHub_README_cover.png ADDED

Git LFS Details

  • SHA256: 13d9356184e861fe9d62dfc359f83b40c7049f464719f2b8aa40fab1a8b80139
  • Pointer size: 132 Bytes
  • Size of remote file: 1.34 MB
images/demo.png ADDED

Git LFS Details

  • SHA256: 99fbb8f7dbbc1029a748bbc70fc2f7fa34c996ff0eed261e1606cfecd51cbb39
  • Pointer size: 131 Bytes
  • Size of remote file: 809 kB
images/models.png ADDED
images/wechat.png ADDED

Git LFS Details

  • SHA256: ab84e14aab8adfb0259cc149197e74dbbde19917c5f0739faa40c47cd83833ea
  • Pointer size: 131 Bytes
  • Size of remote file: 263 kB
sdks/README.md ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SDK
2
+
3
+ ## Java
4
+
5
+ https://github.com/langgenius/java-client/
6
+
7
+ ## Go
8
+
9
+ https://github.com/langgenius/dify-sdk-go
10
+
11
+ ## Ruby
12
+
13
+ https://github.com/langgenius/ruby-sdk
14
+
15
+ ## Python
16
+
17
+ TODO move to another place
18
+
19
+ ## PHP
20
+
21
+ TODO move to another place
22
+
23
+ ## Node.js
24
+
25
+ TODO move to another place
sdks/nodejs-client/.gitignore ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.js
7
+
8
+ # testing
9
+ /coverage
10
+
11
+ # next.js
12
+ /.next/
13
+ /out/
14
+
15
+ # production
16
+ /build
17
+
18
+ # misc
19
+ .DS_Store
20
+ *.pem
21
+
22
+ # debug
23
+ npm-debug.log*
24
+ yarn-debug.log*
25
+ yarn-error.log*
26
+ .pnpm-debug.log*
27
+
28
+ # local env files
29
+ .env*.local
30
+
31
+ # vercel
32
+ .vercel
33
+
34
+ # typescript
35
+ *.tsbuildinfo
36
+ next-env.d.ts
37
+
38
+ # npm
39
+ package-lock.json
40
+
41
+ # yarn
42
+ .pnp.cjs
43
+ .pnp.loader.mjs
44
+ .yarn/
45
+ .yarnrc.yml
46
+
47
+ # pmpm
48
+ pnpm-lock.yaml
sdks/nodejs-client/README.md ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dify Node.js SDK
2
+ This is the Node.js SDK for the Dify API, which allows you to easily integrate Dify into your Node.js applications.
3
+
4
+ ## Install
5
+ ```bash
6
+ npm install dify-client
7
+ ```
8
+
9
+ ## Usage
10
+ After installing the SDK, you can use it in your project like this:
11
+
12
+ ```js
13
+ import { DifyClient, ChatClient, CompletionClient } from 'dify-client'
14
+
15
+ const API_KEY = 'your-api-key-here'
16
+ const user = `random-user-id`
17
+ const query = 'Please tell me a short story in 10 words or less.'
18
+ const remote_url_files = [{
19
+ type: 'image',
20
+ transfer_method: 'remote_url',
21
+ url: 'your_url_address'
22
+ }]
23
+
24
+ // Create a completion client
25
+ const completionClient = new CompletionClient(API_KEY)
26
+ // Create a completion message
27
+ completionClient.createCompletionMessage({'query': query}, user)
28
+ // Create a completion message with vision model
29
+ completionClient.createCompletionMessage({'query': 'Describe the picture.'}, user, false, remote_url_files)
30
+
31
+ // Create a chat client
32
+ const chatClient = new ChatClient(API_KEY)
33
+ // Create a chat message in stream mode
34
+ const response = await chatClient.createChatMessage({}, query, user, true, null)
35
+ const stream = response.data;
36
+ stream.on('data', data => {
37
+ console.log(data);
38
+ });
39
+ stream.on('end', () => {
40
+ console.log('stream done');
41
+ });
42
+ // Create a chat message with vision model
43
+ chatClient.createChatMessage({}, 'Describe the picture.', user, false, null, remote_url_files)
44
+ // Fetch conversations
45
+ chatClient.getConversations(user)
46
+ // Fetch conversation messages
47
+ chatClient.getConversationMessages(conversationId, user)
48
+ // Rename conversation
49
+ chatClient.renameConversation(conversationId, name, user)
50
+
51
+
52
+ const client = new DifyClient(API_KEY)
53
+ // Fetch application parameters
54
+ client.getApplicationParameters(user)
55
+ // Provide feedback for a message
56
+ client.messageFeedback(messageId, rating, user)
57
+
58
+ ```
59
+
60
+ Replace 'your-api-key-here' with your actual Dify API key.Replace 'your-app-id-here' with your actual Dify APP ID.
61
+
62
+ ## License
63
+ This SDK is released under the MIT License.
sdks/nodejs-client/babel.config.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "presets": [
3
+ "@babel/preset-env"
4
+ ]
5
+ }
sdks/nodejs-client/index.d.ts ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Types.d.ts
2
+ export const BASE_URL: string;
3
+
4
+ export type RequestMethods = 'GET' | 'POST' | 'PATCH' | 'DELETE';
5
+
6
+ interface Params {
7
+ [key: string]: any;
8
+ }
9
+
10
+ interface HeaderParams {
11
+ [key: string]: string;
12
+ }
13
+
14
+ interface User {
15
+ }
16
+
17
+ export declare class DifyClient {
18
+ constructor(apiKey: string, baseUrl?: string);
19
+
20
+ updateApiKey(apiKey: string): void;
21
+
22
+ sendRequest(
23
+ method: RequestMethods,
24
+ endpoint: string,
25
+ data?: any,
26
+ params?: Params,
27
+ stream?: boolean,
28
+ headerParams?: HeaderParams
29
+ ): Promise<any>;
30
+
31
+ messageFeedback(message_id: string, rating: number, user: User): Promise<any>;
32
+
33
+ getApplicationParameters(user: User): Promise<any>;
34
+
35
+ fileUpload(data: FormData): Promise<any>;
36
+
37
+ textToAudio(text: string ,user: string, streaming?: boolean): Promise<any>;
38
+
39
+ getMeta(user: User): Promise<any>;
40
+ }
41
+
42
+ export declare class CompletionClient extends DifyClient {
43
+ createCompletionMessage(
44
+ inputs: any,
45
+ user: User,
46
+ stream?: boolean,
47
+ files?: File[] | null
48
+ ): Promise<any>;
49
+ }
50
+
51
+ export declare class ChatClient extends DifyClient {
52
+ createChatMessage(
53
+ inputs: any,
54
+ query: string,
55
+ user: User,
56
+ stream?: boolean,
57
+ conversation_id?: string | null,
58
+ files?: File[] | null
59
+ ): Promise<any>;
60
+
61
+ getSuggested(message_id: string, user: User): Promise<any>;
62
+
63
+ stopMessage(task_id: string, user: User) : Promise<any>;
64
+
65
+
66
+ getConversations(
67
+ user: User,
68
+ first_id?: string | null,
69
+ limit?: number | null,
70
+ pinned?: boolean | null
71
+ ): Promise<any>;
72
+
73
+ getConversationMessages(
74
+ user: User,
75
+ conversation_id?: string,
76
+ first_id?: string | null,
77
+ limit?: number | null
78
+ ): Promise<any>;
79
+
80
+ renameConversation(conversation_id: string, name: string, user: User,auto_generate:boolean): Promise<any>;
81
+
82
+ deleteConversation(conversation_id: string, user: User): Promise<any>;
83
+
84
+ audioToText(data: FormData): Promise<any>;
85
+ }
86
+
87
+ export declare class WorkflowClient extends DifyClient {
88
+ run(inputs: any, user: User, stream?: boolean,): Promise<any>;
89
+
90
+ stop(task_id: string, user: User): Promise<any>;
91
+ }
sdks/nodejs-client/index.js ADDED
@@ -0,0 +1,360 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import axios from "axios";
2
+ export const BASE_URL = "https://api.dify.ai/v1";
3
+
4
+ export const routes = {
5
+ // app's
6
+ feedback: {
7
+ method: "POST",
8
+ url: (message_id) => `/messages/${message_id}/feedbacks`,
9
+ },
10
+ application: {
11
+ method: "GET",
12
+ url: () => `/parameters`,
13
+ },
14
+ fileUpload: {
15
+ method: "POST",
16
+ url: () => `/files/upload`,
17
+ },
18
+ textToAudio: {
19
+ method: "POST",
20
+ url: () => `/text-to-audio`,
21
+ },
22
+ getMeta: {
23
+ method: "GET",
24
+ url: () => `/meta`,
25
+ },
26
+
27
+ // completion's
28
+ createCompletionMessage: {
29
+ method: "POST",
30
+ url: () => `/completion-messages`,
31
+ },
32
+
33
+ // chat's
34
+ createChatMessage: {
35
+ method: "POST",
36
+ url: () => `/chat-messages`,
37
+ },
38
+ getSuggested:{
39
+ method: "GET",
40
+ url: (message_id) => `/messages/${message_id}/suggested`,
41
+ },
42
+ stopChatMessage: {
43
+ method: "POST",
44
+ url: (task_id) => `/chat-messages/${task_id}/stop`,
45
+ },
46
+ getConversations: {
47
+ method: "GET",
48
+ url: () => `/conversations`,
49
+ },
50
+ getConversationMessages: {
51
+ method: "GET",
52
+ url: () => `/messages`,
53
+ },
54
+ renameConversation: {
55
+ method: "POST",
56
+ url: (conversation_id) => `/conversations/${conversation_id}/name`,
57
+ },
58
+ deleteConversation: {
59
+ method: "DELETE",
60
+ url: (conversation_id) => `/conversations/${conversation_id}`,
61
+ },
62
+ audioToText: {
63
+ method: "POST",
64
+ url: () => `/audio-to-text`,
65
+ },
66
+
67
+ // workflow‘s
68
+ runWorkflow: {
69
+ method: "POST",
70
+ url: () => `/workflows/run`,
71
+ },
72
+ stopWorkflow: {
73
+ method: "POST",
74
+ url: (task_id) => `/workflows/${task_id}/stop`,
75
+ }
76
+
77
+ };
78
+
79
+ export class DifyClient {
80
+ constructor(apiKey, baseUrl = BASE_URL) {
81
+ this.apiKey = apiKey;
82
+ this.baseUrl = baseUrl;
83
+ }
84
+
85
+ updateApiKey(apiKey) {
86
+ this.apiKey = apiKey;
87
+ }
88
+
89
+ async sendRequest(
90
+ method,
91
+ endpoint,
92
+ data = null,
93
+ params = null,
94
+ stream = false,
95
+ headerParams = {}
96
+ ) {
97
+ const headers = {
98
+ ...{
99
+ Authorization: `Bearer ${this.apiKey}`,
100
+ "Content-Type": "application/json",
101
+ },
102
+ ...headerParams
103
+ };
104
+
105
+ const url = `${this.baseUrl}${endpoint}`;
106
+ let response;
107
+ if (stream) {
108
+ response = await axios({
109
+ method,
110
+ url,
111
+ data,
112
+ params,
113
+ headers,
114
+ responseType: "stream",
115
+ });
116
+ } else {
117
+ response = await axios({
118
+ method,
119
+ url,
120
+ ...(method !== "GET" && { data }),
121
+ params,
122
+ headers,
123
+ responseType: "json",
124
+ });
125
+ }
126
+
127
+ return response;
128
+ }
129
+
130
+ messageFeedback(message_id, rating, user) {
131
+ const data = {
132
+ rating,
133
+ user,
134
+ };
135
+ return this.sendRequest(
136
+ routes.feedback.method,
137
+ routes.feedback.url(message_id),
138
+ data
139
+ );
140
+ }
141
+
142
+ getApplicationParameters(user) {
143
+ const params = { user };
144
+ return this.sendRequest(
145
+ routes.application.method,
146
+ routes.application.url(),
147
+ null,
148
+ params
149
+ );
150
+ }
151
+
152
+ fileUpload(data) {
153
+ return this.sendRequest(
154
+ routes.fileUpload.method,
155
+ routes.fileUpload.url(),
156
+ data,
157
+ null,
158
+ false,
159
+ {
160
+ "Content-Type": 'multipart/form-data'
161
+ }
162
+ );
163
+ }
164
+
165
+ textToAudio(text, user, streaming = false) {
166
+ const data = {
167
+ text,
168
+ user,
169
+ streaming
170
+ };
171
+ return this.sendRequest(
172
+ routes.textToAudio.method,
173
+ routes.textToAudio.url(),
174
+ data,
175
+ null,
176
+ streaming
177
+ );
178
+ }
179
+
180
+ getMeta(user) {
181
+ const params = { user };
182
+ return this.sendRequest(
183
+ routes.meta.method,
184
+ routes.meta.url(),
185
+ null,
186
+ params
187
+ );
188
+ }
189
+ }
190
+
191
+ export class CompletionClient extends DifyClient {
192
+ createCompletionMessage(inputs, user, stream = false, files = null) {
193
+ const data = {
194
+ inputs,
195
+ user,
196
+ response_mode: stream ? "streaming" : "blocking",
197
+ files,
198
+ };
199
+ return this.sendRequest(
200
+ routes.createCompletionMessage.method,
201
+ routes.createCompletionMessage.url(),
202
+ data,
203
+ null,
204
+ stream
205
+ );
206
+ }
207
+
208
+ runWorkflow(inputs, user, stream = false, files = null) {
209
+ const data = {
210
+ inputs,
211
+ user,
212
+ response_mode: stream ? "streaming" : "blocking",
213
+ };
214
+ return this.sendRequest(
215
+ routes.runWorkflow.method,
216
+ routes.runWorkflow.url(),
217
+ data,
218
+ null,
219
+ stream
220
+ );
221
+ }
222
+ }
223
+
224
+ export class ChatClient extends DifyClient {
225
+ createChatMessage(
226
+ inputs,
227
+ query,
228
+ user,
229
+ stream = false,
230
+ conversation_id = null,
231
+ files = null
232
+ ) {
233
+ const data = {
234
+ inputs,
235
+ query,
236
+ user,
237
+ response_mode: stream ? "streaming" : "blocking",
238
+ files,
239
+ };
240
+ if (conversation_id) data.conversation_id = conversation_id;
241
+
242
+ return this.sendRequest(
243
+ routes.createChatMessage.method,
244
+ routes.createChatMessage.url(),
245
+ data,
246
+ null,
247
+ stream
248
+ );
249
+ }
250
+
251
+ getSuggested(message_id, user) {
252
+ const data = { user };
253
+ return this.sendRequest(
254
+ routes.getSuggested.method,
255
+ routes.getSuggested.url(message_id),
256
+ data
257
+ );
258
+ }
259
+
260
+ stopMessage(task_id, user) {
261
+ const data = { user };
262
+ return this.sendRequest(
263
+ routes.stopChatMessage.method,
264
+ routes.stopChatMessage.url(task_id),
265
+ data
266
+ );
267
+ }
268
+
269
+ getConversations(user, first_id = null, limit = null, pinned = null) {
270
+ const params = { user, first_id: first_id, limit, pinned };
271
+ return this.sendRequest(
272
+ routes.getConversations.method,
273
+ routes.getConversations.url(),
274
+ null,
275
+ params
276
+ );
277
+ }
278
+
279
+ getConversationMessages(
280
+ user,
281
+ conversation_id = "",
282
+ first_id = null,
283
+ limit = null
284
+ ) {
285
+ const params = { user };
286
+
287
+ if (conversation_id) params.conversation_id = conversation_id;
288
+
289
+ if (first_id) params.first_id = first_id;
290
+
291
+ if (limit) params.limit = limit;
292
+
293
+ return this.sendRequest(
294
+ routes.getConversationMessages.method,
295
+ routes.getConversationMessages.url(),
296
+ null,
297
+ params
298
+ );
299
+ }
300
+
301
+ renameConversation(conversation_id, name, user, auto_generate) {
302
+ const data = { name, user, auto_generate };
303
+ return this.sendRequest(
304
+ routes.renameConversation.method,
305
+ routes.renameConversation.url(conversation_id),
306
+ data
307
+ );
308
+ }
309
+
310
+ deleteConversation(conversation_id, user) {
311
+ const data = { user };
312
+ return this.sendRequest(
313
+ routes.deleteConversation.method,
314
+ routes.deleteConversation.url(conversation_id),
315
+ data
316
+ );
317
+ }
318
+
319
+
320
+ audioToText(data) {
321
+ return this.sendRequest(
322
+ routes.audioToText.method,
323
+ routes.audioToText.url(),
324
+ data,
325
+ null,
326
+ false,
327
+ {
328
+ "Content-Type": 'multipart/form-data'
329
+ }
330
+ );
331
+ }
332
+
333
+ }
334
+
335
+ export class WorkflowClient extends DifyClient {
336
+ run(inputs,user,stream) {
337
+ const data = {
338
+ inputs,
339
+ response_mode: stream ? "streaming" : "blocking",
340
+ user
341
+ };
342
+
343
+ return this.sendRequest(
344
+ routes.runWorkflow.method,
345
+ routes.runWorkflow.url(),
346
+ data,
347
+ null,
348
+ stream
349
+ );
350
+ }
351
+
352
+ stop(task_id, user) {
353
+ const data = { user };
354
+ return this.sendRequest(
355
+ routes.stopWorkflow.method,
356
+ routes.stopWorkflow.url(task_id),
357
+ data
358
+ );
359
+ }
360
+ }
sdks/nodejs-client/index.test.js ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { DifyClient, BASE_URL, routes } from ".";
2
+
3
+ import axios from 'axios'
4
+
5
+ jest.mock('axios')
6
+
7
+ describe('Client', () => {
8
+ let difyClient
9
+ beforeEach(() => {
10
+ difyClient = new DifyClient('test')
11
+ })
12
+
13
+ test('should create a client', () => {
14
+ expect(difyClient).toBeDefined();
15
+ })
16
+ // test updateApiKey
17
+ test('should update the api key', () => {
18
+ difyClient.updateApiKey('test2');
19
+ expect(difyClient.apiKey).toBe('test2');
20
+ })
21
+ });
22
+
23
+ describe('Send Requests', () => {
24
+ let difyClient
25
+
26
+ beforeEach(() => {
27
+ difyClient = new DifyClient('test')
28
+ })
29
+
30
+ afterEach(() => {
31
+ jest.resetAllMocks()
32
+ })
33
+
34
+ it('should make a successful request to the application parameter', async () => {
35
+ const method = 'GET'
36
+ const endpoint = routes.application.url
37
+ const expectedResponse = { data: 'response' }
38
+ axios.mockResolvedValue(expectedResponse)
39
+
40
+ await difyClient.sendRequest(method, endpoint)
41
+
42
+ expect(axios).toHaveBeenCalledWith({
43
+ method,
44
+ url: `${BASE_URL}${endpoint}`,
45
+ params: null,
46
+ headers: {
47
+ Authorization: `Bearer ${difyClient.apiKey}`,
48
+ 'Content-Type': 'application/json',
49
+ },
50
+ responseType: 'json',
51
+ })
52
+
53
+ })
54
+
55
+ it('should handle errors from the API', async () => {
56
+ const method = 'GET'
57
+ const endpoint = '/test-endpoint'
58
+ const errorMessage = 'Request failed with status code 404'
59
+ axios.mockRejectedValue(new Error(errorMessage))
60
+
61
+ await expect(difyClient.sendRequest(method, endpoint)).rejects.toThrow(
62
+ errorMessage
63
+ )
64
+ })
65
+ })
sdks/nodejs-client/package.json ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "dify-client",
3
+ "version": "2.3.2",
4
+ "description": "This is the Node.js SDK for the Dify.AI API, which allows you to easily integrate Dify.AI into your Node.js applications.",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "types":"index.d.ts",
8
+ "keywords": [
9
+ "Dify",
10
+ "Dify.AI",
11
+ "LLM"
12
+ ],
13
+ "author": "Joel",
14
+ "contributors": [
15
+ "<crazywoola> <<[email protected]>> (https://github.com/crazywoola)"
16
+ ],
17
+ "license": "MIT",
18
+ "scripts": {
19
+ "test": "jest"
20
+ },
21
+ "jest": {
22
+ "transform": {
23
+ "^.+\\.[t|j]sx?$": "babel-jest"
24
+ }
25
+ },
26
+ "dependencies": {
27
+ "axios": "^1.3.5"
28
+ },
29
+ "devDependencies": {
30
+ "@babel/core": "^7.21.8",
31
+ "@babel/preset-env": "^7.21.5",
32
+ "babel-jest": "^29.5.0",
33
+ "jest": "^29.5.0"
34
+ }
35
+ }
sdks/php-client/README.md ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dify PHP SDK
2
+
3
+ This is the PHP SDK for the Dify API, which allows you to easily integrate Dify into your PHP applications.
4
+
5
+ ## Requirements
6
+
7
+ - PHP 7.2 or later
8
+ - Guzzle HTTP client library
9
+
10
+ ## Usage
11
+
12
+ After installing the SDK, you can use it in your project like this:
13
+
14
+ ```php
15
+ <?php
16
+
17
+ require 'vendor/autoload.php';
18
+
19
+ use YourVendorName\DifyPHP\DifyClient;
20
+ use YourVendorName\DifyPHP\CompletionClient;
21
+ use YourVendorName\DifyPHP\ChatClient;
22
+
23
+ $apiKey = 'your-api-key-here';
24
+
25
+ $difyClient = new DifyClient($apiKey);
26
+
27
+ // Create a completion client
28
+ $completionClient = new CompletionClient($apiKey);
29
+ $response = $completionClient->create_completion_message(array("query" => "Who are you?"), "blocking", "user_id");
30
+
31
+ // Create a chat client
32
+ $chatClient = new ChatClient($apiKey);
33
+ $response = $chatClient->create_chat_message(array(), "Who are you?", "user_id", "blocking", $conversation_id);
34
+
35
+ $fileForVision = [
36
+ [
37
+ "type" => "image",
38
+ "transfer_method" => "remote_url",
39
+ "url" => "your_image_url"
40
+ ]
41
+ ];
42
+
43
+ // $fileForVision = [
44
+ // [
45
+ // "type" => "image",
46
+ // "transfer_method" => "local_file",
47
+ // "url" => "your_file_id"
48
+ // ]
49
+ // ];
50
+
51
+ // Create a completion client with vision model like gpt-4-vision
52
+ $response = $completionClient->create_completion_message(array("query" => "Describe this image."), "blocking", "user_id", $fileForVision);
53
+
54
+ // Create a chat client with vision model like gpt-4-vision
55
+ $response = $chatClient->create_chat_message(array(), "Describe this image.", "user_id", "blocking", $conversation_id, $fileForVision);
56
+
57
+ // File Upload
58
+ $fileForUpload = [
59
+ [
60
+ 'tmp_name' => '/path/to/file/filename.jpg',
61
+ 'name' => 'filename.jpg'
62
+ ]
63
+ ];
64
+ $response = $difyClient->file_upload("user_id", $fileForUpload);
65
+ $result = json_decode($response->getBody(), true);
66
+ echo 'upload_file_id: ' . $result['id'];
67
+
68
+ // Fetch application parameters
69
+ $response = $difyClient->get_application_parameters("user_id");
70
+
71
+ // Provide feedback for a message
72
+ $response = $difyClient->message_feedback($message_id, $rating, "user_id");
73
+
74
+ // Other available methods:
75
+ // - get_conversation_messages()
76
+ // - get_conversations()
77
+ // - rename_conversation()
78
+ ```
79
+
80
+ Replace 'your-api-key-here' with your actual Dify API key.
81
+
82
+ ## License
83
+
84
+ This SDK is released under the MIT License.
sdks/php-client/dify-client.php ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require 'vendor/autoload.php';
4
+
5
+ use GuzzleHttp\Client;
6
+
7
+ class DifyClient {
8
+ protected $api_key;
9
+ protected $base_url;
10
+ protected $client;
11
+
12
+ public function __construct($api_key, $base_url = null) {
13
+ $this->api_key = $api_key;
14
+ $this->base_url = $base_url ?? "https://api.dify.ai/v1/";
15
+ $this->client = new Client([
16
+ 'base_uri' => $this->base_url,
17
+ 'headers' => [
18
+ 'Authorization' => 'Bearer ' . $this->api_key,
19
+ 'Content-Type' => 'application/json',
20
+ ],
21
+ ]);
22
+ $this->file_client = new Client([
23
+ 'base_uri' => $this->base_url,
24
+ 'headers' => [
25
+ 'Authorization' => 'Bearer ' . $this->api_key,
26
+ 'Content-Type' => 'multipart/form-data',
27
+ ],
28
+ ]);
29
+ }
30
+
31
+ protected function send_request($method, $endpoint, $data = null, $params = null, $stream = false) {
32
+ $options = [
33
+ 'json' => $data,
34
+ 'query' => $params,
35
+ 'stream' => $stream,
36
+ ];
37
+
38
+ $response = $this->client->request($method, $endpoint, $options);
39
+ return $response;
40
+ }
41
+
42
+ public function message_feedback($message_id, $rating, $user) {
43
+ $data = [
44
+ 'rating' => $rating,
45
+ 'user' => $user,
46
+ ];
47
+ return $this->send_request('POST', "messages/{$message_id}/feedbacks", $data);
48
+ }
49
+
50
+ public function get_application_parameters($user) {
51
+ $params = ['user' => $user];
52
+ return $this->send_request('GET', 'parameters', null, $params);
53
+ }
54
+
55
+ public function file_upload($user, $files) {
56
+ $data = ['user' => $user];
57
+ $options = [
58
+ 'multipart' => $this->prepareMultipart($data, $files)
59
+ ];
60
+
61
+ return $this->file_client->request('POST', 'files/upload', $options);
62
+ }
63
+
64
+ protected function prepareMultipart($data, $files) {
65
+ $multipart = [];
66
+ foreach ($data as $key => $value) {
67
+ $multipart[] = [
68
+ 'name' => $key,
69
+ 'contents' => $value
70
+ ];
71
+ }
72
+
73
+ foreach ($files as $file) {
74
+ $multipart[] = [
75
+ 'name' => 'file',
76
+ 'contents' => fopen($file['tmp_name'], 'r'),
77
+ 'filename' => $file['name']
78
+ ];
79
+ }
80
+
81
+ return $multipart;
82
+ }
83
+
84
+
85
+ public function text_to_audio($text, $user, $streaming = false) {
86
+ $data = [
87
+ 'text' => $text,
88
+ 'user' => $user,
89
+ 'streaming' => $streaming
90
+ ];
91
+
92
+ return $this->send_request('POST', 'text-to-audio', $data);
93
+ }
94
+
95
+ public function get_meta($user) {
96
+ $params = [
97
+ 'user' => $user
98
+ ];
99
+
100
+ return $this->send_request('GET', 'meta',null, $params);
101
+ }
102
+ }
103
+
104
+ class CompletionClient extends DifyClient {
105
+ public function create_completion_message($inputs, $response_mode, $user, $files = null) {
106
+ $data = [
107
+ 'inputs' => $inputs,
108
+ 'response_mode' => $response_mode,
109
+ 'user' => $user,
110
+ 'files' => $files,
111
+ ];
112
+ return $this->send_request('POST', 'completion-messages', $data, null, $response_mode === 'streaming');
113
+ }
114
+ }
115
+
116
+ class ChatClient extends DifyClient {
117
+ public function create_chat_message($inputs, $query, $user, $response_mode = 'blocking', $conversation_id = null, $files = null) {
118
+ $data = [
119
+ 'inputs' => $inputs,
120
+ 'query' => $query,
121
+ 'user' => $user,
122
+ 'response_mode' => $response_mode,
123
+ 'files' => $files,
124
+ ];
125
+ if ($conversation_id) {
126
+ $data['conversation_id'] = $conversation_id;
127
+ }
128
+
129
+ return $this->send_request('POST', 'chat-messages', $data, null, $response_mode === 'streaming');
130
+ }
131
+
132
+ public function get_suggestions($message_id, $user) {
133
+ $params = [
134
+ 'user' => $user
135
+ ]
136
+ return $this->send_request('GET', "messages/{$message_id}/suggested", null, $params);
137
+ }
138
+
139
+ public function stop_message($task_id, $user) {
140
+ $data = ['user' => $user];
141
+ return $this->send_request('POST', "chat-messages/{$task_id}/stop", $data);
142
+ }
143
+
144
+ public function get_conversations($user, $first_id = null, $limit = null, $pinned = null) {
145
+ $params = [
146
+ 'user' => $user,
147
+ 'first_id' => $first_id,
148
+ 'limit' => $limit,
149
+ 'pinned'=> $pinned,
150
+ ];
151
+ return $this->send_request('GET', 'conversations', null, $params);
152
+ }
153
+
154
+ public function get_conversation_messages($user, $conversation_id = null, $first_id = null, $limit = null) {
155
+ $params = ['user' => $user];
156
+
157
+ if ($conversation_id) {
158
+ $params['conversation_id'] = $conversation_id;
159
+ }
160
+ if ($first_id) {
161
+ $params['first_id'] = $first_id;
162
+ }
163
+ if ($limit) {
164
+ $params['limit'] = $limit;
165
+ }
166
+
167
+ return $this->send_request('GET', 'messages', null, $params);
168
+ }
169
+
170
+ public function rename_conversation($conversation_id, $name,$auto_generate, $user) {
171
+ $data = [
172
+ 'name' => $name,
173
+ 'user' => $user,
174
+ 'auto_generate' => $auto_generate
175
+ ];
176
+ return $this->send_request('PATCH', "conversations/{$conversation_id}", $data);
177
+ }
178
+
179
+ public function delete_conversation($conversation_id, $user) {
180
+ $data = [
181
+ 'user' => $user,
182
+ ];
183
+ return $this->send_request('DELETE', "conversations/{$conversation_id}", $data);
184
+ }
185
+
186
+ public function audio_to_text($audio_file, $user) {
187
+ $data = [
188
+ 'user' => $user,
189
+ ];
190
+ $options = [
191
+ 'multipart' => $this->prepareMultipart($data, $files)
192
+ ];
193
+ return $this->file_client->request('POST', 'audio-to-text', $options);
194
+
195
+ }
196
+
197
+ }
198
+
199
+ class WorkflowClient extends DifyClient{
200
+ public function run($inputs, $response_mode, $user) {
201
+ $data = [
202
+ 'inputs' => $inputs,
203
+ 'response_mode' => $response_mode,
204
+ 'user' => $user,
205
+ ];
206
+ return $this->send_request('POST', 'workflows/run', $data);
207
+ }
208
+
209
+ public function stop($task_id, $user) {
210
+ $data = [
211
+ 'user' => $user,
212
+ ];
213
+ return $this->send_request('POST', "workflows/tasks/{$task_id}/stop",$data);
214
+ }
215
+
216
+ }
sdks/python-client/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2023 LangGenius
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.
sdks/python-client/MANIFEST.in ADDED
@@ -0,0 +1 @@
 
 
1
+ recursive-include dify_client *.py
sdks/python-client/README.md ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # dify-client
2
+
3
+ A Dify App Service-API Client, using for build a webapp by request Service-API
4
+
5
+ ## Usage
6
+
7
+ First, install `dify-client` python sdk package:
8
+
9
+ ```
10
+ pip install dify-client
11
+ ```
12
+
13
+ Write your code with sdk:
14
+
15
+ - completion generate with `blocking` response_mode
16
+
17
+ ```python
18
+ from dify_client import CompletionClient
19
+
20
+ api_key = "your_api_key"
21
+
22
+ # Initialize CompletionClient
23
+ completion_client = CompletionClient(api_key)
24
+
25
+ # Create Completion Message using CompletionClient
26
+ completion_response = completion_client.create_completion_message(inputs={"query": "What's the weather like today?"},
27
+ response_mode="blocking", user="user_id")
28
+ completion_response.raise_for_status()
29
+
30
+ result = completion_response.json()
31
+
32
+ print(result.get('answer'))
33
+ ```
34
+
35
+ - completion using vision model, like gpt-4-vision
36
+
37
+ ```python
38
+ from dify_client import CompletionClient
39
+
40
+ api_key = "your_api_key"
41
+
42
+ # Initialize CompletionClient
43
+ completion_client = CompletionClient(api_key)
44
+
45
+ files = [{
46
+ "type": "image",
47
+ "transfer_method": "remote_url",
48
+ "url": "your_image_url"
49
+ }]
50
+
51
+ # files = [{
52
+ # "type": "image",
53
+ # "transfer_method": "local_file",
54
+ # "upload_file_id": "your_file_id"
55
+ # }]
56
+
57
+ # Create Completion Message using CompletionClient
58
+ completion_response = completion_client.create_completion_message(inputs={"query": "Describe the picture."},
59
+ response_mode="blocking", user="user_id", files=files)
60
+ completion_response.raise_for_status()
61
+
62
+ result = completion_response.json()
63
+
64
+ print(result.get('answer'))
65
+ ```
66
+
67
+ - chat generate with `streaming` response_mode
68
+
69
+ ```python
70
+ import json
71
+ from dify_client import ChatClient
72
+
73
+ api_key = "your_api_key"
74
+
75
+ # Initialize ChatClient
76
+ chat_client = ChatClient(api_key)
77
+
78
+ # Create Chat Message using ChatClient
79
+ chat_response = chat_client.create_chat_message(inputs={}, query="Hello", user="user_id", response_mode="streaming")
80
+ chat_response.raise_for_status()
81
+
82
+ for line in chat_response.iter_lines(decode_unicode=True):
83
+ line = line.split('data:', 1)[-1]
84
+ if line.strip():
85
+ line = json.loads(line.strip())
86
+ print(line.get('answer'))
87
+ ```
88
+
89
+ - chat using vision model, like gpt-4-vision
90
+
91
+ ```python
92
+ from dify_client import ChatClient
93
+
94
+ api_key = "your_api_key"
95
+
96
+ # Initialize ChatClient
97
+ chat_client = ChatClient(api_key)
98
+
99
+ files = [{
100
+ "type": "image",
101
+ "transfer_method": "remote_url",
102
+ "url": "your_image_url"
103
+ }]
104
+
105
+ # files = [{
106
+ # "type": "image",
107
+ # "transfer_method": "local_file",
108
+ # "upload_file_id": "your_file_id"
109
+ # }]
110
+
111
+ # Create Chat Message using ChatClient
112
+ chat_response = chat_client.create_chat_message(inputs={}, query="Describe the picture.", user="user_id",
113
+ response_mode="blocking", files=files)
114
+ chat_response.raise_for_status()
115
+
116
+ result = chat_response.json()
117
+
118
+ print(result.get("answer"))
119
+ ```
120
+
121
+ - upload file when using vision model
122
+
123
+ ```python
124
+ from dify_client import DifyClient
125
+
126
+ api_key = "your_api_key"
127
+
128
+ # Initialize Client
129
+ dify_client = DifyClient(api_key)
130
+
131
+ file_path = "your_image_file_path"
132
+ file_name = "panda.jpeg"
133
+ mime_type = "image/jpeg"
134
+
135
+ with open(file_path, "rb") as file:
136
+ files = {
137
+ "file": (file_name, file, mime_type)
138
+ }
139
+ response = dify_client.file_upload("user_id", files)
140
+
141
+ result = response.json()
142
+ print(f'upload_file_id: {result.get("id")}')
143
+ ```
144
+
145
+
146
+
147
+ - Others
148
+
149
+ ```python
150
+ from dify_client import ChatClient
151
+
152
+ api_key = "your_api_key"
153
+
154
+ # Initialize Client
155
+ client = ChatClient(api_key)
156
+
157
+ # Get App parameters
158
+ parameters = client.get_application_parameters(user="user_id")
159
+ parameters.raise_for_status()
160
+
161
+ print('[parameters]')
162
+ print(parameters.json())
163
+
164
+ # Get Conversation List (only for chat)
165
+ conversations = client.get_conversations(user="user_id")
166
+ conversations.raise_for_status()
167
+
168
+ print('[conversations]')
169
+ print(conversations.json())
170
+
171
+ # Get Message List (only for chat)
172
+ messages = client.get_conversation_messages(user="user_id", conversation_id="conversation_id")
173
+ messages.raise_for_status()
174
+
175
+ print('[messages]')
176
+ print(messages.json())
177
+
178
+ # Rename Conversation (only for chat)
179
+ rename_conversation_response = client.rename_conversation(conversation_id="conversation_id",
180
+ name="new_name", user="user_id")
181
+ rename_conversation_response.raise_for_status()
182
+
183
+ print('[rename result]')
184
+ print(rename_conversation_response.json())
185
+ ```
sdks/python-client/build.sh ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ rm -rf build dist *.egg-info
6
+
7
+ pip install setuptools wheel twine
8
+ python setup.py sdist bdist_wheel
9
+ twine upload dist/*
sdks/python-client/dify_client/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from dify_client.client import ChatClient, CompletionClient, DifyClient
sdks/python-client/dify_client/client.py ADDED
@@ -0,0 +1,459 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+
3
+ import requests
4
+
5
+
6
+ class DifyClient:
7
+ def __init__(self, api_key, base_url: str = "https://api.dify.ai/v1"):
8
+ self.api_key = api_key
9
+ self.base_url = base_url
10
+
11
+ def _send_request(self, method, endpoint, json=None, params=None, stream=False):
12
+ headers = {
13
+ "Authorization": f"Bearer {self.api_key}",
14
+ "Content-Type": "application/json",
15
+ }
16
+
17
+ url = f"{self.base_url}{endpoint}"
18
+ response = requests.request(
19
+ method, url, json=json, params=params, headers=headers, stream=stream
20
+ )
21
+
22
+ return response
23
+
24
+ def _send_request_with_files(self, method, endpoint, data, files):
25
+ headers = {"Authorization": f"Bearer {self.api_key}"}
26
+
27
+ url = f"{self.base_url}{endpoint}"
28
+ response = requests.request(
29
+ method, url, data=data, headers=headers, files=files
30
+ )
31
+
32
+ return response
33
+
34
+ def message_feedback(self, message_id, rating, user):
35
+ data = {"rating": rating, "user": user}
36
+ return self._send_request("POST", f"/messages/{message_id}/feedbacks", data)
37
+
38
+ def get_application_parameters(self, user):
39
+ params = {"user": user}
40
+ return self._send_request("GET", "/parameters", params=params)
41
+
42
+ def file_upload(self, user, files):
43
+ data = {"user": user}
44
+ return self._send_request_with_files(
45
+ "POST", "/files/upload", data=data, files=files
46
+ )
47
+
48
+ def text_to_audio(self, text: str, user: str, streaming: bool = False):
49
+ data = {"text": text, "user": user, "streaming": streaming}
50
+ return self._send_request("POST", "/text-to-audio", data=data)
51
+
52
+ def get_meta(self, user):
53
+ params = {"user": user}
54
+ return self._send_request("GET", "/meta", params=params)
55
+
56
+
57
+ class CompletionClient(DifyClient):
58
+ def create_completion_message(self, inputs, response_mode, user, files=None):
59
+ data = {
60
+ "inputs": inputs,
61
+ "response_mode": response_mode,
62
+ "user": user,
63
+ "files": files,
64
+ }
65
+ return self._send_request(
66
+ "POST",
67
+ "/completion-messages",
68
+ data,
69
+ stream=True if response_mode == "streaming" else False,
70
+ )
71
+
72
+
73
+ class ChatClient(DifyClient):
74
+ def create_chat_message(
75
+ self,
76
+ inputs,
77
+ query,
78
+ user,
79
+ response_mode="blocking",
80
+ conversation_id=None,
81
+ files=None,
82
+ ):
83
+ data = {
84
+ "inputs": inputs,
85
+ "query": query,
86
+ "user": user,
87
+ "response_mode": response_mode,
88
+ "files": files,
89
+ }
90
+ if conversation_id:
91
+ data["conversation_id"] = conversation_id
92
+
93
+ return self._send_request(
94
+ "POST",
95
+ "/chat-messages",
96
+ data,
97
+ stream=True if response_mode == "streaming" else False,
98
+ )
99
+
100
+ def get_suggested(self, message_id, user: str):
101
+ params = {"user": user}
102
+ return self._send_request(
103
+ "GET", f"/messages/{message_id}/suggested", params=params
104
+ )
105
+
106
+ def stop_message(self, task_id, user):
107
+ data = {"user": user}
108
+ return self._send_request("POST", f"/chat-messages/{task_id}/stop", data)
109
+
110
+ def get_conversations(self, user, last_id=None, limit=None, pinned=None):
111
+ params = {"user": user, "last_id": last_id, "limit": limit, "pinned": pinned}
112
+ return self._send_request("GET", "/conversations", params=params)
113
+
114
+ def get_conversation_messages(
115
+ self, user, conversation_id=None, first_id=None, limit=None
116
+ ):
117
+ params = {"user": user}
118
+
119
+ if conversation_id:
120
+ params["conversation_id"] = conversation_id
121
+ if first_id:
122
+ params["first_id"] = first_id
123
+ if limit:
124
+ params["limit"] = limit
125
+
126
+ return self._send_request("GET", "/messages", params=params)
127
+
128
+ def rename_conversation(
129
+ self, conversation_id, name, auto_generate: bool, user: str
130
+ ):
131
+ data = {"name": name, "auto_generate": auto_generate, "user": user}
132
+ return self._send_request(
133
+ "POST", f"/conversations/{conversation_id}/name", data
134
+ )
135
+
136
+ def delete_conversation(self, conversation_id, user):
137
+ data = {"user": user}
138
+ return self._send_request("DELETE", f"/conversations/{conversation_id}", data)
139
+
140
+ def audio_to_text(self, audio_file, user):
141
+ data = {"user": user}
142
+ files = {"audio_file": audio_file}
143
+ return self._send_request_with_files("POST", "/audio-to-text", data, files)
144
+
145
+
146
+ class WorkflowClient(DifyClient):
147
+ def run(
148
+ self, inputs: dict, response_mode: str = "streaming", user: str = "abc-123"
149
+ ):
150
+ data = {"inputs": inputs, "response_mode": response_mode, "user": user}
151
+ return self._send_request("POST", "/workflows/run", data)
152
+
153
+ def stop(self, task_id, user):
154
+ data = {"user": user}
155
+ return self._send_request("POST", f"/workflows/tasks/{task_id}/stop", data)
156
+
157
+ def get_result(self, workflow_run_id):
158
+ return self._send_request("GET", f"/workflows/run/{workflow_run_id}")
159
+
160
+
161
+ class KnowledgeBaseClient(DifyClient):
162
+ def __init__(
163
+ self,
164
+ api_key,
165
+ base_url: str = "https://api.dify.ai/v1",
166
+ dataset_id: str | None = None,
167
+ ):
168
+ """
169
+ Construct a KnowledgeBaseClient object.
170
+
171
+ Args:
172
+ api_key (str): API key of Dify.
173
+ base_url (str, optional): Base URL of Dify API. Defaults to 'https://api.dify.ai/v1'.
174
+ dataset_id (str, optional): ID of the dataset. Defaults to None. You don't need this if you just want to
175
+ create a new dataset. or list datasets. otherwise you need to set this.
176
+ """
177
+ super().__init__(api_key=api_key, base_url=base_url)
178
+ self.dataset_id = dataset_id
179
+
180
+ def _get_dataset_id(self):
181
+ if self.dataset_id is None:
182
+ raise ValueError("dataset_id is not set")
183
+ return self.dataset_id
184
+
185
+ def create_dataset(self, name: str, **kwargs):
186
+ return self._send_request("POST", "/datasets", {"name": name}, **kwargs)
187
+
188
+ def list_datasets(self, page: int = 1, page_size: int = 20, **kwargs):
189
+ return self._send_request(
190
+ "GET", f"/datasets?page={page}&limit={page_size}", **kwargs
191
+ )
192
+
193
+ def create_document_by_text(
194
+ self, name, text, extra_params: dict | None = None, **kwargs
195
+ ):
196
+ """
197
+ Create a document by text.
198
+
199
+ :param name: Name of the document
200
+ :param text: Text content of the document
201
+ :param extra_params: extra parameters pass to the API, such as indexing_technique, process_rule. (optional)
202
+ e.g.
203
+ {
204
+ 'indexing_technique': 'high_quality',
205
+ 'process_rule': {
206
+ 'rules': {
207
+ 'pre_processing_rules': [
208
+ {'id': 'remove_extra_spaces', 'enabled': True},
209
+ {'id': 'remove_urls_emails', 'enabled': True}
210
+ ],
211
+ 'segmentation': {
212
+ 'separator': '\n',
213
+ 'max_tokens': 500
214
+ }
215
+ },
216
+ 'mode': 'custom'
217
+ }
218
+ }
219
+ :return: Response from the API
220
+ """
221
+ data = {
222
+ "indexing_technique": "high_quality",
223
+ "process_rule": {"mode": "automatic"},
224
+ "name": name,
225
+ "text": text,
226
+ }
227
+ if extra_params is not None and isinstance(extra_params, dict):
228
+ data.update(extra_params)
229
+ url = f"/datasets/{self._get_dataset_id()}/document/create_by_text"
230
+ return self._send_request("POST", url, json=data, **kwargs)
231
+
232
+ def update_document_by_text(
233
+ self, document_id, name, text, extra_params: dict | None = None, **kwargs
234
+ ):
235
+ """
236
+ Update a document by text.
237
+
238
+ :param document_id: ID of the document
239
+ :param name: Name of the document
240
+ :param text: Text content of the document
241
+ :param extra_params: extra parameters pass to the API, such as indexing_technique, process_rule. (optional)
242
+ e.g.
243
+ {
244
+ 'indexing_technique': 'high_quality',
245
+ 'process_rule': {
246
+ 'rules': {
247
+ 'pre_processing_rules': [
248
+ {'id': 'remove_extra_spaces', 'enabled': True},
249
+ {'id': 'remove_urls_emails', 'enabled': True}
250
+ ],
251
+ 'segmentation': {
252
+ 'separator': '\n',
253
+ 'max_tokens': 500
254
+ }
255
+ },
256
+ 'mode': 'custom'
257
+ }
258
+ }
259
+ :return: Response from the API
260
+ """
261
+ data = {"name": name, "text": text}
262
+ if extra_params is not None and isinstance(extra_params, dict):
263
+ data.update(extra_params)
264
+ url = (
265
+ f"/datasets/{self._get_dataset_id()}/documents/{document_id}/update_by_text"
266
+ )
267
+ return self._send_request("POST", url, json=data, **kwargs)
268
+
269
+ def create_document_by_file(
270
+ self, file_path, original_document_id=None, extra_params: dict | None = None
271
+ ):
272
+ """
273
+ Create a document by file.
274
+
275
+ :param file_path: Path to the file
276
+ :param original_document_id: pass this ID if you want to replace the original document (optional)
277
+ :param extra_params: extra parameters pass to the API, such as indexing_technique, process_rule. (optional)
278
+ e.g.
279
+ {
280
+ 'indexing_technique': 'high_quality',
281
+ 'process_rule': {
282
+ 'rules': {
283
+ 'pre_processing_rules': [
284
+ {'id': 'remove_extra_spaces', 'enabled': True},
285
+ {'id': 'remove_urls_emails', 'enabled': True}
286
+ ],
287
+ 'segmentation': {
288
+ 'separator': '\n',
289
+ 'max_tokens': 500
290
+ }
291
+ },
292
+ 'mode': 'custom'
293
+ }
294
+ }
295
+ :return: Response from the API
296
+ """
297
+ files = {"file": open(file_path, "rb")}
298
+ data = {
299
+ "process_rule": {"mode": "automatic"},
300
+ "indexing_technique": "high_quality",
301
+ }
302
+ if extra_params is not None and isinstance(extra_params, dict):
303
+ data.update(extra_params)
304
+ if original_document_id is not None:
305
+ data["original_document_id"] = original_document_id
306
+ url = f"/datasets/{self._get_dataset_id()}/document/create_by_file"
307
+ return self._send_request_with_files(
308
+ "POST", url, {"data": json.dumps(data)}, files
309
+ )
310
+
311
+ def update_document_by_file(
312
+ self, document_id, file_path, extra_params: dict | None = None
313
+ ):
314
+ """
315
+ Update a document by file.
316
+
317
+ :param document_id: ID of the document
318
+ :param file_path: Path to the file
319
+ :param extra_params: extra parameters pass to the API, such as indexing_technique, process_rule. (optional)
320
+ e.g.
321
+ {
322
+ 'indexing_technique': 'high_quality',
323
+ 'process_rule': {
324
+ 'rules': {
325
+ 'pre_processing_rules': [
326
+ {'id': 'remove_extra_spaces', 'enabled': True},
327
+ {'id': 'remove_urls_emails', 'enabled': True}
328
+ ],
329
+ 'segmentation': {
330
+ 'separator': '\n',
331
+ 'max_tokens': 500
332
+ }
333
+ },
334
+ 'mode': 'custom'
335
+ }
336
+ }
337
+ :return:
338
+ """
339
+ files = {"file": open(file_path, "rb")}
340
+ data = {}
341
+ if extra_params is not None and isinstance(extra_params, dict):
342
+ data.update(extra_params)
343
+ url = (
344
+ f"/datasets/{self._get_dataset_id()}/documents/{document_id}/update_by_file"
345
+ )
346
+ return self._send_request_with_files(
347
+ "POST", url, {"data": json.dumps(data)}, files
348
+ )
349
+
350
+ def batch_indexing_status(self, batch_id: str, **kwargs):
351
+ """
352
+ Get the status of the batch indexing.
353
+
354
+ :param batch_id: ID of the batch uploading
355
+ :return: Response from the API
356
+ """
357
+ url = f"/datasets/{self._get_dataset_id()}/documents/{batch_id}/indexing-status"
358
+ return self._send_request("GET", url, **kwargs)
359
+
360
+ def delete_dataset(self):
361
+ """
362
+ Delete this dataset.
363
+
364
+ :return: Response from the API
365
+ """
366
+ url = f"/datasets/{self._get_dataset_id()}"
367
+ return self._send_request("DELETE", url)
368
+
369
+ def delete_document(self, document_id):
370
+ """
371
+ Delete a document.
372
+
373
+ :param document_id: ID of the document
374
+ :return: Response from the API
375
+ """
376
+ url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}"
377
+ return self._send_request("DELETE", url)
378
+
379
+ def list_documents(
380
+ self,
381
+ page: int | None = None,
382
+ page_size: int | None = None,
383
+ keyword: str | None = None,
384
+ **kwargs,
385
+ ):
386
+ """
387
+ Get a list of documents in this dataset.
388
+
389
+ :return: Response from the API
390
+ """
391
+ params = {}
392
+ if page is not None:
393
+ params["page"] = page
394
+ if page_size is not None:
395
+ params["limit"] = page_size
396
+ if keyword is not None:
397
+ params["keyword"] = keyword
398
+ url = f"/datasets/{self._get_dataset_id()}/documents"
399
+ return self._send_request("GET", url, params=params, **kwargs)
400
+
401
+ def add_segments(self, document_id, segments, **kwargs):
402
+ """
403
+ Add segments to a document.
404
+
405
+ :param document_id: ID of the document
406
+ :param segments: List of segments to add, example: [{"content": "1", "answer": "1", "keyword": ["a"]}]
407
+ :return: Response from the API
408
+ """
409
+ data = {"segments": segments}
410
+ url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}/segments"
411
+ return self._send_request("POST", url, json=data, **kwargs)
412
+
413
+ def query_segments(
414
+ self,
415
+ document_id,
416
+ keyword: str | None = None,
417
+ status: str | None = None,
418
+ **kwargs,
419
+ ):
420
+ """
421
+ Query segments in this document.
422
+
423
+ :param document_id: ID of the document
424
+ :param keyword: query keyword, optional
425
+ :param status: status of the segment, optional, e.g. completed
426
+ """
427
+ url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}/segments"
428
+ params = {}
429
+ if keyword is not None:
430
+ params["keyword"] = keyword
431
+ if status is not None:
432
+ params["status"] = status
433
+ if "params" in kwargs:
434
+ params.update(kwargs["params"])
435
+ return self._send_request("GET", url, params=params, **kwargs)
436
+
437
+ def delete_document_segment(self, document_id, segment_id):
438
+ """
439
+ Delete a segment from a document.
440
+
441
+ :param document_id: ID of the document
442
+ :param segment_id: ID of the segment
443
+ :return: Response from the API
444
+ """
445
+ url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}/segments/{segment_id}"
446
+ return self._send_request("DELETE", url)
447
+
448
+ def update_document_segment(self, document_id, segment_id, segment_data, **kwargs):
449
+ """
450
+ Update a segment in a document.
451
+
452
+ :param document_id: ID of the document
453
+ :param segment_id: ID of the segment
454
+ :param segment_data: Data of the segment, example: {"content": "1", "answer": "1", "keyword": ["a"], "enabled": True}
455
+ :return: Response from the API
456
+ """
457
+ data = {"segment": segment_data}
458
+ url = f"/datasets/{self._get_dataset_id()}/documents/{document_id}/segments/{segment_id}"
459
+ return self._send_request("POST", url, json=data, **kwargs)
sdks/python-client/setup.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from setuptools import setup
2
+
3
+ with open("README.md", "r", encoding="utf-8") as fh:
4
+ long_description = fh.read()
5
+
6
+ setup(
7
+ name="dify-client",
8
+ version="0.1.12",
9
+ author="Dify",
10
+ author_email="[email protected]",
11
+ description="A package for interacting with the Dify Service-API",
12
+ long_description=long_description,
13
+ long_description_content_type="text/markdown",
14
+ url="https://github.com/langgenius/dify",
15
+ license="MIT",
16
+ packages=["dify_client"],
17
+ classifiers=[
18
+ "Programming Language :: Python :: 3",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Operating System :: OS Independent",
21
+ ],
22
+ python_requires=">=3.6",
23
+ install_requires=["requests"],
24
+ keywords="dify nlp ai language-processing",
25
+ include_package_data=True,
26
+ )
sdks/python-client/tests/__init__.py ADDED
File without changes
sdks/python-client/tests/test_client.py ADDED
@@ -0,0 +1,274 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ import unittest
4
+
5
+ from dify_client.client import (
6
+ ChatClient,
7
+ CompletionClient,
8
+ DifyClient,
9
+ KnowledgeBaseClient,
10
+ )
11
+
12
+ API_KEY = os.environ.get("API_KEY")
13
+ APP_ID = os.environ.get("APP_ID")
14
+ API_BASE_URL = os.environ.get("API_BASE_URL", "https://api.dify.ai/v1")
15
+ FILE_PATH_BASE = os.path.dirname(__file__)
16
+
17
+
18
+ class TestKnowledgeBaseClient(unittest.TestCase):
19
+ def setUp(self):
20
+ self.knowledge_base_client = KnowledgeBaseClient(API_KEY, base_url=API_BASE_URL)
21
+ self.README_FILE_PATH = os.path.abspath(
22
+ os.path.join(FILE_PATH_BASE, "../README.md")
23
+ )
24
+ self.dataset_id = None
25
+ self.document_id = None
26
+ self.segment_id = None
27
+ self.batch_id = None
28
+
29
+ def _get_dataset_kb_client(self):
30
+ self.assertIsNotNone(self.dataset_id)
31
+ return KnowledgeBaseClient(
32
+ API_KEY, base_url=API_BASE_URL, dataset_id=self.dataset_id
33
+ )
34
+
35
+ def test_001_create_dataset(self):
36
+ response = self.knowledge_base_client.create_dataset(name="test_dataset")
37
+ data = response.json()
38
+ self.assertIn("id", data)
39
+ self.dataset_id = data["id"]
40
+ self.assertEqual("test_dataset", data["name"])
41
+
42
+ # the following tests require to be executed in order because they use
43
+ # the dataset/document/segment ids from the previous test
44
+ self._test_002_list_datasets()
45
+ self._test_003_create_document_by_text()
46
+ time.sleep(1)
47
+ self._test_004_update_document_by_text()
48
+ # self._test_005_batch_indexing_status()
49
+ time.sleep(1)
50
+ self._test_006_update_document_by_file()
51
+ time.sleep(1)
52
+ self._test_007_list_documents()
53
+ self._test_008_delete_document()
54
+ self._test_009_create_document_by_file()
55
+ time.sleep(1)
56
+ self._test_010_add_segments()
57
+ self._test_011_query_segments()
58
+ self._test_012_update_document_segment()
59
+ self._test_013_delete_document_segment()
60
+ self._test_014_delete_dataset()
61
+
62
+ def _test_002_list_datasets(self):
63
+ response = self.knowledge_base_client.list_datasets()
64
+ data = response.json()
65
+ self.assertIn("data", data)
66
+ self.assertIn("total", data)
67
+
68
+ def _test_003_create_document_by_text(self):
69
+ client = self._get_dataset_kb_client()
70
+ response = client.create_document_by_text("test_document", "test_text")
71
+ data = response.json()
72
+ self.assertIn("document", data)
73
+ self.document_id = data["document"]["id"]
74
+ self.batch_id = data["batch"]
75
+
76
+ def _test_004_update_document_by_text(self):
77
+ client = self._get_dataset_kb_client()
78
+ self.assertIsNotNone(self.document_id)
79
+ response = client.update_document_by_text(
80
+ self.document_id, "test_document_updated", "test_text_updated"
81
+ )
82
+ data = response.json()
83
+ self.assertIn("document", data)
84
+ self.assertIn("batch", data)
85
+ self.batch_id = data["batch"]
86
+
87
+ def _test_005_batch_indexing_status(self):
88
+ client = self._get_dataset_kb_client()
89
+ response = client.batch_indexing_status(self.batch_id)
90
+ response.json()
91
+ self.assertEqual(response.status_code, 200)
92
+
93
+ def _test_006_update_document_by_file(self):
94
+ client = self._get_dataset_kb_client()
95
+ self.assertIsNotNone(self.document_id)
96
+ response = client.update_document_by_file(
97
+ self.document_id, self.README_FILE_PATH
98
+ )
99
+ data = response.json()
100
+ self.assertIn("document", data)
101
+ self.assertIn("batch", data)
102
+ self.batch_id = data["batch"]
103
+
104
+ def _test_007_list_documents(self):
105
+ client = self._get_dataset_kb_client()
106
+ response = client.list_documents()
107
+ data = response.json()
108
+ self.assertIn("data", data)
109
+
110
+ def _test_008_delete_document(self):
111
+ client = self._get_dataset_kb_client()
112
+ self.assertIsNotNone(self.document_id)
113
+ response = client.delete_document(self.document_id)
114
+ data = response.json()
115
+ self.assertIn("result", data)
116
+ self.assertEqual("success", data["result"])
117
+
118
+ def _test_009_create_document_by_file(self):
119
+ client = self._get_dataset_kb_client()
120
+ response = client.create_document_by_file(self.README_FILE_PATH)
121
+ data = response.json()
122
+ self.assertIn("document", data)
123
+ self.document_id = data["document"]["id"]
124
+ self.batch_id = data["batch"]
125
+
126
+ def _test_010_add_segments(self):
127
+ client = self._get_dataset_kb_client()
128
+ response = client.add_segments(
129
+ self.document_id, [{"content": "test text segment 1"}]
130
+ )
131
+ data = response.json()
132
+ self.assertIn("data", data)
133
+ self.assertGreater(len(data["data"]), 0)
134
+ segment = data["data"][0]
135
+ self.segment_id = segment["id"]
136
+
137
+ def _test_011_query_segments(self):
138
+ client = self._get_dataset_kb_client()
139
+ response = client.query_segments(self.document_id)
140
+ data = response.json()
141
+ self.assertIn("data", data)
142
+ self.assertGreater(len(data["data"]), 0)
143
+
144
+ def _test_012_update_document_segment(self):
145
+ client = self._get_dataset_kb_client()
146
+ self.assertIsNotNone(self.segment_id)
147
+ response = client.update_document_segment(
148
+ self.document_id,
149
+ self.segment_id,
150
+ {"content": "test text segment 1 updated"},
151
+ )
152
+ data = response.json()
153
+ self.assertIn("data", data)
154
+ self.assertGreater(len(data["data"]), 0)
155
+ segment = data["data"]
156
+ self.assertEqual("test text segment 1 updated", segment["content"])
157
+
158
+ def _test_013_delete_document_segment(self):
159
+ client = self._get_dataset_kb_client()
160
+ self.assertIsNotNone(self.segment_id)
161
+ response = client.delete_document_segment(self.document_id, self.segment_id)
162
+ data = response.json()
163
+ self.assertIn("result", data)
164
+ self.assertEqual("success", data["result"])
165
+
166
+ def _test_014_delete_dataset(self):
167
+ client = self._get_dataset_kb_client()
168
+ response = client.delete_dataset()
169
+ self.assertEqual(204, response.status_code)
170
+
171
+
172
+ class TestChatClient(unittest.TestCase):
173
+ def setUp(self):
174
+ self.chat_client = ChatClient(API_KEY)
175
+
176
+ def test_create_chat_message(self):
177
+ response = self.chat_client.create_chat_message(
178
+ {}, "Hello, World!", "test_user"
179
+ )
180
+ self.assertIn("answer", response.text)
181
+
182
+ def test_create_chat_message_with_vision_model_by_remote_url(self):
183
+ files = [
184
+ {"type": "image", "transfer_method": "remote_url", "url": "your_image_url"}
185
+ ]
186
+ response = self.chat_client.create_chat_message(
187
+ {}, "Describe the picture.", "test_user", files=files
188
+ )
189
+ self.assertIn("answer", response.text)
190
+
191
+ def test_create_chat_message_with_vision_model_by_local_file(self):
192
+ files = [
193
+ {
194
+ "type": "image",
195
+ "transfer_method": "local_file",
196
+ "upload_file_id": "your_file_id",
197
+ }
198
+ ]
199
+ response = self.chat_client.create_chat_message(
200
+ {}, "Describe the picture.", "test_user", files=files
201
+ )
202
+ self.assertIn("answer", response.text)
203
+
204
+ def test_get_conversation_messages(self):
205
+ response = self.chat_client.get_conversation_messages(
206
+ "test_user", "your_conversation_id"
207
+ )
208
+ self.assertIn("answer", response.text)
209
+
210
+ def test_get_conversations(self):
211
+ response = self.chat_client.get_conversations("test_user")
212
+ self.assertIn("data", response.text)
213
+
214
+
215
+ class TestCompletionClient(unittest.TestCase):
216
+ def setUp(self):
217
+ self.completion_client = CompletionClient(API_KEY)
218
+
219
+ def test_create_completion_message(self):
220
+ response = self.completion_client.create_completion_message(
221
+ {"query": "What's the weather like today?"}, "blocking", "test_user"
222
+ )
223
+ self.assertIn("answer", response.text)
224
+
225
+ def test_create_completion_message_with_vision_model_by_remote_url(self):
226
+ files = [
227
+ {"type": "image", "transfer_method": "remote_url", "url": "your_image_url"}
228
+ ]
229
+ response = self.completion_client.create_completion_message(
230
+ {"query": "Describe the picture."}, "blocking", "test_user", files
231
+ )
232
+ self.assertIn("answer", response.text)
233
+
234
+ def test_create_completion_message_with_vision_model_by_local_file(self):
235
+ files = [
236
+ {
237
+ "type": "image",
238
+ "transfer_method": "local_file",
239
+ "upload_file_id": "your_file_id",
240
+ }
241
+ ]
242
+ response = self.completion_client.create_completion_message(
243
+ {"query": "Describe the picture."}, "blocking", "test_user", files
244
+ )
245
+ self.assertIn("answer", response.text)
246
+
247
+
248
+ class TestDifyClient(unittest.TestCase):
249
+ def setUp(self):
250
+ self.dify_client = DifyClient(API_KEY)
251
+
252
+ def test_message_feedback(self):
253
+ response = self.dify_client.message_feedback(
254
+ "your_message_id", "like", "test_user"
255
+ )
256
+ self.assertIn("success", response.text)
257
+
258
+ def test_get_application_parameters(self):
259
+ response = self.dify_client.get_application_parameters("test_user")
260
+ self.assertIn("user_input_form", response.text)
261
+
262
+ def test_file_upload(self):
263
+ file_path = "your_image_file_path"
264
+ file_name = "panda.jpeg"
265
+ mime_type = "image/jpeg"
266
+
267
+ with open(file_path, "rb") as file:
268
+ files = {"file": (file_name, file, mime_type)}
269
+ response = self.dify_client.file_upload("test_user", files)
270
+ self.assertIn("name", response.text)
271
+
272
+
273
+ if __name__ == "__main__":
274
+ unittest.main()