wtfai 1.7.3 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -0
- package/dist/client.d.ts +10 -1
- package/dist/client.js +8 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/realtime.d.ts +23 -0
- package/dist/realtime.js +76 -0
- package/dist/session.d.ts +11 -1
- package/dist/session.js +27 -0
- package/dist/types.d.ts +2 -2
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -680,6 +680,50 @@ Content-Type: application/json
|
|
|
680
680
|
|
|
681
681
|
---
|
|
682
682
|
|
|
683
|
+
---
|
|
684
|
+
|
|
685
|
+
### RealtimeService
|
|
686
|
+
|
|
687
|
+
提供跨端的实时消息总线功能(通过 `client.realtime` 访问)。
|
|
688
|
+
|
|
689
|
+
##### `subscribe(topic: string, onEvent: (event: RealtimeEvent) => void)`
|
|
690
|
+
|
|
691
|
+
订阅实时消息流。适合大屏端监听由其他端发送的指令或通知。
|
|
692
|
+
|
|
693
|
+
- **自动管理**:方法内部会自动计算并附加当前浏览器的设备指纹(deviceId),激活在线状态追踪。
|
|
694
|
+
- **返回值**:返回一个对象,包含 `unsubscribe()` 方法用于断开连接。
|
|
695
|
+
|
|
696
|
+
```typescript
|
|
697
|
+
const sub = await client.realtime.subscribe('class:101', (event) => {
|
|
698
|
+
console.log('收到实时更新:', event.data);
|
|
699
|
+
// event.event 为事件名称,event.data 为业务数据
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
// 当页面销毁或不需要监听时,请务必手动释放:
|
|
703
|
+
// sub.unsubscribe();
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
##### `push(topic: string, event: string, data: any)`
|
|
707
|
+
|
|
708
|
+
推送实时消息。适合手机端(或其他客户端)发送指令。
|
|
709
|
+
|
|
710
|
+
```typescript
|
|
711
|
+
await client.realtime.push('class:101', 'msgA', {
|
|
712
|
+
foo: 'bar'
|
|
713
|
+
});
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
##### `getPresence(topic: string)`
|
|
717
|
+
|
|
718
|
+
获取指定主题下的在线状态详情(如当前有多少个大屏在线)。
|
|
719
|
+
|
|
720
|
+
```typescript
|
|
721
|
+
const { onlineCount, clients } = await client.realtime.getPresence('class:101');
|
|
722
|
+
console.log(`当前在线设备数: ${onlineCount}`);
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
---
|
|
726
|
+
|
|
683
727
|
### UploadService
|
|
684
728
|
|
|
685
729
|
文件上传服务(通过 `client.upload` 访问)。
|
package/dist/client.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { Workflow, WorkflowListResponse } from '@our-llm/shared/types';
|
|
1
|
+
import type { Workflow, WorkflowListResponse, DeviceInfo } from '@our-llm/shared/types';
|
|
2
2
|
import type { ClientConfig, SessionOptions } from './types';
|
|
3
3
|
import { WorkflowSession } from './session';
|
|
4
4
|
import { UploadService } from './upload';
|
|
5
|
+
import { RealtimeService } from './realtime';
|
|
5
6
|
/**
|
|
6
7
|
* Wtfai 客户端
|
|
7
8
|
*
|
|
@@ -33,6 +34,10 @@ export declare class Wtfai {
|
|
|
33
34
|
* 文件上传服务
|
|
34
35
|
*/
|
|
35
36
|
readonly upload: UploadService;
|
|
37
|
+
/**
|
|
38
|
+
* 实时同步服务
|
|
39
|
+
*/
|
|
40
|
+
readonly realtime: RealtimeService;
|
|
36
41
|
private readonly headers;
|
|
37
42
|
constructor(config: ClientConfig);
|
|
38
43
|
/**
|
|
@@ -47,4 +52,8 @@ export declare class Wtfai {
|
|
|
47
52
|
* 创建工作流会话
|
|
48
53
|
*/
|
|
49
54
|
createSession(workflowId: string, options?: SessionOptions): WorkflowSession;
|
|
55
|
+
/**
|
|
56
|
+
* 获取当前详细的设备信息(本地计算)
|
|
57
|
+
*/
|
|
58
|
+
getDeviceInfo(): Promise<DeviceInfo>;
|
|
50
59
|
}
|
package/dist/client.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { getDeviceInfo } from "@our-llm/shared/fingerprint";
|
|
1
2
|
import { WorkflowSession } from "./session.js";
|
|
2
3
|
import { UploadService } from "./upload.js";
|
|
4
|
+
import { RealtimeService } from "./realtime.js";
|
|
3
5
|
function _define_property(obj, key, value) {
|
|
4
6
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
5
7
|
value: value,
|
|
@@ -32,12 +34,16 @@ class Wtfai {
|
|
|
32
34
|
createSession(workflowId, options) {
|
|
33
35
|
return new WorkflowSession(workflowId, this.baseUrl, this.uploadService, this.headers, options);
|
|
34
36
|
}
|
|
37
|
+
async getDeviceInfo() {
|
|
38
|
+
return getDeviceInfo();
|
|
39
|
+
}
|
|
35
40
|
constructor(config){
|
|
36
41
|
_define_property(this, "baseUrl", void 0);
|
|
37
42
|
_define_property(this, "uploadService", void 0);
|
|
38
43
|
_define_property(this, "upload", void 0);
|
|
44
|
+
_define_property(this, "realtime", void 0);
|
|
39
45
|
_define_property(this, "headers", void 0);
|
|
40
|
-
this.baseUrl = config.baseUrl.replace(/\/$/, '');
|
|
46
|
+
this.baseUrl = (config.baseUrl || 'https://llm-api.ourschool.cc').replace(/\/$/, '');
|
|
41
47
|
const headers = {
|
|
42
48
|
...config.headers
|
|
43
49
|
};
|
|
@@ -48,6 +54,7 @@ class Wtfai {
|
|
|
48
54
|
this.headers = headers;
|
|
49
55
|
this.uploadService = new UploadService(this.baseUrl, this.headers);
|
|
50
56
|
this.upload = this.uploadService;
|
|
57
|
+
this.realtime = new RealtimeService(this.baseUrl, this.headers);
|
|
51
58
|
}
|
|
52
59
|
}
|
|
53
60
|
export { Wtfai };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { Wtfai } from './client';
|
|
|
2
2
|
export default Wtfai;
|
|
3
3
|
export { WorkflowSession } from './session';
|
|
4
4
|
export { UploadService } from './upload';
|
|
5
|
+
export { RealtimeService } from './realtime';
|
|
5
6
|
export type { ClientConfig, SessionOptions, SessionState, SessionEventListeners, SendInput, UploadParams, UploadImageParams, CompressOptions, WorkflowInfo, ContentPart, ContentAction, } from './types';
|
|
6
|
-
export type { SimpleMessage, Workflow, WorkflowConfig, } from '@our-llm/shared/types';
|
|
7
|
+
export type { SimpleMessage, Workflow, WorkflowConfig, RealtimeEvent, RealtimePresence, RealtimePushDto, } from '@our-llm/shared/types';
|
|
7
8
|
export { Markdown, type MarkdownProps, WorkflowRegistry } from './ui/markdown';
|
|
8
9
|
export type { WorkflowInfoMapping } from './ui/context';
|
package/dist/index.js
CHANGED
|
@@ -2,5 +2,6 @@ import { Wtfai } from "./client.js";
|
|
|
2
2
|
const src = Wtfai;
|
|
3
3
|
export { WorkflowSession } from "./session.js";
|
|
4
4
|
export { UploadService } from "./upload.js";
|
|
5
|
+
export { RealtimeService } from "./realtime.js";
|
|
5
6
|
export { Markdown, WorkflowRegistry } from "./ui/markdown.js";
|
|
6
7
|
export default src;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { RealtimeEvent, RealtimePresence } from '@our-llm/shared/types';
|
|
2
|
+
export declare class RealtimeService {
|
|
3
|
+
private readonly baseUrl;
|
|
4
|
+
private readonly headers;
|
|
5
|
+
constructor(baseUrl: string, headers: Record<string, string>);
|
|
6
|
+
/**
|
|
7
|
+
* 订阅实时消息流 (适合大屏端)
|
|
8
|
+
* @param topic 主题名称
|
|
9
|
+
* @param onEvent 事件回调
|
|
10
|
+
* @returns 返回一个包含 unsubscribe 方法的对象
|
|
11
|
+
*/
|
|
12
|
+
subscribe(topic: string, onEvent: (event: RealtimeEvent) => void): Promise<{
|
|
13
|
+
unsubscribe: () => void;
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* 推送实时消息 (适合手机端)
|
|
17
|
+
*/
|
|
18
|
+
push(topic: string, event: string, data: any): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* 获取当前主题的在线状态
|
|
21
|
+
*/
|
|
22
|
+
getPresence(topic: string): Promise<RealtimePresence>;
|
|
23
|
+
}
|
package/dist/realtime.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { fetchEventSource } from "@microsoft/fetch-event-source";
|
|
2
|
+
import { getFingerprint } from "@our-llm/shared/fingerprint";
|
|
3
|
+
function _define_property(obj, key, value) {
|
|
4
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
5
|
+
value: value,
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true
|
|
9
|
+
});
|
|
10
|
+
else obj[key] = value;
|
|
11
|
+
return obj;
|
|
12
|
+
}
|
|
13
|
+
class RealtimeService {
|
|
14
|
+
async subscribe(topic, onEvent) {
|
|
15
|
+
const deviceId = await getFingerprint();
|
|
16
|
+
const controller = new AbortController();
|
|
17
|
+
const query = `?deviceId=${encodeURIComponent(deviceId)}`;
|
|
18
|
+
const url = `${this.baseUrl}/workflows/realtime/streams/${topic}${query}`;
|
|
19
|
+
fetchEventSource(url, {
|
|
20
|
+
method: 'GET',
|
|
21
|
+
headers: {
|
|
22
|
+
Accept: 'text/event-stream',
|
|
23
|
+
...this.headers
|
|
24
|
+
},
|
|
25
|
+
signal: controller.signal,
|
|
26
|
+
onmessage (msg) {
|
|
27
|
+
if (!msg.data) return;
|
|
28
|
+
if ('message' === msg.event || !msg.event) try {
|
|
29
|
+
const event = JSON.parse(msg.data);
|
|
30
|
+
onEvent(event);
|
|
31
|
+
} catch (e) {
|
|
32
|
+
console.error('[SDK Realtime] Failed to parse message', e);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
onerror (err) {
|
|
36
|
+
console.error('[SDK Realtime] Connection error', err);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return {
|
|
40
|
+
unsubscribe: ()=>{
|
|
41
|
+
controller.abort();
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
async push(topic, event, data) {
|
|
46
|
+
const dto = {
|
|
47
|
+
topic,
|
|
48
|
+
event,
|
|
49
|
+
data
|
|
50
|
+
};
|
|
51
|
+
const response = await fetch(`${this.baseUrl}/workflows/realtime/push`, {
|
|
52
|
+
method: 'POST',
|
|
53
|
+
headers: {
|
|
54
|
+
'Content-Type': 'application/json',
|
|
55
|
+
...this.headers
|
|
56
|
+
},
|
|
57
|
+
body: JSON.stringify(dto)
|
|
58
|
+
});
|
|
59
|
+
if (!response.ok) throw new Error(`推送实时消息失败: ${response.statusText}`);
|
|
60
|
+
}
|
|
61
|
+
async getPresence(topic) {
|
|
62
|
+
const response = await fetch(`${this.baseUrl}/workflows/realtime/presence/${topic}`, {
|
|
63
|
+
method: 'GET',
|
|
64
|
+
headers: this.headers
|
|
65
|
+
});
|
|
66
|
+
if (!response.ok) throw new Error(`获取在线状态失败: ${response.statusText}`);
|
|
67
|
+
return response.json();
|
|
68
|
+
}
|
|
69
|
+
constructor(baseUrl, headers){
|
|
70
|
+
_define_property(this, "baseUrl", void 0);
|
|
71
|
+
_define_property(this, "headers", void 0);
|
|
72
|
+
this.baseUrl = baseUrl;
|
|
73
|
+
this.headers = headers;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export { RealtimeService };
|
package/dist/session.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SimpleMessage } from '@our-llm/shared/types';
|
|
1
|
+
import type { SimpleMessage, DeviceInfo } from '@our-llm/shared/types';
|
|
2
2
|
import type { SessionOptions, SessionState, SessionEventListeners, SendInput, ListRecordsOptions, ListRecordsResult } from './types';
|
|
3
3
|
import { UploadService } from './upload';
|
|
4
4
|
/**
|
|
@@ -137,6 +137,16 @@ export declare class WorkflowSession {
|
|
|
137
137
|
* @param options 请求配置
|
|
138
138
|
*/
|
|
139
139
|
fetch(url: string, options?: RequestInit): Promise<any>;
|
|
140
|
+
/**
|
|
141
|
+
* 上报设备信息相关(用于日志监控)
|
|
142
|
+
*/
|
|
143
|
+
reportDeviceInfo(): Promise<{
|
|
144
|
+
success: boolean;
|
|
145
|
+
}>;
|
|
146
|
+
/**
|
|
147
|
+
* 获取当前详细的设备信息(本地计算)
|
|
148
|
+
*/
|
|
149
|
+
getDeviceInfo(): Promise<DeviceInfo>;
|
|
140
150
|
/**
|
|
141
151
|
* 发送消息执行工作流
|
|
142
152
|
*/
|
package/dist/session.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getDeviceInfo, getFingerprint } from "@our-llm/shared/fingerprint";
|
|
1
2
|
import { executeWorkflowSSE } from "./sse.js";
|
|
2
3
|
import { v7 } from "uuid";
|
|
3
4
|
function _define_property(obj, key, value) {
|
|
@@ -371,6 +372,32 @@ class WorkflowSession {
|
|
|
371
372
|
}
|
|
372
373
|
return response.json();
|
|
373
374
|
}
|
|
375
|
+
async reportDeviceInfo() {
|
|
376
|
+
this.assertNotDisposed();
|
|
377
|
+
const deviceId = await getFingerprint();
|
|
378
|
+
const userAgent = navigator.userAgent;
|
|
379
|
+
const sessionId = this.state.threadId;
|
|
380
|
+
if (!sessionId) throw new Error('No active session (missing threadId)');
|
|
381
|
+
const payload = {
|
|
382
|
+
deviceId,
|
|
383
|
+
userAgent,
|
|
384
|
+
sessionId
|
|
385
|
+
};
|
|
386
|
+
const response = await fetch(`${this.baseUrl}/workflows/device-info`, {
|
|
387
|
+
method: 'POST',
|
|
388
|
+
headers: {
|
|
389
|
+
'Content-Type': 'application/json',
|
|
390
|
+
...this.headers
|
|
391
|
+
},
|
|
392
|
+
body: JSON.stringify(payload)
|
|
393
|
+
});
|
|
394
|
+
if (!response.ok) throw new Error('Failed to report device info');
|
|
395
|
+
return response.json();
|
|
396
|
+
}
|
|
397
|
+
async getDeviceInfo() {
|
|
398
|
+
this.assertNotDisposed();
|
|
399
|
+
return getDeviceInfo();
|
|
400
|
+
}
|
|
374
401
|
async send({ parts: inputParts, ...rest }) {
|
|
375
402
|
this.assertNotDisposed();
|
|
376
403
|
if (this.state.isExecuting) throw new Error('工作流正在执行中');
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wtfai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -15,16 +15,16 @@
|
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@eslint/js": "^9.39.4",
|
|
17
17
|
"@rsbuild/plugin-react": "^1.4.6",
|
|
18
|
-
"@rslib/core": "^0.21.
|
|
18
|
+
"@rslib/core": "^0.21.2",
|
|
19
19
|
"@types/react": "^19.2.14",
|
|
20
20
|
"eslint": "^9.39.4",
|
|
21
21
|
"eslint-config-prettier": "^10.1.8",
|
|
22
22
|
"eslint-plugin-prettier": "^5.5.5",
|
|
23
|
-
"globals": "^17.
|
|
24
|
-
"prettier": "^3.8.
|
|
23
|
+
"globals": "^17.5.0",
|
|
24
|
+
"prettier": "^3.8.3",
|
|
25
25
|
"react": "^19.2.5",
|
|
26
26
|
"typescript": "^5.9.3",
|
|
27
|
-
"typescript-eslint": "^8.58.
|
|
27
|
+
"typescript-eslint": "^8.58.2"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"react": ">=16.9.0",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"clsx": "^2.1.1",
|
|
39
39
|
"compressorjs": "^1.3.0",
|
|
40
40
|
"cos-js-sdk-v5": "^1.10.1",
|
|
41
|
-
"jsonrepair": "^3.
|
|
41
|
+
"jsonrepair": "^3.14.0",
|
|
42
42
|
"uuid": "^13.0.0",
|
|
43
43
|
"@our-llm/shared": "3.0.1"
|
|
44
44
|
},
|