coze_lab 0.1.46 → 0.1.48

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coze_lab",
3
- "version": "0.1.46",
3
+ "version": "0.1.48",
4
4
  "description": "Configure local AI agents (Claude Code, Codex, OpenClaw) to report traces to CozeLoop",
5
5
  "keywords": [
6
6
  "cozeloop",
@@ -107,6 +107,36 @@ _OTEL_SUFFIX = "/v1/loop/opentelemetry"
107
107
  _DEFAULT_WORKSPACE_ID = "7649231955045072915" # hardcoded spaceID fallback
108
108
 
109
109
 
110
+ def _normalize_api_base_url(raw: str) -> str:
111
+ """Normalize CozeLoop API/OTLP endpoint env vars to the SDK api_base_url."""
112
+ base = (raw or "").strip().rstrip("/")
113
+ if not base:
114
+ return ""
115
+ suffixes = [
116
+ "/api/v1/loop/traces/ingest",
117
+ "/v1/loop/traces/ingest",
118
+ "/api/v1/loop/opentelemetry/v1/traces",
119
+ "/v1/loop/opentelemetry/v1/traces",
120
+ "/api/v1/loop/opentelemetry",
121
+ "/v1/loop/opentelemetry",
122
+ "/api/v1",
123
+ "/v1",
124
+ ]
125
+ for suffix in suffixes:
126
+ if base.endswith(suffix):
127
+ return base[:-len(suffix)].rstrip("/")
128
+ return base
129
+
130
+
131
+ def get_api_base_url() -> str:
132
+ """Return optional CozeLoop SDK api_base_url from onboard-injected env."""
133
+ return _normalize_api_base_url(
134
+ os.environ.get("COZELOOP_API_BASE_URL")
135
+ or os.environ.get("OTEL_ENDPOINT")
136
+ or ""
137
+ )
138
+
139
+
110
140
  # --- coze-context parsing -------------------------------------------------
111
141
  # User messages may embed a block like:
112
142
  # <coze-context>
@@ -1442,7 +1472,12 @@ def send_steps_realtime(turns, session_id, history_turns, state, coze_tags_overr
1442
1472
  if token:
1443
1473
  os.environ["COZELOOP_API_TOKEN"] = token
1444
1474
  workspace_id = os.environ.get("COZELOOP_WORKSPACE_ID", "") or _DEFAULT_WORKSPACE_ID
1445
- client_kwargs = {"ultra_large_report": True, "upload_timeout": 120}
1475
+ upload_events: List[str] = []
1476
+ client_kwargs = {
1477
+ "ultra_large_report": True,
1478
+ "upload_timeout": 120,
1479
+ "trace_finish_event_processor": _make_finish_event_processor(upload_events),
1480
+ }
1446
1481
  if workspace_id:
1447
1482
  client_kwargs["workspace_id"] = workspace_id
1448
1483
  if token:
@@ -1509,6 +1544,9 @@ def send_steps_realtime(turns, session_id, history_turns, state, coze_tags_overr
1509
1544
  # 实测:root finish 后,子 span 仍可用其 header 挂到同一 trace_id 下,不影响后续增量。
1510
1545
  root_span.finish()
1511
1546
  client.flush()
1547
+ if upload_events:
1548
+ debug_log(f"[rt] upload failed after root flush: {upload_events[-1][:500]}")
1549
+ return None
1512
1550
  debug_log(f"[rt] root created+finished trace_id={getattr(root_ctx,'trace_id','?')}")
1513
1551
 
1514
1552
  # ---- 发新完成的 step ----
@@ -1592,6 +1630,9 @@ def send_steps_realtime(turns, session_id, history_turns, state, coze_tags_overr
1592
1630
  sent += 1
1593
1631
  # 每个 step 立即 flush —— 这是“结束即可见”的关键。
1594
1632
  client.flush()
1633
+ if upload_events:
1634
+ debug_log(f"[rt] upload failed after step flush: {upload_events[-1][:500]}")
1635
+ return None
1595
1636
 
1596
1637
  new_last = last_global + sent
1597
1638
  state["rt_last_global_step"] = new_last
@@ -1620,8 +1661,15 @@ def send_steps_realtime(turns, session_id, history_turns, state, coze_tags_overr
1620
1661
  fin.set_output(last_output)
1621
1662
  fin.finish()
1622
1663
  client.flush()
1664
+ if upload_events:
1665
+ debug_log(f"[rt] upload failed after final flush: {upload_events[-1][:500]}")
1666
+ return None
1623
1667
  debug_log(f"[rt] finalized, total sent steps={new_last}")
1624
1668
 
1669
+ if upload_events:
1670
+ debug_log(f"[rt] upload failed, state not advanced. Last failure: {upload_events[-1][:500]}")
1671
+ return None
1672
+
1625
1673
  debug_log(f"[rt] sent {sent} new step(s), last_global={new_last}/{total_steps}, terminal={is_terminal}")
1626
1674
  return True
1627
1675
  except Exception as e:
@@ -133,6 +133,36 @@ else:
133
133
  DEBUG = os.environ.get("CC_COZELOOP_DEBUG", "").lower() == "true"
134
134
 
135
135
 
136
+ def _normalize_api_base_url(raw: str) -> str:
137
+ """Normalize CozeLoop API/OTLP endpoint env vars to the SDK api_base_url."""
138
+ base = (raw or "").strip().rstrip("/")
139
+ if not base:
140
+ return ""
141
+ suffixes = [
142
+ "/api/v1/loop/traces/ingest",
143
+ "/v1/loop/traces/ingest",
144
+ "/api/v1/loop/opentelemetry/v1/traces",
145
+ "/v1/loop/opentelemetry/v1/traces",
146
+ "/api/v1/loop/opentelemetry",
147
+ "/v1/loop/opentelemetry",
148
+ "/api/v1",
149
+ "/v1",
150
+ ]
151
+ for suffix in suffixes:
152
+ if base.endswith(suffix):
153
+ return base[:-len(suffix)].rstrip("/")
154
+ return base
155
+
156
+
157
+ def get_api_base_url() -> str:
158
+ """Return optional CozeLoop SDK api_base_url from onboard-injected env."""
159
+ return _normalize_api_base_url(
160
+ os.environ.get("COZELOOP_API_BASE_URL")
161
+ or os.environ.get("OTEL_ENDPOINT")
162
+ or ""
163
+ )
164
+
165
+
136
166
  def _log_file_path() -> str:
137
167
  return os.environ.get("COZELOOP_HOOK_LOG", "").strip()
138
168
 
@@ -1221,7 +1251,12 @@ def send_steps_realtime(turns, session_id, state, model_name="codex", coze_tags_
1221
1251
  if token:
1222
1252
  os.environ["COZELOOP_API_TOKEN"] = token
1223
1253
  workspace_id = os.environ.get("COZELOOP_WORKSPACE_ID", "") or _DEFAULT_WORKSPACE_ID
1224
- client_kwargs = {"ultra_large_report": True, "upload_timeout": 120}
1254
+ upload_events: List[str] = []
1255
+ client_kwargs = {
1256
+ "ultra_large_report": True,
1257
+ "upload_timeout": 120,
1258
+ "trace_finish_event_processor": _make_finish_event_processor(upload_events),
1259
+ }
1225
1260
  if workspace_id:
1226
1261
  client_kwargs["workspace_id"] = workspace_id
1227
1262
  if token:
@@ -1277,6 +1312,9 @@ def send_steps_realtime(turns, session_id, state, model_name="codex", coze_tags_
1277
1312
  root_ctx = client.get_span_from_header(state["rt_root_header"])
1278
1313
  root_span.finish() # 立即 finish 落库(后端按 root_span 查),子 span 仍可挂同 trace
1279
1314
  client.flush()
1315
+ if upload_events:
1316
+ hook_log(f"[rt] upload failed after root flush detail={upload_events[-1][:500]}")
1317
+ return None
1280
1318
  hook_log(f"[rt] root created trace_id={getattr(root_ctx,'trace_id','?')}")
1281
1319
 
1282
1320
  # 线性展开所有 tool_call(带 result 的才算完成),按全局序增量发
@@ -1331,6 +1369,9 @@ def send_steps_realtime(turns, session_id, state, model_name="codex", coze_tags_
1331
1369
  tspan.finish()
1332
1370
  sent += 1
1333
1371
  client.flush() # 每个 tool_call 立即 flush → 结束即可见
1372
+ if upload_events:
1373
+ hook_log(f"[rt] upload failed after tool flush detail={upload_events[-1][:500]}")
1374
+ return None
1334
1375
 
1335
1376
  state["rt_last_tool"] = last_tool + sent
1336
1377
  if sent > 0:
@@ -1352,8 +1393,15 @@ def send_steps_realtime(turns, session_id, state, model_name="codex", coze_tags_
1352
1393
  fin.set_output(truncate_text(last_out))
1353
1394
  fin.finish()
1354
1395
  client.flush()
1396
+ if upload_events:
1397
+ hook_log(f"[rt] upload failed after final flush detail={upload_events[-1][:500]}")
1398
+ return None
1355
1399
  hook_log(f"[rt] finalized total_tools={state['rt_last_tool']}")
1356
1400
 
1401
+ if upload_events:
1402
+ hook_log(f"[rt] upload failed state not advanced detail={upload_events[-1][:500]}")
1403
+ return None
1404
+
1357
1405
  hook_log(f"[rt] sent {sent} tool span(s), last_tool={state['rt_last_tool']}, terminal={is_terminal}")
1358
1406
  return True
1359
1407
  except Exception as e: