promptlayer 1.0.16__py3-none-any.whl → 1.0.78__py3-none-any.whl
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.
- promptlayer/__init__.py +38 -3
- promptlayer/exceptions.py +119 -0
- promptlayer/groups/__init__.py +16 -4
- promptlayer/groups/groups.py +7 -4
- promptlayer/promptlayer.py +542 -230
- promptlayer/promptlayer_base.py +31 -40
- promptlayer/promptlayer_mixins.py +460 -0
- promptlayer/span_exporter.py +19 -24
- promptlayer/streaming/__init__.py +64 -0
- promptlayer/streaming/blueprint_builder.py +382 -0
- promptlayer/streaming/response_handlers.py +960 -0
- promptlayer/streaming/stream_processor.py +106 -0
- promptlayer/templates.py +22 -5
- promptlayer/track/__init__.py +50 -12
- promptlayer/track/track.py +76 -15
- promptlayer/types/__init__.py +2 -1
- promptlayer/types/prompt_template.py +59 -3
- promptlayer/types/request_log.py +8 -0
- promptlayer/utils.py +1517 -341
- {promptlayer-1.0.16.dist-info → promptlayer-1.0.78.dist-info}/METADATA +19 -10
- promptlayer-1.0.78.dist-info/RECORD +23 -0
- {promptlayer-1.0.16.dist-info → promptlayer-1.0.78.dist-info}/WHEEL +1 -1
- promptlayer-1.0.16.dist-info/RECORD +0 -16
- {promptlayer-1.0.16.dist-info → promptlayer-1.0.78.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,960 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Response handlers for different LLM providers
|
|
3
|
+
|
|
4
|
+
This module contains handlers that process streaming responses from various
|
|
5
|
+
LLM providers and return both the final response and prompt blueprint.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from typing import Any, AsyncIterable, List
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def openai_stream_chat(results: list):
|
|
13
|
+
"""Process OpenAI streaming chat results and return response + blueprint"""
|
|
14
|
+
from openai.types.chat import (
|
|
15
|
+
ChatCompletion,
|
|
16
|
+
ChatCompletionChunk,
|
|
17
|
+
ChatCompletionMessage,
|
|
18
|
+
ChatCompletionMessageToolCall,
|
|
19
|
+
)
|
|
20
|
+
from openai.types.chat.chat_completion import Choice
|
|
21
|
+
from openai.types.chat.chat_completion_message_tool_call import Function
|
|
22
|
+
|
|
23
|
+
chat_completion_chunks: List[ChatCompletionChunk] = results
|
|
24
|
+
response: ChatCompletion = ChatCompletion(
|
|
25
|
+
id="",
|
|
26
|
+
object="chat.completion",
|
|
27
|
+
choices=[
|
|
28
|
+
Choice(
|
|
29
|
+
finish_reason="stop",
|
|
30
|
+
index=0,
|
|
31
|
+
message=ChatCompletionMessage(role="assistant"),
|
|
32
|
+
)
|
|
33
|
+
],
|
|
34
|
+
created=0,
|
|
35
|
+
model="",
|
|
36
|
+
)
|
|
37
|
+
last_result = chat_completion_chunks[-1]
|
|
38
|
+
response.id = last_result.id
|
|
39
|
+
response.created = last_result.created
|
|
40
|
+
response.model = last_result.model
|
|
41
|
+
response.system_fingerprint = last_result.system_fingerprint
|
|
42
|
+
response.usage = last_result.usage
|
|
43
|
+
content = ""
|
|
44
|
+
tool_calls: List[ChatCompletionMessageToolCall] = []
|
|
45
|
+
|
|
46
|
+
for result in chat_completion_chunks:
|
|
47
|
+
choices = result.choices
|
|
48
|
+
if len(choices) == 0:
|
|
49
|
+
continue
|
|
50
|
+
if choices[0].delta.content:
|
|
51
|
+
content = f"{content}{result.choices[0].delta.content}"
|
|
52
|
+
|
|
53
|
+
delta = choices[0].delta
|
|
54
|
+
if delta.tool_calls:
|
|
55
|
+
last_tool_call = None
|
|
56
|
+
if len(tool_calls) > 0:
|
|
57
|
+
last_tool_call = tool_calls[-1]
|
|
58
|
+
tool_call = delta.tool_calls[0]
|
|
59
|
+
if not tool_call.function:
|
|
60
|
+
continue
|
|
61
|
+
if not last_tool_call or tool_call.id:
|
|
62
|
+
tool_calls.append(
|
|
63
|
+
ChatCompletionMessageToolCall(
|
|
64
|
+
id=tool_call.id or "",
|
|
65
|
+
function=Function(
|
|
66
|
+
name=tool_call.function.name or "",
|
|
67
|
+
arguments=tool_call.function.arguments or "",
|
|
68
|
+
),
|
|
69
|
+
type=tool_call.type or "function",
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
continue
|
|
73
|
+
last_tool_call.function.name = f"{last_tool_call.function.name}{tool_call.function.name or ''}"
|
|
74
|
+
last_tool_call.function.arguments = (
|
|
75
|
+
f"{last_tool_call.function.arguments}{tool_call.function.arguments or ''}"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
response.choices[0].message.content = content
|
|
79
|
+
response.choices[0].message.tool_calls = tool_calls if tool_calls else None
|
|
80
|
+
return response
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
async def aopenai_stream_chat(generator: AsyncIterable[Any]) -> Any:
|
|
84
|
+
"""Async version of openai_stream_chat"""
|
|
85
|
+
from openai.types.chat import (
|
|
86
|
+
ChatCompletion,
|
|
87
|
+
ChatCompletionChunk,
|
|
88
|
+
ChatCompletionMessage,
|
|
89
|
+
ChatCompletionMessageToolCall,
|
|
90
|
+
)
|
|
91
|
+
from openai.types.chat.chat_completion import Choice
|
|
92
|
+
from openai.types.chat.chat_completion_message_tool_call import Function
|
|
93
|
+
|
|
94
|
+
chat_completion_chunks: List[ChatCompletionChunk] = []
|
|
95
|
+
response: ChatCompletion = ChatCompletion(
|
|
96
|
+
id="",
|
|
97
|
+
object="chat.completion",
|
|
98
|
+
choices=[
|
|
99
|
+
Choice(
|
|
100
|
+
finish_reason="stop",
|
|
101
|
+
index=0,
|
|
102
|
+
message=ChatCompletionMessage(role="assistant"),
|
|
103
|
+
)
|
|
104
|
+
],
|
|
105
|
+
created=0,
|
|
106
|
+
model="",
|
|
107
|
+
)
|
|
108
|
+
content = ""
|
|
109
|
+
tool_calls: List[ChatCompletionMessageToolCall] = []
|
|
110
|
+
|
|
111
|
+
async for result in generator:
|
|
112
|
+
chat_completion_chunks.append(result)
|
|
113
|
+
choices = result.choices
|
|
114
|
+
if len(choices) == 0:
|
|
115
|
+
continue
|
|
116
|
+
if choices[0].delta.content:
|
|
117
|
+
content = f"{content}{choices[0].delta.content}"
|
|
118
|
+
|
|
119
|
+
delta = choices[0].delta
|
|
120
|
+
if delta.tool_calls:
|
|
121
|
+
last_tool_call = None
|
|
122
|
+
if len(tool_calls) > 0:
|
|
123
|
+
last_tool_call = tool_calls[-1]
|
|
124
|
+
tool_call = delta.tool_calls[0]
|
|
125
|
+
if not tool_call.function:
|
|
126
|
+
continue
|
|
127
|
+
if not last_tool_call or tool_call.id:
|
|
128
|
+
tool_calls.append(
|
|
129
|
+
ChatCompletionMessageToolCall(
|
|
130
|
+
id=tool_call.id or "",
|
|
131
|
+
function=Function(
|
|
132
|
+
name=tool_call.function.name or "",
|
|
133
|
+
arguments=tool_call.function.arguments or "",
|
|
134
|
+
),
|
|
135
|
+
type=tool_call.type or "function",
|
|
136
|
+
)
|
|
137
|
+
)
|
|
138
|
+
continue
|
|
139
|
+
last_tool_call.function.name = f"{last_tool_call.function.name}{tool_call.function.name or ''}"
|
|
140
|
+
last_tool_call.function.arguments = (
|
|
141
|
+
f"{last_tool_call.function.arguments}{tool_call.function.arguments or ''}"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# After collecting all chunks, set the response attributes
|
|
145
|
+
if chat_completion_chunks:
|
|
146
|
+
last_result = chat_completion_chunks[-1]
|
|
147
|
+
response.id = last_result.id
|
|
148
|
+
response.created = last_result.created
|
|
149
|
+
response.model = last_result.model
|
|
150
|
+
response.system_fingerprint = getattr(last_result, "system_fingerprint", None)
|
|
151
|
+
response.usage = last_result.usage
|
|
152
|
+
|
|
153
|
+
response.choices[0].message.content = content
|
|
154
|
+
response.choices[0].message.tool_calls = tool_calls if tool_calls else None
|
|
155
|
+
return response
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _initialize_openai_response_data():
|
|
159
|
+
"""Initialize the response data structure for OpenAI responses"""
|
|
160
|
+
return {
|
|
161
|
+
"id": None,
|
|
162
|
+
"object": "response",
|
|
163
|
+
"created_at": None,
|
|
164
|
+
"status": None,
|
|
165
|
+
"error": None,
|
|
166
|
+
"incomplete_details": None,
|
|
167
|
+
"instructions": None,
|
|
168
|
+
"max_output_tokens": None,
|
|
169
|
+
"model": None,
|
|
170
|
+
"output": [],
|
|
171
|
+
"parallel_tool_calls": True,
|
|
172
|
+
"previous_response_id": None,
|
|
173
|
+
"reasoning": {"effort": None, "summary": None},
|
|
174
|
+
"store": True,
|
|
175
|
+
"temperature": 1,
|
|
176
|
+
"text": {"format": {"type": "text"}},
|
|
177
|
+
"tool_choice": "auto",
|
|
178
|
+
"tools": [],
|
|
179
|
+
"top_p": 1,
|
|
180
|
+
"truncation": "disabled",
|
|
181
|
+
"usage": None,
|
|
182
|
+
"user": None,
|
|
183
|
+
"metadata": {},
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _process_openai_response_event(chunk_dict, response_data, current_items):
|
|
188
|
+
"""Process a single OpenAI response event and update the response data"""
|
|
189
|
+
event_type = chunk_dict.get("type")
|
|
190
|
+
has_reasoning = False
|
|
191
|
+
|
|
192
|
+
if event_type == "response.created":
|
|
193
|
+
response_info = chunk_dict.get("response", {})
|
|
194
|
+
response_data["id"] = response_info.get("id")
|
|
195
|
+
response_data["created_at"] = response_info.get("created_at")
|
|
196
|
+
response_data["model"] = response_info.get("model")
|
|
197
|
+
response_data["status"] = response_info.get("status")
|
|
198
|
+
response_data["parallel_tool_calls"] = response_info.get("parallel_tool_calls", True)
|
|
199
|
+
response_data["temperature"] = response_info.get("temperature", 1)
|
|
200
|
+
response_data["tool_choice"] = response_info.get("tool_choice", "auto")
|
|
201
|
+
response_data["tools"] = response_info.get("tools", [])
|
|
202
|
+
response_data["top_p"] = response_info.get("top_p", 1)
|
|
203
|
+
response_data["truncation"] = response_info.get("truncation", "disabled")
|
|
204
|
+
response_data["max_output_tokens"] = response_info.get("max_output_tokens")
|
|
205
|
+
response_data["previous_response_id"] = response_info.get("previous_response_id")
|
|
206
|
+
response_data["store"] = response_info.get("store", True)
|
|
207
|
+
response_data["user"] = response_info.get("user")
|
|
208
|
+
response_data["metadata"] = response_info.get("metadata", {})
|
|
209
|
+
|
|
210
|
+
text_config = response_info.get("text", {})
|
|
211
|
+
if text_config:
|
|
212
|
+
response_data["text"] = text_config
|
|
213
|
+
|
|
214
|
+
reasoning = response_info.get("reasoning", {})
|
|
215
|
+
if reasoning:
|
|
216
|
+
response_data["reasoning"] = reasoning
|
|
217
|
+
has_reasoning = True
|
|
218
|
+
|
|
219
|
+
elif event_type == "response.in_progress":
|
|
220
|
+
response_info = chunk_dict.get("response", {})
|
|
221
|
+
response_data["status"] = response_info.get("status")
|
|
222
|
+
|
|
223
|
+
elif event_type == "response.output_item.added":
|
|
224
|
+
item = chunk_dict.get("item", {})
|
|
225
|
+
item_id = item.get("id")
|
|
226
|
+
item_type = item.get("type")
|
|
227
|
+
|
|
228
|
+
if item_type == "reasoning":
|
|
229
|
+
current_items[item_id] = {
|
|
230
|
+
"type": "reasoning",
|
|
231
|
+
"id": item_id,
|
|
232
|
+
"summary": [],
|
|
233
|
+
"status": item.get("status", "in_progress"),
|
|
234
|
+
}
|
|
235
|
+
has_reasoning = True
|
|
236
|
+
|
|
237
|
+
elif item_type == "function_call":
|
|
238
|
+
current_items[item_id] = {
|
|
239
|
+
"type": "function_call",
|
|
240
|
+
"id": item_id,
|
|
241
|
+
"call_id": item.get("call_id"),
|
|
242
|
+
"name": item.get("name"),
|
|
243
|
+
"arguments": "",
|
|
244
|
+
"status": item.get("status", "in_progress"),
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
elif item_type == "message":
|
|
248
|
+
current_items[item_id] = {
|
|
249
|
+
"type": "message",
|
|
250
|
+
"id": item_id,
|
|
251
|
+
"role": item.get("role", "assistant"),
|
|
252
|
+
"content": [],
|
|
253
|
+
"status": item.get("status", "in_progress"),
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
elif item_type == "code_interpreter_call":
|
|
257
|
+
current_items[item_id] = {
|
|
258
|
+
"type": "code_interpreter_call",
|
|
259
|
+
"id": item_id,
|
|
260
|
+
"code": item.get("code", ""),
|
|
261
|
+
"container_id": item.get("container_id"),
|
|
262
|
+
"status": item.get("status", "in_progress"),
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
elif event_type == "response.reasoning_summary_part.added":
|
|
266
|
+
item_id = chunk_dict.get("item_id")
|
|
267
|
+
part = chunk_dict.get("part", {})
|
|
268
|
+
|
|
269
|
+
if item_id in current_items and current_items[item_id]["type"] == "reasoning":
|
|
270
|
+
summary_part = {"type": part.get("type", "summary_text"), "text": part.get("text", "")}
|
|
271
|
+
current_items[item_id]["summary"].append(summary_part)
|
|
272
|
+
|
|
273
|
+
elif event_type == "response.reasoning_summary_text.delta":
|
|
274
|
+
item_id = chunk_dict.get("item_id")
|
|
275
|
+
delta = chunk_dict.get("delta", "")
|
|
276
|
+
summary_index = chunk_dict.get("summary_index", 0)
|
|
277
|
+
|
|
278
|
+
if item_id in current_items and current_items[item_id]["type"] == "reasoning":
|
|
279
|
+
while len(current_items[item_id]["summary"]) <= summary_index:
|
|
280
|
+
current_items[item_id]["summary"].append({"type": "summary_text", "text": ""})
|
|
281
|
+
|
|
282
|
+
current_items[item_id]["summary"][summary_index]["text"] += delta
|
|
283
|
+
|
|
284
|
+
elif event_type == "response.reasoning_summary_text.done":
|
|
285
|
+
item_id = chunk_dict.get("item_id")
|
|
286
|
+
final_text = chunk_dict.get("text", "")
|
|
287
|
+
summary_index = chunk_dict.get("summary_index", 0)
|
|
288
|
+
|
|
289
|
+
if item_id in current_items and current_items[item_id]["type"] == "reasoning":
|
|
290
|
+
while len(current_items[item_id]["summary"]) <= summary_index:
|
|
291
|
+
current_items[item_id]["summary"].append({"type": "summary_text", "text": ""})
|
|
292
|
+
|
|
293
|
+
current_items[item_id]["summary"][summary_index]["text"] = final_text
|
|
294
|
+
|
|
295
|
+
elif event_type == "response.reasoning_summary_part.done":
|
|
296
|
+
item_id = chunk_dict.get("item_id")
|
|
297
|
+
part = chunk_dict.get("part", {})
|
|
298
|
+
|
|
299
|
+
if item_id in current_items and current_items[item_id]["type"] == "reasoning":
|
|
300
|
+
summary_index = chunk_dict.get("summary_index", 0)
|
|
301
|
+
if summary_index < len(current_items[item_id]["summary"]):
|
|
302
|
+
current_items[item_id]["summary"][summary_index] = {
|
|
303
|
+
"type": part.get("type", "summary_text"),
|
|
304
|
+
"text": part.get("text", ""),
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
elif event_type == "response.function_call_arguments.delta":
|
|
308
|
+
item_id = chunk_dict.get("item_id")
|
|
309
|
+
delta = chunk_dict.get("delta", "")
|
|
310
|
+
|
|
311
|
+
if item_id in current_items:
|
|
312
|
+
current_items[item_id]["arguments"] += delta
|
|
313
|
+
|
|
314
|
+
elif event_type == "response.function_call_arguments.done":
|
|
315
|
+
item_id = chunk_dict.get("item_id")
|
|
316
|
+
final_arguments = chunk_dict.get("arguments", "")
|
|
317
|
+
|
|
318
|
+
if item_id in current_items:
|
|
319
|
+
current_items[item_id]["arguments"] = final_arguments
|
|
320
|
+
|
|
321
|
+
elif event_type == "response.content_part.added":
|
|
322
|
+
part = chunk_dict.get("part", {})
|
|
323
|
+
|
|
324
|
+
message_item = None
|
|
325
|
+
for item in current_items.values():
|
|
326
|
+
if item.get("type") == "message":
|
|
327
|
+
message_item = item
|
|
328
|
+
break
|
|
329
|
+
|
|
330
|
+
if message_item:
|
|
331
|
+
content_part = {
|
|
332
|
+
"type": part.get("type", "output_text"),
|
|
333
|
+
"text": part.get("text", ""),
|
|
334
|
+
"annotations": part.get("annotations", []),
|
|
335
|
+
}
|
|
336
|
+
message_item["content"].append(content_part)
|
|
337
|
+
|
|
338
|
+
elif event_type == "response.output_text.delta":
|
|
339
|
+
delta_text = chunk_dict.get("delta", "")
|
|
340
|
+
|
|
341
|
+
for item in current_items.values():
|
|
342
|
+
if item.get("type") == "message" and item.get("content"):
|
|
343
|
+
if item["content"] and item["content"][-1].get("type") == "output_text":
|
|
344
|
+
item["content"][-1]["text"] += delta_text
|
|
345
|
+
break
|
|
346
|
+
|
|
347
|
+
elif event_type == "response.output_text.done":
|
|
348
|
+
final_text = chunk_dict.get("text", "")
|
|
349
|
+
|
|
350
|
+
for item in current_items.values():
|
|
351
|
+
if item.get("type") == "message" and item.get("content"):
|
|
352
|
+
if item["content"] and item["content"][-1].get("type") == "output_text":
|
|
353
|
+
item["content"][-1]["text"] = final_text
|
|
354
|
+
break
|
|
355
|
+
|
|
356
|
+
elif event_type == "response.output_item.done":
|
|
357
|
+
item = chunk_dict.get("item", {})
|
|
358
|
+
item_id = item.get("id")
|
|
359
|
+
|
|
360
|
+
if item_id in current_items:
|
|
361
|
+
current_items[item_id]["status"] = item.get("status", "completed")
|
|
362
|
+
|
|
363
|
+
if item.get("type") == "reasoning":
|
|
364
|
+
current_items[item_id].update({"summary": item.get("summary", current_items[item_id]["summary"])})
|
|
365
|
+
elif item.get("type") == "function_call":
|
|
366
|
+
current_items[item_id].update(
|
|
367
|
+
{
|
|
368
|
+
"arguments": item.get("arguments", current_items[item_id]["arguments"]),
|
|
369
|
+
"call_id": item.get("call_id", current_items[item_id]["call_id"]),
|
|
370
|
+
"name": item.get("name", current_items[item_id]["name"]),
|
|
371
|
+
}
|
|
372
|
+
)
|
|
373
|
+
elif item.get("type") == "message":
|
|
374
|
+
current_items[item_id].update(
|
|
375
|
+
{
|
|
376
|
+
"content": item.get("content", current_items[item_id]["content"]),
|
|
377
|
+
"role": item.get("role", current_items[item_id]["role"]),
|
|
378
|
+
}
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
response_data["output"].append(current_items[item_id])
|
|
382
|
+
|
|
383
|
+
elif event_type == "response.completed":
|
|
384
|
+
response_info = chunk_dict.get("response", {})
|
|
385
|
+
response_data["status"] = response_info.get("status", "completed")
|
|
386
|
+
response_data["usage"] = response_info.get("usage")
|
|
387
|
+
response_data["output"] = response_info.get("output", response_data["output"])
|
|
388
|
+
|
|
389
|
+
if response_info.get("reasoning"):
|
|
390
|
+
response_data["reasoning"] = response_info["reasoning"]
|
|
391
|
+
|
|
392
|
+
return has_reasoning
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def openai_responses_stream_chat(results: list):
|
|
396
|
+
"""Process OpenAI Responses streaming chat results and return response"""
|
|
397
|
+
from openai.types.responses import Response
|
|
398
|
+
|
|
399
|
+
response_data = _initialize_openai_response_data()
|
|
400
|
+
current_items = {}
|
|
401
|
+
|
|
402
|
+
for chunk in results:
|
|
403
|
+
chunk_dict = chunk.model_dump()
|
|
404
|
+
_process_openai_response_event(chunk_dict, response_data, current_items)
|
|
405
|
+
|
|
406
|
+
return Response(**response_data)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
async def aopenai_responses_stream_chat(generator: AsyncIterable[Any]) -> Any:
|
|
410
|
+
"""Async version of openai_responses_stream_chat"""
|
|
411
|
+
from openai.types.responses import Response
|
|
412
|
+
|
|
413
|
+
response_data = _initialize_openai_response_data()
|
|
414
|
+
current_items = {}
|
|
415
|
+
|
|
416
|
+
async for chunk in generator:
|
|
417
|
+
chunk_dict = chunk.model_dump()
|
|
418
|
+
_process_openai_response_event(chunk_dict, response_data, current_items)
|
|
419
|
+
|
|
420
|
+
return Response(**response_data)
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def anthropic_stream_message(results: list):
|
|
424
|
+
"""Process Anthropic streaming message results and return response + blueprint"""
|
|
425
|
+
from anthropic.types import Message, MessageStreamEvent, Usage
|
|
426
|
+
|
|
427
|
+
from promptlayer.utils import build_anthropic_content_blocks
|
|
428
|
+
|
|
429
|
+
message_stream_events: List[MessageStreamEvent] = results
|
|
430
|
+
response: Message = Message(
|
|
431
|
+
id="",
|
|
432
|
+
model="",
|
|
433
|
+
content=[],
|
|
434
|
+
role="assistant",
|
|
435
|
+
type="message",
|
|
436
|
+
stop_reason="stop_sequence",
|
|
437
|
+
stop_sequence=None,
|
|
438
|
+
usage=Usage(input_tokens=0, output_tokens=0),
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
for event in message_stream_events:
|
|
442
|
+
if event.type == "message_start":
|
|
443
|
+
response = event.message
|
|
444
|
+
break
|
|
445
|
+
|
|
446
|
+
content_blocks, usage, stop_reason = build_anthropic_content_blocks(message_stream_events)
|
|
447
|
+
response.content = content_blocks
|
|
448
|
+
if usage:
|
|
449
|
+
response.usage.output_tokens = usage.output_tokens
|
|
450
|
+
if stop_reason:
|
|
451
|
+
response.stop_reason = stop_reason
|
|
452
|
+
|
|
453
|
+
return response
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
async def aanthropic_stream_message(generator: AsyncIterable[Any]) -> Any:
|
|
457
|
+
"""Async version of anthropic_stream_message"""
|
|
458
|
+
from anthropic.types import Message, MessageStreamEvent, Usage
|
|
459
|
+
|
|
460
|
+
from promptlayer.utils import build_anthropic_content_blocks
|
|
461
|
+
|
|
462
|
+
message_stream_events: List[MessageStreamEvent] = []
|
|
463
|
+
response: Message = Message(
|
|
464
|
+
id="",
|
|
465
|
+
model="",
|
|
466
|
+
content=[],
|
|
467
|
+
role="assistant",
|
|
468
|
+
type="message",
|
|
469
|
+
stop_reason="stop_sequence",
|
|
470
|
+
stop_sequence=None,
|
|
471
|
+
usage=Usage(input_tokens=0, output_tokens=0),
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
async for event in generator:
|
|
475
|
+
if event.type == "message_start":
|
|
476
|
+
response = event.message
|
|
477
|
+
message_stream_events.append(event)
|
|
478
|
+
|
|
479
|
+
content_blocks, usage, stop_reason = build_anthropic_content_blocks(message_stream_events)
|
|
480
|
+
response.content = content_blocks
|
|
481
|
+
if usage:
|
|
482
|
+
response.usage.output_tokens = usage.output_tokens
|
|
483
|
+
if stop_reason:
|
|
484
|
+
response.stop_reason = stop_reason
|
|
485
|
+
|
|
486
|
+
return response
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def openai_stream_completion(results: list):
|
|
490
|
+
from openai.types.completion import Completion, CompletionChoice
|
|
491
|
+
|
|
492
|
+
completions: List[Completion] = results
|
|
493
|
+
last_chunk = completions[-1]
|
|
494
|
+
response = Completion(
|
|
495
|
+
id=last_chunk.id,
|
|
496
|
+
created=last_chunk.created,
|
|
497
|
+
model=last_chunk.model,
|
|
498
|
+
object="text_completion",
|
|
499
|
+
choices=[CompletionChoice(finish_reason="stop", index=0, text="")],
|
|
500
|
+
)
|
|
501
|
+
text = ""
|
|
502
|
+
for completion in completions:
|
|
503
|
+
usage = completion.usage
|
|
504
|
+
system_fingerprint = completion.system_fingerprint
|
|
505
|
+
if len(completion.choices) > 0 and completion.choices[0].text:
|
|
506
|
+
text = f"{text}{completion.choices[0].text}"
|
|
507
|
+
if usage:
|
|
508
|
+
response.usage = usage
|
|
509
|
+
if system_fingerprint:
|
|
510
|
+
response.system_fingerprint = system_fingerprint
|
|
511
|
+
response.choices[0].text = text
|
|
512
|
+
return response
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
async def aopenai_stream_completion(generator: AsyncIterable[Any]) -> Any:
|
|
516
|
+
from openai.types.completion import Completion, CompletionChoice
|
|
517
|
+
|
|
518
|
+
completions: List[Completion] = []
|
|
519
|
+
text = ""
|
|
520
|
+
response = Completion(
|
|
521
|
+
id="",
|
|
522
|
+
created=0,
|
|
523
|
+
model="",
|
|
524
|
+
object="text_completion",
|
|
525
|
+
choices=[CompletionChoice(finish_reason="stop", index=0, text="")],
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
async for completion in generator:
|
|
529
|
+
completions.append(completion)
|
|
530
|
+
usage = completion.usage
|
|
531
|
+
system_fingerprint = getattr(completion, "system_fingerprint", None)
|
|
532
|
+
if len(completion.choices) > 0 and completion.choices[0].text:
|
|
533
|
+
text = f"{text}{completion.choices[0].text}"
|
|
534
|
+
if usage:
|
|
535
|
+
response.usage = usage
|
|
536
|
+
if system_fingerprint:
|
|
537
|
+
response.system_fingerprint = system_fingerprint
|
|
538
|
+
|
|
539
|
+
# After collecting all completions, set the response attributes
|
|
540
|
+
if completions:
|
|
541
|
+
last_chunk = completions[-1]
|
|
542
|
+
response.id = last_chunk.id
|
|
543
|
+
response.created = last_chunk.created
|
|
544
|
+
response.model = last_chunk.model
|
|
545
|
+
|
|
546
|
+
response.choices[0].text = text
|
|
547
|
+
return response
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def anthropic_stream_completion(results: list):
|
|
551
|
+
from anthropic.types import Completion
|
|
552
|
+
|
|
553
|
+
completions: List[Completion] = results
|
|
554
|
+
last_chunk = completions[-1]
|
|
555
|
+
response = Completion(
|
|
556
|
+
id=last_chunk.id,
|
|
557
|
+
completion="",
|
|
558
|
+
model=last_chunk.model,
|
|
559
|
+
stop_reason="stop",
|
|
560
|
+
type="completion",
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
text = ""
|
|
564
|
+
for completion in completions:
|
|
565
|
+
text = f"{text}{completion.completion}"
|
|
566
|
+
response.completion = text
|
|
567
|
+
return response
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
async def aanthropic_stream_completion(generator: AsyncIterable[Any]) -> Any:
|
|
571
|
+
from anthropic.types import Completion
|
|
572
|
+
|
|
573
|
+
completions: List[Completion] = []
|
|
574
|
+
text = ""
|
|
575
|
+
response = Completion(
|
|
576
|
+
id="",
|
|
577
|
+
completion="",
|
|
578
|
+
model="",
|
|
579
|
+
stop_reason="stop",
|
|
580
|
+
type="completion",
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
async for completion in generator:
|
|
584
|
+
completions.append(completion)
|
|
585
|
+
text = f"{text}{completion.completion}"
|
|
586
|
+
|
|
587
|
+
# After collecting all completions, set the response attributes
|
|
588
|
+
if completions:
|
|
589
|
+
last_chunk = completions[-1]
|
|
590
|
+
response.id = last_chunk.id
|
|
591
|
+
response.model = last_chunk.model
|
|
592
|
+
|
|
593
|
+
response.completion = text
|
|
594
|
+
return response
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
def _build_google_response_from_parts(thought_content: str, regular_content: str, function_calls: list, last_result):
|
|
598
|
+
"""Helper function to build Google response with thought, regular, and function call parts."""
|
|
599
|
+
from google.genai.chats import Part
|
|
600
|
+
|
|
601
|
+
response = last_result.model_copy()
|
|
602
|
+
final_parts = []
|
|
603
|
+
|
|
604
|
+
if thought_content:
|
|
605
|
+
thought_part = Part(text=thought_content, thought=True)
|
|
606
|
+
final_parts.append(thought_part)
|
|
607
|
+
|
|
608
|
+
if regular_content:
|
|
609
|
+
text_part = Part(text=regular_content, thought=None)
|
|
610
|
+
final_parts.append(text_part)
|
|
611
|
+
|
|
612
|
+
for function_call in function_calls:
|
|
613
|
+
function_part = Part(function_call=function_call, thought=None)
|
|
614
|
+
final_parts.append(function_part)
|
|
615
|
+
|
|
616
|
+
if final_parts:
|
|
617
|
+
response.candidates[0].content.parts = final_parts
|
|
618
|
+
|
|
619
|
+
return response
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
async def amap_google_stream_response(generator: AsyncIterable[Any]):
|
|
623
|
+
from google.genai.chats import GenerateContentResponse
|
|
624
|
+
|
|
625
|
+
response = GenerateContentResponse()
|
|
626
|
+
|
|
627
|
+
thought_content = ""
|
|
628
|
+
regular_content = ""
|
|
629
|
+
function_calls = []
|
|
630
|
+
last_result = None
|
|
631
|
+
|
|
632
|
+
async for result in generator:
|
|
633
|
+
last_result = result
|
|
634
|
+
if result.candidates and result.candidates[0].content.parts:
|
|
635
|
+
for part in result.candidates[0].content.parts:
|
|
636
|
+
if hasattr(part, "text") and part.text:
|
|
637
|
+
if hasattr(part, "thought") and part.thought:
|
|
638
|
+
thought_content = f"{thought_content}{part.text}"
|
|
639
|
+
else:
|
|
640
|
+
regular_content = f"{regular_content}{part.text}"
|
|
641
|
+
elif hasattr(part, "function_call") and part.function_call:
|
|
642
|
+
function_calls.append(part.function_call)
|
|
643
|
+
|
|
644
|
+
if not last_result:
|
|
645
|
+
return response
|
|
646
|
+
|
|
647
|
+
return _build_google_response_from_parts(thought_content, regular_content, function_calls, last_result)
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
async def agoogle_stream_chat(generator: AsyncIterable[Any]):
|
|
651
|
+
return await amap_google_stream_response(generator)
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
async def agoogle_stream_completion(generator: AsyncIterable[Any]):
|
|
655
|
+
return await amap_google_stream_response(generator)
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
def map_google_stream_response(results: list):
|
|
659
|
+
from google.genai.chats import GenerateContentResponse
|
|
660
|
+
|
|
661
|
+
response = GenerateContentResponse()
|
|
662
|
+
if not results:
|
|
663
|
+
return response
|
|
664
|
+
results: List[GenerateContentResponse] = results
|
|
665
|
+
|
|
666
|
+
thought_content = ""
|
|
667
|
+
regular_content = ""
|
|
668
|
+
function_calls = []
|
|
669
|
+
|
|
670
|
+
for result in results:
|
|
671
|
+
if result.candidates and result.candidates[0].content.parts:
|
|
672
|
+
for part in result.candidates[0].content.parts:
|
|
673
|
+
if hasattr(part, "text") and part.text:
|
|
674
|
+
if hasattr(part, "thought") and part.thought:
|
|
675
|
+
thought_content = f"{thought_content}{part.text}"
|
|
676
|
+
else:
|
|
677
|
+
regular_content = f"{regular_content}{part.text}"
|
|
678
|
+
elif hasattr(part, "function_call") and part.function_call:
|
|
679
|
+
function_calls.append(part.function_call)
|
|
680
|
+
|
|
681
|
+
return _build_google_response_from_parts(thought_content, regular_content, function_calls, results[-1])
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def google_stream_chat(results: list):
|
|
685
|
+
return map_google_stream_response(results)
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
def google_stream_completion(results: list):
|
|
689
|
+
return map_google_stream_response(results)
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
def mistral_stream_chat(results: list):
|
|
693
|
+
from openai.types.chat import ChatCompletion, ChatCompletionMessage, ChatCompletionMessageToolCall
|
|
694
|
+
from openai.types.chat.chat_completion import Choice
|
|
695
|
+
from openai.types.chat.chat_completion_message_tool_call import Function
|
|
696
|
+
|
|
697
|
+
last_result = results[-1]
|
|
698
|
+
response = ChatCompletion(
|
|
699
|
+
id=last_result.data.id,
|
|
700
|
+
object="chat.completion",
|
|
701
|
+
choices=[
|
|
702
|
+
Choice(
|
|
703
|
+
finish_reason=last_result.data.choices[0].finish_reason or "stop",
|
|
704
|
+
index=0,
|
|
705
|
+
message=ChatCompletionMessage(role="assistant"),
|
|
706
|
+
)
|
|
707
|
+
],
|
|
708
|
+
created=last_result.data.created,
|
|
709
|
+
model=last_result.data.model,
|
|
710
|
+
)
|
|
711
|
+
|
|
712
|
+
content = ""
|
|
713
|
+
tool_calls = None
|
|
714
|
+
|
|
715
|
+
for result in results:
|
|
716
|
+
choices = result.data.choices
|
|
717
|
+
if len(choices) == 0:
|
|
718
|
+
continue
|
|
719
|
+
|
|
720
|
+
delta = choices[0].delta
|
|
721
|
+
if delta.content is not None:
|
|
722
|
+
content = f"{content}{delta.content}"
|
|
723
|
+
|
|
724
|
+
if delta.tool_calls:
|
|
725
|
+
tool_calls = tool_calls or []
|
|
726
|
+
for tool_call in delta.tool_calls:
|
|
727
|
+
if len(tool_calls) == 0 or tool_call.id:
|
|
728
|
+
tool_calls.append(
|
|
729
|
+
ChatCompletionMessageToolCall(
|
|
730
|
+
id=tool_call.id or "",
|
|
731
|
+
function=Function(
|
|
732
|
+
name=tool_call.function.name,
|
|
733
|
+
arguments=tool_call.function.arguments,
|
|
734
|
+
),
|
|
735
|
+
type="function",
|
|
736
|
+
)
|
|
737
|
+
)
|
|
738
|
+
else:
|
|
739
|
+
last_tool_call = tool_calls[-1]
|
|
740
|
+
if tool_call.function.name:
|
|
741
|
+
last_tool_call.function.name = f"{last_tool_call.function.name}{tool_call.function.name}"
|
|
742
|
+
if tool_call.function.arguments:
|
|
743
|
+
last_tool_call.function.arguments = (
|
|
744
|
+
f"{last_tool_call.function.arguments}{tool_call.function.arguments}"
|
|
745
|
+
)
|
|
746
|
+
|
|
747
|
+
response.choices[0].message.content = content
|
|
748
|
+
response.choices[0].message.tool_calls = tool_calls
|
|
749
|
+
response.usage = last_result.data.usage
|
|
750
|
+
return response
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
async def amistral_stream_chat(generator: AsyncIterable[Any]) -> Any:
|
|
754
|
+
from openai.types.chat import ChatCompletion, ChatCompletionMessage, ChatCompletionMessageToolCall
|
|
755
|
+
from openai.types.chat.chat_completion import Choice
|
|
756
|
+
from openai.types.chat.chat_completion_message_tool_call import Function
|
|
757
|
+
|
|
758
|
+
completion_chunks = []
|
|
759
|
+
response = ChatCompletion(
|
|
760
|
+
id="",
|
|
761
|
+
object="chat.completion",
|
|
762
|
+
choices=[
|
|
763
|
+
Choice(
|
|
764
|
+
finish_reason="stop",
|
|
765
|
+
index=0,
|
|
766
|
+
message=ChatCompletionMessage(role="assistant"),
|
|
767
|
+
)
|
|
768
|
+
],
|
|
769
|
+
created=0,
|
|
770
|
+
model="",
|
|
771
|
+
)
|
|
772
|
+
content = ""
|
|
773
|
+
tool_calls = None
|
|
774
|
+
|
|
775
|
+
async for result in generator:
|
|
776
|
+
completion_chunks.append(result)
|
|
777
|
+
choices = result.data.choices
|
|
778
|
+
if len(choices) == 0:
|
|
779
|
+
continue
|
|
780
|
+
delta = choices[0].delta
|
|
781
|
+
if delta.content is not None:
|
|
782
|
+
content = f"{content}{delta.content}"
|
|
783
|
+
|
|
784
|
+
if delta.tool_calls:
|
|
785
|
+
tool_calls = tool_calls or []
|
|
786
|
+
for tool_call in delta.tool_calls:
|
|
787
|
+
if len(tool_calls) == 0 or tool_call.id:
|
|
788
|
+
tool_calls.append(
|
|
789
|
+
ChatCompletionMessageToolCall(
|
|
790
|
+
id=tool_call.id or "",
|
|
791
|
+
function=Function(
|
|
792
|
+
name=tool_call.function.name,
|
|
793
|
+
arguments=tool_call.function.arguments,
|
|
794
|
+
),
|
|
795
|
+
type="function",
|
|
796
|
+
)
|
|
797
|
+
)
|
|
798
|
+
else:
|
|
799
|
+
last_tool_call = tool_calls[-1]
|
|
800
|
+
if tool_call.function.name:
|
|
801
|
+
last_tool_call.function.name = f"{last_tool_call.function.name}{tool_call.function.name}"
|
|
802
|
+
if tool_call.function.arguments:
|
|
803
|
+
last_tool_call.function.arguments = (
|
|
804
|
+
f"{last_tool_call.function.arguments}{tool_call.function.arguments}"
|
|
805
|
+
)
|
|
806
|
+
|
|
807
|
+
if completion_chunks:
|
|
808
|
+
last_result = completion_chunks[-1]
|
|
809
|
+
response.id = last_result.data.id
|
|
810
|
+
response.created = last_result.data.created
|
|
811
|
+
response.model = last_result.data.model
|
|
812
|
+
response.usage = last_result.data.usage
|
|
813
|
+
|
|
814
|
+
response.choices[0].message.content = content
|
|
815
|
+
response.choices[0].message.tool_calls = tool_calls
|
|
816
|
+
return response
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
def bedrock_stream_message(results: list):
|
|
820
|
+
"""Process Amazon Bedrock streaming message results and return response + blueprint"""
|
|
821
|
+
|
|
822
|
+
response = {"ResponseMetadata": {}, "output": {"message": {}}, "stopReason": "end_turn", "metrics": {}, "usage": {}}
|
|
823
|
+
|
|
824
|
+
content_blocks = []
|
|
825
|
+
current_tool_call = None
|
|
826
|
+
current_tool_input = ""
|
|
827
|
+
current_text = ""
|
|
828
|
+
current_signature = ""
|
|
829
|
+
current_thinking = ""
|
|
830
|
+
|
|
831
|
+
for event in results:
|
|
832
|
+
if "contentBlockStart" in event:
|
|
833
|
+
content_block = event["contentBlockStart"]
|
|
834
|
+
if "start" in content_block and "toolUse" in content_block["start"]:
|
|
835
|
+
tool_use = content_block["start"]["toolUse"]
|
|
836
|
+
current_tool_call = {"toolUse": {"toolUseId": tool_use["toolUseId"], "name": tool_use["name"]}}
|
|
837
|
+
current_tool_input = ""
|
|
838
|
+
|
|
839
|
+
elif "contentBlockDelta" in event:
|
|
840
|
+
delta = event["contentBlockDelta"]["delta"]
|
|
841
|
+
if "text" in delta:
|
|
842
|
+
current_text += delta["text"]
|
|
843
|
+
elif "reasoningContent" in delta:
|
|
844
|
+
reasoning_content = delta["reasoningContent"]
|
|
845
|
+
if "text" in reasoning_content:
|
|
846
|
+
current_thinking += reasoning_content["text"]
|
|
847
|
+
elif "signature" in reasoning_content:
|
|
848
|
+
current_signature += reasoning_content["signature"]
|
|
849
|
+
elif "toolUse" in delta:
|
|
850
|
+
if "input" in delta["toolUse"]:
|
|
851
|
+
input_chunk = delta["toolUse"]["input"]
|
|
852
|
+
current_tool_input += input_chunk
|
|
853
|
+
if not input_chunk.strip():
|
|
854
|
+
continue
|
|
855
|
+
|
|
856
|
+
elif "contentBlockStop" in event:
|
|
857
|
+
if current_tool_call and current_tool_input:
|
|
858
|
+
try:
|
|
859
|
+
current_tool_call["toolUse"]["input"] = json.loads(current_tool_input)
|
|
860
|
+
except json.JSONDecodeError:
|
|
861
|
+
current_tool_call["toolUse"]["input"] = {}
|
|
862
|
+
content_blocks.append(current_tool_call)
|
|
863
|
+
current_tool_call = None
|
|
864
|
+
current_tool_input = ""
|
|
865
|
+
elif current_text:
|
|
866
|
+
content_blocks.append({"text": current_text})
|
|
867
|
+
current_text = ""
|
|
868
|
+
elif current_thinking and current_signature:
|
|
869
|
+
content_blocks.append(
|
|
870
|
+
{
|
|
871
|
+
"reasoningContent": {
|
|
872
|
+
"reasoningText": {"text": current_thinking, "signature": current_signature},
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
)
|
|
876
|
+
current_thinking = ""
|
|
877
|
+
current_signature = ""
|
|
878
|
+
|
|
879
|
+
elif "messageStop" in event:
|
|
880
|
+
response["stopReason"] = event["messageStop"]["stopReason"]
|
|
881
|
+
|
|
882
|
+
elif "metadata" in event:
|
|
883
|
+
metadata = event["metadata"]
|
|
884
|
+
response["usage"] = metadata.get("usage", {})
|
|
885
|
+
response["metrics"] = metadata.get("metrics", {})
|
|
886
|
+
|
|
887
|
+
response["output"]["message"] = {"role": "assistant", "content": content_blocks}
|
|
888
|
+
return response
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
async def abedrock_stream_message(generator: AsyncIterable[Any]) -> Any:
|
|
892
|
+
"""Async version of bedrock_stream_message"""
|
|
893
|
+
|
|
894
|
+
response = {"ResponseMetadata": {}, "output": {"message": {}}, "stopReason": "end_turn", "metrics": {}, "usage": {}}
|
|
895
|
+
|
|
896
|
+
content_blocks = []
|
|
897
|
+
current_tool_call = None
|
|
898
|
+
current_tool_input = ""
|
|
899
|
+
current_text = ""
|
|
900
|
+
current_signature = ""
|
|
901
|
+
current_thinking = ""
|
|
902
|
+
|
|
903
|
+
async for event in generator:
|
|
904
|
+
if "contentBlockStart" in event:
|
|
905
|
+
content_block = event["contentBlockStart"]
|
|
906
|
+
if "start" in content_block and "toolUse" in content_block["start"]:
|
|
907
|
+
tool_use = content_block["start"]["toolUse"]
|
|
908
|
+
current_tool_call = {"toolUse": {"toolUseId": tool_use["toolUseId"], "name": tool_use["name"]}}
|
|
909
|
+
current_tool_input = ""
|
|
910
|
+
|
|
911
|
+
elif "contentBlockDelta" in event:
|
|
912
|
+
delta = event["contentBlockDelta"]["delta"]
|
|
913
|
+
if "text" in delta:
|
|
914
|
+
current_text += delta["text"]
|
|
915
|
+
elif "reasoningContent" in delta:
|
|
916
|
+
reasoning_content = delta["reasoningContent"]
|
|
917
|
+
if "text" in reasoning_content:
|
|
918
|
+
current_thinking += reasoning_content["text"]
|
|
919
|
+
elif "signature" in reasoning_content:
|
|
920
|
+
current_signature += reasoning_content["signature"]
|
|
921
|
+
elif "toolUse" in delta:
|
|
922
|
+
if "input" in delta["toolUse"]:
|
|
923
|
+
input_chunk = delta["toolUse"]["input"]
|
|
924
|
+
current_tool_input += input_chunk
|
|
925
|
+
if not input_chunk.strip():
|
|
926
|
+
continue
|
|
927
|
+
|
|
928
|
+
elif "contentBlockStop" in event:
|
|
929
|
+
if current_tool_call and current_tool_input:
|
|
930
|
+
try:
|
|
931
|
+
current_tool_call["toolUse"]["input"] = json.loads(current_tool_input)
|
|
932
|
+
except json.JSONDecodeError:
|
|
933
|
+
current_tool_call["toolUse"]["input"] = {}
|
|
934
|
+
content_blocks.append(current_tool_call)
|
|
935
|
+
current_tool_call = None
|
|
936
|
+
current_tool_input = ""
|
|
937
|
+
elif current_text:
|
|
938
|
+
content_blocks.append({"text": current_text})
|
|
939
|
+
current_text = ""
|
|
940
|
+
elif current_thinking and current_signature:
|
|
941
|
+
content_blocks.append(
|
|
942
|
+
{
|
|
943
|
+
"reasoningContent": {
|
|
944
|
+
"reasoningText": {"text": current_thinking, "signature": current_signature},
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
)
|
|
948
|
+
current_thinking = ""
|
|
949
|
+
current_signature = ""
|
|
950
|
+
|
|
951
|
+
elif "messageStop" in event:
|
|
952
|
+
response["stopReason"] = event["messageStop"]["stopReason"]
|
|
953
|
+
|
|
954
|
+
elif "metadata" in event:
|
|
955
|
+
metadata = event["metadata"]
|
|
956
|
+
response["usage"] = metadata.get("usage", {})
|
|
957
|
+
response["metrics"] = metadata.get("metrics", {})
|
|
958
|
+
|
|
959
|
+
response["output"]["message"] = {"role": "assistant", "content": content_blocks}
|
|
960
|
+
return response
|