@volcengine/ark-runtime 1.0.0

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.
Files changed (123) hide show
  1. package/LICENSE.txt +202 -0
  2. package/README.md +104 -0
  3. package/dist/cjs/index.js +1717 -0
  4. package/dist/esm/client.d.ts +97 -0
  5. package/dist/esm/client.d.ts.map +1 -0
  6. package/dist/esm/config.d.ts +46 -0
  7. package/dist/esm/config.d.ts.map +1 -0
  8. package/dist/esm/encryption/encrypt-chat.d.ts +24 -0
  9. package/dist/esm/encryption/encrypt-chat.d.ts.map +1 -0
  10. package/dist/esm/encryption/index.d.ts +4 -0
  11. package/dist/esm/encryption/index.d.ts.map +1 -0
  12. package/dist/esm/encryption/key-agreement.d.ts +73 -0
  13. package/dist/esm/encryption/key-agreement.d.ts.map +1 -0
  14. package/dist/esm/index.d.ts +11 -0
  15. package/dist/esm/index.d.ts.map +1 -0
  16. package/dist/esm/index.mjs +1476 -0
  17. package/dist/esm/rslib-runtime.mjs +37 -0
  18. package/dist/esm/types/bot.d.ts +109 -0
  19. package/dist/esm/types/bot.d.ts.map +1 -0
  20. package/dist/esm/types/chat-completion.d.ts +167 -0
  21. package/dist/esm/types/chat-completion.d.ts.map +1 -0
  22. package/dist/esm/types/common.d.ts +29 -0
  23. package/dist/esm/types/common.d.ts.map +1 -0
  24. package/dist/esm/types/content-generation.d.ts +118 -0
  25. package/dist/esm/types/content-generation.d.ts.map +1 -0
  26. package/dist/esm/types/context.d.ts +49 -0
  27. package/dist/esm/types/context.d.ts.map +1 -0
  28. package/dist/esm/types/embeddings.d.ts +44 -0
  29. package/dist/esm/types/embeddings.d.ts.map +1 -0
  30. package/dist/esm/types/error.d.ts +45 -0
  31. package/dist/esm/types/error.d.ts.map +1 -0
  32. package/dist/esm/types/file.d.ts +66 -0
  33. package/dist/esm/types/file.d.ts.map +1 -0
  34. package/dist/esm/types/http-request-error.d.ts +13 -0
  35. package/dist/esm/types/http-request-error.d.ts.map +1 -0
  36. package/dist/esm/types/images.d.ts +78 -0
  37. package/dist/esm/types/images.d.ts.map +1 -0
  38. package/dist/esm/types/index.d.ts +13 -0
  39. package/dist/esm/types/index.d.ts.map +1 -0
  40. package/dist/esm/types/multimodal-embedding.d.ts +56 -0
  41. package/dist/esm/types/multimodal-embedding.d.ts.map +1 -0
  42. package/dist/esm/types/responses/enums.d.ts +38 -0
  43. package/dist/esm/types/responses/enums.d.ts.map +1 -0
  44. package/dist/esm/types/responses/helpers.d.ts +22 -0
  45. package/dist/esm/types/responses/helpers.d.ts.map +1 -0
  46. package/dist/esm/types/responses/index.d.ts +4 -0
  47. package/dist/esm/types/responses/index.d.ts.map +1 -0
  48. package/dist/esm/types/responses/types.d.ts +906 -0
  49. package/dist/esm/types/responses/types.d.ts.map +1 -0
  50. package/dist/esm/types/tokenization.d.ts +22 -0
  51. package/dist/esm/types/tokenization.d.ts.map +1 -0
  52. package/dist/esm/utils/breaker-provider.d.ts +9 -0
  53. package/dist/esm/utils/breaker-provider.d.ts.map +1 -0
  54. package/dist/esm/utils/breaker.d.ts +28 -0
  55. package/dist/esm/utils/breaker.d.ts.map +1 -0
  56. package/dist/esm/utils/normalize.d.ts +51 -0
  57. package/dist/esm/utils/normalize.d.ts.map +1 -0
  58. package/dist/esm/utils/request-builder.d.ts +15 -0
  59. package/dist/esm/utils/request-builder.d.ts.map +1 -0
  60. package/dist/esm/utils/request-id.d.ts +5 -0
  61. package/dist/esm/utils/request-id.d.ts.map +1 -0
  62. package/dist/esm/utils/retry.d.ts +11 -0
  63. package/dist/esm/utils/retry.d.ts.map +1 -0
  64. package/dist/esm/utils/sse-decoder.d.ts +23 -0
  65. package/dist/esm/utils/sse-decoder.d.ts.map +1 -0
  66. package/dist/esm/utils/stream-reader.d.ts +67 -0
  67. package/dist/esm/utils/stream-reader.d.ts.map +1 -0
  68. package/dist/tsconfig.tsbuildinfo +1 -0
  69. package/example/README.md +118 -0
  70. package/example/batch-chat.ts +64 -0
  71. package/example/bot-chat.ts +66 -0
  72. package/example/chat-completion-function-call.ts +141 -0
  73. package/example/chat-completion-reasoning.ts +64 -0
  74. package/example/chat-completion-vision.ts +70 -0
  75. package/example/chat-completion.ts +62 -0
  76. package/example/content-generation.ts +70 -0
  77. package/example/context.ts +69 -0
  78. package/example/embeddings.ts +31 -0
  79. package/example/file-upload.ts +53 -0
  80. package/example/images.ts +74 -0
  81. package/example/list-input-items.ts +34 -0
  82. package/example/multimodal-embeddings.ts +36 -0
  83. package/example/responses/basic.ts +75 -0
  84. package/example/responses/doubao-app.ts +53 -0
  85. package/example/responses/mcp.ts +66 -0
  86. package/example/responses/streaming.ts +45 -0
  87. package/example/responses/video.ts +74 -0
  88. package/example/responses/web-search.ts +52 -0
  89. package/example/structured-outputs.ts +71 -0
  90. package/example/tokenization.ts +30 -0
  91. package/package.json +47 -0
  92. package/src/client.ts +1199 -0
  93. package/src/config.ts +68 -0
  94. package/src/encryption/encrypt-chat.ts +146 -0
  95. package/src/encryption/index.ts +21 -0
  96. package/src/encryption/key-agreement.ts +270 -0
  97. package/src/index.ts +10 -0
  98. package/src/types/ark.d.ts +9 -0
  99. package/src/types/bot.ts +127 -0
  100. package/src/types/chat-completion.ts +228 -0
  101. package/src/types/common.ts +37 -0
  102. package/src/types/content-generation.ts +135 -0
  103. package/src/types/context.ts +59 -0
  104. package/src/types/embeddings.ts +74 -0
  105. package/src/types/error.ts +93 -0
  106. package/src/types/file.ts +76 -0
  107. package/src/types/http-request-error.ts +34 -0
  108. package/src/types/images.ts +102 -0
  109. package/src/types/index.ts +12 -0
  110. package/src/types/multimodal-embedding.ts +67 -0
  111. package/src/types/responses/enums.ts +163 -0
  112. package/src/types/responses/helpers.ts +67 -0
  113. package/src/types/responses/index.ts +3 -0
  114. package/src/types/responses/types.ts +1335 -0
  115. package/src/types/tokenization.ts +24 -0
  116. package/src/utils/breaker-provider.ts +17 -0
  117. package/src/utils/breaker.ts +56 -0
  118. package/src/utils/normalize.ts +154 -0
  119. package/src/utils/request-builder.ts +51 -0
  120. package/src/utils/request-id.ts +17 -0
  121. package/src/utils/retry.ts +76 -0
  122. package/src/utils/sse-decoder.ts +140 -0
  123. package/src/utils/stream-reader.ts +270 -0
