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
|
-
|
|
85
|
-
url
|
|
86
|
-
headers
|
|
87
|
-
json_data
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
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
|
-
|
|
85
|
+
error_msg = f"连接失败:{e}"
|
|
86
|
+
logger.error(error_msg)
|
|
85
87
|
except Timeout as e:
|
|
86
|
-
|
|
88
|
+
error_msg = f"请求超时:{e}"
|
|
89
|
+
logger.error(error_msg)
|
|
87
90
|
except RequestException as e:
|
|
88
|
-
|
|
91
|
+
error_msg = f"请求异常:{e}"
|
|
92
|
+
logger.error(error_msg)
|
|
89
93
|
except Exception as e:
|
|
90
|
-
|
|
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
|
-
|
|
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
|
@@ -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:
|