66
77use DateTimeInterface ;
88use Cortex \LLM \Enums \ChunkType ;
9- use Cortex \LLM \Enums \MessageRole ;
109use Cortex \LLM \Enums \FinishReason ;
10+ use Illuminate \Contracts \Support \Arrayable ;
1111use Cortex \LLM \Data \Messages \AssistantMessage ;
1212
13- readonly class ChatGenerationChunk
13+ /**
14+ * @implements Arrayable<string, mixed>
15+ */
16+ readonly class ChatGenerationChunk implements Arrayable
1417{
15- public ?ChunkType $ type ;
16-
1718 public function __construct (
1819 public string $ id ,
1920 public AssistantMessage $ message ,
20- public int $ index ,
2121 public DateTimeInterface $ createdAt ,
22- ? ChunkType $ type = null ,
22+ public ChunkType $ type ,
2323 public ?FinishReason $ finishReason = null ,
2424 public ?Usage $ usage = null ,
2525 public string $ contentSoFar = '' ,
2626 public bool $ isFinal = false ,
2727 public mixed $ parsedOutput = null ,
2828 public ?string $ outputParserError = null ,
29- ) {
30- $ this ->type = $ type ?? $ this ->resolveType ();
31- }
32-
33- /**
34- * Fallback method to resolve chunk type when not explicitly provided by the LLM driver.
35- * This is a best-effort inference and may not be as accurate as driver-specific logic.
36- * Prefer having the LLM driver provide the chunk type explicitly when possible.
37- */
38- private function resolveType (): ChunkType
39- {
40- // Final chunks: Use finish reason to determine completion type
41- if ($ this ->finishReason !== null ) {
42- return match ($ this ->finishReason ) {
43- FinishReason::ToolCalls => ChunkType::ToolInputEnd,
44- default => ChunkType::Done,
45- };
46- }
47-
48- // First chunk: Assistant message with no accumulated content
49- if ($ this ->message ->role () === MessageRole::Assistant && $ this ->contentSoFar === '' ) {
50- return ChunkType::MessageStart;
51- }
52-
53- // Tool-related chunks: Has tool calls
54- if ($ this ->message ->toolCalls ?->isNotEmpty()) {
55- return ChunkType::ToolInputDelta;
56- }
57-
58- // Default: Treat as text delta
59- return ChunkType::TextDelta;
60- }
29+ ) {}
6130
6231 public function cloneWithParsedOutput (mixed $ parsedOutput ): self
6332 {
6433 return new self (
6534 $ this ->id ,
6635 $ this ->message ,
67- $ this ->index ,
6836 $ this ->createdAt ,
6937 $ this ->type ,
7038 $ this ->finishReason ,
@@ -75,4 +43,20 @@ public function cloneWithParsedOutput(mixed $parsedOutput): self
7543 $ this ->outputParserError ,
7644 );
7745 }
46+
47+ public function toArray (): array
48+ {
49+ return [
50+ 'id ' => $ this ->id ,
51+ 'type ' => $ this ->type ->value ,
52+ 'message ' => $ this ->message ->toArray (),
53+ 'finish_reason ' => $ this ->finishReason ?->value,
54+ 'usage ' => $ this ->usage ?->toArray(),
55+ 'content_so_far ' => $ this ->contentSoFar ,
56+ 'is_final ' => $ this ->isFinal ,
57+ 'parsed_output ' => $ this ->parsedOutput ,
58+ 'output_parser_error ' => $ this ->outputParserError ,
59+ 'created_at ' => $ this ->createdAt ,
60+ ];
61+ }
7862}
0 commit comments