@axiom-lattice/client-sdk 1.0.22 → 1.0.23

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/README.md CHANGED
@@ -14,6 +14,7 @@ The Client SDK provides a simple interface for interacting with the Axiom Lattic
14
14
  - Register client-side tools
15
15
  - Handle tool calls and responses
16
16
  - Process streaming message chunks with ChunkMessageMerger
17
+ - Standardized HTTP client using axios for all API calls
17
18
 
18
19
  ## Installation
19
20
 
@@ -32,6 +33,7 @@ const client = new Client({
32
33
  apiKey: "your-api-key",
33
34
  assistantId: "your-assistant-id",
34
35
  transport: "sse",
36
+ environment: "web", // Optional: "web" (default) or "wechat-miniprogram"
35
37
  });
36
38
 
37
39
  // Create a thread
@@ -121,6 +123,35 @@ const messages = merger.getMessages();
121
123
 
122
124
  ## Migration Notes
123
125
 
126
+ ### Runtime Environment Support
127
+
128
+ As of version 1.0.13, the client SDK now supports different runtime environments through the `environment` configuration option. Currently supported environments are:
129
+
130
+ - `"web"` (default): Uses axios for all HTTP requests, including streaming responses
131
+ - `"wechat-miniprogram"`: Uses a custom WeChat Mini Program HTTP client implementation for compatibility with WeChat Mini Programs
132
+
133
+ To specify the environment:
134
+
135
+ ```typescript
136
+ const client = new Client({
137
+ // ... other options
138
+ environment: "wechat-miniprogram", // For WeChat Mini Program environments
139
+ });
140
+ ```
141
+
142
+ The WeChat Mini Program environment uses a different approach for streaming responses since true streaming is not supported in that environment. It uses a polling mechanism instead.
143
+
144
+ ### HTTP Client Standardization
145
+
146
+ As of version 1.0.12, the client SDK now uses axios exclusively for all HTTP requests, including streaming responses. Previously, the SDK used a combination of axios for regular requests and the native fetch API for streaming. This change standardizes the HTTP client implementation across the SDK.
147
+
148
+ Benefits:
149
+
150
+ - Consistent error handling
151
+ - Unified request/response interceptors
152
+ - Simplified maintenance and debugging
153
+ - Better compatibility with different JavaScript environments
154
+
124
155
  ### ChunkMessageMerger
125
156
 
126
157
  The `ChunkMessageMerger` module has been moved from the web project to the client-sdk package. It provides functionality for processing streaming message chunks and merging them into complete messages.
package/dist/index.d.ts CHANGED
@@ -9,6 +9,10 @@ import { Message, MessageChunk, AssistantMessage } from '@axiom-lattice/protocol
9
9
  * Note: Currently only "ws" is used for tool registration
10
10
  */
11
11
  type Transport = "sse" | "ws";
12
+ /**
13
+ * Runtime environment for the client
14
+ */
15
+ type RuntimeEnvironment = "web" | "wechat-miniprogram";
12
16
  /**
13
17
  * Configuration options for the Client
14
18
  */
