simple-ai-sdk 1.0.11 → 1.0.14
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/dist/client/chat-session-utils.d.ts +5 -19
- package/dist/client/chat-session-utils.d.ts.map +1 -1
- package/dist/client/chat-session-utils.js +10 -19
- package/dist/client/manager.d.ts.map +1 -1
- package/dist/client/manager.js +25 -10
- package/dist/client/useChatSession.d.ts.map +1 -1
- package/dist/client/useChatSession.js +39 -17
- package/package.json +2 -2
|
@@ -1,31 +1,17 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type JSONValue } from "../shared/index.js";
|
|
2
2
|
export type PrefetchChatSessionOptions = {
|
|
3
|
-
initialMessages: Message[];
|
|
4
3
|
api: string;
|
|
5
4
|
body?: Record<string, JSONValue>;
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* ここにcontentを指定すると呼び出し時に即座に送信される
|
|
7
|
+
*/
|
|
8
|
+
streamMessageContent?: string;
|
|
7
9
|
};
|
|
8
10
|
/**
|
|
9
11
|
* ページ遷移前にセッションを事前に初期化し、オプションでストリーミングを開始するフック
|
|
10
12
|
*
|
|
11
13
|
* @returns prefetchChatSession - セッションの初期化とストリーミング開始を行う関数
|
|
12
14
|
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```tsx
|
|
15
|
-
* const prefetch = usePrefetchChatSession();
|
|
16
|
-
*
|
|
17
|
-
* // セッション初期化のみ
|
|
18
|
-
* prefetch('session-1', {
|
|
19
|
-
* initialMessages: [{ id: '1', role: 'user', content: 'Hello' }]
|
|
20
|
-
* });
|
|
21
|
-
*
|
|
22
|
-
* // セッション初期化とストリーミング開始(完了を待たない)
|
|
23
|
-
* prefetch('session-2', {
|
|
24
|
-
* initialMessages: [{ id: '1', role: 'user', content: 'Hello' }],
|
|
25
|
-
* startStream: true,
|
|
26
|
-
* api: '/api/chat'
|
|
27
|
-
* });
|
|
28
|
-
* ```
|
|
29
15
|
*/
|
|
30
16
|
export declare function usePrefetchChatSession(): (sessionId: string, options: PrefetchChatSessionOptions) => void;
|
|
31
17
|
//# sourceMappingURL=chat-session-utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-session-utils.d.ts","sourceRoot":"","sources":["../../src/client/chat-session-utils.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"chat-session-utils.d.ts","sourceRoot":"","sources":["../../src/client/chat-session-utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAc,MAAM,oBAAoB,CAAC;AAGhE,MAAM,MAAM,0BAA0B,GAAG;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACjC;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,sBAAsB,gBAItB,MAAM,WAAW,0BAA0B,UAqC1D"}
|
|
@@ -1,38 +1,29 @@
|
|
|
1
1
|
import { useCallback } from "react";
|
|
2
|
+
import { generateId } from "../shared/index.js";
|
|
2
3
|
import { useAIStreamManager } from "./context.js";
|
|
3
4
|
/**
|
|
4
5
|
* ページ遷移前にセッションを事前に初期化し、オプションでストリーミングを開始するフック
|
|
5
6
|
*
|
|
6
7
|
* @returns prefetchChatSession - セッションの初期化とストリーミング開始を行う関数
|
|
7
8
|
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```tsx
|
|
10
|
-
* const prefetch = usePrefetchChatSession();
|
|
11
|
-
*
|
|
12
|
-
* // セッション初期化のみ
|
|
13
|
-
* prefetch('session-1', {
|
|
14
|
-
* initialMessages: [{ id: '1', role: 'user', content: 'Hello' }]
|
|
15
|
-
* });
|
|
16
|
-
*
|
|
17
|
-
* // セッション初期化とストリーミング開始(完了を待たない)
|
|
18
|
-
* prefetch('session-2', {
|
|
19
|
-
* initialMessages: [{ id: '1', role: 'user', content: 'Hello' }],
|
|
20
|
-
* startStream: true,
|
|
21
|
-
* api: '/api/chat'
|
|
22
|
-
* });
|
|
23
|
-
* ```
|
|
24
9
|
*/
|
|
25
10
|
export function usePrefetchChatSession() {
|
|
26
11
|
const manager = useAIStreamManager();
|
|
27
12
|
const prefetchChatSession = useCallback((sessionId, options) => {
|
|
28
13
|
// セッションの初期化(すでに存在する場合はスキップされる)
|
|
29
|
-
manager.initSession(sessionId
|
|
14
|
+
manager.initSession(sessionId);
|
|
30
15
|
// startStreamがtrueの場合、ストリーミングを開始(完了を待たない)
|
|
31
|
-
if (options.
|
|
16
|
+
if (options.streamMessageContent) {
|
|
32
17
|
// streamChatを呼び出すが、awaitせずに即座に返す
|
|
33
18
|
// これによりストリーミングはバックグラウンドで継続される
|
|
34
19
|
manager
|
|
35
|
-
.streamChat(sessionId,
|
|
20
|
+
.streamChat(sessionId, [
|
|
21
|
+
{
|
|
22
|
+
id: generateId({ prefix: "msg-" }),
|
|
23
|
+
role: "user",
|
|
24
|
+
content: options.streamMessageContent,
|
|
25
|
+
},
|
|
26
|
+
], {
|
|
36
27
|
api: options.api,
|
|
37
28
|
body: options.body,
|
|
38
29
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/client/manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAI7D,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,IAAI,CAAC;IACvD,WAAW,IAAI,kBAAkB,CAAC;IAClC,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;IACvD,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAClE;;;;OAIG;IACH,kBAAkB,CAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC,GAAG,SAAS,GACnD,IAAI,CAAC;IACR;;OAEG;IACH,oBAAoB,CAClB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,GACnE,IAAI,CAAC;IACR;;OAEG;IACH,iBAAiB,CACf,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,SAAS,GAC5C,IAAI,CAAC;IACR;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC;;;OAGG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC;IACtE,UAAU,CACR,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE;QACP,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;KAClC,GACA,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC;;;OAGG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,cAAM,aAAc,YAAW,cAAc;IAC3C,OAAO,CAAC,KAAK,CAEX;IAEF,OAAO,CAAC,SAAS,CAAoC;IAGrD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,gBAAgB,CAA8C;IAEtE,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,KAAK,CAAC,CAAS;IACvB,OAAO,CAAC,UAAU,CAA+C;IAEjE,OAAO,CAAC,iBAAiB,CAA6B;IAEtD,OAAO,CAAC,cAAc,CAAoD;IAE1E,OAAO,CAAC,gBAAgB,CAGpB;IAEJ,OAAO,CAAC,aAAa,CAA6C;IAElE,OAAO,CAAC,SAAS,CAA6B;gBAElC,OAAO,GAAE,oBAAyB;
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/client/manager.ts"],"names":[],"mappings":"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"}
|
package/dist/client/manager.js
CHANGED
|
@@ -44,20 +44,13 @@ class StreamManager {
|
|
|
44
44
|
// TTL が設定されている場合、一定間隔で古いセッションを掃除する
|
|
45
45
|
const sweepEnabled = this.ttlMs && this.ttlMs > 0;
|
|
46
46
|
if (sweepEnabled) {
|
|
47
|
-
// サーバーサイドでは
|
|
47
|
+
// サーバーサイドでは sweepは不要
|
|
48
48
|
if (typeof window !== "undefined") {
|
|
49
49
|
// ブラウザ環境でのみ setInterval を使用
|
|
50
50
|
this.sweepTimer = setInterval(() => {
|
|
51
51
|
this.performSweep();
|
|
52
52
|
}, this.ttlMs);
|
|
53
53
|
}
|
|
54
|
-
// サーバーサイドでは初回実行のみ行う
|
|
55
|
-
else {
|
|
56
|
-
// 初回のみ実行してタイマーは設定しない
|
|
57
|
-
setTimeout(() => {
|
|
58
|
-
this.performSweep();
|
|
59
|
-
}, 0);
|
|
60
|
-
}
|
|
61
54
|
}
|
|
62
55
|
}
|
|
63
56
|
// 状態変更を監視するリスナーを登録する
|
|
@@ -189,9 +182,22 @@ class StreamManager {
|
|
|
189
182
|
}
|
|
190
183
|
retainSession(sessionId) {
|
|
191
184
|
const prev = this.activeSubscribers.get(sessionId) ?? 0;
|
|
192
|
-
|
|
185
|
+
const next = prev + 1;
|
|
186
|
+
this.activeSubscribers.set(sessionId, next);
|
|
193
187
|
if (this.debug) {
|
|
194
|
-
console.info(`[SM] retainSession: ${sessionId} -> ${
|
|
188
|
+
console.info(`[SM] retainSession: ${sessionId} -> ${next}`);
|
|
189
|
+
}
|
|
190
|
+
// 修正意図(日本語):
|
|
191
|
+
// - 0→1 に購読者数が増えた直後は、これまで抑制されていたストリーミング更新を
|
|
192
|
+
// 即座に UI に反映させたいケースがある。
|
|
193
|
+
// - そこで、対象セッションが streaming/submitted の場合に限り、即時通知を一度だけ行う。
|
|
194
|
+
// (注意: 現在の useChatSession 実装では retain が subscribe より先に呼ばれるため、
|
|
195
|
+
// この通知は新規購読者より前に発生することがある。将来の呼び出し順や他の購読者に対しては有効。)
|
|
196
|
+
if (next === 1) {
|
|
197
|
+
const s = this.state.sessions.get(sessionId);
|
|
198
|
+
if (s && (s.status === "streaming" || s.status === "submitted")) {
|
|
199
|
+
this.notify(true);
|
|
200
|
+
}
|
|
195
201
|
}
|
|
196
202
|
}
|
|
197
203
|
releaseSession(sessionId) {
|
|
@@ -666,6 +672,15 @@ class StreamManager {
|
|
|
666
672
|
clearInterval(this.sweepTimer);
|
|
667
673
|
this.sweepTimer = null;
|
|
668
674
|
}
|
|
675
|
+
// 追加クリーンアップ(意図):
|
|
676
|
+
// - 参照を明示的に破棄してリークを防止する。
|
|
677
|
+
// - Provider のライフサイクル終了時にのみ実行される想定。
|
|
678
|
+
this.listeners.clear();
|
|
679
|
+
this.activeSubscribers.clear();
|
|
680
|
+
this.latestOnFinish.clear();
|
|
681
|
+
this.latestOnMetadata.clear();
|
|
682
|
+
this.latestOnError.clear();
|
|
683
|
+
this.streamIds.clear();
|
|
669
684
|
}
|
|
670
685
|
}
|
|
671
686
|
// Export the StreamManager class for custom initialization
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useChatSession.d.ts","sourceRoot":"","sources":["../../src/client/useChatSession.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useChatSession.d.ts","sourceRoot":"","sources":["../../src/client/useChatSession.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAK9E,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,qBAA0B,GAClC,oBAAoB,CA2ItB"}
|
|
@@ -1,37 +1,54 @@
|
|
|
1
|
-
import { useEffect, useSyncExternalStore, useCallback,
|
|
1
|
+
import { useEffect, useSyncExternalStore, useCallback, useRef } from "react";
|
|
2
2
|
import { useAIStreamManager } from "./context.js";
|
|
3
3
|
import { generateId } from "../shared/generate-id.js";
|
|
4
4
|
export function useChatSession(sessionId, options = {}) {
|
|
5
5
|
const { api = "/api/chat", initialMessages = [], onMetadata, onError, onFinish, } = options;
|
|
6
6
|
const manager = useAIStreamManager();
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
// セッションIDの変更検知用。初期化時に正しい initialMessages を渡すため、
|
|
8
|
+
// レンダリング中に参照を更新する(副作用ではないので安全)。
|
|
9
|
+
const lastSessionIdRef = useRef(sessionId);
|
|
10
|
+
const initialMessagesRef = useRef(initialMessages);
|
|
11
|
+
if (lastSessionIdRef.current !== sessionId) {
|
|
12
|
+
initialMessagesRef.current = initialMessages;
|
|
13
|
+
lastSessionIdRef.current = sessionId;
|
|
14
|
+
}
|
|
15
|
+
// useSyncExternalStoreでmanagerの状態をReactコンポーネントと同期
|
|
16
|
+
// - 第1引数: 状態変更の監視を開始/停止する関数(subscribe)
|
|
17
|
+
// - 第2引数: 現在の状態を取得する関数(getSnapshot)
|
|
18
|
+
// managerの状態が変更されると自動的に再レンダリングされる
|
|
19
|
+
//
|
|
20
|
+
// - initialMessages の参照がレンダリングごとに変わると、subscribe の依存関係が変化し、
|
|
21
|
+
// 購読の再登録(retain/release)が発生して一時的に subCount=0 となる時間が生まれる。
|
|
22
|
+
// その間のストリーミング更新は manager 側で通知が抑制され、UI が更新されないことがある。
|
|
23
|
+
// - これを避けるため、initialMessages は useRef で固定化し、subscribe の依存配列から外す。
|
|
24
|
+
// - また、initSession の即時通知を確実に受け取れるよう、retain → subscribe → init の順に実行する。
|
|
25
|
+
const state = useSyncExternalStore(useCallback((callback) => {
|
|
26
|
+
// セッション初期化とサブスクリプション管理を同期的に実行
|
|
27
|
+
// - レース回避のため retain を先に行い subCount>0 を担保
|
|
28
|
+
// - initSession の即時通知を受け取るため、subscribe 登録後に init を実行
|
|
9
29
|
manager.retainSession(sessionId);
|
|
10
|
-
manager.
|
|
30
|
+
const unsubscribe = manager.subscribe(callback);
|
|
31
|
+
manager.initSession(sessionId, initialMessagesRef.current);
|
|
11
32
|
return () => {
|
|
12
|
-
//
|
|
33
|
+
// 先に release して購読者数を即時に正しく反映
|
|
13
34
|
manager.releaseSession(sessionId);
|
|
35
|
+
unsubscribe();
|
|
14
36
|
// アンマウント時にハンドラは消さない(完了時に呼ぶため残す)
|
|
15
37
|
};
|
|
16
|
-
}, [sessionId, manager,
|
|
38
|
+
}, [manager, sessionId]), useCallback(() => manager.getSnapshot(), [manager]));
|
|
17
39
|
// ハンドラが変わるたびに最新を登録
|
|
18
40
|
useEffect(() => {
|
|
19
|
-
|
|
20
|
-
|
|
41
|
+
// onFinish が undefined の場合は登録解除される
|
|
42
|
+
manager.setOnFinishHandler(sessionId, onFinish);
|
|
21
43
|
}, [manager, sessionId, onFinish]);
|
|
22
44
|
useEffect(() => {
|
|
23
|
-
|
|
24
|
-
|
|
45
|
+
// onMetadata が undefined の場合は登録解除される
|
|
46
|
+
manager.setOnMetadataHandler(sessionId, onMetadata);
|
|
25
47
|
}, [manager, sessionId, onMetadata]);
|
|
26
48
|
useEffect(() => {
|
|
27
|
-
|
|
28
|
-
|
|
49
|
+
// onError が undefined の場合は登録解除される
|
|
50
|
+
manager.setOnErrorHandler(sessionId, onError);
|
|
29
51
|
}, [manager, sessionId, onError]);
|
|
30
|
-
// useSyncExternalStoreでmanagerの状態をReactコンポーネントと同期
|
|
31
|
-
// - 第1引数: 状態変更の監視を開始/停止する関数(subscribe)
|
|
32
|
-
// - 第2引数: 現在の状態を取得する関数(getSnapshot)
|
|
33
|
-
// managerの状態が変更されると自動的に再レンダリングされる
|
|
34
|
-
const state = useSyncExternalStore(useCallback((callback) => manager.subscribe(callback), [manager]), useCallback(() => manager.getSnapshot(), [manager]));
|
|
35
52
|
const session = state.sessions.get(sessionId) || {
|
|
36
53
|
id: sessionId,
|
|
37
54
|
messages: initialMessages,
|
|
@@ -62,6 +79,11 @@ export function useChatSession(sessionId, options = {}) {
|
|
|
62
79
|
if (lastUserIndex === -1)
|
|
63
80
|
return;
|
|
64
81
|
const messagesUntilLastUser = session.messages.slice(0, lastUserIndex + 1);
|
|
82
|
+
// キャッシュなどを防ぐため、最後のmessageのidを変える
|
|
83
|
+
messagesUntilLastUser[messagesUntilLastUser.length - 1] = {
|
|
84
|
+
...messagesUntilLastUser[lastUserIndex],
|
|
85
|
+
id: generateId({ prefix: "msg-" }),
|
|
86
|
+
};
|
|
65
87
|
await manager.streamChat(sessionId, messagesUntilLastUser, {
|
|
66
88
|
api,
|
|
67
89
|
body: params?.body,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "simple-ai-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Simple AI SDK for Hono / React19+ / OpenAI",
|
|
6
6
|
"type": "module",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"typecheck": "tsc --noEmit",
|
|
54
54
|
"lint": "eslint .",
|
|
55
55
|
"prepublish": "pnpm typecheck && pnpm lint",
|
|
56
|
-
"publish-sdk": "pnpm build && pnpm publish --no-git-checks --access public",
|
|
56
|
+
"publish-sdk": "npm version patch --no-git-tag-version && pnpm build && pnpm publish --no-git-checks --access public",
|
|
57
57
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
58
58
|
}
|
|
59
59
|
}
|