@@ -0,0 +1,270 @@
1
+ import { EventStreamDecoder } from "./sse-decoder";
2
+ import type { SSEEvent } from "./sse-decoder";
3
+ import type { ChatCompletionStreamResponse } from "../types/chat-completion";
4
+ import type { BotChatCompletionStreamResponse } from "../types/bot";
5
+ import type { ImagesStreamResponse } from "../types/images";
6
+ import type { ErrorResponse } from "../types/error";
7
+ import { ArkAPIError, ErrTooManyEmptyStreamMessages } from "../types/error";
8
+ import { decryptChatStreamResponse } from "../encryption/encrypt-chat";
9
+ import { normalizeChatCompletionStreamChunk } from "./normalize";
10
+
11
+ const ERROR_PREFIX = '{"error":';
12
+
13
+ /**
14
+ * Base SSE stream that parses `data:` lines from an HTTP response body.
15
+ * Subclassed for each response type.
16
+ */
17
+ abstract class BaseStreamReader<T> implements AsyncIterable<T> {
18
+ protected isFinished = false;
19
+ protected emptyMessagesCount = 0;
20
+ protected cleanup?: () => void;
21
+
22
+ constructor(
23
+ protected stream: ReadableStream<Uint8Array> | NodeJS.ReadableStream,
24
+ protected emptyMessagesLimit: number,
25
+ protected headers?: Record<string, string>,
26
+ cleanup?: () => void,
27
+ ) {
28
+ this.cleanup = cleanup;
29
+ }
30
+
31
+ abstract [Symbol.asyncIterator](): AsyncIterator<T>;
32
+
33
+ close(): void {
34
+ this.isFinished = true;
35
+ this.cleanup?.();
36
+ this.cleanup = undefined;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Reads `data: <json>` lines from SSE (chat completion, bot, images).
42
+ * Handles `data: [DONE]` termination and error prefixes.
43
+ */
44
+ class DataLineStreamReader<T> extends BaseStreamReader<T> {
45
+ private decoder = new TextDecoder();
46
+ private buffer = "";
47
+
48
+ async *[Symbol.asyncIterator](): AsyncIterator<T> {
49
+ const chunks = this.iterateChunks();
50
+ let errBuffer = "";
51
+ let hasError = false;
52
+
53
+ for await (const rawLine of this.readLines(chunks)) {
54
+ if (this.isFinished) return;
55
+
56
+ const trimmed = rawLine.trim();
57
+ if (trimmed === "") {
58
+ continue;
59
+ }
60
+
61
+ // Extract data after "data:" prefix
62
+ let dataContent: string | null = null;
63
+ if (trimmed.startsWith("data:")) {
64
+ dataContent = trimmed.slice(5).trim();
65
+ }
66
+
67
+ // Check for error prefix in the data content
68
+ if (dataContent && dataContent.startsWith(ERROR_PREFIX)) {
69
+ hasError = true;
70
+ errBuffer += dataContent;
71
+ continue;
72
+ }
73
+
74
+ if (hasError) {
75
+ errBuffer += trimmed.startsWith("data:") ? trimmed.slice(5).trim() : trimmed;
76
+ continue;
77
+ }
78
+
79
+ // Non-data lines
80
+ if (dataContent === null) {
81
+ this.emptyMessagesCount++;
82
+ if (this.emptyMessagesCount > this.emptyMessagesLimit) {
83
+ throw ErrTooManyEmptyStreamMessages;
84
+ }
85
+ continue;
86
+ }
87
+
88
+ // Check for [DONE]
89
+ if (dataContent === "[DONE]") {
90
+ this.close();
91
+ return;
92
+ }
93
+
94
+ // Parse JSON
95
+ const parsed = JSON.parse(dataContent) as T;
96
+ this.emptyMessagesCount = 0;
97
+ yield parsed;
98
+ }
99
+
100
+ // Handle accumulated error
101
+ if (hasError && errBuffer) {
102
+ const errResp = JSON.parse(errBuffer) as ErrorResponse;
103
+ if (errResp.error) {
104
+ throw new ArkAPIError({
105
+ message: errResp.error.message,
106
+ code: errResp.error.code,
107
+ param: errResp.error.param,
108
+ type: errResp.error.type,
109
+ httpStatusCode: 0,
110
+ requestId:
111
+ errResp.error.request_id ??
112
+ this.headers?.["x-client-request-id"] ??
113
+ "",
114
+ });
115
+ }
116
+ }
117
+ }
118
+
119
+ private async *readLines(
120
+ chunks: AsyncIterable<string>,
121
+ ): AsyncGenerator<string> {
122
+ for await (const chunk of chunks) {
123
+ this.buffer += chunk;
124
+ const lines = this.buffer.split("\n");
125
+ this.buffer = lines.pop() ?? "";
126
+ for (const line of lines) {
127
+ yield line;
128
+ }
129
+ }
130
+ if (this.buffer) {
131
+ yield this.buffer;
132
+ this.buffer = "";
133
+ }
134
+ }
135
+
136
+ private async *iterateChunks(): AsyncGenerator<string> {
137
+ const stream = this.stream as any;
138
+ if (typeof stream.getReader === "function") {
139
+ const reader = stream.getReader() as ReadableStreamDefaultReader<Uint8Array>;
140
+ try {
141
+ while (true) {
142
+ const { done, value } = await reader.read();
143
+ if (done) break;
144
+ yield typeof value === "string" ? value : this.decoder.decode(value, { stream: true });
145
+ }
146
+ } finally {
147
+ reader.releaseLock();
148
+ }
149
+ } else if (Symbol.asyncIterator in stream) {
150
+ for await (const chunk of stream) {
151
+ yield typeof chunk === "string" ? chunk : this.decoder.decode(chunk, { stream: true });
152
+ }
153
+ }
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Chat completion stream reader.
159
+ * Yields ChatCompletionStreamResponse chunks.
160
+ * Supports automatic E2EE decryption when keyNonce is provided.
161
+ */
162
+ export class ChatCompletionStreamReader extends DataLineStreamReader<ChatCompletionStreamResponse> {
163
+ private keyNonce: Buffer;
164
+
165
+ constructor(
166
+ stream: ReadableStream<Uint8Array> | NodeJS.ReadableStream,
167
+ emptyMessagesLimit: number,
168
+ headers?: Record<string, string>,
169
+ keyNonce?: Buffer,
170
+ cleanup?: () => void,
171
+ ) {
172
+ super(stream, emptyMessagesLimit, headers, cleanup);
173
+ this.keyNonce = keyNonce ?? Buffer.alloc(0);
174
+ }
175
+
176
+ override async *[Symbol.asyncIterator](): AsyncIterator<ChatCompletionStreamResponse> {
177
+ for await (const chunk of this.iterateBase()) {
178
+ if (this.keyNonce.length > 0) {
179
+ decryptChatStreamResponse(this.keyNonce, chunk);
180
+ }
181
+ yield normalizeChatCompletionStreamChunk(chunk);
182
+ }
183
+ }
184
+
185
+ /** Iterate base class to get raw parsed chunks before decryption */
186
+ private async *iterateBase(): AsyncGenerator<ChatCompletionStreamResponse> {
187
+ yield* super[Symbol.asyncIterator]() as any;
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Bot chat completion stream reader.
193
+ * Yields BotChatCompletionStreamResponse chunks.
194
+ */
195
+ export class BotChatCompletionStreamReader extends DataLineStreamReader<BotChatCompletionStreamResponse> {
196
+ override async *[Symbol.asyncIterator](): AsyncIterator<BotChatCompletionStreamResponse> {
197
+ for await (const chunk of this.iterateBase()) {
198
+ yield normalizeChatCompletionStreamChunk(chunk as any) as any;
199
+ }
200
+ }
201
+
202
+ private async *iterateBase(): AsyncGenerator<BotChatCompletionStreamResponse> {
203
+ yield* super[Symbol.asyncIterator]() as any;
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Image generation stream reader.
209
+ * Yields ImagesStreamResponse chunks.
210
+ */
211
+ export class ImageGenerationStreamReader extends DataLineStreamReader<ImagesStreamResponse> {}
212
+
213
+ /**
214
+ * Responses API stream reader.
215
+ * Uses full SSE event parsing (event: + data:).
216
+ */
217
+ export class ResponsesStreamReader<T = unknown> extends BaseStreamReader<T> {
218
+ private decoder: EventStreamDecoder;
219
+
220
+ constructor(
221
+ stream: ReadableStream<Uint8Array> | NodeJS.ReadableStream,
222
+ emptyMessagesLimit: number,
223
+ headers?: Record<string, string>,
224
+ cleanup?: () => void,
225
+ ) {
226
+ super(stream, emptyMessagesLimit, headers, cleanup);
227
+ this.decoder = new EventStreamDecoder(stream);
228
+ }
229
+
230
+ async *[Symbol.asyncIterator](): AsyncIterator<T> {
231
+ for await (const sseEvent of this.decoder) {
232
+ if (this.isFinished) return;
233
+
234
+ const data = sseEvent.data.trim();
235
+
236
+ // Error check
237
+ if (data.startsWith(ERROR_PREFIX)) {
238
+ const errResp = JSON.parse(data) as ErrorResponse;
239
+ if (errResp.error) {
240
+ throw new ArkAPIError({
241
+ message: errResp.error.message,
242
+ code: errResp.error.code,
243
+ param: errResp.error.param,
244
+ type: errResp.error.type,
245
+ httpStatusCode: 0,
246
+ requestId:
247
+ errResp.error.request_id ??
248
+ this.headers?.["x-client-request-id"] ??
249
+ "",
250
+ });
251
+ }
252
+ continue;
253
+ }
254
+
255
+ // [DONE]
256
+ if (data.startsWith("[DONE]")) {
257
+ this.close();
258
+ return;
259
+ }
260
+
261
+ const parsed = JSON.parse(data) as T;
262
+ yield parsed;
263
+ }
264
+ }
265
+
266
+ override close(): void {
267
+ super.close();
268
+ this.decoder.cancel();
269
+ }
270
+ }