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