@juspay/neurolink 7.7.1 → 7.8.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 (79) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +33 -2
  3. package/dist/cli/commands/config.d.ts +3 -3
  4. package/dist/cli/commands/sagemaker.d.ts +11 -0
  5. package/dist/cli/commands/sagemaker.js +778 -0
  6. package/dist/cli/factories/commandFactory.js +1 -0
  7. package/dist/cli/index.js +3 -0
  8. package/dist/cli/utils/interactiveSetup.js +28 -0
  9. package/dist/core/baseProvider.d.ts +2 -2
  10. package/dist/core/types.d.ts +1 -0
  11. package/dist/core/types.js +1 -0
  12. package/dist/factories/providerRegistry.js +5 -0
  13. package/dist/lib/core/baseProvider.d.ts +2 -2
  14. package/dist/lib/core/types.d.ts +1 -0
  15. package/dist/lib/core/types.js +1 -0
  16. package/dist/lib/factories/providerRegistry.js +5 -0
  17. package/dist/lib/providers/amazonSagemaker.d.ts +67 -0
  18. package/dist/lib/providers/amazonSagemaker.js +149 -0
  19. package/dist/lib/providers/index.d.ts +4 -0
  20. package/dist/lib/providers/index.js +4 -0
  21. package/dist/lib/providers/sagemaker/adaptive-semaphore.d.ts +86 -0
  22. package/dist/lib/providers/sagemaker/adaptive-semaphore.js +212 -0
  23. package/dist/lib/providers/sagemaker/client.d.ts +156 -0
  24. package/dist/lib/providers/sagemaker/client.js +462 -0
  25. package/dist/lib/providers/sagemaker/config.d.ts +73 -0
  26. package/dist/lib/providers/sagemaker/config.js +308 -0
  27. package/dist/lib/providers/sagemaker/detection.d.ts +176 -0
  28. package/dist/lib/providers/sagemaker/detection.js +596 -0
  29. package/dist/lib/providers/sagemaker/diagnostics.d.ts +37 -0
  30. package/dist/lib/providers/sagemaker/diagnostics.js +137 -0
  31. package/dist/lib/providers/sagemaker/error-constants.d.ts +78 -0
  32. package/dist/lib/providers/sagemaker/error-constants.js +227 -0
  33. package/dist/lib/providers/sagemaker/errors.d.ts +83 -0
  34. package/dist/lib/providers/sagemaker/errors.js +216 -0
  35. package/dist/lib/providers/sagemaker/index.d.ts +35 -0
  36. package/dist/lib/providers/sagemaker/index.js +67 -0
  37. package/dist/lib/providers/sagemaker/language-model.d.ts +182 -0
  38. package/dist/lib/providers/sagemaker/language-model.js +755 -0
  39. package/dist/lib/providers/sagemaker/parsers.d.ts +136 -0
  40. package/dist/lib/providers/sagemaker/parsers.js +625 -0
  41. package/dist/lib/providers/sagemaker/streaming.d.ts +39 -0
  42. package/dist/lib/providers/sagemaker/streaming.js +320 -0
  43. package/dist/lib/providers/sagemaker/structured-parser.d.ts +117 -0
  44. package/dist/lib/providers/sagemaker/structured-parser.js +625 -0
  45. package/dist/lib/providers/sagemaker/types.d.ts +456 -0
  46. package/dist/lib/providers/sagemaker/types.js +7 -0
  47. package/dist/lib/types/cli.d.ts +36 -1
  48. package/dist/providers/amazonSagemaker.d.ts +67 -0
  49. package/dist/providers/amazonSagemaker.js +149 -0
  50. package/dist/providers/index.d.ts +4 -0
  51. package/dist/providers/index.js +4 -0
  52. package/dist/providers/sagemaker/adaptive-semaphore.d.ts +86 -0
  53. package/dist/providers/sagemaker/adaptive-semaphore.js +212 -0
  54. package/dist/providers/sagemaker/client.d.ts +156 -0
  55. package/dist/providers/sagemaker/client.js +462 -0
  56. package/dist/providers/sagemaker/config.d.ts +73 -0
  57. package/dist/providers/sagemaker/config.js +308 -0
  58. package/dist/providers/sagemaker/detection.d.ts +176 -0
  59. package/dist/providers/sagemaker/detection.js +596 -0
  60. package/dist/providers/sagemaker/diagnostics.d.ts +37 -0
  61. package/dist/providers/sagemaker/diagnostics.js +137 -0
  62. package/dist/providers/sagemaker/error-constants.d.ts +78 -0
  63. package/dist/providers/sagemaker/error-constants.js +227 -0
  64. package/dist/providers/sagemaker/errors.d.ts +83 -0
  65. package/dist/providers/sagemaker/errors.js +216 -0
  66. package/dist/providers/sagemaker/index.d.ts +35 -0
  67. package/dist/providers/sagemaker/index.js +67 -0
  68. package/dist/providers/sagemaker/language-model.d.ts +182 -0
  69. package/dist/providers/sagemaker/language-model.js +755 -0
  70. package/dist/providers/sagemaker/parsers.d.ts +136 -0
  71. package/dist/providers/sagemaker/parsers.js +625 -0
  72. package/dist/providers/sagemaker/streaming.d.ts +39 -0
  73. package/dist/providers/sagemaker/streaming.js +320 -0
  74. package/dist/providers/sagemaker/structured-parser.d.ts +117 -0
  75. package/dist/providers/sagemaker/structured-parser.js +625 -0
  76. package/dist/providers/sagemaker/types.d.ts +456 -0
  77. package/dist/providers/sagemaker/types.js +7 -0
  78. package/dist/types/cli.d.ts +36 -1
  79. package/package.json +4 -1
