ai 2.1.26 → 2.1.27

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.
package/dist/index.mjs CHANGED
@@ -1,43 +1,3 @@
1
- var __defProp = Object.defineProperty;
2
- var __defProps = Object.defineProperties;
3
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
- var __spreadValues = (a, b) => {
9
- for (var prop in b || (b = {}))
10
- if (__hasOwnProp.call(b, prop))
11
- __defNormalProp(a, prop, b[prop]);
12
- if (__getOwnPropSymbols)
13
- for (var prop of __getOwnPropSymbols(b)) {
14
- if (__propIsEnum.call(b, prop))
15
- __defNormalProp(a, prop, b[prop]);
16
- }
17
- return a;
18
- };
19
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
- var __async = (__this, __arguments, generator) => {
21
- return new Promise((resolve, reject) => {
22
- var fulfilled = (value) => {
23
- try {
24
- step(generator.next(value));
25
- } catch (e) {
26
- reject(e);
27
- }
28
- };
29
- var rejected = (value) => {
30
- try {
31
- step(generator.throw(value));
32
- } catch (e) {
33
- reject(e);
34
- }
35
- };
36
- var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
37
- step((generator = generator.apply(__this, __arguments)).next());
38
- });
39
- };
40
-
41
1
  // streams/ai-stream.ts
