@unif/react-native-chat-sdk 0.1.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/package.json +49 -0
- package/src/hooks/ChatProvider.tsx +131 -0
- package/src/hooks/useXChat.ts +136 -0
- package/src/hooks/useXConversations.ts +90 -0
- package/src/hooks/useXModel.ts +33 -0
- package/src/index.md +188 -0
- package/src/index.ts +61 -0
- package/src/providers/AbstractChatProvider.ts +74 -0
- package/src/providers/DeepSeekChatProvider.ts +105 -0
- package/src/providers/OpenAIChatProvider.ts +130 -0
- package/src/stores/chatStore.ts +79 -0
- package/src/stores/historyStore.ts +117 -0
- package/src/stores/modelStore.ts +17 -0
- package/src/stores/sessionStore.ts +21 -0
- package/src/stores/storage.ts +10 -0
- package/src/tools/SSEParser.ts +52 -0
- package/src/tools/XRequest.ts +99 -0
- package/src/tools/XStream.ts +112 -0
- package/src/types/message.ts +26 -0
- package/src/types/provider.ts +24 -0
- package/src/types/sse.ts +93 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XStream — SSE 流适配器(React Native 版本)
|
|
3
|
+
*
|
|
4
|
+
* 基于 react-native-sse 封装,支持 POST 方法 + 自定义请求体
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type EventSource from 'react-native-sse';
|
|
8
|
+
import type {SSEOutput} from '../types/provider';
|
|
9
|
+
|
|
10
|
+
export interface XStreamConfig {
|
|
11
|
+
url: string;
|
|
12
|
+
method?: 'GET' | 'POST';
|
|
13
|
+
headers?: Record<string, string>;
|
|
14
|
+
body?: Record<string, unknown>;
|
|
15
|
+
timeout?: number;
|
|
16
|
+
transformStream?: (event: { data: string; event?: string }) => SSEOutput;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface XStreamCallbacks {
|
|
20
|
+
onMessage: (data: SSEOutput) => void;
|
|
21
|
+
onError?: (error: Error) => void;
|
|
22
|
+
onComplete?: () => void;
|
|
23
|
+
onOpen?: () => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const defaultTransform = (event: {
|
|
27
|
+
data: string;
|
|
28
|
+
event?: string;
|
|
29
|
+
}): SSEOutput => ({
|
|
30
|
+
data: event.data,
|
|
31
|
+
event: event.event,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export class XStream {
|
|
35
|
+
private config: XStreamConfig;
|
|
36
|
+
private es: EventSource | null = null;
|
|
37
|
+
private timeoutTimer: ReturnType<typeof setTimeout> | null = null;
|
|
38
|
+
|
|
39
|
+
constructor(config: XStreamConfig) {
|
|
40
|
+
this.config = config;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
connect(callbacks: XStreamCallbacks): void {
|
|
44
|
+
this.abort();
|
|
45
|
+
|
|
46
|
+
const {
|
|
47
|
+
url,
|
|
48
|
+
method = 'POST',
|
|
49
|
+
headers,
|
|
50
|
+
body,
|
|
51
|
+
timeout = 120000,
|
|
52
|
+
transformStream = defaultTransform,
|
|
53
|
+
} = this.config;
|
|
54
|
+
|
|
55
|
+
// react-native-sse 动态 require,避免非 RN 环境报错
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
57
|
+
const RNEventSource = require('react-native-sse').default;
|
|
58
|
+
|
|
59
|
+
const es = new RNEventSource(url, {
|
|
60
|
+
method,
|
|
61
|
+
headers: {
|
|
62
|
+
'Content-Type': 'application/json',
|
|
63
|
+
...headers,
|
|
64
|
+
},
|
|
65
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
this.es = es;
|
|
69
|
+
|
|
70
|
+
this.timeoutTimer = setTimeout(() => {
|
|
71
|
+
this.abort();
|
|
72
|
+
callbacks.onError?.(new Error('SSE 连接超时'));
|
|
73
|
+
}, timeout);
|
|
74
|
+
|
|
75
|
+
es.addEventListener('open', () => {
|
|
76
|
+
callbacks.onOpen?.();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
es.addEventListener('message', (event: { data?: string }) => {
|
|
80
|
+
if (event.data) {
|
|
81
|
+
const output = transformStream({ data: event.data });
|
|
82
|
+
callbacks.onMessage(output);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
es.addEventListener('error', (event: { type?: string; message?: string }) => {
|
|
87
|
+
this.clearTimer();
|
|
88
|
+
callbacks.onError?.(
|
|
89
|
+
new Error(event.message || 'SSE 连接错误')
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
abort(): void {
|
|
95
|
+
this.clearTimer();
|
|
96
|
+
if (this.es) {
|
|
97
|
+
this.es.close();
|
|
98
|
+
this.es = null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
dispose(): void {
|
|
103
|
+
this.abort();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private clearTimer(): void {
|
|
107
|
+
if (this.timeoutTimer) {
|
|
108
|
+
clearTimeout(this.timeoutTimer);
|
|
109
|
+
this.timeoutTimer = null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat 消息类型定义
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type MessageRole = 'user' | 'assistant' | 'system';
|
|
6
|
+
export type MessageType = 'text' | 'card' | 'system';
|
|
7
|
+
export type MessageStatus =
|
|
8
|
+
| 'local'
|
|
9
|
+
| 'loading'
|
|
10
|
+
| 'updating'
|
|
11
|
+
| 'success'
|
|
12
|
+
| 'error'
|
|
13
|
+
| 'abort';
|
|
14
|
+
|
|
15
|
+
export interface ChatMessage {
|
|
16
|
+
id: string;
|
|
17
|
+
text: string;
|
|
18
|
+
role: MessageRole;
|
|
19
|
+
createdAt: Date;
|
|
20
|
+
messageType: MessageType;
|
|
21
|
+
status: MessageStatus;
|
|
22
|
+
cardType?: string;
|
|
23
|
+
cardData?: { data: Record<string, unknown>; actions: string[] };
|
|
24
|
+
turnId: string;
|
|
25
|
+
extra?: Record<string, unknown>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider 协议类型
|
|
3
|
+
* 定义 ChatProvider ↔ useXChat 之间的数据契约
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {ChatMessage} from './message';
|
|
7
|
+
|
|
8
|
+
export interface RequestParams<T = ChatMessage> {
|
|
9
|
+
message: T;
|
|
10
|
+
messages?: T[];
|
|
11
|
+
model?: string;
|
|
12
|
+
extra?: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface SSEOutput {
|
|
16
|
+
data: string;
|
|
17
|
+
event?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ProviderCallbacks<Message = ChatMessage> {
|
|
21
|
+
onUpdate: (message: Message) => void;
|
|
22
|
+
onSuccess: (message: Message) => void;
|
|
23
|
+
onError: (error: Error) => void;
|
|
24
|
+
}
|
package/src/types/sse.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSEEnvelope 协议类型定义
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type SSEEventType =
|
|
6
|
+
| 'text'
|
|
7
|
+
| 'card_data'
|
|
8
|
+
| 'suggestion'
|
|
9
|
+
| 'error'
|
|
10
|
+
| 'done';
|
|
11
|
+
|
|
12
|
+
export interface SSEEnvelopeBase {
|
|
13
|
+
event_id: string;
|
|
14
|
+
turn_id: string;
|
|
15
|
+
seq: number;
|
|
16
|
+
ts: string;
|
|
17
|
+
session_id: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SSETextEvent extends SSEEnvelopeBase {
|
|
21
|
+
type: 'text';
|
|
22
|
+
payload: { content: string };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface SSECardDataEvent extends SSEEnvelopeBase {
|
|
26
|
+
type: 'card_data';
|
|
27
|
+
payload: {
|
|
28
|
+
cardType: string;
|
|
29
|
+
payload: CardPayload;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface SSESuggestionEvent extends SSEEnvelopeBase {
|
|
34
|
+
type: 'suggestion';
|
|
35
|
+
payload: {
|
|
36
|
+
items: SuggestionItem[];
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface SSEErrorEvent extends SSEEnvelopeBase {
|
|
41
|
+
type: 'error';
|
|
42
|
+
payload: {
|
|
43
|
+
code: string;
|
|
44
|
+
message: string;
|
|
45
|
+
retryable: boolean;
|
|
46
|
+
recover_actions?: ActionPayload[];
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface SSEDoneEvent extends SSEEnvelopeBase {
|
|
51
|
+
type: 'done';
|
|
52
|
+
payload: {
|
|
53
|
+
messageId: string;
|
|
54
|
+
reason: 'ok' | 'abort' | 'error';
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type SSEEnvelope =
|
|
59
|
+
| SSETextEvent
|
|
60
|
+
| SSECardDataEvent
|
|
61
|
+
| SSESuggestionEvent
|
|
62
|
+
| SSEErrorEvent
|
|
63
|
+
| SSEDoneEvent;
|
|
64
|
+
|
|
65
|
+
export interface SuggestionItem {
|
|
66
|
+
id: string;
|
|
67
|
+
label: string;
|
|
68
|
+
action?: ActionPayload;
|
|
69
|
+
mode?: 'action' | 'input_focus';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export type ActionType =
|
|
73
|
+
| 'confirm_order'
|
|
74
|
+
| 'select_product'
|
|
75
|
+
| 'reorder'
|
|
76
|
+
| 'add_product'
|
|
77
|
+
| 'clear_draft'
|
|
78
|
+
| 'push_dealer'
|
|
79
|
+
| 'new_order'
|
|
80
|
+
| 'view_detail'
|
|
81
|
+
| 'add_to_order'
|
|
82
|
+
| 'browse_products'
|
|
83
|
+
| 'retry_order';
|
|
84
|
+
|
|
85
|
+
export interface ActionPayload {
|
|
86
|
+
type: ActionType;
|
|
87
|
+
payload?: Record<string, unknown>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface CardPayload {
|
|
91
|
+
data: Record<string, unknown>;
|
|
92
|
+
actions: string[];
|
|
93
|
+
}
|