Repository navigation
Commit 1ccd48a
authored
fix(openai): latch the stream-only constraint and widen its detection (#85)
OpenAI-compatible proxies exist that refuse unary calls outright, answering a
`stream: false` body with an HTTP 400 `{"detail":"Stream must be set to true"}`.
`ChatModel::invoke` already falls back to the streaming path on that rejection,
but the fallback has two gaps that keep it from actually converging.
**The constraint is never remembered.** `requires_streaming` is a plain `bool`
that only `with_requires_streaming` can set, and nothing calls that builder —
`invoke` takes `&self`, so the auto-detected discovery had nowhere to go. Every
single unary call therefore re-pays a guaranteed-400 round trip before falling
back: one wasted request per agent turn, forever. Make it an `AtomicBool` and
latch it on first discovery, so only the first call pays and the rest go
straight to the streaming path. `latch_stream_required` logs on the transition
only, so the discovery is visible exactly once per process.
**The detection is one case-sensitive literal.** The trigger was
`err.message.contains("Stream must be set to true")`, so any other phrasing —
lower-case, `'stream' must be true`, `streaming is required`, `only streaming
is supported`, `stream=true is required` — still hard-failed the harness run.
Replace it with `is_stream_required_error`, which matches that family
case-insensitively and searches the raw body as well as the decoded message:
`parse_error_body` only promotes `error.message` / `message`, so a reason nested
under any other key (`detail`, `errors[].reason`) was invisible. `422` joins
`400` because the wording arrives in a FastAPI `{"detail": …}` envelope and
FastAPI's own field-validation status is 422.
A `stream` mention is required alongside the "must be true" phrasing so
unrelated 400s never get rerouted — the `degrade_for_400` `tool_choice` /
`response_format` handlers and the `/responses` `max_output_tokens` retry keep
their bodies to themselves.
`requires_streaming()` is now `pub` (it was `#[cfg(test)]`-only) so hosts can
observe a learned constraint.
Tests: latch set/idempotence, latch visibility through a shared `Arc` handle,
an 8-body wording matrix including the exact reported body and one nested under
a non-standard key, and a negative matrix (wrong status, `tool_choice`,
`max_output_tokens`, `stream_options is not supported`, `'store' must be true`).
Known gap, deliberately out of scope: the `/responses` path
(`responses_api_primary`) returns before this check and sends `stream: None`, so
a streaming-only Responses backend still surfaces the raw 400. Folding Responses
SSE is a separate piece of work — the transport notes it as a follow-up.
Refs openhuman#5165.1 parent 1862845 commit 1ccd48a
3 files changed
Lines changed: 251 additions & 28 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
29 | 38 | | |
30 | 39 | | |
31 | 40 | | |
| |||
90 | 99 | | |
91 | 100 | | |
92 | 101 | | |
93 | | - | |
| 102 | + | |
94 | 103 | | |
95 | 104 | | |
96 | 105 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
678 | 678 | | |
679 | 679 | | |
680 | 680 | | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
681 | 786 | | |
682 | 787 | | |
683 | 788 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
| 11 | + | |
10 | 12 | | |
11 | 13 | | |
12 | 14 | | |
| |||
113 | 115 | | |
114 | 116 | | |
115 | 117 | | |
116 | | - | |
117 | | - | |
| 118 | + | |
| 119 | + | |
118 | 120 | | |
119 | | - | |
120 | | - | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
121 | 134 | | |
122 | 135 | | |
123 | 136 | | |
| |||
345 | 358 | | |
346 | 359 | | |
347 | 360 | | |
348 | | - | |
| 361 | + | |
349 | 362 | | |
350 | 363 | | |
351 | 364 | | |
352 | 365 | | |
353 | 366 | | |
354 | | - | |
355 | | - | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
356 | 373 | | |
357 | 374 | | |
358 | 375 | | |
359 | | - | |
360 | | - | |
361 | | - | |
362 | | - | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
363 | 400 | | |
364 | 401 | | |
365 | 402 | | |
| |||
1390 | 1427 | | |
1391 | 1428 | | |
1392 | 1429 | | |
| 1430 | + | |
| 1431 | + | |
| 1432 | + | |
| 1433 | + | |
| 1434 | + | |
| 1435 | + | |
| 1436 | + | |
| 1437 | + | |
| 1438 | + | |
| 1439 | + | |
| 1440 | + | |
| 1441 | + | |
| 1442 | + | |
| 1443 | + | |
| 1444 | + | |
| 1445 | + | |
| 1446 | + | |
| 1447 | + | |
| 1448 | + | |
| 1449 | + | |
| 1450 | + | |
| 1451 | + | |
| 1452 | + | |
| 1453 | + | |
| 1454 | + | |
| 1455 | + | |
| 1456 | + | |
| 1457 | + | |
| 1458 | + | |
| 1459 | + | |
| 1460 | + | |
| 1461 | + | |
| 1462 | + | |
| 1463 | + | |
| 1464 | + | |
| 1465 | + | |
| 1466 | + | |
| 1467 | + | |
| 1468 | + | |
| 1469 | + | |
| 1470 | + | |
| 1471 | + | |
| 1472 | + | |
| 1473 | + | |
| 1474 | + | |
| 1475 | + | |
| 1476 | + | |
| 1477 | + | |
| 1478 | + | |
| 1479 | + | |
| 1480 | + | |
| 1481 | + | |
| 1482 | + | |
| 1483 | + | |
| 1484 | + | |
| 1485 | + | |
| 1486 | + | |
| 1487 | + | |
| 1488 | + | |
| 1489 | + | |
| 1490 | + | |
| 1491 | + | |
| 1492 | + | |
| 1493 | + | |
| 1494 | + | |
| 1495 | + | |
| 1496 | + | |
1393 | 1497 | | |
1394 | 1498 | | |
1395 | 1499 | | |
| |||
1514 | 1618 | | |
1515 | 1619 | | |
1516 | 1620 | | |
1517 | | - | |
1518 | | - | |
1519 | | - | |
1520 | | - | |
| 1621 | + | |
| 1622 | + | |
| 1623 | + | |
| 1624 | + | |
| 1625 | + | |
| 1626 | + | |
1521 | 1627 | | |
1522 | 1628 | | |
1523 | 1629 | | |
| |||
1529 | 1635 | | |
1530 | 1636 | | |
1531 | 1637 | | |
1532 | | - | |
1533 | | - | |
1534 | | - | |
1535 | | - | |
1536 | | - | |
| 1638 | + | |
| 1639 | + | |
| 1640 | + | |
| 1641 | + | |
| 1642 | + | |
| 1643 | + | |
| 1644 | + | |
1537 | 1645 | | |
1538 | | - | |
| 1646 | + | |
1539 | 1647 | | |
1540 | 1648 | | |
1541 | 1649 | | |
| |||
1545 | 1653 | | |
1546 | 1654 | | |
1547 | 1655 | | |
1548 | | - | |
1549 | | - | |
1550 | | - | |
1551 | | - | |
1552 | | - | |
1553 | | - | |
| 1656 | + | |
| 1657 | + | |
| 1658 | + | |
| 1659 | + | |
| 1660 | + | |
| 1661 | + | |
1554 | 1662 | | |
1555 | 1663 | | |
1556 | 1664 | | |
| 1665 | + | |
1557 | 1666 | | |
1558 | 1667 | | |
1559 | 1668 | | |
| |||
0 commit comments