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.
@@ -1,31 +1,17 @@
1
- import type { Message, JSONValue } from "../shared/index.js";
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
- startStream?: boolean;
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,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAG7D,MAAM,MAAM,0BAA0B,GAAG;IACvC,eAAe,EAAE,OAAO,EAAE,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACjC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,sBAAsB,gBAItB,MAAM,WAAW,0BAA0B,UA2B1D"}
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, options.initialMessages);
14
+ manager.initSession(sessionId);
30
15
  // startStreamがtrueの場合、ストリーミングを開始(完了を待たない)
31
- if (options.startStream) {
16
+ if (options.streamMessageContent) {
32
17
  // streamChatを呼び出すが、awaitせずに即座に返す
33
18
  // これによりストリーミングはバックグラウンドで継続される
34
19
  manager
35
- .streamChat(sessionId, options.initialMessages, {
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":"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"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/client/manager.ts"],"names":[],"mappings":"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"}
@@ -44,20 +44,13 @@ class StreamManager {
44
44
  // TTL が設定されている場合、一定間隔で古いセッションを掃除する
45
45
  const sweepEnabled = this.ttlMs && this.ttlMs > 0;
46
46
  if (sweepEnabled) {
47
- // サーバーサイドでは setInterval を使わずに、必要時に手動で sweep を呼ぶ
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
- this.activeSubscribers.set(sessionId, prev + 1);
185
+ const next = prev + 1;
186
+ this.activeSubscribers.set(sessionId, next);
193
187
  if (this.debug) {
194
- console.info(`[SM] retainSession: ${sessionId} -> ${prev + 1}`);
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":"AAMA,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAK9E,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,qBAA0B,GAClC,oBAAoB,CA8GtB"}
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, useLayoutEffect, } from "react";
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
- useLayoutEffect(() => {
8
- // このフックの購読開始を通知(TTL GC から保護)
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.initSession(sessionId, initialMessages);
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, initialMessages]);
38
+ }, [manager, sessionId]), useCallback(() => manager.getSnapshot(), [manager]));
17
39
  // ハンドラが変わるたびに最新を登録
18
40
  useEffect(() => {
19
- if (onFinish)
20
- manager.setOnFinishHandler(sessionId, onFinish);
41
+ // onFinish が undefined の場合は登録解除される
42
+ manager.setOnFinishHandler(sessionId, onFinish);
21
43
  }, [manager, sessionId, onFinish]);
22
44
  useEffect(() => {
23
- if (onMetadata)
24
- manager.setOnMetadataHandler(sessionId, onMetadata);
45
+ // onMetadata が undefined の場合は登録解除される
46
+ manager.setOnMetadataHandler(sessionId, onMetadata);
25
47
  }, [manager, sessionId, onMetadata]);
26
48
  useEffect(() => {
27
- if (onError)
28
- manager.setOnErrorHandler(sessionId, onError);
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.11",
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
  }