@@ -0,0 +1,320 @@
1
+ /**
2
+ * Streaming response handling for Amazon SageMaker Provider (Phase 2)
3
+ *
4
+ * This module provides full streaming support with automatic protocol detection
5
+ * and model-specific parsing for various SageMaker deployment patterns.
6
+ */
7
+ import { ReadableStream } from "stream/web";
8
+ import { handleSageMakerError, SageMakerError } from "./errors.js";
9
+ import { logger } from "../../utils/logger.js";
10
+ import { createSageMakerDetector, } from "./detection.js";
11
+ import { StreamingParserFactory } from "./parsers.js";
12
+ /**
13
+ * Synthetic streaming delay in milliseconds for simulating real-time response
14
+ */
15
+ const SYNTHETIC_STREAMING_DELAY_MS = 50;
16
+ /**
17
+ * Create a SageMaker streaming response with automatic protocol detection
18
+ *
19
+ * @param responseStream - Raw response stream from SageMaker endpoint
20
+ * @param endpointName - SageMaker endpoint name for capability detection
21
+ * @param config - SageMaker configuration
22
+ * @param options - Stream options and metadata
23
+ * @returns Promise resolving to ReadableStream compatible with AI SDK
24
+ */
25
+ export async function createSageMakerStream(responseStream, endpointName, config, options = {}) {
26
+ const detector = createSageMakerDetector(config);
27
+ try {
28
+ // Detect streaming capabilities for this endpoint
29
+ logger.debug("Detecting streaming capabilities", { endpointName });
30
+ const capability = await detector.detectStreamingCapability(endpointName);
31
+ if (!capability.supported) {
32
+ logger.info("Streaming not supported, falling back to synthetic stream", {
33
+ endpointName,
34
+ modelType: capability.modelType,
35
+ });
36
+ // Create synthetic stream from complete response
37
+ return createSyntheticStreamFromResponse(responseStream, options);
38
+ }
39
+ logger.info("Creating streaming response", {
40
+ endpointName,
41
+ protocol: capability.protocol,
42
+ modelType: capability.modelType,
43
+ confidence: capability.confidence,
44
+ });
45
+ // Create appropriate parser for detected protocol
46
+ const parser = StreamingParserFactory.createParser(capability.protocol);
47
+ return createProtocolSpecificStream(responseStream, parser, capability, options);
48
+ }
49
+ catch (error) {
50
+ logger.error("Failed to create streaming response", {
51
+ endpointName,
52
+ error: error instanceof Error ? error.message : String(error),
53
+ });
54
+ // Fallback to synthetic stream on error
55
+ return createSyntheticStreamFromResponse(responseStream, options);
56
+ }
57
+ }
58
+ /**
59
+ * Create a protocol-specific streaming implementation
60
+ */
61
+ async function createProtocolSpecificStream(responseStream, parser, capability, options) {
62
+ return new ReadableStream({
63
+ async start(controller) {
64
+ const reader = responseStream[Symbol.asyncIterator]();
65
+ let accumulatedText = "";
66
+ let finalUsage;
67
+ try {
68
+ parser.reset();
69
+ while (true) {
70
+ // Check for abort signal
71
+ if (options.abortSignal?.aborted) {
72
+ throw new SageMakerError("Stream aborted by user", "NETWORK_ERROR", 499);
73
+ }
74
+ const { done, value } = await reader.next();
75
+ if (done) {
76
+ // Stream ended - send final chunk if needed
77
+ if (!finalUsage && accumulatedText) {
78
+ finalUsage = estimateTokenUsage("", accumulatedText);
79
+ }
80
+ const finalChunk = {
81
+ type: "finish",
82
+ finishReason: "stop",
83
+ usage: finalUsage,
84
+ };
85
+ controller.enqueue(finalChunk);
86
+ options.onComplete?.(finalUsage || {
87
+ promptTokens: 0,
88
+ completionTokens: 0,
89
+ totalTokens: 0,
90
+ });
91
+ controller.close();
92
+ break;
93
+ }
94
+ // Parse the chunk
95
+ const chunks = parser.parse(value);
96
+ for (const chunk of chunks) {
97
+ // Accumulate text content
98
+ if (chunk.content) {
99
+ accumulatedText += chunk.content;
100
+ }
101
+ // Phase 2.3: Handle streaming tool calls
102
+ if (chunk.toolCall) {
103
+ const toolCallPart = {
104
+ type: "tool-call-delta",
105
+ toolCallType: "function",
106
+ toolCallId: chunk.toolCall.id,
107
+ toolName: chunk.toolCall.name || "",
108
+ argsTextDelta: chunk.toolCall.argumentsDelta || "",
109
+ };
110
+ controller.enqueue(toolCallPart);
111
+ options.onChunk?.(chunk);
112
+ // If tool call is complete, send tool-call part
113
+ if (chunk.toolCall.complete && chunk.toolCall.arguments) {
114
+ const completedToolCall = {
115
+ type: "tool-call",
116
+ toolCallType: "function",
117
+ toolCallId: chunk.toolCall.id,
118
+ toolName: chunk.toolCall.name || "",
119
+ args: JSON.parse(chunk.toolCall.arguments),
120
+ };
121
+ controller.enqueue(completedToolCall);
122
+ }
123
+ continue;
124
+ }
125
+ // Phase 2.3: Handle streaming tool results
126
+ if (chunk.toolResult) {
127
+ const toolResultPart = {
128
+ type: "tool-result",
129
+ toolCallId: chunk.toolResult.toolCallId,
130
+ toolName: chunk.toolResult.toolName,
131
+ result: chunk.toolResult.result,
132
+ args: {}, // Tool args would be tracked separately
133
+ };
134
+ controller.enqueue(toolResultPart);
135
+ options.onChunk?.(chunk);
136
+ continue;
137
+ }
138
+ // Phase 2.3: Handle structured output streaming
139
+ if (chunk.structuredOutput) {
140
+ const structuredPart = {
141
+ type: "object-delta",
142
+ objectDelta: chunk.structuredOutput.partialObject || {},
143
+ objectPath: chunk.structuredOutput.currentPath || "",
144
+ isComplete: chunk.structuredOutput.complete || false,
145
+ validationErrors: chunk.structuredOutput.validationErrors || [],
146
+ };
147
+ controller.enqueue(structuredPart);
148
+ options.onChunk?.(chunk);
149
+ // If structured output is complete, send object part
150
+ if (chunk.structuredOutput.complete &&
151
+ chunk.structuredOutput.partialObject) {
152
+ const completedObject = {
153
+ type: "object",
154
+ object: chunk.structuredOutput.partialObject,
155
+ };
156
+ controller.enqueue(completedObject);
157
+ }
158
+ continue;
159
+ }
160
+ // Regular text content
161
+ if (chunk.content) {
162
+ const streamPart = {
163
+ type: "text-delta",
164
+ textDelta: chunk.content,
165
+ };
166
+ controller.enqueue(streamPart);
167
+ options.onChunk?.(chunk);
168
+ }
169
+ // Check for completion
170
+ if (parser.isComplete(chunk)) {
171
+ finalUsage =
172
+ parser.extractUsage(chunk) ||
173
+ estimateTokenUsage("", accumulatedText);
174
+ const finalChunk = {
175
+ type: "finish",
176
+ finishReason: chunk.finishReason || "stop",
177
+ usage: finalUsage,
178
+ };
179
+ controller.enqueue(finalChunk);
180
+ options.onComplete?.(finalUsage);
181
+ controller.close();
182
+ return;
183
+ }
184
+ }
185
+ }
186
+ }
187
+ catch (error) {
188
+ const sagemakerError = handleSageMakerError(error);
189
+ logger.error("Streaming error", {
190
+ error: sagemakerError.message,
191
+ modelType: capability.modelType,
192
+ protocol: capability.protocol,
193
+ });
194
+ options.onError?.(sagemakerError);
195
+ controller.error(sagemakerError);
196
+ }
197
+ },
198
+ });
199
+ }
200
+ /**
201
+ * Create a synthetic stream from complete response (fallback)
202
+ */
203
+ async function createSyntheticStreamFromResponse(responseStream, options) {
204
+ return new ReadableStream({
205
+ async start(controller) {
206
+ try {
207
+ // Collect complete response
208
+ const chunks = [];
209
+ const reader = responseStream[Symbol.asyncIterator]();
210
+ while (true) {
211
+ const { done, value } = await reader.next();
212
+ if (done) {
213
+ break;
214
+ }
215
+ chunks.push(value);
216
+ }
217
+ // Optimized concatenation: calculate total size first to avoid intermediate arrays
218
+ // This prevents memory allocation overhead for large responses
219
+ let totalSize = 0;
220
+ for (const chunk of chunks) {
221
+ totalSize += chunk.length;
222
+ }
223
+ // Pre-allocate buffer with exact size to avoid reallocations
224
+ const completeData = new Uint8Array(totalSize);
225
+ let offset = 0;
226
+ // Direct memory copy without intermediate buffer creation
227
+ for (const chunk of chunks) {
228
+ completeData.set(chunk, offset);
229
+ offset += chunk.length;
230
+ }
231
+ const responseText = new TextDecoder().decode(completeData);
232
+ const parsedResponse = JSON.parse(responseText);
233
+ // Extract text content
234
+ const text = parsedResponse.generated_text ||
235
+ parsedResponse.text ||
236
+ parsedResponse.output ||
237
+ parsedResponse[0]?.generated_text ||
238
+ String(parsedResponse);
239
+ // Create synthetic streaming by chunking the text
240
+ const words = text.split(/\s+/);
241
+ const chunkSize = Math.max(1, Math.floor(words.length / 10)); // ~10 chunks
242
+ for (let i = 0; i < words.length; i += chunkSize) {
243
+ const chunk = words.slice(i, i + chunkSize).join(" ");
244
+ const deltaChunk = i === 0 ? chunk : " " + chunk;
245
+ const streamPart = {
246
+ type: "text-delta",
247
+ textDelta: deltaChunk,
248
+ };
249
+ controller.enqueue(streamPart);
250
+ options.onChunk?.({
251
+ content: deltaChunk,
252
+ done: false,
253
+ });
254
+ // Add small delay to simulate streaming
255
+ await new Promise((resolve) => setTimeout(resolve, SYNTHETIC_STREAMING_DELAY_MS));
256
+ }
257
+ // Final chunk with usage
258
+ const usage = estimateTokenUsage(options.prompt || "", text);
259
+ const finalChunk = {
260
+ type: "finish",
261
+ finishReason: "stop",
262
+ usage,
263
+ };
264
+ controller.enqueue(finalChunk);
265
+ options.onComplete?.(usage);
266
+ controller.close();
267
+ }
268
+ catch (error) {
269
+ const sagemakerError = handleSageMakerError(error);
270
+ options.onError?.(sagemakerError);
271
+ controller.error(sagemakerError);
272
+ }
273
+ },
274
+ });
275
+ }
276
+ /**
277
+ * Create a synthetic stream from complete text (for backward compatibility)
278
+ */
279
+ export async function createSyntheticStream(text, usage, options = {}) {
280
+ return new ReadableStream({
281
+ start(controller) {
282
+ // Send the complete text as a single delta
283
+ const streamPart = {
284
+ type: "text-delta",
285
+ textDelta: text,
286
+ };
287
+ controller.enqueue(streamPart);
288
+ options.onChunk?.({
289
+ content: text,
290
+ done: false,
291
+ });
292
+ // Send completion
293
+ const finalChunk = {
294
+ type: "finish",
295
+ finishReason: "stop",
296
+ usage,
297
+ };
298
+ controller.enqueue(finalChunk);
299
+ options.onComplete?.(usage);
300
+ controller.close();
301
+ },
302
+ });
303
+ }
304
+ /**
305
+ * Estimate token usage from text content
306
+ *
307
+ * @param prompt - Input prompt text
308
+ * @param completion - Generated completion text
309
+ * @returns Estimated usage information
310
+ */
311
+ export function estimateTokenUsage(prompt, completion) {
312
+ // Simple estimation: ~4 characters per token (rough average for English)
313
+ const promptTokens = Math.ceil(prompt.length / 4);
314
+ const completionTokens = Math.ceil(completion.length / 4);
315
+ return {
316
+ promptTokens,
317
+ completionTokens,
318
+ totalTokens: promptTokens + completionTokens,
319
+ };
320
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Structured Output Streaming Parser (Phase 2.3)
3
+ *
4
+ * This module provides partial JSON parsing for streaming structured output
5
+ * responses from SageMaker endpoints with real-time validation.
6
+ */
7
+ import type { SageMakerStructuredOutput } from "./types.js";
8
+ /**
9
+ * Partial JSON parser for streaming structured output
10
+ */
11
+ export declare class StructuredOutputParser {
12
+ private buffer;
13
+ private currentObject;
14
+ private currentPath;
15
+ private schema?;
16
+ private inString;
17
+ private escapeNext;
18
+ private bracketCount;
19
+ private arrayBracketCount;
20
+ private lastProcessedLength;
21
+ private lastKeyValueParsePosition;
22
+ private bracketTypeStack;
23
+ constructor(schema?: Record<string, unknown>);
24
+ /**
25
+ * Parse a chunk of JSON text and return structured output info
26
+ */
27
+ parseChunk(chunk: string): SageMakerStructuredOutput;
28
+ /**
29
+ * Parse partial JSON by tracking structure with consolidated bracket counting
30
+ */
31
+ private parsePartialJSON;
32
+ /**
33
+ * Consolidated bracket structure processing - handles both counting and path navigation
34
+ * Uses shared bracket counting logic to reduce code duplication
35
+ */
36
+ private processBracketStructure;
37
+ /**
38
+ * Extract partial object from buffer by finding valid JSON fragments
39
+ * Optimized for large JSON strings using true incremental parsing to prevent O(n²) performance
40
+ */
41
+ private extractPartialObject;
42
+ /**
43
+ * Efficiently parse key-value pairs from JSON buffer using true incremental approach
44
+ * Optimized for large JSON strings to avoid O(n²) performance by only processing new content
45
+ */
46
+ private parseKeyValuePairsIncrementally;
47
+ /**
48
+ * Parse a quoted string from buffer starting at given index
49
+ */
50
+ private parseQuotedString;
51
+ /**
52
+ * Parse a JSON value (string, number, boolean, null) from buffer
53
+ */
54
+ private parseJsonValue;
55
+ /**
56
+ * Parse a number from buffer starting at given index
57
+ */
58
+ private parseNumber;
59
+ /**
60
+ * Validate partial object against schema
61
+ */
62
+ private validatePartialObject;
63
+ /**
64
+ * Validate complete object against schema
65
+ */
66
+ private validateAgainstSchema;
67
+ /**
68
+ * Validate a single property against its schema
69
+ */
70
+ private validateProperty;
71
+ /**
72
+ * Check if the current object appears to be complete using efficient bracket counting
73
+ */
74
+ private isObjectComplete;
75
+ /**
76
+ * Optimized whitespace check to replace regex for performance with large strings
77
+ */
78
+ private isWhitespace;
79
+ /**
80
+ * Optimized digit check to replace regex for performance with large strings
81
+ */
82
+ private isDigit;
83
+ /**
84
+ * Optimized hex string validation to replace regex for unicode escape sequences
85
+ */
86
+ private isValidHexString;
87
+ /**
88
+ * Reset parser state
89
+ */
90
+ reset(): void;
91
+ /**
92
+ * Get current parsing state for debugging
93
+ */
94
+ getState(): {
95
+ bufferLength: number;
96
+ currentPath: string[];
97
+ inString: boolean;
98
+ objectKeys: string[];
99
+ bracketCount: number;
100
+ arrayBracketCount: number;
101
+ lastProcessedLength: number;
102
+ lastKeyValueParsePosition: number;
103
+ bracketTypeStack: string[];
104
+ };
105
+ }
106
+ /**
107
+ * Factory function to create structured output parser
108
+ */
109
+ export declare function createStructuredOutputParser(schema?: Record<string, unknown>): StructuredOutputParser;
110
+ /**
111
+ * Utility function to detect if content is structured JSON
112
+ */
113
+ export declare function isStructuredContent(content: string): boolean;
114
+ /**
115
+ * Utility function to extract JSON schema from response format
116
+ */
117
+ export declare function extractSchemaFromResponseFormat(responseFormat: Record<string, unknown>): Record<string, unknown> | undefined;