@agentscope-ai/chat 1.1.20 → 1.1.22
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/bin/client.js +1 -1
- package/components/AGUI/components/HelpModal/icons.tsx +68 -0
- package/components/AGUI/components/HelpModal/index.tsx +1 -0
- package/components/AGUI/components/HelpModal/modal.tsx +101 -0
- package/components/AGUI/components/chat/Button.tsx +18 -0
- package/components/AGUI/components/chat/Chat.tsx +780 -0
- package/components/AGUI/components/chat/ChatContext.tsx +248 -0
- package/components/AGUI/components/chat/CodeBlock.tsx +406 -0
- package/components/AGUI/components/chat/Header.tsx +22 -0
- package/components/AGUI/components/chat/Icons.tsx +237 -0
- package/components/AGUI/components/chat/ImageUploadQueue.tsx +77 -0
- package/components/AGUI/components/chat/Input.tsx +24 -0
- package/components/AGUI/components/chat/Markdown.tsx +134 -0
- package/components/AGUI/components/chat/Messages.tsx +259 -0
- package/components/AGUI/components/chat/Modal.tsx +133 -0
- package/components/AGUI/components/chat/Popup.tsx +57 -0
- package/components/AGUI/components/chat/PoweredByTag.tsx +29 -0
- package/components/AGUI/components/chat/Sidebar.tsx +74 -0
- package/components/AGUI/components/chat/Suggestion.tsx +132 -0
- package/components/AGUI/components/chat/Suggestions.tsx +20 -0
- package/components/AGUI/components/chat/Textarea.tsx +61 -0
- package/components/AGUI/components/chat/Window.tsx +152 -0
- package/components/AGUI/components/chat/index.tsx +11 -0
- package/components/AGUI/components/chat/messages/AssistantMessage.tsx +69 -0
- package/components/AGUI/components/chat/messages/RenderActionExecutionMessage.tsx +129 -0
- package/components/AGUI/components/chat/messages/RenderAgentStateMessage.tsx +116 -0
- package/components/AGUI/components/chat/messages/RenderImageMessage.tsx +64 -0
- package/components/AGUI/components/chat/messages/RenderResultMessage.tsx +26 -0
- package/components/AGUI/components/chat/messages/RenderTextMessage.tsx +51 -0
- package/components/AGUI/components/chat/messages/UserMessage.tsx +10 -0
- package/components/AGUI/components/chat/props.ts +186 -0
- package/components/AGUI/components/index.ts +1 -0
- package/components/AGUI/context/index.ts +1 -0
- package/components/AGUI/hooks/index.ts +1 -0
- package/components/AGUI/hooks/use-copilot-chat-suggestions.tsx +122 -0
- package/components/AGUI/hooks/use-copy-to-clipboard.tsx +29 -0
- package/components/AGUI/hooks/use-dark-mode.ts +10 -0
- package/components/AGUI/hooks/use-push-to-talk.tsx +166 -0
- package/components/AGUI/index.tsx +4 -0
- package/components/AGUI/lib/utils.test.ts +7 -0
- package/components/AGUI/lib/utils.ts +27 -0
- package/components/AGUI/styles.css +0 -0
- package/components/AGUI/types/css.ts +0 -0
- package/components/AGUI/types/index.ts +1 -0
- package/components/AGUI/types/suggestions.ts +6 -0
- package/components/Accordion/Accordion.tsx +203 -0
- package/components/Accordion/BodyContent.tsx +28 -0
- package/components/Accordion/DeepThinking.tsx +91 -0
- package/components/Accordion/SoftLightTitle.tsx +13 -0
- package/components/Accordion/demo/search.tsx +39 -0
- package/components/Accordion/demo/steps.tsx +135 -0
- package/components/Accordion/demo/thinking.tsx +62 -0
- package/components/Accordion/index.en-US.md +34 -0
- package/components/Accordion/index.tsx +3 -0
- package/components/Accordion/index.zh-CN.md +34 -0
- package/components/Accordion/style.ts +208 -0
- package/components/AgentScopeRuntimeWebUI/demo/index.tsx +4 -0
- package/components/AgentScopeRuntimeWebUI/index.tsx +3 -0
- package/components/AgentScopeRuntimeWebUI/lib/AgentScopeRuntime/Request/Builder.tsx +82 -0
- package/components/AgentScopeRuntimeWebUI/lib/AgentScopeRuntime/Request/Card.tsx +52 -0
- package/components/AgentScopeRuntimeWebUI/lib/AgentScopeRuntime/Response/Actions.tsx +39 -0
- package/components/AgentScopeRuntimeWebUI/lib/AgentScopeRuntime/Response/Builder.tsx +205 -0
- package/components/AgentScopeRuntimeWebUI/lib/AgentScopeRuntime/Response/Card.tsx +44 -0
- package/components/AgentScopeRuntimeWebUI/lib/AgentScopeRuntime/Response/Error.tsx +7 -0
- package/components/AgentScopeRuntimeWebUI/lib/AgentScopeRuntime/Response/Message.tsx +23 -0
- package/components/AgentScopeRuntimeWebUI/lib/AgentScopeRuntime/Response/Reasoning.tsx +16 -0
- package/components/AgentScopeRuntimeWebUI/lib/AgentScopeRuntime/Response/Tool.tsx +19 -0
- package/components/AgentScopeRuntimeWebUI/lib/AgentScopeRuntime/index.tsx +0 -0
- package/components/AgentScopeRuntimeWebUI/lib/AgentScopeRuntime/types.tsx +100 -0
- package/components/AgentScopeRuntimeWebUI/lib/Chat/Input/index.tsx +77 -0
- package/components/AgentScopeRuntimeWebUI/lib/Chat/Input/useAttachments.tsx +53 -0
- package/components/AgentScopeRuntimeWebUI/lib/Chat/MessageList/index.tsx +30 -0
- package/components/AgentScopeRuntimeWebUI/lib/Chat/Welcome/index.tsx +66 -0
- package/components/AgentScopeRuntimeWebUI/lib/Chat/Welcome/styles.ts +51 -0
- package/components/AgentScopeRuntimeWebUI/lib/Chat/hooks/index.tsx +5 -0
- package/components/AgentScopeRuntimeWebUI/lib/Chat/hooks/useChatController.tsx +130 -0
- package/components/AgentScopeRuntimeWebUI/lib/Chat/hooks/useChatMessageHandler.tsx +87 -0
- package/components/AgentScopeRuntimeWebUI/lib/Chat/hooks/useChatRequest.tsx +114 -0
- package/components/AgentScopeRuntimeWebUI/lib/Chat/hooks/useChatSessionHandler.tsx +50 -0
- package/components/AgentScopeRuntimeWebUI/lib/Chat/index.tsx +18 -0
- package/components/AgentScopeRuntimeWebUI/lib/Chat/styles.tsx +59 -0
- package/components/AgentScopeRuntimeWebUI/lib/ChatAnywhere/ComposedProvider.tsx +27 -0
- package/components/AgentScopeRuntimeWebUI/lib/ChatAnywhere/index.tsx +29 -0
- package/components/AgentScopeRuntimeWebUI/lib/Context/ChatAnywhereControl.tsx +0 -0
- package/components/AgentScopeRuntimeWebUI/lib/Context/ChatAnywhereInputContext.tsx +27 -0
- package/components/AgentScopeRuntimeWebUI/lib/Context/ChatAnywhereLayoutContext.tsx +27 -0
- package/components/AgentScopeRuntimeWebUI/lib/Context/ChatAnywhereMessagesContext.tsx +82 -0
- package/components/AgentScopeRuntimeWebUI/lib/Context/ChatAnywhereOptionsContext.tsx +76 -0
- package/components/AgentScopeRuntimeWebUI/lib/Context/ChatAnywhereSessionsContext.tsx +111 -0
- package/components/AgentScopeRuntimeWebUI/lib/Context/useChatAnywhereEventEmitter.tsx +30 -0
- package/components/AgentScopeRuntimeWebUI/lib/Header/index.tsx +45 -0
- package/components/AgentScopeRuntimeWebUI/lib/Header/styles.ts +23 -0
- package/components/AgentScopeRuntimeWebUI/lib/Layout/index.tsx +45 -0
- package/components/AgentScopeRuntimeWebUI/lib/Layout/styles.tsx +104 -0
- package/components/AgentScopeRuntimeWebUI/lib/Sessions/index.tsx +114 -0
- package/components/AgentScopeRuntimeWebUI/lib/Sessions/styles.tsx +0 -0
- package/components/AgentScopeRuntimeWebUI/lib/demo/OptionsPanel/FormItem.tsx +37 -0
- package/components/AgentScopeRuntimeWebUI/lib/demo/OptionsPanel/OptionsEditor.tsx +163 -0
- package/components/AgentScopeRuntimeWebUI/lib/demo/OptionsPanel/defaultConfig.ts +43 -0
- package/components/AgentScopeRuntimeWebUI/lib/demo/OptionsPanel/index.tsx +27 -0
- package/components/AgentScopeRuntimeWebUI/lib/demo/index.tsx +57 -0
- package/components/AgentScopeRuntimeWebUI/lib/demo/sessionApi/index.ts +51 -0
- package/components/AgentScopeRuntimeWebUI/lib/types/IChatAnywhere.ts +358 -0
- package/components/AgentScopeRuntimeWebUI/lib/types/IMessages.tsx +52 -0
- package/components/AgentScopeRuntimeWebUI/lib/types/ISessions.tsx +29 -0
- package/components/AgentScopeRuntimeWebUI/lib/types/index.tsx +3 -0
- package/components/AgentScopeRuntimeWebUI/starter/OptionsPanel/FormItem.tsx +37 -0
- package/components/AgentScopeRuntimeWebUI/starter/OptionsPanel/OptionsEditor.tsx +163 -0
- package/components/AgentScopeRuntimeWebUI/starter/OptionsPanel/defaultConfig.ts +43 -0
- package/components/AgentScopeRuntimeWebUI/starter/OptionsPanel/index.tsx +27 -0
- package/components/AgentScopeRuntimeWebUI/starter/index.tsx +57 -0
- package/components/AgentScopeRuntimeWebUI/starter/sessionApi/index.ts +51 -0
- package/components/Attachments/DropArea.tsx +97 -0
- package/components/Attachments/FileList/AudioIcon.tsx +20 -0
- package/components/Attachments/FileList/FileListCard.tsx +270 -0
- package/components/Attachments/FileList/Progress.tsx +30 -0
- package/components/Attachments/FileList/VideoIcon.tsx +20 -0
- package/components/Attachments/FileList/index.tsx +201 -0
- package/components/Attachments/PlaceholderUploader.tsx +124 -0
- package/components/Attachments/SilentUploader.tsx +39 -0
- package/components/Attachments/context.tsx +11 -0
- package/components/Attachments/demo/basic.tsx +71 -0
- package/components/Attachments/index.en-US.md.bk +10 -0
- package/components/Attachments/index.tsx +274 -0
- package/components/Attachments/index.zh-CN.md.bk +10 -0
- package/components/Attachments/style/fileCard.ts +163 -0
- package/components/Attachments/style/index.ts +232 -0
- package/components/Attachments/util.ts +56 -0
- package/components/Bubble/Avatar.tsx +39 -0
- package/components/Bubble/Bubble.tsx +131 -0
- package/components/Bubble/BubbleList.tsx +105 -0
- package/components/Bubble/Cards.tsx +61 -0
- package/components/Bubble/Footer.tsx +55 -0
- package/components/Bubble/Interrupted.tsx +82 -0
- package/components/Bubble/ScrollToBottom.tsx +42 -0
- package/components/Bubble/Spin.tsx +13 -0
- package/components/Bubble/demo/assistantWithCode.tsx +60 -0
- package/components/Bubble/demo/assistantWithErrorStatus.tsx +102 -0
- package/components/Bubble/demo/assistantWithImage.tsx +46 -0
- package/components/Bubble/demo/assistantWithProcess.tsx +103 -0
- package/components/Bubble/demo/assistantWithSearch.tsx +69 -0
- package/components/Bubble/demo/assistantWithThinking.tsx +54 -0
- package/components/Bubble/demo/basic.tsx +55 -0
- package/components/Bubble/demo/userWithFile.tsx +27 -0
- package/components/Bubble/demo/userWithImage.tsx +26 -0
- package/components/Bubble/hooks/useDisplayData.ts +0 -0
- package/components/Bubble/hooks/useListData.ts +0 -0
- package/components/Bubble/index.en-US.md +33 -0
- package/components/Bubble/index.tsx +21 -0
- package/components/Bubble/index.zh-CN.md +33 -0
- package/components/Bubble/interface.ts +71 -0
- package/components/Bubble/loading.tsx +15 -0
- package/components/Bubble/style/avatar.ts +43 -0
- package/components/Bubble/style/footer.ts +45 -0
- package/components/Bubble/style/index.ts +192 -0
- package/components/Bubble/style/list.ts +51 -0
- package/components/ChatAnywhere/Chat/Ref.tsx +20 -0
- package/components/ChatAnywhere/Chat/index.tsx +56 -0
- package/components/ChatAnywhere/Chat/style.ts +59 -0
- package/components/ChatAnywhere/Header/index.tsx +37 -0
- package/components/ChatAnywhere/Header/style.ts +27 -0
- package/components/ChatAnywhere/Input/index.tsx +158 -0
- package/components/ChatAnywhere/Input/style.ts +14 -0
- package/components/ChatAnywhere/Layout/index.tsx +70 -0
- package/components/ChatAnywhere/Layout/style.ts +51 -0
- package/components/ChatAnywhere/SessionList/index.tsx +122 -0
- package/components/ChatAnywhere/SessionList/style.ts +53 -0
- package/components/ChatAnywhere/hooks/ChatAnywhereProvider.tsx +219 -0
- package/components/ChatAnywhere/hooks/types.ts +333 -0
- package/components/ChatAnywhere/hooks/useInput.tsx +22 -0
- package/components/ChatAnywhere/hooks/useMessages.tsx +63 -0
- package/components/ChatAnywhere/hooks/useSessionList.tsx +123 -0
- package/components/ChatAnywhere/index.tsx +49 -0
- package/components/Conversations/GroupTitle.tsx +28 -0
- package/components/Conversations/Item.tsx +189 -0
- package/components/Conversations/demo/basic.tsx +107 -0
- package/components/Conversations/demo/timeline.tsx +111 -0
- package/components/Conversations/demo/timestamp.tsx +110 -0
- package/components/Conversations/hooks/useGroupable.ts +81 -0
- package/components/Conversations/index.en-US.md +32 -0
- package/components/Conversations/index.tsx +176 -0
- package/components/Conversations/index.zh-CN.md +31 -0
- package/components/Conversations/interface.ts +69 -0
- package/components/Conversations/style.ts +153 -0
- package/components/DefaultCards/DeepThinking/index.tsx +33 -0
- package/components/DefaultCards/Files/index.tsx +69 -0
- package/components/DefaultCards/Footer/index.tsx +16 -0
- package/components/DefaultCards/Images/index.tsx +53 -0
- package/components/DefaultCards/Interrupted/index.tsx +13 -0
- package/components/DefaultCards/Text/index.tsx +13 -0
- package/components/DefaultCards/index.tsx +6 -0
- package/components/DeviceAction/actionMap.tsx +100 -0
- package/components/DeviceAction/demo/index.tsx +132 -0
- package/components/DeviceAction/index.en-US.md +19 -0
- package/components/DeviceAction/index.tsx +75 -0
- package/components/DeviceAction/index.zh-CN.md +19 -0
- package/components/Disclaimer/demo/index.tsx +4 -0
- package/components/Disclaimer/demo/withLink.tsx +4 -0
- package/components/Disclaimer/index.en-US.md +23 -0
- package/components/Disclaimer/index.tsx +42 -0
- package/components/Disclaimer/index.zh-CN.md +24 -0
- package/components/Disclaimer/style.ts +17 -0
- package/components/ImageGenerator/demo/basic.tsx +23 -0
- package/components/ImageGenerator/demo/custom.tsx +56 -0
- package/components/ImageGenerator/demo/size.tsx +15 -0
- package/components/ImageGenerator/index.en-US.md +23 -0
- package/components/ImageGenerator/index.tsx +124 -0
- package/components/ImageGenerator/index.zh-CN.md +23 -0
- package/components/ImageGenerator/style.ts +99 -0
- package/components/Markdown/Markdown/AnimationNode.tsx +89 -0
- package/components/Markdown/Markdown/Markdown.tsx +61 -0
- package/components/Markdown/Markdown/core/Parser.ts +52 -0
- package/components/Markdown/Markdown/core/Renderer.ts +121 -0
- package/components/Markdown/Markdown/core/index.ts +4 -0
- package/components/Markdown/Markdown/defaultComponents/CodeBlock.tsx +113 -0
- package/components/Markdown/Markdown/defaultComponents/DisabledImage.tsx +3 -0
- package/components/Markdown/Markdown/defaultComponents/Media.tsx +71 -0
- package/components/Markdown/Markdown/hooks/index.ts +4 -0
- package/components/Markdown/Markdown/hooks/useAnimation.tsx +27 -0
- package/components/Markdown/Markdown/hooks/useCitationsData.tsx +36 -0
- package/components/Markdown/Markdown/hooks/useStreaming.ts +503 -0
- package/components/Markdown/Markdown/hooks/useTyping.ts +22 -0
- package/components/Markdown/Markdown/index.tsx +198 -0
- package/components/Markdown/Markdown/interface.ts +217 -0
- package/components/Markdown/Markdown/style.ts +152 -0
- package/components/Markdown/demo/basic.tsx +107 -0
- package/components/Markdown/demo/citations.tsx +47 -0
- package/components/Markdown/demo/cursor.tsx +9 -0
- package/components/Markdown/demo/latex.tsx +77 -0
- package/components/Markdown/demo/typing.tsx +82 -0
- package/components/Markdown/index.en-US.md +27 -0
- package/components/Markdown/index.ts +1 -0
- package/components/Markdown/index.zh-CN.md +28 -0
- package/components/Markdown/plugins/citations/CitationComponent.tsx +72 -0
- package/components/Markdown/plugins/citations/index.tsx +37 -0
- package/components/Markdown/plugins/cursor/Dot.tsx +106 -0
- package/components/Markdown/plugins/cursor/Underline.tsx +38 -0
- package/components/Markdown/plugins/cursor/index.tsx +59 -0
- package/components/Markdown/plugins/latex/index.ts +109 -0
- package/components/Markdown/plugins/type.ts +71 -0
- package/components/Mermaid/demo/basic.tsx +12 -0
- package/components/Mermaid/demo/class.tsx +31 -0
- package/components/Mermaid/demo/flowchart.tsx +13 -0
- package/components/Mermaid/demo/sequence.tsx +18 -0
- package/components/Mermaid/demo/state.tsx +16 -0
- package/components/Mermaid/demo/timeline.tsx +15 -0
- package/components/Mermaid/index.en-US.md +27 -0
- package/components/Mermaid/index.tsx +118 -0
- package/components/Mermaid/index.zh-CN.md +27 -0
- package/components/OperateCard/OperateCard.tsx +93 -0
- package/components/OperateCard/demo/index.tsx +35 -0
- package/components/OperateCard/demo/rag.tsx +19 -0
- package/components/OperateCard/demo/thinking.tsx +17 -0
- package/components/OperateCard/demo/todo.tsx +28 -0
- package/components/OperateCard/demo/toolCall.tsx +14 -0
- package/components/OperateCard/demo/webSearch.tsx +12 -0
- package/components/OperateCard/index.en-US.md +51 -0
- package/components/OperateCard/index.tsx +1 -0
- package/components/OperateCard/index.zh-CN.md +51 -0
- package/components/OperateCard/preset/Rag.tsx +90 -0
- package/components/OperateCard/preset/Thinking.tsx +45 -0
- package/components/OperateCard/preset/TodoList.tsx +73 -0
- package/components/OperateCard/preset/ToolCall.tsx +67 -0
- package/components/OperateCard/preset/WebSearch.tsx +65 -0
- package/components/OperateCard/preset/index.tsx +5 -0
- package/components/OperateCard/style.ts +268 -0
- package/components/Provider/CustomCardsProvider.tsx +17 -0
- package/components/Provider/GlobalProvider.tsx +17 -0
- package/components/Provider/index.tsx +33 -0
- package/components/Provider/types.ts +23 -0
- package/components/ResponsesAPI/index.tsx +0 -0
- package/components/Sender/ModeSelect/index.tsx +160 -0
- package/components/Sender/ModeSelect/styles.ts +0 -0
- package/components/Sender/SenderHeader.tsx +164 -0
- package/components/Sender/StopLoading.tsx +48 -0
- package/components/Sender/components/ActionButton.tsx +106 -0
- package/components/Sender/components/ClearButton.tsx +10 -0
- package/components/Sender/components/LoadingButton.tsx +26 -0
- package/components/Sender/components/SendButton.tsx +22 -0
- package/components/Sender/components/SpeechButton/RecordingIcon.tsx +68 -0
- package/components/Sender/components/SpeechButton/index.tsx +30 -0
- package/components/Sender/demo/asr.tsx +7 -0
- package/components/Sender/demo/basic.tsx +7 -0
- package/components/Sender/demo/loading.tsx +5 -0
- package/components/Sender/demo/moreMode.tsx +45 -0
- package/components/Sender/demo/morePrefixAction.tsx +40 -0
- package/components/Sender/demo/withFile.tsx +54 -0
- package/components/Sender/demo/withImage.tsx +52 -0
- package/components/Sender/index.en-US.md +29 -0
- package/components/Sender/index.tsx +491 -0
- package/components/Sender/index.zh-CN.md +30 -0
- package/components/Sender/style/index.ts +150 -0
- package/components/Sender/useSpeech.ts +133 -0
- package/components/StatusCard/demo/index.tsx +32 -0
- package/components/StatusCard/index.en-US.md +30 -0
- package/components/StatusCard/index.tsx +162 -0
- package/components/StatusCard/index.zh-CN.md +27 -0
- package/components/StatusCard/style.ts +119 -0
- package/components/Stream/index.ts +203 -0
- package/components/Util/hooks/use-proxy-imperative-handle.ts +25 -0
- package/components/Util/sleep.ts +3 -0
- package/components/Util/type.ts +1 -0
- package/components/Util/warning.ts +118 -0
- package/components/Version/index.ts +3 -0
- package/components/Voice/Recorder.tsx +3 -0
- package/components/Voice/demo/index.tsx +0 -0
- package/components/Voice/index.en-US.md +8 -0
- package/components/Voice/index.tsx +0 -0
- package/components/Voice/index.zh-CN.md +9 -0
- package/components/Welcome/demo/EyeFollower.tsx +161 -0
- package/components/Welcome/demo/demo0.tsx +14 -0
- package/components/Welcome/demo/demo1.tsx +16 -0
- package/components/Welcome/demo/demo2.tsx +30 -0
- package/components/Welcome/demo/demo3.tsx +16 -0
- package/components/Welcome/index.en-US.md +28 -0
- package/components/Welcome/index.tsx +72 -0
- package/components/Welcome/index.zh-CN.md +28 -0
- package/components/index.ts +88 -0
- package/components/overview.en-US.md +10 -0
- package/components/overview.zh-CN.md +10 -0
- package/lib/AgentScopeRuntimeWebUI/index.d.ts +1 -0
- package/lib/AgentScopeRuntimeWebUI/index.js +1 -0
- package/lib/AgentScopeRuntimeWebUI/starter/OptionsPanel/FormItem.d.ts +9 -0
- package/lib/AgentScopeRuntimeWebUI/starter/OptionsPanel/FormItem.js +33 -0
- package/lib/AgentScopeRuntimeWebUI/starter/OptionsPanel/OptionsEditor.d.ts +7 -0
- package/lib/AgentScopeRuntimeWebUI/starter/OptionsPanel/OptionsEditor.js +188 -0
- package/lib/AgentScopeRuntimeWebUI/starter/OptionsPanel/defaultConfig.d.ts +29 -0
- package/lib/AgentScopeRuntimeWebUI/starter/OptionsPanel/defaultConfig.js +35 -0
- package/lib/AgentScopeRuntimeWebUI/starter/OptionsPanel/index.d.ts +6 -0
- package/lib/AgentScopeRuntimeWebUI/starter/OptionsPanel/index.js +49 -0
- package/lib/AgentScopeRuntimeWebUI/starter/index.d.ts +1 -0
- package/lib/AgentScopeRuntimeWebUI/starter/index.js +70 -0
- package/lib/AgentScopeRuntimeWebUI/starter/sessionApi/index.d.ts +14 -0
- package/lib/AgentScopeRuntimeWebUI/starter/sessionApi/index.js +143 -0
- package/package.json +3 -2
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { useEvent, useMergedState } from 'rc-util';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
// Ensure that the SpeechRecognition API is available in the browser
|
|
5
|
+
let SpeechRecognition: any;
|
|
6
|
+
|
|
7
|
+
if (!SpeechRecognition && typeof window !== 'undefined') {
|
|
8
|
+
SpeechRecognition =
|
|
9
|
+
(window as any).SpeechRecognition ||
|
|
10
|
+
(window as any).webkitSpeechRecognition;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type ControlledSpeechConfig = {
|
|
14
|
+
recording?: boolean;
|
|
15
|
+
onRecordingChange: (recording: boolean) => void;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type AllowSpeech = boolean | ControlledSpeechConfig;
|
|
19
|
+
|
|
20
|
+
export default function useSpeech(
|
|
21
|
+
onSpeech: (transcript: string) => void,
|
|
22
|
+
allowSpeech?: AllowSpeech,
|
|
23
|
+
) {
|
|
24
|
+
const onEventSpeech = useEvent(onSpeech);
|
|
25
|
+
|
|
26
|
+
// ========================== Speech Config ==========================
|
|
27
|
+
const [controlledRecording, onControlledRecordingChange, speechInControlled] =
|
|
28
|
+
React.useMemo(() => {
|
|
29
|
+
if (typeof allowSpeech === 'object') {
|
|
30
|
+
return [
|
|
31
|
+
allowSpeech.recording,
|
|
32
|
+
allowSpeech.onRecordingChange,
|
|
33
|
+
typeof allowSpeech.recording === 'boolean',
|
|
34
|
+
] as const;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return [undefined, undefined, false] as const;
|
|
38
|
+
}, [allowSpeech]);
|
|
39
|
+
|
|
40
|
+
// ======================== Speech Permission ========================
|
|
41
|
+
const [permissionState, setPermissionState] =
|
|
42
|
+
React.useState<PermissionState | null>(null);
|
|
43
|
+
|
|
44
|
+
React.useEffect(() => {
|
|
45
|
+
if (typeof navigator !== 'undefined' && 'permissions' in navigator) {
|
|
46
|
+
let lastPermission: PermissionStatus | null = null;
|
|
47
|
+
|
|
48
|
+
(navigator as any).permissions
|
|
49
|
+
.query({ name: 'microphone' })
|
|
50
|
+
.then((permissionStatus: PermissionStatus) => {
|
|
51
|
+
setPermissionState(permissionStatus.state);
|
|
52
|
+
|
|
53
|
+
// Keep the last permission status.
|
|
54
|
+
permissionStatus.onchange = function () {
|
|
55
|
+
setPermissionState(this.state);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
lastPermission = permissionStatus;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return () => {
|
|
62
|
+
// Avoid memory leaks
|
|
63
|
+
if (lastPermission) {
|
|
64
|
+
lastPermission.onchange = null;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}, []);
|
|
69
|
+
|
|
70
|
+
// Convert permission state to a simple type
|
|
71
|
+
const mergedAllowSpeech = SpeechRecognition && permissionState !== 'denied';
|
|
72
|
+
|
|
73
|
+
// ========================== Speech Events ==========================
|
|
74
|
+
const recognitionRef = React.useRef<any | null>(null);
|
|
75
|
+
const [recording, setRecording] = useMergedState(false, {
|
|
76
|
+
value: controlledRecording,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const forceBreakRef = React.useRef(false);
|
|
80
|
+
|
|
81
|
+
const ensureRecognition = () => {
|
|
82
|
+
if (mergedAllowSpeech && !recognitionRef.current) {
|
|
83
|
+
const recognition = new SpeechRecognition();
|
|
84
|
+
|
|
85
|
+
recognition.onstart = () => {
|
|
86
|
+
setRecording(true);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
recognition.onend = () => {
|
|
90
|
+
setRecording(false);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
recognition.onresult = (event: SpeechRecognitionResult) => {
|
|
94
|
+
if (!forceBreakRef.current) {
|
|
95
|
+
const transcript = (event as any).results?.[0]?.[0]?.transcript;
|
|
96
|
+
onEventSpeech(transcript);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
forceBreakRef.current = false;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
recognitionRef.current = recognition;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const triggerSpeech = useEvent((forceBreak: boolean) => {
|
|
107
|
+
// Ignore if `forceBreak` but is not recording
|
|
108
|
+
if (forceBreak && !recording) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
forceBreakRef.current = forceBreak;
|
|
113
|
+
|
|
114
|
+
if (speechInControlled) {
|
|
115
|
+
// If in controlled mode, do nothing
|
|
116
|
+
onControlledRecordingChange?.(!recording);
|
|
117
|
+
} else {
|
|
118
|
+
ensureRecognition();
|
|
119
|
+
|
|
120
|
+
if (recognitionRef.current) {
|
|
121
|
+
if (recording) {
|
|
122
|
+
recognitionRef.current.stop();
|
|
123
|
+
onControlledRecordingChange?.(false);
|
|
124
|
+
} else {
|
|
125
|
+
recognitionRef.current.start();
|
|
126
|
+
onControlledRecordingChange?.(true);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return [mergedAllowSpeech, triggerSpeech, recording] as const;
|
|
133
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { StatusCard } from '@agentscope-ai/chat';
|
|
2
|
+
import { Flex } from 'antd';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export default function () {
|
|
7
|
+
const [done, setDone] = useState(false);
|
|
8
|
+
return <Flex vertical gap={16}>
|
|
9
|
+
<StatusCard title="Success" status="success">
|
|
10
|
+
|
|
11
|
+
<StatusCard.Statistic values={[
|
|
12
|
+
{ title: 'Runtime', value: '23min' },
|
|
13
|
+
{ title: 'Input Token', value: '200k' },
|
|
14
|
+
{ title: 'Output Token', value: '1000k' },
|
|
15
|
+
]} />
|
|
16
|
+
</StatusCard>
|
|
17
|
+
<StatusCard title="Execution Failed" description="Failed to load resource: net::ERR_CONNECTION_CLOSED" status="error" />
|
|
18
|
+
<StatusCard title="Warning" status="warning" />
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
<StatusCard.HITL
|
|
22
|
+
done={done}
|
|
23
|
+
onDone={() => setDone(true)}
|
|
24
|
+
title="Human intervention required"
|
|
25
|
+
description="Please log in after entering your account and password"
|
|
26
|
+
waitButtonText="I have completed, continue task"
|
|
27
|
+
doneButtonText="User has confirmed"
|
|
28
|
+
/>
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
</Flex>
|
|
32
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
group:
|
|
3
|
+
title: Output
|
|
4
|
+
order: 3
|
|
5
|
+
title: StatusCard
|
|
6
|
+
description: Express the execution status of LLM or Agent
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<DemoTitle title="StatusCard" desc="Express the execution status of LLM or Agent"></DemoTitle>
|
|
10
|
+
|
|
11
|
+
<code src="./demo/index.tsx" height="auto">Example</code>
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
<Install>import { StatusCard } from '@agentscope-ai/chat'</Install>
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
#### API
|
|
19
|
+
|
|
20
|
+
##### IStatusCardProps
|
|
21
|
+
|
|
22
|
+
<ApiParser source="./index.tsx" id="IStatusCardProps"></ApiParser>
|
|
23
|
+
|
|
24
|
+
##### IStatusCardHITLProps
|
|
25
|
+
|
|
26
|
+
<ApiParser source="./index.tsx" id="IStatusCardHITLProps"></ApiParser>
|
|
27
|
+
|
|
28
|
+
##### IStatusCardStatisticProps
|
|
29
|
+
|
|
30
|
+
<ApiParser source="./index.tsx" id="IStatusCardStatisticProps"></ApiParser>
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { SparkCheckCircleFill, SparkCheckCircleLine, SparkErrorCircleFill, SparkErrorCircleLine, SparkStopCircleFill, SparkStopCircleLine, SparkTrueLine, SparkWarningCircleFill, SparkWarningCircleLine } from '@agentscope-ai/icons';
|
|
2
|
+
import { useProviderContext } from '../Provider';
|
|
3
|
+
import Style from './style';
|
|
4
|
+
import classNames from 'classnames';
|
|
5
|
+
import { ButtonProps } from 'antd';
|
|
6
|
+
import { Button } from '@agentscope-ai/design';
|
|
7
|
+
|
|
8
|
+
export interface IStatusCardProps {
|
|
9
|
+
/**
|
|
10
|
+
* @description 标题
|
|
11
|
+
* @descriptionEn Title
|
|
12
|
+
*/
|
|
13
|
+
title: string;
|
|
14
|
+
/**
|
|
15
|
+
* @description 状态
|
|
16
|
+
* @descriptionEn Status
|
|
17
|
+
*/
|
|
18
|
+
status: 'success' | 'error' | 'warning' | 'info';
|
|
19
|
+
/**
|
|
20
|
+
* @description 描述
|
|
21
|
+
* @descriptionEn Description
|
|
22
|
+
*/
|
|
23
|
+
description?: string;
|
|
24
|
+
/**
|
|
25
|
+
* @description 图标
|
|
26
|
+
* @descriptionEn Icon
|
|
27
|
+
*/
|
|
28
|
+
icon?: React.ReactNode;
|
|
29
|
+
/**
|
|
30
|
+
* @description 子元素
|
|
31
|
+
* @descriptionEn Children
|
|
32
|
+
*/
|
|
33
|
+
children?: React.ReactNode;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
function StatusCard(props: IStatusCardProps) {
|
|
39
|
+
const { getPrefixCls } = useProviderContext();
|
|
40
|
+
const prefixCls = getPrefixCls('status-card');
|
|
41
|
+
|
|
42
|
+
const icon = props.icon || {
|
|
43
|
+
'success': <SparkCheckCircleFill />,
|
|
44
|
+
'error': <SparkErrorCircleFill />,
|
|
45
|
+
'warning': <SparkStopCircleLine />,
|
|
46
|
+
'info': <SparkWarningCircleFill />,
|
|
47
|
+
}[props.status];
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
return <>
|
|
51
|
+
<Style />
|
|
52
|
+
<div className={classNames(prefixCls, `${prefixCls}-${props.status}`)}>
|
|
53
|
+
<div className={`${prefixCls}-header`}>
|
|
54
|
+
<div className={`${prefixCls}-header-top`}>
|
|
55
|
+
<div className={`${prefixCls}-header-icon`}>{icon}</div>
|
|
56
|
+
<div className={`${prefixCls}-header-title`}>{props.title}</div>
|
|
57
|
+
</div>
|
|
58
|
+
{
|
|
59
|
+
props.description && <div className={`${prefixCls}-header-description`}>{props.description}</div>
|
|
60
|
+
}
|
|
61
|
+
</div>
|
|
62
|
+
{
|
|
63
|
+
props.children && <div className={`${prefixCls}-body`}>{props.children}</div>
|
|
64
|
+
}
|
|
65
|
+
</div>
|
|
66
|
+
</>
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
export interface IStatusCardHITLProps {
|
|
71
|
+
/**
|
|
72
|
+
* @description 标题
|
|
73
|
+
* @descriptionEn Title
|
|
74
|
+
*/
|
|
75
|
+
title: string;
|
|
76
|
+
/**
|
|
77
|
+
* @description 描述
|
|
78
|
+
* @descriptionEn Description
|
|
79
|
+
* @default '需要用户人工干预'
|
|
80
|
+
*/
|
|
81
|
+
description?: string;
|
|
82
|
+
/**
|
|
83
|
+
* @description 等待按钮文本
|
|
84
|
+
* @descriptionEn Wait Button Text
|
|
85
|
+
* @default '我已完成,继续任务'
|
|
86
|
+
*/
|
|
87
|
+
waitButtonText?: string;
|
|
88
|
+
/**
|
|
89
|
+
* @description 完成按钮文本
|
|
90
|
+
* @descriptionEn Done Button Text
|
|
91
|
+
* @default '用户已确认'
|
|
92
|
+
*/
|
|
93
|
+
doneButtonText?: string;
|
|
94
|
+
/**
|
|
95
|
+
* @description 是否完成
|
|
96
|
+
* @descriptionEn Done
|
|
97
|
+
*/
|
|
98
|
+
done: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* @description 完成回调
|
|
101
|
+
* @descriptionEn Done Callback
|
|
102
|
+
*/
|
|
103
|
+
onDone: () => void;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
StatusCard.HITL = function (props: IStatusCardHITLProps) {
|
|
108
|
+
const { title = '需要用户人工干预', description, waitButtonText = '我已完成,继续任务', doneButtonText = '用户已确认' } = props;
|
|
109
|
+
|
|
110
|
+
const { getPrefixCls } = useProviderContext();
|
|
111
|
+
const prefixCls = getPrefixCls('status-card');
|
|
112
|
+
|
|
113
|
+
const button = props.done ?
|
|
114
|
+
<Button onClick={props.onDone} type="primary" disabled icon={<SparkTrueLine />}>{doneButtonText}</Button> :
|
|
115
|
+
<Button onClick={props.onDone} type="primary" >{waitButtonText}</Button>;
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
return <StatusCard
|
|
119
|
+
status='info'
|
|
120
|
+
title={title}
|
|
121
|
+
|
|
122
|
+
>
|
|
123
|
+
<div className={`${prefixCls}-HITL`}>
|
|
124
|
+
{
|
|
125
|
+
description && <div className={`${prefixCls}-HITL-desc`}>{description}</div>
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
<div className={`${prefixCls}-HITL-button`}>
|
|
129
|
+
{button}
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</StatusCard>
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface IStatusCardStatisticProps {
|
|
136
|
+
/**
|
|
137
|
+
* @description 统计数据
|
|
138
|
+
* @descriptionEn Values
|
|
139
|
+
*/
|
|
140
|
+
values: {
|
|
141
|
+
title: string;
|
|
142
|
+
value: string;
|
|
143
|
+
}[];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
StatusCard.Statistic = function (props: IStatusCardStatisticProps) {
|
|
148
|
+
const { getPrefixCls } = useProviderContext();
|
|
149
|
+
const prefixCls = getPrefixCls('status-card');
|
|
150
|
+
|
|
151
|
+
return <div className={`${prefixCls}-statistic`}>
|
|
152
|
+
{props.values.map(item => {
|
|
153
|
+
return <div className={`${prefixCls}-statistic-item`}>
|
|
154
|
+
<div className={`${prefixCls}-statistic-item-title`}>{item.title}</div>
|
|
155
|
+
<div className={`${prefixCls}-statistic-item-value`}>{item.value}</div>
|
|
156
|
+
</div>
|
|
157
|
+
})}
|
|
158
|
+
</div>
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
export default StatusCard;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
group:
|
|
3
|
+
title: 输出
|
|
4
|
+
order: 3
|
|
5
|
+
title: StatusCard
|
|
6
|
+
description: 表达LLM或Agent的执行任务状态
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<DemoTitle title="StatusCard" desc="表达LLM或Agent的执行任务状态"></DemoTitle>
|
|
10
|
+
|
|
11
|
+
<code src="./demo/index.tsx" height="auto">示例</code>
|
|
12
|
+
|
|
13
|
+
<Install>import { StatusCard } from '@agentscope-ai/chat'</Install>
|
|
14
|
+
|
|
15
|
+
#### API
|
|
16
|
+
|
|
17
|
+
##### IStatusCardProps
|
|
18
|
+
|
|
19
|
+
<ApiParser source="./index.tsx" id="IStatusCardProps"></ApiParser>
|
|
20
|
+
|
|
21
|
+
##### IStatusCardHITLProps
|
|
22
|
+
|
|
23
|
+
<ApiParser source="./index.tsx" id="IStatusCardHITLProps"></ApiParser>
|
|
24
|
+
|
|
25
|
+
##### IStatusCardStatisticProps
|
|
26
|
+
|
|
27
|
+
<ApiParser source="./index.tsx" id="IStatusCardStatisticProps"></ApiParser>
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { createGlobalStyle } from 'antd-style';
|
|
2
|
+
|
|
3
|
+
export default createGlobalStyle`
|
|
4
|
+
.${(p) => p.theme.prefixCls}-status-card {
|
|
5
|
+
width: 100%;
|
|
6
|
+
border-radius: ${(p) => p.theme.borderRadiusLG}px;
|
|
7
|
+
border: 1px solid ${(p) => p.theme.colorBorderSecondary};
|
|
8
|
+
overflow: hidden;
|
|
9
|
+
&-success {
|
|
10
|
+
background-color: ${(p) => p.theme.colorSuccessBg};
|
|
11
|
+
|
|
12
|
+
.${(p) => p.theme.prefixCls}-status-card-header-icon {
|
|
13
|
+
color: ${(p) => p.theme.colorSuccess};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
&-error {
|
|
17
|
+
background-color: ${(p) => p.theme.colorErrorBg};
|
|
18
|
+
|
|
19
|
+
.${(p) => p.theme.prefixCls}-status-card-header-icon {
|
|
20
|
+
color: ${(p) => p.theme.colorError};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
&-warning {
|
|
24
|
+
background-color: ${(p) => p.theme.colorWarningBg};
|
|
25
|
+
|
|
26
|
+
.${(p) => p.theme.prefixCls}-status-card-header-icon {
|
|
27
|
+
color: ${(p) => p.theme.colorWarning};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
&-info {
|
|
31
|
+
background-color: ${(p) => p.theme.colorFillTertiary};
|
|
32
|
+
|
|
33
|
+
.${(p) => p.theme.prefixCls}-status-card-header-icon {
|
|
34
|
+
color: ${(p) => p.theme.colorInfo};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
&-header-icon {
|
|
39
|
+
font-size: 20px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
&-header-title {
|
|
44
|
+
font-size: 16px;
|
|
45
|
+
color: ${(p) => p.theme.colorText};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
&-header {
|
|
49
|
+
padding: 0 12px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
&-header-top {
|
|
53
|
+
display: flex;
|
|
54
|
+
align-items: center;
|
|
55
|
+
margin: 8px 0;
|
|
56
|
+
gap: 8px;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
&-header-description {
|
|
61
|
+
margin-top: -6px;
|
|
62
|
+
margin-bottom: 8px;
|
|
63
|
+
margin-left: 30px;
|
|
64
|
+
font-size: 12px;
|
|
65
|
+
color: ${(p) => p.theme.colorTextTertiary};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
&-HITL {
|
|
73
|
+
padding: 16px;
|
|
74
|
+
border-top: 1px solid ${(p) => p.theme.colorBorderSecondary};
|
|
75
|
+
background-color: ${(p) => p.theme.colorBgBase};
|
|
76
|
+
border-radius: ${(p) => p.theme.borderRadiusLG}px ${(p) =>
|
|
77
|
+
p.theme.borderRadiusLG}px 0 0;
|
|
78
|
+
|
|
79
|
+
&-desc {
|
|
80
|
+
color: ${(p) => p.theme.colorTextTertiary};
|
|
81
|
+
margin-bottom: 12px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
&-button {
|
|
85
|
+
display: flex;
|
|
86
|
+
justify-content: flex-end;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
&-statistic {
|
|
92
|
+
display: flex;
|
|
93
|
+
padding: 16px 26px;
|
|
94
|
+
border-top: 1px solid ${(p) => p.theme.colorBorderSecondary};
|
|
95
|
+
background-color: ${(p) => p.theme.colorBgBase};
|
|
96
|
+
border-radius: ${(p) => p.theme.borderRadiusLG}px ${(p) =>
|
|
97
|
+
p.theme.borderRadiusLG}px 0 0;
|
|
98
|
+
|
|
99
|
+
&-item {
|
|
100
|
+
display: flex;
|
|
101
|
+
flex-direction: column;
|
|
102
|
+
flex: 1;
|
|
103
|
+
gap: 8px;
|
|
104
|
+
|
|
105
|
+
&-title {
|
|
106
|
+
font-size: 12px;
|
|
107
|
+
color: ${(p) => p.theme.colorTextTertiary};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
&-value {
|
|
111
|
+
font-size: 18px;
|
|
112
|
+
line-height: 32px;
|
|
113
|
+
color: ${(p) => p.theme.colorText};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
}
|
|
119
|
+
`;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description default separator for {@link splitStream}
|
|
3
|
+
*/
|
|
4
|
+
const DEFAULT_STREAM_SEPARATOR = '\n\n';
|
|
5
|
+
/**
|
|
6
|
+
* @description Default separator for {@link splitPart}
|
|
7
|
+
* @example "event: delta\ndata: {\"key\": \"value\"}"
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_PART_SEPARATOR = '\n';
|
|
10
|
+
/**
|
|
11
|
+
* @description Default separator for key value, A colon (`:`) is used to separate keys from values
|
|
12
|
+
* @example "event: delta"
|
|
13
|
+
*/
|
|
14
|
+
const DEFAULT_KV_SEPARATOR = ':';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Check if a string is not empty or only contains whitespace characters
|
|
18
|
+
*/
|
|
19
|
+
const isValidString = (str: string) => (str ?? '').trim() !== '';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @description A TransformStream inst that splits a stream into parts based on {@link DEFAULT_STREAM_SEPARATOR}
|
|
23
|
+
* @example
|
|
24
|
+
*
|
|
25
|
+
* `event: delta
|
|
26
|
+
* data: { content: 'hello' }
|
|
27
|
+
*
|
|
28
|
+
* event: delta
|
|
29
|
+
* data: { key: 'world!' }
|
|
30
|
+
*
|
|
31
|
+
* `
|
|
32
|
+
*/
|
|
33
|
+
function splitStream() {
|
|
34
|
+
// Buffer to store incomplete data chunks between transformations
|
|
35
|
+
let buffer = '';
|
|
36
|
+
|
|
37
|
+
return new TransformStream<string, string>({
|
|
38
|
+
transform(streamChunk, controller) {
|
|
39
|
+
buffer += streamChunk;
|
|
40
|
+
|
|
41
|
+
// Split the buffer based on the separator
|
|
42
|
+
const parts = buffer.split(DEFAULT_STREAM_SEPARATOR);
|
|
43
|
+
|
|
44
|
+
// Enqueue all complete parts except for the last incomplete one
|
|
45
|
+
parts.slice(0, -1).forEach((part) => {
|
|
46
|
+
// Skip empty parts
|
|
47
|
+
if (isValidString(part)) {
|
|
48
|
+
controller.enqueue(part);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Save the last incomplete part back to the buffer for the next chunk
|
|
53
|
+
buffer = parts[parts.length - 1];
|
|
54
|
+
},
|
|
55
|
+
flush(controller) {
|
|
56
|
+
// If there's any remaining data in the buffer, enqueue it as the final part
|
|
57
|
+
if (isValidString(buffer)) {
|
|
58
|
+
controller.enqueue(buffer);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#fields
|
|
66
|
+
*/
|
|
67
|
+
export type SSEFields = 'data' | 'event' | 'id' | 'retry';
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @example
|
|
71
|
+
* const sseObject = {
|
|
72
|
+
* event: 'delta',
|
|
73
|
+
* data: '{ key: "world!" }',
|
|
74
|
+
* };
|
|
75
|
+
*/
|
|
76
|
+
export type SSEOutput = Partial<Record<SSEFields, any>>;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @description A TransformStream inst that transforms a part string into {@link SSEOutput}
|
|
80
|
+
* @example part string
|
|
81
|
+
*
|
|
82
|
+
* "event: delta\ndata: { key: 'world!' }\n"
|
|
83
|
+
*
|
|
84
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/API/EventSource
|
|
85
|
+
*
|
|
86
|
+
* When handling responses with `Content-Type: text/event-stream`, the following standard practices are commonly observed:
|
|
87
|
+
* - Double newline characters (`\n\n`) are used to separate individual events.
|
|
88
|
+
* - Single newline characters (`\n`) are employed to separate line within an event.
|
|
89
|
+
*/
|
|
90
|
+
function splitPart() {
|
|
91
|
+
return new TransformStream<string, SSEOutput>({
|
|
92
|
+
transform(partChunk, controller) {
|
|
93
|
+
// Split the chunk into key-value pairs using the partSeparator
|
|
94
|
+
const lines = partChunk.split(DEFAULT_PART_SEPARATOR);
|
|
95
|
+
|
|
96
|
+
const sseEvent = lines.reduce<SSEOutput>((acc, line) => {
|
|
97
|
+
const separatorIndex = line.indexOf(DEFAULT_KV_SEPARATOR);
|
|
98
|
+
|
|
99
|
+
if (separatorIndex === -1) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`The key-value separator "${DEFAULT_KV_SEPARATOR}" is not found in the sse line chunk!`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Extract the key from the beginning of the line up to the separator
|
|
106
|
+
const key = line.slice(0, separatorIndex);
|
|
107
|
+
|
|
108
|
+
// The colon is used for comment lines, skip directly
|
|
109
|
+
if (!isValidString(key)) return acc;
|
|
110
|
+
|
|
111
|
+
// Extract the value from the line after the separator
|
|
112
|
+
const value = line.slice(separatorIndex + 1);
|
|
113
|
+
|
|
114
|
+
return { ...acc, [key]: value };
|
|
115
|
+
}, {});
|
|
116
|
+
|
|
117
|
+
if (Object.keys(sseEvent).length === 0) return;
|
|
118
|
+
|
|
119
|
+
// Reduce the key-value pairs into a single object and enqueue
|
|
120
|
+
controller.enqueue(sseEvent);
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface StreamOptions<Output> {
|
|
126
|
+
/**
|
|
127
|
+
* @description 二进制数据的可读流
|
|
128
|
+
* @descriptionEn Readable stream of binary data
|
|
129
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream
|
|
130
|
+
*/
|
|
131
|
+
readableStream: ReadableStream<Uint8Array>;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @description 支持自定义转换流来转换数据流
|
|
135
|
+
* @descriptionEn Support customizable transformStream to transform streams
|
|
136
|
+
* @default sseTransformStream
|
|
137
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/API/TransformStream
|
|
138
|
+
*/
|
|
139
|
+
transformStream?: TransformStream<string, Output>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
type XReadableStream<R = SSEOutput> = ReadableStream<R> & AsyncGenerator<R>;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @description Transform Uint8Array binary stream to {@link SSEOutput} by default
|
|
146
|
+
* @warning The `Stream` only support the `utf-8` encoding. More encoding support maybe in the future.
|
|
147
|
+
*/
|
|
148
|
+
function Stream<Output = SSEOutput>(
|
|
149
|
+
options: StreamOptions<Output>,
|
|
150
|
+
config?: {
|
|
151
|
+
openaiCompatible?: boolean;
|
|
152
|
+
},
|
|
153
|
+
) {
|
|
154
|
+
const { readableStream, transformStream } = options;
|
|
155
|
+
|
|
156
|
+
if (!(readableStream instanceof ReadableStream)) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
'The options.readableStream must be an instance of ReadableStream.',
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Default encoding is `utf-8`
|
|
163
|
+
const decoderStream = new TextDecoderStream();
|
|
164
|
+
|
|
165
|
+
const stream = (
|
|
166
|
+
transformStream
|
|
167
|
+
? /**
|
|
168
|
+
* Uint8Array binary -> string -> Output
|
|
169
|
+
*/
|
|
170
|
+
readableStream.pipeThrough(decoderStream).pipeThrough(transformStream)
|
|
171
|
+
: /**
|
|
172
|
+
* Uint8Array binary -> string -> SSE part string -> Default Output {@link SSEOutput}
|
|
173
|
+
*/
|
|
174
|
+
readableStream
|
|
175
|
+
.pipeThrough(decoderStream)
|
|
176
|
+
.pipeThrough(splitStream())
|
|
177
|
+
.pipeThrough(splitPart())
|
|
178
|
+
) as XReadableStream<Output>;
|
|
179
|
+
|
|
180
|
+
/** support async iterator */
|
|
181
|
+
stream[Symbol.asyncIterator] = async function* () {
|
|
182
|
+
const reader = this.getReader();
|
|
183
|
+
|
|
184
|
+
while (true) {
|
|
185
|
+
const { done, value } = await reader.read();
|
|
186
|
+
if (done) break;
|
|
187
|
+
|
|
188
|
+
if (!value) continue;
|
|
189
|
+
|
|
190
|
+
// Transformed data through all transform pipes
|
|
191
|
+
yield config?.openaiCompatible
|
|
192
|
+
? {
|
|
193
|
+
...value,
|
|
194
|
+
data: value.data.slice(1),
|
|
195
|
+
}
|
|
196
|
+
: value;
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
return stream;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export default Stream;
|