ftjc2021 commited on
Commit
711e765
·
verified ·
1 Parent(s): 6b07a4b

Upload 7 files

Browse files
Files changed (7) hide show
  1. .gitignore +39 -0
  2. Procfile +1 -0
  3. README.md +52 -11
  4. app.py +101 -0
  5. config.ini +11 -0
  6. dify_api_client.py +424 -0
  7. requirements.txt +6 -0
.gitignore ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ env/
8
+ build/
9
+ develop-eggs/
10
+ dist/
11
+ downloads/
12
+ eggs/
13
+ .eggs/
14
+ lib/
15
+ lib64/
16
+ parts/
17
+ sdist/
18
+ var/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual Environment
24
+ venv/
25
+ ENV/
26
+
27
+ # Logs
28
+ logs/
29
+ *.log
30
+
31
+ # IDE
32
+ .idea/
33
+ .vscode/
34
+ *.swp
35
+ *.swo
36
+
37
+ # OS specific
38
+ .DS_Store
39
+ Thumbs.db
Procfile ADDED
@@ -0,0 +1 @@
 
 
1
+ web: uvicorn app:app --host 0.0.0.0 --port $PORT
README.md CHANGED
@@ -1,11 +1,52 @@
1
- ---
2
- title: Dify1
3
- emoji: 📈
4
- colorFrom: pink
5
- colorTo: purple
6
- sdk: docker
7
- pinned: false
8
- short_description: dify
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dify API 中转服务
2
+
3
+ 这是一个简单的API中转服务,用于将请求转发到Dify API。
4
+
5
+ ## API文档
6
+
7
+ 部署后,可以通过 `/docs` 或 `/redoc` 路径访问API文档。
8
+
9
+ ## 使用方法
10
+
11
+ ### 健康检查
12
+
13
+ ```
14
+ GET /health
15
+ ```
16
+
17
+ 返回服务状态。
18
+
19
+ ### 中转API
20
+
21
+ ```
22
+ POST /dify_api
23
+ ```
24
+
25
+ #### 请求头
26
+
27
+ - `dify_url`: Dify API的基础URL (必需)
28
+ - `dify_key`: Dify API密钥 (必需)
29
+
30
+ #### 请求体
31
+
32
+ ```json
33
+ {
34
+ "query": "您的问题或指令",
35
+ "conversation_id": "可选的会话ID",
36
+ "user": "用户标识,默认为'user'",
37
+ "response_mode": "请求模式,'blocking'或'streaming',默认为'blocking'"
38
+ }
39
+ ```
40
+
41
+ #### 示例请求
42
+
43
+ ```bash
44
+ curl -X POST "https://your-app-url/dify_api" \
45
+ -H "Content-Type: application/json" \
46
+ -H "dify_url: https://api.dify.ai/v1" \
47
+ -H "dify_key: your-dify-api-key" \
48
+ -d '{
49
+ "query": "请告诉我关于人工智能的信息",
50
+ "response_mode": "blocking"
51
+ }'
52
+ ```
app.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ Dify API 中转服务
6
+ 提供REST API端点,转发请求到Dify API
7
+ """
8
+
9
+ import os
10
+ import json
11
+ import logging
12
+ from typing import Dict, Any, Optional
13
+ from fastapi import FastAPI, Header, Request, HTTPException, Response
14
+ from fastapi.middleware.cors import CORSMiddleware
15
+ from pydantic import BaseModel
16
+ import uvicorn
17
+ import configparser
18
+ from dify_api_client import DifyAPIClient, setup_logging
19
+
20
+ # 创建FastAPI应用
21
+ app = FastAPI(
22
+ title="Dify API 中转服务",
23
+ description="提供REST API端点,转发请求到Dify API",
24
+ version="1.0.0"
25
+ )
26
+
27
+ # 添加CORS中间件
28
+ app.add_middleware(
29
+ CORSMiddleware,
30
+ allow_origins=["*"], # 允许所有来源
31
+ allow_credentials=True,
32
+ allow_methods=["*"], # 允许所有方法
33
+ allow_headers=["*"], # 允许所有头
34
+ )
35
+
36
+ # 配置日志
37
+ setup_logging(debug=False)
38
+
39
+ # 请求模型
40
+ class QueryRequest(BaseModel):
41
+ query: str
42
+ conversation_id: Optional[str] = None
43
+ user: Optional[str] = "user"
44
+ response_mode: Optional[str] = "blocking" # 'blocking' 或 'streaming'
45
+
46
+ # 健康检查端点
47
+ @app.get("/health")
48
+ def health_check():
49
+ return {"status": "ok"}
50
+
51
+ # Dify API中转端点
52
+ @app.post("/dify_api")
53
+ async def dify_api(
54
+ request: Request,
55
+ query_request: QueryRequest,
56
+ dify_url: Optional[str] = Header(None),
57
+ dify_key: Optional[str] = Header(None)
58
+ ):
59
+ # 检查必要的头信息
60
+ if not dify_url:
61
+ raise HTTPException(status_code=400, detail="缺少必要的请求头: dify_url")
62
+ if not dify_key:
63
+ raise HTTPException(status_code=400, detail="缺少必要的请求头: dify_key")
64
+
65
+ logging.info(f"接收到API请求,目标URL: {dify_url}")
66
+
67
+ try:
68
+ # 创建Dify客户端
69
+ client = DifyAPIClient(api_key=dify_key, base_url=dify_url)
70
+
71
+ # 根据请求模式处理
72
+ if query_request.response_mode == "streaming":
73
+ # 对于流式响应,我们需要使用StreamingResponse
74
+ # 但由于原始客户端不支持异步流式返回,这里我们先返回完整响应
75
+ # 在实际生产环境中,应该重构客户端代码以支持异步流式响应
76
+ response_text = client.send_request_streaming(
77
+ content=query_request.query,
78
+ conversation_id=query_request.conversation_id,
79
+ user=query_request.user
80
+ )
81
+ return {"answer": response_text}
82
+ else:
83
+ # 阻塞模式
84
+ response_data = client.send_request_blocking(
85
+ content=query_request.query,
86
+ conversation_id=query_request.conversation_id,
87
+ user=query_request.user
88
+ )
89
+ return response_data
90
+
91
+ except Exception as e:
92
+ logging.error(f"处理请求时出错: {str(e)}", exc_info=True)
93
+ raise HTTPException(status_code=500, detail=f"处理请求时出错: {str(e)}")
94
+
95
+ # 主函数
96
+ def main():
97
+ # 在本地运行时使用
98
+ uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)
99
+
100
+ if __name__ == "__main__":
101
+ main()
config.ini ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [API]
2
+ # 替换为您的Dify API密钥
3
+ api_key = app-besh0Rt34xVTMiKWoZeKD1HN
4
+ # Dify API基础URL
5
+ base_url = http://114.132.220.127:8088/v1
6
+
7
+ [SETTINGS]
8
+ # 默认请求模式:streaming(流式) 或 blocking(阻塞)
9
+ default_mode = streaming
10
+ # 默认用户标识
11
+ default_user = user
dify_api_client.py ADDED
@@ -0,0 +1,424 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ Dify API 客户端
6
+ 从本地txt文件读取内容,发送到Dify API
7
+ 支持阻塞模式和流式模式
8
+ """
9
+
10
+ import os
11
+ import sys
12
+ import json
13
+ import argparse
14
+ import requests
15
+ import sseclient
16
+ import logging
17
+ import configparser
18
+ from typing import Dict, Any, Optional, Union
19
+ from datetime import datetime
20
+
21
+
22
+ # 配置日志
23
+ def setup_logging(debug: bool = False) -> None:
24
+ """
25
+ 设置日志记录
26
+
27
+ Args:
28
+ debug: 是否启用调试模式
29
+ """
30
+ log_level = logging.DEBUG if debug else logging.INFO
31
+ log_format = '%(asctime)s - %(levelname)s - %(message)s'
32
+
33
+ # 创建logs目录(如果不存在)
34
+ os.makedirs('logs', exist_ok=True)
35
+
36
+ # 设置文件日志
37
+ log_file = f'logs/dify_api_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log'
38
+
39
+ # 配置日志记录器
40
+ logging.basicConfig(
41
+ level=log_level,
42
+ format=log_format,
43
+ handlers=[
44
+ logging.FileHandler(log_file, encoding='utf-8'),
45
+ logging.StreamHandler()
46
+ ]
47
+ )
48
+
49
+ # 加载配置文件
50
+ def load_config(config_path='config.ini'):
51
+ """
52
+ 从配置文件加载设置
53
+
54
+ Args:
55
+ config_path: 配置文件路径
56
+
57
+ Returns:
58
+ 配置对象
59
+ """
60
+ config = configparser.ConfigParser()
61
+
62
+ # 检查配置文件是否存在
63
+ if os.path.exists(config_path):
64
+ config.read(config_path, encoding='utf-8')
65
+ logging.debug(f"已加载配置文件: {config_path}")
66
+ else:
67
+ logging.warning(f"配置文件不存在: {config_path}, 将使用默认值或命令行参数")
68
+
69
+ return config
70
+
71
+
72
+ class DifyAPIClient:
73
+ """Dify API 客户端类"""
74
+
75
+ DEFAULT_BASE_URL = "http://114.132.220.127:8088/v1"
76
+
77
+ def __init__(self, api_key: str, base_url: str = None):
78
+ """
79
+ 初始化Dify API客户端
80
+
81
+ Args:
82
+ api_key: API密钥
83
+ base_url: API基础URL,如果未提供则使用默认值
84
+ """
85
+ self.api_key = api_key
86
+ self.base_url = base_url or self.DEFAULT_BASE_URL
87
+ self.headers = {
88
+ "Authorization": f"Bearer {api_key}",
89
+ "Content-Type": "application/json"
90
+ }
91
+ logging.debug(f"初始化Dify客户端,BASE_URL: {self.base_url}")
92
+
93
+ def read_content_from_file(self, file_path: str) -> str:
94
+ """
95
+ 从文件中读取内容
96
+
97
+ Args:
98
+ file_path: 文件路径
99
+
100
+ Returns:
101
+ 文件内容
102
+ """
103
+ try:
104
+ logging.info(f"正在读取文件: {file_path}")
105
+ with open(file_path, 'r', encoding='utf-8') as file:
106
+ content = file.read().strip()
107
+ logging.debug(f"成功读取文件,内容长度: {len(content)} 字符")
108
+ return content
109
+ except FileNotFoundError:
110
+ logging.error(f"文件不存在: {file_path}")
111
+ print(f"错误: 文件 '{file_path}' 不存在")
112
+ sys.exit(1)
113
+ except PermissionError:
114
+ logging.error(f"没有权限访问文件: {file_path}")
115
+ print(f"错误: 没有权限读取文件 '{file_path}'")
116
+ sys.exit(1)
117
+ except Exception as e:
118
+ logging.error(f"读取文件时出错: {str(e)}", exc_info=True)
119
+ print(f"读取文件时出错: {str(e)}")
120
+ sys.exit(1)
121
+
122
+ def send_request_blocking(self, content: str, conversation_id: str = None, user: str = "user") -> Dict[str, Any]:
123
+ """
124
+ 发送阻塞模式请求
125
+
126
+ Args:
127
+ content: 请求内容
128
+ conversation_id: 会话ID
129
+ user: 用户标识
130
+
131
+ Returns:
132
+ API响应
133
+ """
134
+ url = f"{self.base_url}/chat-messages"
135
+
136
+ payload = {
137
+ "query": content,
138
+ "inputs": {},
139
+ "response_mode": "blocking",
140
+ "user": user
141
+ }
142
+
143
+ if conversation_id:
144
+ payload["conversation_id"] = conversation_id
145
+
146
+ logging.info(f"发送阻塞模式请求,用户: {user}")
147
+ if conversation_id:
148
+ logging.info(f"会话ID: {conversation_id}")
149
+
150
+ logging.debug(f"请求URL: {url}")
151
+ logging.debug(f"请求负载: {json.dumps(payload, ensure_ascii=False)}")
152
+
153
+ try:
154
+ response = requests.post(url, headers=self.headers, json=payload)
155
+ response.raise_for_status()
156
+ response_data = response.json()
157
+ logging.info("API请求成功")
158
+ logging.debug(f"API响应: {json.dumps(response_data, ensure_ascii=False)}")
159
+ return response_data
160
+ except requests.exceptions.HTTPError as e:
161
+ logging.error(f"HTTP错误: {e}")
162
+ if hasattr(response, 'text'):
163
+ logging.error(f"API响应内容: {response.text}")
164
+ print(f"HTTP错误: {e}")
165
+ if hasattr(response, 'text') and response.text:
166
+ print(f"API响应: {response.text}")
167
+ sys.exit(1)
168
+ except requests.exceptions.ConnectionError:
169
+ logging.error("连接错误: 无法连接到API服务器")
170
+ print("连接错误: 无法连接到API服务器")
171
+ sys.exit(1)
172
+ except requests.exceptions.Timeout:
173
+ logging.error("超时错误: API请求超时")
174
+ print("超时错误: API请求超时")
175
+ sys.exit(1)
176
+ except requests.exceptions.RequestException as e:
177
+ logging.error(f"请求错误: {str(e)}", exc_info=True)
178
+ print(f"请求错误: {e}")
179
+ sys.exit(1)
180
+ except json.JSONDecodeError:
181
+ logging.error("解析错误: API返回的响应不是有效的JSON格式")
182
+ if hasattr(response, 'text'):
183
+ logging.error(f"API响应内容: {response.text}")
184
+ print("解析错误: API返回的响应不是有效的JSON格式")
185
+ if hasattr(response, 'text'):
186
+ print(f"API响应: {response.text}")
187
+ sys.exit(1)
188
+ except Exception as e:
189
+ logging.error(f"未知错误: {str(e)}", exc_info=True)
190
+ print(f"发送请求时发生未知错误: {str(e)}")
191
+ sys.exit(1)
192
+
193
+ def send_request_streaming(self, content: str, conversation_id: str = None, user: str = "user") -> str:
194
+ """
195
+ 发送流式模式请求
196
+
197
+ Args:
198
+ content: 请求内容
199
+ conversation_id: 会话ID
200
+ user: 用户标识
201
+
202
+ Returns:
203
+ 完整的响应文本
204
+ """
205
+ url = f"{self.base_url}/chat-messages"
206
+
207
+ payload = {
208
+ "query": content,
209
+ "inputs": {},
210
+ "response_mode": "streaming",
211
+ "user": user
212
+ }
213
+
214
+ if conversation_id:
215
+ payload["conversation_id"] = conversation_id
216
+
217
+ logging.info(f"发送流式模式请求,用户: {user}")
218
+ if conversation_id:
219
+ logging.info(f"会话ID: {conversation_id}")
220
+
221
+ logging.debug(f"请求URL: {url}")
222
+ logging.debug(f"请求负载: {json.dumps(payload, ensure_ascii=False)}")
223
+
224
+ try:
225
+ # 使用流式请求
226
+ response = requests.post(url, headers=self.headers, json=payload, stream=True)
227
+ response.raise_for_status()
228
+
229
+ # 创建SSE客户端
230
+ client = sseclient.SSEClient(response)
231
+
232
+ # 初始化变量,用于累积回答
233
+ full_answer = ""
234
+ conversation_id = None
235
+
236
+ # 处理每个事件
237
+ for event in client.events():
238
+ try:
239
+ # 解析事件数据
240
+ data = json.loads(event.data)
241
+ event_type = data.get("event")
242
+
243
+ # 记录会话ID(如果存在且我们尚未捕获它)
244
+ if not conversation_id and "conversation_id" in data:
245
+ conversation_id = data["conversation_id"]
246
+ logging.debug(f"获取到会话ID: {conversation_id}")
247
+
248
+ if event_type == "message":
249
+ # 提取回答文本并打印
250
+ answer_chunk = data.get("answer", "")
251
+ full_answer += answer_chunk
252
+ print(answer_chunk, end="", flush=True)
253
+ logging.debug(f"接收到消息事件: {answer_chunk}")
254
+
255
+ elif event_type == "message_end":
256
+ # 消息结束事件
257
+ print("\n\n--- 消息结束 ---")
258
+ logging.info("消息流结束")
259
+
260
+ # 输出元数据
261
+ if "metadata" in data:
262
+ usage = data["metadata"].get("usage", {})
263
+ if usage:
264
+ print(f"\n使用情况统计:")
265
+ print(f" 总Token数: {usage.get('total_tokens', 'N/A')}")
266
+ print(f" 提示Token数: {usage.get('prompt_tokens', 'N/A')}")
267
+ print(f" 补全Token数: {usage.get('completion_tokens', 'N/A')}")
268
+ print(f" 总费用: {usage.get('total_price', 'N/A')} {usage.get('currency', 'USD')}")
269
+
270
+ logging.info(f"使用统计 - 总Token数: {usage.get('total_tokens', 'N/A')}, "
271
+ f"总费用: {usage.get('total_price', 'N/A')} {usage.get('currency', 'USD')}")
272
+
273
+ elif event_type == "workflow_started":
274
+ logging.info("工作流开始")
275
+
276
+ elif event_type == "node_started":
277
+ node_id = data.get("data", {}).get("node_id", "unknown")
278
+ logging.debug(f"节点开始执行: {node_id}")
279
+
280
+ elif event_type == "node_finished":
281
+ node_id = data.get("data", {}).get("node_id", "unknown")
282
+ status = data.get("data", {}).get("status", "unknown")
283
+ logging.debug(f"节点执行完成: {node_id}, 状态: {status}")
284
+
285
+ elif event_type == "workflow_finished":
286
+ status = data.get("data", {}).get("status", "unknown")
287
+ logging.info(f"工作流完成,状态: {status}")
288
+
289
+ elif event_type == "error":
290
+ # 错误事件
291
+ error_message = data.get("message", "未知错误")
292
+ logging.error(f"API错误: {error_message}")
293
+ print(f"\n错误: {error_message}")
294
+ break
295
+
296
+ except json.JSONDecodeError:
297
+ logging.error(f"解析错误: 无法解析事件数据: {event.data}")
298
+ print(f"\n解析错误: 无法解析事件数据: {event.data}")
299
+ except Exception as e:
300
+ logging.error(f"处理事件时出错: {str(e)}", exc_info=True)
301
+ print(f"\n处理事件时出错: {str(e)}")
302
+
303
+ # 如果获取到了会话ID,显示给用户
304
+ if conversation_id:
305
+ print(f"\n会话ID: {conversation_id}")
306
+
307
+ return full_answer
308
+
309
+ except requests.exceptions.HTTPError as e:
310
+ logging.error(f"HTTP错误: {e}")
311
+ if hasattr(response, 'text'):
312
+ logging.error(f"API响应内容: {response.text}")
313
+ print(f"HTTP错误: {e}")
314
+ if hasattr(response, 'text') and response.text:
315
+ print(f"API响应: {response.text}")
316
+ sys.exit(1)
317
+ except requests.exceptions.ConnectionError:
318
+ logging.error("连接错误: 无法连接到API服务器")
319
+ print("连接错误: 无法连接到API服务器")
320
+ sys.exit(1)
321
+ except requests.exceptions.Timeout:
322
+ logging.error("超时错误: API请求超时")
323
+ print("超时错误: API请求超时")
324
+ sys.exit(1)
325
+ except requests.exceptions.RequestException as e:
326
+ logging.error(f"请求错误: {str(e)}", exc_info=True)
327
+ print(f"请求错误: {e}")
328
+ sys.exit(1)
329
+ except Exception as e:
330
+ logging.error(f"处理流式响应时出错: {str(e)}", exc_info=True)
331
+ print(f"处理流式响应时出错: {str(e)}")
332
+ sys.exit(1)
333
+
334
+
335
+ def main():
336
+ """主函数"""
337
+ # 加载配置文件
338
+ config = load_config()
339
+
340
+ # 从配置文件获取默认值
341
+ default_api_key = config.get('API', 'api_key', fallback=None)
342
+ default_mode = config.get('SETTINGS', 'default_mode', fallback='streaming')
343
+ default_user = config.get('SETTINGS', 'default_user', fallback='user')
344
+ default_base_url = config.get('API', 'base_url', fallback=None)
345
+
346
+ # 设置默认文件
347
+ default_file = 'example_query.txt'
348
+
349
+ parser = argparse.ArgumentParser(description='Dify API 客户端 - 从本地txt文件读取内容发送到API')
350
+ parser.add_argument('file_path', nargs='?', default=default_file,
351
+ help=f'本地txt文件路径 (默认: {default_file})')
352
+ parser.add_argument('--api-key', help='Dify API密钥(如果未指定,将从config.ini读取)')
353
+ parser.add_argument('--base-url', help='Dify API基础URL(如果未指定,将从config.ini读取)')
354
+ parser.add_argument('--mode', choices=['blocking', 'streaming'], default=default_mode,
355
+ help=f'请求模式: blocking(阻塞) 或 streaming(流式), 默认为 {default_mode}')
356
+ parser.add_argument('--conversation-id', help='会话ID,用于继续已有对话')
357
+ parser.add_argument('--user', default=default_user, help=f'用户标识, 默认为 {default_user}')
358
+ parser.add_argument('--debug', action='store_true', help='启用调试日志')
359
+
360
+ args = parser.parse_args()
361
+
362
+ # 设置日志
363
+ setup_logging(args.debug)
364
+
365
+ logging.info("Dify API客户端启动")
366
+ logging.info(f"模式: {args.mode}")
367
+
368
+ # 确定API密钥
369
+ api_key = args.api_key or default_api_key
370
+
371
+ if api_key is None or api_key == 'your_api_key_here':
372
+ logging.error("未提供API密钥,请在命令行参数中使用--api-key指定,或在config.ini中配置")
373
+ print("错误: 未提供API密钥,请使用--api-key参数提供,或在config.ini中配置")
374
+ sys.exit(1)
375
+
376
+ # 确定API基础URL
377
+ base_url = args.base_url or default_base_url
378
+
379
+ try:
380
+ # 创建客户端实例
381
+ client = DifyAPIClient(api_key, base_url)
382
+
383
+ # 从文件读取内容
384
+ content = client.read_content_from_file(args.file_path)
385
+
386
+ if not content:
387
+ logging.error("文件内容为空")
388
+ print("错误: 文件内容为空")
389
+ sys.exit(1)
390
+
391
+ print(f"正在使用 {args.mode} 模式发送请求...")
392
+
393
+ # 根据模式发送请求
394
+ if args.mode == 'blocking':
395
+ response = client.send_request_blocking(content, args.conversation_id, args.user)
396
+ print("\n响应:")
397
+ print(f"{response['answer']}")
398
+
399
+ if 'metadata' in response and 'usage' in response['metadata']:
400
+ usage = response['metadata']['usage']
401
+ print(f"\n使用情况统计:")
402
+ print(f" 总Token数: {usage.get('total_tokens', 'N/A')}")
403
+ print(f" 提示Token数: {usage.get('prompt_tokens', 'N/A')}")
404
+ print(f" 补全Token数: {usage.get('completion_tokens', 'N/A')}")
405
+ print(f" 总费用: {usage.get('total_price', 'N/A')} {usage.get('currency', 'USD')}")
406
+
407
+ print(f"\n会话ID: {response.get('conversation_id', 'N/A')}")
408
+ else:
409
+ client.send_request_streaming(content, args.conversation_id, args.user)
410
+
411
+ logging.info("请求执行完毕")
412
+
413
+ except KeyboardInterrupt:
414
+ logging.info("用户中断操作")
415
+ print("\n操作被用户中断")
416
+ sys.exit(0)
417
+ except Exception as e:
418
+ logging.error(f"未捕获的异常: {str(e)}", exc_info=True)
419
+ print(f"发生未知错误: {str(e)}")
420
+ sys.exit(1)
421
+
422
+
423
+ if __name__ == "__main__":
424
+ main()
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ requests>=2.28.0
2
+ sseclient-py>=1.7.2
3
+ fastapi>=0.95.0
4
+ uvicorn>=0.21.0
5
+ python-multipart>=0.0.6
6
+ pydantic>=1.10.7