Repository navigation
Commit b213390
authored
feat: add SSE streaming support with billing extraction (#7)
* feat: add SSE streaming support with billing extraction
- Add streaming SSE parser for OpenAI and Anthropic responses
- Implement StreamingResponseExtractor interface for all providers
- Use http.ResponseController for efficient flushing
- Add simdjson-go for fast JSON parsing of usage data
- Auto-inject stream_options.include_usage for billing
- Extract token usage from streaming responses for cost calculation
- Add comprehensive unit tests for streaming functionality
* docs: add streaming support documentation
* fix: use exclusion list for stream_options injection, add Anthropic streaming tests
Replace whitelist-based provider switch with exclusion list approach for
stream_options injection. Providers with native streaming usage reporting
(Anthropic, Bedrock, Google AI) are excluded; all others automatically get
stream_options injected. This is more extensible for new providers.
Also adds comprehensive Anthropic streaming tests:
- TestParseAnthropicSSEEvent
- TestExtractUsageFromAnthropicEvent
- TestAnthropicSSEParser (full event stream)
- TestAutoRouter_AnthropicStreamingNoStreamOptions
* fix: improve test assertions in streaming_test.go
- Add error checking for ParseAnthropicSSEEvent calls to prevent panics
- Require strict nil result for expected-nil test cases
- Replace no-op _ = chunk.ID with real assertions for ID and content
* fix: address PR review findings
Critical fixes:
- ForwardStreaming now wraps upstream call with interceptor chain
- Use HTTP trailers for billing headers in streaming (fixes timing)
- SSE events now forwarded even when parsing fails
- stream_options merged instead of overwritten
Robustness improvements:
- Add nil guards in BillingCalculator.Calculate
- Increase SSEParser buffer to 1MB for large payloads
- FormatSSEEvent splits multi-line data correctly
- Add TotalTokens fallback in extractOpenAIStd
- Support Anthropic ephemeral cache token fields
Test improvements:
- Remove unused mockExtractor embedding
- Add cacheUsage nil assertion in tests
- Add error checking for ParseAnthropicSSEEvent calls
Dependency fix:
- Run go mod tidy to mark simdjson-go as direct dependency
* feat: implement true streaming for Bedrock and Google AI
Previously both providers buffered entire responses before forwarding to
client, defeating SSE streaming. Now:
Resolvers:
- Google AI: Use :streamGenerateContent?alt=sse for streaming
- Bedrock: Use /converse-stream for streaming
Streaming extractors:
- Google AI: Incremental SSE parsing, forward each event immediately
- Bedrock: Binary AWS event stream parsing, forward each message immediately
Tests:
- Verify incremental streaming (data arrives before stream completes)
- Test metadata extraction from streams
- Test resolver endpoint selection based on stream flag
All 100+ tests pass.
* fix: resolve data race in streaming tests
Use thread-safe response writer with channel-based signaling instead of
polling shared buffer. This eliminates the race condition where one
goroutine writes to the buffer while another reads its length.
- Added threadSafeResponseWriter with mutex-protected buffer
- Use channel to signal first write time (eliminates race on time.Time)
- Added Flush() method to satisfy http.Flusher interface
- Both bedrock and googleai streaming tests now pass with -race1 parent b1ae20e commit b213390
29 files changed
Lines changed: 4274 additions & 89 deletions
File tree
- examples/basic
- internal/fastjson
- providers
- anthropic
- azure
- bedrock
- googleai
- openai_compatible
- perplexity
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
394 | 394 | | |
395 | 395 | | |
396 | 396 | | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
397 | 529 | | |
398 | 530 | | |
399 | 531 | | |
| |||
884 | 1016 | | |
885 | 1017 | | |
886 | 1018 | | |
887 | | - | |
| 1019 | + | |
888 | 1020 | | |
| 1021 | + | |
889 | 1022 | | |
890 | 1023 | | |
891 | | - | |
| 1024 | + | |
892 | 1025 | | |
| 1026 | + | |
| 1027 | + | |
| 1028 | + | |
893 | 1029 | | |
894 | 1030 | | |
895 | 1031 | | |
896 | 1032 | | |
897 | 1033 | | |
898 | 1034 | | |
899 | 1035 | | |
| 1036 | + | |
900 | 1037 | | |
901 | 1038 | | |
902 | 1039 | | |
| |||
911 | 1048 | | |
912 | 1049 | | |
913 | 1050 | | |
| 1051 | + | |
914 | 1052 | | |
915 | 1053 | | |
| 1054 | + | |
916 | 1055 | | |
917 | 1056 | | |
| 1057 | + | |
918 | 1058 | | |
919 | 1059 | | |
920 | 1060 | | |
921 | 1061 | | |
| 1062 | + | |
922 | 1063 | | |
923 | 1064 | | |
924 | 1065 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
108 | 108 | | |
109 | 109 | | |
110 | 110 | | |
| 111 | + | |
111 | 112 | | |
112 | 113 | | |
113 | 114 | | |
| |||
153 | 154 | | |
154 | 155 | | |
155 | 156 | | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
156 | 188 | | |
157 | 189 | | |
158 | 190 | | |
| |||
0 commit comments