|
10 | 10 | import os |
11 | 11 | from typing import Any, BinaryIO, Dict, Generator, List, Optional, Tuple, Union |
12 | 12 |
|
13 | | -from .common import DifyBaseClient |
| 13 | +from .common import DifyBaseClient, DifyType |
14 | 14 |
|
15 | 15 |
|
16 | 16 | class AgentClient(DifyBaseClient): |
17 | 17 | """Dify Agent应用客户端类。 |
18 | 18 |
|
19 | 19 | 提供与Dify Agent应用API交互的方法,包括发送消息、获取历史消息、管理会话、 |
20 | 20 | 上传文件、语音转文字、文字转语音等功能。Agent应用支持迭代式规划推理和自主工具调用。 |
21 | | - """ |
| 21 | + """ |
| 22 | + type = DifyType.Agent |
22 | 23 |
|
23 | 24 | def send_message( |
24 | 25 | self, |
@@ -185,358 +186,6 @@ def get_suggested_questions( |
185 | 186 | print(f"所有推荐问题端点请求都失败。最后错误: {str(last_error)}") |
186 | 187 | return {"data": []} |
187 | 188 |
|
188 | | - def get_messages( |
189 | | - self, |
190 | | - conversation_id: str, |
191 | | - user: str, |
192 | | - first_id: str = None, |
193 | | - limit: int = 20, |
194 | | - ) -> Dict[str, Any]: |
195 | | - """ |
196 | | - 获取会话历史消息,滚动加载形式返回历史聊天记录,第一页返回最新limit条(倒序返回)。 |
197 | | -
|
198 | | - Args: |
199 | | - conversation_id (str): 会话ID |
200 | | - user (str): 用户标识 |
201 | | - first_id (str, optional): 当前页第一条聊天记录的ID。默认为None |
202 | | - limit (int, optional): 一次请求返回多少条聊天记录。默认为20 |
203 | | -
|
204 | | - Returns: |
205 | | - Dict[str, Any]: 消息列表及分页信息,包含agent_thoughts(Agent的思考过程) |
206 | | -
|
207 | | - Raises: |
208 | | - requests.HTTPError: 当API请求失败时 |
209 | | - """ |
210 | | - endpoint = "messages" |
211 | | - |
212 | | - params = { |
213 | | - "conversation_id": conversation_id, |
214 | | - "user": user, |
215 | | - "limit": limit, |
216 | | - } |
217 | | - |
218 | | - if first_id: |
219 | | - params["first_id"] = first_id |
220 | | - |
221 | | - return self.get(endpoint, params=params) |
222 | | - |
223 | | - def get_conversations( |
224 | | - self, |
225 | | - user: str, |
226 | | - last_id: str = None, |
227 | | - limit: int = 20, |
228 | | - sort_by: str = "-updated_at", |
229 | | - ) -> Dict[str, Any]: |
230 | | - """ |
231 | | - 获取会话列表,默认返回最近的20条。 |
232 | | -
|
233 | | - Args: |
234 | | - user (str): 用户标识 |
235 | | - last_id (str, optional): 当前页最后面一条记录的ID。默认为None |
236 | | - limit (int, optional): 一次请求返回多少条记录,默认20条,最大100条,最小1条。默认为20 |
237 | | - sort_by (str, optional): 排序字段,可选值:created_at, -created_at, updated_at, -updated_at。默认为"-updated_at" |
238 | | -
|
239 | | - Returns: |
240 | | - Dict[str, Any]: 会话列表及分页信息 |
241 | | -
|
242 | | - Raises: |
243 | | - ValueError: 当提供了无效的参数时 |
244 | | - requests.HTTPError: 当API请求失败时 |
245 | | - """ |
246 | | - valid_sort_values = ["created_at", "-created_at", "updated_at", "-updated_at"] |
247 | | - if sort_by not in valid_sort_values: |
248 | | - raise ValueError(f"sort_by must be one of {valid_sort_values}") |
249 | | - |
250 | | - if limit < 1 or limit > 100: |
251 | | - raise ValueError("limit must be between 1 and 100") |
252 | | - |
253 | | - endpoint = "conversations" |
254 | | - |
255 | | - params = { |
256 | | - "user": user, |
257 | | - "limit": limit, |
258 | | - "sort_by": sort_by, |
259 | | - } |
260 | | - |
261 | | - if last_id: |
262 | | - params["last_id"] = last_id |
263 | | - |
264 | | - return self.get(endpoint, params=params) |
265 | | - |
266 | | - def delete_conversation(self, conversation_id: str, user: str) -> Dict[str, Any]: |
267 | | - """ |
268 | | - 删除会话。 |
269 | | -
|
270 | | - Args: |
271 | | - conversation_id (str): 会话ID |
272 | | - user (str): 用户标识 |
273 | | -
|
274 | | - Returns: |
275 | | - Dict[str, Any]: 删除结果 |
276 | | -
|
277 | | - Raises: |
278 | | - requests.HTTPError: 当API请求失败时 |
279 | | - """ |
280 | | - endpoint = f"conversations/{conversation_id}" |
281 | | - payload = {"user": user} |
282 | | - return self._request("DELETE", endpoint, json=payload).json() |
283 | | - |
284 | | - def rename_conversation( |
285 | | - self, |
286 | | - conversation_id: str, |
287 | | - user: str, |
288 | | - name: str = None, |
289 | | - auto_generate: bool = False, |
290 | | - ) -> Dict[str, Any]: |
291 | | - """ |
292 | | - 会话重命名,对会话进行重命名,会话名称用于显示在支持多会话的客户端上。 |
293 | | -
|
294 | | - Args: |
295 | | - conversation_id (str): 会话ID |
296 | | - user (str): 用户标识 |
297 | | - name (str, optional): 名称,若auto_generate为True时,该参数可不传。默认为None |
298 | | - auto_generate (bool, optional): 自动生成标题。默认为False |
299 | | -
|
300 | | - Returns: |
301 | | - Dict[str, Any]: 重命名后的会话信息 |
302 | | -
|
303 | | - Raises: |
304 | | - ValueError: 当提供了无效的参数时 |
305 | | - requests.HTTPError: 当API请求失败时 |
306 | | - """ |
307 | | - if not auto_generate and not name: |
308 | | - raise ValueError("name is required when auto_generate is False") |
309 | | - |
310 | | - endpoint = f"conversations/{conversation_id}/name" |
311 | | - |
312 | | - payload = {"user": user, "auto_generate": auto_generate} |
313 | | - |
314 | | - if name: |
315 | | - payload["name"] = name |
316 | | - |
317 | | - return self.post(endpoint, json_data=payload) |
318 | | - |
319 | | - def audio_to_text(self, file_path: str, user: str) -> Dict[str, Any]: |
320 | | - """ |
321 | | - 语音转文字。 |
322 | | -
|
323 | | - Args: |
324 | | - file_path (str): 语音文件路径,支持格式:['mp3', 'mp4', 'mpeg', 'mpga', 'm4a', 'wav', 'webm'] |
325 | | - user (str): 用户标识 |
326 | | -
|
327 | | - Returns: |
328 | | - Dict[str, Any]: 转换结果,包含文字内容 |
329 | | -
|
330 | | - Raises: |
331 | | - FileNotFoundError: 当文件不存在时 |
332 | | - ValueError: 当文件格式不支持时 |
333 | | - requests.HTTPError: 当API请求失败时 |
334 | | - """ |
335 | | - if not os.path.exists(file_path): |
336 | | - raise FileNotFoundError(f"File not found: {file_path}") |
337 | | - |
338 | | - # 检查文件类型 |
339 | | - supported_extensions = ["mp3", "mp4", "mpeg", "mpga", "m4a", "wav", "webm"] |
340 | | - file_extension = os.path.splitext(file_path)[1].lower().replace(".", "") |
341 | | - |
342 | | - if file_extension not in supported_extensions: |
343 | | - raise ValueError( |
344 | | - f"Unsupported file type. Supported types: {supported_extensions}" |
345 | | - ) |
346 | | - |
347 | | - with open(file_path, "rb") as file: |
348 | | - files = {"file": file} |
349 | | - data = {"user": user} |
350 | | - |
351 | | - url = os.path.join(self.base_url, "audio-to-text") |
352 | | - |
353 | | - headers = self._get_headers() |
354 | | - # 移除Content-Type,让requests自动设置multipart/form-data |
355 | | - headers.pop("Content-Type", None) |
356 | | - |
357 | | - response = self._request( |
358 | | - "POST", "audio-to-text", headers=headers, files=files, data=data |
359 | | - ) |
360 | | - return response.json() |
361 | | - |
362 | | - def audio_to_text_obj( |
363 | | - self, file_obj: BinaryIO, filename: str, user: str |
364 | | - ) -> Dict[str, Any]: |
365 | | - """ |
366 | | - 使用文件对象进行语音转文字。 |
367 | | -
|
368 | | - Args: |
369 | | - file_obj (BinaryIO): 语音文件对象 |
370 | | - filename (str): 文件名,用于确定文件类型 |
371 | | - user (str): 用户标识 |
372 | | -
|
373 | | - Returns: |
374 | | - Dict[str, Any]: 转换结果,包含文字内容 |
375 | | -
|
376 | | - Raises: |
377 | | - ValueError: 当文件格式不支持时 |
378 | | - requests.HTTPError: 当API请求失败时 |
379 | | - """ |
380 | | - # 检查文件类型 |
381 | | - supported_extensions = ["mp3", "mp4", "mpeg", "mpga", "m4a", "wav", "webm"] |
382 | | - file_extension = os.path.splitext(filename)[1].lower().replace(".", "") |
383 | | - |
384 | | - if file_extension not in supported_extensions: |
385 | | - raise ValueError( |
386 | | - f"Unsupported file type. Supported types: {supported_extensions}" |
387 | | - ) |
388 | | - |
389 | | - files = {"file": (filename, file_obj)} |
390 | | - data = {"user": user} |
391 | | - |
392 | | - headers = self._get_headers() |
393 | | - # 移除Content-Type,让requests自动设置multipart/form-data |
394 | | - headers.pop("Content-Type", None) |
395 | | - |
396 | | - response = self._request( |
397 | | - "POST", "audio-to-text", headers=headers, files=files, data=data |
398 | | - ) |
399 | | - return response.json() |
400 | | - |
401 | | - def text_to_audio( |
402 | | - self, |
403 | | - user: str, |
404 | | - message_id: str = None, |
405 | | - text: str = None, |
406 | | - ) -> Dict[str, Any]: |
407 | | - """ |
408 | | - 文字转语音。 |
409 | | -
|
410 | | - Args: |
411 | | - user (str): 用户标识 |
412 | | - message_id (str, optional): Dify生成的文本消息ID,如果提供,系统会自动查找相应的内容直接合成语音。默认为None |
413 | | - text (str, optional): 语音生成内容,如果没有传message_id,则使用此字段内容。默认为None |
414 | | -
|
415 | | - Returns: |
416 | | - Dict[str, Any]: 转换结果,包含音频数据 |
417 | | -
|
418 | | - Raises: |
419 | | - ValueError: 当必要参数缺失时 |
420 | | - requests.HTTPError: 当API请求失败时 |
421 | | - """ |
422 | | - if not message_id and not text: |
423 | | - raise ValueError("Either message_id or text must be provided") |
424 | | - |
425 | | - endpoint = "text-to-audio" |
426 | | - |
427 | | - payload = {"user": user} |
428 | | - |
429 | | - if message_id: |
430 | | - payload["message_id"] = message_id |
431 | | - |
432 | | - if text: |
433 | | - payload["text"] = text |
434 | | - |
435 | | - return self.post(endpoint, json_data=payload) |
436 | | - |
437 | | - def upload_file(self, file_path: str, user: str) -> Dict[str, Any]: |
438 | | - """ |
439 | | - 上传文件(目前仅支持图片)到Dify API,可用于图文多模态理解。 |
440 | | -
|
441 | | - Args: |
442 | | - file_path (str): 要上传的文件路径,支持png, jpg, jpeg, webp, gif格式 |
443 | | - user (str): 用户标识 |
444 | | -
|
445 | | - Returns: |
446 | | - Dict[str, Any]: 上传文件的响应数据 |
447 | | -
|
448 | | - Raises: |
449 | | - FileNotFoundError: 当文件不存在时 |
450 | | - ValueError: 当文件格式不支持时 |
451 | | - requests.HTTPError: 当API请求失败时 |
452 | | - """ |
453 | | - if not os.path.exists(file_path): |
454 | | - raise FileNotFoundError(f"File not found: {file_path}") |
455 | | - |
456 | | - # 检查文件类型 |
457 | | - supported_extensions = ["png", "jpg", "jpeg", "webp", "gif"] |
458 | | - file_extension = os.path.splitext(file_path)[1].lower().replace(".", "") |
459 | | - |
460 | | - if file_extension not in supported_extensions: |
461 | | - raise ValueError( |
462 | | - f"Unsupported file type. Supported types: {supported_extensions}" |
463 | | - ) |
464 | | - |
465 | | - with open(file_path, "rb") as file: |
466 | | - files = {"file": file} |
467 | | - data = {"user": user} |
468 | | - |
469 | | - headers = self._get_headers() |
470 | | - # 移除Content-Type,让requests自动设置multipart/form-data |
471 | | - headers.pop("Content-Type", None) |
472 | | - |
473 | | - response = self._request( |
474 | | - "POST", "files/upload", headers=headers, files=files, data=data |
475 | | - ) |
476 | | - return response.json() |
477 | | - |
478 | | - def upload_file_obj( |
479 | | - self, file_obj: BinaryIO, filename: str, user: str |
480 | | - ) -> Dict[str, Any]: |
481 | | - """ |
482 | | - 使用文件对象上传文件(目前仅支持图片)到Dify API。 |
483 | | -
|
484 | | - Args: |
485 | | - file_obj (BinaryIO): 文件对象 |
486 | | - filename (str): 文件名,用于确定文件类型 |
487 | | - user (str): 用户标识 |
488 | | -
|
489 | | - Returns: |
490 | | - Dict[str, Any]: 上传文件的响应数据 |
491 | | -
|
492 | | - Raises: |
493 | | - ValueError: 当文件格式不支持时 |
494 | | - requests.HTTPError: 当API请求失败时 |
495 | | - """ |
496 | | - # 检查文件类型 |
497 | | - supported_extensions = ["png", "jpg", "jpeg", "webp", "gif"] |
498 | | - file_extension = os.path.splitext(filename)[1].lower().replace(".", "") |
499 | | - |
500 | | - if file_extension not in supported_extensions: |
501 | | - raise ValueError( |
502 | | - f"Unsupported file type. Supported types: {supported_extensions}" |
503 | | - ) |
504 | | - |
505 | | - files = {"file": (filename, file_obj)} |
506 | | - data = {"user": user} |
507 | | - |
508 | | - headers = self._get_headers() |
509 | | - # 移除Content-Type,让requests自动设置multipart/form-data |
510 | | - headers.pop("Content-Type", None) |
511 | | - |
512 | | - response = self._request( |
513 | | - "POST", "files/upload", headers=headers, files=files, data=data |
514 | | - ) |
515 | | - return response.json() |
516 | | - |
517 | | - def get_app_info(self) -> Dict[str, Any]: |
518 | | - """ |
519 | | - 获取应用基本信息。 |
520 | | -
|
521 | | - Returns: |
522 | | - Dict[str, Any]: 应用信息,包含名称、描述和标签 |
523 | | -
|
524 | | - Raises: |
525 | | - requests.HTTPError: 当API请求失败时 |
526 | | - """ |
527 | | - return self.get("info") |
528 | | - |
529 | | - def get_parameters(self) -> Dict[str, Any]: |
530 | | - """ |
531 | | - 获取应用参数,包括功能开关、输入参数名称、类型及默认值等。 |
532 | | -
|
533 | | - Returns: |
534 | | - Dict[str, Any]: 应用参数配置 |
535 | | -
|
536 | | - Raises: |
537 | | - requests.HTTPError: 当API请求失败时 |
538 | | - """ |
539 | | - return self.get("parameters") |
540 | 189 |
|
541 | 190 | def get_meta(self) -> Dict[str, Any]: |
542 | 191 | """ |
|
0 commit comments