JC321 commited on
Commit
d3bf78b
·
verified ·
1 Parent(s): 5c7ef4d

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -44
app.py CHANGED
@@ -213,36 +213,45 @@ def chatbot_response(message, history):
213
 
214
  for tool_call in choice.message.tool_calls:
215
  tool_name = tool_call.function.name
216
- tool_args = json.loads(tool_call.function.arguments)
 
 
 
217
 
218
  # 调用 MCP 工具
219
  tool_result = call_mcp_tool(tool_name, tool_args)
220
 
221
- # 限制返回结果大小
222
- result_str = json.dumps(tool_result, ensure_ascii=False)
223
-
224
- if len(result_str) > MAX_TOOL_RESULT_CHARS:
225
- if isinstance(tool_result, dict) and "text" in tool_result:
226
- truncated_text = truncate_text(tool_result["text"], MAX_TOOL_RESULT_CHARS - 50)
227
- tool_result_truncated = {"text": truncated_text, "_truncated": True}
228
- elif isinstance(tool_result, dict):
229
- truncated = {}
230
- char_count = 0
231
- for k, v in list(tool_result.items())[:8]: # 保留前8个字段
232
- v_str = str(v)[:300] # 每个值最多300字符
233
- truncated[k] = v_str
234
- char_count += len(k) + len(v_str)
235
- if char_count > MAX_TOOL_RESULT_CHARS:
236
- break
237
- tool_result_truncated = {**truncated, "_truncated": True}
238
- else:
239
- tool_result_truncated = {"preview": truncate_text(result_str, MAX_TOOL_RESULT_CHARS), "_truncated": True}
240
- result_for_llm = json.dumps(tool_result_truncated, ensure_ascii=False)
241
  else:
242
- result_for_llm = result_str
243
-
244
- # 记录工具调用(包含完整结果用于UI显示)
245
- tool_calls_log.append({"name": tool_name, "arguments": tool_args, "result": tool_result})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
 
