Skip to content

Commit d05e0f5

Browse files
committed
updateupdate
1 parent ff0f73c commit d05e0f5

File tree

5 files changed

+222
-91
lines changed

5 files changed

+222
-91
lines changed

example.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
这是一个测试文件,用于演示 Dify API 的文件上传功能。

examples/workflow_example.py

Lines changed: 112 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,38 @@ def example_run_workflow_blocking():
8787
"""以阻塞模式运行工作流示例"""
8888
print("\n==== 以阻塞模式运行工作流 ====")
8989

90+
file = 'example.txt'
91+
if not os.path.exists(file):
92+
with open(file, 'w') as f:
93+
f.write('这是一个测试文件,用于演示 Dify API 的文件上传功能。')
94+
95+
file_id = client.upload_file(file, USER_ID)['id']
96+
9097
# 准备输入参数
9198
inputs = {
9299
"input": "请写一首关于人工智能的诗",
100+
'file1': {
101+
'type': 'document',
102+
'transfer_method': 'local_file',
103+
'upload_file_id': file_id
104+
},
105+
'files1': [
106+
{
107+
'type': 'document',
108+
'transfer_method': 'local_file',
109+
'upload_file_id': file_id
110+
},
111+
{
112+
'type': 'document',
113+
'transfer_method': 'local_file',
114+
'upload_file_id': file_id
115+
}
116+
]
93117
}
94-
118+
95119
# 获取请求参数
96120
request_kwargs = get_request_kwargs()
97-
121+
98122
try:
99123
# 执行工作流(阻塞模式)
100124
result = client.run(
@@ -103,7 +127,7 @@ def example_run_workflow_blocking():
103127
response_mode="blocking",
104128
**request_kwargs, # 传递请求参数
105129
)
106-
130+
107131
print("工作流执行结果:")
108132
pprint(result)
109133
return result
@@ -182,23 +206,94 @@ def on_workflow_finished(data):
182206
def example_upload_file():
183207
"""上传文件示例"""
184208
print("\n==== 上传文件 ====")
185-
186-
# 确保文件存在
187-
file_path = "example.txt"
188-
if not os.path.exists(file_path):
189-
with open(file_path, "w") as f:
190-
f.write("这是一个测试文件,用于演示 Dify API 的文件上传功能。")
191-
192-
# 上传文件
209+
210+
# 获取请求参数
211+
request_kwargs = get_request_kwargs()
212+
193213
try:
194-
result = client.upload_file(file_path, USER_ID)
214+
# 1. 首先检查应用参数配置
215+
params = client.get_parameters(**request_kwargs)
216+
217+
# 创建一个带有特定内容的测试文件
218+
file_path = "example.txt"
219+
if not os.path.exists(file_path):
220+
with open(file_path, "w") as f:
221+
f.write("这是一个测试文件,用于演示 Dify API 的文件上传功能。")
222+
223+
# 检查文件大小
224+
file_size = os.path.getsize(file_path) / (1024 * 1024) # 转换为MB
225+
print(f"文件大小: {file_size:.2f}MB")
226+
227+
system_params = params.get("system_parameters", {})
228+
file_size_limit = system_params.get("file_size_limit", 15) # 默认15MB
229+
230+
print(f"系统文件上传限制: {file_size_limit}MB")
231+
232+
if file_size > file_size_limit:
233+
print(f"警告: 文件大小({file_size:.2f}MB)超过限制({file_size_limit}MB)")
234+
235+
# 打印调试信息
236+
print(f"API地址: {client.base_url}")
237+
print(f"API密钥前缀: {client.api_key[:8]}...")
238+
239+
# 尝试创建一个图片文件(某些API可能只接受图片)
240+
try_image = True
241+
if try_image:
242+
print("创建测试图片文件...")
243+
image_path = "test_image.png"
244+
from PIL import Image
245+
import numpy as np
246+
247+
# 创建一个简单的彩色图片
248+
try:
249+
img = Image.new('RGB', (100, 100), color=(73, 109, 137))
250+
img.save(image_path)
251+
print(f"已创建测试图片: {image_path}")
252+
file_path = image_path
253+
except ImportError:
254+
print("无法创建图片文件,将使用文本文件")
255+
except Exception as e:
256+
print(f"创建图片失败: {e}")
257+
258+
# 上传文件,添加超时和重试参数
259+
print(f"正在上传文件: {file_path}")
260+
261+
# 增加超时时间,避免大文件上传超时
262+
request_kwargs["timeout"] = 60
263+
264+
result = client.upload_file(
265+
file_path,
266+
USER_ID,
267+
**request_kwargs # 传递请求参数
268+
)
269+
195270
print("文件上传成功:")
196271
pprint(result)
197-
272+
198273
# 返回上传的文件ID,可用于后续调用
199274
return result.get("id")
200275
except Exception as e:
201276
print(f"文件上传失败: {e}")
277+
278+
# 添加更详细的错误分析
279+
if "400" in str(e):
280+
print("\n可能的原因:")
281+
print("1. API密钥权限不足 - 确保您的密钥有文件上传权限")
282+
print("2. 应用模式不支持 - 确认您的应用是Workflow模式且支持文件操作")
283+
print("3. 文件格式不支持 - 尝试上传其他格式的文件,如PDF或图片")
284+
print("4. API地址不正确 - 检查BASE_URL是否正确")
285+
286+
# 尝试获取支持的文件类型
287+
try:
288+
if "file_upload" in params:
289+
print("\n支持的文件上传配置:")
290+
pprint(params.get("file_upload", {}))
291+
except:
292+
pass
293+
294+
# 打印完整错误堆栈
295+
import traceback
296+
traceback.print_exc()
202297
return None
203298