@@ -29,6 +33,11 @@ interface ClientConfig {
29
33
  * Transport method (Server-Sent Events or WebSocket)
30
34
  */
31
35
  transport: Transport;
36
+ /**
37
+ * Runtime environment (web or wechat-miniprogram)
38
+ * Defaults to "web" if not specified
39
+ */
40
+ environment?: RuntimeEnvironment;
32
41
  /**
33
42
  * Request timeout in milliseconds (optional, defaults to 30000)
34
43
  */
@@ -110,6 +119,10 @@ interface ChatStreamOptions extends ChatSendOptions {
110
119
  * Whether to run in background (optional)
111
120
  */
112
121
  background?: boolean;
122
+ /**
123
+ * Whether to return agent state when stream completes (optional)
124
+ */
125
+ enableReturnStateWhenSteamCompleted?: boolean;
113
126
  }
114
127
  /**
115
128
  * Stream callbacks interface
@@ -120,9 +133,9 @@ interface StreamCallbacks {
120
133
  */
121
134
  onEvent: (event: MessageChunk) => void;
122
135
  /**
123
- * Called when the stream completes
136
+ * Called when the stream completes, with optional agent state
124
137
  */
125
- onComplete?: () => void;
138
+ onComplete?: (state?: AgentState) => void;
126
139
  /**
127
140
  * Called when an error occurs
128
141
  */
@@ -220,6 +233,10 @@ interface RunOptions {
220
233
  * Whether to run in background (optional)
221
234
  */
222
235
  background?: boolean;
236
+ /**
237
+ * Whether to return agent state when stream completes (optional)
238
+ */
239
+ enableReturnStateWhenSteamCompleted?: boolean;
223
240
  }
224
241
  /**
225
242
  * Agent state
@@ -259,6 +276,7 @@ declare class Client {
259
276
  private assistantId;
260
277
  private tenantId;
261
278
  private registeredTools;
279
+ private environment;
262
280
  /**
263
281
  * Creates a new Client instance
264
282
  * @param config - Configuration options for the client
@@ -330,6 +348,16 @@ declare class Client {
330
348
  * @returns A function that can be called to stop the stream
331
349
  */
332
350
  private streamRun;
351
+ /**
352
+ * Stream run results using axios for web environment
353
+ * @private
354
+ */
355
+ private streamRunWeb;
356
+ /**
357
+ * Stream run results using WeChat Mini Program request API
358
+ * @private
359
+ */
360
+ private streamRunWechat;
333
361
  /**
334
362
  * Chat namespace for sending messages and streaming responses
335
363
  */
@@ -348,7 +376,7 @@ declare class Client {
348
376
  * @param onError - Optional callback function called when an error occurs
349
377
  * @returns A function that can be called to stop the stream
350
378
  */
351
- stream: (options: ChatStreamOptions, onEvent: (event: MessageChunk) => void, onComplete?: () => void, onError?: (error: Error) => void) => (() => void);
379
+ stream: (options: ChatStreamOptions, onEvent: (event: MessageChunk) => void, onComplete?: (state?: AgentState) => void, onError?: (error: Error) => void) => (() => void);
352
380
  };
353
381
  /**
354
382
  * Tools namespace for registering and unregistering client-side tools
@@ -385,4 +413,4 @@ declare function createSimpleMessageMerger(): {
385
413
  reset: () => void;
386
414
  };
387
415
 
388
- export { AgentState, ApiError, AuthenticationError, ChatResponse, ChatSendOptions, ChatStreamOptions, Client, ClientConfig, CreateThreadOptions, GetMessagesOptions, ListThreadsOptions, NetworkError, RegisterToolOptions, RetryConfig, RunOptions, StreamCallbacks, Thread, Transport, createSimpleMessageMerger };
416
+ export { AgentState, ApiError, AuthenticationError, ChatResponse, ChatSendOptions, ChatStreamOptions, Client, ClientConfig, CreateThreadOptions, GetMessagesOptions, ListThreadsOptions, NetworkError, RegisterToolOptions, RetryConfig, RunOptions, RuntimeEnvironment, StreamCallbacks, Thread, Transport, createSimpleMessageMerger };
package/dist/index.js CHANGED
@@ -62,6 +62,181 @@ var AuthenticationError = class extends Error {
62
62
 
63
63
  // src/client.ts
64
64
  var import_axios = __toESM(require("axios"));
65
+
66
+ // src/wechat-client.ts
67
+ var import_miniprogram_api_promise = require("miniprogram-api-promise");
68
+ function createWechatClient(config) {
69
+ let wxApi = null;
70
+ if (typeof global !== "undefined" && global.wx) {
71
+ wxApi = global.wx;
72
+ } else if (typeof globalThis !== "undefined" && globalThis.wx) {
73
+ wxApi = globalThis.wx;
74
+ } else {
75
+ try {
76
+ const win = globalThis;
77
+ if (win.wx) {
78
+ wxApi = win.wx;
79
+ }
80
+ } catch (e) {
81
+ }
82
+ }
83
+ if (!wxApi) {
84
+ throw new Error("WeChat Mini Program environment not detected");
85
+ }
86
+ const request = (0, import_miniprogram_api_promise.promisify)(wxApi.request);
87
+ const defaultHeaders = {
88
+ "Content-Type": "application/json",
89
+ ...config.headers
90
+ };
91
+ const interceptors = {
92
+ request: {
93
+ handlers: [],
94
+ use(fulfilled, rejected) {
95
+ const id = this.handlers.length;
96
+ this.handlers.push({ fulfilled, rejected });
97
+ return id;
98
+ },
99
+ eject(id) {
100
+ if (this.handlers[id]) {
101
+ this.handlers[id] = {};
102
+ }
103
+ }
104
+ },
105
+ response: {
106
+ handlers: [],
107
+ use(fulfilled, rejected) {
108
+ const id = this.handlers.length;
109
+ this.handlers.push({ fulfilled, rejected });
110
+ return id;
111
+ },
112
+ eject(id) {
113
+ if (this.handlers[id]) {
114
+ this.handlers[id] = {};
115
+ }
116
+ }
117
+ }
118
+ };
119
+ const processRequestInterceptors = (requestConfig) => {
120
+ let config2 = { ...requestConfig };
121
+ for (const handler of interceptors.request.handlers) {
122
+ if (handler.fulfilled) {
123
+ try {
124
+ config2 = handler.fulfilled(config2);
125
+ } catch (error) {
126
+ if (handler.rejected) {
127
+ handler.rejected(error);
128
+ }
129
+ throw error;
130
+ }
131
+ }
132
+ }
133
+ return config2;
134
+ };
135
+ const processResponseInterceptors = async (response) => {
136
+ let result = response;
137
+ for (const handler of interceptors.response.handlers) {
138
+ if (handler.fulfilled) {
139
+ try {
140
+ result = await handler.fulfilled(result);
141
+ } catch (error) {
142
+ if (handler.rejected) {
143
+ result = await handler.rejected(error);
144
+ } else {
145
+ throw error;
146
+ }
147
+ }
148
+ }
149
+ }
150
+ return result;
151
+ };
152
+ const processErrorInterceptors = async (error) => {
153
+ let result = error;
154
+ for (const handler of interceptors.response.handlers) {
155
+ if (handler.rejected) {
156
+ try {
157
+ result = await handler.rejected(result);
158
+ } catch (newError) {
159
+ result = newError;
160
+ }
161
+ }
162
+ }
163
+ return Promise.reject(result);
164
+ };
165
+ const CancelToken = {
166
+ source() {
167
+ let cancel = () => {
168
+ };
169
+ const token = {
170
+ promise: new Promise((resolve) => {
171
+ cancel = (message = "Request cancelled") => resolve(message);
172
+ }),
173
+ reason: void 0
174
+ };
175
+ return {
176
+ token,
177
+ cancel
178
+ };
179
+ }
180
+ };
181
+ const isCancel = (value) => {
182
+ return value && value.__CANCEL__;
183
+ };
184
+ const makeRequest = async (method, url, data, customConfig) => {
185
+ try {
186
+ const requestConfig = processRequestInterceptors({
187
+ url: `${config.baseURL}${url}`,
188
+ method,
189
+ data,
190
+ header: { ...defaultHeaders, ...customConfig?.headers },
191
+ timeout: config.timeout || 3e4,
192
+ ...customConfig
193
+ });
194
+ if (customConfig?.cancelToken) {
195
+ customConfig.cancelToken.promise.then((reason) => {
196
+ throw Object.assign(new Error(reason), { __CANCEL__: true });
197
+ });
198
+ }
199
+ const response = await request(requestConfig);
200
+ const transformedResponse = {
201
+ data: response.data,
202
+ status: response.statusCode,
203
+ statusText: response.errMsg,
204
+ headers: response.header,
205
+ config: requestConfig
206
+ };
207
+ return await processResponseInterceptors(transformedResponse);
208
+ } catch (error) {
209
+ return processErrorInterceptors(error);
210
+ }
211
+ };
212
+ const client = {
213
+ get: (url, customConfig) => makeRequest("GET", url, null, customConfig),
214
+ post: (url, data, customConfig) => makeRequest("POST", url, data, customConfig),
215
+ delete: (url, customConfig) => makeRequest("DELETE", url, null, customConfig),
216
+ put: (url, data, customConfig) => makeRequest("PUT", url, data, customConfig),
217
+ patch: (url, data, customConfig) => makeRequest("PATCH", url, data, customConfig),
218
+ head: (url, customConfig) => makeRequest("HEAD", url, null, customConfig),
219
+ options: (url, customConfig) => makeRequest("OPTIONS", url, null, customConfig),
220
+ defaults: {
221
+ headers: defaultHeaders
222
+ },
223
+ interceptors: {
224
+ request: {
225
+ use: interceptors.request.use.bind(interceptors.request),
226
+ eject: interceptors.request.eject.bind(interceptors.request)
227
+ },
228
+ response: {
229
+ use: interceptors.response.use.bind(interceptors.response),
230
+ eject: interceptors.response.eject.bind(interceptors.response)
231
+ }
232
+ },
233
+ CancelToken,
234
+ isCancel
235
+ };
236
+ return client;
237
+ }
238
+
239
+ // src/client.ts
65
240
  var Client = class {
66
241
  /**
67
242
  * Creates a new Client instance
@@ -114,7 +289,8 @@ var Client = class {
114
289
  threadId: options.threadId,
115
290
  message: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
116
291
  streaming: true,
117
- background: options.background
292
+ background: options.background,
293
+ enableReturnStateWhenSteamCompleted: options.enableReturnStateWhenSteamCompleted
118
294
  },
119
295
  onEvent,
120
296
  onComplete,
@@ -148,18 +324,33 @@ var Client = class {
148
324
  };
149
325
  this.config = {
150
326
  timeout: 3e4,
327
+ environment: "web",
328
+ // Default to web environment
151
329
  ...config
152
330
  };
331
+ this.environment = this.config.environment || "web";
153
332
  this.assistantId = config.assistantId;
154
- this.client = import_axios.default.create({
155
- baseURL: this.config.baseURL,
156
- timeout: this.config.timeout,
157
- headers: {
158
- "Content-Type": "application/json",
159
- Authorization: `Bearer ${this.config.apiKey}`,
160
- ...this.config.headers
161
- }
162
- });
333
+ if (this.environment === "wechat-miniprogram") {
334
+ this.client = createWechatClient({
335
+ baseURL: this.config.baseURL,
336
+ timeout: this.config.timeout,
337
+ headers: {
338
+ "Content-Type": "application/json",
339
+ Authorization: `Bearer ${this.config.apiKey}`,
340
+ ...this.config.headers
341
+ }
342
+ });
343
+ } else {
344
+ this.client = import_axios.default.create({
345
+ baseURL: this.config.baseURL,
346
+ timeout: this.config.timeout,
347
+ headers: {
348
+ "Content-Type": "application/json",
349
+ Authorization: `Bearer ${this.config.apiKey}`,
350
+ ...this.config.headers
351
+ }
352
+ });
353
+ }
163
354
  this.setupInterceptors();
164
355
  }
165
356
  /**
@@ -361,24 +552,30 @@ var Client = class {
361
552
  * @returns A function that can be called to stop the stream
362
553
  */
363
554
  streamRun(options, onEvent, onComplete, onError) {
555
+ if (this.environment === "wechat-miniprogram") {
556
+ return this.streamRunWechat(options, onEvent, onComplete, onError);
557
+ } else {
558
+ return this.streamRunWeb(options, onEvent, onComplete, onError);
559
+ }
560
+ }
561
+ /**
562
+ * Stream run results using axios for web environment
563
+ * @private
564
+ */
565
+ streamRunWeb(options, onEvent, onComplete, onError) {
364
566
  const headers = {
365
567
  "Content-Type": "application/json"
366
568
  };
367
569
  if (this.tenantId) {
368
570
  headers["x-tenant-id"] = this.tenantId;
369
571
  }
370
- const controller = new AbortController();
371
- const signal = controller.signal;
572
+ const source = import_axios.default.CancelToken.source();
372
573
  (async () => {
373
574
  try {
374
- const response = await fetch(`${this.config.baseURL}/api/runs`, {
375
- method: "POST",
376
- headers: {
377
- "Content-Type": "application/json",
378
- Authorization: `Bearer ${this.config.apiKey}`,
379
- ...headers
380
- },
381
- body: JSON.stringify({
575
+ const axiosClient = this.client;
576
+ const response = await axiosClient.post(
577
+ "/api/runs",
578
+ {
382
579
  assistant_id: this.assistantId,
383
580
  thread_id: options.threadId,
384
581
  message: options.message,
@@ -386,54 +583,47 @@ var Client = class {
386
583
  command: options.command,
387
584
  streaming: true,
388
585
  background: options.background || false
389
- }),
390
- signal
391
- });
392
- if (!response.ok) {
393
- throw new Error(`HTTP error! status: ${response.status}`);
394
- }
395
- if (!response.body) {
396
- throw new Error("Response body is null");
397
- }
398
- const reader = response.body.getReader();
399
- const decoder = new TextDecoder();
400
- let buffer = "";
401
- while (true) {
402
- const { value, done } = await reader.read();
403
- if (done) {
404
- if (buffer.trim()) {
405
- try {
406
- const data = JSON.parse(buffer.trim());
407
- onEvent(data);
408
- } catch (error) {
409
- }
410
- }
411
- break;
412
- }
413
- const chunk = decoder.decode(value, { stream: true });
414
- buffer += chunk;
415
- const lines = buffer.split("\n");
416
- buffer = lines.pop() || "";
417
- for (const line of lines) {
418
- if (line.trim().startsWith("data: ")) {
419
- try {
420
- const data = JSON.parse(line.trim().slice(6));
421
- onEvent(data);
422
- } catch (error) {
423
- if (onError) {
424
- onError(
425
- error instanceof Error ? error : new Error(String(error))
426
- );
586
+ },
587
+ {
588
+ headers,
589
+ cancelToken: source.token,
590
+ responseType: "text",
591
+ transformResponse: (data) => {
592
+ if (!data)
593
+ return data;
594
+ const lines = data.split("\n");
595
+ for (const line of lines) {
596
+ if (line.trim().startsWith("data: ")) {
597
+ try {
598
+ const eventData = JSON.parse(line.trim().slice(6));
599
+ onEvent(eventData);
600
+ } catch (error) {
601
+ if (onError) {
602
+ onError(
603
+ error instanceof Error ? error : new Error(String(error))
604
+ );
605
+ }
606
+ }
427
607
  }
428
608
  }
609
+ return data;
429
610
  }
430
611
  }
431
- }
612
+ );
432
613
  if (onComplete) {
433
- onComplete();
614
+ if (options.enableReturnStateWhenSteamCompleted) {
615
+ try {
616
+ const state = await this.getAgentState(options.threadId);
617
+ onComplete(state);
618
+ } catch (error) {
619
+ onComplete();
620
+ }
621
+ } else {
622
+ onComplete();
623
+ }
434
624
  }
435
625
  } catch (error) {
436
- if (signal.aborted) {
626
+ if (import_axios.default.isCancel(error)) {
437
627
  return;
438
628
  }
439
629
  if (onError) {
@@ -442,7 +632,74 @@ var Client = class {
442
632
  }
443
633
  })();
444
634
  return () => {
445
- controller.abort();
635
+ source.cancel("Request cancelled by user");
636
+ };
637
+ }
638
+ /**
639
+ * Stream run results using WeChat Mini Program request API
640
+ * @private
641
+ */
642
+ streamRunWechat(options, onEvent, onComplete, onError) {
643
+ const wechatClient = this.client;
644
+ let isRunning = true;
645
+ let messageId = null;
646
+ const poll = async () => {
647
+ if (!isRunning)
648
+ return;
649
+ try {
650
+ if (!messageId) {
651
+ const response = await wechatClient.post("/api/runs", {
652
+ assistant_id: this.assistantId,
653
+ thread_id: options.threadId,
654
+ message: options.message,
655
+ files: options.files,
656
+ command: options.command,
657
+ streaming: false,
658
+ // We can't do true streaming in WeChat
659
+ background: options.background || false
660
+ });
661
+ if (response.data && response.data.id) {
662
+ messageId = response.data.id;
663
+ onEvent({
664
+ type: "ai",
665
+ data: {
666
+ id: messageId,
667
+ content: response.data.output || ""
668
+ }
669
+ });
670
+ if (response.data.tool_calls && response.data.tool_calls.length > 0) {
671
+ onEvent({
672
+ type: "ai",
673
+ data: {
674
+ id: messageId,
675
+ tool_calls: response.data.tool_calls
676
+ }
677
+ });
678
+ }
679
+ if (onComplete) {
680
+ if (options.enableReturnStateWhenSteamCompleted) {
681
+ try {
682
+ const state = await this.getAgentState(options.threadId);
683
+ onComplete(state);
684
+ } catch (error) {
685
+ onComplete();
686
+ }
687
+ } else {
688
+ onComplete();
689
+ }
690
+ }
691
+ }
692
+ }
693
+ } catch (error) {
694
+ if (onError) {
695
+ onError(error instanceof Error ? error : new Error(String(error)));
696
+ }
697
+ isRunning = false;
698
+ }
699
+ };
700
+ poll();
701
+ return () => {
702
+ isRunning = false;
446
703
  };
447
704
  }
448
705
  };
@@ -529,7 +786,6 @@ function createSimpleMessageMerger() {
529
786
  messages = msgs;
530
787
  }
531
788
  function push(chunk) {
532
- console.log(chunk);
533
789
  const role = normalizeRole(chunk.type);
534
790
  const message = ensureMessage(chunk.data.id, role);
535
791
  if (chunk.data.content) {
@@ -565,7 +821,8 @@ function createSimpleMessageMerger() {
565
821
  name: tc.name,
566
822
  args: tc.args,
567
823
  type: "tool_call",
568
- response: tc.response
824
+ response: tc.response,
825
+ status: "success"
569
826
  }));
570
827
  if (toolCalls.length > 0) {
571
828
  message.tool_calls = toolCalls;
@@ -608,7 +865,8 @@ function createSimpleMessageMerger() {
608
865
  if (toolResponse) {
609
866
  return {
610
867
  ...toolCall,
611
- response: toolResponse.content
868
+ response: toolResponse.content,
869
+ status: "success"
612
870
  };
613
871
  }
614
872
  return toolCall;