router-maestro 0.1.7__py3-none-any.whl → 0.1.8__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.
- router_maestro/__init__.py +1 -1
- router_maestro/providers/copilot.py +12 -10
- router_maestro/server/routes/responses.py +109 -20
- {router_maestro-0.1.7.dist-info → router_maestro-0.1.8.dist-info}/METADATA +1 -1
- {router_maestro-0.1.7.dist-info → router_maestro-0.1.8.dist-info}/RECORD +8 -8
- {router_maestro-0.1.7.dist-info → router_maestro-0.1.8.dist-info}/WHEEL +0 -0
- {router_maestro-0.1.7.dist-info → router_maestro-0.1.8.dist-info}/entry_points.txt +0 -0
- {router_maestro-0.1.7.dist-info → router_maestro-0.1.8.dist-info}/licenses/LICENSE +0 -0
router_maestro/__init__.py
CHANGED
|
@@ -618,16 +618,18 @@ class CopilotProvider(BaseProvider):
|
|
|
618
618
|
elif event_type == "response.output_item.done":
|
|
619
619
|
item = data.get("item", {})
|
|
620
620
|
if item.get("type") == "function_call":
|
|
621
|
-
#
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
621
|
+
# Only emit if not already done via function_call_arguments.done
|
|
622
|
+
# (current_fc would be None if already emitted)
|
|
623
|
+
if current_fc is not None:
|
|
624
|
+
yield ResponsesStreamChunk(
|
|
625
|
+
content="",
|
|
626
|
+
tool_call=ResponsesToolCall(
|
|
627
|
+
call_id=item.get("call_id", ""),
|
|
628
|
+
name=item.get("name", ""),
|
|
629
|
+
arguments=item.get("arguments", "{}"),
|
|
630
|
+
),
|
|
631
|
+
)
|
|
632
|
+
current_fc = None
|
|
631
633
|
|
|
632
634
|
# Handle done event to get final usage
|
|
633
635
|
elif event_type == "response.done":
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Responses API route for Codex models."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import time
|
|
4
5
|
import uuid
|
|
5
6
|
from collections.abc import AsyncGenerator
|
|
6
7
|
from typing import Any
|
|
@@ -163,6 +164,23 @@ def make_text_content(text: str) -> dict[str, Any]:
|
|
|
163
164
|
return {"type": "output_text", "text": text, "annotations": []}
|
|
164
165
|
|
|
165
166
|
|
|
167
|
+
def make_usage(raw_usage: dict[str, Any] | None) -> dict[str, Any] | None:
|
|
168
|
+
"""Create properly structured usage object matching OpenAI spec."""
|
|
169
|
+
if not raw_usage:
|
|
170
|
+
return None
|
|
171
|
+
|
|
172
|
+
input_tokens = raw_usage.get("input_tokens", 0)
|
|
173
|
+
output_tokens = raw_usage.get("output_tokens", 0)
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
"input_tokens": input_tokens,
|
|
177
|
+
"input_tokens_details": {"cached_tokens": 0},
|
|
178
|
+
"output_tokens": output_tokens,
|
|
179
|
+
"output_tokens_details": {"reasoning_tokens": 0},
|
|
180
|
+
"total_tokens": input_tokens + output_tokens,
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
|
|
166
184
|
def make_message_item(msg_id: str, text: str, status: str = "completed") -> dict[str, Any]:
|
|
167
185
|
"""Create message output item."""
|
|
168
186
|
return {
|
|
@@ -191,12 +209,17 @@ def make_function_call_item(
|
|
|
191
209
|
@router.post("/api/openai/v1/responses")
|
|
192
210
|
async def create_response(request: ResponsesRequest):
|
|
193
211
|
"""Handle Responses API requests (for Codex models)."""
|
|
212
|
+
request_id = generate_id("req")
|
|
213
|
+
start_time = time.time()
|
|
214
|
+
|
|
194
215
|
logger.info(
|
|
195
|
-
"Received responses request: model=%s, stream=%s, has_tools=%s",
|
|
216
|
+
"Received responses request: req_id=%s, model=%s, stream=%s, has_tools=%s",
|
|
217
|
+
request_id,
|
|
196
218
|
request.model,
|
|
197
219
|
request.stream,
|
|
198
220
|
request.tools is not None,
|
|
199
221
|
)
|
|
222
|
+
|
|
200
223
|
model_router = get_router()
|
|
201
224
|
|
|
202
225
|
input_value = convert_input_to_internal(request.input)
|
|
@@ -215,8 +238,13 @@ async def create_response(request: ResponsesRequest):
|
|
|
215
238
|
|
|
216
239
|
if request.stream:
|
|
217
240
|
return StreamingResponse(
|
|
218
|
-
stream_response(model_router, internal_request),
|
|
241
|
+
stream_response(model_router, internal_request, request_id, start_time),
|
|
219
242
|
media_type="text/event-stream",
|
|
243
|
+
headers={
|
|
244
|
+
"Cache-Control": "no-cache",
|
|
245
|
+
"Connection": "keep-alive",
|
|
246
|
+
"X-Accel-Buffering": "no",
|
|
247
|
+
},
|
|
220
248
|
)
|
|
221
249
|
|
|
222
250
|
try:
|
|
@@ -250,17 +278,44 @@ async def create_response(request: ResponsesRequest):
|
|
|
250
278
|
usage=usage,
|
|
251
279
|
)
|
|
252
280
|
except ProviderError as e:
|
|
253
|
-
|
|
281
|
+
elapsed_ms = (time.time() - start_time) * 1000
|
|
282
|
+
logger.error(
|
|
283
|
+
"Responses request failed: req_id=%s, elapsed=%.1fms, error=%s",
|
|
284
|
+
request_id,
|
|
285
|
+
elapsed_ms,
|
|
286
|
+
e,
|
|
287
|
+
)
|
|
254
288
|
raise HTTPException(status_code=e.status_code, detail=str(e))
|
|
255
289
|
|
|
256
290
|
|
|
257
291
|
async def stream_response(
|
|
258
|
-
model_router: Router,
|
|
292
|
+
model_router: Router,
|
|
293
|
+
request: InternalResponsesRequest,
|
|
294
|
+
request_id: str,
|
|
295
|
+
start_time: float,
|
|
259
296
|
) -> AsyncGenerator[str, None]:
|
|
260
297
|
"""Stream Responses API response."""
|
|
261
298
|
try:
|
|
262
299
|
stream, provider_name = await model_router.responses_completion_stream(request)
|
|
263
300
|
response_id = generate_id("resp")
|
|
301
|
+
created_at = int(time.time())
|
|
302
|
+
|
|
303
|
+
logger.debug(
|
|
304
|
+
"Stream started: req_id=%s, resp_id=%s, provider=%s",
|
|
305
|
+
request_id,
|
|
306
|
+
response_id,
|
|
307
|
+
provider_name,
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
# Base response object with all required fields (matching OpenAI spec)
|
|
311
|
+
base_response = {
|
|
312
|
+
"id": response_id,
|
|
313
|
+
"object": "response",
|
|
314
|
+
"created_at": created_at,
|
|
315
|
+
"model": request.model,
|
|
316
|
+
"error": None,
|
|
317
|
+
"incomplete_details": None,
|
|
318
|
+
}
|
|
264
319
|
|
|
265
320
|
output_items: list[dict[str, Any]] = []
|
|
266
321
|
output_index = 0
|
|
@@ -278,8 +333,7 @@ async def stream_response(
|
|
|
278
333
|
{
|
|
279
334
|
"type": "response.created",
|
|
280
335
|
"response": {
|
|
281
|
-
|
|
282
|
-
"object": "response",
|
|
336
|
+
**base_response,
|
|
283
337
|
"status": "in_progress",
|
|
284
338
|
"output": [],
|
|
285
339
|
},
|
|
@@ -291,8 +345,7 @@ async def stream_response(
|
|
|
291
345
|
{
|
|
292
346
|
"type": "response.in_progress",
|
|
293
347
|
"response": {
|
|
294
|
-
|
|
295
|
-
"object": "response",
|
|
348
|
+
**base_response,
|
|
296
349
|
"status": "in_progress",
|
|
297
350
|
"output": [],
|
|
298
351
|
},
|
|
@@ -306,11 +359,18 @@ async def stream_response(
|
|
|
306
359
|
current_message_id = generate_id("msg")
|
|
307
360
|
message_started = True
|
|
308
361
|
|
|
362
|
+
# Note: content starts as empty array, matching OpenAI spec
|
|
309
363
|
yield sse_event(
|
|
310
364
|
{
|
|
311
365
|
"type": "response.output_item.added",
|
|
312
366
|
"output_index": output_index,
|
|
313
|
-
"item":
|
|
367
|
+
"item": {
|
|
368
|
+
"type": "message",
|
|
369
|
+
"id": current_message_id,
|
|
370
|
+
"role": "assistant",
|
|
371
|
+
"content": [],
|
|
372
|
+
"status": "in_progress",
|
|
373
|
+
},
|
|
314
374
|
}
|
|
315
375
|
)
|
|
316
376
|
|
|
@@ -440,12 +500,10 @@ async def stream_response(
|
|
|
440
500
|
{
|
|
441
501
|
"type": "response.completed",
|
|
442
502
|
"response": {
|
|
443
|
-
|
|
444
|
-
"object": "response",
|
|
503
|
+
**base_response,
|
|
445
504
|
"status": "completed",
|
|
446
|
-
"model": request.model,
|
|
447
505
|
"output": output_items,
|
|
448
|
-
"usage": final_usage,
|
|
506
|
+
"usage": make_usage(final_usage),
|
|
449
507
|
},
|
|
450
508
|
}
|
|
451
509
|
)
|
|
@@ -467,21 +525,52 @@ async def stream_response(
|
|
|
467
525
|
{
|
|
468
526
|
"type": "response.completed",
|
|
469
527
|
"response": {
|
|
470
|
-
|
|
471
|
-
"object": "response",
|
|
528
|
+
**base_response,
|
|
472
529
|
"status": "completed",
|
|
473
|
-
"model": request.model,
|
|
474
530
|
"output": output_items,
|
|
475
|
-
"usage": final_usage,
|
|
531
|
+
"usage": make_usage(final_usage),
|
|
476
532
|
},
|
|
477
533
|
}
|
|
478
534
|
)
|
|
479
535
|
|
|
480
|
-
|
|
536
|
+
elapsed_ms = (time.time() - start_time) * 1000
|
|
537
|
+
logger.info(
|
|
538
|
+
"Stream completed: req_id=%s, elapsed=%.1fms, output_items=%d",
|
|
539
|
+
request_id,
|
|
540
|
+
elapsed_ms,
|
|
541
|
+
len(output_items),
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
# NOTE: Do NOT send "data: [DONE]\n\n" - agent-maestro doesn't send it
|
|
545
|
+
# for Responses API
|
|
481
546
|
|
|
482
547
|
except ProviderError as e:
|
|
483
|
-
|
|
484
|
-
|
|
548
|
+
elapsed_ms = (time.time() - start_time) * 1000
|
|
549
|
+
logger.error(
|
|
550
|
+
"Stream failed: req_id=%s, elapsed=%.1fms, error=%s",
|
|
551
|
+
request_id,
|
|
552
|
+
elapsed_ms,
|
|
553
|
+
e,
|
|
554
|
+
)
|
|
555
|
+
# Send response.failed event matching OpenAI spec
|
|
556
|
+
yield sse_event(
|
|
557
|
+
{
|
|
558
|
+
"type": "response.failed",
|
|
559
|
+
"response": {
|
|
560
|
+
"id": response_id,
|
|
561
|
+
"object": "response",
|
|
562
|
+
"status": "failed",
|
|
563
|
+
"created_at": created_at,
|
|
564
|
+
"model": request.model,
|
|
565
|
+
"output": [],
|
|
566
|
+
"error": {
|
|
567
|
+
"code": "server_error",
|
|
568
|
+
"message": str(e),
|
|
569
|
+
},
|
|
570
|
+
"incomplete_details": None,
|
|
571
|
+
},
|
|
572
|
+
}
|
|
573
|
+
)
|
|
485
574
|
|
|
486
575
|
|
|
487
576
|
def _close_message_events(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
router_maestro/__init__.py,sha256=
|
|
1
|
+
router_maestro/__init__.py,sha256=GKAiKIjY7YgU-RUu1wYqMOh8QQzcwBku1sGSfeGIKKw,92
|
|
2
2
|
router_maestro/__main__.py,sha256=cUHr8B7JBiv5HhnN6l2iayDkGSBpI5Kf4I3jv9I_I3o,121
|
|
3
3
|
router_maestro/auth/__init__.py,sha256=0JgD1w2gtGSkj809kgSKQanYYkncg6eF-hHoz-jQPgo,353
|
|
4
4
|
router_maestro/auth/github_oauth.py,sha256=acQlAA2Zh6c8KQYdzXbC4ww0EJ41AgvbI5ixpFuNoRg,5060
|
|
@@ -22,7 +22,7 @@ router_maestro/config/settings.py,sha256=cEgE0XjdYMbB39wJDQiTurWSbfJwjAjLI8g7ZOi
|
|
|
22
22
|
router_maestro/providers/__init__.py,sha256=ozZMBw5SPoUd8OoTaLWX7ddlxz6hHMlMpsJXf3D9g8g,955
|
|
23
23
|
router_maestro/providers/anthropic.py,sha256=zhBDtPootCVCYfGyrsogdy3j8jC1aMwi01hWkg-uKmA,8077
|
|
24
24
|
router_maestro/providers/base.py,sha256=YeejIPYXPv00fbup5ahuYvL362rXw2yn4RHY5TElCw0,5367
|
|
25
|
-
router_maestro/providers/copilot.py,sha256=
|
|
25
|
+
router_maestro/providers/copilot.py,sha256=Mfvx4qIZUZMHIjHWTaC4JTDC3d62KrnhLHRA4QwFpZA,26662
|
|
26
26
|
router_maestro/providers/openai.py,sha256=Bsq5mzAVf4CawH2Tn80y3-MyLLVeZ3VsPxGDNH1t_Nk,7647
|
|
27
27
|
router_maestro/providers/openai_compat.py,sha256=ef4RttKVZUTBiRed4BEuC2Jg8vr5GM7YqPUtFYeAhZo,6383
|
|
28
28
|
router_maestro/routing/__init__.py,sha256=eCEQVbg1LAfcSVLQZpZtYf8ImbOhFIaR7POUb1pCbXM,169
|
|
@@ -38,7 +38,7 @@ router_maestro/server/routes/admin.py,sha256=oub4hDrYaytuorXkJzmz0YZ4Z2rcyNuwKcK
|
|
|
38
38
|
router_maestro/server/routes/anthropic.py,sha256=vzbgnYNYt5mOCeZgarSZ6jiIr_d9v64BPtIjIquz6Ew,13287
|
|
39
39
|
router_maestro/server/routes/chat.py,sha256=nXeZVXaJl9BsU3OMs1OW6vFV5PRHOjylyOitM2dXpFM,4729
|
|
40
40
|
router_maestro/server/routes/models.py,sha256=SYHwaMCLrz3F-9fIpPAFjmS_wil5DQIPtzqHV_rimfc,704
|
|
41
|
-
router_maestro/server/routes/responses.py,sha256=
|
|
41
|
+
router_maestro/server/routes/responses.py,sha256=9_qrJdEAKuo3Mgc3FoieyDAbGOgAbF2woB-ZX8Joa1o,20402
|
|
42
42
|
router_maestro/server/schemas/__init__.py,sha256=VQqkyEhoRuXJxPs87rMT6rrfupxcY_qBSbU-7t6ZdZQ,2102
|
|
43
43
|
router_maestro/server/schemas/admin.py,sha256=DuUojkCcq9n8pDhWG6L0SpzQooh91lmHjCRzgZ4AMwk,2369
|
|
44
44
|
router_maestro/server/schemas/anthropic.py,sha256=S5TFYDd8Iw7Oxjki6ng84DGVB90G0-mOza5D5r3rwOY,6566
|
|
@@ -47,8 +47,8 @@ router_maestro/server/schemas/responses.py,sha256=v6AqmgE4OVz8Funu2e-SIP52eIJlRW
|
|
|
47
47
|
router_maestro/utils/__init__.py,sha256=oSQyV--FueMPggRfjWWVnAKtjkcZWFOm9hCTymu0oZU,409
|
|
48
48
|
router_maestro/utils/logging.py,sha256=gJWoRYibAxCWn4VmTmnrwpBRzQ7Uu5YIEk5zDiF9X_k,2393
|
|
49
49
|
router_maestro/utils/tokens.py,sha256=U5PXJv_6ba5xgMBG0c5qB96Yu6uLscSUjMWYTdNests,1530
|
|
50
|
-
router_maestro-0.1.
|
|
51
|
-
router_maestro-0.1.
|
|
52
|
-
router_maestro-0.1.
|
|
53
|
-
router_maestro-0.1.
|
|
54
|
-
router_maestro-0.1.
|
|
50
|
+
router_maestro-0.1.8.dist-info/METADATA,sha256=ZissyATuGtEmN0BbLn5Pi5x9ponbMLqWfMIh4oodKUQ,12984
|
|
51
|
+
router_maestro-0.1.8.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
52
|
+
router_maestro-0.1.8.dist-info/entry_points.txt,sha256=zoFUxxvNcFe0nTgpRbIdygIDEOla3KbvW6HbOCOlgv4,63
|
|
53
|
+
router_maestro-0.1.8.dist-info/licenses/LICENSE,sha256=Ea86BSGu7_tpLAuzif_JmM9zjMoKQEf95VVF9sZw3Jo,1084
|
|
54
|
+
router_maestro-0.1.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|