union-app-chat-stream 1.0.5 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -156,14 +156,14 @@ class ChatService:
156
156
  def tool_result_event(tool_result: str) -> ChatResponse:
157
157
  return ChatResponse(conversationId=conversation_id, tool_result=_public_tool_result(tool_result))
158
158
 
159
- def heartbeat_event(tool_name: str, elapsed_seconds: float) -> ChatResponse:
159
+ def heartbeat_event(tool_name: str, elapsed_seconds: float, message: str = "") -> ChatResponse:
160
160
  return ChatResponse(
161
161
  conversationId=conversation_id,
162
162
  heartbeat={
163
163
  "type": "tool_call_running",
164
164
  "tool": tool_name,
165
165
  "elapsedSeconds": round(elapsed_seconds, 3),
166
- "message": f"工具 {tool_name} 仍在执行,请继续等待。",
166
+ "message": message or f"工具 {tool_name} 仍在执行,请继续等待。",
167
167
  },
168
168
  )
169
169
 
@@ -241,16 +241,23 @@ class ChatService:
241
241
  logger.info(f"执行工具调用。conversation_id={conversation_id} tool={name} args={_preview(args, 200)}")
242
242
  yield tool_call_event(f"\n[调用工具: {name}({args})]\n")
243
243
 
244
+ retry_notice = {"message": ""}
245
+
246
+ def retry_callback(message: str) -> None:
247
+ retry_notice["message"] = message
248
+
244
249
  tool_context = ToolContext(
245
250
  union_service=self._union_service,
246
251
  rag_service=self._rag,
247
252
  jsessionid=jsessionid,
253
+ retry_callback=retry_callback,
248
254
  )
249
255
  result = yield from self._call_function_with_heartbeats(
250
256
  name,
251
257
  args,
252
258
  tool_context,
253
259
  heartbeat_event,
260
+ retry_notice,
254
261
  )
255
262
  logger.info(f"工具调用完成。conversation_id={conversation_id} tool={name} result_preview={_preview(result, 300)}")
256
263
  yield tool_result_event(result)
@@ -291,6 +298,7 @@ class ChatService:
291
298
  args: str,
292
299
  tool_context: ToolContext,
293
300
  heartbeat_event,
301
+ retry_notice,
294
302
  ) -> Generator[ChatResponse, None, str]:
295
303
  interval = self._tool_heartbeat_interval
296
304
  if interval <= 0:
@@ -304,7 +312,7 @@ class ChatService:
304
312
  try:
305
313
  return future.result(timeout=interval)
306
314
  except FutureTimeoutError:
307
- yield heartbeat_event(name, time.monotonic() - started_at)
315
+ yield heartbeat_event(name, time.monotonic() - started_at, retry_notice.get("message", ""))
308
316
  finally:
309
317
  executor.shutdown(wait=False, cancel_futures=True)
310
318
 
@@ -12,7 +12,6 @@ class UnionService:
12
12
 
13
13
  # 常量定义
14
14
  API_MAX_RETRIES = 10 # API最大重试次数
15
- BIGDATA_API_TIMEOUT = 300
16
15
  BIGDATA_INTERFACE_FULL_LINK = "running_cnt.full_link_monthly"
17
16
  BIGDATA_INTERFACE_BANK_MONTHLY = "running_cnt.bank_monthly"
18
17
 
@@ -72,6 +71,8 @@ class UnionService:
72
71
  jsessionid: str,
73
72
  description: str = "查询联合运维数据",
74
73
  path: str = "",
74
+ timeout: Optional[int] = None,
75
+ retry_callback=None,
75
76
  ) -> Tuple[Optional[Any], str]:
76
77
  """按 tool definition 中配置的 path 和 JSON 参数查询联合运维 API。"""
77
78
  url = self._build_union_url(self._union_base_url, path)
@@ -81,13 +82,17 @@ class UnionService:
81
82
  return None, f"{description}失败: 登录态ID为空"
82
83
 
83
84
  try:
84
- response = common_utils.call_https_api(
85
- url=url,
86
- headers=self._get_union_headers(jsessionid),
87
- json_data=payload,
88
- timeout=self.BIGDATA_API_TIMEOUT,
89
- max_retries=self.API_MAX_RETRIES,
90
- )
85
+ kwargs = {
86
+ "url": url,
87
+ "headers": self._get_union_headers(jsessionid),
88
+ "json_data": payload,
89
+ "max_retries": self.API_MAX_RETRIES,
90
+ }
91
+ if timeout is not None:
92
+ kwargs["timeout"] = timeout
93
+ if retry_callback:
94
+ kwargs["retry_callback"] = retry_callback
95
+ response = common_utils.call_https_api(**kwargs)
91
96
  if not response.get("success", True):