42
2
  import {
43
3
  createParser
@@ -46,22 +6,20 @@ function createEventStreamTransformer(customParser) {
46
6
  const textDecoder = new TextDecoder();
47
7
  let eventSourceParser;
48
8
  return new TransformStream({
49
- start(controller) {
50
- return __async(this, null, function* () {
51
- eventSourceParser = createParser(
52
- (event) => {
53
- if ("data" in event && event.type === "event" && event.data === "[DONE]") {
54
- controller.terminate();
55
- return;
56
- }
57
- if ("data" in event) {
58
- const parsedMessage = customParser(event.data);
59
- if (parsedMessage)
60
- controller.enqueue(parsedMessage);
61
- }
9
+ async start(controller) {
10
+ eventSourceParser = createParser(
11
+ (event) => {
12
+ if ("data" in event && event.type === "event" && event.data === "[DONE]") {
13
+ controller.terminate();
14
+ return;
62
15
  }
63
- );
64
- });
16
+ if ("data" in event) {
17
+ const parsedMessage = customParser(event.data);
18
+ if (parsedMessage)
19
+ controller.enqueue(parsedMessage);
20
+ }
21
+ }
22
+ );
65
23
  },
66
24
  transform(chunk) {
67
25
  eventSourceParser.feed(textDecoder.decode(chunk));
@@ -73,26 +31,20 @@ function createCallbacksTransformer(callbacks) {
73
31
  let aggregatedResponse = "";
74
32
  const { onStart, onToken, onCompletion } = callbacks || {};
75
33
  return new TransformStream({
76
- start() {
77
- return __async(this, null, function* () {
78
- if (onStart)
79
- yield onStart();
80
- });
34
+ async start() {
35
+ if (onStart)
36
+ await onStart();
81
37
  },
82
- transform(message, controller) {
83
- return __async(this, null, function* () {
84
- controller.enqueue(textEncoder.encode(message));
85
- if (onToken)
86
- yield onToken(message);
87
- if (onCompletion)
88
- aggregatedResponse += message;
89
- });
38
+ async transform(message, controller) {
39
+ controller.enqueue(textEncoder.encode(message));
40
+ if (onToken)
41
+ await onToken(message);
42
+ if (onCompletion)
43
+ aggregatedResponse += message;
90
44
  },
91
- flush() {
92
- return __async(this, null, function* () {
93
- if (onCompletion)
94
- yield onCompletion(aggregatedResponse);
95
- });
45
+ async flush() {
46
+ if (onCompletion)
47
+ await onCompletion(aggregatedResponse);
96
48
  }
97
49
  });
98
50
  }
@@ -110,17 +62,14 @@ function trimStartOfStreamHelper() {
110
62
  function AIStream(response, customParser, callbacks) {
111
63
  if (!response.ok) {
112
64
  if (response.body) {
113
- let _a;
114
65
  const reader = response.body.getReader();
115
66
  return new ReadableStream({
116
- start(controller) {
117
- return __async(this, null, function* () {
118
- const { done, value } = yield reader.read();
119
- if (!done) {
120
- const errorText = new TextDecoder().decode(value);
121
- controller.error(new Error(`Response error: ${errorText}`));
122
- }
123
- });
67
+ async start(controller) {
68
+ const { done, value } = await reader.read();
69
+ if (!done) {
70
+ const errorText = new TextDecoder().decode(value);
71
+ controller.error(new Error(`Response error: ${errorText}`));
72
+ }
124
73
  }
125
74
  });
126
75
  } else {
@@ -141,14 +90,43 @@ function createEmptyReadableStream() {
141
90
  }
142
91
  });
143
92
  }
93
+ function readableFromAsyncIterable(iterable) {
94
+ let it = iterable[Symbol.asyncIterator]();
95
+ return new ReadableStream({
96
+ async pull(controller) {
97
+ const { done, value } = await it.next();
98
+ if (done)
99
+ controller.close();
100
+ else
101
+ controller.enqueue(value);
102
+ },
103
+ async cancel(reason) {
104
+ var _a;
105
+ await ((_a = it.return) == null ? void 0 : _a.call(it, reason));
106
+ }
107
+ });
108
+ }
144
109
 
145
110
  // streams/openai-stream.ts
146
111
  function parseOpenAIStream() {
112
+ const extract = chunkToText();
113
+ return (data) => {
114
+ return extract(JSON.parse(data));
115
+ };
116
+ }
117
+ async function* streamable(stream) {
118
+ const extract = chunkToText();
119
+ for await (const chunk of stream) {
120
+ const text = extract(chunk);
121
+ if (text)
122
+ yield text;
123
+ }
124
+ }
125
+ function chunkToText() {
147
126
  const trimStartOfStream = trimStartOfStreamHelper();
148
127
  let isFunctionStreamingIn;
149
- return (data) => {
128
+ return (json) => {
150
129
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
151
- const json = JSON.parse(data);
152
130
  if ((_c = (_b = (_a = json.choices[0]) == null ? void 0 : _a.delta) == null ? void 0 : _b.function_call) == null ? void 0 : _c.name) {
153
131
  isFunctionStreamingIn = true;
154
132
  return `{"function_call": {"name": "${(_e = (_d = json.choices[0]) == null ? void 0 : _d.delta) == null ? void 0 : _e.function_call.name}", "arguments": "`;
@@ -156,7 +134,7 @@ function parseOpenAIStream() {
156
134
  const argumentChunk = json.choices[0].delta.function_call.arguments;
157
135
  let escapedPartialJson = argumentChunk.replace(/\\/g, "\\\\").replace(/\//g, "\\/").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t").replace(/\f/g, "\\f");
158
136
  return `${escapedPartialJson}`;
159
- } else if ((((_i = json.choices[0]) == null ? void 0 : _i.finish_reason) === "function_call" || ((_j = json.choices[0]) == null ? void 0 : _j.finish_reason) === "stop") && isFunctionStreamingIn) {
137
+ } else if (isFunctionStreamingIn && (((_i = json.choices[0]) == null ? void 0 : _i.finish_reason) === "function_call" || ((_j = json.choices[0]) == null ? void 0 : _j.finish_reason) === "stop")) {
160
138
  isFunctionStreamingIn = false;
161
139
  return '"}}';
162
140
  }
@@ -169,7 +147,14 @@ function parseOpenAIStream() {
169
147
  var __internal__OpenAIFnMessagesSymbol = Symbol("internal_openai_fn_messages");
170
148
  function OpenAIStream(res, callbacks) {
171
149
  const cb = callbacks;
172
- const stream = AIStream(res, parseOpenAIStream(), cb);
150
+ let stream;
151
+ if (Symbol.asyncIterator in res) {
152
+ stream = readableFromAsyncIterable(streamable(res)).pipeThrough(
153
+ createCallbacksTransformer(cb)
154
+ );
155
+ } else {
156
+ stream = AIStream(res, parseOpenAIStream(), cb);
157
+ }
173
158
  if (cb && cb.experimental_onFunctionCall) {
174
159
  const functionCallTransformer = createFunctionCallTransformer(cb);
175
160
  return stream.pipeThrough(functionCallTransformer);
@@ -184,78 +169,76 @@ function createFunctionCallTransformer(callbacks) {
184
169
  let isFunctionStreamingIn = false;
185
170
  let functionCallMessages = callbacks[__internal__OpenAIFnMessagesSymbol] || [];
186
171
  return new TransformStream({
187
- transform(chunk, controller) {
188
- return __async(this, null, function* () {
189
- const message = new TextDecoder().decode(chunk);
190
- const shouldHandleAsFunction = isFirstChunk && message.startsWith('{"function_call":');
191
- if (shouldHandleAsFunction) {
192
- isFunctionStreamingIn = true;
193
- aggregatedResponse += message;
194
- isFirstChunk = false;
172
+ async transform(chunk, controller) {
173
+ const message = new TextDecoder().decode(chunk);
174
+ const shouldHandleAsFunction = isFirstChunk && message.startsWith('{"function_call":');
175
+ if (shouldHandleAsFunction) {
176
+ isFunctionStreamingIn = true;
177
+ aggregatedResponse += message;
178
+ isFirstChunk = false;
179
+ return;
180
+ }
181
+ if (!isFunctionStreamingIn) {
182
+ controller.enqueue(chunk);
183
+ return;
184
+ } else {
185
+ aggregatedResponse += message;
186
+ }
187
+ },
188
+ async flush(controller) {
189
+ const isEndOfFunction = !isFirstChunk && callbacks.experimental_onFunctionCall && isFunctionStreamingIn;
190
+ if (isEndOfFunction && callbacks.experimental_onFunctionCall) {
191
+ isFunctionStreamingIn = false;
192
+ const payload = JSON.parse(aggregatedResponse);
193
+ const argumentsPayload = JSON.parse(payload.function_call.arguments);
194
+ let newFunctionCallMessages = [...functionCallMessages];
195
+ const functionResponse = await callbacks.experimental_onFunctionCall(
196
+ {
197
+ name: payload.function_call.name,
198
+ arguments: argumentsPayload
199
+ },
200
+ (result) => {
201
+ newFunctionCallMessages = [
202
+ ...functionCallMessages,
203
+ {
204
+ role: "assistant",
205
+ content: "",
206
+ function_call: payload.function_call
207
+ },
208
+ {
209
+ role: "function",
210
+ name: payload.function_call.name,
211
+ content: JSON.stringify(result)
212
+ }
213
+ ];
214
+ return newFunctionCallMessages;
215
+ }
216
+ );
217
+ if (!functionResponse) {
218
+ controller.enqueue(textEncoder.encode(aggregatedResponse));
195
219
  return;
196
- }
197
- if (!isFunctionStreamingIn) {
198
- controller.enqueue(chunk);
220
+ } else if (typeof functionResponse === "string") {
221
+ controller.enqueue(textEncoder.encode(functionResponse));
199
222
  return;
200
- } else {
201
- aggregatedResponse += message;
202
223
  }
203
- });
204
- },
205
- flush(controller) {
206
- return __async(this, null, function* () {
207
- const isEndOfFunction = !isFirstChunk && callbacks.experimental_onFunctionCall && isFunctionStreamingIn;
208
- if (isEndOfFunction && callbacks.experimental_onFunctionCall) {
209
- isFunctionStreamingIn = false;
210
- const payload = JSON.parse(aggregatedResponse);
211
- const argumentsPayload = JSON.parse(payload.function_call.arguments);
212
- let newFunctionCallMessages = [...functionCallMessages];
213
- const functionResponse = yield callbacks.experimental_onFunctionCall(
214
- {
215
- name: payload.function_call.name,
216
- arguments: argumentsPayload
217
- },
218
- (result) => {
219
- newFunctionCallMessages = [
220
- ...functionCallMessages,
221
- {
222
- role: "assistant",
223
- content: "",
224
- function_call: payload.function_call
225
- },
226
- {
227
- role: "function",
228
- name: payload.function_call.name,
229
- content: JSON.stringify(result)
230
- }
231
- ];
232
- return newFunctionCallMessages;
233
- }
234
- );
235
- if (!functionResponse) {
236
- controller.enqueue(textEncoder.encode(aggregatedResponse));
237
- return;
238
- } else if (typeof functionResponse === "string") {
239
- controller.enqueue(textEncoder.encode(functionResponse));
240
- return;
241
- }
242
- const filteredCallbacks = __spreadProps(__spreadValues({}, callbacks), {
243
- onStart: void 0,
244
- onCompletion: void 0
245
- });
246
- const openAIStream = OpenAIStream(functionResponse, __spreadProps(__spreadValues({}, filteredCallbacks), {
247
- [__internal__OpenAIFnMessagesSymbol]: newFunctionCallMessages
248
- }));
249
- const reader = openAIStream.getReader();
250
- while (true) {
251
- const { done, value } = yield reader.read();
252
- if (done) {
253
- break;
254
- }
255
- controller.enqueue(value);
224
+ const filteredCallbacks = {
225
+ ...callbacks,
226
+ onStart: void 0,
227
+ onCompletion: void 0
228
+ };
229
+ const openAIStream = OpenAIStream(functionResponse, {
230
+ ...filteredCallbacks,
231
+ [__internal__OpenAIFnMessagesSymbol]: newFunctionCallMessages
232
+ });
233
+ const reader = openAIStream.getReader();
234
+ while (true) {
235
+ const { done, value } = await reader.read();
236
+ if (done) {
237
+ break;
256
238
  }
239
+ controller.enqueue(value);
257
240
  }
258
- });
241
+ }
259
242
  }
260
243
  });
261
244
  }
@@ -263,18 +246,21 @@ function createFunctionCallTransformer(callbacks) {
263
246
  // streams/streaming-text-response.ts
264
247
  var StreamingTextResponse = class extends Response {
265
248
  constructor(res, init) {
266
- super(res, __spreadProps(__spreadValues({}, init), {
249
+ super(res, {
250
+ ...init,
267
251
  status: 200,
268
- headers: __spreadValues({
269
- "Content-Type": "text/plain; charset=utf-8"
270
- }, init == null ? void 0 : init.headers)
271
- }));
252
+ headers: {
253
+ "Content-Type": "text/plain; charset=utf-8",
254
+ ...init == null ? void 0 : init.headers
255
+ }
256
+ });
272
257
  }
273
258
  };
274
259
  function streamToResponse(res, response, init) {
275
- response.writeHead((init == null ? void 0 : init.status) || 200, __spreadValues({
276
- "Content-Type": "text/plain; charset=utf-8"
277
- }, init == null ? void 0 : init.headers));
260
+ response.writeHead((init == null ? void 0 : init.status) || 200, {
261
+ "Content-Type": "text/plain; charset=utf-8",
262
+ ...init == null ? void 0 : init.headers
263
+ });
278
264
  const reader = res.getReader();
279
265
  function read() {
280
266
  reader.read().then(({ done, value }) => {
@@ -293,27 +279,25 @@ function streamToResponse(res, response, init) {
293
279
  function createParser2(res) {
294
280
  const trimStartOfStream = trimStartOfStreamHelper();
295
281
  return new ReadableStream({
296
- pull(controller) {
297
- return __async(this, null, function* () {
298
- var _a2, _b;
299
- const { value, done } = yield res.next();
300
- if (done) {
301
- controller.close();
302
- return;
303
- }
304
- const text = trimStartOfStream((_b = (_a2 = value.token) == null ? void 0 : _a2.text) != null ? _b : "");
305
- if (!text)
306
- return;
307
- if (value.generated_text != null && value.generated_text.length > 0) {
308
- controller.close();
309
- return;
310
- }
311
- if (text === "</s>" || text === "<|endoftext|>" || text === "<|end|>") {
312
- controller.close();
313
- } else {
314
- controller.enqueue(text);
315
- }
316
- });
282
+ async pull(controller) {
283
+ var _a, _b;
284
+ const { value, done } = await res.next();
285
+ if (done) {
286
+ controller.close();
287
+ return;
288
+ }
289
+ const text = trimStartOfStream((_b = (_a = value.token) == null ? void 0 : _a.text) != null ? _b : "");
290
+ if (!text)
291
+ return;
292
+ if (value.generated_text != null && value.generated_text.length > 0) {
293
+ controller.close();
294
+ return;
295
+ }
296
+ if (text === "</s>" || text === "<|endoftext|>" || text === "<|end|>") {
297
+ controller.close();
298
+ } else {
299
+ controller.enqueue(text);
300
+ }
317
301
  }
318
302
  });
319
303
  }
@@ -323,50 +307,44 @@ function HuggingFaceStream(res, callbacks) {
323
307
 
324
308
  // streams/cohere-stream.ts
325
309
  var utf8Decoder = new TextDecoder("utf-8");
326
- function processLines(lines, controller) {
327
- return __async(this, null, function* () {
328
- for (const line of lines) {
329
- const { text, is_finished } = JSON.parse(line);
330
- if (is_finished === true) {
331
- controller.close();
332
- } else {
333
- controller.enqueue(text);
334
- }
310
+ async function processLines(lines, controller) {
311
+ for (const line of lines) {
312
+ const { text, is_finished } = JSON.parse(line);
313
+ if (is_finished === true) {
314
+ controller.close();
315
+ } else {
316
+ controller.enqueue(text);
335
317
  }
336
- });
318
+ }
337
319
  }
338
- function readAndProcessLines(reader, controller) {
339
- return __async(this, null, function* () {
340
- let segment = "";
341
- while (true) {
342
- const { value: chunk, done } = yield reader.read();
343
- if (done) {
344
- break;
345
- }
346
- segment += utf8Decoder.decode(chunk, { stream: true });
347
- const linesArray = segment.split(/\r\n|\n|\r/g);
348
- segment = linesArray.pop() || "";
349
- yield processLines(linesArray, controller);
320
+ async function readAndProcessLines(reader, controller) {
321
+ let segment = "";
322
+ while (true) {
323
+ const { value: chunk, done } = await reader.read();
324
+ if (done) {
325
+ break;
350
326
  }
351
- if (segment) {
352
- const linesArray = [segment];
353
- yield processLines(linesArray, controller);
354
- }
355
- controller.close();
356
- });
327
+ segment += utf8Decoder.decode(chunk, { stream: true });
328
+ const linesArray = segment.split(/\r\n|\n|\r/g);
329
+ segment = linesArray.pop() || "";
330
+ await processLines(linesArray, controller);
331
+ }
332
+ if (segment) {
333
+ const linesArray = [segment];
334
+ await processLines(linesArray, controller);
335
+ }
336
+ controller.close();
357
337
  }
358
338
  function createParser3(res) {
359
339
  var _a;
360
340
  const reader = (_a = res.body) == null ? void 0 : _a.getReader();
361
341
  return new ReadableStream({
362
- start(controller) {
363
- return __async(this, null, function* () {
364
- if (!reader) {
365
- controller.close();
366
- return;
367
- }
368
- yield readAndProcessLines(reader, controller);
369
- });
342
+ async start(controller) {
343
+ if (!reader) {
344
+ controller.close();
345
+ return;
346
+ }
347
+ await readAndProcessLines(reader, controller);
370
348
  }
371
349
  });
372
350
  }
@@ -394,55 +372,55 @@ function LangChainStream(callbacks) {
394
372
  const stream = new TransformStream();
395
373
  const writer = stream.writable.getWriter();
396
374
  const runs = /* @__PURE__ */ new Set();
397
- const handleError = (e, runId) => __async(this, null, function* () {
375
+ const handleError = async (e, runId) => {
398
376
  runs.delete(runId);
399
- yield writer.ready;
400
- yield writer.abort(e);
401
- });
402
- const handleStart = (runId) => __async(this, null, function* () {
377
+ await writer.ready;
378
+ await writer.abort(e);
379
+ };
380
+ const handleStart = async (runId) => {
403
381
  runs.add(runId);
404
- });
405
- const handleEnd = (runId) => __async(this, null, function* () {
382
+ };
383
+ const handleEnd = async (runId) => {
406
384
  runs.delete(runId);
407
385
  if (runs.size === 0) {
408
- yield writer.ready;
409
- yield writer.close();
386
+ await writer.ready;
387
+ await writer.close();
410
388
  }
411
- });
389
+ };
412
390
  return {
413
391
  stream: stream.readable.pipeThrough(createCallbacksTransformer(callbacks)),
414
392
  handlers: {
415
- handleLLMNewToken: (token) => __async(this, null, function* () {
416
- yield writer.ready;
417
- yield writer.write(token);
418
- }),
419
- handleLLMStart: (_llm, _prompts, runId) => __async(this, null, function* () {
393
+ handleLLMNewToken: async (token) => {
394
+ await writer.ready;
395
+ await writer.write(token);
396
+ },
397
+ handleLLMStart: async (_llm, _prompts, runId) => {
420
398
  handleStart(runId);
421
- }),
422
- handleLLMEnd: (_output, runId) => __async(this, null, function* () {
423
- yield handleEnd(runId);
424
- }),
425
- handleLLMError: (e, runId) => __async(this, null, function* () {
426
- yield handleError(e, runId);
427
- }),
428
- handleChainStart: (_chain, _inputs, runId) => __async(this, null, function* () {
399
+ },
400
+ handleLLMEnd: async (_output, runId) => {
401
+ await handleEnd(runId);
402
+ },
403
+ handleLLMError: async (e, runId) => {
404
+ await handleError(e, runId);
405
+ },
406
+ handleChainStart: async (_chain, _inputs, runId) => {
429
407
  handleStart(runId);
430
- }),
431
- handleChainEnd: (_outputs, runId) => __async(this, null, function* () {
432
- yield handleEnd(runId);
433
- }),
434
- handleChainError: (e, runId) => __async(this, null, function* () {
435
- yield handleError(e, runId);
436
- }),
437
- handleToolStart: (_tool, _input, runId) => __async(this, null, function* () {
408
+ },
409
+ handleChainEnd: async (_outputs, runId) => {
410
+ await handleEnd(runId);
411
+ },
412
+ handleChainError: async (e, runId) => {
413
+ await handleError(e, runId);
414
+ },
415
+ handleToolStart: async (_tool, _input, runId) => {
438
416
  handleStart(runId);
439
- }),
440
- handleToolEnd: (_output, runId) => __async(this, null, function* () {
441
- yield handleEnd(runId);
442
- }),
443
- handleToolError: (e, runId) => __async(this, null, function* () {
444
- yield handleError(e, runId);
445
- })
417
+ },
418
+ handleToolEnd: async (_output, runId) => {
419
+ await handleEnd(runId);
420
+ },
421
+ handleToolError: async (e, runId) => {
422
+ await handleError(e, runId);
423
+ }
446
424
  }
447
425
  };
448
426
  }
@@ -473,6 +451,7 @@ export {
473
451
  createChunkDecoder,
474
452
  createEventStreamTransformer,
475
453
  nanoid,
454
+ readableFromAsyncIterable,
476
455
  streamToResponse,
477
456
  trimStartOfStreamHelper
478
457
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "2.1.26",
3
+ "version": "2.1.27",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -79,8 +79,8 @@
79
79
  "ts-jest": "29.0.3",
80
80
  "tsup": "^6.7.0",
81
81
  "typescript": "5.1.3",
82
- "eslint-config-vercel-ai": "0.0.0",
83
- "@vercel/ai-tsconfig": "0.0.0"
82
+ "@vercel/ai-tsconfig": "0.0.0",
83
+ "eslint-config-vercel-ai": "0.0.0"
84
84
  },
85
85
  "peerDependencies": {
86
86
  "react": "^18.2.0",