@xinghunm/ai-chat 1.1.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -1
- package/dist/index.d.mts +35 -2
- package/dist/index.d.ts +35 -2
- package/dist/index.js +94 -18
- package/dist/index.mjs +94 -18
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -55,6 +55,28 @@ const transport = createDefaultChatTransport({
|
|
|
55
55
|
})
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
如果后端协议大体一致,只是模型列表或 `/chat/completions` 请求体需要调整,也可以继续复用默认 transport,只覆盖局部扩展点:
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
const transport = createDefaultChatTransport({
|
|
62
|
+
apiBaseUrl: '/ai-api',
|
|
63
|
+
authToken: 'Bearer your-token-here',
|
|
64
|
+
resolveModels: async () => ({
|
|
65
|
+
data: [{ id: 'deepseek-chat', object: 'model' }],
|
|
66
|
+
}),
|
|
67
|
+
buildRequestBody: ({ model, mode, content }) => ({
|
|
68
|
+
model: 'deepseek-chat',
|
|
69
|
+
base_url: 'https://api.deepseek.com/v1',
|
|
70
|
+
api_key: 'sk-xxxxx',
|
|
71
|
+
mode,
|
|
72
|
+
stream: true,
|
|
73
|
+
messages: [{ role: 'user', content }],
|
|
74
|
+
}),
|
|
75
|
+
})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
默认 transport 在检测到图片附件时,会自动把用户输入组装成多模态 `messages[].content` 数组,并将图片文件序列化为 Data URL 后发送;如果你自定义 `buildRequestBody`,也会同时收到 `attachments` 参数,可按后端协议自行处理。
|
|
79
|
+
|
|
58
80
|
## 完整用法
|
|
59
81
|
|
|
60
82
|
如需最大灵活性,可在 `AiChatProvider` 内手动组合子组件:
|
|
@@ -74,6 +96,7 @@ const transport: ChatTransport = {
|
|
|
74
96
|
model,
|
|
75
97
|
mode,
|
|
76
98
|
content,
|
|
99
|
+
attachments,
|
|
77
100
|
onSessionId,
|
|
78
101
|
onUpdate,
|
|
79
102
|
onDone,
|
|
@@ -85,7 +108,7 @@ const transport: ChatTransport = {
|
|
|
85
108
|
method: 'POST',
|
|
86
109
|
signal,
|
|
87
110
|
headers: { 'Content-Type': 'application/json' },
|
|
88
|
-
body: JSON.stringify({ sessionId, model, mode, content }),
|
|
111
|
+
body: JSON.stringify({ sessionId, model, mode, content, attachments }),
|
|
89
112
|
})
|
|
90
113
|
const data = await response.json()
|
|
91
114
|
onSessionId?.(data.sessionId)
|
|
@@ -193,6 +216,13 @@ export const CustomChat = () => (
|
|
|
193
216
|
| `completions` | `"/chat/completions"` | 流式聊天接口路径。 |
|
|
194
217
|
| `terminate` | `"/chat/terminate"` | 停止流式响应接口路径。 |
|
|
195
218
|
|
|
219
|
+
此外还支持两个轻量扩展点:
|
|
220
|
+
|
|
221
|
+
| 键 | 类型 | 说明 |
|
|
222
|
+
| ------------------ | ----------------------------------- | ------------------------------------------------------------------------------ |
|
|
223
|
+
| `resolveModels` | `() => Promise<ChatModelsResponse>` | 覆盖默认的 `/models` 请求,直接返回模型列表。 |
|
|
224
|
+
| `buildRequestBody` | `(args) => unknown` | 覆盖默认的 `/chat/completions` 请求体构造逻辑;`args` 同时包含 `attachments`。 |
|
|
225
|
+
|
|
196
226
|
### `AiChatLabels`
|
|
197
227
|
|
|
198
228
|
所有字段均为可选,未指定的字段回退到 `DEFAULT_AI_CHAT_LABELS` 中的英文默认值。
|
package/dist/index.d.mts
CHANGED
|
@@ -42,6 +42,12 @@ interface ChatImageAttachment {
|
|
|
42
42
|
mimeType: string;
|
|
43
43
|
size: number;
|
|
44
44
|
previewUrl: string;
|
|
45
|
+
/**
|
|
46
|
+
* Original file kept for transports that need to upload or serialize the image.
|
|
47
|
+
*
|
|
48
|
+
* This is only available for client-originated attachments selected in the composer.
|
|
49
|
+
*/
|
|
50
|
+
file?: File;
|
|
45
51
|
}
|
|
46
52
|
/**
|
|
47
53
|
* Select option metadata used by plan questionnaires.
|
|
@@ -291,6 +297,10 @@ interface ChatTransportStartStreamArgs {
|
|
|
291
297
|
mode: ChatAgentMode;
|
|
292
298
|
/** User message content that should be sent to the backend. */
|
|
293
299
|
content: string;
|
|
300
|
+
/**
|
|
301
|
+
* Optional image attachments selected in the composer and sent alongside the text.
|
|
302
|
+
*/
|
|
303
|
+
attachments?: ChatImageAttachment[];
|
|
294
304
|
/** Abort signal controlled by the chat composer stop action. */
|
|
295
305
|
signal?: AbortSignal;
|
|
296
306
|
/** Emits normalized streaming patches that should update the assistant message. */
|
|
@@ -511,6 +521,25 @@ interface ChatToolExecutionPolicy {
|
|
|
511
521
|
/** Approval timeout in seconds, forwarded when approval is enabled. */
|
|
512
522
|
approvalTimeoutSec?: number;
|
|
513
523
|
}
|
|
524
|
+
/**
|
|
525
|
+
* Arguments passed to a custom request body builder for the default transport.
|
|
526
|
+
*/
|
|
527
|
+
interface DefaultChatTransportRequestBodyBuilderArgs {
|
|
528
|
+
sessionId?: string;
|
|
529
|
+
model: string;
|
|
530
|
+
mode: ChatAgentMode;
|
|
531
|
+
content: string;
|
|
532
|
+
attachments?: ChatImageAttachment[];
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Optional builder used to customize the `/chat/completions` request body while
|
|
536
|
+
* reusing the default streaming transport behavior.
|
|
537
|
+
*/
|
|
538
|
+
type DefaultChatTransportRequestBodyBuilder = (args: DefaultChatTransportRequestBodyBuilderArgs) => unknown;
|
|
539
|
+
/**
|
|
540
|
+
* Optional model resolver used to override the default `/models` request.
|
|
541
|
+
*/
|
|
542
|
+
type DefaultChatTransportModelsResolver = () => Promise<ChatModelsResponse>;
|
|
514
543
|
/**
|
|
515
544
|
* Options for the built-in HTTP transport adapter.
|
|
516
545
|
*/
|
|
@@ -523,6 +552,10 @@ interface CreateDefaultChatTransportOptions {
|
|
|
523
552
|
toolExecutionPolicy?: ChatToolExecutionPolicy;
|
|
524
553
|
/** Optional extra headers appended to each streaming chat completion request. */
|
|
525
554
|
streamHeaders?: Record<string, string>;
|
|
555
|
+
/** Optional resolver used to override the built-in model catalog request. */
|
|
556
|
+
resolveModels?: DefaultChatTransportModelsResolver;
|
|
557
|
+
/** Optional builder used to override the built-in chat completion request body. */
|
|
558
|
+
buildRequestBody?: DefaultChatTransportRequestBodyBuilder;
|
|
526
559
|
/** Optional transformer used to normalize custom stream packets. */
|
|
527
560
|
transformStreamPacket?: TransformChatStreamPacket;
|
|
528
561
|
/** Optional endpoint overrides for backends that use different paths. */
|
|
@@ -533,7 +566,7 @@ interface CreateDefaultChatTransportOptions {
|
|
|
533
566
|
/**
|
|
534
567
|
* Creates the built-in transport backed by the current HTTP chat API.
|
|
535
568
|
*/
|
|
536
|
-
declare const createDefaultChatTransport: ({ apiBaseUrl, authToken, toolExecutionPolicy, streamHeaders, transformStreamPacket, endpoints, axiosInstance, }: CreateDefaultChatTransportOptions) => ChatTransport;
|
|
569
|
+
declare const createDefaultChatTransport: ({ apiBaseUrl, authToken, toolExecutionPolicy, streamHeaders, resolveModels, buildRequestBody, transformStreamPacket, endpoints, axiosInstance, }: CreateDefaultChatTransportOptions) => ChatTransport;
|
|
537
570
|
|
|
538
571
|
declare const ChatThread: () => _emotion_react_jsx_runtime.JSX.Element;
|
|
539
572
|
|
|
@@ -654,4 +687,4 @@ declare const useChatContext: () => ChatContextValue;
|
|
|
654
687
|
*/
|
|
655
688
|
declare const useChatStore: <T>(selector: (state: ChatStore) => T) => T;
|
|
656
689
|
|
|
657
|
-
export { AiChat, type AiChatLabels, type AiChatProps, AiChatProvider, type AiChatProviderProps, CHAT_AGENT_MODES, CHAT_MESSAGE_RENDER_ORDERS, type ChatAgentMode, ChatComposer, type ChatComposerViewProps, type ChatConfirmationSubmitHandler, ChatConversationList, type ChatImageAttachment, type ChatMessage, type ChatMessageBlock, type ChatMessageBlockRenderer, type ChatMessageBlockRendererProps, type ChatMessageRenderOrder, type ChatMessageStatus, type ChatModel, type ChatParameterSummaryItem, type ChatQuestionnaireSubmitHandler, type ChatRole, type ChatSession, type ChatStreamMessagePatch, type ChatStreamPacketTransformArgs, type ChatStreamPacketUpdate, type ChatSubmissionContext, ChatThread, type ChatToolExecutionPolicy, type ChatTransport, type ChatTransportStartStreamArgs, type CreateDefaultChatTransportOptions, DEFAULT_AI_CHAT_LABELS, DEFAULT_CHAT_AGENT_MODE, type DefaultChatTransportEndpoints, type ExecutionConfirmationSubmission, type ExecutionProposal, type PlanQuestionOption, type PlanQuestionSubmissionDetail, type PlanQuestionnaire, type PlanQuestionnaireSubmission, type ResultSummary, type TransformChatStreamPacket, createDefaultChatTransport, useChatContext, useChatStore };
|
|
690
|
+
export { AiChat, type AiChatLabels, type AiChatProps, AiChatProvider, type AiChatProviderProps, CHAT_AGENT_MODES, CHAT_MESSAGE_RENDER_ORDERS, type ChatAgentMode, ChatComposer, type ChatComposerViewProps, type ChatConfirmationSubmitHandler, ChatConversationList, type ChatImageAttachment, type ChatMessage, type ChatMessageBlock, type ChatMessageBlockRenderer, type ChatMessageBlockRendererProps, type ChatMessageRenderOrder, type ChatMessageStatus, type ChatModel, type ChatParameterSummaryItem, type ChatQuestionnaireSubmitHandler, type ChatRole, type ChatSession, type ChatStreamMessagePatch, type ChatStreamPacketTransformArgs, type ChatStreamPacketUpdate, type ChatSubmissionContext, ChatThread, type ChatToolExecutionPolicy, type ChatTransport, type ChatTransportStartStreamArgs, type CreateDefaultChatTransportOptions, DEFAULT_AI_CHAT_LABELS, DEFAULT_CHAT_AGENT_MODE, type DefaultChatTransportEndpoints, type DefaultChatTransportModelsResolver, type DefaultChatTransportRequestBodyBuilder, type DefaultChatTransportRequestBodyBuilderArgs, type ExecutionConfirmationSubmission, type ExecutionProposal, type PlanQuestionOption, type PlanQuestionSubmissionDetail, type PlanQuestionnaire, type PlanQuestionnaireSubmission, type ResultSummary, type TransformChatStreamPacket, createDefaultChatTransport, useChatContext, useChatStore };
|
package/dist/index.d.ts
CHANGED
|
@@ -42,6 +42,12 @@ interface ChatImageAttachment {
|
|
|
42
42
|
mimeType: string;
|
|
43
43
|
size: number;
|
|
44
44
|
previewUrl: string;
|
|
45
|
+
/**
|
|
46
|
+
* Original file kept for transports that need to upload or serialize the image.
|
|
47
|
+
*
|
|
48
|
+
* This is only available for client-originated attachments selected in the composer.
|
|
49
|
+
*/
|
|
50
|
+
file?: File;
|
|
45
51
|
}
|
|
46
52
|
/**
|
|
47
53
|
* Select option metadata used by plan questionnaires.
|
|
@@ -291,6 +297,10 @@ interface ChatTransportStartStreamArgs {
|
|
|
291
297
|
mode: ChatAgentMode;
|
|
292
298
|
/** User message content that should be sent to the backend. */
|
|
293
299
|
content: string;
|
|
300
|
+
/**
|
|
301
|
+
* Optional image attachments selected in the composer and sent alongside the text.
|
|
302
|
+
*/
|
|
303
|
+
attachments?: ChatImageAttachment[];
|
|
294
304
|
/** Abort signal controlled by the chat composer stop action. */
|
|
295
305
|
signal?: AbortSignal;
|
|
296
306
|
/** Emits normalized streaming patches that should update the assistant message. */
|
|
@@ -511,6 +521,25 @@ interface ChatToolExecutionPolicy {
|
|
|
511
521
|
/** Approval timeout in seconds, forwarded when approval is enabled. */
|
|
512
522
|
approvalTimeoutSec?: number;
|
|
513
523
|
}
|
|
524
|
+
/**
|
|
525
|
+
* Arguments passed to a custom request body builder for the default transport.
|
|
526
|
+
*/
|
|
527
|
+
interface DefaultChatTransportRequestBodyBuilderArgs {
|
|
528
|
+
sessionId?: string;
|
|
529
|
+
model: string;
|
|
530
|
+
mode: ChatAgentMode;
|
|
531
|
+
content: string;
|
|
532
|
+
attachments?: ChatImageAttachment[];
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Optional builder used to customize the `/chat/completions` request body while
|
|
536
|
+
* reusing the default streaming transport behavior.
|
|
537
|
+
*/
|
|
538
|
+
type DefaultChatTransportRequestBodyBuilder = (args: DefaultChatTransportRequestBodyBuilderArgs) => unknown;
|
|
539
|
+
/**
|
|
540
|
+
* Optional model resolver used to override the default `/models` request.
|
|
541
|
+
*/
|
|
542
|
+
type DefaultChatTransportModelsResolver = () => Promise<ChatModelsResponse>;
|
|
514
543
|
/**
|
|
515
544
|
* Options for the built-in HTTP transport adapter.
|
|
516
545
|
*/
|
|
@@ -523,6 +552,10 @@ interface CreateDefaultChatTransportOptions {
|
|
|
523
552
|
toolExecutionPolicy?: ChatToolExecutionPolicy;
|
|
524
553
|
/** Optional extra headers appended to each streaming chat completion request. */
|
|
525
554
|
streamHeaders?: Record<string, string>;
|
|
555
|
+
/** Optional resolver used to override the built-in model catalog request. */
|
|
556
|
+
resolveModels?: DefaultChatTransportModelsResolver;
|
|
557
|
+
/** Optional builder used to override the built-in chat completion request body. */
|
|
558
|
+
buildRequestBody?: DefaultChatTransportRequestBodyBuilder;
|
|
526
559
|
/** Optional transformer used to normalize custom stream packets. */
|
|
527
560
|
transformStreamPacket?: TransformChatStreamPacket;
|
|
528
561
|
/** Optional endpoint overrides for backends that use different paths. */
|
|
@@ -533,7 +566,7 @@ interface CreateDefaultChatTransportOptions {
|
|
|
533
566
|
/**
|
|
534
567
|
* Creates the built-in transport backed by the current HTTP chat API.
|
|
535
568
|
*/
|
|
536
|
-
declare const createDefaultChatTransport: ({ apiBaseUrl, authToken, toolExecutionPolicy, streamHeaders, transformStreamPacket, endpoints, axiosInstance, }: CreateDefaultChatTransportOptions) => ChatTransport;
|
|
569
|
+
declare const createDefaultChatTransport: ({ apiBaseUrl, authToken, toolExecutionPolicy, streamHeaders, resolveModels, buildRequestBody, transformStreamPacket, endpoints, axiosInstance, }: CreateDefaultChatTransportOptions) => ChatTransport;
|
|
537
570
|
|
|
538
571
|
declare const ChatThread: () => _emotion_react_jsx_runtime.JSX.Element;
|
|
539
572
|
|
|
@@ -654,4 +687,4 @@ declare const useChatContext: () => ChatContextValue;
|
|
|
654
687
|
*/
|
|
655
688
|
declare const useChatStore: <T>(selector: (state: ChatStore) => T) => T;
|
|
656
689
|
|
|
657
|
-
export { AiChat, type AiChatLabels, type AiChatProps, AiChatProvider, type AiChatProviderProps, CHAT_AGENT_MODES, CHAT_MESSAGE_RENDER_ORDERS, type ChatAgentMode, ChatComposer, type ChatComposerViewProps, type ChatConfirmationSubmitHandler, ChatConversationList, type ChatImageAttachment, type ChatMessage, type ChatMessageBlock, type ChatMessageBlockRenderer, type ChatMessageBlockRendererProps, type ChatMessageRenderOrder, type ChatMessageStatus, type ChatModel, type ChatParameterSummaryItem, type ChatQuestionnaireSubmitHandler, type ChatRole, type ChatSession, type ChatStreamMessagePatch, type ChatStreamPacketTransformArgs, type ChatStreamPacketUpdate, type ChatSubmissionContext, ChatThread, type ChatToolExecutionPolicy, type ChatTransport, type ChatTransportStartStreamArgs, type CreateDefaultChatTransportOptions, DEFAULT_AI_CHAT_LABELS, DEFAULT_CHAT_AGENT_MODE, type DefaultChatTransportEndpoints, type ExecutionConfirmationSubmission, type ExecutionProposal, type PlanQuestionOption, type PlanQuestionSubmissionDetail, type PlanQuestionnaire, type PlanQuestionnaireSubmission, type ResultSummary, type TransformChatStreamPacket, createDefaultChatTransport, useChatContext, useChatStore };
|
|
690
|
+
export { AiChat, type AiChatLabels, type AiChatProps, AiChatProvider, type AiChatProviderProps, CHAT_AGENT_MODES, CHAT_MESSAGE_RENDER_ORDERS, type ChatAgentMode, ChatComposer, type ChatComposerViewProps, type ChatConfirmationSubmitHandler, ChatConversationList, type ChatImageAttachment, type ChatMessage, type ChatMessageBlock, type ChatMessageBlockRenderer, type ChatMessageBlockRendererProps, type ChatMessageRenderOrder, type ChatMessageStatus, type ChatModel, type ChatParameterSummaryItem, type ChatQuestionnaireSubmitHandler, type ChatRole, type ChatSession, type ChatStreamMessagePatch, type ChatStreamPacketTransformArgs, type ChatStreamPacketUpdate, type ChatSubmissionContext, ChatThread, type ChatToolExecutionPolicy, type ChatTransport, type ChatTransportStartStreamArgs, type CreateDefaultChatTransportOptions, DEFAULT_AI_CHAT_LABELS, DEFAULT_CHAT_AGENT_MODE, type DefaultChatTransportEndpoints, type DefaultChatTransportModelsResolver, type DefaultChatTransportRequestBodyBuilder, type DefaultChatTransportRequestBodyBuilderArgs, type ExecutionConfirmationSubmission, type ExecutionProposal, type PlanQuestionOption, type PlanQuestionSubmissionDetail, type PlanQuestionnaire, type PlanQuestionnaireSubmission, type ResultSummary, type TransformChatStreamPacket, createDefaultChatTransport, useChatContext, useChatStore };
|
package/dist/index.js
CHANGED
|
@@ -491,6 +491,7 @@ var startChatStream = async ({
|
|
|
491
491
|
sessionId,
|
|
492
492
|
authToken,
|
|
493
493
|
requestHeaders,
|
|
494
|
+
requestBody,
|
|
494
495
|
model,
|
|
495
496
|
mode,
|
|
496
497
|
content,
|
|
@@ -512,12 +513,14 @@ var startChatStream = async ({
|
|
|
512
513
|
const response = await fetch(`${apiBaseUrl}${endpointPath}`, {
|
|
513
514
|
method: "POST",
|
|
514
515
|
headers,
|
|
515
|
-
body: JSON.stringify(
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
516
|
+
body: JSON.stringify(
|
|
517
|
+
requestBody ?? {
|
|
518
|
+
model,
|
|
519
|
+
mode,
|
|
520
|
+
stream: true,
|
|
521
|
+
messages: [{ role: "user", content }]
|
|
522
|
+
}
|
|
523
|
+
),
|
|
521
524
|
signal
|
|
522
525
|
});
|
|
523
526
|
const contentType = response.headers.get("content-type") ?? "";
|
|
@@ -593,11 +596,70 @@ var createModeDefaultHeaders = (mode) => {
|
|
|
593
596
|
}
|
|
594
597
|
return {};
|
|
595
598
|
};
|
|
599
|
+
var readFileAsDataUrl = (file) => new Promise((resolve, reject) => {
|
|
600
|
+
const reader = new FileReader();
|
|
601
|
+
reader.onload = () => {
|
|
602
|
+
if (typeof reader.result === "string") {
|
|
603
|
+
resolve(reader.result);
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
reject(new Error(`Failed to read image attachment: ${file.name}`));
|
|
607
|
+
};
|
|
608
|
+
reader.onerror = () => {
|
|
609
|
+
reject(new Error(`Failed to read image attachment: ${file.name}`));
|
|
610
|
+
};
|
|
611
|
+
reader.readAsDataURL(file);
|
|
612
|
+
});
|
|
613
|
+
var resolveAttachmentDataUrl = async (attachment) => {
|
|
614
|
+
if (attachment.file) {
|
|
615
|
+
return readFileAsDataUrl(attachment.file);
|
|
616
|
+
}
|
|
617
|
+
if (attachment.previewUrl.startsWith("data:image/")) {
|
|
618
|
+
return attachment.previewUrl;
|
|
619
|
+
}
|
|
620
|
+
throw new Error(`Attachment is missing file data: ${attachment.name}`);
|
|
621
|
+
};
|
|
622
|
+
var createDefaultRequestBody = async ({
|
|
623
|
+
model,
|
|
624
|
+
mode,
|
|
625
|
+
content,
|
|
626
|
+
attachments
|
|
627
|
+
}) => {
|
|
628
|
+
const hasAttachments = Boolean(attachments?.length);
|
|
629
|
+
if (!hasAttachments) {
|
|
630
|
+
return {
|
|
631
|
+
model,
|
|
632
|
+
mode,
|
|
633
|
+
stream: true,
|
|
634
|
+
messages: [{ role: "user", content }]
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
const imageParts = await Promise.all(
|
|
638
|
+
(attachments ?? []).map(async (attachment) => ({
|
|
639
|
+
type: "image_url",
|
|
640
|
+
image_url: {
|
|
641
|
+
url: await resolveAttachmentDataUrl(attachment)
|
|
642
|
+
}
|
|
643
|
+
}))
|
|
644
|
+
);
|
|
645
|
+
const messageContent = [
|
|
646
|
+
...content ? [{ type: "text", text: content }] : [],
|
|
647
|
+
...imageParts
|
|
648
|
+
];
|
|
649
|
+
return {
|
|
650
|
+
model,
|
|
651
|
+
mode,
|
|
652
|
+
stream: true,
|
|
653
|
+
messages: [{ role: "user", content: messageContent }]
|
|
654
|
+
};
|
|
655
|
+
};
|
|
596
656
|
var createDefaultChatTransport = ({
|
|
597
657
|
apiBaseUrl,
|
|
598
658
|
authToken,
|
|
599
659
|
toolExecutionPolicy,
|
|
600
660
|
streamHeaders,
|
|
661
|
+
resolveModels,
|
|
662
|
+
buildRequestBody,
|
|
601
663
|
transformStreamPacket,
|
|
602
664
|
endpoints,
|
|
603
665
|
axiosInstance
|
|
@@ -612,12 +674,13 @@ var createDefaultChatTransport = ({
|
|
|
612
674
|
...streamHeaders
|
|
613
675
|
};
|
|
614
676
|
return {
|
|
615
|
-
getModels: () => getChatModels(client, resolvedEndpoints.models),
|
|
677
|
+
getModels: () => resolveModels?.() ?? getChatModels(client, resolvedEndpoints.models),
|
|
616
678
|
startStream: async ({
|
|
617
679
|
sessionId,
|
|
618
680
|
model,
|
|
619
681
|
mode,
|
|
620
682
|
content,
|
|
683
|
+
attachments,
|
|
621
684
|
signal,
|
|
622
685
|
onUpdate,
|
|
623
686
|
onSessionId,
|
|
@@ -628,12 +691,20 @@ var createDefaultChatTransport = ({
|
|
|
628
691
|
...createModeDefaultHeaders(mode),
|
|
629
692
|
...resolvedStreamHeaders
|
|
630
693
|
};
|
|
694
|
+
const requestBody = buildRequestBody ? buildRequestBody({
|
|
695
|
+
sessionId,
|
|
696
|
+
model,
|
|
697
|
+
mode,
|
|
698
|
+
content,
|
|
699
|
+
attachments
|
|
700
|
+
}) : await createDefaultRequestBody({ model, mode, content, attachments });
|
|
631
701
|
await startChatStream({
|
|
632
702
|
apiBaseUrl,
|
|
633
703
|
endpointPath: resolvedEndpoints.completions,
|
|
634
704
|
sessionId,
|
|
635
705
|
authToken,
|
|
636
706
|
requestHeaders,
|
|
707
|
+
requestBody,
|
|
637
708
|
model,
|
|
638
709
|
mode,
|
|
639
710
|
content,
|
|
@@ -2339,7 +2410,7 @@ var TextInput = import_styled4.default.input`
|
|
|
2339
2410
|
color: rgba(255, 255, 255, 0.34);
|
|
2340
2411
|
}
|
|
2341
2412
|
`;
|
|
2342
|
-
var InlineOtherInput = (0, import_styled4.default)(import_compass_ui.
|
|
2413
|
+
var InlineOtherInput = (0, import_styled4.default)(import_compass_ui.InputField)`
|
|
2343
2414
|
width: 100%;
|
|
2344
2415
|
margin-top: 0;
|
|
2345
2416
|
|
|
@@ -4087,7 +4158,8 @@ var useComposerAttachments = () => {
|
|
|
4087
4158
|
return [];
|
|
4088
4159
|
}
|
|
4089
4160
|
const nextMessageAttachments = currentAttachments.map(({ file: _file, ...attachment }) => ({
|
|
4090
|
-
...attachment
|
|
4161
|
+
...attachment,
|
|
4162
|
+
file: _file
|
|
4091
4163
|
}));
|
|
4092
4164
|
attachmentsRef.current = [];
|
|
4093
4165
|
setAttachments([]);
|
|
@@ -4237,6 +4309,7 @@ var useChatComposer = () => {
|
|
|
4237
4309
|
localSessionId,
|
|
4238
4310
|
sessionId,
|
|
4239
4311
|
content,
|
|
4312
|
+
attachments: attachments2,
|
|
4240
4313
|
model,
|
|
4241
4314
|
mode
|
|
4242
4315
|
}) => {
|
|
@@ -4251,7 +4324,7 @@ var useChatComposer = () => {
|
|
|
4251
4324
|
startStreamingMessage(currentSessionId, assistantMessage);
|
|
4252
4325
|
abortControllerRef.current?.abort();
|
|
4253
4326
|
abortControllerRef.current = new AbortController();
|
|
4254
|
-
lastRequestRef.current = { localSessionId, sessionId, content, model, mode };
|
|
4327
|
+
lastRequestRef.current = { localSessionId, sessionId, content, attachments: attachments2, model, mode };
|
|
4255
4328
|
let accumulated = "";
|
|
4256
4329
|
try {
|
|
4257
4330
|
await transport.startStream({
|
|
@@ -4259,6 +4332,7 @@ var useChatComposer = () => {
|
|
|
4259
4332
|
model,
|
|
4260
4333
|
mode,
|
|
4261
4334
|
content,
|
|
4335
|
+
attachments: attachments2,
|
|
4262
4336
|
signal: abortControllerRef.current.signal,
|
|
4263
4337
|
onSessionId: (nextSessionId) => {
|
|
4264
4338
|
if (!nextSessionId || nextSessionId === currentSessionId)
|
|
@@ -4269,6 +4343,7 @@ var useChatComposer = () => {
|
|
|
4269
4343
|
localSessionId: nextSessionId,
|
|
4270
4344
|
sessionId: nextSessionId,
|
|
4271
4345
|
content,
|
|
4346
|
+
attachments: attachments2,
|
|
4272
4347
|
model,
|
|
4273
4348
|
mode
|
|
4274
4349
|
};
|
|
@@ -4324,8 +4399,6 @@ var useChatComposer = () => {
|
|
|
4324
4399
|
const send = (0, import_react13.useCallback)(
|
|
4325
4400
|
async (contentOverride) => {
|
|
4326
4401
|
const content = (contentOverride ?? value).trim();
|
|
4327
|
-
const hasText = Boolean(content);
|
|
4328
|
-
const hasAttachments = attachments.length > 0;
|
|
4329
4402
|
if (!canSendChatMessage({
|
|
4330
4403
|
value: content,
|
|
4331
4404
|
attachmentCount: attachments.length,
|
|
@@ -4335,7 +4408,7 @@ var useChatComposer = () => {
|
|
|
4335
4408
|
})) {
|
|
4336
4409
|
return;
|
|
4337
4410
|
}
|
|
4338
|
-
if (
|
|
4411
|
+
if (!(selectedModel || activeSession?.model || availableModels[0]?.id)) {
|
|
4339
4412
|
return;
|
|
4340
4413
|
}
|
|
4341
4414
|
const resolvedModel = selectedModel || activeSession?.model || availableModels[0]?.id || "local-image";
|
|
@@ -4353,19 +4426,18 @@ var useChatComposer = () => {
|
|
|
4353
4426
|
sessionId: localSessionId,
|
|
4354
4427
|
content,
|
|
4355
4428
|
attachments: messageAttachments,
|
|
4356
|
-
localOnly:
|
|
4429
|
+
localOnly: false,
|
|
4357
4430
|
createdAt: nowIso(),
|
|
4358
4431
|
createMessageId: () => `user-${Date.now()}`
|
|
4359
4432
|
});
|
|
4360
4433
|
appendMessage(localSessionId, userMessage);
|
|
4361
4434
|
setAttachmentNotice(null);
|
|
4362
4435
|
setValue("");
|
|
4363
|
-
if (!hasText)
|
|
4364
|
-
return;
|
|
4365
4436
|
await runStream({
|
|
4366
4437
|
localSessionId,
|
|
4367
4438
|
sessionId,
|
|
4368
4439
|
content,
|
|
4440
|
+
attachments: messageAttachments,
|
|
4369
4441
|
model: resolvedModel,
|
|
4370
4442
|
mode: selectedMode
|
|
4371
4443
|
});
|
|
@@ -5324,9 +5396,9 @@ var ComposerExpandButton = import_styled14.default.button`
|
|
|
5324
5396
|
var Footer = import_styled14.default.div`
|
|
5325
5397
|
grid-area: footer;
|
|
5326
5398
|
display: grid;
|
|
5327
|
-
grid-template-columns: minmax(0, 1fr)
|
|
5399
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
5328
5400
|
align-items: flex-end;
|
|
5329
|
-
gap:
|
|
5401
|
+
gap: 8px;
|
|
5330
5402
|
padding: 0 14px 14px;
|
|
5331
5403
|
`;
|
|
5332
5404
|
var LeadingActions = import_styled14.default.div`
|
|
@@ -5341,6 +5413,9 @@ var TrailingActions = import_styled14.default.div`
|
|
|
5341
5413
|
align-items: center;
|
|
5342
5414
|
flex-wrap: wrap;
|
|
5343
5415
|
min-width: 0;
|
|
5416
|
+
width: fit-content;
|
|
5417
|
+
max-width: 100%;
|
|
5418
|
+
justify-self: end;
|
|
5344
5419
|
justify-content: flex-end;
|
|
5345
5420
|
gap: 8px;
|
|
5346
5421
|
`;
|
|
@@ -5555,6 +5630,7 @@ var Workspace = import_styled17.default.section`
|
|
|
5555
5630
|
flex: 1;
|
|
5556
5631
|
display: flex;
|
|
5557
5632
|
flex-direction: column;
|
|
5633
|
+
gap: 12px;
|
|
5558
5634
|
min-height: 0;
|
|
5559
5635
|
overflow: hidden;
|
|
5560
5636
|
`;
|
package/dist/index.mjs
CHANGED
|
@@ -444,6 +444,7 @@ var startChatStream = async ({
|
|
|
444
444
|
sessionId,
|
|
445
445
|
authToken,
|
|
446
446
|
requestHeaders,
|
|
447
|
+
requestBody,
|
|
447
448
|
model,
|
|
448
449
|
mode,
|
|
449
450
|
content,
|
|
@@ -465,12 +466,14 @@ var startChatStream = async ({
|
|
|
465
466
|
const response = await fetch(`${apiBaseUrl}${endpointPath}`, {
|
|
466
467
|
method: "POST",
|
|
467
468
|
headers,
|
|
468
|
-
body: JSON.stringify(
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
469
|
+
body: JSON.stringify(
|
|
470
|
+
requestBody ?? {
|
|
471
|
+
model,
|
|
472
|
+
mode,
|
|
473
|
+
stream: true,
|
|
474
|
+
messages: [{ role: "user", content }]
|
|
475
|
+
}
|
|
476
|
+
),
|
|
474
477
|
signal
|
|
475
478
|
});
|
|
476
479
|
const contentType = response.headers.get("content-type") ?? "";
|
|
@@ -546,11 +549,70 @@ var createModeDefaultHeaders = (mode) => {
|
|
|
546
549
|
}
|
|
547
550
|
return {};
|
|
548
551
|
};
|
|
552
|
+
var readFileAsDataUrl = (file) => new Promise((resolve, reject) => {
|
|
553
|
+
const reader = new FileReader();
|
|
554
|
+
reader.onload = () => {
|
|
555
|
+
if (typeof reader.result === "string") {
|
|
556
|
+
resolve(reader.result);
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
reject(new Error(`Failed to read image attachment: ${file.name}`));
|
|
560
|
+
};
|
|
561
|
+
reader.onerror = () => {
|
|
562
|
+
reject(new Error(`Failed to read image attachment: ${file.name}`));
|
|
563
|
+
};
|
|
564
|
+
reader.readAsDataURL(file);
|
|
565
|
+
});
|
|
566
|
+
var resolveAttachmentDataUrl = async (attachment) => {
|
|
567
|
+
if (attachment.file) {
|
|
568
|
+
return readFileAsDataUrl(attachment.file);
|
|
569
|
+
}
|
|
570
|
+
if (attachment.previewUrl.startsWith("data:image/")) {
|
|
571
|
+
return attachment.previewUrl;
|
|
572
|
+
}
|
|
573
|
+
throw new Error(`Attachment is missing file data: ${attachment.name}`);
|
|
574
|
+
};
|
|
575
|
+
var createDefaultRequestBody = async ({
|
|
576
|
+
model,
|
|
577
|
+
mode,
|
|
578
|
+
content,
|
|
579
|
+
attachments
|
|
580
|
+
}) => {
|
|
581
|
+
const hasAttachments = Boolean(attachments?.length);
|
|
582
|
+
if (!hasAttachments) {
|
|
583
|
+
return {
|
|
584
|
+
model,
|
|
585
|
+
mode,
|
|
586
|
+
stream: true,
|
|
587
|
+
messages: [{ role: "user", content }]
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
const imageParts = await Promise.all(
|
|
591
|
+
(attachments ?? []).map(async (attachment) => ({
|
|
592
|
+
type: "image_url",
|
|
593
|
+
image_url: {
|
|
594
|
+
url: await resolveAttachmentDataUrl(attachment)
|
|
595
|
+
}
|
|
596
|
+
}))
|
|
597
|
+
);
|
|
598
|
+
const messageContent = [
|
|
599
|
+
...content ? [{ type: "text", text: content }] : [],
|
|
600
|
+
...imageParts
|
|
601
|
+
];
|
|
602
|
+
return {
|
|
603
|
+
model,
|
|
604
|
+
mode,
|
|
605
|
+
stream: true,
|
|
606
|
+
messages: [{ role: "user", content: messageContent }]
|
|
607
|
+
};
|
|
608
|
+
};
|
|
549
609
|
var createDefaultChatTransport = ({
|
|
550
610
|
apiBaseUrl,
|
|
551
611
|
authToken,
|
|
552
612
|
toolExecutionPolicy,
|
|
553
613
|
streamHeaders,
|
|
614
|
+
resolveModels,
|
|
615
|
+
buildRequestBody,
|
|
554
616
|
transformStreamPacket,
|
|
555
617
|
endpoints,
|
|
556
618
|
axiosInstance
|
|
@@ -565,12 +627,13 @@ var createDefaultChatTransport = ({
|
|
|
565
627
|
...streamHeaders
|
|
566
628
|
};
|
|
567
629
|
return {
|
|
568
|
-
getModels: () => getChatModels(client, resolvedEndpoints.models),
|
|
630
|
+
getModels: () => resolveModels?.() ?? getChatModels(client, resolvedEndpoints.models),
|
|
569
631
|
startStream: async ({
|
|
570
632
|
sessionId,
|
|
571
633
|
model,
|
|
572
634
|
mode,
|
|
573
635
|
content,
|
|
636
|
+
attachments,
|
|
574
637
|
signal,
|
|
575
638
|
onUpdate,
|
|
576
639
|
onSessionId,
|
|
@@ -581,12 +644,20 @@ var createDefaultChatTransport = ({
|
|
|
581
644
|
...createModeDefaultHeaders(mode),
|
|
582
645
|
...resolvedStreamHeaders
|
|
583
646
|
};
|
|
647
|
+
const requestBody = buildRequestBody ? buildRequestBody({
|
|
648
|
+
sessionId,
|
|
649
|
+
model,
|
|
650
|
+
mode,
|
|
651
|
+
content,
|
|
652
|
+
attachments
|
|
653
|
+
}) : await createDefaultRequestBody({ model, mode, content, attachments });
|
|
584
654
|
await startChatStream({
|
|
585
655
|
apiBaseUrl,
|
|
586
656
|
endpointPath: resolvedEndpoints.completions,
|
|
587
657
|
sessionId,
|
|
588
658
|
authToken,
|
|
589
659
|
requestHeaders,
|
|
660
|
+
requestBody,
|
|
590
661
|
model,
|
|
591
662
|
mode,
|
|
592
663
|
content,
|
|
@@ -1495,7 +1566,7 @@ import {
|
|
|
1495
1566
|
useState as useState2
|
|
1496
1567
|
} from "react";
|
|
1497
1568
|
import styled4 from "@emotion/styled";
|
|
1498
|
-
import { Input } from "@xinghunm/compass-ui";
|
|
1569
|
+
import { InputField as Input } from "@xinghunm/compass-ui";
|
|
1499
1570
|
|
|
1500
1571
|
// src/components/chat-thread/components/questionnaire-card-helpers.ts
|
|
1501
1572
|
var OTHER_OPTION_VALUE = "__other__";
|
|
@@ -4044,7 +4115,8 @@ var useComposerAttachments = () => {
|
|
|
4044
4115
|
return [];
|
|
4045
4116
|
}
|
|
4046
4117
|
const nextMessageAttachments = currentAttachments.map(({ file: _file, ...attachment }) => ({
|
|
4047
|
-
...attachment
|
|
4118
|
+
...attachment,
|
|
4119
|
+
file: _file
|
|
4048
4120
|
}));
|
|
4049
4121
|
attachmentsRef.current = [];
|
|
4050
4122
|
setAttachments([]);
|
|
@@ -4194,6 +4266,7 @@ var useChatComposer = () => {
|
|
|
4194
4266
|
localSessionId,
|
|
4195
4267
|
sessionId,
|
|
4196
4268
|
content,
|
|
4269
|
+
attachments: attachments2,
|
|
4197
4270
|
model,
|
|
4198
4271
|
mode
|
|
4199
4272
|
}) => {
|
|
@@ -4208,7 +4281,7 @@ var useChatComposer = () => {
|
|
|
4208
4281
|
startStreamingMessage(currentSessionId, assistantMessage);
|
|
4209
4282
|
abortControllerRef.current?.abort();
|
|
4210
4283
|
abortControllerRef.current = new AbortController();
|
|
4211
|
-
lastRequestRef.current = { localSessionId, sessionId, content, model, mode };
|
|
4284
|
+
lastRequestRef.current = { localSessionId, sessionId, content, attachments: attachments2, model, mode };
|
|
4212
4285
|
let accumulated = "";
|
|
4213
4286
|
try {
|
|
4214
4287
|
await transport.startStream({
|
|
@@ -4216,6 +4289,7 @@ var useChatComposer = () => {
|
|
|
4216
4289
|
model,
|
|
4217
4290
|
mode,
|
|
4218
4291
|
content,
|
|
4292
|
+
attachments: attachments2,
|
|
4219
4293
|
signal: abortControllerRef.current.signal,
|
|
4220
4294
|
onSessionId: (nextSessionId) => {
|
|
4221
4295
|
if (!nextSessionId || nextSessionId === currentSessionId)
|
|
@@ -4226,6 +4300,7 @@ var useChatComposer = () => {
|
|
|
4226
4300
|
localSessionId: nextSessionId,
|
|
4227
4301
|
sessionId: nextSessionId,
|
|
4228
4302
|
content,
|
|
4303
|
+
attachments: attachments2,
|
|
4229
4304
|
model,
|
|
4230
4305
|
mode
|
|
4231
4306
|
};
|
|
@@ -4281,8 +4356,6 @@ var useChatComposer = () => {
|
|
|
4281
4356
|
const send = useCallback4(
|
|
4282
4357
|
async (contentOverride) => {
|
|
4283
4358
|
const content = (contentOverride ?? value).trim();
|
|
4284
|
-
const hasText = Boolean(content);
|
|
4285
|
-
const hasAttachments = attachments.length > 0;
|
|
4286
4359
|
if (!canSendChatMessage({
|
|
4287
4360
|
value: content,
|
|
4288
4361
|
attachmentCount: attachments.length,
|
|
@@ -4292,7 +4365,7 @@ var useChatComposer = () => {
|
|
|
4292
4365
|
})) {
|
|
4293
4366
|
return;
|
|
4294
4367
|
}
|
|
4295
|
-
if (
|
|
4368
|
+
if (!(selectedModel || activeSession?.model || availableModels[0]?.id)) {
|
|
4296
4369
|
return;
|
|
4297
4370
|
}
|
|
4298
4371
|
const resolvedModel = selectedModel || activeSession?.model || availableModels[0]?.id || "local-image";
|
|
@@ -4310,19 +4383,18 @@ var useChatComposer = () => {
|
|
|
4310
4383
|
sessionId: localSessionId,
|
|
4311
4384
|
content,
|
|
4312
4385
|
attachments: messageAttachments,
|
|
4313
|
-
localOnly:
|
|
4386
|
+
localOnly: false,
|
|
4314
4387
|
createdAt: nowIso(),
|
|
4315
4388
|
createMessageId: () => `user-${Date.now()}`
|
|
4316
4389
|
});
|
|
4317
4390
|
appendMessage(localSessionId, userMessage);
|
|
4318
4391
|
setAttachmentNotice(null);
|
|
4319
4392
|
setValue("");
|
|
4320
|
-
if (!hasText)
|
|
4321
|
-
return;
|
|
4322
4393
|
await runStream({
|
|
4323
4394
|
localSessionId,
|
|
4324
4395
|
sessionId,
|
|
4325
4396
|
content,
|
|
4397
|
+
attachments: messageAttachments,
|
|
4326
4398
|
model: resolvedModel,
|
|
4327
4399
|
mode: selectedMode
|
|
4328
4400
|
});
|
|
@@ -5281,9 +5353,9 @@ var ComposerExpandButton = styled14.button`
|
|
|
5281
5353
|
var Footer = styled14.div`
|
|
5282
5354
|
grid-area: footer;
|
|
5283
5355
|
display: grid;
|
|
5284
|
-
grid-template-columns: minmax(0, 1fr)
|
|
5356
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
5285
5357
|
align-items: flex-end;
|
|
5286
|
-
gap:
|
|
5358
|
+
gap: 8px;
|
|
5287
5359
|
padding: 0 14px 14px;
|
|
5288
5360
|
`;
|
|
5289
5361
|
var LeadingActions = styled14.div`
|
|
@@ -5298,6 +5370,9 @@ var TrailingActions = styled14.div`
|
|
|
5298
5370
|
align-items: center;
|
|
5299
5371
|
flex-wrap: wrap;
|
|
5300
5372
|
min-width: 0;
|
|
5373
|
+
width: fit-content;
|
|
5374
|
+
max-width: 100%;
|
|
5375
|
+
justify-self: end;
|
|
5301
5376
|
justify-content: flex-end;
|
|
5302
5377
|
gap: 8px;
|
|
5303
5378
|
`;
|
|
@@ -5512,6 +5587,7 @@ var Workspace = styled17.section`
|
|
|
5512
5587
|
flex: 1;
|
|
5513
5588
|
display: flex;
|
|
5514
5589
|
flex-direction: column;
|
|
5590
|
+
gap: 12px;
|
|
5515
5591
|
min-height: 0;
|
|
5516
5592
|
overflow: hidden;
|
|
5517
5593
|
`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xinghunm/ai-chat",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "AI chat React component library",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"peerDependencies": {
|
|
20
20
|
"@emotion/react": ">=11",
|
|
21
21
|
"@emotion/styled": ">=11",
|
|
22
|
-
"@xinghunm/compass-ui": "
|
|
22
|
+
"@xinghunm/compass-ui": "0.8.3",
|
|
23
23
|
"axios": ">=1.0",
|
|
24
24
|
"react": ">=18",
|
|
25
25
|
"react-dom": ">=18",
|