grasp_agents 0.5.6__py3-none-any.whl → 0.5.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- grasp_agents/cloud_llm.py +11 -5
- grasp_agents/litellm/lite_llm.py +22 -1
- grasp_agents/llm.py +134 -1
- grasp_agents/llm_policy_executor.py +9 -3
- grasp_agents/openai/openai_llm.py +11 -3
- grasp_agents/packet_pool.py +23 -43
- grasp_agents/printer.py +75 -77
- grasp_agents/processors/parallel_processor.py +15 -13
- grasp_agents/runner.py +27 -24
- grasp_agents/typing/completion_chunk.py +302 -3
- grasp_agents/typing/events.py +256 -50
- {grasp_agents-0.5.6.dist-info → grasp_agents-0.5.9.dist-info}/METADATA +12 -13
- {grasp_agents-0.5.6.dist-info → grasp_agents-0.5.9.dist-info}/RECORD +15 -15
- {grasp_agents-0.5.6.dist-info → grasp_agents-0.5.9.dist-info}/WHEEL +0 -0
- {grasp_agents-0.5.6.dist-info → grasp_agents-0.5.9.dist-info}/licenses/LICENSE.md +0 -0
@@ -15,7 +15,7 @@ from openai.types.chat.chat_completion_chunk import (
|
|
15
15
|
from openai.types.chat.chat_completion_token_logprob import (
|
16
16
|
ChatCompletionTokenLogprob as OpenAITokenLogprob,
|
17
17
|
)
|
18
|
-
from pydantic import BaseModel, Field
|
18
|
+
from pydantic import BaseModel, Field, ValidationError, field_validator
|
19
19
|
|
20
20
|
from ..errors import CombineCompletionChunksError
|
21
21
|
from .completion import Completion, CompletionChoice, FinishReason, Usage
|
@@ -38,13 +38,77 @@ class CompletionChunkDeltaToolCall(BaseModel):
|
|
38
38
|
class CompletionChunkChoiceDelta(BaseModel):
|
39
39
|
content: str | None = None
|
40
40
|
refusal: str | None = None
|
41
|
-
role: Role | None
|
42
|
-
tool_calls: list[CompletionChunkDeltaToolCall] | None
|
41
|
+
role: Role | None = None
|
42
|
+
tool_calls: list[CompletionChunkDeltaToolCall] | None = None
|
43
43
|
reasoning_content: str | None = None
|
44
44
|
thinking_blocks: list[ThinkingBlock | RedactedThinkingBlock] | None = None
|
45
45
|
annotations: list[LiteLLMAnnotation] | None = None
|
46
46
|
provider_specific_fields: dict[str, Any] | None = None
|
47
47
|
|
48
|
+
@property
|
49
|
+
def thinking_delta(self) -> "CompletionChunkChoiceDelta | None":
|
50
|
+
return (
|
51
|
+
CompletionChunkChoiceDelta(
|
52
|
+
reasoning_content=self.reasoning_content,
|
53
|
+
thinking_blocks=self.thinking_blocks,
|
54
|
+
role=self.role,
|
55
|
+
provider_specific_fields=self.provider_specific_fields,
|
56
|
+
)
|
57
|
+
if self.reasoning_content or self.thinking_blocks
|
58
|
+
else None
|
59
|
+
)
|
60
|
+
|
61
|
+
@property
|
62
|
+
def tool_call_deltas(self) -> "list[CompletionChunkChoiceDelta] | None":
|
63
|
+
return (
|
64
|
+
[
|
65
|
+
CompletionChunkChoiceDelta(
|
66
|
+
tool_calls=[tool_call],
|
67
|
+
role=self.role,
|
68
|
+
provider_specific_fields=self.provider_specific_fields,
|
69
|
+
)
|
70
|
+
for tool_call in self.tool_calls
|
71
|
+
]
|
72
|
+
if self.tool_calls
|
73
|
+
else None
|
74
|
+
)
|
75
|
+
|
76
|
+
@property
|
77
|
+
def response_delta(self) -> "CompletionChunkChoiceDelta | None":
|
78
|
+
return (
|
79
|
+
CompletionChunkChoiceDelta(
|
80
|
+
content=self.content,
|
81
|
+
role=self.role,
|
82
|
+
provider_specific_fields=self.provider_specific_fields,
|
83
|
+
)
|
84
|
+
if self.content
|
85
|
+
else None
|
86
|
+
)
|
87
|
+
|
88
|
+
@property
|
89
|
+
def annotations_delta(self) -> "CompletionChunkChoiceDelta | None":
|
90
|
+
return (
|
91
|
+
CompletionChunkChoiceDelta(
|
92
|
+
annotations=self.annotations,
|
93
|
+
role=self.role,
|
94
|
+
provider_specific_fields=self.provider_specific_fields,
|
95
|
+
)
|
96
|
+
if self.annotations
|
97
|
+
else None
|
98
|
+
)
|
99
|
+
|
100
|
+
@property
|
101
|
+
def refusal_delta(self) -> "CompletionChunkChoiceDelta | None":
|
102
|
+
return (
|
103
|
+
CompletionChunkChoiceDelta(
|
104
|
+
refusal=self.refusal,
|
105
|
+
role=self.role,
|
106
|
+
provider_specific_fields=self.provider_specific_fields,
|
107
|
+
)
|
108
|
+
if self.refusal
|
109
|
+
else None
|
110
|
+
)
|
111
|
+
|
48
112
|
|
49
113
|
class CompletionChunkChoice(BaseModel):
|
50
114
|
delta: CompletionChunkChoiceDelta
|
@@ -66,6 +130,241 @@ class CompletionChunk(BaseModel):
|
|
66
130
|
response_ms: float | None = None
|
67
131
|
hidden_params: dict[str, Any] | None = None
|
68
132
|
|
133
|
+
def split_into_specialized(
|
134
|
+
self,
|
135
|
+
) -> "list[CompletionChunk]":
|
136
|
+
if len(self.choices) != 1:
|
137
|
+
raise ValidationError(
|
138
|
+
"CompletionChunk must have exactly one choice for specialization."
|
139
|
+
)
|
140
|
+
delta = self.choices[0].delta
|
141
|
+
|
142
|
+
specialized_chunks: list[CompletionChunk] = []
|
143
|
+
|
144
|
+
thinking_delta = delta.thinking_delta
|
145
|
+
tool_call_deltas = delta.tool_call_deltas
|
146
|
+
response_delta = delta.response_delta
|
147
|
+
annotations_delta = delta.annotations_delta
|
148
|
+
refusal_delta = delta.refusal_delta
|
149
|
+
|
150
|
+
if thinking_delta is not None:
|
151
|
+
new_choice = self.choices[0].model_copy(update={"delta": thinking_delta})
|
152
|
+
new_chunk = self.model_copy(update={"choices": [new_choice]})
|
153
|
+
specialized_chunks.append(
|
154
|
+
ThinkingChunk.model_validate(new_chunk.model_dump())
|
155
|
+
)
|
156
|
+
|
157
|
+
if tool_call_deltas:
|
158
|
+
for delta_tool_call in tool_call_deltas:
|
159
|
+
new_choice = self.choices[0].model_copy(
|
160
|
+
update={"delta": delta_tool_call}
|
161
|
+
)
|
162
|
+
new_chunk = self.model_copy(update={"choices": [new_choice]})
|
163
|
+
specialized_chunks.append(
|
164
|
+
ToolCallChunk.model_validate(new_chunk.model_dump())
|
165
|
+
)
|
166
|
+
|
167
|
+
if response_delta is not None:
|
168
|
+
new_choice = self.choices[0].model_copy(update={"delta": response_delta})
|
169
|
+
new_chunk = self.model_copy(update={"choices": [new_choice]})
|
170
|
+
specialized_chunks.append(
|
171
|
+
ResponseChunk.model_validate(new_chunk.model_dump())
|
172
|
+
)
|
173
|
+
|
174
|
+
if annotations_delta is not None:
|
175
|
+
new_choice = self.choices[0].model_copy(update={"delta": annotations_delta})
|
176
|
+
new_chunk = self.model_copy(update={"choices": [new_choice]})
|
177
|
+
specialized_chunks.append(
|
178
|
+
AnnotationsChunk.model_validate(new_chunk.model_dump())
|
179
|
+
)
|
180
|
+
|
181
|
+
if refusal_delta is not None:
|
182
|
+
new_choice = self.choices[0].model_copy(update={"delta": refusal_delta})
|
183
|
+
new_chunk = self.model_copy(update={"choices": [new_choice]})
|
184
|
+
specialized_chunks.append(
|
185
|
+
RefusalChunk.model_validate(new_chunk.model_dump())
|
186
|
+
)
|
187
|
+
|
188
|
+
return specialized_chunks
|
189
|
+
|
190
|
+
|
191
|
+
class ResponseChunk(CompletionChunk):
|
192
|
+
@field_validator("choices")
|
193
|
+
@classmethod
|
194
|
+
def validate_response_chunk(
|
195
|
+
cls, choices: list[CompletionChunkChoice]
|
196
|
+
) -> list[CompletionChunkChoice]:
|
197
|
+
if len(choices) != 1:
|
198
|
+
raise ValidationError("ResponseChunk must have exactly one choice.")
|
199
|
+
|
200
|
+
delta = choices[0].delta
|
201
|
+
|
202
|
+
if not delta.content:
|
203
|
+
raise ValidationError("ResponseChunk must have content in deltas.")
|
204
|
+
|
205
|
+
if (
|
206
|
+
delta.reasoning_content is not None
|
207
|
+
or delta.thinking_blocks is not None
|
208
|
+
or delta.tool_calls is not None
|
209
|
+
or delta.refusal is not None
|
210
|
+
or delta.annotations is not None
|
211
|
+
):
|
212
|
+
raise ValidationError(
|
213
|
+
"ResponseChunk should not have reasoning content, thinking blocks, "
|
214
|
+
"tool calls, refusal, or annotations in deltas."
|
215
|
+
)
|
216
|
+
|
217
|
+
return choices
|
218
|
+
|
219
|
+
@property
|
220
|
+
def response(self) -> str:
|
221
|
+
assert self.choices[0].delta.content
|
222
|
+
return self.choices[0].delta.content
|
223
|
+
|
224
|
+
|
225
|
+
class ThinkingChunk(CompletionChunk):
|
226
|
+
@field_validator("choices")
|
227
|
+
@classmethod
|
228
|
+
def validate_thinking_chunk(
|
229
|
+
cls, choices: list[CompletionChunkChoice]
|
230
|
+
) -> list[CompletionChunkChoice]:
|
231
|
+
if len(choices) != 1:
|
232
|
+
raise ValidationError("ThinkingChunk must have exactly one choice.")
|
233
|
+
|
234
|
+
delta = choices[0].delta
|
235
|
+
|
236
|
+
if not (delta.thinking_blocks or delta.reasoning_content):
|
237
|
+
raise ValidationError(
|
238
|
+
"ThinkingChunk must have reasoning content or "
|
239
|
+
"at least one thinking block."
|
240
|
+
)
|
241
|
+
if (
|
242
|
+
delta.content is not None
|
243
|
+
or delta.tool_calls is not None
|
244
|
+
or delta.refusal is not None
|
245
|
+
or delta.annotations is not None
|
246
|
+
):
|
247
|
+
raise ValidationError(
|
248
|
+
"ThinkingChunk should not have content, tool calls, "
|
249
|
+
"refusal, or annotations in deltas."
|
250
|
+
)
|
251
|
+
|
252
|
+
return choices
|
253
|
+
|
254
|
+
@property
|
255
|
+
def thinking(self) -> str | list[ThinkingBlock | RedactedThinkingBlock]:
|
256
|
+
delta = self.choices[0].delta
|
257
|
+
if delta.reasoning_content:
|
258
|
+
return delta.reasoning_content
|
259
|
+
if delta.thinking_blocks:
|
260
|
+
return delta.thinking_blocks
|
261
|
+
raise ValueError("ThinkingChunk has no reasoning_content or thinking_blocks")
|
262
|
+
|
263
|
+
|
264
|
+
class ToolCallChunk(CompletionChunk):
|
265
|
+
@field_validator("choices")
|
266
|
+
@classmethod
|
267
|
+
def validate_tool_call_chunk(
|
268
|
+
cls, choices: list[CompletionChunkChoice]
|
269
|
+
) -> list[CompletionChunkChoice]:
|
270
|
+
if len(choices) != 1:
|
271
|
+
raise ValidationError("ToolCallChunk must have exactly one choice.")
|
272
|
+
|
273
|
+
delta = choices[0].delta
|
274
|
+
|
275
|
+
if not delta.tool_calls:
|
276
|
+
raise ValidationError("ToolCallChunk must have tool calls in deltas.")
|
277
|
+
if len(delta.tool_calls) != 1:
|
278
|
+
raise ValidationError(
|
279
|
+
"ToolCallChunk must have exactly one tool call in deltas."
|
280
|
+
)
|
281
|
+
|
282
|
+
if (
|
283
|
+
delta.reasoning_content is not None
|
284
|
+
or delta.thinking_blocks is not None
|
285
|
+
or delta.content is not None
|
286
|
+
or delta.refusal is not None
|
287
|
+
or delta.annotations is not None
|
288
|
+
):
|
289
|
+
raise ValidationError(
|
290
|
+
"ToolCallChunk should not have reasoning content, thinking blocks, "
|
291
|
+
"content, refusal, or annotations in deltas."
|
292
|
+
)
|
293
|
+
|
294
|
+
return choices
|
295
|
+
|
296
|
+
@property
|
297
|
+
def tool_call(self) -> CompletionChunkDeltaToolCall:
|
298
|
+
assert self.choices[0].delta.tool_calls is not None
|
299
|
+
return self.choices[0].delta.tool_calls[0]
|
300
|
+
|
301
|
+
|
302
|
+
class AnnotationsChunk(CompletionChunk):
|
303
|
+
@field_validator("choices")
|
304
|
+
@classmethod
|
305
|
+
def validate_annotations_chunk(
|
306
|
+
cls, choices: list[CompletionChunkChoice]
|
307
|
+
) -> list[CompletionChunkChoice]:
|
308
|
+
if len(choices) != 1:
|
309
|
+
raise ValidationError("AnnotationsChunk must have exactly one choice.")
|
310
|
+
|
311
|
+
delta = choices[0].delta
|
312
|
+
|
313
|
+
if not delta.annotations:
|
314
|
+
raise ValidationError("AnnotationsChunk must have annotations in deltas.")
|
315
|
+
|
316
|
+
if (
|
317
|
+
delta.reasoning_content is not None
|
318
|
+
or delta.thinking_blocks is not None
|
319
|
+
or delta.content is not None
|
320
|
+
or delta.tool_calls is not None
|
321
|
+
or delta.refusal is not None
|
322
|
+
):
|
323
|
+
raise ValidationError(
|
324
|
+
"AnnotationsChunk should not have reasoning content, thinking blocks, "
|
325
|
+
"content, tool calls, or refusal in deltas."
|
326
|
+
)
|
327
|
+
|
328
|
+
return choices
|
329
|
+
|
330
|
+
@property
|
331
|
+
def annotations(self) -> list[LiteLLMAnnotation]:
|
332
|
+
assert self.choices[0].delta.annotations is not None
|
333
|
+
return self.choices[0].delta.annotations
|
334
|
+
|
335
|
+
|
336
|
+
class RefusalChunk(CompletionChunk):
|
337
|
+
@field_validator("choices")
|
338
|
+
@classmethod
|
339
|
+
def validate_refusal_chunk(
|
340
|
+
cls, choices: list[CompletionChunkChoice]
|
341
|
+
) -> list[CompletionChunkChoice]:
|
342
|
+
if len(choices) != 1:
|
343
|
+
raise ValidationError("RefusalChunk must have exactly one choice.")
|
344
|
+
|
345
|
+
delta = choices[0].delta
|
346
|
+
|
347
|
+
if not delta.refusal:
|
348
|
+
raise ValidationError("RefusalChunk must have refusal in deltas.")
|
349
|
+
|
350
|
+
if (
|
351
|
+
delta.reasoning_content is not None
|
352
|
+
or delta.thinking_blocks is not None
|
353
|
+
or delta.content is not None
|
354
|
+
or delta.tool_calls is not None
|
355
|
+
or delta.annotations is not None
|
356
|
+
):
|
357
|
+
raise ValidationError(
|
358
|
+
"RefusalChunk should not have reasoning content, thinking blocks, "
|
359
|
+
"content, tool calls, or annotations in deltas."
|
360
|
+
)
|
361
|
+
|
362
|
+
return choices
|
363
|
+
|
364
|
+
@property
|
365
|
+
def refusal(self) -> str | None:
|
366
|
+
return self.choices[0].delta.refusal
|
367
|
+
|
69
368
|
|
70
369
|
def combine_completion_chunks(chunks: list[CompletionChunk]) -> Completion:
|
71
370
|
if not chunks:
|
grasp_agents/typing/events.py
CHANGED
@@ -1,14 +1,28 @@
|
|
1
1
|
import time
|
2
2
|
from enum import StrEnum
|
3
|
-
from typing import Any, Generic, Literal, TypeVar
|
3
|
+
from typing import Any, Generic, Literal, TypeVar, get_args
|
4
4
|
from uuid import uuid4
|
5
5
|
|
6
6
|
from pydantic import BaseModel, ConfigDict, Field
|
7
7
|
|
8
8
|
from ..packet import Packet
|
9
9
|
from .completion import Completion
|
10
|
-
from .completion_chunk import
|
11
|
-
|
10
|
+
from .completion_chunk import (
|
11
|
+
AnnotationsChunk,
|
12
|
+
CompletionChunk,
|
13
|
+
RefusalChunk,
|
14
|
+
ResponseChunk,
|
15
|
+
ThinkingChunk,
|
16
|
+
ToolCallChunk,
|
17
|
+
)
|
18
|
+
from .message import (
|
19
|
+
AssistantMessage,
|
20
|
+
MessageBase,
|
21
|
+
SystemMessage,
|
22
|
+
ToolCall,
|
23
|
+
ToolMessage,
|
24
|
+
UserMessage,
|
25
|
+
)
|
12
26
|
|
13
27
|
|
14
28
|
class EventSourceType(StrEnum):
|
@@ -29,73 +43,68 @@ class EventType(StrEnum):
|
|
29
43
|
GEN_MSG = "gen_message"
|
30
44
|
|
31
45
|
COMP = "completion"
|
46
|
+
COMP_START = "completion_start"
|
47
|
+
COMP_END = "completion_end"
|
48
|
+
|
32
49
|
COMP_CHUNK = "completion_chunk"
|
50
|
+
THINK_CHUNK = "thinking_chunk"
|
51
|
+
RESP_CHUNK = "response_chunk"
|
52
|
+
TOOL_CALL_CHUNK = "tool_call_chunk"
|
53
|
+
ANNOT_CHUNK = "annotations_chunk"
|
54
|
+
REFUSAL_CHUNK = "refusal_chunk"
|
55
|
+
|
56
|
+
RESP_START = "response_start"
|
57
|
+
RESP_END = "response_end"
|
58
|
+
THINK_START = "thinking_start"
|
59
|
+
THINK_END = "thinking_end"
|
60
|
+
TOOL_CALL_START = "tool_call_start"
|
61
|
+
TOOL_CALL_END = "tool_call_end"
|
62
|
+
ANNOT_START = "annotations_start"
|
63
|
+
ANNOT_END = "annotations_end"
|
64
|
+
|
33
65
|
LLM_ERR = "llm_error"
|
34
66
|
|
35
|
-
PROC_START = "processor_start"
|
36
67
|
PACKET_OUT = "packet_output"
|
37
68
|
PAYLOAD_OUT = "payload_output"
|
38
69
|
PROC_FINISH = "processor_finish"
|
39
70
|
PROC_ERR = "processor_error"
|
71
|
+
PROC_START = "processor_start"
|
72
|
+
PROC_END = "processor_end"
|
40
73
|
|
41
74
|
WORKFLOW_RES = "workflow_result"
|
42
75
|
RUN_RES = "run_result"
|
43
76
|
|
44
|
-
# COMP_THINK_CHUNK = "completion_thinking_chunk"
|
45
|
-
# COMP_RESP_CHUNK = "completion_response_chunk"
|
46
77
|
|
78
|
+
_T_co = TypeVar("_T_co", covariant=True)
|
79
|
+
_M_co = TypeVar("_M_co", covariant=True, bound=MessageBase)
|
80
|
+
_C_co = TypeVar("_C_co", covariant=True, bound=CompletionChunk)
|
47
81
|
|
48
|
-
_T = TypeVar("_T")
|
49
82
|
|
50
|
-
|
51
|
-
class Event(BaseModel, Generic[_T], frozen=True):
|
83
|
+
class Event(BaseModel, Generic[_T_co], frozen=True):
|
52
84
|
type: EventType
|
53
85
|
source: EventSourceType
|
54
86
|
id: str = Field(default_factory=lambda: str(uuid4()))
|
55
87
|
created: int = Field(default_factory=lambda: int(time.time()))
|
56
88
|
proc_name: str | None = None
|
57
89
|
call_id: str | None = None
|
58
|
-
data:
|
90
|
+
data: _T_co
|
91
|
+
|
59
92
|
|
60
93
|
class DummyEvent(Event[Any], frozen=True):
|
61
94
|
type: Literal[EventType.PAYLOAD_OUT] = EventType.PAYLOAD_OUT
|
62
95
|
source: Literal[EventSourceType.PROC] = EventSourceType.PROC
|
63
96
|
data: Any = None
|
64
97
|
|
65
|
-
class CompletionEvent(Event[Completion], frozen=True):
|
66
|
-
type: Literal[EventType.COMP] = EventType.COMP
|
67
|
-
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
68
|
-
|
69
98
|
|
70
|
-
|
71
|
-
type: Literal[EventType.COMP_CHUNK] = EventType.COMP_CHUNK
|
72
|
-
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
99
|
+
# Non-streamed completion events
|
73
100
|
|
74
101
|
|
75
|
-
class
|
76
|
-
|
77
|
-
model_name: str | None = None
|
78
|
-
model_id: str | None = None
|
79
|
-
|
80
|
-
model_config = ConfigDict(arbitrary_types_allowed=True)
|
81
|
-
|
82
|
-
|
83
|
-
class LLMStreamingErrorEvent(Event[LLMStreamingErrorData], frozen=True):
|
84
|
-
type: Literal[EventType.LLM_ERR] = EventType.LLM_ERR
|
102
|
+
class CompletionEvent(Event[Completion], frozen=True):
|
103
|
+
type: Literal[EventType.COMP] = EventType.COMP
|
85
104
|
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
86
105
|
|
87
106
|
|
88
|
-
|
89
|
-
# type: Literal[EventType.COMP_THINK_CHUNK] = EventType.COMP_THINK_CHUNK
|
90
|
-
# source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
91
|
-
|
92
|
-
|
93
|
-
# class CompletionResponseChunkEvent(Event[CompletionChunk], frozen=True):
|
94
|
-
# type: Literal[EventType.COMP_RESP_CHUNK] = EventType.COMP_RESP_CHUNK
|
95
|
-
# source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
96
|
-
|
97
|
-
|
98
|
-
class MessageEvent(Event[_T], Generic[_T], frozen=True):
|
107
|
+
class MessageEvent(Event[_M_co], Generic[_M_co], frozen=True):
|
99
108
|
pass
|
100
109
|
|
101
110
|
|
@@ -124,13 +133,207 @@ class ToolCallEvent(Event[ToolCall], frozen=True):
|
|
124
133
|
source: Literal[EventSourceType.AGENT] = EventSourceType.AGENT
|
125
134
|
|
126
135
|
|
136
|
+
# Streamed completion events
|
137
|
+
|
138
|
+
StreamedCompletionEventTypes = Literal[
|
139
|
+
EventType.COMP_CHUNK,
|
140
|
+
EventType.COMP_START,
|
141
|
+
EventType.COMP_END,
|
142
|
+
EventType.RESP_CHUNK,
|
143
|
+
EventType.RESP_START,
|
144
|
+
EventType.RESP_END,
|
145
|
+
EventType.THINK_CHUNK,
|
146
|
+
EventType.THINK_START,
|
147
|
+
EventType.THINK_END,
|
148
|
+
EventType.TOOL_CALL_CHUNK,
|
149
|
+
EventType.TOOL_CALL_START,
|
150
|
+
EventType.TOOL_CALL_END,
|
151
|
+
EventType.ANNOT_CHUNK,
|
152
|
+
EventType.ANNOT_START,
|
153
|
+
EventType.ANNOT_END,
|
154
|
+
EventType.REFUSAL_CHUNK,
|
155
|
+
]
|
156
|
+
|
157
|
+
|
158
|
+
class CompletionChunkEvent(Event[_C_co], Generic[_C_co], frozen=True):
|
159
|
+
type: StreamedCompletionEventTypes = EventType.COMP_CHUNK
|
160
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
161
|
+
data: _C_co
|
162
|
+
|
163
|
+
def split_into_specialized(
|
164
|
+
self,
|
165
|
+
) -> "list[CompletionChunkEvent[Any]]":
|
166
|
+
specialized_events: list[CompletionChunkEvent[Any]] = []
|
167
|
+
|
168
|
+
for sub_chunk in self.data.split_into_specialized():
|
169
|
+
if isinstance(sub_chunk, ResponseChunk):
|
170
|
+
new_event = self.model_copy(
|
171
|
+
update={"data": sub_chunk, "type": EventType.RESP_CHUNK}
|
172
|
+
)
|
173
|
+
specialized_events.append(
|
174
|
+
ResponseChunkEvent.model_validate(new_event.model_dump())
|
175
|
+
)
|
176
|
+
if isinstance(sub_chunk, ThinkingChunk):
|
177
|
+
new_event = self.model_copy(
|
178
|
+
update={"data": sub_chunk, "type": EventType.THINK_CHUNK}
|
179
|
+
)
|
180
|
+
specialized_events.append(
|
181
|
+
ThinkingChunkEvent.model_validate(new_event.model_dump())
|
182
|
+
)
|
183
|
+
if isinstance(sub_chunk, ToolCallChunk):
|
184
|
+
new_event = self.model_copy(
|
185
|
+
update={"data": sub_chunk, "type": EventType.TOOL_CALL_CHUNK}
|
186
|
+
)
|
187
|
+
specialized_events.append(
|
188
|
+
ToolCallChunkEvent.model_validate(new_event.model_dump())
|
189
|
+
)
|
190
|
+
if isinstance(sub_chunk, AnnotationsChunk):
|
191
|
+
new_event = self.model_copy(
|
192
|
+
update={"data": sub_chunk, "type": EventType.ANNOT_CHUNK}
|
193
|
+
)
|
194
|
+
specialized_events.append(
|
195
|
+
AnnotationsChunkEvent.model_validate(new_event.model_dump())
|
196
|
+
)
|
197
|
+
if isinstance(sub_chunk, RefusalChunk):
|
198
|
+
new_event = self.model_copy(
|
199
|
+
update={"data": sub_chunk, "type": EventType.REFUSAL_CHUNK}
|
200
|
+
)
|
201
|
+
specialized_events.append(
|
202
|
+
RefusalChunkEvent.model_validate(new_event.model_dump())
|
203
|
+
)
|
204
|
+
|
205
|
+
return specialized_events
|
206
|
+
|
207
|
+
|
208
|
+
class ResponseChunkEvent(CompletionChunkEvent[ResponseChunk], frozen=True):
|
209
|
+
type: Literal[EventType.RESP_CHUNK] = EventType.RESP_CHUNK
|
210
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
211
|
+
|
212
|
+
|
213
|
+
class ThinkingChunkEvent(CompletionChunkEvent[ThinkingChunk], frozen=True):
|
214
|
+
type: Literal[EventType.THINK_CHUNK] = EventType.THINK_CHUNK
|
215
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
216
|
+
|
217
|
+
|
218
|
+
class ToolCallChunkEvent(CompletionChunkEvent[ToolCallChunk], frozen=True):
|
219
|
+
type: Literal[EventType.TOOL_CALL_CHUNK] = EventType.TOOL_CALL_CHUNK
|
220
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
221
|
+
|
222
|
+
|
223
|
+
class AnnotationsChunkEvent(CompletionChunkEvent[AnnotationsChunk], frozen=True):
|
224
|
+
type: Literal[EventType.ANNOT_CHUNK] = EventType.ANNOT_CHUNK
|
225
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
226
|
+
|
227
|
+
|
228
|
+
class RefusalChunkEvent(CompletionChunkEvent[RefusalChunk], frozen=True):
|
229
|
+
type: Literal[EventType.REFUSAL_CHUNK] = EventType.REFUSAL_CHUNK
|
230
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
231
|
+
|
232
|
+
|
233
|
+
START_END_MAP: dict[EventType, list[EventType]] = {
|
234
|
+
EventType.COMP_CHUNK: [EventType.COMP_START, EventType.COMP_END],
|
235
|
+
EventType.RESP_CHUNK: [EventType.RESP_START, EventType.RESP_END],
|
236
|
+
EventType.THINK_CHUNK: [EventType.THINK_START, EventType.THINK_END],
|
237
|
+
EventType.TOOL_CALL_CHUNK: [EventType.TOOL_CALL_START, EventType.TOOL_CALL_END],
|
238
|
+
EventType.ANNOT_CHUNK: [EventType.ANNOT_START, EventType.ANNOT_END],
|
239
|
+
}
|
240
|
+
|
241
|
+
|
242
|
+
class LLMStateChangeEvent(CompletionChunkEvent[_C_co], Generic[_C_co], frozen=True):
|
243
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
244
|
+
start: bool = True
|
245
|
+
|
246
|
+
@classmethod
|
247
|
+
def from_chunk_event(
|
248
|
+
cls, event: CompletionChunkEvent[CompletionChunk]
|
249
|
+
) -> "LLMStateChangeEvent[_C_co]":
|
250
|
+
_type = get_args(cls.model_fields["type"].annotation)[0]
|
251
|
+
return cls(**event.model_copy(update={"type": _type}).model_dump())
|
252
|
+
|
253
|
+
|
254
|
+
class CompletionStartEvent(LLMStateChangeEvent[CompletionChunk], frozen=True):
|
255
|
+
type: Literal[EventType.COMP_START] = EventType.COMP_START
|
256
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
257
|
+
start: Literal[True] = True
|
258
|
+
|
259
|
+
|
260
|
+
class CompletionEndEvent(LLMStateChangeEvent[CompletionChunk], frozen=True):
|
261
|
+
type: Literal[EventType.COMP_END] = EventType.COMP_END
|
262
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
263
|
+
start: Literal[False] = False
|
264
|
+
|
265
|
+
|
266
|
+
class ResponseStartEvent(LLMStateChangeEvent[ResponseChunk], frozen=True):
|
267
|
+
type: Literal[EventType.RESP_START] = EventType.RESP_START
|
268
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
269
|
+
start: Literal[True] = True
|
270
|
+
|
271
|
+
|
272
|
+
class ResponseEndEvent(LLMStateChangeEvent[ResponseChunk], frozen=True):
|
273
|
+
type: Literal[EventType.RESP_END] = EventType.RESP_END
|
274
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
275
|
+
start: Literal[False] = False
|
276
|
+
|
277
|
+
|
278
|
+
class ThinkingStartEvent(LLMStateChangeEvent[ThinkingChunk], frozen=True):
|
279
|
+
type: Literal[EventType.THINK_START] = EventType.THINK_START
|
280
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
281
|
+
start: Literal[True] = True
|
282
|
+
|
283
|
+
|
284
|
+
class ThinkingEndEvent(LLMStateChangeEvent[ThinkingChunk], frozen=True):
|
285
|
+
type: Literal[EventType.THINK_END] = EventType.THINK_END
|
286
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
287
|
+
start: Literal[False] = False
|
288
|
+
|
289
|
+
|
290
|
+
class ToolCallStartEvent(LLMStateChangeEvent[ToolCallChunk], frozen=True):
|
291
|
+
type: Literal[EventType.TOOL_CALL_START] = EventType.TOOL_CALL_START
|
292
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
293
|
+
start: Literal[True] = True
|
294
|
+
|
295
|
+
|
296
|
+
class ToolCallEndEvent(LLMStateChangeEvent[ToolCallChunk], frozen=True):
|
297
|
+
type: Literal[EventType.TOOL_CALL_END] = EventType.TOOL_CALL_END
|
298
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
299
|
+
start: Literal[False] = False
|
300
|
+
|
301
|
+
|
302
|
+
class AnnotationsStartEvent(LLMStateChangeEvent[AnnotationsChunk], frozen=True):
|
303
|
+
type: Literal[EventType.ANNOT_START] = EventType.ANNOT_START
|
304
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
305
|
+
start: Literal[True] = True
|
306
|
+
|
307
|
+
|
308
|
+
class AnnotationsEndEvent(LLMStateChangeEvent[AnnotationsChunk], frozen=True):
|
309
|
+
type: Literal[EventType.ANNOT_END] = EventType.ANNOT_END
|
310
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
311
|
+
start: Literal[False] = False
|
312
|
+
|
313
|
+
|
314
|
+
class LLMStreamingErrorData(BaseModel):
|
315
|
+
error: Exception
|
316
|
+
model_name: str | None = None
|
317
|
+
model_id: str | None = None
|
318
|
+
|
319
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
320
|
+
|
321
|
+
|
322
|
+
class LLMStreamingErrorEvent(Event[LLMStreamingErrorData], frozen=True):
|
323
|
+
type: Literal[EventType.LLM_ERR] = EventType.LLM_ERR
|
324
|
+
source: Literal[EventSourceType.LLM] = EventSourceType.LLM
|
325
|
+
|
326
|
+
|
327
|
+
# Processor events
|
328
|
+
|
329
|
+
|
127
330
|
class ProcStartEvent(Event[None], frozen=True):
|
128
331
|
type: Literal[EventType.PROC_START] = EventType.PROC_START
|
129
332
|
source: Literal[EventSourceType.PROC] = EventSourceType.PROC
|
130
333
|
|
131
334
|
|
132
|
-
class
|
133
|
-
type: Literal[EventType.
|
335
|
+
class ProcEndEvent(Event[None], frozen=True):
|
336
|
+
type: Literal[EventType.PROC_END] = EventType.PROC_END
|
134
337
|
source: Literal[EventSourceType.PROC] = EventSourceType.PROC
|
135
338
|
|
136
339
|
|
@@ -148,16 +351,6 @@ class ProcPacketOutputEvent(Event[Packet[Any]], frozen=True):
|
|
148
351
|
] = EventSourceType.PROC
|
149
352
|
|
150
353
|
|
151
|
-
class WorkflowResultEvent(ProcPacketOutputEvent, frozen=True):
|
152
|
-
type: Literal[EventType.WORKFLOW_RES] = EventType.WORKFLOW_RES
|
153
|
-
source: Literal[EventSourceType.WORKFLOW] = EventSourceType.WORKFLOW
|
154
|
-
|
155
|
-
|
156
|
-
class RunResultEvent(ProcPacketOutputEvent, frozen=True):
|
157
|
-
type: Literal[EventType.RUN_RES] = EventType.RUN_RES
|
158
|
-
source: Literal[EventSourceType.RUN] = EventSourceType.RUN
|
159
|
-
|
160
|
-
|
161
354
|
class ProcStreamingErrorData(BaseModel):
|
162
355
|
error: Exception
|
163
356
|
call_id: str | None = None
|
@@ -168,3 +361,16 @@ class ProcStreamingErrorData(BaseModel):
|
|
168
361
|
class ProcStreamingErrorEvent(Event[ProcStreamingErrorData], frozen=True):
|
169
362
|
type: Literal[EventType.PROC_ERR] = EventType.PROC_ERR
|
170
363
|
source: Literal[EventSourceType.PROC] = EventSourceType.PROC
|
364
|
+
|
365
|
+
|
366
|
+
# Workflow and run events
|
367
|
+
|
368
|
+
|
369
|
+
class WorkflowResultEvent(ProcPacketOutputEvent, frozen=True):
|
370
|
+
type: Literal[EventType.WORKFLOW_RES] = EventType.WORKFLOW_RES
|
371
|
+
source: Literal[EventSourceType.WORKFLOW] = EventSourceType.WORKFLOW
|
372
|
+
|
373
|
+
|
374
|
+
class RunResultEvent(ProcPacketOutputEvent, frozen=True):
|
375
|
+
type: Literal[EventType.RUN_RES] = EventType.RUN_RES
|
376
|
+
source: Literal[EventSourceType.RUN] = EventSourceType.RUN
|