Commit 8af963b
fix(streaming): comply with OpenAI usage / stream_options spec (#9815)
* fix(streaming): comply with OpenAI usage / stream_options spec (#8546)
LocalAI emitted `"usage":{"prompt_tokens":0,...}` on every streamed
chunk because `OpenAIResponse.Usage` was a value type without
`omitempty`. The official OpenAI Node SDK and its consumers
(continuedev/continue, Kilo Code, Roo Code, Zed, IntelliJ Continue)
filter on a truthy `result.usage` to detect the trailing usage chunk;
LocalAI's zero-but-non-null usage on every intermediate chunk made
that filter swallow every content chunk and surface an empty chat
response while the server log looked successful.
Changes:
- `core/schema/openai.go`: `Usage *OpenAIUsage \`json:"usage,omitempty"\``
so intermediate chunks no longer carry a `usage` key. Add
`OpenAIRequest.StreamOptions` with `include_usage` to mirror OpenAI's
request field.
- `core/http/endpoints/openai/chat.go` and `completion.go`: keep using
the `Usage` struct field as an in-process channel for the running
cumulative, but strip it before JSON marshalling. When the request
set `stream_options.include_usage: true`, emit a dedicated trailing
chunk with `"choices": []` and the populated usage (matching the
OpenAI spec and llama.cpp's server behavior).
- `chat_emit.go`: new `streamUsageTrailerJSON` helper; drop the
`usage` parameter from `buildNoActionFinalChunks` since chunks no
longer carry usage.
- Update `image.go`, `inpainting.go`, `edit.go` to wrap their Usage
values with `&` for the new pointer field.
- UI: send `stream_options:{include_usage:true}` from the React
(`useChat.js`) and legacy (`static/chat.js`) chat clients so the
token-count badge keeps populating now that the server is
spec-compliant.
Tests:
- New `chat_stream_usage_test.go` pins the spec invariants:
intermediate chunks have no `usage` key, the trailer JSON has
`"choices":[]` and a populated `usage`, and `OpenAIRequest` parses
`stream_options.include_usage`.
- Update `chat_emit_test.go` to reflect that finals no longer embed
usage.
Verified against the live LocalAI instance: before the fix Continue's
filter logic swallowed 16/16 token chunks; with the new shape it
yields 4/5 and routes usage through the dedicated trailer chunk.
Fixes #8546
Assisted-by: Claude:opus-4.7 [Claude Code]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
* fix(streaming): silence errcheck on usage trailer Fprintf
The new spec-compliant `stream_options.include_usage` trailer writes
were flagged by errcheck since they're new code (golangci-lint runs
new-from-merge-base on master); the surrounding `fmt.Fprintf` data:
writes are grandfathered. Drop the return values explicitly to match
the linter's contract without adding a nolint shim.
Assisted-by: Claude:opus-4.7 [Claude Code]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
---------
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>1 parent 6e1dbae commit 8af963b
11 files changed
Lines changed: 342 additions & 63 deletions
File tree
- core
- http
- endpoints/openai
- react-ui/src/hooks
- static
- schema
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
131 | 131 | | |
132 | 132 | | |
133 | 133 | | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
134 | 140 | | |
135 | 141 | | |
136 | 142 | | |
137 | 143 | | |
138 | 144 | | |
139 | 145 | | |
140 | | - | |
| 146 | + | |
141 | 147 | | |
142 | 148 | | |
143 | 149 | | |
| |||
164 | 170 | | |
165 | 171 | | |
166 | 172 | | |
167 | | - | |
| 173 | + | |
168 | 174 | | |
169 | 175 | | |
170 | 176 | | |
| |||
387 | 393 | | |
388 | 394 | | |
389 | 395 | | |
390 | | - | |
391 | | - | |
392 | | - | |
393 | | - | |
394 | | - | |
395 | | - | |
396 | | - | |
397 | | - | |
398 | | - | |
399 | | - | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
400 | 401 | | |
401 | 402 | | |
402 | 403 | | |
| |||
409 | 410 | | |
410 | 411 | | |
411 | 412 | | |
412 | | - | |
| 413 | + | |
413 | 414 | | |
414 | 415 | | |
415 | 416 | | |
| |||
724 | 725 | | |
725 | 726 | | |
726 | 727 | | |
727 | | - | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
728 | 735 | | |
729 | 736 | | |
730 | 737 | | |
| |||
740 | 747 | | |
741 | 748 | | |
742 | 749 | | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
743 | 755 | | |
744 | 756 | | |
745 | 757 | | |
| |||
888 | 900 | | |
889 | 901 | | |
890 | 902 | | |
| 903 | + | |
| 904 | + | |
| 905 | + | |
891 | 906 | | |
892 | 907 | | |
893 | 908 | | |
| |||
899 | 914 | | |
900 | 915 | | |
901 | 916 | | |
902 | | - | |
903 | 917 | | |
904 | 918 | | |
905 | | - | |
906 | 919 | | |
| 920 | + | |
| 921 | + | |
| 922 | + | |
| 923 | + | |
| 924 | + | |
| 925 | + | |
| 926 | + | |
| 927 | + | |
| 928 | + | |
907 | 929 | | |
908 | 930 | | |
909 | 931 | | |
| |||
1263 | 1285 | | |
1264 | 1286 | | |
1265 | 1287 | | |
1266 | | - | |
| 1288 | + | |
1267 | 1289 | | |
1268 | 1290 | | |
1269 | 1291 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
10 | 43 | | |
11 | 44 | | |
12 | 45 | | |
| |||
25 | 58 | | |
26 | 59 | | |
27 | 60 | | |
28 | | - | |
29 | | - | |
30 | | - | |
31 | | - | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
32 | 65 | | |
33 | 66 | | |
34 | 67 | | |
35 | 68 | | |
36 | 69 | | |
37 | 70 | | |
38 | 71 | | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
39 | 76 | | |
40 | 77 | | |
41 | 78 | | |
42 | 79 | | |
43 | 80 | | |
44 | 81 | | |
45 | 82 | | |
46 | | - | |
47 | 83 | | |
48 | 84 | | |
49 | 85 | | |
50 | 86 | | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
| 87 | + | |
| 88 | + | |
55 | 89 | | |
| 90 | + | |
56 | 91 | | |
57 | 92 | | |
58 | | - | |
59 | | - | |
60 | | - | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
61 | 98 | | |
62 | 99 | | |
63 | 100 | | |
64 | 101 | | |
65 | | - | |
| 102 | + | |
66 | 103 | | |
67 | 104 | | |
68 | 105 | | |
| |||
82 | 119 | | |
83 | 120 | | |
84 | 121 | | |
85 | | - | |
86 | 122 | | |
87 | 123 | | |
88 | 124 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
609 | 609 | | |
610 | 610 | | |
611 | 611 | | |
612 | | - | |
613 | 612 | | |
614 | | - | |
615 | | - | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
616 | 620 | | |
617 | 621 | | |
618 | 622 | | |
619 | | - | |
| 623 | + | |
620 | 624 | | |
621 | | - | |
622 | | - | |
623 | | - | |
624 | | - | |
625 | | - | |
626 | | - | |
| 625 | + | |
627 | 626 | | |
628 | 627 | | |
629 | 628 | | |
630 | 629 | | |
631 | 630 | | |
632 | 631 | | |
633 | | - | |
| 632 | + | |
634 | 633 | | |
635 | 634 | | |
636 | 635 | | |
637 | 636 | | |
638 | 637 | | |
639 | | - | |
| 638 | + | |
| 639 | + | |
640 | 640 | | |
641 | 641 | | |
642 | | - | |
| 642 | + | |
643 | 643 | | |
644 | 644 | | |
645 | 645 | | |
646 | | - | |
| 646 | + | |
647 | 647 | | |
648 | | - | |
649 | | - | |
650 | | - | |
| 648 | + | |
651 | 649 | | |
652 | 650 | | |
653 | 651 | | |
654 | | - | |
| 652 | + | |
655 | 653 | | |
656 | 654 | | |
657 | 655 | | |
658 | 656 | | |
659 | | - | |
| 657 | + | |
660 | 658 | | |
661 | 659 | | |
662 | 660 | | |
| |||
666 | 664 | | |
667 | 665 | | |
668 | 666 | | |
669 | | - | |
| 667 | + | |
670 | 668 | | |
671 | 669 | | |
672 | 670 | | |
673 | 671 | | |
674 | 672 | | |
675 | 673 | | |
676 | | - | |
| 674 | + | |
677 | 675 | | |
678 | 676 | | |
679 | 677 | | |
680 | 678 | | |
681 | 679 | | |
682 | 680 | | |
683 | 681 | | |
684 | | - | |
| 682 | + | |
685 | 683 | | |
686 | 684 | | |
687 | 685 | | |
688 | 686 | | |
689 | 687 | | |
690 | 688 | | |
691 | | - | |
| 689 | + | |
692 | 690 | | |
693 | 691 | | |
694 | 692 | | |
| |||
702 | 700 | | |
703 | 701 | | |
704 | 702 | | |
705 | | - | |
| 703 | + | |
706 | 704 | | |
707 | 705 | | |
708 | 706 | | |
| |||
0 commit comments