coze_lab 0.1.26 → 0.1.27
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.
- package/index.js +35 -12
- package/package.json +1 -1
- package/scripts/claude-code/cozeloop_hook.py +54 -12
- package/scripts/codex/cozeloop_hook.py +59 -16
- package/scripts/openclaw/package.json +1 -1
package/index.js
CHANGED
|
@@ -103,6 +103,18 @@ function successBox(lines) {
|
|
|
103
103
|
// ─── 2. Script loading (scripts live as real files under ./scripts) ───────────
|
|
104
104
|
const SCRIPTS_DIR = require('path').join(__dirname, 'scripts');
|
|
105
105
|
|
|
106
|
+
// OpenClaw 插件版本:来自 scripts/openclaw/package.json。写进 openclaw.json 的 pcfg,
|
|
107
|
+
// 参与 isOpenClawAlreadyInjected 幂等比对——插件代码升级(bump 该 version)后即使
|
|
108
|
+
// token/endpoint/workspace 不变也会被判定为“需更新”,强制重写插件文件 + npm install +
|
|
109
|
+
// gateway restart,避免旧插件 dist 滞留在云端 pluginDir 里不生效。
|
|
110
|
+
const OPENCLAW_PLUGIN_VERSION = (() => {
|
|
111
|
+
try {
|
|
112
|
+
return require('./scripts/openclaw/package.json').version || '';
|
|
113
|
+
} catch {
|
|
114
|
+
return '';
|
|
115
|
+
}
|
|
116
|
+
})();
|
|
117
|
+
|
|
106
118
|
// Read a single script file (Python hook / refresh) as a UTF-8 string.
|
|
107
119
|
function readScript(relPath) {
|
|
108
120
|
return require('fs').readFileSync(require('path').join(SCRIPTS_DIR, relPath), 'utf8');
|
|
@@ -4378,28 +4390,34 @@ function checkPython() {
|
|
|
4378
4390
|
return pythonCmd;
|
|
4379
4391
|
}
|
|
4380
4392
|
|
|
4381
|
-
// Verify the `cozeloop` Python SDK is importable;
|
|
4382
|
-
// Claude Code / Codex hooks `import cozeloop` at runtime
|
|
4383
|
-
//
|
|
4393
|
+
// Verify the `cozeloop` Python SDK is importable AND new enough; install/upgrade otherwise.
|
|
4394
|
+
// Claude Code / Codex hooks `import cozeloop` at runtime and call set_finish_time(0.1.25+)。
|
|
4395
|
+
// 旧版(<=0.1.24)能 import 但缺该方法,会让 hook 上报整条 trace 失败,所以这里用能力探测
|
|
4396
|
+
// (hasattr set_finish_time)而非单纯 import,并在不达标时升级到带下限的版本。
|
|
4397
|
+
// 注意:这是安装阶段的 python,可能不是跑 hook 的那个 python(已知坑),所以它只是双保险——
|
|
4398
|
+
// 真正的运行时拦截在 hook 脚本的 _ensure_cozeloop_sdk 里。
|
|
4399
|
+
const COZELOOP_MIN_SPEC = 'cozeloop>=0.1.28';
|
|
4400
|
+
// 探测脚本:import 成功且具备 set_finish_time 能力 → exit 0;否则非 0。
|
|
4401
|
+
const COZELOOP_CAPABLE_PROBE = `import cozeloop,sys; sys.exit(0 if hasattr(cozeloop.Span,'set_finish_time') else 3)`;
|
|
4384
4402
|
function checkCozeloopSdk(pythonCmd) {
|
|
4385
4403
|
try {
|
|
4386
|
-
execSync(`${pythonCmd} -c "
|
|
4404
|
+
execSync(`${pythonCmd} -c "${COZELOOP_CAPABLE_PROBE}"`, { stdio: 'pipe' });
|
|
4387
4405
|
ok('cozeloop SDK — OK');
|
|
4388
4406
|
return;
|
|
4389
|
-
} catch { /*
|
|
4407
|
+
} catch { /* 未装或版本过旧 — 下面安装/升级 */ }
|
|
4390
4408
|
|
|
4391
|
-
info(
|
|
4409
|
+
info(`cozeloop SDK 不可用或版本过旧,正在安装/升级 (pip install -U '${COZELOOP_MIN_SPEC}')...`);
|
|
4392
4410
|
try {
|
|
4393
|
-
execSync(`${pythonCmd} -m pip install --quiet --upgrade
|
|
4394
|
-
// Confirm it imports now
|
|
4395
|
-
execSync(`${pythonCmd} -c "
|
|
4396
|
-
ok('cozeloop SDK
|
|
4411
|
+
execSync(`${pythonCmd} -m pip install --quiet --upgrade '${COZELOOP_MIN_SPEC}'`, { stdio: 'pipe' });
|
|
4412
|
+
// Confirm it imports and is capable now
|
|
4413
|
+
execSync(`${pythonCmd} -c "${COZELOOP_CAPABLE_PROBE}"`, { stdio: 'pipe' });
|
|
4414
|
+
ok('cozeloop SDK 安装/升级成功');
|
|
4397
4415
|
} catch (e) {
|
|
4398
4416
|
warnBox([
|
|
4399
|
-
'⚠ WARNING:
|
|
4417
|
+
'⚠ WARNING: 无法自动安装/升级 cozeloop SDK 到所需版本',
|
|
4400
4418
|
'',
|
|
4401
4419
|
'请手动安装后再使用,否则 hook 触发时 trace 上报会失败:',
|
|
4402
|
-
` ${pythonCmd} -m pip install
|
|
4420
|
+
` ${pythonCmd} -m pip install -U '${COZELOOP_MIN_SPEC}'`,
|
|
4403
4421
|
'',
|
|
4404
4422
|
(e.stderr ? e.stderr.toString().trim() : e.message),
|
|
4405
4423
|
]);
|
|
@@ -4772,6 +4790,11 @@ function applyOpenClawPluginConfig(existing, token, workspaceId, agentId, cloud)
|
|
|
4772
4790
|
pcfg.endpoint = getOtelEndpointBase(cloud);
|
|
4773
4791
|
pcfg.workspaceId = workspaceId;
|
|
4774
4792
|
pcfg.debug = true;
|
|
4793
|
+
// 插件代码版本:参与幂等比对,升级插件(bump scripts/openclaw/package.json version)后
|
|
4794
|
+
// 强制触发重写+重装+重启,避免云端 pluginDir 滞留旧插件 dist。
|
|
4795
|
+
if (OPENCLAW_PLUGIN_VERSION) {
|
|
4796
|
+
pcfg.pluginVersion = OPENCLAW_PLUGIN_VERSION;
|
|
4797
|
+
}
|
|
4775
4798
|
// per-agent trace 放行:把当前 agentId 并入 traceAgentIds(去重、归一为小写,
|
|
4776
4799
|
// 与插件侧 resolveAgentIdFromHookCtx 的归一一致)。无 agentId(全局模式)则不动
|
|
4777
4800
|
// allowlist —— 空 allowlist 表示全部放行。
|
package/package.json
CHANGED
|
@@ -29,19 +29,43 @@ from pathlib import Path
|
|
|
29
29
|
from typing import Optional, List, Dict, Any
|
|
30
30
|
|
|
31
31
|
# --- SDK Import ---
|
|
32
|
-
|
|
32
|
+
# 最低版本:set_finish_time 是 cozeloop SDK 0.1.25 才引入的方法,云端预装旧版(<=0.1.24)
|
|
33
|
+
# 调用会抛 AttributeError 并使整条 trace 上报失败。统一用能力探测(hasattr)判定,与
|
|
34
|
+
# _set_finish_time_safe 兜底口径一致,避免版本号字符串比较的边界问题。
|
|
35
|
+
_MIN_COZELOOP_SPEC = "cozeloop>=0.1.28"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _cozeloop_capable():
|
|
39
|
+
"""已装 cozeloop 是否具备 set_finish_time 能力。未装/异常返回 None(无法判定)。"""
|
|
33
40
|
try:
|
|
34
41
|
import cozeloop # noqa: F401
|
|
35
|
-
return True
|
|
36
42
|
except ImportError:
|
|
37
|
-
|
|
43
|
+
return None
|
|
44
|
+
try:
|
|
45
|
+
return hasattr(cozeloop.Span, "set_finish_time")
|
|
46
|
+
except Exception:
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _ensure_cozeloop_sdk():
|
|
51
|
+
"""确保 cozeloop 可 import 且尽量满足 set_finish_time 能力。
|
|
52
|
+
|
|
53
|
+
返回 True 表示 cozeloop 可 import(不保证版本达标——能力不足时由
|
|
54
|
+
_set_finish_time_safe 兜底,不阻断上报);返回 False 表示完全无法 import。
|
|
55
|
+
"""
|
|
56
|
+
capable = _cozeloop_capable()
|
|
57
|
+
if capable is True:
|
|
58
|
+
return True
|
|
59
|
+
# 已装但能力不足(capable is False)→ 需升级;未装(None)→ 需安装。
|
|
38
60
|
import subprocess
|
|
39
61
|
import importlib
|
|
40
62
|
import site
|
|
63
|
+
pkg = _MIN_COZELOOP_SPEC
|
|
64
|
+
base_flags = ["--quiet", "--disable-pip-version-check", "--upgrade"]
|
|
41
65
|
attempts = (
|
|
42
|
-
[
|
|
43
|
-
[
|
|
44
|
-
[
|
|
66
|
+
[*base_flags, pkg],
|
|
67
|
+
[*base_flags, "--break-system-packages", pkg],
|
|
68
|
+
[*base_flags, "--break-system-packages", "--user", pkg],
|
|
45
69
|
)
|
|
46
70
|
for extra in attempts:
|
|
47
71
|
try:
|
|
@@ -60,11 +84,12 @@ def _ensure_cozeloop_sdk():
|
|
|
60
84
|
sys.path.insert(0, p)
|
|
61
85
|
importlib.invalidate_caches()
|
|
62
86
|
import cozeloop # noqa: F401
|
|
63
|
-
print("[CozeLoop] cozeloop SDK
|
|
87
|
+
print("[CozeLoop] cozeloop SDK installed/upgraded at runtime.", file=sys.stderr)
|
|
64
88
|
return True
|
|
65
89
|
except ImportError:
|
|
66
90
|
continue
|
|
67
|
-
|
|
91
|
+
# 升级没成功,但只要原本能 import(capable is False)就继续——兜底会处理能力缺失。
|
|
92
|
+
return capable is False
|
|
68
93
|
|
|
69
94
|
|
|
70
95
|
if _ensure_cozeloop_sdk():
|
|
@@ -539,6 +564,23 @@ def _ts_ms(dt):
|
|
|
539
564
|
return int(dt.timestamp() * 1000) if dt is not None else None
|
|
540
565
|
|
|
541
566
|
|
|
567
|
+
def _set_finish_time_safe(span, dt):
|
|
568
|
+
"""安全设置 span 结束时间。
|
|
569
|
+
|
|
570
|
+
set_finish_time 是 cozeloop SDK 0.1.25+ 的方法,云端旧版没有;缺失或异常都不能
|
|
571
|
+
让整条 trace 上报失败(real_*_ms tag 仍保留耗时信息)。
|
|
572
|
+
"""
|
|
573
|
+
if dt is None:
|
|
574
|
+
return
|
|
575
|
+
fn = getattr(span, "set_finish_time", None)
|
|
576
|
+
if fn is None:
|
|
577
|
+
return
|
|
578
|
+
try:
|
|
579
|
+
fn(dt)
|
|
580
|
+
except Exception:
|
|
581
|
+
pass
|
|
582
|
+
|
|
583
|
+
|
|
542
584
|
def group_messages_into_turns(messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
543
585
|
"""Group messages into conversation turns (user -> assistant -> tool_results).
|
|
544
586
|
|
|
@@ -926,7 +968,7 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, history
|
|
|
926
968
|
}
|
|
927
969
|
# 真实耗时写 tag(SDK finish 不支持显式结束时间,duration 用 tag 兜底)
|
|
928
970
|
if root_start_dt is not None and root_end_dt is not None:
|
|
929
|
-
root_span
|
|
971
|
+
_set_finish_time_safe(root_span, root_end_dt)
|
|
930
972
|
root_tags["real_start_ms"] = _ts_ms(root_start_dt)
|
|
931
973
|
root_tags["real_end_ms"] = _ts_ms(root_end_dt)
|
|
932
974
|
root_tags["latency_ms"] = _ts_ms(root_end_dt) - _ts_ms(root_start_dt)
|
|
@@ -985,7 +1027,7 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, history
|
|
|
985
1027
|
"source": "claude_code",
|
|
986
1028
|
}
|
|
987
1029
|
if turn_start_dt is not None and turn_end_dt is not None:
|
|
988
|
-
turn_span
|
|
1030
|
+
_set_finish_time_safe(turn_span, turn_end_dt)
|
|
989
1031
|
_turn_tags["real_start_ms"] = _ts_ms(turn_start_dt)
|
|
990
1032
|
_turn_tags["real_end_ms"] = _ts_ms(turn_end_dt)
|
|
991
1033
|
_turn_tags["latency_ms"] = _ts_ms(turn_end_dt) - _ts_ms(turn_start_dt)
|
|
@@ -1019,7 +1061,7 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, history
|
|
|
1019
1061
|
model_span.set_runtime(Runtime(library="claude-code"))
|
|
1020
1062
|
model_span.set_model_name(model_name)
|
|
1021
1063
|
if step_start_dt is not None and step_end_dt is not None:
|
|
1022
|
-
model_span
|
|
1064
|
+
_set_finish_time_safe(model_span, step_end_dt)
|
|
1023
1065
|
model_span.set_tags({
|
|
1024
1066
|
"real_start_ms": _ts_ms(step_start_dt),
|
|
1025
1067
|
"real_end_ms": _ts_ms(step_end_dt),
|
|
@@ -1132,7 +1174,7 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, history
|
|
|
1132
1174
|
tool_span.set_runtime(Runtime(library="claude-code"))
|
|
1133
1175
|
# 设真实完成时间,让每个 tool 的 duration 反映实际耗时。
|
|
1134
1176
|
if tool_end_dt is not None:
|
|
1135
|
-
tool_span
|
|
1177
|
+
_set_finish_time_safe(tool_span, tool_end_dt)
|
|
1136
1178
|
tags = {
|
|
1137
1179
|
"tool_name": tool_name,
|
|
1138
1180
|
"tool_call_id": tool_call.get("id"),
|
|
@@ -249,19 +249,44 @@ def get_fresh_token():
|
|
|
249
249
|
# -------------------------------------------------------------------------
|
|
250
250
|
|
|
251
251
|
# --- SDK Import ---
|
|
252
|
-
|
|
252
|
+
# 最低版本:set_finish_time 是 cozeloop SDK 0.1.25 才引入的方法,云端预装旧版(<=0.1.24)
|
|
253
|
+
# 调用会抛 AttributeError 并使整条 trace 上报失败。统一用能力探测(hasattr)判定,与
|
|
254
|
+
# _set_finish_time_safe 兜底口径一致,避免版本号字符串比较的边界问题。
|
|
255
|
+
_MIN_COZELOOP_SPEC = "cozeloop>=0.1.28"
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _cozeloop_capable():
|
|
259
|
+
"""已装 cozeloop 是否具备 set_finish_time 能力。未装/异常返回 None(无法判定)。"""
|
|
253
260
|
try:
|
|
254
261
|
import cozeloop # noqa: F401
|
|
255
|
-
return True
|
|
256
262
|
except ImportError:
|
|
257
|
-
|
|
263
|
+
return None
|
|
264
|
+
try:
|
|
265
|
+
return hasattr(cozeloop.Span, "set_finish_time")
|
|
266
|
+
except Exception:
|
|
267
|
+
return False
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _ensure_cozeloop_sdk():
|
|
271
|
+
"""确保 cozeloop 可 import 且尽量满足 set_finish_time 能力。
|
|
272
|
+
|
|
273
|
+
返回 True 表示 cozeloop 可 import(不保证版本达标——能力不足时由
|
|
274
|
+
_set_finish_time_safe 兜底,不阻断上报);返回 False 表示完全无法 import。
|
|
275
|
+
"""
|
|
276
|
+
capable = _cozeloop_capable()
|
|
277
|
+
if capable is True:
|
|
278
|
+
return True
|
|
279
|
+
# 已装但能力不足(capable is False)→ 需升级;未装(None)→ 需安装。
|
|
258
280
|
import subprocess
|
|
259
281
|
import importlib
|
|
260
282
|
import site
|
|
283
|
+
# 能力不足时强制升级到带下限的版本;未装时直接装下限版本。
|
|
284
|
+
pkg = _MIN_COZELOOP_SPEC
|
|
285
|
+
base_flags = ["--quiet", "--disable-pip-version-check", "--upgrade"]
|
|
261
286
|
attempts = (
|
|
262
|
-
[
|
|
263
|
-
[
|
|
264
|
-
[
|
|
287
|
+
[*base_flags, pkg],
|
|
288
|
+
[*base_flags, "--break-system-packages", pkg],
|
|
289
|
+
[*base_flags, "--break-system-packages", "--user", pkg],
|
|
265
290
|
)
|
|
266
291
|
for extra in attempts:
|
|
267
292
|
try:
|
|
@@ -280,11 +305,12 @@ def _ensure_cozeloop_sdk():
|
|
|
280
305
|
sys.path.insert(0, p)
|
|
281
306
|
importlib.invalidate_caches()
|
|
282
307
|
import cozeloop # noqa: F401
|
|
283
|
-
print("[CozeLoop] cozeloop SDK
|
|
308
|
+
print("[CozeLoop] cozeloop SDK installed/upgraded at runtime.", file=sys.stderr)
|
|
284
309
|
return True
|
|
285
310
|
except ImportError:
|
|
286
311
|
continue
|
|
287
|
-
|
|
312
|
+
# 升级没成功,但只要原本能 import(capable is False)就继续——兜底会处理能力缺失。
|
|
313
|
+
return capable is False
|
|
288
314
|
|
|
289
315
|
|
|
290
316
|
if _ensure_cozeloop_sdk():
|
|
@@ -640,6 +666,23 @@ def _ts_ms(dt):
|
|
|
640
666
|
return int(dt.timestamp() * 1000) if dt is not None else None
|
|
641
667
|
|
|
642
668
|
|
|
669
|
+
def _set_finish_time_safe(span, dt):
|
|
670
|
+
"""安全设置 span 结束时间。
|
|
671
|
+
|
|
672
|
+
set_finish_time 是 cozeloop SDK 0.1.25+ 的方法,云端旧版没有;缺失或异常都不能
|
|
673
|
+
让整条 trace 上报失败(real_*_ms tag 仍保留耗时信息)。
|
|
674
|
+
"""
|
|
675
|
+
if dt is None:
|
|
676
|
+
return
|
|
677
|
+
fn = getattr(span, "set_finish_time", None)
|
|
678
|
+
if fn is None:
|
|
679
|
+
return
|
|
680
|
+
try:
|
|
681
|
+
fn(dt)
|
|
682
|
+
except Exception:
|
|
683
|
+
pass
|
|
684
|
+
|
|
685
|
+
|
|
643
686
|
def _max_dt(*values):
|
|
644
687
|
result = None
|
|
645
688
|
for value in values:
|
|
@@ -955,7 +998,7 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, model_n
|
|
|
955
998
|
"source": "codex_cli",
|
|
956
999
|
}
|
|
957
1000
|
if root_start_dt is not None and root_end_dt is not None:
|
|
958
|
-
root_span
|
|
1001
|
+
_set_finish_time_safe(root_span, root_end_dt)
|
|
959
1002
|
root_tags["real_start_ms"] = _ts_ms(root_start_dt)
|
|
960
1003
|
root_tags["real_end_ms"] = _ts_ms(root_end_dt)
|
|
961
1004
|
root_tags["latency_ms"] = _ts_ms(root_end_dt) - _ts_ms(root_start_dt)
|
|
@@ -1008,7 +1051,7 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, model_n
|
|
|
1008
1051
|
"source": "codex_cli",
|
|
1009
1052
|
}
|
|
1010
1053
|
if turn_start_dt is not None and turn_end_dt is not None:
|
|
1011
|
-
turn_span
|
|
1054
|
+
_set_finish_time_safe(turn_span, turn_end_dt)
|
|
1012
1055
|
_turn_tags["real_start_ms"] = _ts_ms(turn_start_dt)
|
|
1013
1056
|
_turn_tags["real_end_ms"] = _ts_ms(turn_end_dt)
|
|
1014
1057
|
_turn_tags["latency_ms"] = _ts_ms(turn_end_dt) - _ts_ms(turn_start_dt)
|
|
@@ -1026,7 +1069,7 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, model_n
|
|
|
1026
1069
|
model_span.set_runtime(Runtime(library="codex-cli"))
|
|
1027
1070
|
model_span.set_model_name(model_name)
|
|
1028
1071
|
if _model_start_dt is not None and _model_end_dt is not None:
|
|
1029
|
-
model_span
|
|
1072
|
+
_set_finish_time_safe(model_span, _model_end_dt)
|
|
1030
1073
|
model_span.set_tags({
|
|
1031
1074
|
"real_start_ms": _ts_ms(_model_start_dt),
|
|
1032
1075
|
"real_end_ms": _ts_ms(_model_end_dt),
|
|
@@ -1114,7 +1157,7 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, model_n
|
|
|
1114
1157
|
"call_id": call_id,
|
|
1115
1158
|
}
|
|
1116
1159
|
if tool_start_dt is not None and tool_finish_dt is not None:
|
|
1117
|
-
tool_span
|
|
1160
|
+
_set_finish_time_safe(tool_span, tool_finish_dt)
|
|
1118
1161
|
tool_tags["real_start_ms"] = _ts_ms(tool_start_dt)
|
|
1119
1162
|
tool_tags["real_end_ms"] = _ts_ms(tool_finish_dt)
|
|
1120
1163
|
tool_tags["latency_ms"] = _ts_ms(tool_finish_dt) - _ts_ms(tool_start_dt)
|
|
@@ -1148,7 +1191,7 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, model_n
|
|
|
1148
1191
|
"agent_model": sc.get("model") or "",
|
|
1149
1192
|
}
|
|
1150
1193
|
if subagent_start_dt is not None and subagent_finish_dt is not None:
|
|
1151
|
-
subagent_span
|
|
1194
|
+
_set_finish_time_safe(subagent_span, subagent_finish_dt)
|
|
1152
1195
|
subagent_tags["real_start_ms"] = _ts_ms(subagent_start_dt)
|
|
1153
1196
|
subagent_tags["real_end_ms"] = _ts_ms(subagent_finish_dt)
|
|
1154
1197
|
subagent_tags["latency_ms"] = _ts_ms(subagent_finish_dt) - _ts_ms(subagent_start_dt)
|
|
@@ -1177,7 +1220,7 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, model_n
|
|
|
1177
1220
|
"agent_name": nickname,
|
|
1178
1221
|
}
|
|
1179
1222
|
if sa_turn_start_dt is not None and sa_turn_finish_dt is not None:
|
|
1180
|
-
sa_turn_span
|
|
1223
|
+
_set_finish_time_safe(sa_turn_span, sa_turn_finish_dt)
|
|
1181
1224
|
sa_turn_tags["real_start_ms"] = _ts_ms(sa_turn_start_dt)
|
|
1182
1225
|
sa_turn_tags["real_end_ms"] = _ts_ms(sa_turn_finish_dt)
|
|
1183
1226
|
sa_turn_tags["latency_ms"] = _ts_ms(sa_turn_finish_dt) - _ts_ms(sa_turn_start_dt)
|
|
@@ -1196,7 +1239,7 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, model_n
|
|
|
1196
1239
|
sa_model_span.set_model_name(sa_model)
|
|
1197
1240
|
sa_model_tags = {"agent_name": nickname}
|
|
1198
1241
|
if sa_model_start_dt is not None and sa_model_finish_dt is not None:
|
|
1199
|
-
sa_model_span
|
|
1242
|
+
_set_finish_time_safe(sa_model_span, sa_model_finish_dt)
|
|
1200
1243
|
sa_model_tags["real_start_ms"] = _ts_ms(sa_model_start_dt)
|
|
1201
1244
|
sa_model_tags["real_end_ms"] = _ts_ms(sa_model_finish_dt)
|
|
1202
1245
|
sa_model_tags["latency_ms"] = _ts_ms(sa_model_finish_dt) - _ts_ms(sa_model_start_dt)
|
|
@@ -1260,7 +1303,7 @@ def send_turns_to_cozeloop(turns: List[Dict[str, Any]], session_id: str, model_n
|
|
|
1260
1303
|
"agent_name": nickname,
|
|
1261
1304
|
}
|
|
1262
1305
|
if sa_tool_start_dt is not None and sa_tool_finish_dt is not None:
|
|
1263
|
-
sa_tool_span
|
|
1306
|
+
_set_finish_time_safe(sa_tool_span, sa_tool_finish_dt)
|
|
1264
1307
|
sa_tool_tags["real_start_ms"] = _ts_ms(sa_tool_start_dt)
|
|
1265
1308
|
sa_tool_tags["real_end_ms"] = _ts_ms(sa_tool_finish_dt)
|
|
1266
1309
|
sa_tool_tags["latency_ms"] = _ts_ms(sa_tool_finish_dt) - _ts_ms(sa_tool_start_dt)
|