@chaoslabs/ai-sdk 0.0.3 → 0.0.5

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.
@@ -0,0 +1,359 @@
1
+ // HTTP Streaming Module
2
+ //
3
+ // This module provides low-level HTTP streaming using Node's native http/https modules.
4
+ // It yields data incrementally as it arrives, unlike fetch which buffers the response.
5
+ import * as http from 'node:http';
6
+ import * as https from 'node:https';
7
+ import { ChaosError, ChaosTimeoutError } from './types.js';
8
+ // ============================================================================
9
+ // httpStreamRequest - Low-level streaming function
10
+ // ============================================================================
11
+ /**
12
+ * Makes an HTTP request using Node's native http/https modules and returns
13
+ * an async iterator that yields chunks as they arrive (true streaming).
14
+ *
15
+ * @param options - Request options
16
+ * @returns AsyncIterable that yields string chunks
17
+ */
18
+ export function httpStreamRequest(options) {
19
+ const { url, method, headers, body, timeout } = options;
20
+ return {
21
+ [Symbol.asyncIterator]() {
22
+ const parsedUrl = new URL(url);
23
+ const isHttps = parsedUrl.protocol === 'https:';
24
+ const httpModule = isHttps ? https : http;
25
+ let request = null;
26
+ let response = null;
27
+ let destroyed = false;
28
+ let pendingChunks = [];
29
+ let resolveNext = null;
30
+ let rejectNext = null;
31
+ let ended = false;
32
+ let error = null;
33
+ let requestStarted = false;
34
+ let requestPromise = null;
35
+ // Store timeout reference so we can restart it
36
+ let timeoutId = null;
37
+ // Store the startRequest reject function so timeout can use it
38
+ let startRequestReject = null;
39
+ const cleanup = () => {
40
+ if (timeoutId) {
41
+ clearTimeout(timeoutId);
42
+ timeoutId = null;
43
+ }
44
+ if (request && !destroyed) {
45
+ destroyed = true;
46
+ request.destroy();
47
+ }
48
+ if (response) {
49
+ response.removeAllListeners();
50
+ }
51
+ };
52
+ const resetTimeout = () => {
53
+ if (timeoutId) {
54
+ clearTimeout(timeoutId);
55
+ }
56
+ if (!ended && !destroyed && !error) {
57
+ timeoutId = setTimeout(() => {
58
+ const err = new ChaosTimeoutError(timeout);
59
+ // Set error BEFORE cleanup so that handleEnd doesn't resolve
60
+ error = err;
61
+ // Reject the appropriate promise
62
+ if (rejectNext) {
63
+ // We're waiting for data during iteration
64
+ const reject = rejectNext;
65
+ rejectNext = null;
66
+ resolveNext = null;
67
+ reject(err);
68
+ }
69
+ else if (startRequestReject) {
70
+ // We're still waiting for the response headers
71
+ const reject = startRequestReject;
72
+ startRequestReject = null;
73
+ reject(err);
74
+ }
75
+ cleanup();
76
+ }, timeout);
77
+ }
78
+ };
79
+ const handleError = (err) => {
80
+ if (error)
81
+ return; // Already have an error
82
+ error = err;
83
+ cleanup();
84
+ if (rejectNext) {
85
+ rejectNext(err);
86
+ rejectNext = null;
87
+ resolveNext = null;
88
+ }
89
+ };
90
+ const handleData = (chunk) => {
91
+ // Reset timeout on each data chunk
92
+ resetTimeout();
93
+ const str = chunk.toString('utf8');
94
+ if (resolveNext) {
95
+ resolveNext({ value: str, done: false });
96
+ resolveNext = null;
97
+ rejectNext = null;
98
+ }
99
+ else {
100
+ pendingChunks.push(str);
101
+ }
102
+ };
103
+ const handleEnd = () => {
104
+ ended = true;
105
+ // Don't cleanup yet - let the caller handle it
106
+ if (timeoutId) {
107
+ clearTimeout(timeoutId);
108
+ timeoutId = null;
109
+ }
110
+ if (resolveNext && !error) {
111
+ resolveNext({ value: undefined, done: true });
112
+ resolveNext = null;
113
+ rejectNext = null;
114
+ }
115
+ };
116
+ // Start the request
117
+ const requestOptions = {
118
+ hostname: parsedUrl.hostname,
119
+ port: parsedUrl.port || (isHttps ? 443 : 80),
120
+ path: parsedUrl.pathname + parsedUrl.search,
121
+ method,
122
+ headers: {
123
+ ...headers,
124
+ 'Content-Length': Buffer.byteLength(body, 'utf8').toString(),
125
+ },
126
+ };
127
+ const startRequest = () => {
128
+ return new Promise((resolve, reject) => {
129
+ // Store reject so timeout can use it
130
+ startRequestReject = reject;
131
+ // Start initial timeout
132
+ resetTimeout();
133
+ request = httpModule.request(requestOptions, (res) => {
134
+ response = res;
135
+ // Reset timeout when we get response
136
+ resetTimeout();
137
+ // Check for HTTP errors
138
+ if (res.statusCode && res.statusCode >= 400) {
139
+ let errorBody = '';
140
+ res.on('data', (chunk) => {
141
+ errorBody += chunk.toString();
142
+ });
143
+ res.on('end', () => {
144
+ const err = new ChaosError(`HTTP error: ${res.statusCode} ${res.statusMessage || ''}`, res.statusCode);
145
+ handleError(err);
146
+ reject(err);
147
+ });
148
+ res.on('error', () => {
149
+ // Ignore errors during error body collection
150
+ });
151
+ return;
152
+ }
153
+ // Set up data handlers
154
+ res.on('data', handleData);
155
+ res.on('end', handleEnd);
156
+ res.on('error', (err) => {
157
+ handleError(new ChaosError(`Stream error: ${err.message}`));
158
+ });
159
+ res.on('close', () => {
160
+ // If closed without ending properly, it's an error
161
+ if (!ended && !error) {
162
+ handleError(new ChaosError('Connection closed unexpectedly'));
163
+ }
164
+ });
165
+ // Clear startRequestReject since we've resolved
166
+ startRequestReject = null;
167
+ resolve();
168
+ });
169
+ request.on('error', (err) => {
170
+ const chaosErr = new ChaosError(`Connection error: ${err.message}`);
171
+ handleError(chaosErr);
172
+ reject(chaosErr);
173
+ });
174
+ request.on('timeout', () => {
175
+ const err = new ChaosTimeoutError(timeout);
176
+ handleError(err);
177
+ reject(err);
178
+ });
179
+ // Send the request body
180
+ request.write(body);
181
+ request.end();
182
+ });
183
+ };
184
+ return {
185
+ async next() {
186
+ // Start request on first next() call
187
+ if (!requestStarted) {
188
+ requestStarted = true;
189
+ requestPromise = startRequest();
190
+ try {
191
+ await requestPromise;
192
+ }
193
+ catch (err) {
194
+ throw err;
195
+ }
196
+ }
197
+ // Check for error
198
+ if (error) {
199
+ throw error;
200
+ }
201
+ // Check for pending chunks
202
+ if (pendingChunks.length > 0) {
203
+ return { value: pendingChunks.shift(), done: false };
204
+ }
205
+ // Check if ended
206
+ if (ended) {
207
+ return { value: undefined, done: true };
208
+ }
209
+ // Wait for next chunk (timeout will fire if nothing comes)
210
+ return new Promise((resolve, reject) => {
211
+ resolveNext = resolve;
212
+ rejectNext = reject;
213
+ });
214
+ },
215
+ async return() {
216
+ cleanup();
217
+ return { value: undefined, done: true };
218
+ },
219
+ async throw(err) {
220
+ cleanup();
221
+ throw err;
222
+ },
223
+ };
224
+ },
225
+ };
226
+ }
227
+ // ============================================================================
228
+ // StreamingHttpClient - High-level streaming client
229
+ // ============================================================================
230
+ /**
231
+ * High-level HTTP streaming client that provides methods for streaming
232
+ * requests with proper chunk and line parsing.
233
+ */
234
+ export class StreamingHttpClient {
235
+ baseUrl;
236
+ timeout;
237
+ aborted = false;
238
+ currentIterator = null;
239
+ constructor(options) {
240
+ this.baseUrl = options.baseUrl;
241
+ this.timeout = options.timeout;
242
+ }
243
+ /**
244
+ * Stream raw chunks from a request.
245
+ *
246
+ * @param path - URL path (will be appended to baseUrl)
247
+ * @param options - Request options
248
+ * @returns AsyncIterable of raw string chunks
249
+ */
250
+ stream(path, options) {
251
+ const fullUrl = this.baseUrl + path;
252
+ this.aborted = false;
253
+ const self = this;
254
+ const innerIterable = httpStreamRequest({
255
+ url: fullUrl,
256
+ method: options.method,
257
+ headers: options.headers || {},
258
+ body: options.body,
259
+ timeout: this.timeout,
260
+ });
261
+ return {
262
+ [Symbol.asyncIterator]() {
263
+ const iterator = innerIterable[Symbol.asyncIterator]();
264
+ self.currentIterator = iterator;
265
+ return {
266
+ async next() {
267
+ if (self.aborted) {
268
+ // Call return to cleanup the underlying iterator
269
+ await iterator.return?.();
270
+ throw new ChaosError('Request aborted');
271
+ }
272
+ try {
273
+ const result = await iterator.next();
274
+ if (self.aborted) {
275
+ await iterator.return?.();
276
+ throw new ChaosError('Request aborted');
277
+ }
278
+ return result;
279
+ }
280
+ catch (err) {
281
+ if (self.aborted) {
282
+ throw new ChaosError('Request aborted');
283
+ }
284
+ throw err;
285
+ }
286
+ },
287
+ async return() {
288
+ self.currentIterator = null;
289
+ return iterator.return?.() || { value: undefined, done: true };
290
+ },
291
+ async throw(err) {
292
+ self.currentIterator = null;
293
+ return iterator.throw?.(err) || Promise.reject(err);
294
+ },
295
+ };
296
+ },
297
+ };
298
+ }
299
+ /**
300
+ * Stream parsed NDJSON lines from a request.
301
+ *
302
+ * @param path - URL path (will be appended to baseUrl)
303
+ * @param options - Request options
304
+ * @returns AsyncIterable of complete JSON lines (without newline)
305
+ */
306
+ streamLines(path, options) {
307
+ const rawStream = this.stream(path, options);
308
+ return {
309
+ [Symbol.asyncIterator]() {
310
+ const rawIterator = rawStream[Symbol.asyncIterator]();
311
+ let buffer = '';
312
+ return {
313
+ async next() {
314
+ while (true) {
315
+ // Check for complete lines in buffer
316
+ const newlineIndex = buffer.indexOf('\n');
317
+ if (newlineIndex !== -1) {
318
+ const line = buffer.substring(0, newlineIndex);
319
+ buffer = buffer.substring(newlineIndex + 1);
320
+ if (line.trim()) {
321
+ return { value: line, done: false };
322
+ }
323
+ continue;
324
+ }
325
+ // Get more data
326
+ const result = await rawIterator.next();
327
+ if (result.done) {
328
+ // Handle remaining buffer
329
+ if (buffer.trim()) {
330
+ const remaining = buffer.trim();
331
+ buffer = '';
332
+ return { value: remaining, done: false };
333
+ }
334
+ return { value: undefined, done: true };
335
+ }
336
+ buffer += result.value;
337
+ }
338
+ },
339
+ async return() {
340
+ return rawIterator.return?.() || { value: undefined, done: true };
341
+ },
342
+ async throw(err) {
343
+ return rawIterator.throw?.(err) || Promise.reject(err);
344
+ },
345
+ };
346
+ },
347
+ };
348
+ }
349
+ /**
350
+ * Abort the current in-progress request.
351
+ */
352
+ abort() {
353
+ this.aborted = true;
354
+ if (this.currentIterator) {
355
+ this.currentIterator.return?.();
356
+ this.currentIterator = null;
357
+ }
358
+ }
359
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export { Chaos as default, Chaos, WALLET_MODEL, ASK_MODEL } from './client.js';
2
+ export { httpStreamRequest, StreamingHttpClient } from './http-streaming.js';
3
+ export type { HttpStreamOptions, StreamingHttpClientOptions, StreamRequestOptions } from './http-streaming.js';
2
4
  export type { V1WalletRequest, V1AskRequest } from './request.js';
3
5
  export type { V1StreamEvent, V1FinalState } from './response.js';
4
6
  export type { ChaosConfig, CreateResponseParams, RequestMetadata, InputItem, Response, OutputItem, ContentPart, Block, OutputText, ChaosBlock, MarkdownBlock, TableBlock, TableColumnType, TableSortDirection, TableColumnConfig, ChartBlock, ChartSeries, ChartSegment, ChartAxis, ChartDataPoint, TransactionActionBlock, Primitive, PrimitiveIcon, PrimitiveLineItem, PrimitiveDisplay, RawTransaction, TransactionGroup, Risks, RiskInfoItem, InteractiveBlock, InteractiveOption, ResponseError, } from './types.js';
@@ -6,8 +8,5 @@ export { ChaosError, ChaosTimeoutError } from './types.js';
6
8
  export { extractText, extractBlocks, hasRisks, hasBlockers, isTableBlock, isChartBlock, isTransactionActionBlock, isInteractiveBlock, isMarkdownBlock, isOutputText, isChaosBlock, } from './types.js';
7
9
  export { BlockSchema, parseRawBlock, detectBlockType } from './schemas.js';
8
10
  export type { BlockParsed } from './schemas.js';
9
- export { Conversation } from './conversation.js';
10
- export type { ConversationOptions, ConversationStats } from './conversation.js';
11
- export { PRIMITIVE_SWAP, PRIMITIVE_SUPPLY, PRIMITIVE_WITHDRAW, PRIMITIVE_BORROW, PRIMITIVE_REPAY, PRIMITIVE_STAKE, PRIMITIVE_UNSTAKE, PRIMITIVE_CLAIM, PRIMITIVE_BRIDGE, PRIMITIVE_ADD_LIQUIDITY, PRIMITIVE_REMOVE_LIQUIDITY, PRIMITIVE_OPEN_POSITION, PRIMITIVE_CLOSE_POSITION, PRIMITIVE_DEPOSIT, PRIMITIVE_MINT, PRIMITIVE_BURN, PRIMITIVE_RESTAKE, PRIMITIVE_APPROVE, PRIMITIVE_TRANSFER, PRIMITIVE_WRAP, PRIMITIVE_UNWRAP, ALL_PRIMITIVES, LENDING_PRIMITIVES, TRADING_PRIMITIVES, STAKING_PRIMITIVES, LIQUIDITY_PRIMITIVES, TRANSFER_PRIMITIVES, isValidPrimitive, isLendingPrimitive, isTradingPrimitive, isStakingPrimitive, isLiquidityPrimitive, isTransferPrimitive, } from './primitives.js';
12
- export type { PrimitiveType } from './primitives.js';
13
- export { extractTableBlocks, extractChartBlocks, extractTransactionBlocks, extractMarkdownBlocks, extractInteractiveBlocks, findTableByTitle, findChartByTitle, findTransactionsByPrimitive, extractPrimitives, extractPrimitivesByType, getPrimitiveTypes, tableToObjects, getTableColumn, findTableRow, getTableDimensions, getChartData, getChartTotal, getChartPercentages, getAllWarnings, getAllBlockers, getHighestRiskLevel, countBlocksByType, hasBlocks, hasBlockType, } from './blocks.js';
11
+ export { isAgentStatusMessage, isAgentMessage, isReportMessage, isFollowUpSuggestions, isUserInputMessage, parseAgentStatus, isTerminalStatus, extractAgentMessageText, extractSuggestions, extractReportBlock, parseStreamLine, parseStreamLines, } from './stream.js';
12
+ export type { MessageType, AgentStatus, StreamMessage, StreamMessageContext, } from './stream.js';
package/dist/index.js CHANGED
@@ -1,34 +1,11 @@
1
1
  // Chaos AI SDK V1 - Public API
2
2
  export { Chaos as default, Chaos, WALLET_MODEL, ASK_MODEL } from './client.js';
3
+ export { httpStreamRequest, StreamingHttpClient } from './http-streaming.js';
3
4
  // Export error classes
4
5
  export { ChaosError, ChaosTimeoutError } from './types.js';
5
6
  // Export helper functions
6
7
  export { extractText, extractBlocks, hasRisks, hasBlockers, isTableBlock, isChartBlock, isTransactionActionBlock, isInteractiveBlock, isMarkdownBlock, isOutputText, isChaosBlock, } from './types.js';
7
8
  // Export schemas for validation
8
9
  export { BlockSchema, parseRawBlock, detectBlockType } from './schemas.js';
9
- // Export Conversation class
10
- export { Conversation } from './conversation.js';
11
- // Export primitive constants and helpers
12
- export {
13
- // Primitive type constants
14
- PRIMITIVE_SWAP, PRIMITIVE_SUPPLY, PRIMITIVE_WITHDRAW, PRIMITIVE_BORROW, PRIMITIVE_REPAY, PRIMITIVE_STAKE, PRIMITIVE_UNSTAKE, PRIMITIVE_CLAIM, PRIMITIVE_BRIDGE, PRIMITIVE_ADD_LIQUIDITY, PRIMITIVE_REMOVE_LIQUIDITY, PRIMITIVE_OPEN_POSITION, PRIMITIVE_CLOSE_POSITION, PRIMITIVE_DEPOSIT, PRIMITIVE_MINT, PRIMITIVE_BURN, PRIMITIVE_RESTAKE, PRIMITIVE_APPROVE, PRIMITIVE_TRANSFER, PRIMITIVE_WRAP, PRIMITIVE_UNWRAP,
15
- // Primitive categories
16
- ALL_PRIMITIVES, LENDING_PRIMITIVES, TRADING_PRIMITIVES, STAKING_PRIMITIVES, LIQUIDITY_PRIMITIVES, TRANSFER_PRIMITIVES,
17
- // Primitive helpers
18
- isValidPrimitive, isLendingPrimitive, isTradingPrimitive, isStakingPrimitive, isLiquidityPrimitive, isTransferPrimitive, } from './primitives.js';
19
- // Export block utilities
20
- export {
21
- // Block extraction
22
- extractTableBlocks, extractChartBlocks, extractTransactionBlocks, extractMarkdownBlocks, extractInteractiveBlocks,
23
- // Block search
24
- findTableByTitle, findChartByTitle, findTransactionsByPrimitive,
25
- // Primitive extraction
26
- extractPrimitives, extractPrimitivesByType, getPrimitiveTypes,
27
- // Table utilities
28
- tableToObjects, getTableColumn, findTableRow, getTableDimensions,
29
- // Chart utilities
30
- getChartData, getChartTotal, getChartPercentages,
31
- // Risk utilities
32
- getAllWarnings, getAllBlockers, getHighestRiskLevel,
33
- // Block statistics
34
- countBlocksByType, hasBlocks, hasBlockType, } from './blocks.js';
10
+ // Export stream message utilities
11
+ export { isAgentStatusMessage, isAgentMessage, isReportMessage, isFollowUpSuggestions, isUserInputMessage, parseAgentStatus, isTerminalStatus, extractAgentMessageText, extractSuggestions, extractReportBlock, parseStreamLine, parseStreamLines, } from './stream.js';