@sinas/sdk 0.1.1 → 0.2.1

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/client.js CHANGED
@@ -5,16 +5,21 @@ import {
5
5
  SinasNotFoundError,
6
6
  SinasPermissionError,
7
7
  SinasTimeoutError,
8
+ approveToolCall,
8
9
  createChat,
9
10
  executeFunction,
10
11
  executeQuery,
12
+ getChat,
13
+ listCollections,
14
+ listFiles,
11
15
  sendMessage,
12
16
  stateDelete,
13
17
  stateGet,
14
18
  stateList,
15
19
  stateSet,
20
+ streamFromChannel,
16
21
  streamMessage
17
- } from "./chunk-M7YHE5Y6.js";
22
+ } from "./chunk-EV5ARSUJ.js";
18
23
  export {
19
24
  SinasAuthError,
20
25
  SinasClient,
@@ -22,14 +27,19 @@ export {
22
27
  SinasNotFoundError,
23
28
  SinasPermissionError,
24
29
  SinasTimeoutError,
30
+ approveToolCall,
25
31
  createChat,
26
32
  executeFunction,
27
33
  executeQuery,
34
+ getChat,
35
+ listCollections,
36
+ listFiles,
28
37
  sendMessage,
29
38
  stateDelete,
30
39
  stateGet,
31
40
  stateList,
32
41
  stateSet,
42
+ streamFromChannel,
33
43
  streamMessage
34
44
  };
35
45
  //# sourceMappingURL=client.js.map
package/dist/index.cjs CHANGED
@@ -37,14 +37,19 @@ __export(src_exports, {
37
37
  SinasPermissionError: () => SinasPermissionError,
38
38
  SinasProvider: () => SinasProvider,
39
39
  SinasTimeoutError: () => SinasTimeoutError,
40
+ approveToolCall: () => approveToolCall,
40
41
  createChat: () => createChat,
41
42
  executeFunction: () => executeFunction,
42
43
  executeQuery: () => executeQuery,
44
+ getChat: () => getChat,
45
+ listCollections: () => listCollections,
46
+ listFiles: () => listFiles,
43
47
  sendMessage: () => sendMessage,
44
48
  stateDelete: () => stateDelete,
45
49
  stateGet: () => stateGet,
46
50
  stateList: () => stateList,
47
51
  stateSet: () => stateSet,
52
+ streamFromChannel: () => streamFromChannel,
48
53
  streamMessage: () => streamMessage,
49
54
  useChat: () => useChat,
50
55
  useExecute: () => useExecute,
@@ -268,6 +273,88 @@ async function sendMessage(client, chatId, content) {
268
273
  createdAt: data.created_at
269
274
  };
270
275
  }
276
+ function mapMessage(data) {
277
+ const toolCalls = data.tool_calls;
278
+ return {
279
+ id: data.id,
280
+ chatId: data.chat_id,
281
+ role: data.role,
282
+ content: data.content ?? null,
283
+ toolCalls: toolCalls ?? null,
284
+ toolCallId: data.tool_call_id ?? null,
285
+ name: data.name ?? null,
286
+ createdAt: data.created_at
287
+ };
288
+ }
289
+ async function getChat(client, chatId) {
290
+ const config = client.getConfig();
291
+ const res = await client.fetch(`${config.apiBase}/chats/${chatId}`);
292
+ const data = await res.json();
293
+ return {
294
+ id: data.id,
295
+ agentNamespace: data.agent_namespace,
296
+ agentName: data.agent_name,
297
+ title: data.title,
298
+ messages: (data.messages || []).map(mapMessage)
299
+ };
300
+ }
301
+ async function approveToolCall(client, chatId, toolCallId, approved) {
302
+ const config = client.getConfig();
303
+ const res = await client.fetch(
304
+ `${config.apiBase}/chats/${chatId}/approve-tool/${toolCallId}`,
305
+ { method: "POST", body: JSON.stringify({ approved }) }
306
+ );
307
+ const data = await res.json();
308
+ return {
309
+ status: data.status,
310
+ toolCallId: data.tool_call_id,
311
+ channelId: data.channel_id
312
+ };
313
+ }
314
+ function streamFromChannel(client, chatId, channelId, onChunk, onDone, onError) {
315
+ const controller = new AbortController();
316
+ const config = client.getConfig();
317
+ (async () => {
318
+ try {
319
+ const res = await fetch(
320
+ `${config.apiBase}/chats/${chatId}/stream/${channelId}?last_id=0`,
321
+ { headers: client.getAuthHeaders(), signal: controller.signal }
322
+ );
323
+ if (!res.ok) {
324
+ const body = await res.json().catch(() => ({ detail: res.statusText }));
325
+ throw new Error(body.detail || "Stream reconnect failed");
326
+ }
327
+ const reader = res.body.getReader();
328
+ const decoder = new TextDecoder();
329
+ let buffer = "";
330
+ while (true) {
331
+ const { done, value } = await reader.read();
332
+ if (done) break;
333
+ buffer += decoder.decode(value, { stream: true });
334
+ const { chunks, remaining } = parseSSE(buffer);
335
+ buffer = remaining;
336
+ for (const chunk of chunks) {
337
+ onChunk(chunk);
338
+ if (chunk.event === "done") {
339
+ onDone?.();
340
+ return;
341
+ }
342
+ }
343
+ }
344
+ if (buffer.trim()) {
345
+ const { chunks } = parseSSE(buffer + "\n\n");
346
+ for (const chunk of chunks) {
347
+ onChunk(chunk);
348
+ }
349
+ }
350
+ onDone?.();
351
+ } catch (err) {
352
+ if (err.name === "AbortError") return;
353
+ onError?.(err);
354
+ }
355
+ })();
356
+ return controller;
357
+ }
271
358
  function parseSSE(buffer) {
272
359
  const chunks = [];
273
360
  const lines = buffer.split("\n");
@@ -341,6 +428,18 @@ function streamMessage(client, chatId, content, onChunk, onDone, onError) {
341
428
  return controller;
342
429
  }
343
430
 
431
+ // src/client/collections.ts
432
+ async function listCollections(client) {
433
+ const base = client.getProxyBase();
434
+ const res = await client.fetch(`${base}/collections`);
435
+ return res.json();
436
+ }
437
+ async function listFiles(client, namespace, collection) {
438
+ const base = client.getProxyBase();
439
+ const res = await client.fetch(`${base}/files/${namespace}/${collection}`);
440
+ return res.json();
441
+ }
442
+
344
443
  // src/hooks/useQuery.ts
345
444
  var import_react2 = __toESM(require("react"), 1);
346
445
 
@@ -516,125 +615,318 @@ function useStateStore(ns, key) {
516
615
 
517
616
  // src/hooks/useChat.ts
518
617
  var import_react5 = __toESM(require("react"), 1);
519
- function useChat(agentRef) {
618
+ function useChat(agentRef, options) {
520
619
  const client = useClient();
521
- const [chatId, setChatId] = import_react5.default.useState(null);
620
+ const [chatId, setChatId] = import_react5.default.useState(options?.chatId ?? null);
522
621
  const [messages, setMessages] = import_react5.default.useState([]);
523
622
  const [loading, setLoading] = import_react5.default.useState(false);
524
623
  const [streaming, setStreaming] = import_react5.default.useState(false);
525
624
  const [error, setError] = import_react5.default.useState(null);
625
+ const [pendingApprovals, setPendingApprovals] = import_react5.default.useState([]);
626
+ const [toolStatus, setToolStatus] = import_react5.default.useState([]);
526
627
  const abortRef = import_react5.default.useRef(null);
628
+ const chatIdRef = import_react5.default.useRef(chatId);
629
+ chatIdRef.current = chatId;
630
+ const activeAssistantIdRef = import_react5.default.useRef("");
631
+ import_react5.default.useEffect(() => {
632
+ if (!options?.chatId) return;
633
+ let cancelled = false;
634
+ setLoading(true);
635
+ getChat(client, options.chatId).then((detail) => {
636
+ if (cancelled) return;
637
+ setChatId(detail.id);
638
+ setMessages(detail.messages);
639
+ }).catch((e) => {
640
+ if (cancelled) return;
641
+ setError(e.message);
642
+ }).finally(() => {
643
+ if (!cancelled) setLoading(false);
644
+ });
645
+ return () => {
646
+ cancelled = true;
647
+ };
648
+ }, [client, options?.chatId]);
527
649
  import_react5.default.useEffect(() => {
528
650
  return () => {
529
651
  abortRef.current?.abort();
530
652
  };
531
653
  }, []);
532
- const doCreateChat = import_react5.default.useCallback(
533
- async (input) => {
534
- setLoading(true);
535
- setError(null);
536
- try {
537
- const result = await createChat(client, agentRef, input);
538
- setChatId(result.id);
539
- setMessages([]);
540
- return result.id;
541
- } catch (e) {
542
- setError(e.message);
543
- } finally {
544
- setLoading(false);
654
+ const handleStreamChunks = import_react5.default.useCallback(
655
+ (chunk) => {
656
+ if (chunk.event === "message") {
657
+ try {
658
+ const parsed = JSON.parse(chunk.data);
659
+ if (parsed.content) {
660
+ const targetId = activeAssistantIdRef.current;
661
+ setMessages(
662
+ (prev) => prev.map(
663
+ (m) => m.id === targetId ? { ...m, content: (m.content || "") + parsed.content } : m
664
+ )
665
+ );
666
+ }
667
+ } catch {
668
+ const targetId = activeAssistantIdRef.current;
669
+ setMessages(
670
+ (prev) => prev.map(
671
+ (m) => m.id === targetId ? { ...m, content: (m.content || "") + chunk.data } : m
672
+ )
673
+ );
674
+ }
675
+ } else if (chunk.event === "tool_start") {
676
+ try {
677
+ const parsed = JSON.parse(chunk.data);
678
+ setToolStatus((prev) => [
679
+ ...prev,
680
+ { toolCallId: parsed.tool_call_id, name: parsed.name, description: parsed.description, status: "running" }
681
+ ]);
682
+ const placeholderId = activeAssistantIdRef.current;
683
+ const toolCallMsg = {
684
+ id: `tmp-tc-${parsed.tool_call_id}`,
685
+ chatId: chatIdRef.current || "",
686
+ role: "assistant",
687
+ content: null,
688
+ toolCalls: [{
689
+ id: parsed.tool_call_id,
690
+ type: "function",
691
+ function: { name: parsed.name, arguments: parsed.arguments || "{}" }
692
+ }],
693
+ toolCallId: null,
694
+ name: null,
695
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
696
+ };
697
+ setMessages((prev) => {
698
+ const filtered = prev.filter(
699
+ (m) => !(m.id === placeholderId && m.streaming && !m.content)
700
+ );
701
+ return [...filtered, toolCallMsg];
702
+ });
703
+ } catch {
704
+ }
705
+ } else if (chunk.event === "tool_end") {
706
+ try {
707
+ const parsed = JSON.parse(chunk.data);
708
+ setToolStatus(
709
+ (prev) => prev.map((t) => t.toolCallId === parsed.tool_call_id ? { ...t, status: "complete" } : t)
710
+ );
711
+ if (parsed.result !== void 0) {
712
+ const toolResultMsg = {
713
+ id: `tmp-tr-${parsed.tool_call_id}`,
714
+ chatId: chatIdRef.current || "",
715
+ role: "tool",
716
+ content: typeof parsed.result === "string" ? parsed.result : JSON.stringify(parsed.result),
717
+ toolCalls: null,
718
+ toolCallId: parsed.tool_call_id,
719
+ name: parsed.name,
720
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
721
+ };
722
+ setMessages((prev) => [...prev, toolResultMsg]);
723
+ }
724
+ setToolStatus((currentStatus) => {
725
+ const stillRunning = currentStatus.filter((t) => t.status === "running");
726
+ if (stillRunning.length === 0) {
727
+ const newAssistantId = `tmp-assistant-${Date.now()}`;
728
+ activeAssistantIdRef.current = newAssistantId;
729
+ const newMsg = {
730
+ id: newAssistantId,
731
+ chatId: chatIdRef.current || "",
732
+ role: "assistant",
733
+ content: "",
734
+ toolCalls: null,
735
+ toolCallId: null,
736
+ name: null,
737
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
738
+ streaming: true
739
+ };
740
+ setMessages((prev) => [...prev, newMsg]);
741
+ }
742
+ return currentStatus;
743
+ });
744
+ } catch {
745
+ }
746
+ } else if (chunk.event === "approval_required") {
747
+ try {
748
+ const parsed = JSON.parse(chunk.data);
749
+ setPendingApprovals((prev) => [...prev, parsed]);
750
+ } catch {
751
+ }
752
+ } else if (chunk.event === "error") {
753
+ try {
754
+ const parsed = JSON.parse(chunk.data);
755
+ setError(parsed.error || "Stream error");
756
+ } catch {
757
+ setError(chunk.data || "Stream error");
758
+ }
545
759
  }
546
760
  },
547
- [client, agentRef]
761
+ []
548
762
  );
549
- const doSendMessage = import_react5.default.useCallback(
550
- async (content) => {
551
- if (!chatId) {
552
- setError("No active chat. Call createChat first.");
553
- return;
554
- }
555
- setLoading(true);
556
- setError(null);
557
- const userMsg = {
558
- id: `tmp-${Date.now()}`,
559
- chatId,
560
- role: "user",
561
- content,
562
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
563
- };
564
- setMessages((prev) => [...prev, userMsg]);
763
+ const refreshMessages = import_react5.default.useCallback(
764
+ async (id) => {
565
765
  try {
566
- const msg = await sendMessage(client, chatId, content);
567
- setMessages((prev) => [...prev, msg]);
568
- return msg;
569
- } catch (e) {
570
- setError(e.message);
571
- } finally {
572
- setLoading(false);
766
+ const detail = await getChat(client, id);
767
+ setMessages(detail.messages);
768
+ } catch {
573
769
  }
574
770
  },
575
- [client, chatId]
771
+ [client]
576
772
  );
577
- const doStreamMessage = import_react5.default.useCallback(
773
+ const send = import_react5.default.useCallback(
578
774
  (content) => {
579
- if (!chatId) {
580
- setError("No active chat. Call createChat first.");
581
- return;
582
- }
583
- abortRef.current?.abort();
775
+ const doSend = async () => {
776
+ let activeChatId = chatIdRef.current;
777
+ if (!activeChatId) {
778
+ setLoading(true);
779
+ setError(null);
780
+ try {
781
+ const result = await createChat(client, agentRef, options?.input);
782
+ activeChatId = result.id;
783
+ setChatId(result.id);
784
+ chatIdRef.current = result.id;
785
+ } catch (e) {
786
+ setError(e.message);
787
+ setLoading(false);
788
+ return;
789
+ }
790
+ setLoading(false);
791
+ }
792
+ abortRef.current?.abort();
793
+ setStreaming(true);
794
+ setError(null);
795
+ setPendingApprovals([]);
796
+ setToolStatus([]);
797
+ const displayContent = typeof content === "string" ? content : JSON.stringify(content);
798
+ const userMsg = {
799
+ id: `tmp-user-${Date.now()}`,
800
+ chatId: activeChatId,
801
+ role: "user",
802
+ content: displayContent,
803
+ toolCalls: null,
804
+ toolCallId: null,
805
+ name: null,
806
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
807
+ };
808
+ setMessages((prev) => [...prev, userMsg]);
809
+ const assistantId = `tmp-assistant-${Date.now()}`;
810
+ activeAssistantIdRef.current = assistantId;
811
+ const assistantMsg = {
812
+ id: assistantId,
813
+ chatId: activeChatId,
814
+ role: "assistant",
815
+ content: "",
816
+ toolCalls: null,
817
+ toolCallId: null,
818
+ name: null,
819
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
820
+ streaming: true
821
+ };
822
+ setMessages((prev) => [...prev, assistantMsg]);
823
+ const controller = streamMessage(
824
+ client,
825
+ activeChatId,
826
+ content,
827
+ handleStreamChunks,
828
+ () => {
829
+ setStreaming(false);
830
+ setToolStatus([]);
831
+ refreshMessages(activeChatId);
832
+ },
833
+ (err) => {
834
+ setError(err.message);
835
+ setStreaming(false);
836
+ setToolStatus([]);
837
+ }
838
+ );
839
+ abortRef.current = controller;
840
+ };
841
+ doSend();
842
+ },
843
+ [client, agentRef, options?.input, handleStreamChunks, refreshMessages]
844
+ );
845
+ const handleApproval = import_react5.default.useCallback(
846
+ (toolCallId, approved) => {
847
+ const currentChatId = chatIdRef.current;
848
+ if (!currentChatId) return;
849
+ setPendingApprovals((prev) => prev.filter((a) => a.toolCallId !== toolCallId));
584
850
  setStreaming(true);
585
851
  setError(null);
586
- const userMsg = {
587
- id: `tmp-${Date.now()}`,
588
- chatId,
589
- role: "user",
590
- content,
591
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
592
- };
593
- setMessages((prev) => [...prev, userMsg]);
594
- const assistantId = `stream-${Date.now()}`;
595
- const assistantMsg = {
596
- id: assistantId,
597
- chatId,
598
- role: "assistant",
599
- content: "",
600
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
601
- };
602
- setMessages((prev) => [...prev, assistantMsg]);
603
- const controller = streamMessage(
604
- client,
605
- chatId,
606
- content,
607
- (chunk) => {
608
- if (chunk.event === "content" || chunk.event === "message") {
609
- setMessages(
610
- (prev) => prev.map(
611
- (m) => m.id === assistantId ? { ...m, content: m.content + chunk.data } : m
612
- )
613
- );
852
+ const assistantId = `tmp-resume-${Date.now()}`;
853
+ activeAssistantIdRef.current = assistantId;
854
+ if (approved) {
855
+ const assistantMsg = {
856
+ id: assistantId,
857
+ chatId: currentChatId,
858
+ role: "assistant",
859
+ content: "",
860
+ toolCalls: null,
861
+ toolCallId: null,
862
+ name: null,
863
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
864
+ streaming: true
865
+ };
866
+ setMessages((prev) => [...prev, assistantMsg]);
867
+ }
868
+ (async () => {
869
+ try {
870
+ const result = await approveToolCall(client, currentChatId, toolCallId, approved);
871
+ if (!approved) {
872
+ setStreaming(false);
873
+ refreshMessages(currentChatId);
874
+ return;
614
875
  }
615
- },
616
- () => {
617
- setStreaming(false);
618
- },
619
- (err) => {
620
- setError(err.message);
876
+ const controller = streamFromChannel(
877
+ client,
878
+ currentChatId,
879
+ result.channelId,
880
+ handleStreamChunks,
881
+ () => {
882
+ setStreaming(false);
883
+ setToolStatus([]);
884
+ refreshMessages(currentChatId);
885
+ },
886
+ (err) => {
887
+ setError(err.message);
888
+ setStreaming(false);
889
+ setToolStatus([]);
890
+ }
891
+ );
892
+ abortRef.current = controller;
893
+ } catch (e) {
894
+ setError(e.message);
621
895
  setStreaming(false);
622
896
  }
623
- );
624
- abortRef.current = controller;
625
- return controller;
897
+ })();
626
898
  },
627
- [client, chatId]
899
+ [client, handleStreamChunks, refreshMessages]
900
+ );
901
+ const approve = import_react5.default.useCallback(
902
+ (toolCallId) => handleApproval(toolCallId, true),
903
+ [handleApproval]
628
904
  );
905
+ const reject = import_react5.default.useCallback(
906
+ (toolCallId) => handleApproval(toolCallId, false),
907
+ [handleApproval]
908
+ );
909
+ const stop = import_react5.default.useCallback(() => {
910
+ abortRef.current?.abort();
911
+ abortRef.current = null;
912
+ setStreaming(false);
913
+ }, []);
914
+ const clearError = import_react5.default.useCallback(() => {
915
+ setError(null);
916
+ }, []);
629
917
  return {
630
918
  chatId,
631
919
  messages,
632
920
  loading,
633
921
  streaming,
634
922
  error,
635
- createChat: doCreateChat,
636
- sendMessage: doSendMessage,
637
- streamMessage: doStreamMessage
923
+ pendingApprovals,
924
+ toolStatus,
925
+ send,
926
+ approve,
927
+ reject,
928
+ stop,
929
+ clearError
638
930
  };
639
931
  }
640
932
 
@@ -660,14 +952,19 @@ function useSinas() {
660
952
  SinasPermissionError,
661
953
  SinasProvider,
662
954
  SinasTimeoutError,
955
+ approveToolCall,
663
956
  createChat,
664
957
  executeFunction,
665
958
  executeQuery,
959
+ getChat,
960
+ listCollections,
961
+ listFiles,
666
962
  sendMessage,
667
963
  stateDelete,
668
964
  stateGet,
669
965
  stateList,
670
966
  stateSet,
967
+ streamFromChannel,
671
968
  streamMessage,
672
969
  useChat,
673
970
  useExecute,