247
  messages.append({
248
  "role": "tool",
@@ -269,13 +278,21 @@ def chatbot_response(message, history):
269
  """.format(len(tool_calls_log))
270
 
271
  for idx, tool_call in enumerate(tool_calls_log):
 
 
 
 
 
 
 
 
272
  # 使用原生 HTML5 details/summary 标签(不需要 JavaScript)
273
  response_prefix += f"""<details style='margin: 8px 0; border: 1px solid #ddd; border-radius: 6px; overflow: hidden;'>
274
  <summary style='background: #fff; padding: 10px; cursor: pointer; user-select: none; list-style: none;'>
275
  <div style='display: flex; justify-content: space-between; align-items: center;'>
276
  <div style='flex: 1;'>
277
- <strong style='color: #2c5aa0;'>📌 {idx+1}. {tool_call['name']}</strong>
278
- <div style='font-size: 0.85em; color: #666; margin-top: 4px;'>📥 Input: <code style='background: #f5f5f5; padding: 2px 6px; border-radius: 3px;'>{json.dumps(tool_call['arguments'], ensure_ascii=False)}</code></div>
279
  </div>
280
  <span style='font-size: 1.2em; color: #999; margin-left: 10px;'>▶</span>
281
  </div>
@@ -283,7 +300,7 @@ def chatbot_response(message, history):
283
  <div style='background: #f9f9f9; padding: 12px; border-top: 1px solid #eee;'>
284
  <div style='font-size: 0.9em; color: #333;'>
285
  <strong>📤 Output:</strong>
286
- <pre style='background: #fff; padding: 10px; border-radius: 4px; overflow-x: auto; margin-top: 6px; font-size: 0.85em; border: 1px solid #e0e0e0; max-height: 400px; white-space: pre-wrap;'>{json.dumps(tool_call.get('result', {}), ensure_ascii=False, indent=2)[:1500]}{'...' if len(json.dumps(tool_call.get('result', {}), ensure_ascii=False)) > 1500 else ''}</pre>
287
  </div>
288
  </div>
289
  </details>
@@ -299,26 +316,38 @@ def chatbot_response(message, history):
299
  # 流式输出最终答案
300
  yield response_prefix
301
 
302
- # 如果已经有最终答案,直接流式输出
303
  if final_response_content:
304
  # 已经从循环中获得了最终答案,直接输出
305
  yield response_prefix + final_response_content
306
  else:
307
  # 如果循环结束但没有最终答案(达到最大迭代次数),需要再调用一次让模型总结
308
- stream = client.chat.completions.create(
309
- model="Qwen/Qwen3-32B:groq",
310
- messages=messages,
311
- tools=None, # 不再允许调用工具
312
- max_tokens=MAX_OUTPUT_TOKENS,
313
- temperature=0.5,
314
- stream=True
315
- )
316
-
317
- accumulated_text = ""
318
- for chunk in stream:
319
- if chunk.choices and len(chunk.choices) > 0 and chunk.choices[0].delta.content:
320
- accumulated_text += chunk.choices[0].delta.content
321
- yield response_prefix + accumulated_text
 
 
 
 
 
 
 
 
 
 
 
 
322
 
323
  except Exception as e:
324
  import traceback
@@ -347,7 +376,6 @@ with gr.Blocks(title="Financial AI Assistant") as demo:
347
  # 启动应用
348
  if __name__ == "__main__":
349
  import sys
350
- import asyncio
351
 
352
  # 修复 asyncio 事件循环问题
353
  if sys.platform == 'linux':
 
213
 
214
  for tool_call in choice.message.tool_calls:
215
  tool_name = tool_call.function.name
216
+ try:
217
+ tool_args = json.loads(tool_call.function.arguments)
218
+ except json.JSONDecodeError:
219
+ tool_args = {}
220
 
221
  # 调用 MCP 工具
222
  tool_result = call_mcp_tool(tool_name, tool_args)
223
 
224
+ # 检查错误
225
+ if isinstance(tool_result, dict) and "error" in tool_result:
226
+ # 工具调用失败,记录错误
227
+ tool_calls_log.append({"name": tool_name, "arguments": tool_args, "result": tool_result, "error": True})
228
+ result_for_llm = json.dumps({"error": tool_result.get("error", "Unknown error")}, ensure_ascii=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  else:
230
+ # 限制返回结果大小
231
+ result_str = json.dumps(tool_result, ensure_ascii=False)
232
+
233
+ if len(result_str) > MAX_TOOL_RESULT_CHARS:
234
+ if isinstance(tool_result, dict) and "text" in tool_result:
235
+ truncated_text = truncate_text(tool_result["text"], MAX_TOOL_RESULT_CHARS - 50)
236
+ tool_result_truncated = {"text": truncated_text, "_truncated": True}
237
+ elif isinstance(tool_result, dict):
238
+ truncated = {}
239
+ char_count = 0
240
+ for k, v in list(tool_result.items())[:8]: # 保留前8个字段
241
+ v_str = str(v)[:300] # 每个值最多300字符
242
+ truncated[k] = v_str
243
+ char_count += len(k) + len(v_str)
244
+ if char_count > MAX_TOOL_RESULT_CHARS:
245
+ break
246
+ tool_result_truncated = {**truncated, "_truncated": True}
247
+ else:
248
+ tool_result_truncated = {"preview": truncate_text(result_str, MAX_TOOL_RESULT_CHARS), "_truncated": True}
249
+ result_for_llm = json.dumps(tool_result_truncated, ensure_ascii=False)
250
+ else:
251
+ result_for_llm = result_str
252
+
253
+ # 记录成功的工具调用
254
+ tool_calls_log.append({"name": tool_name, "arguments": tool_args, "result": tool_result})
255
 
256
  messages.append({
257
  "role": "tool",
 
278
  """.format(len(tool_calls_log))
279
 
280
  for idx, tool_call in enumerate(tool_calls_log):
281
+ # 预先计算 JSON 字符串,避免重复调用
282
+ args_json = json.dumps(tool_call['arguments'], ensure_ascii=False)
283
+ result_json = json.dumps(tool_call.get('result', {}), ensure_ascii=False, indent=2)
284
+ result_preview = result_json[:1500] + ('...' if len(result_json) > 1500 else '')
285
+
286
+ # 显示错误状态
287
+ error_indicator = " ❌ Error" if tool_call.get('error') else ""
288
+
289
  # 使用原生 HTML5 details/summary 标签(不需要 JavaScript)
290
  response_prefix += f"""<details style='margin: 8px 0; border: 1px solid #ddd; border-radius: 6px; overflow: hidden;'>
291
  <summary style='background: #fff; padding: 10px; cursor: pointer; user-select: none; list-style: none;'>
292
  <div style='display: flex; justify-content: space-between; align-items: center;'>
293
  <div style='flex: 1;'>
294
+ <strong style='color: #2c5aa0;'>📌 {idx+1}. {tool_call['name']}{error_indicator}</strong>
295
+ <div style='font-size: 0.85em; color: #666; margin-top: 4px;'>📥 Input: <code style='background: #f5f5f5; padding: 2px 6px; border-radius: 3px;'>{args_json}</code></div>
296
  </div>
297
  <span style='font-size: 1.2em; color: #999; margin-left: 10px;'>▶</span>
298
  </div>
 
300
  <div style='background: #f9f9f9; padding: 12px; border-top: 1px solid #eee;'>
301
  <div style='font-size: 0.9em; color: #333;'>
302
  <strong>📤 Output:</strong>
303
+ <pre style='background: #fff; padding: 10px; border-radius: 4px; overflow-x: auto; margin-top: 6px; font-size: 0.85em; border: 1px solid #e0e0e0; max-height: 400px; white-space: pre-wrap;'>{result_preview}</pre>
304
  </div>
305
  </div>
306
  </details>
 
316
  # 流式输出最终答案
317
  yield response_prefix
318
 
319
+ # 如果已经有最终答案,直接输出
320
  if final_response_content:
321
  # 已经从循环中获得了最终答案,直接输出
322
  yield response_prefix + final_response_content
323
  else:
324
  # 如果循环结束但没有最终答案(达到最大迭代次数),需要再调用一次让模型总结
325
+ try:
326
+ stream = client.chat.completions.create(
327
+ model="Qwen/Qwen3-32B:groq",
328
+ messages=messages,
329
+ tools=None, # 不再允许调用工具
330
+ max_tokens=MAX_OUTPUT_TOKENS,
331
+ temperature=0.5,
332
+ stream=True
333
+ )
334
+
335
+ accumulated_text = ""
336
+ for chunk in stream:
337
+ if chunk.choices and len(chunk.choices) > 0 and chunk.choices[0].delta.content:
338
+ accumulated_text += chunk.choices[0].delta.content
339
+ yield response_prefix + accumulated_text
340
+ except Exception as stream_error:
341
+ # 流式输出失败,尝试非流式
342
+ final_resp = client.chat.completions.create(
343
+ model="Qwen/Qwen3-32B:groq",
344
+ messages=messages,
345
+ tools=None,
346
+ max_tokens=MAX_OUTPUT_TOKENS,
347
+ temperature=0.5,
348
+ stream=False
349
+ )
350
+ yield response_prefix + final_resp.choices[0].message.content
351
 
352
  except Exception as e:
353
  import traceback
 
376
  # 启动应用
377
  if __name__ == "__main__":
378
  import sys
 
379
 
380
  # 修复 asyncio 事件循环问题
381
  if sys.platform == 'linux':