1
- import { FunctionCall , JSONValue } from './types' ;
1
+ import { AssistantMessage , FunctionCall , JSONValue } from './types' ;
2
2
import { StreamString } from './utils' ;
3
3
4
4
export interface StreamPart < CODE extends string , NAME extends string , TYPE > {
@@ -7,7 +7,7 @@ export interface StreamPart<CODE extends string, NAME extends string, TYPE> {
7
7
parse : ( value : JSONValue ) => { type : NAME ; value : TYPE } ;
8
8
}
9
9
10
- export const textStreamPart : StreamPart < '0' , 'text' , string > = {
10
+ const textStreamPart : StreamPart < '0' , 'text' , string > = {
11
11
code : '0' ,
12
12
name : 'text' ,
13
13
parse : ( value : JSONValue ) => {
@@ -18,7 +18,7 @@ export const textStreamPart: StreamPart<'0', 'text', string> = {
18
18
} ,
19
19
} ;
20
20
21
- export const functionCallStreamPart : StreamPart <
21
+ const functionCallStreamPart : StreamPart <
22
22
'1' ,
23
23
'function_call' ,
24
24
{ function_call : FunctionCall }
@@ -29,34 +29,27 @@ export const functionCallStreamPart: StreamPart<
29
29
if (
30
30
value == null ||
31
31
typeof value !== 'object' ||
32
- ! ( 'function_call' in value )
32
+ ! ( 'function_call' in value ) ||
33
+ typeof value . function_call !== 'object' ||
34
+ value . function_call == null ||
35
+ ! ( 'name' in value . function_call ) ||
36
+ ! ( 'arguments' in value . function_call ) ||
37
+ typeof value . function_call . name !== 'string' ||
38
+ typeof value . function_call . arguments !== 'string'
33
39
) {
34
40
throw new Error (
35
41
'"function_call" parts expect an object with a "function_call" property.' ,
36
42
) ;
37
43
}
38
44
39
- const functionCall = value . function_call ;
40
-
41
- if (
42
- functionCall == null ||
43
- typeof functionCall !== 'object' ||
44
- ! ( 'name' in functionCall ) ||
45
- ! ( 'arguments' in functionCall )
46
- ) {
47
- throw new Error (
48
- '"function_call" parts expect an object with a "name" and "arguments" property.' ,
49
- ) ;
50
- }
51
-
52
45
return {
53
46
type : 'function_call' ,
54
47
value : value as unknown as { function_call : FunctionCall } ,
55
48
} ;
56
49
} ,
57
50
} ;
58
51
59
- export const dataStreamPart : StreamPart < '2' , 'data' , Array < JSONValue > > = {
52
+ const dataStreamPart : StreamPart < '2' , 'data' , Array < JSONValue > > = {
60
53
code : '2' ,
61
54
name : 'data' ,
62
55
parse : ( value : JSONValue ) => {
@@ -68,17 +61,108 @@ export const dataStreamPart: StreamPart<'2', 'data', Array<JSONValue>> = {
68
61
} ,
69
62
} ;
70
63
64
+ const errorStreamPart : StreamPart < '3' , 'error' , string > = {
65
+ code : '3' ,
66
+ name : 'error' ,
67
+ parse : ( value : JSONValue ) => {
68
+ if ( typeof value !== 'string' ) {
69
+ throw new Error ( '"error" parts expect a string value.' ) ;
70
+ }
71
+ return { type : 'error' , value } ;
72
+ } ,
73
+ } ;
74
+
75
+ const assistantMessage : StreamPart < '4' , 'assistant_message' , AssistantMessage > =
76
+ {
77
+ code : '4' ,
78
+ name : 'assistant_message' ,
79
+ parse : ( value : JSONValue ) => {
80
+ if (
81
+ value == null ||
82
+ typeof value !== 'object' ||
83
+ ! ( 'id' in value ) ||
84
+ ! ( 'role' in value ) ||
85
+ ! ( 'content' in value ) ||
86
+ typeof value . id !== 'string' ||
87
+ typeof value . role !== 'string' ||
88
+ value . role !== 'assistant' ||
89
+ ! Array . isArray ( value . content ) ||
90
+ ! value . content . every (
91
+ item =>
92
+ item != null &&
93
+ typeof item === 'object' &&
94
+ 'type' in item &&
95
+ item . type === 'text' &&
96
+ 'text' in item &&
97
+ item . text != null &&
98
+ typeof item . text === 'object' &&
99
+ 'value' in item . text &&
100
+ typeof item . text . value === 'string' ,
101
+ )
102
+ ) {
103
+ throw new Error (
104
+ '"assistant_message" parts expect an object with an "id", "role", and "content" property.' ,
105
+ ) ;
106
+ }
107
+
108
+ return {
109
+ type : 'assistant_message' ,
110
+ value : value as AssistantMessage ,
111
+ } ;
112
+ } ,
113
+ } ;
114
+
115
+ const assistantControlData : StreamPart <
116
+ '5' ,
117
+ 'assistant_control_data' ,
118
+ {
119
+ threadId : string ;
120
+ messageId : string ;
121
+ }
122
+ > = {
123
+ code : '5' ,
124
+ name : 'assistant_control_data' ,
125
+ parse : ( value : JSONValue ) => {
126
+ if (
127
+ value == null ||
128
+ typeof value !== 'object' ||
129
+ ! ( 'threadId' in value ) ||
130
+ ! ( 'messageId' in value ) ||
131
+ typeof value . threadId !== 'string' ||
132
+ typeof value . messageId !== 'string'
133
+ ) {
134
+ throw new Error (
135
+ '"assistant_control_data" parts expect an object with a "threadId" and "messageId" property.' ,
136
+ ) ;
137
+ }
138
+
139
+ return {
140
+ type : 'assistant_control_data' ,
141
+ value : {
142
+ threadId : value . threadId ,
143
+ messageId : value . messageId ,
144
+ } ,
145
+ } ;
146
+ } ,
147
+ } ;
148
+
71
149
const streamParts = [
72
150
textStreamPart ,
73
151
functionCallStreamPart ,
74
152
dataStreamPart ,
153
+ errorStreamPart ,
154
+ assistantMessage ,
155
+ assistantControlData ,
75
156
] as const ;
76
157
77
158
// union type of all stream parts
78
159
type StreamParts =
79
160
| typeof textStreamPart
80
161
| typeof functionCallStreamPart
81
- | typeof dataStreamPart ;
162
+ | typeof dataStreamPart
163
+ | typeof errorStreamPart
164
+ | typeof assistantMessage
165
+ | typeof assistantControlData ;
82
166
83
167
/**
84
168
* Maps the type of a stream part to its value type.
@@ -90,12 +174,47 @@ type StreamPartValueType = {
90
174
export type StreamPartType =
91
175
| ReturnType < typeof textStreamPart . parse >
92
176
| ReturnType < typeof functionCallStreamPart . parse >
93
- | ReturnType < typeof dataStreamPart . parse > ;
177
+ | ReturnType < typeof dataStreamPart . parse >
178
+ | ReturnType < typeof errorStreamPart . parse >
179
+ | ReturnType < typeof assistantMessage . parse >
180
+ | ReturnType < typeof assistantControlData . parse > ;
94
181
95
182
export const streamPartsByCode = {
96
183
[ textStreamPart . code ] : textStreamPart ,
97
184
[ functionCallStreamPart . code ] : functionCallStreamPart ,
98
185
[ dataStreamPart . code ] : dataStreamPart ,
186
+ [ errorStreamPart . code ] : errorStreamPart ,
187
+ [ assistantMessage . code ] : assistantMessage ,
188
+ [ assistantControlData . code ] : assistantControlData ,
189
+ } as const ;
190
+
191
+ /**
192
+ * The map of prefixes for data in the stream
193
+ *
194
+ * - 0: Text from the LLM response
195
+ * - 1: (OpenAI) function_call responses
196
+ * - 2: custom JSON added by the user using `Data`
197
+ *
198
+ * Example:
199
+ * ```
200
+ * 0:Vercel
201
+ * 0:'s
202
+ * 0: AI
203
+ * 0: AI
204
+ * 0: SDK
205
+ * 0: is great
206
+ * 0:!
207
+ * 2: { "someJson": "value" }
208
+ * 1: {"function_call": {"name": "get_current_weather", "arguments": "{\\n\\"location\\": \\"Charlottesville, Virginia\\",\\n\\"format\\": \\"celsius\\"\\n}"}}
209
+ *```
210
+ */
211
+ export const StreamStringPrefixes = {
212
+ [ textStreamPart . name ] : textStreamPart . code ,
213
+ [ functionCallStreamPart . name ] : functionCallStreamPart . code ,
214
+ [ dataStreamPart . name ] : dataStreamPart . code ,
215
+ [ errorStreamPart . name ] : errorStreamPart . code ,
216
+ [ assistantMessage . name ] : assistantMessage . code ,
217
+ [ assistantControlData . name ] : assistantControlData . code ,
99
218
} as const ;
100
219
101
220
export const validCodes = streamParts . map ( part => part . code ) ;
@@ -108,21 +227,21 @@ export const validCodes = streamParts.map(part => part.code);
108
227
* @throws An error if the string cannot be parsed.
109
228
*/
110
229
export const parseStreamPart = ( line : string ) : StreamPartType => {
111
- const firstSeperatorIndex = line . indexOf ( ':' ) ;
230
+ const firstSeparatorIndex = line . indexOf ( ':' ) ;
112
231
113
- if ( firstSeperatorIndex === - 1 ) {
114
- throw new Error ( 'Failed to parse stream string. No seperator found.' ) ;
232
+ if ( firstSeparatorIndex === - 1 ) {
233
+ throw new Error ( 'Failed to parse stream string. No separator found.' ) ;
115
234
}
116
235
117
- const prefix = line . slice ( 0 , firstSeperatorIndex ) ;
236
+ const prefix = line . slice ( 0 , firstSeparatorIndex ) ;
118
237
119
238
if ( ! validCodes . includes ( prefix as keyof typeof streamPartsByCode ) ) {
120
239
throw new Error ( `Failed to parse stream string. Invalid code ${ prefix } .` ) ;
121
240
}
122
241
123
242
const code = prefix as keyof typeof streamPartsByCode ;
124
243
125
- const textValue = line . slice ( firstSeperatorIndex + 1 ) ;
244
+ const textValue = line . slice ( firstSeparatorIndex + 1 ) ;
126
245
const jsonValue : JSONValue = JSON . parse ( textValue ) ;
127
246
128
247
return streamPartsByCode [ code ] . parse ( jsonValue ) ;
0 commit comments