92
97
  raise RuntimeError(response.get("error_msg", "请求失败"))
93
98
  status_code = response.get("status_code")
@@ -1,5 +1,5 @@
1
1
  import requests
2
- from typing import Optional, Dict, Any
2
+ from typing import Optional, Dict, Any, Callable
3
3
  from requests.exceptions import RequestException, Timeout, ConnectionError
4
4
  import json
5
5
  import time
@@ -30,12 +30,13 @@ def call_https_api(
30
30
  data: Optional[Dict[str, Any]] = None,
31
31
  json_data: Optional[Dict[str, Any]] = None,
32
32
  headers: Optional[Dict[str, str]] = None,
33
- timeout: int = 10,
33
+ timeout: int = 10,
34
34
  verify_ssl: bool = False,
35
35
  auth: Optional[tuple] = None,
36
36
  proxies: Optional[Dict[str, Any]] = None,
37
37
  max_retries: int = 0,
38
- retry_delay: float = 1.0
38
+ retry_delay: float = 1.0,
39
+ retry_callback: Optional[Callable[[str], None]] = None,
39
40
 
40
41
  ) -> Dict[str, Any]:
41
42
  default_headers = {
@@ -81,14 +82,20 @@ def call_https_api(
81
82
  logger.info(f"请求成功:{response.status_code},返回值:{response.text}")
82
83
  return result
83
84
  except ConnectionError as e:
84
- logger.error(f"连接失败:{e}")
85
+ error_msg = f"连接失败:{e}"
86
+ logger.error(error_msg)
85
87
  except Timeout as e:
86
- logger.error(f"请求超时:{e}")
88
+ error_msg = f"请求超时:{e}"
89
+ logger.error(error_msg)
87
90
  except RequestException as e:
88
- logger.error(f"请求异常:{e}")
91
+ error_msg = f"请求异常:{e}"
92
+ logger.error(error_msg)
89
93
  except Exception as e:
90
- logger.error(f"未知错误:{e}")
94
+ error_msg = f"未知错误:{e}"
95
+ logger.error(error_msg)
91
96
  if attempt < max_retries:
97
+ if retry_callback:
98
+ retry_callback(f"{error_msg},正在重试(第{attempt + 2}次)")
92
99
  time.sleep(retry_delay)
93
100
  else:
94
101
  break
@@ -21,6 +21,7 @@ class ToolContext:
21
21
  union_service: Optional[Any] = None
22
22
  rag_service: Optional[Any] = None
23
23
  jsessionid: str = ""
24
+ retry_callback: Optional[Callable[[str], None]] = None
24
25
 
25
26
 
26
27
  ToolFunction = Callable[[Dict[str, Any], ToolContext], Dict[str, Any]]
@@ -213,7 +214,12 @@ def _run_backend_tool(
213
214
  if context.union_service is None:
214
215
  raise ValueError("Union服务未初始化")
215
216
  method = getattr(context.union_service, method_name)
216
- data, message = method(payload, context.jsessionid, description=description, path=path)
217
+ kwargs = {"description": description, "path": path}
218
+ if isinstance(backend.get("timeout"), int):
219
+ kwargs["timeout"] = backend["timeout"]
220
+ if context.retry_callback:
221
+ kwargs["retry_callback"] = context.retry_callback
222
+ data, message = method(payload, context.jsessionid, **kwargs)
217
223
  elif service_name == "rag_service":
218
224
  if context.rag_service is None:
219
225
  raise ValueError("知识库检索服务未初始化")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "union-app-chat-stream",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Union operations chat stream Flask application package.",
5
5
  "license": "UNLICENSED",
6
6
  "files": [
@@ -123,6 +123,7 @@ tools:
123
123
  method: "query_union"
124
124
  description: "查询成员机构运行指标"
125
125
  path: "/api/queryBigData"
126
+ timeout: 300
126
127
  params:
127
128
  interfaceName: "runing_cnt.bank"
128
129
  params:
@@ -173,6 +174,7 @@ tools:
173
174
  method: "query_union"
174
175
  description: "查询全链路运行指标"
175
176
  path: "/api/queryBigData"
177
+ timeout: 300
176
178
  params:
177
179
  interfaceName: "runing_cnt.full_link"
178
180
  params: