55namespace Cortex \LLM \Data ;
66
77use Generator ;
8- use DateTimeImmutable ;
98use Cortex \LLM \Enums \ChunkType ;
109use Cortex \LLM \Enums \FinishReason ;
1110use Cortex \Pipeline \RuntimeConfig ;
1211use Illuminate \Support \LazyCollection ;
13- use Cortex \LLM \Streaming \AgUiDataStream ;
14- use Cortex \LLM \Streaming \VercelDataStream ;
15- use Cortex \LLM \Streaming \VercelTextStream ;
1612use Cortex \Events \RuntimeConfigStreamChunk ;
17- use Cortex \LLM \Contracts \StreamingProtocol ;
18- use Cortex \LLM \Streaming \DefaultDataStream ;
1913use Cortex \LLM \Data \Messages \AssistantMessage ;
20- use Symfony \ Component \ HttpFoundation \ StreamedResponse ;
14+ use Cortex \ LLM \ Data \ Concerns \ HasStreamResponses ;
2115
2216/**
2317 * @extends LazyCollection<int, \Cortex\LLM\Data\ChatGenerationChunk>
2418 */
2519class ChatStreamResult extends LazyCollection
2620{
21+ use HasStreamResponses;
22+
2723 public function appendStreamBuffer (RuntimeConfig $ config ): self
2824 {
2925 return new self (function () use ($ config ): Generator {
@@ -34,7 +30,11 @@ public function appendStreamBuffer(RuntimeConfig $config): self
3430 // Drain items from the buffer and dispatch events for them
3531 if ($ config ->stream ->isNotEmpty ()) {
3632 foreach ($ config ->stream ->drain () as $ chunk ) {
37- $ shouldYieldBeforeEvent = $ chunk instanceof ChatGenerationChunk && ! $ chunk ->type ->isEnd ();
33+ if (! $ chunk instanceof ChatGenerationChunk) {
34+ continue ;
35+ }
36+
37+ $ shouldYieldBeforeEvent = ! $ chunk ->type ->isEnd ();
3838
3939 if ($ shouldYieldBeforeEvent ) {
4040 $ config ->dispatchEvent (
@@ -56,55 +56,6 @@ public function appendStreamBuffer(RuntimeConfig $config): self
5656 });
5757 }
5858
59- /**
60- * Create a streaming response using the Vercel AI SDK protocol.
61- */
62- public function streamResponse (): StreamedResponse
63- {
64- return $ this ->toStreamedResponse (new DefaultDataStream ());
65- }
66-
67- /**
68- * Create a plain text streaming response (Vercel AI SDK text format).
69- * Streams only the text content without any JSON encoding or metadata.
70- *
71- * @see https://sdk.vercel.ai/docs/ai-sdk-core/generating-text
72- */
73- public function vercelTextStreamResponse (): StreamedResponse
74- {
75- return $ this ->toStreamedResponse (new VercelTextStream ());
76- }
77-
78- public function vercelDataStreamResponse (): StreamedResponse
79- {
80- return $ this ->toStreamedResponse (new VercelDataStream ());
81- }
82-
83- /**
84- * Create a streaming response using the AG-UI protocol.
85- *
86- * @see https://docs.ag-ui.com/concepts/events.md
87- */
88- public function agUiStreamResponse (): StreamedResponse
89- {
90- return $ this ->toStreamedResponse (new AgUiDataStream ());
91- }
92-
93- /**
94- * Create a streaming response using a custom streaming protocol.
95- */
96- public function toStreamedResponse (StreamingProtocol $ protocol ): StreamedResponse
97- {
98- /** @var \Illuminate\Routing\ResponseFactory $responseFactory */
99- $ responseFactory = response ();
100-
101- return $ responseFactory ->stream ($ protocol ->streamResponse ($ this ), headers: [
102- 'Content-Type ' => 'text/event-stream ' ,
103- 'Cache-Control ' => 'no-cache ' ,
104- 'X-Accel-Buffering ' => 'no ' ,
105- ]);
106- }
107-
10859 public static function fake (?string $ string = null , ?ToolCallCollection $ toolCalls = null ): self
10960 {
11061 return new self (function () use ($ string , $ toolCalls ) {
@@ -120,7 +71,6 @@ public static function fake(?string $string = null, ?ToolCallCollection $toolCal
12071 type: ChunkType::TextDelta,
12172 id: 'fake- ' . $ index ,
12273 message: new AssistantMessage ($ chunk , $ toolCalls ),
123- createdAt: new DateTimeImmutable (),
12474 finishReason: $ isFinal ? FinishReason::Stop : null ,
12575 usage: new Usage (
12676 promptTokens: 0 ,
0 commit comments