promptlayer 1.3.2 → 1.3.3
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/README.md +111 -85
- package/dist/claude-agents.js +1 -1
- package/dist/esm/{chunk-CRODAGHV.js → chunk-BT4UJ2MR.js} +2 -2
- package/dist/esm/claude-agents.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/openai-agents.js +1 -1
- package/dist/index.js +1 -1
- package/dist/openai-agents.js +1 -1
- package/package.json +2 -1
- package/vendor/claude-agents/trace/hooks/py/handlers.py +2 -2
- package/vendor/claude-agents/trace/hooks/py/stop_parser.py +90 -8
- /package/dist/esm/{chunk-CRODAGHV.js.map → chunk-BT4UJ2MR.js.map} +0 -0
|
@@ -68,6 +68,22 @@ def message_text(content):
|
|
|
68
68
|
return ""
|
|
69
69
|
|
|
70
70
|
|
|
71
|
+
def message_thinking(content):
|
|
72
|
+
if isinstance(content, list):
|
|
73
|
+
parts = []
|
|
74
|
+
for block in content:
|
|
75
|
+
if isinstance(block, dict) and block.get("type") == "thinking":
|
|
76
|
+
thinking = block.get("thinking")
|
|
77
|
+
if isinstance(thinking, str) and thinking:
|
|
78
|
+
parts.append(thinking)
|
|
79
|
+
return "\n".join(parts).strip()
|
|
80
|
+
if isinstance(content, dict) and content.get("type") == "thinking":
|
|
81
|
+
thinking = content.get("thinking")
|
|
82
|
+
if isinstance(thinking, str):
|
|
83
|
+
return thinking
|
|
84
|
+
return ""
|
|
85
|
+
|
|
86
|
+
|
|
71
87
|
def flatten_indexed(prefix, items, out):
|
|
72
88
|
for i, item in enumerate(items):
|
|
73
89
|
for key, value in item.items():
|
|
@@ -101,6 +117,69 @@ def is_tool_result_user(rec):
|
|
|
101
117
|
)
|
|
102
118
|
|
|
103
119
|
|
|
120
|
+
def assistant_message_id(rec):
|
|
121
|
+
if rec.get("type") != "assistant":
|
|
122
|
+
return ""
|
|
123
|
+
msg = rec.get("message", {})
|
|
124
|
+
if not isinstance(msg, dict):
|
|
125
|
+
return ""
|
|
126
|
+
return stringify(msg.get("id"))
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def merge_content_blocks(existing, incoming):
|
|
130
|
+
if isinstance(existing, list) and isinstance(incoming, list):
|
|
131
|
+
return existing + incoming
|
|
132
|
+
if existing is None:
|
|
133
|
+
return incoming
|
|
134
|
+
if incoming is None:
|
|
135
|
+
return existing
|
|
136
|
+
if isinstance(existing, list):
|
|
137
|
+
return existing + [incoming]
|
|
138
|
+
if isinstance(incoming, list):
|
|
139
|
+
return [existing] + incoming
|
|
140
|
+
return [existing, incoming]
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def coalesce_assistant_message_fragments(records):
|
|
144
|
+
coalesced = []
|
|
145
|
+
for rec in records:
|
|
146
|
+
if not coalesced:
|
|
147
|
+
coalesced.append(rec)
|
|
148
|
+
continue
|
|
149
|
+
|
|
150
|
+
prev = coalesced[-1]
|
|
151
|
+
rec_msg_id = assistant_message_id(rec)
|
|
152
|
+
prev_msg_id = assistant_message_id(prev)
|
|
153
|
+
same_assistant_message = (
|
|
154
|
+
rec_msg_id
|
|
155
|
+
and prev_msg_id == rec_msg_id
|
|
156
|
+
and rec.get("sessionId") == prev.get("sessionId")
|
|
157
|
+
)
|
|
158
|
+
if not same_assistant_message:
|
|
159
|
+
coalesced.append(rec)
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
prev_msg = prev.get("message", {})
|
|
163
|
+
rec_msg = rec.get("message", {})
|
|
164
|
+
if not isinstance(prev_msg, dict) or not isinstance(rec_msg, dict):
|
|
165
|
+
coalesced.append(rec)
|
|
166
|
+
continue
|
|
167
|
+
|
|
168
|
+
prev_msg["content"] = merge_content_blocks(
|
|
169
|
+
prev_msg.get("content"),
|
|
170
|
+
rec_msg.get("content"),
|
|
171
|
+
)
|
|
172
|
+
for key in ("model", "stop_reason", "stop_sequence", "stop_details", "context_management"):
|
|
173
|
+
if rec_msg.get(key) is not None:
|
|
174
|
+
prev_msg[key] = rec_msg.get(key)
|
|
175
|
+
if rec_msg.get("usage"):
|
|
176
|
+
prev_msg["usage"] = rec_msg.get("usage")
|
|
177
|
+
if rec.get("timestamp"):
|
|
178
|
+
prev["timestamp"] = rec.get("timestamp")
|
|
179
|
+
|
|
180
|
+
return coalesced
|
|
181
|
+
|
|
182
|
+
|
|
104
183
|
def parse_transcript(transcript_path, turn_start_fallback, pending_payloads, expected_session_id=None):
|
|
105
184
|
records = []
|
|
106
185
|
with open(transcript_path, encoding="utf-8") as f:
|
|
@@ -118,6 +197,8 @@ def parse_transcript(transcript_path, turn_start_fallback, pending_payloads, exp
|
|
|
118
197
|
except Exception:
|
|
119
198
|
continue
|
|
120
199
|
|
|
200
|
+
records = coalesce_assistant_message_fragments(records)
|
|
201
|
+
|
|
121
202
|
if not records:
|
|
122
203
|
now_ns = int(datetime.now(timezone.utc).timestamp() * 1_000_000_000)
|
|
123
204
|
return {"turn": {"start_ns": now_ns, "end_ns": now_ns}, "tools": [], "llms": []}
|
|
@@ -137,7 +218,6 @@ def parse_transcript(transcript_path, turn_start_fallback, pending_payloads, exp
|
|
|
137
218
|
llms = []
|
|
138
219
|
pending_tool_uses = []
|
|
139
220
|
pending_payload_idx = 0
|
|
140
|
-
saw_human_input = False
|
|
141
221
|
|
|
142
222
|
turn_start_ns = turn_start_fallback
|
|
143
223
|
turn_end_ns = turn_start_fallback
|
|
@@ -160,7 +240,6 @@ def parse_transcript(transcript_path, turn_start_fallback, pending_payloads, exp
|
|
|
160
240
|
if content:
|
|
161
241
|
append_history_item(history, {"role": "user", "content": content})
|
|
162
242
|
last_input_ns = timestamp_ns or last_input_ns
|
|
163
|
-
saw_human_input = True
|
|
164
243
|
continue
|
|
165
244
|
|
|
166
245
|
if rec_type == "user":
|
|
@@ -231,7 +310,6 @@ def parse_transcript(transcript_path, turn_start_fallback, pending_payloads, exp
|
|
|
231
310
|
user_text = content_to_text(content)
|
|
232
311
|
append_history_item(history, {"role": "user", "content": user_text})
|
|
233
312
|
last_input_ns = timestamp_ns or last_input_ns
|
|
234
|
-
saw_human_input = True
|
|
235
313
|
continue
|
|
236
314
|
|
|
237
315
|
if rec_type != "assistant":
|
|
@@ -248,6 +326,7 @@ def parse_transcript(transcript_path, turn_start_fallback, pending_payloads, exp
|
|
|
248
326
|
prompt_tokens = safe_int(usage.get("input_tokens"), 0)
|
|
249
327
|
completion_tokens = safe_int(usage.get("output_tokens"), 0)
|
|
250
328
|
output_text = message_text(msg.get("content"))
|
|
329
|
+
output_thinking = message_thinking(msg.get("content"))
|
|
251
330
|
|
|
252
331
|
tool_calls = []
|
|
253
332
|
content_blocks = msg.get("content")
|
|
@@ -277,7 +356,7 @@ def parse_transcript(transcript_path, turn_start_fallback, pending_payloads, exp
|
|
|
277
356
|
}
|
|
278
357
|
)
|
|
279
358
|
|
|
280
|
-
if not output_text and not tool_calls:
|
|
359
|
+
if not output_text and not output_thinking and not tool_calls:
|
|
281
360
|
continue
|
|
282
361
|
|
|
283
362
|
llm_start_ns = last_input_ns or timestamp_ns or turn_start_ns
|
|
@@ -309,11 +388,13 @@ def parse_transcript(transcript_path, turn_start_fallback, pending_payloads, exp
|
|
|
309
388
|
flatten_indexed("gen_ai.prompt", history, attrs)
|
|
310
389
|
|
|
311
390
|
completion_item = {"role": "assistant", "content": output_text}
|
|
391
|
+
if output_thinking:
|
|
392
|
+
completion_item["thinking"] = output_thinking
|
|
312
393
|
if tool_calls:
|
|
313
394
|
completion_item["tool_calls"] = tool_calls
|
|
314
395
|
flatten_indexed("gen_ai.completion", [completion_item], attrs)
|
|
315
396
|
|
|
316
|
-
span_name = "LLM
|
|
397
|
+
span_name = "LLM call"
|
|
317
398
|
|
|
318
399
|
if emit_for_turn:
|
|
319
400
|
llms.append(
|
|
@@ -326,10 +407,11 @@ def parse_transcript(transcript_path, turn_start_fallback, pending_payloads, exp
|
|
|
326
407
|
)
|
|
327
408
|
|
|
328
409
|
assistant_history = {"role": "assistant", "content": output_text}
|
|
410
|
+
if output_thinking:
|
|
411
|
+
assistant_history["thinking"] = output_thinking
|
|
329
412
|
if tool_calls:
|
|
330
413
|
assistant_history["tool_calls"] = tool_calls
|
|
331
414
|
history.append(assistant_history)
|
|
332
|
-
saw_human_input = False
|
|
333
415
|
|
|
334
416
|
if turn_start_ns is None:
|
|
335
417
|
turn_start_ns = int(datetime.now(timezone.utc).timestamp() * 1_000_000_000)
|
|
@@ -369,14 +451,14 @@ def build_stop_hook_span_specs(
|
|
|
369
451
|
trace_id=trace_id,
|
|
370
452
|
span_id=session_span_id,
|
|
371
453
|
parent_span_id=session_parent_span_id,
|
|
372
|
-
name="
|
|
454
|
+
name="LLM session",
|
|
373
455
|
kind="1",
|
|
374
456
|
start_ns=str(session_start_ns),
|
|
375
457
|
end_ns=turn_end_ns,
|
|
376
458
|
attrs={
|
|
377
459
|
"source": "claude-code",
|
|
378
460
|
"hook": session_hook_attr,
|
|
379
|
-
"node_type": "
|
|
461
|
+
"node_type": "LLM_SESSION",
|
|
380
462
|
"session.lifecycle": session_lifecycle_attr,
|
|
381
463
|
},
|
|
382
464
|
)
|
|
File without changes
|