204299

@@ -384,11 +479,12 @@ def validate_workflow_app():
384479
# 运行基本示例
385480
example_get_app_info()
386481
example_get_app_parameters()
482+
example_upload_file()
387483
# 如果是Workflow应用,或者用户选择继续运行,则执行以下示例
388484
example_run_workflow_blocking()
389-
example_run_workflow_streaming()
390-
example_get_logs()
391-
485+
# example_run_workflow_streaming()
486+
# example_get_logs()
487+
392488
# 运行文件相关示例和停止任务示例默认不启用
393489
# if is_workflow_app:
394490
# example_workflow_with_file()

pydify/common.py

Lines changed: 108 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -379,29 +379,38 @@ def post_stream(
379379

380380
# 通用方法 - 这些方法在多个子类中重复出现,可以移到基类
381381

382-
def upload_file(self, file_path: str, user: str) -> Dict[str, Any]:
382+
def upload_file(self, file_path: str, user: str, **kwargs) -> Dict[str, Any]:
383383
"""
384384
上传文件到Dify API。
385385
386386
Args:
387387
file_path (str): 要上传的文件路径
388388
user (str): 用户标识
389-
389+
**kwargs: 额外的请求参数,如timeout、max_retries等
390390
Returns:
391391
Dict[str, Any]: 上传文件的响应数据
392392
393393
Raises:
394394
FileNotFoundError: 当文件不存在时
395-
DifyAPIError: 当API请求失败时
395+
DifyAPIError: 当API请求失败时,可能的错误包括:
396+
- 400 no_file_uploaded: 必须提供文件
397+
- 400 too_many_files: 目前只接受一个文件
398+
- 400 unsupported_preview: 该文件不支持预览
399+
- 400 unsupported_estimate: 该文件不支持估算
400+
- 413 file_too_large: 文件太大
401+
- 415 unsupported_file_type: 不支持的扩展名,当前只接受文档类文件
402+
- 503 s3_connection_failed: 无法连接到 S3 服务
403+
- 503 s3_permission_denied: 无权限上传文件到 S3
404+
- 503 s3_file_too_large: 文件超出 S3 大小限制
396405
"""
397406
if not os.path.exists(file_path):
398-
raise FileNotFoundError(f"File not found: {file_path}")
407+
raise FileNotFoundError(f"文件未找到: {file_path}")
399408

400409
with open(file_path, "rb") as file:
401-
return self.upload_file_obj(file, os.path.basename(file_path), user)
410+
return self.upload_file_obj(file, os.path.basename(file_path), user, **kwargs)
402411

403412
def upload_file_obj(
404-
self, file_obj: BinaryIO, filename: str, user: str
413+
self, file_obj: BinaryIO, filename: str, user: str, **kwargs
405414
) -> Dict[str, Any]:
406415
"""
407416
使用文件对象上传文件到Dify API。
@@ -410,25 +419,100 @@ def upload_file_obj(
410419
file_obj (BinaryIO): 文件对象
411420
filename (str): 文件名
412421
user (str): 用户标识
422+
**kwargs: 额外的请求参数,如timeout、max_retries等
413423
414424
Returns:
415-
Dict[str, Any]: 上传文件的响应数据
416-
425+
Dict[str, Any]: 上传文件的响应数据,包含以下字段:
426+
- id (str): 文件ID
427+
- name (str): 文件名
428+
- size (int): 文件大小(字节)
429+
- extension (str): 文件扩展名
430+
- mime_type (str): 文件MIME类型
431+
- created_at (int): 创建时间戳
432+
- created_by (uuid): 创建者ID
433+
例子:
434+
```
435+
{
436+
'created_at': 1742181534,
437+
'created_by': 'df217b97-2203-4fb9-a4b6-ebe74ac6a315',
438+
'extension': 'png',
439+
'id': '88fb21ea-e0b9-48a9-a315-e44e6cfcbcb7',
440+
'mime_type': 'image/png',
441+
'name': 'test_image.png',
442+
'size': 289
443+
}
444+
```
445+
417446
Raises:
418-
DifyAPIError: 当API请求失败时
419-
"""
420-
files = {"file": (filename, file_obj)}
421-
data = {"user": user}
422-
423-
headers = self._get_headers()
424-
# 移除Content-Type,让requests自动设置multipart/form-data
425-
headers.pop("Content-Type", None)
426-
427-
response = self._request(
428-
"POST", "files/upload", headers=headers, files=files, data=data
429-
)
430-
return response.json()
431-
447+
DifyAPIError: 当API请求失败时,可能的错误包括:
448+
- 400 bad_request_key_error: 请求格式错误
449+
- 400 no_file_uploaded: 必须提供文件
450+
- 413 file_too_large: 文件太大
451+
- 415 unsupported_file_type: 不支持的文件类型
452+
"""
453+
# 根据文件扩展名推断MIME类型
454+
import mimetypes
455+
mime_type = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
456+
457+
# 直接使用requests库进行请求,而不是通过_request方法
458+
try:
459+
url = urljoin(self.base_url, "files/upload")
460+
461+
# 打印调试信息
462+
print(f"文件上传请求URL: {url}")
463+
print(f"文件名: {filename}, MIME类型: {mime_type}")
464+
465+
# 准备请求头(不包含Content-Type,让requests自动处理)
466+
headers = {
467+
"Authorization": f"Bearer {self.api_key}",
468+
}
469+
470+
# 准备文件和表单数据
471+
files = {
472+
"file": (filename, file_obj, mime_type)
473+
}
474+
data = {
475+
"user": user
476+
}
477+
478+
# 设置超时参数
479+
timeout = kwargs.pop("timeout", 30)
480+
481+
# 直接发送请求
482+
response = requests.post(
483+
url,
484+
headers=headers,
485+
files=files,
486+
data=data,
487+
timeout=timeout,
488+
)
489+
490+
# 检查响应状态
491+
if not response.ok:
492+
error_msg = f"API request failed: {response.status_code}"
493+
error_data = {}
494+
try:
495+
error_data = response.json()
496+
error_msg = f"{error_msg} - {json.dumps(error_data)}"
497+
except:
498+
if response.text:
499+
error_msg = f"{error_msg} - {response.text[:100]}"
500+
501+
raise DifyAPIError(
502+
error_msg,
503+
status_code=response.status_code,
504+
error_data=error_data
505+
)
506+
507+
return response.json()
508+
509+
except requests.RequestException as e:
510+
raise DifyAPIError(f"文件上传网络错误: {str(e)}")
511+
except Exception as e:
512+
if isinstance(e, DifyAPIError):
513+
raise
514+
raise DifyAPIError(f"文件上传失败: {str(e)}")
515+
432516
def text_to_audio(
433517
self,
434518
user: str,
@@ -665,7 +749,7 @@ def get_parameters(self, raw: bool = False, **kwargs) -> Dict[str, Any]:
665749
```
666750
"""
667751
params = self.get("parameters", **kwargs)
668-
752+
669753
if raw:
670754
return params
671755

@@ -862,7 +946,7 @@ def __init__(self, message: str, status_code: int = None, error_data: Dict = Non
862946

863947
def __str__(self) -> str:
864948
if self.status_code:
865-
return f"[{self.status_code}] {self.message}"
949+
return f"[{self.status_code}] {self.message} {self.error_data}"
866950
return self.message
867951

868952

0 commit comments

Comments
 (0)