@gravity-ui/aikit 2.20.1 → 2.20.2-beta.1b9a51bfdf2818e8caefe25d7de3af232af29ca8.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.
Files changed (33) hide show
  1. package/build/cjs/adapters/openai/helpers/buildFinalMessages.d.ts +1 -5
  2. package/build/cjs/adapters/openai/helpers/buildFinalMessages.js +1 -9
  3. package/build/cjs/adapters/openai/helpers/buildFinalMessages.js.map +1 -1
  4. package/build/cjs/adapters/openai/helpers/consumeOpenAIStream.d.ts +1 -2
  5. package/build/cjs/adapters/openai/helpers/consumeOpenAIStream.js +17 -27
  6. package/build/cjs/adapters/openai/helpers/consumeOpenAIStream.js.map +1 -1
  7. package/build/cjs/adapters/openai/useOpenAIResponsesAdapter.d.ts +0 -9
  8. package/build/cjs/adapters/openai/useOpenAIResponsesAdapter.js +8 -74
  9. package/build/cjs/adapters/openai/useOpenAIResponsesAdapter.js.map +1 -1
  10. package/build/cjs/components/atoms/SubmitButton/SubmitButton.css +1 -0
  11. package/build/cjs/hooks/useMobileControlSize.d.ts +1 -1
  12. package/build/cjs/hooks/useMobileControlSize.js.map +1 -1
  13. package/build/cjs/package.json +1 -1
  14. package/build/esm/adapters/openai/helpers/buildFinalMessages.d.ts +1 -5
  15. package/build/esm/adapters/openai/helpers/buildFinalMessages.js +1 -9
  16. package/build/esm/adapters/openai/helpers/buildFinalMessages.js.map +1 -1
  17. package/build/esm/adapters/openai/helpers/consumeOpenAIStream.d.ts +1 -2
  18. package/build/esm/adapters/openai/helpers/consumeOpenAIStream.js +17 -27
  19. package/build/esm/adapters/openai/helpers/consumeOpenAIStream.js.map +1 -1
  20. package/build/esm/adapters/openai/useOpenAIResponsesAdapter.d.ts +0 -9
  21. package/build/esm/adapters/openai/useOpenAIResponsesAdapter.js +8 -73
  22. package/build/esm/adapters/openai/useOpenAIResponsesAdapter.js.map +1 -1
  23. package/build/esm/components/atoms/SubmitButton/SubmitButton.css +1 -0
  24. package/build/esm/hooks/useMobileControlSize.d.ts +1 -1
  25. package/build/esm/hooks/useMobileControlSize.js.map +1 -1
  26. package/build/esm/package.json +1 -1
  27. package/package.json +2 -2
  28. package/build/cjs/adapters/openai/helpers/isMessageOutputItemDoneEvent.d.ts +0 -3
  29. package/build/cjs/adapters/openai/helpers/isMessageOutputItemDoneEvent.js +0 -22
  30. package/build/cjs/adapters/openai/helpers/isMessageOutputItemDoneEvent.js.map +0 -1
  31. package/build/esm/adapters/openai/helpers/isMessageOutputItemDoneEvent.d.ts +0 -3
  32. package/build/esm/adapters/openai/helpers/isMessageOutputItemDoneEvent.js +0 -19
  33. package/build/esm/adapters/openai/helpers/isMessageOutputItemDoneEvent.js.map +0 -1
@@ -1,10 +1,6 @@
1
- import type { TAssistantMessage, TChatMessage, TMessageContentUnion } from "../../../types/index.js";
1
+ import type { TChatMessage, TMessageContentUnion } from "../../../types/index.js";
2
2
  export type BuildFinalMessagesParams = {
3
3
  baseMessages: TChatMessage[];
4
- completedAssistantMessages: {
5
- id: string;
6
- content: TAssistantMessage['content'];
7
- }[];
8
4
  currentAssistantMessageId: string;
9
5
  contentParts: TMessageContentUnion[];
10
6
  };
@@ -8,17 +8,9 @@ function isMessageContentEmpty(content) {
8
8
  return Array.isArray(content) && content.length === 0;
9
9
  }
10
10
  function buildFinalMessages(params) {
11
- const { baseMessages, completedAssistantMessages, currentAssistantMessageId, contentParts } = params;
11
+ const { baseMessages, currentAssistantMessageId, contentParts } = params;
12
12
  const currentContent = (0, contentPartsToMessageContent_1.contentPartsToMessageContent)(contentParts);
13
- const completedWithContent = completedAssistantMessages.filter((m) => !isMessageContentEmpty(m.content));
14
13
  const result = [...baseMessages];
15
- for (const m of completedWithContent) {
16
- result.push({
17
- id: m.id,
18
- role: 'assistant',
19
- content: m.content,
20
- });
21
- }
22
14
  if (!isMessageContentEmpty(currentContent)) {
23
15
  result.push({
24
16
  id: currentAssistantMessageId,
@@ -1 +1 @@
1
- {"version":3,"file":"buildFinalMessages.js","sourceRoot":"../../../../../src","sources":["adapters/openai/helpers/buildFinalMessages.ts"],"names":[],"mappings":";;AAgBA,gDAuBC;AArCD,oFAA4E;AAE5E,SAAS,qBAAqB,CAAC,OAAqC;IAChE,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;IAC9D,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC;AAC1D,CAAC;AASD,SAAgB,kBAAkB,CAAC,MAAgC;IAC/D,MAAM,EAAC,YAAY,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,YAAY,EAAC,GACrF,MAAM,CAAC;IACX,MAAM,cAAc,GAAG,IAAA,2DAA4B,EAAC,YAAY,CAAC,CAAC;IAClE,MAAM,oBAAoB,GAAG,0BAA0B,CAAC,MAAM,CAC1D,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,OAAO,CAAC,CAC3C,CAAC;IACF,MAAM,MAAM,GAAmB,CAAC,GAAG,YAAY,CAAC,CAAC;IACjD,KAAK,MAAM,CAAC,IAAI,oBAAoB,EAAE,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC;YACR,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,IAAI,EAAE,WAAoB;YAC1B,OAAO,EAAE,CAAC,CAAC,OAAO;SACA,CAAC,CAAC;IAC5B,CAAC;IACD,IAAI,CAAC,qBAAqB,CAAC,cAAc,CAAC,EAAE,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC;YACR,EAAE,EAAE,yBAAyB;YAC7B,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,cAAc;SACL,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC","sourcesContent":["import type {TAssistantMessage, TChatMessage, TMessageContentUnion} from '../../../types';\n\nimport {contentPartsToMessageContent} from './contentPartsToMessageContent';\n\nfunction isMessageContentEmpty(content: TAssistantMessage['content']): boolean {\n if (typeof content === 'string') return content.trim() === '';\n return Array.isArray(content) && content.length === 0;\n}\n\nexport type BuildFinalMessagesParams = {\n baseMessages: TChatMessage[];\n completedAssistantMessages: {id: string; content: TAssistantMessage['content']}[];\n currentAssistantMessageId: string;\n contentParts: TMessageContentUnion[];\n};\n\nexport function buildFinalMessages(params: BuildFinalMessagesParams): TChatMessage[] {\n const {baseMessages, completedAssistantMessages, currentAssistantMessageId, contentParts} =\n params;\n const currentContent = contentPartsToMessageContent(contentParts);\n const completedWithContent = completedAssistantMessages.filter(\n (m) => !isMessageContentEmpty(m.content),\n );\n const result: TChatMessage[] = [...baseMessages];\n for (const m of completedWithContent) {\n result.push({\n id: m.id,\n role: 'assistant' as const,\n content: m.content,\n } as TAssistantMessage);\n }\n if (!isMessageContentEmpty(currentContent)) {\n result.push({\n id: currentAssistantMessageId,\n role: 'assistant',\n content: currentContent,\n } as TAssistantMessage);\n }\n return result;\n}\n"]}
1
+ {"version":3,"file":"buildFinalMessages.js","sourceRoot":"../../../../../src","sources":["adapters/openai/helpers/buildFinalMessages.ts"],"names":[],"mappings":";;AAeA,gDAYC;AAzBD,oFAA4E;AAE5E,SAAS,qBAAqB,CAAC,OAAqC;IAChE,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;IAC9D,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC;AAC1D,CAAC;AAQD,SAAgB,kBAAkB,CAAC,MAAgC;IAC/D,MAAM,EAAC,YAAY,EAAE,yBAAyB,EAAE,YAAY,EAAC,GAAG,MAAM,CAAC;IACvE,MAAM,cAAc,GAAG,IAAA,2DAA4B,EAAC,YAAY,CAAC,CAAC;IAClE,MAAM,MAAM,GAAmB,CAAC,GAAG,YAAY,CAAC,CAAC;IACjD,IAAI,CAAC,qBAAqB,CAAC,cAAc,CAAC,EAAE,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC;YACR,EAAE,EAAE,yBAAyB;YAC7B,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,cAAc;SACL,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC","sourcesContent":["import type {TAssistantMessage, TChatMessage, TMessageContentUnion} from '../../../types';\n\nimport {contentPartsToMessageContent} from './contentPartsToMessageContent';\n\nfunction isMessageContentEmpty(content: TAssistantMessage['content']): boolean {\n if (typeof content === 'string') return content.trim() === '';\n return Array.isArray(content) && content.length === 0;\n}\n\nexport type BuildFinalMessagesParams = {\n baseMessages: TChatMessage[];\n currentAssistantMessageId: string;\n contentParts: TMessageContentUnion[];\n};\n\nexport function buildFinalMessages(params: BuildFinalMessagesParams): TChatMessage[] {\n const {baseMessages, currentAssistantMessageId, contentParts} = params;\n const currentContent = contentPartsToMessageContent(contentParts);\n const result: TChatMessage[] = [...baseMessages];\n if (!isMessageContentEmpty(currentContent)) {\n result.push({\n id: currentAssistantMessageId,\n role: 'assistant',\n content: currentContent,\n } as TAssistantMessage);\n }\n return result;\n}\n"]}
@@ -2,11 +2,10 @@ import type { TAssistantMessage, TChatMessage } from "../../../types/index.js";
2
2
  import type { OpenAIStreamEventLike } from "../types/index.js";
3
3
  export type ConsumeStreamCallbacks = {
4
4
  baseMessages: TChatMessage[];
5
- getAssistantMessageId: (index: number) => string;
5
+ getAssistantMessageId: () => string;
6
6
  /** When the API sends `response.output_item.added` for a message, exposes OpenAI item id (e.g. msg-cc-…) for reactions. */
7
7
  onAssistantMessageIdResolved?: (previousId: string, openaiItemId: string) => void;
8
8
  onContentUpdate: (messageId: string, content: TAssistantMessage['content']) => void;
9
- onNewMessage: (messageId: string) => void;
10
9
  onEnd: (finalMessages: TChatMessage[], status: 'done' | 'error', error?: Error) => void;
11
10
  getIsCancelled: () => boolean;
12
11
  };
@@ -8,7 +8,6 @@ const contentPartsToMessageContent_1 = require("./contentPartsToMessageContent.j
8
8
  const getOpenAIMessageItemIdFromOutputItemAdded_1 = require("./getOpenAIMessageItemIdFromOutputItemAdded.js");
9
9
  const getStreamEndResult_1 = require("./getStreamEndResult.js");
10
10
  const getStreamEventContentUpdate_1 = require("./getStreamEventContentUpdate.js");
11
- const isMessageOutputItemDoneEvent_1 = require("./isMessageOutputItemDoneEvent.js");
12
11
  const isStreamEndOrErrorEvent_1 = require("./isStreamEndOrErrorEvent.js");
13
12
  function waitForNextTask() {
14
13
  return new Promise((resolve) => setTimeout(resolve, 0));
@@ -20,16 +19,27 @@ function shouldYieldAfterUpdate(update) {
20
19
  /** Consumes stream, invokes callbacks. State/setters live in caller. */
21
20
  async function consumeOpenAIStream(stream, callbacks) {
22
21
  var _a, e_1, _b, _c;
23
- const { baseMessages, getAssistantMessageId, onAssistantMessageIdResolved, onContentUpdate, onNewMessage, onEnd, getIsCancelled, } = callbacks;
24
- const completedAssistantMessages = [];
25
- let messageIndex = 0;
26
- let currentAssistantMessageId = getAssistantMessageId(0);
22
+ const { baseMessages, getAssistantMessageId, onAssistantMessageIdResolved, onContentUpdate, onEnd, getIsCancelled, } = callbacks;
23
+ let currentAssistantMessageId = getAssistantMessageId();
24
+ let assistantMessageItemIdResolved = false;
27
25
  let contentParts = [];
28
26
  const applyContentToCurrentMessage = (parts) => {
29
27
  if (getIsCancelled())
30
28
  return;
31
29
  onContentUpdate(currentAssistantMessageId, (0, contentPartsToMessageContent_1.contentPartsToMessageContent)(parts));
32
30
  };
31
+ const resolveAssistantMessageId = (openaiMessageItemId) => {
32
+ if (assistantMessageItemIdResolved)
33
+ return;
34
+ assistantMessageItemIdResolved = true;
35
+ if (openaiMessageItemId === currentAssistantMessageId)
36
+ return;
37
+ const previousId = currentAssistantMessageId;
38
+ currentAssistantMessageId = openaiMessageItemId;
39
+ if (!getIsCancelled()) {
40
+ onAssistantMessageIdResolved === null || onAssistantMessageIdResolved === void 0 ? void 0 : onAssistantMessageIdResolved(previousId, openaiMessageItemId);
41
+ }
42
+ };
33
43
  try {
34
44
  try {
35
45
  for (var _d = true, stream_1 = tslib_1.__asyncValues(stream), stream_1_1; stream_1_1 = await stream_1.next(), _a = stream_1_1.done, !_a; _d = true) {
@@ -39,18 +49,13 @@ async function consumeOpenAIStream(stream, callbacks) {
39
49
  if (getIsCancelled())
40
50
  return;
41
51
  const openaiMessageItemId = (0, getOpenAIMessageItemIdFromOutputItemAdded_1.getOpenAIMessageItemIdFromOutputItemAdded)(event);
42
- if (openaiMessageItemId && openaiMessageItemId !== currentAssistantMessageId) {
43
- const previousId = currentAssistantMessageId;
44
- currentAssistantMessageId = openaiMessageItemId;
45
- if (!getIsCancelled()) {
46
- onAssistantMessageIdResolved === null || onAssistantMessageIdResolved === void 0 ? void 0 : onAssistantMessageIdResolved(previousId, openaiMessageItemId);
47
- }
52
+ if (openaiMessageItemId) {
53
+ resolveAssistantMessageId(openaiMessageItemId);
48
54
  continue;
49
55
  }
50
56
  if ((0, isStreamEndOrErrorEvent_1.isStreamEndOrErrorEvent)(event)) {
51
57
  const finalMessages = (0, buildFinalMessages_1.buildFinalMessages)({
52
58
  baseMessages,
53
- completedAssistantMessages,
54
59
  currentAssistantMessageId,
55
60
  contentParts,
56
61
  });
@@ -60,19 +65,6 @@ async function consumeOpenAIStream(stream, callbacks) {
60
65
  onEnd(finalMessages, endResult.status, endResult.status === 'error' ? endResult.error : undefined);
61
66
  return;
62
67
  }
63
- if ((0, isMessageOutputItemDoneEvent_1.isMessageOutputItemDoneEvent)(event)) {
64
- completedAssistantMessages.push({
65
- id: currentAssistantMessageId,
66
- content: (0, contentPartsToMessageContent_1.contentPartsToMessageContent)(contentParts),
67
- });
68
- messageIndex += 1;
69
- currentAssistantMessageId = getAssistantMessageId(messageIndex);
70
- contentParts = [];
71
- if (getIsCancelled())
72
- return;
73
- onNewMessage(currentAssistantMessageId);
74
- continue;
75
- }
76
68
  const update = (0, getStreamEventContentUpdate_1.getStreamEventContentUpdate)(event);
77
69
  if (!update)
78
70
  continue;
@@ -98,7 +90,6 @@ async function consumeOpenAIStream(stream, callbacks) {
98
90
  if (!getIsCancelled()) {
99
91
  const finalMessages = (0, buildFinalMessages_1.buildFinalMessages)({
100
92
  baseMessages,
101
- completedAssistantMessages,
102
93
  currentAssistantMessageId,
103
94
  contentParts,
104
95
  });
@@ -109,7 +100,6 @@ async function consumeOpenAIStream(stream, callbacks) {
109
100
  if (!getIsCancelled()) {
110
101
  const finalMessages = (0, buildFinalMessages_1.buildFinalMessages)({
111
102
  baseMessages,
112
- completedAssistantMessages,
113
103
  currentAssistantMessageId,
114
104
  contentParts,
115
105
  });
@@ -1 +1 @@
1
- {"version":3,"file":"consumeOpenAIStream.js","sourceRoot":"../../../../../src","sources":["adapters/openai/helpers/consumeOpenAIStream.ts"],"names":[],"mappings":"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","sourcesContent":["import type {TAssistantMessage, TChatMessage, TMessageContentUnion} from '../../../types';\nimport type {OpenAIStreamEventLike} from '../types';\n\nimport {applyContentUpdate} from './applyContentUpdate';\nimport {buildFinalMessages} from './buildFinalMessages';\nimport {contentPartsToMessageContent} from './contentPartsToMessageContent';\nimport {getOpenAIMessageItemIdFromOutputItemAdded} from './getOpenAIMessageItemIdFromOutputItemAdded';\nimport {getStreamEndResult} from './getStreamEndResult';\nimport {getStreamEventContentUpdate} from './getStreamEventContentUpdate';\nimport {isMessageOutputItemDoneEvent} from './isMessageOutputItemDoneEvent';\nimport {isStreamEndOrErrorEvent} from './isStreamEndOrErrorEvent';\n\nfunction waitForNextTask(): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, 0));\n}\n\nfunction shouldYieldAfterUpdate(update: ReturnType<typeof getStreamEventContentUpdate>): boolean {\n return update?.kind === 'tool_add' && (update.status ?? 'loading') === 'loading';\n}\n\nexport type ConsumeStreamCallbacks = {\n baseMessages: TChatMessage[];\n getAssistantMessageId: (index: number) => string;\n /** When the API sends `response.output_item.added` for a message, exposes OpenAI item id (e.g. msg-cc-…) for reactions. */\n onAssistantMessageIdResolved?: (previousId: string, openaiItemId: string) => void;\n onContentUpdate: (messageId: string, content: TAssistantMessage['content']) => void;\n onNewMessage: (messageId: string) => void;\n onEnd: (finalMessages: TChatMessage[], status: 'done' | 'error', error?: Error) => void;\n getIsCancelled: () => boolean;\n};\n\n/** Consumes stream, invokes callbacks. State/setters live in caller. */\n\nexport async function consumeOpenAIStream(\n stream: AsyncIterable<OpenAIStreamEventLike>,\n callbacks: ConsumeStreamCallbacks,\n): Promise<void> {\n const {\n baseMessages,\n getAssistantMessageId,\n onAssistantMessageIdResolved,\n onContentUpdate,\n onNewMessage,\n onEnd,\n getIsCancelled,\n } = callbacks;\n\n const completedAssistantMessages: {id: string; content: TAssistantMessage['content']}[] = [];\n let messageIndex = 0;\n let currentAssistantMessageId = getAssistantMessageId(0);\n let contentParts: TMessageContentUnion[] = [];\n\n const applyContentToCurrentMessage = (parts: TMessageContentUnion[]) => {\n if (getIsCancelled()) return;\n onContentUpdate(currentAssistantMessageId, contentPartsToMessageContent(parts));\n };\n\n try {\n for await (const event of stream) {\n if (getIsCancelled()) return;\n\n const openaiMessageItemId = getOpenAIMessageItemIdFromOutputItemAdded(event);\n if (openaiMessageItemId && openaiMessageItemId !== currentAssistantMessageId) {\n const previousId = currentAssistantMessageId;\n currentAssistantMessageId = openaiMessageItemId;\n if (!getIsCancelled()) {\n onAssistantMessageIdResolved?.(previousId, openaiMessageItemId);\n }\n continue;\n }\n\n if (isStreamEndOrErrorEvent(event)) {\n const finalMessages = buildFinalMessages({\n baseMessages,\n completedAssistantMessages,\n currentAssistantMessageId,\n contentParts,\n });\n const endResult = getStreamEndResult(event);\n if (getIsCancelled()) return;\n onEnd(\n finalMessages,\n endResult.status,\n endResult.status === 'error' ? endResult.error : undefined,\n );\n return;\n }\n\n if (isMessageOutputItemDoneEvent(event)) {\n completedAssistantMessages.push({\n id: currentAssistantMessageId,\n content: contentPartsToMessageContent(contentParts),\n });\n messageIndex += 1;\n currentAssistantMessageId = getAssistantMessageId(messageIndex);\n contentParts = [];\n if (getIsCancelled()) return;\n onNewMessage(currentAssistantMessageId);\n continue;\n }\n\n const update = getStreamEventContentUpdate(event);\n if (!update) continue;\n\n const nextParts = applyContentUpdate(contentParts, update);\n if (nextParts !== null) {\n contentParts = nextParts;\n applyContentToCurrentMessage(contentParts);\n if (shouldYieldAfterUpdate(update)) {\n await waitForNextTask();\n if (getIsCancelled()) return;\n }\n }\n }\n\n if (!getIsCancelled()) {\n const finalMessages = buildFinalMessages({\n baseMessages,\n completedAssistantMessages,\n currentAssistantMessageId,\n contentParts,\n });\n onEnd(finalMessages, 'done');\n }\n } catch (err) {\n if (!getIsCancelled()) {\n const finalMessages = buildFinalMessages({\n baseMessages,\n completedAssistantMessages,\n currentAssistantMessageId,\n contentParts,\n });\n onEnd(finalMessages, 'error', err instanceof Error ? err : new Error(String(err)));\n }\n }\n}\n"]}
1
+ {"version":3,"file":"consumeOpenAIStream.js","sourceRoot":"../../../../../src","sources":["adapters/openai/helpers/consumeOpenAIStream.ts"],"names":[],"mappings":"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","sourcesContent":["import type {TAssistantMessage, TChatMessage, TMessageContentUnion} from '../../../types';\nimport type {OpenAIStreamEventLike} from '../types';\n\nimport {applyContentUpdate} from './applyContentUpdate';\nimport {buildFinalMessages} from './buildFinalMessages';\nimport {contentPartsToMessageContent} from './contentPartsToMessageContent';\nimport {getOpenAIMessageItemIdFromOutputItemAdded} from './getOpenAIMessageItemIdFromOutputItemAdded';\nimport {getStreamEndResult} from './getStreamEndResult';\nimport {getStreamEventContentUpdate} from './getStreamEventContentUpdate';\nimport {isStreamEndOrErrorEvent} from './isStreamEndOrErrorEvent';\n\nfunction waitForNextTask(): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, 0));\n}\n\nfunction shouldYieldAfterUpdate(update: ReturnType<typeof getStreamEventContentUpdate>): boolean {\n return update?.kind === 'tool_add' && (update.status ?? 'loading') === 'loading';\n}\n\nexport type ConsumeStreamCallbacks = {\n baseMessages: TChatMessage[];\n getAssistantMessageId: () => string;\n /** When the API sends `response.output_item.added` for a message, exposes OpenAI item id (e.g. msg-cc-…) for reactions. */\n onAssistantMessageIdResolved?: (previousId: string, openaiItemId: string) => void;\n onContentUpdate: (messageId: string, content: TAssistantMessage['content']) => void;\n onEnd: (finalMessages: TChatMessage[], status: 'done' | 'error', error?: Error) => void;\n getIsCancelled: () => boolean;\n};\n\n/** Consumes stream, invokes callbacks. State/setters live in caller. */\n\nexport async function consumeOpenAIStream(\n stream: AsyncIterable<OpenAIStreamEventLike>,\n callbacks: ConsumeStreamCallbacks,\n): Promise<void> {\n const {\n baseMessages,\n getAssistantMessageId,\n onAssistantMessageIdResolved,\n onContentUpdate,\n onEnd,\n getIsCancelled,\n } = callbacks;\n\n let currentAssistantMessageId = getAssistantMessageId();\n let assistantMessageItemIdResolved = false;\n let contentParts: TMessageContentUnion[] = [];\n\n const applyContentToCurrentMessage = (parts: TMessageContentUnion[]) => {\n if (getIsCancelled()) return;\n onContentUpdate(currentAssistantMessageId, contentPartsToMessageContent(parts));\n };\n\n const resolveAssistantMessageId = (openaiMessageItemId: string) => {\n if (assistantMessageItemIdResolved) return;\n assistantMessageItemIdResolved = true;\n if (openaiMessageItemId === currentAssistantMessageId) return;\n\n const previousId = currentAssistantMessageId;\n currentAssistantMessageId = openaiMessageItemId;\n if (!getIsCancelled()) {\n onAssistantMessageIdResolved?.(previousId, openaiMessageItemId);\n }\n };\n\n try {\n for await (const event of stream) {\n if (getIsCancelled()) return;\n\n const openaiMessageItemId = getOpenAIMessageItemIdFromOutputItemAdded(event);\n if (openaiMessageItemId) {\n resolveAssistantMessageId(openaiMessageItemId);\n continue;\n }\n\n if (isStreamEndOrErrorEvent(event)) {\n const finalMessages = buildFinalMessages({\n baseMessages,\n currentAssistantMessageId,\n contentParts,\n });\n const endResult = getStreamEndResult(event);\n if (getIsCancelled()) return;\n onEnd(\n finalMessages,\n endResult.status,\n endResult.status === 'error' ? endResult.error : undefined,\n );\n return;\n }\n\n const update = getStreamEventContentUpdate(event);\n if (!update) continue;\n\n const nextParts = applyContentUpdate(contentParts, update);\n if (nextParts !== null) {\n contentParts = nextParts;\n applyContentToCurrentMessage(contentParts);\n if (shouldYieldAfterUpdate(update)) {\n await waitForNextTask();\n if (getIsCancelled()) return;\n }\n }\n }\n\n if (!getIsCancelled()) {\n const finalMessages = buildFinalMessages({\n baseMessages,\n currentAssistantMessageId,\n contentParts,\n });\n onEnd(finalMessages, 'done');\n }\n } catch (err) {\n if (!getIsCancelled()) {\n const finalMessages = buildFinalMessages({\n baseMessages,\n currentAssistantMessageId,\n contentParts,\n });\n onEnd(finalMessages, 'error', err instanceof Error ? err : new Error(String(err)));\n }\n }\n}\n"]}
@@ -2,14 +2,5 @@ import type { TChatMessage } from "../../types/index.js";
2
2
  import type { OpenAIStreamAdapterOptions, OpenAIStreamAdapterResult, OpenAIStreamSource } from "./types/index.js";
3
3
  import { OpenAIResponseLike } from "./types/openAiTypes.js";
4
4
  export type { OpenAIStreamAdapterOptions, OpenAIStreamAdapterResult, OpenAIStreamAdapterStatus, OpenAIStreamSource, } from "./types/index.js";
5
- /**
6
- * When the stream splits the reply into two consecutive assistant messages, the same tool call can
7
- * appear in both. Moves matching tool parts into the earlier segment and drops the duplicate from
8
- * the later one so the UI does not show orphaned repeats.
9
- *
10
- * @param messages - Chat messages (possibly with duplicate tool parts across adjacent assistants).
11
- * @returns A shallow-copied message list with orphan duplicates collapsed.
12
- */
13
- export declare function collapseOrphanToolBetweenAssistantSegments(messages: TChatMessage[]): TChatMessage[];
14
5
  export declare function useOpenAIResponsesAdapter(response: OpenAIResponseLike | null): TChatMessage[];
15
6
  export declare function useOpenAIStreamAdapter(stream: OpenAIStreamSource | null, options?: OpenAIStreamAdapterOptions): OpenAIStreamAdapterResult;
@@ -1,70 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.collapseOrphanToolBetweenAssistantSegments = collapseOrphanToolBetweenAssistantSegments;
4
3
  exports.useOpenAIResponsesAdapter = useOpenAIResponsesAdapter;
5
4
  exports.useOpenAIStreamAdapter = useOpenAIStreamAdapter;
6
5
  const tslib_1 = require("tslib");
7
6
  const react_1 = require("react");
8
7
  const consumeOpenAIStream_1 = require("./helpers/consumeOpenAIStream.js");
9
- const contentPartsToMessageContent_1 = require("./helpers/contentPartsToMessageContent.js");
10
8
  const fetchResponseToStreamEvents_1 = require("./helpers/fetchResponseToStreamEvents.js");
11
9
  const isFetchResponse_1 = require("./helpers/isFetchResponse.js");
12
10
  const openAIResponseToMessages_1 = require("./helpers/openAIResponseToMessages.js");
13
11
  /** Single non-streaming response → TChatMessage[]. For streaming use useOpenAIStreamAdapter. */
14
- function isToolPart(p) {
15
- return p.type === 'tool';
16
- }
17
- /**
18
- * When the stream splits the reply into two consecutive assistant messages, the same tool call can
19
- * appear in both. Moves matching tool parts into the earlier segment and drops the duplicate from
20
- * the later one so the UI does not show orphaned repeats.
21
- *
22
- * @param messages - Chat messages (possibly with duplicate tool parts across adjacent assistants).
23
- * @returns A shallow-copied message list with orphan duplicates collapsed.
24
- */
25
- function collapseOrphanToolBetweenAssistantSegments(messages) {
26
- const result = messages.map((m) => m.role === 'assistant' ? Object.assign({}, m) : m);
27
- for (let i = result.length - 1; i >= 1; i--) {
28
- const next = result[i];
29
- const prev = result[i - 1];
30
- if (prev.role !== 'assistant' || next.role !== 'assistant') {
31
- continue;
32
- }
33
- const nextContent = next.content;
34
- if (!Array.isArray(nextContent)) {
35
- continue;
36
- }
37
- const prevContent = prev.content;
38
- if (!Array.isArray(prevContent)) {
39
- continue;
40
- }
41
- const updatedNextParts = [...nextContent];
42
- let updatedPrevParts = [...prevContent];
43
- let changed = false;
44
- for (let t = updatedNextParts.length - 1; t >= 0; t--) {
45
- const part = updatedNextParts[t];
46
- if (!isToolPart(part) || !part.id) {
47
- continue;
48
- }
49
- const pIdx = updatedPrevParts.findIndex((p) => isToolPart(p) && p.id === part.id);
50
- if (pIdx < 0) {
51
- continue;
52
- }
53
- updatedPrevParts = updatedPrevParts.map((p, j) => (j === pIdx ? part : p));
54
- updatedNextParts.splice(t, 1);
55
- changed = true;
56
- }
57
- if (!changed) {
58
- continue;
59
- }
60
- result[i - 1] = Object.assign(Object.assign({}, prev), { content: (0, contentPartsToMessageContent_1.contentPartsToMessageContent)(updatedPrevParts) });
61
- result[i] = Object.assign(Object.assign({}, next), { content: (0, contentPartsToMessageContent_1.contentPartsToMessageContent)(updatedNextParts) });
62
- }
63
- return result;
64
- }
65
12
  /**
66
13
  * Index of the assistant message that should receive streamed content for `messageId`.
67
- * Uses an exact id match when present; otherwise the last assistant (current streaming segment),
14
+ * Uses an exact id match when present; otherwise the last assistant (current streaming message),
68
15
  * which covers events that reference an id before our list has caught up.
69
16
  *
70
17
  * @param messages - Current chat messages while streaming.
@@ -166,11 +113,11 @@ function useOpenAIStreamAdapter(stream, options = {}) {
166
113
  },
167
114
  };
168
115
  const baseMessages = initialMessagesRef.current;
169
- const getAssistantMessageId = (index) => index === 0 ? assistantMessageId : `${assistantMessageId}-${index}`;
116
+ const getAssistantMessageId = () => assistantMessageId;
170
117
  setMessages([
171
118
  ...baseMessages,
172
119
  {
173
- id: getAssistantMessageId(0),
120
+ id: getAssistantMessageId(),
174
121
  role: 'assistant',
175
122
  content: '',
176
123
  },
@@ -192,12 +139,12 @@ function useOpenAIStreamAdapter(stream, options = {}) {
192
139
  onAssistantMessageIdResolved: (previousId, openaiItemId) => {
193
140
  if (cancelled)
194
141
  return;
195
- setMessages((prev) => collapseOrphanToolBetweenAssistantSegments(prev.map((msg) => {
142
+ setMessages((prev) => prev.map((msg) => {
196
143
  if (msg.id === previousId && msg.role === 'assistant') {
197
144
  return Object.assign(Object.assign({}, msg), { id: openaiItemId });
198
145
  }
199
146
  return msg;
200
- })));
147
+ }));
201
148
  },
202
149
  onContentUpdate: (messageId, content) => {
203
150
  if (cancelled)
@@ -205,34 +152,21 @@ function useOpenAIStreamAdapter(stream, options = {}) {
205
152
  setMessages((prev) => {
206
153
  const targetIndex = resolveStreamContentTargetIndex(prev, messageId);
207
154
  if (targetIndex < 0) {
208
- return collapseOrphanToolBetweenAssistantSegments(prev);
155
+ return prev;
209
156
  }
210
- const mapped = prev.map((msg, i) => {
157
+ return prev.map((msg, i) => {
211
158
  if (i !== targetIndex) {
212
159
  return msg;
213
160
  }
214
161
  return Object.assign(Object.assign({}, msg), { content });
215
162
  });
216
- return collapseOrphanToolBetweenAssistantSegments(mapped);
217
163
  });
218
164
  },
219
- onNewMessage: (messageId) => {
220
- if (cancelled)
221
- return;
222
- setMessages((prev) => [
223
- ...prev,
224
- {
225
- id: messageId,
226
- role: 'assistant',
227
- content: '',
228
- },
229
- ]);
230
- },
231
165
  onEnd: (finalMessages, s, err) => {
232
166
  var _a;
233
167
  setStatus(s === 'done' ? 'ready' : s);
234
168
  setError(err !== null && err !== void 0 ? err : null);
235
- const normalized = injectUsageMetadata(collapseOrphanToolBetweenAssistantSegments(finalMessages));
169
+ const normalized = injectUsageMetadata(finalMessages);
236
170
  if (!cancelled) {
237
171
  setMessages(normalized);
238
172
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useOpenAIResponsesAdapter.js","sourceRoot":"../../../../src","sources":["adapters/openai/useOpenAIResponsesAdapter.ts"],"names":[],"mappings":"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","sourcesContent":["import {useEffect, useMemo, useRef, useState} from 'react';\n\nimport type {TAssistantMessage, TChatMessage, TMessageContentUnion} from '../../types';\n\nimport {type ConsumeStreamCallbacks, consumeOpenAIStream} from './helpers/consumeOpenAIStream';\nimport {contentPartsToMessageContent} from './helpers/contentPartsToMessageContent';\nimport {fetchResponseToStreamEvents} from './helpers/fetchResponseToStreamEvents';\nimport {isFetchResponse} from './helpers/isFetchResponse';\nimport {openAIResponseToMessages} from './helpers/openAIResponseToMessages';\nimport type {\n OpenAIStreamAdapterOptions,\n OpenAIStreamAdapterResult,\n OpenAIStreamSource,\n} from './types';\nimport {OpenAIResponseLike, OpenAIStreamEventLike} from './types/openAiTypes';\n\nexport type {\n OpenAIStreamAdapterOptions,\n OpenAIStreamAdapterResult,\n OpenAIStreamAdapterStatus,\n OpenAIStreamSource,\n} from './types';\n\n/** Single non-streaming response → TChatMessage[]. For streaming use useOpenAIStreamAdapter. */\n\nfunction isToolPart(p: TMessageContentUnion): p is Extract<TMessageContentUnion, {type: 'tool'}> {\n return p.type === 'tool';\n}\n\n/**\n * When the stream splits the reply into two consecutive assistant messages, the same tool call can\n * appear in both. Moves matching tool parts into the earlier segment and drops the duplicate from\n * the later one so the UI does not show orphaned repeats.\n *\n * @param messages - Chat messages (possibly with duplicate tool parts across adjacent assistants).\n * @returns A shallow-copied message list with orphan duplicates collapsed.\n */\nexport function collapseOrphanToolBetweenAssistantSegments(\n messages: TChatMessage[],\n): TChatMessage[] {\n const result = messages.map((m) =>\n m.role === 'assistant' ? ({...m} as TAssistantMessage) : m,\n );\n\n for (let i = result.length - 1; i >= 1; i--) {\n const next = result[i] as TAssistantMessage;\n const prev = result[i - 1] as TAssistantMessage;\n if (prev.role !== 'assistant' || next.role !== 'assistant') {\n continue;\n }\n\n const nextContent = next.content;\n if (!Array.isArray(nextContent)) {\n continue;\n }\n\n const prevContent = prev.content;\n if (!Array.isArray(prevContent)) {\n continue;\n }\n\n const updatedNextParts = [...nextContent] as TMessageContentUnion[];\n let updatedPrevParts = [...prevContent] as TMessageContentUnion[];\n let changed = false;\n\n for (let t = updatedNextParts.length - 1; t >= 0; t--) {\n const part = updatedNextParts[t];\n if (!isToolPart(part) || !part.id) {\n continue;\n }\n const pIdx = updatedPrevParts.findIndex((p) => isToolPart(p) && p.id === part.id);\n if (pIdx < 0) {\n continue;\n }\n updatedPrevParts = updatedPrevParts.map((p, j) => (j === pIdx ? part : p));\n updatedNextParts.splice(t, 1);\n changed = true;\n }\n\n if (!changed) {\n continue;\n }\n\n result[i - 1] = {\n ...prev,\n content: contentPartsToMessageContent(updatedPrevParts),\n };\n result[i] = {\n ...next,\n content: contentPartsToMessageContent(updatedNextParts),\n };\n }\n\n return result;\n}\n\n/**\n * Index of the assistant message that should receive streamed content for `messageId`.\n * Uses an exact id match when present; otherwise the last assistant (current streaming segment),\n * which covers events that reference an id before our list has caught up.\n *\n * @param messages - Current chat messages while streaming.\n * @param messageId - Target assistant id from the stream event.\n * @returns Index of the assistant to update, or `-1` if there is no assistant.\n */\nfunction resolveStreamContentTargetIndex(messages: TChatMessage[], messageId: string): number {\n const byId = messages.findIndex((m) => m.id === messageId && m.role === 'assistant');\n if (byId >= 0) {\n return byId;\n }\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === 'assistant') {\n return i;\n }\n }\n return -1;\n}\n\nfunction getResponseIdFromEvent(event: OpenAIStreamEventLike): string | null {\n const response = (event as {response?: unknown}).response;\n if (!response || typeof response !== 'object') {\n return null;\n }\n\n const id = (response as {id?: unknown}).id;\n return typeof id === 'string' ? id : null;\n}\n\nexport function useOpenAIResponsesAdapter(response: OpenAIResponseLike | null): TChatMessage[] {\n return useMemo(() => openAIResponseToMessages(response), [response]);\n}\n\nexport function useOpenAIStreamAdapter(\n stream: OpenAIStreamSource | null,\n options: OpenAIStreamAdapterOptions = {},\n): OpenAIStreamAdapterResult {\n const {\n initialMessages = [],\n assistantMessageId: optionId,\n onStreamEnd,\n trackTokenUsage = false,\n } = options;\n const onStreamEndRef = useRef(onStreamEnd);\n onStreamEndRef.current = onStreamEnd;\n const initialMessagesRef = useRef(initialMessages);\n initialMessagesRef.current = initialMessages;\n\n // messageId → output_tokens from response.completed event\n const usageMapRef = useRef<Map<string, number>>(new Map());\n\n const [messages, setMessages] = useState<TChatMessage[]>(initialMessages);\n const [status, setStatus] = useState<OpenAIStreamAdapterResult['status']>('ready');\n const [error, setError] = useState<Error | null>(null);\n const [responseId, setResponseId] = useState<string | null>(null);\n\n const assistantMessageId = useMemo(() => optionId ?? `assistant-${Date.now()}`, [optionId]);\n\n useEffect(() => {\n if (!stream) {\n setStatus('ready');\n setError(null);\n setResponseId(null);\n return undefined;\n }\n\n const sourceStream: AsyncIterable<OpenAIStreamEventLike> = isFetchResponse(stream)\n ? fetchResponseToStreamEvents(stream)\n : stream;\n\n let cancelled = false;\n setStatus('streaming');\n setError(null);\n setResponseId(null);\n usageMapRef.current = new Map();\n\n const streamToConsume: AsyncIterable<OpenAIStreamEventLike> = {\n async *[Symbol.asyncIterator]() {\n for await (const event of sourceStream) {\n const nextResponseId = getResponseIdFromEvent(event);\n if (!cancelled && nextResponseId) {\n setResponseId((prev) => (prev === nextResponseId ? prev : nextResponseId));\n }\n\n // Extract token usage from response.completed (only when opt-in)\n if (trackTokenUsage) {\n const e = event as Record<string, unknown>;\n const eventType =\n (e.type as string | undefined) ?? (e.event as string | undefined);\n if (eventType === 'response.completed' && e.response) {\n const response = e.response as {\n usage?: {output_tokens?: number};\n output?: Array<{id?: string; type?: string}>;\n };\n const tokens = response.usage?.output_tokens ?? 0;\n if (tokens > 0) {\n (response.output ?? []).forEach(({id, type}) => {\n if (id && type === 'message') {\n usageMapRef.current.set(id, tokens);\n }\n });\n }\n }\n }\n\n yield event;\n }\n },\n };\n\n const baseMessages = initialMessagesRef.current;\n const getAssistantMessageId = (index: number) =>\n index === 0 ? assistantMessageId : `${assistantMessageId}-${index}`;\n\n setMessages([\n ...baseMessages,\n {\n id: getAssistantMessageId(0),\n role: 'assistant',\n content: '',\n } as TAssistantMessage,\n ]);\n\n const injectUsageMetadata = (msgs: TChatMessage[]): TChatMessage[] => {\n if (usageMapRef.current.size === 0) return msgs;\n return msgs.map((msg) => {\n if (msg.role === 'assistant' && msg.id && usageMapRef.current.has(msg.id)) {\n return {\n ...msg,\n metadata: {\n ...msg.metadata,\n outputTokens: usageMapRef.current.get(msg.id),\n },\n } as TAssistantMessage;\n }\n return msg;\n });\n };\n\n const callbacks: ConsumeStreamCallbacks = {\n baseMessages,\n getAssistantMessageId,\n // Provisional id from getAssistantMessageId → OpenAI item id once the stream provides it.\n onAssistantMessageIdResolved: (previousId, openaiItemId) => {\n if (cancelled) return;\n setMessages((prev) =>\n collapseOrphanToolBetweenAssistantSegments(\n prev.map((msg): TChatMessage => {\n if (msg.id === previousId && msg.role === 'assistant') {\n return {...msg, id: openaiItemId} as TAssistantMessage;\n }\n return msg;\n }),\n ),\n );\n },\n onContentUpdate: (messageId, content) => {\n if (cancelled) return;\n setMessages((prev) => {\n const targetIndex = resolveStreamContentTargetIndex(prev, messageId);\n if (targetIndex < 0) {\n return collapseOrphanToolBetweenAssistantSegments(prev);\n }\n const mapped = prev.map((msg, i) => {\n if (i !== targetIndex) {\n return msg;\n }\n return {...msg, content} as TAssistantMessage;\n });\n return collapseOrphanToolBetweenAssistantSegments(mapped);\n });\n },\n onNewMessage: (messageId) => {\n if (cancelled) return;\n setMessages((prev) => [\n ...prev,\n {\n id: messageId,\n role: 'assistant',\n content: '',\n } as TAssistantMessage,\n ]);\n },\n onEnd: (finalMessages, s, err) => {\n setStatus(s === 'done' ? 'ready' : s);\n setError(err ?? null);\n const normalized = injectUsageMetadata(\n collapseOrphanToolBetweenAssistantSegments(finalMessages),\n );\n if (!cancelled) {\n setMessages(normalized);\n }\n onStreamEndRef.current?.(normalized);\n },\n getIsCancelled: () => cancelled,\n };\n\n consumeOpenAIStream(streamToConsume, callbacks);\n\n return () => {\n cancelled = true;\n };\n }, [stream, assistantMessageId]);\n\n return {messages, status, error, responseId};\n}\n"]}
1
+ {"version":3,"file":"useOpenAIResponsesAdapter.js","sourceRoot":"../../../../src","sources":["adapters/openai/useOpenAIResponsesAdapter.ts"],"names":[],"mappings":"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","sourcesContent":["import {useEffect, useMemo, useRef, useState} from 'react';\n\nimport type {TAssistantMessage, TChatMessage} from '../../types';\n\nimport {type ConsumeStreamCallbacks, consumeOpenAIStream} from './helpers/consumeOpenAIStream';\nimport {fetchResponseToStreamEvents} from './helpers/fetchResponseToStreamEvents';\nimport {isFetchResponse} from './helpers/isFetchResponse';\nimport {openAIResponseToMessages} from './helpers/openAIResponseToMessages';\nimport type {\n OpenAIStreamAdapterOptions,\n OpenAIStreamAdapterResult,\n OpenAIStreamSource,\n} from './types';\nimport {OpenAIResponseLike, OpenAIStreamEventLike} from './types/openAiTypes';\n\nexport type {\n OpenAIStreamAdapterOptions,\n OpenAIStreamAdapterResult,\n OpenAIStreamAdapterStatus,\n OpenAIStreamSource,\n} from './types';\n\n/** Single non-streaming response → TChatMessage[]. For streaming use useOpenAIStreamAdapter. */\n\n/**\n * Index of the assistant message that should receive streamed content for `messageId`.\n * Uses an exact id match when present; otherwise the last assistant (current streaming message),\n * which covers events that reference an id before our list has caught up.\n *\n * @param messages - Current chat messages while streaming.\n * @param messageId - Target assistant id from the stream event.\n * @returns Index of the assistant to update, or `-1` if there is no assistant.\n */\nfunction resolveStreamContentTargetIndex(messages: TChatMessage[], messageId: string): number {\n const byId = messages.findIndex((m) => m.id === messageId && m.role === 'assistant');\n if (byId >= 0) {\n return byId;\n }\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === 'assistant') {\n return i;\n }\n }\n return -1;\n}\n\nfunction getResponseIdFromEvent(event: OpenAIStreamEventLike): string | null {\n const response = (event as {response?: unknown}).response;\n if (!response || typeof response !== 'object') {\n return null;\n }\n\n const id = (response as {id?: unknown}).id;\n return typeof id === 'string' ? id : null;\n}\n\nexport function useOpenAIResponsesAdapter(response: OpenAIResponseLike | null): TChatMessage[] {\n return useMemo(() => openAIResponseToMessages(response), [response]);\n}\n\nexport function useOpenAIStreamAdapter(\n stream: OpenAIStreamSource | null,\n options: OpenAIStreamAdapterOptions = {},\n): OpenAIStreamAdapterResult {\n const {\n initialMessages = [],\n assistantMessageId: optionId,\n onStreamEnd,\n trackTokenUsage = false,\n } = options;\n const onStreamEndRef = useRef(onStreamEnd);\n onStreamEndRef.current = onStreamEnd;\n const initialMessagesRef = useRef(initialMessages);\n initialMessagesRef.current = initialMessages;\n\n // messageId → output_tokens from response.completed event\n const usageMapRef = useRef<Map<string, number>>(new Map());\n\n const [messages, setMessages] = useState<TChatMessage[]>(initialMessages);\n const [status, setStatus] = useState<OpenAIStreamAdapterResult['status']>('ready');\n const [error, setError] = useState<Error | null>(null);\n const [responseId, setResponseId] = useState<string | null>(null);\n\n const assistantMessageId = useMemo(() => optionId ?? `assistant-${Date.now()}`, [optionId]);\n\n useEffect(() => {\n if (!stream) {\n setStatus('ready');\n setError(null);\n setResponseId(null);\n return undefined;\n }\n\n const sourceStream: AsyncIterable<OpenAIStreamEventLike> = isFetchResponse(stream)\n ? fetchResponseToStreamEvents(stream)\n : stream;\n\n let cancelled = false;\n setStatus('streaming');\n setError(null);\n setResponseId(null);\n usageMapRef.current = new Map();\n\n const streamToConsume: AsyncIterable<OpenAIStreamEventLike> = {\n async *[Symbol.asyncIterator]() {\n for await (const event of sourceStream) {\n const nextResponseId = getResponseIdFromEvent(event);\n if (!cancelled && nextResponseId) {\n setResponseId((prev) => (prev === nextResponseId ? prev : nextResponseId));\n }\n\n // Extract token usage from response.completed (only when opt-in)\n if (trackTokenUsage) {\n const e = event as Record<string, unknown>;\n const eventType =\n (e.type as string | undefined) ?? (e.event as string | undefined);\n if (eventType === 'response.completed' && e.response) {\n const response = e.response as {\n usage?: {output_tokens?: number};\n output?: Array<{id?: string; type?: string}>;\n };\n const tokens = response.usage?.output_tokens ?? 0;\n if (tokens > 0) {\n (response.output ?? []).forEach(({id, type}) => {\n if (id && type === 'message') {\n usageMapRef.current.set(id, tokens);\n }\n });\n }\n }\n }\n\n yield event;\n }\n },\n };\n\n const baseMessages = initialMessagesRef.current;\n const getAssistantMessageId = () => assistantMessageId;\n\n setMessages([\n ...baseMessages,\n {\n id: getAssistantMessageId(),\n role: 'assistant',\n content: '',\n } as TAssistantMessage,\n ]);\n\n const injectUsageMetadata = (msgs: TChatMessage[]): TChatMessage[] => {\n if (usageMapRef.current.size === 0) return msgs;\n return msgs.map((msg) => {\n if (msg.role === 'assistant' && msg.id && usageMapRef.current.has(msg.id)) {\n return {\n ...msg,\n metadata: {\n ...msg.metadata,\n outputTokens: usageMapRef.current.get(msg.id),\n },\n } as TAssistantMessage;\n }\n return msg;\n });\n };\n\n const callbacks: ConsumeStreamCallbacks = {\n baseMessages,\n getAssistantMessageId,\n // Provisional id from getAssistantMessageId → OpenAI item id once the stream provides it.\n onAssistantMessageIdResolved: (previousId, openaiItemId) => {\n if (cancelled) return;\n setMessages((prev) =>\n prev.map((msg): TChatMessage => {\n if (msg.id === previousId && msg.role === 'assistant') {\n return {...msg, id: openaiItemId} as TAssistantMessage;\n }\n return msg;\n }),\n );\n },\n onContentUpdate: (messageId, content) => {\n if (cancelled) return;\n setMessages((prev) => {\n const targetIndex = resolveStreamContentTargetIndex(prev, messageId);\n if (targetIndex < 0) {\n return prev;\n }\n return prev.map((msg, i) => {\n if (i !== targetIndex) {\n return msg;\n }\n return {...msg, content} as TAssistantMessage;\n });\n });\n },\n onEnd: (finalMessages, s, err) => {\n setStatus(s === 'done' ? 'ready' : s);\n setError(err ?? null);\n const normalized = injectUsageMetadata(finalMessages);\n if (!cancelled) {\n setMessages(normalized);\n }\n onStreamEndRef.current?.(normalized);\n },\n getIsCancelled: () => cancelled,\n };\n\n consumeOpenAIStream(streamToConsume, callbacks);\n\n return () => {\n cancelled = true;\n };\n }, [stream, assistantMessageId]);\n\n return {messages, status, error, responseId};\n}\n"]}
@@ -12,4 +12,5 @@
12
12
  }
13
13
  .g-aikit-submit-button__spinner {
14
14
  --g-color-line-brand: var(--g-color-text-brand-contrast);
15
+ --g-spin-color: var(--g-color-text-brand-contrast);
15
16
  }
@@ -1,4 +1,4 @@
1
- export type ControlSize = 'xs' | 's' | 'm' | 'l' | 'xl';
1
+ export type ControlSize = 'xxs' | 'xs' | 's' | 'm' | 'l' | 'xl';
2
2
  /**
3
3
  * Resolves a control size: an explicit size always wins, otherwise the mobile
4
4
  * default is used in mobile mode and the desktop default elsewhere.
@@ -1 +1 @@
1
- {"version":3,"file":"useMobileControlSize.js","sourceRoot":"../../../src","sources":["hooks/useMobileControlSize.ts"],"names":[],"mappings":";;AAQA,4DASC;AAKD,oDAQC;AAKD,gDAEC;AArCD,6CAA4C;AAI5C;;;GAGG;AACH,SAAgB,wBAAwB,CAAwB,MAK/D;IACG,MAAM,EAAC,IAAI,EAAE,cAAc,EAAE,aAAa,EAAE,QAAQ,EAAC,GAAG,MAAM,CAAC;IAE/D,OAAO,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;AAC/D,CAAC;AAED;;GAEG;AACH,SAAgB,oBAAoB,CAChC,IAAmB,EACnB,cAAiB,EACjB,aAAgB;IAEhB,MAAM,QAAQ,GAAG,IAAA,iBAAS,GAAE,CAAC;IAE7B,OAAO,wBAAwB,CAAC,EAAC,IAAI,EAAE,cAAc,EAAE,aAAa,EAAE,QAAQ,EAAC,CAAC,CAAC;AACrF,CAAC;AAED;;GAEG;AACH,SAAgB,kBAAkB,CAAC,IAA6B;IAC5D,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACnC,CAAC","sourcesContent":["import {useMobile} from '@gravity-ui/uikit';\n\nexport type ControlSize = 'xs' | 's' | 'm' | 'l' | 'xl';\n\n/**\n * Resolves a control size: an explicit size always wins, otherwise the mobile\n * default is used in mobile mode and the desktop default elsewhere.\n */\nexport function resolveMobileControlSize<T extends ControlSize>(params: {\n size: T | undefined;\n desktopDefault: T;\n mobileDefault: T;\n isMobile: boolean;\n}): T {\n const {size, desktopDefault, mobileDefault, isMobile} = params;\n\n return size ?? (isMobile ? mobileDefault : desktopDefault);\n}\n\n/**\n * Mobile-aware control size, driven by the uikit `MobileProvider` context.\n */\nexport function useMobileControlSize<T extends ControlSize>(\n size: T | undefined,\n desktopDefault: T,\n mobileDefault: T,\n): T {\n const isMobile = useMobile();\n\n return resolveMobileControlSize({size, desktopDefault, mobileDefault, isMobile});\n}\n\n/**\n * Icon size matching the resolved control size.\n */\nexport function getControlIconSize(size: ControlSize | undefined): number {\n return size === 'xl' ? 20 : 16;\n}\n"]}
1
+ {"version":3,"file":"useMobileControlSize.js","sourceRoot":"../../../src","sources":["hooks/useMobileControlSize.ts"],"names":[],"mappings":";;AAUA,4DASC;AAKD,oDAQC;AAKD,gDAEC;AAvCD,6CAA4C;AAM5C;;;GAGG;AACH,SAAgB,wBAAwB,CAAwB,MAK/D;IACG,MAAM,EAAC,IAAI,EAAE,cAAc,EAAE,aAAa,EAAE,QAAQ,EAAC,GAAG,MAAM,CAAC;IAE/D,OAAO,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;AAC/D,CAAC;AAED;;GAEG;AACH,SAAgB,oBAAoB,CAChC,IAAmB,EACnB,cAAiB,EACjB,aAAgB;IAEhB,MAAM,QAAQ,GAAG,IAAA,iBAAS,GAAE,CAAC;IAE7B,OAAO,wBAAwB,CAAC,EAAC,IAAI,EAAE,cAAc,EAAE,aAAa,EAAE,QAAQ,EAAC,CAAC,CAAC;AACrF,CAAC;AAED;;GAEG;AACH,SAAgB,kBAAkB,CAAC,IAA6B;IAC5D,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACnC,CAAC","sourcesContent":["import {useMobile} from '@gravity-ui/uikit';\n\n// `xxs` only exists on `Label`, which uikit 7.48 gained. It is listed here so that the helpers\n// stay usable with the whole `LabelProps['size']` union across the supported uikit range.\nexport type ControlSize = 'xxs' | 'xs' | 's' | 'm' | 'l' | 'xl';\n\n/**\n * Resolves a control size: an explicit size always wins, otherwise the mobile\n * default is used in mobile mode and the desktop default elsewhere.\n */\nexport function resolveMobileControlSize<T extends ControlSize>(params: {\n size: T | undefined;\n desktopDefault: T;\n mobileDefault: T;\n isMobile: boolean;\n}): T {\n const {size, desktopDefault, mobileDefault, isMobile} = params;\n\n return size ?? (isMobile ? mobileDefault : desktopDefault);\n}\n\n/**\n * Mobile-aware control size, driven by the uikit `MobileProvider` context.\n */\nexport function useMobileControlSize<T extends ControlSize>(\n size: T | undefined,\n desktopDefault: T,\n mobileDefault: T,\n): T {\n const isMobile = useMobile();\n\n return resolveMobileControlSize({size, desktopDefault, mobileDefault, isMobile});\n}\n\n/**\n * Icon size matching the resolved control size.\n */\nexport function getControlIconSize(size: ControlSize | undefined): number {\n return size === 'xl' ? 20 : 16;\n}\n"]}
@@ -1 +1 @@
1
- {"version":"2.20.1","type":"commonjs","sideEffects":["*.css","*.scss"]}
1
+ {"version":"2.20.2-beta.1b9a51bfdf2818e8caefe25d7de3af232af29ca8.0","type":"commonjs","sideEffects":["*.css","*.scss"]}
@@ -1,10 +1,6 @@
1
- import type { TAssistantMessage, TChatMessage, TMessageContentUnion } from "../../../types/index.js";
1
+ import type { TChatMessage, TMessageContentUnion } from "../../../types/index.js";
2
2
  export type BuildFinalMessagesParams = {
3
3
  baseMessages: TChatMessage[];
4
- completedAssistantMessages: {
5
- id: string;
6
- content: TAssistantMessage['content'];
7
- }[];
8
4
  currentAssistantMessageId: string;
9
5
  contentParts: TMessageContentUnion[];
10
6
  };
@@ -5,17 +5,9 @@ function isMessageContentEmpty(content) {
5
5
  return Array.isArray(content) && content.length === 0;
6
6
  }
7
7
  export function buildFinalMessages(params) {
8
- const { baseMessages, completedAssistantMessages, currentAssistantMessageId, contentParts } = params;
8
+ const { baseMessages, currentAssistantMessageId, contentParts } = params;
9
9
  const currentContent = contentPartsToMessageContent(contentParts);
10
- const completedWithContent = completedAssistantMessages.filter((m) => !isMessageContentEmpty(m.content));
11
10
  const result = [...baseMessages];
12
- for (const m of completedWithContent) {
13
- result.push({
14
- id: m.id,
15
- role: 'assistant',
16
- content: m.content,
17
- });
18
- }
19
11
  if (!isMessageContentEmpty(currentContent)) {
20
12
  result.push({
21
13
  id: currentAssistantMessageId,
@@ -1 +1 @@
1
- {"version":3,"file":"buildFinalMessages.js","sourceRoot":"../../../../../src","sources":["adapters/openai/helpers/buildFinalMessages.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,4BAA4B,EAAC,0CAAuC;AAE5E,SAAS,qBAAqB,CAAC,OAAqC;IAChE,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;IAC9D,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC;AAC1D,CAAC;AASD,MAAM,UAAU,kBAAkB,CAAC,MAAgC;IAC/D,MAAM,EAAC,YAAY,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,YAAY,EAAC,GACrF,MAAM,CAAC;IACX,MAAM,cAAc,GAAG,4BAA4B,CAAC,YAAY,CAAC,CAAC;IAClE,MAAM,oBAAoB,GAAG,0BAA0B,CAAC,MAAM,CAC1D,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,OAAO,CAAC,CAC3C,CAAC;IACF,MAAM,MAAM,GAAmB,CAAC,GAAG,YAAY,CAAC,CAAC;IACjD,KAAK,MAAM,CAAC,IAAI,oBAAoB,EAAE,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC;YACR,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,IAAI,EAAE,WAAoB;YAC1B,OAAO,EAAE,CAAC,CAAC,OAAO;SACA,CAAC,CAAC;IAC5B,CAAC;IACD,IAAI,CAAC,qBAAqB,CAAC,cAAc,CAAC,EAAE,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC;YACR,EAAE,EAAE,yBAAyB;YAC7B,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,cAAc;SACL,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC","sourcesContent":["import type {TAssistantMessage, TChatMessage, TMessageContentUnion} from '../../../types';\n\nimport {contentPartsToMessageContent} from './contentPartsToMessageContent';\n\nfunction isMessageContentEmpty(content: TAssistantMessage['content']): boolean {\n if (typeof content === 'string') return content.trim() === '';\n return Array.isArray(content) && content.length === 0;\n}\n\nexport type BuildFinalMessagesParams = {\n baseMessages: TChatMessage[];\n completedAssistantMessages: {id: string; content: TAssistantMessage['content']}[];\n currentAssistantMessageId: string;\n contentParts: TMessageContentUnion[];\n};\n\nexport function buildFinalMessages(params: BuildFinalMessagesParams): TChatMessage[] {\n const {baseMessages, completedAssistantMessages, currentAssistantMessageId, contentParts} =\n params;\n const currentContent = contentPartsToMessageContent(contentParts);\n const completedWithContent = completedAssistantMessages.filter(\n (m) => !isMessageContentEmpty(m.content),\n );\n const result: TChatMessage[] = [...baseMessages];\n for (const m of completedWithContent) {\n result.push({\n id: m.id,\n role: 'assistant' as const,\n content: m.content,\n } as TAssistantMessage);\n }\n if (!isMessageContentEmpty(currentContent)) {\n result.push({\n id: currentAssistantMessageId,\n role: 'assistant',\n content: currentContent,\n } as TAssistantMessage);\n }\n return result;\n}\n"]}
1
+ {"version":3,"file":"buildFinalMessages.js","sourceRoot":"../../../../../src","sources":["adapters/openai/helpers/buildFinalMessages.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,4BAA4B,EAAC,0CAAuC;AAE5E,SAAS,qBAAqB,CAAC,OAAqC;IAChE,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;IAC9D,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC;AAC1D,CAAC;AAQD,MAAM,UAAU,kBAAkB,CAAC,MAAgC;IAC/D,MAAM,EAAC,YAAY,EAAE,yBAAyB,EAAE,YAAY,EAAC,GAAG,MAAM,CAAC;IACvE,MAAM,cAAc,GAAG,4BAA4B,CAAC,YAAY,CAAC,CAAC;IAClE,MAAM,MAAM,GAAmB,CAAC,GAAG,YAAY,CAAC,CAAC;IACjD,IAAI,CAAC,qBAAqB,CAAC,cAAc,CAAC,EAAE,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC;YACR,EAAE,EAAE,yBAAyB;YAC7B,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,cAAc;SACL,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC","sourcesContent":["import type {TAssistantMessage, TChatMessage, TMessageContentUnion} from '../../../types';\n\nimport {contentPartsToMessageContent} from './contentPartsToMessageContent';\n\nfunction isMessageContentEmpty(content: TAssistantMessage['content']): boolean {\n if (typeof content === 'string') return content.trim() === '';\n return Array.isArray(content) && content.length === 0;\n}\n\nexport type BuildFinalMessagesParams = {\n baseMessages: TChatMessage[];\n currentAssistantMessageId: string;\n contentParts: TMessageContentUnion[];\n};\n\nexport function buildFinalMessages(params: BuildFinalMessagesParams): TChatMessage[] {\n const {baseMessages, currentAssistantMessageId, contentParts} = params;\n const currentContent = contentPartsToMessageContent(contentParts);\n const result: TChatMessage[] = [...baseMessages];\n if (!isMessageContentEmpty(currentContent)) {\n result.push({\n id: currentAssistantMessageId,\n role: 'assistant',\n content: currentContent,\n } as TAssistantMessage);\n }\n return result;\n}\n"]}
@@ -2,11 +2,10 @@ import type { TAssistantMessage, TChatMessage } from "../../../types/index.js";
2
2
  import type { OpenAIStreamEventLike } from "../types/index.js";
3
3
  export type ConsumeStreamCallbacks = {
4
4
  baseMessages: TChatMessage[];
5
- getAssistantMessageId: (index: number) => string;
5
+ getAssistantMessageId: () => string;
6
6
  /** When the API sends `response.output_item.added` for a message, exposes OpenAI item id (e.g. msg-cc-…) for reactions. */
7
7
  onAssistantMessageIdResolved?: (previousId: string, openaiItemId: string) => void;
8
8
  onContentUpdate: (messageId: string, content: TAssistantMessage['content']) => void;
9
- onNewMessage: (messageId: string) => void;
10
9
  onEnd: (finalMessages: TChatMessage[], status: 'done' | 'error', error?: Error) => void;
11
10
  getIsCancelled: () => boolean;
12
11
  };
@@ -5,7 +5,6 @@ import { contentPartsToMessageContent } from "./contentPartsToMessageContent.js"
5
5
  import { getOpenAIMessageItemIdFromOutputItemAdded } from "./getOpenAIMessageItemIdFromOutputItemAdded.js";
6
6
  import { getStreamEndResult } from "./getStreamEndResult.js";
7
7
  import { getStreamEventContentUpdate } from "./getStreamEventContentUpdate.js";
8
- import { isMessageOutputItemDoneEvent } from "./isMessageOutputItemDoneEvent.js";
9
8
  import { isStreamEndOrErrorEvent } from "./isStreamEndOrErrorEvent.js";
10
9
  function waitForNextTask() {
11
10
  return new Promise((resolve) => setTimeout(resolve, 0));
@@ -17,16 +16,27 @@ function shouldYieldAfterUpdate(update) {
17
16
  /** Consumes stream, invokes callbacks. State/setters live in caller. */
18
17
  export async function consumeOpenAIStream(stream, callbacks) {
19
18
  var _a, e_1, _b, _c;
20
- const { baseMessages, getAssistantMessageId, onAssistantMessageIdResolved, onContentUpdate, onNewMessage, onEnd, getIsCancelled, } = callbacks;
21
- const completedAssistantMessages = [];
22
- let messageIndex = 0;
23
- let currentAssistantMessageId = getAssistantMessageId(0);
19
+ const { baseMessages, getAssistantMessageId, onAssistantMessageIdResolved, onContentUpdate, onEnd, getIsCancelled, } = callbacks;
20
+ let currentAssistantMessageId = getAssistantMessageId();
21
+ let assistantMessageItemIdResolved = false;
24
22
  let contentParts = [];
25
23
  const applyContentToCurrentMessage = (parts) => {
26
24
  if (getIsCancelled())
27
25
  return;
28
26
  onContentUpdate(currentAssistantMessageId, contentPartsToMessageContent(parts));
29
27
  };
28
+ const resolveAssistantMessageId = (openaiMessageItemId) => {
29
+ if (assistantMessageItemIdResolved)
30
+ return;
31
+ assistantMessageItemIdResolved = true;
32
+ if (openaiMessageItemId === currentAssistantMessageId)
33
+ return;
34
+ const previousId = currentAssistantMessageId;
35
+ currentAssistantMessageId = openaiMessageItemId;
36
+ if (!getIsCancelled()) {
37
+ onAssistantMessageIdResolved === null || onAssistantMessageIdResolved === void 0 ? void 0 : onAssistantMessageIdResolved(previousId, openaiMessageItemId);
38
+ }
39
+ };
30
40
  try {
31
41
  try {
32
42
  for (var _d = true, stream_1 = __asyncValues(stream), stream_1_1; stream_1_1 = await stream_1.next(), _a = stream_1_1.done, !_a; _d = true) {
@@ -36,18 +46,13 @@ export async function consumeOpenAIStream(stream, callbacks) {
36
46
  if (getIsCancelled())
37
47
  return;
38
48
  const openaiMessageItemId = getOpenAIMessageItemIdFromOutputItemAdded(event);
39
- if (openaiMessageItemId && openaiMessageItemId !== currentAssistantMessageId) {
40
- const previousId = currentAssistantMessageId;
41
- currentAssistantMessageId = openaiMessageItemId;
42
- if (!getIsCancelled()) {
43
- onAssistantMessageIdResolved === null || onAssistantMessageIdResolved === void 0 ? void 0 : onAssistantMessageIdResolved(previousId, openaiMessageItemId);
44
- }
49
+ if (openaiMessageItemId) {
50
+ resolveAssistantMessageId(openaiMessageItemId);
45
51
  continue;
46
52
  }
47
53
  if (isStreamEndOrErrorEvent(event)) {
48
54
  const finalMessages = buildFinalMessages({
49
55
  baseMessages,
50
- completedAssistantMessages,
51
56
  currentAssistantMessageId,
52
57
  contentParts,
53
58
  });
@@ -57,19 +62,6 @@ export async function consumeOpenAIStream(stream, callbacks) {
57
62
  onEnd(finalMessages, endResult.status, endResult.status === 'error' ? endResult.error : undefined);
58
63
  return;
59
64
  }
60
- if (isMessageOutputItemDoneEvent(event)) {
61
- completedAssistantMessages.push({
62
- id: currentAssistantMessageId,
63
- content: contentPartsToMessageContent(contentParts),
64
- });
65
- messageIndex += 1;
66
- currentAssistantMessageId = getAssistantMessageId(messageIndex);
67
- contentParts = [];
68
- if (getIsCancelled())
69
- return;
70
- onNewMessage(currentAssistantMessageId);
71
- continue;
72
- }
73
65
  const update = getStreamEventContentUpdate(event);
74
66
  if (!update)
75
67
  continue;
@@ -95,7 +87,6 @@ export async function consumeOpenAIStream(stream, callbacks) {
95
87
  if (!getIsCancelled()) {
96
88
  const finalMessages = buildFinalMessages({
97
89
  baseMessages,
98
- completedAssistantMessages,
99
90
  currentAssistantMessageId,
100
91
  contentParts,
101
92
  });
@@ -106,7 +97,6 @@ export async function consumeOpenAIStream(stream, callbacks) {
106
97
  if (!getIsCancelled()) {
107
98
  const finalMessages = buildFinalMessages({
108
99
  baseMessages,
109
- completedAssistantMessages,
110
100
  currentAssistantMessageId,
111
101
  contentParts,
112
102
  });
@@ -1 +1 @@
1
- {"version":3,"file":"consumeOpenAIStream.js","sourceRoot":"../../../../../src","sources":["adapters/openai/helpers/consumeOpenAIStream.ts"],"names":[],"mappings":"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","sourcesContent":["import type {TAssistantMessage, TChatMessage, TMessageContentUnion} from '../../../types';\nimport type {OpenAIStreamEventLike} from '../types';\n\nimport {applyContentUpdate} from './applyContentUpdate';\nimport {buildFinalMessages} from './buildFinalMessages';\nimport {contentPartsToMessageContent} from './contentPartsToMessageContent';\nimport {getOpenAIMessageItemIdFromOutputItemAdded} from './getOpenAIMessageItemIdFromOutputItemAdded';\nimport {getStreamEndResult} from './getStreamEndResult';\nimport {getStreamEventContentUpdate} from './getStreamEventContentUpdate';\nimport {isMessageOutputItemDoneEvent} from './isMessageOutputItemDoneEvent';\nimport {isStreamEndOrErrorEvent} from './isStreamEndOrErrorEvent';\n\nfunction waitForNextTask(): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, 0));\n}\n\nfunction shouldYieldAfterUpdate(update: ReturnType<typeof getStreamEventContentUpdate>): boolean {\n return update?.kind === 'tool_add' && (update.status ?? 'loading') === 'loading';\n}\n\nexport type ConsumeStreamCallbacks = {\n baseMessages: TChatMessage[];\n getAssistantMessageId: (index: number) => string;\n /** When the API sends `response.output_item.added` for a message, exposes OpenAI item id (e.g. msg-cc-…) for reactions. */\n onAssistantMessageIdResolved?: (previousId: string, openaiItemId: string) => void;\n onContentUpdate: (messageId: string, content: TAssistantMessage['content']) => void;\n onNewMessage: (messageId: string) => void;\n onEnd: (finalMessages: TChatMessage[], status: 'done' | 'error', error?: Error) => void;\n getIsCancelled: () => boolean;\n};\n\n/** Consumes stream, invokes callbacks. State/setters live in caller. */\n\nexport async function consumeOpenAIStream(\n stream: AsyncIterable<OpenAIStreamEventLike>,\n callbacks: ConsumeStreamCallbacks,\n): Promise<void> {\n const {\n baseMessages,\n getAssistantMessageId,\n onAssistantMessageIdResolved,\n onContentUpdate,\n onNewMessage,\n onEnd,\n getIsCancelled,\n } = callbacks;\n\n const completedAssistantMessages: {id: string; content: TAssistantMessage['content']}[] = [];\n let messageIndex = 0;\n let currentAssistantMessageId = getAssistantMessageId(0);\n let contentParts: TMessageContentUnion[] = [];\n\n const applyContentToCurrentMessage = (parts: TMessageContentUnion[]) => {\n if (getIsCancelled()) return;\n onContentUpdate(currentAssistantMessageId, contentPartsToMessageContent(parts));\n };\n\n try {\n for await (const event of stream) {\n if (getIsCancelled()) return;\n\n const openaiMessageItemId = getOpenAIMessageItemIdFromOutputItemAdded(event);\n if (openaiMessageItemId && openaiMessageItemId !== currentAssistantMessageId) {\n const previousId = currentAssistantMessageId;\n currentAssistantMessageId = openaiMessageItemId;\n if (!getIsCancelled()) {\n onAssistantMessageIdResolved?.(previousId, openaiMessageItemId);\n }\n continue;\n }\n\n if (isStreamEndOrErrorEvent(event)) {\n const finalMessages = buildFinalMessages({\n baseMessages,\n completedAssistantMessages,\n currentAssistantMessageId,\n contentParts,\n });\n const endResult = getStreamEndResult(event);\n if (getIsCancelled()) return;\n onEnd(\n finalMessages,\n endResult.status,\n endResult.status === 'error' ? endResult.error : undefined,\n );\n return;\n }\n\n if (isMessageOutputItemDoneEvent(event)) {\n completedAssistantMessages.push({\n id: currentAssistantMessageId,\n content: contentPartsToMessageContent(contentParts),\n });\n messageIndex += 1;\n currentAssistantMessageId = getAssistantMessageId(messageIndex);\n contentParts = [];\n if (getIsCancelled()) return;\n onNewMessage(currentAssistantMessageId);\n continue;\n }\n\n const update = getStreamEventContentUpdate(event);\n if (!update) continue;\n\n const nextParts = applyContentUpdate(contentParts, update);\n if (nextParts !== null) {\n contentParts = nextParts;\n applyContentToCurrentMessage(contentParts);\n if (shouldYieldAfterUpdate(update)) {\n await waitForNextTask();\n if (getIsCancelled()) return;\n }\n }\n }\n\n if (!getIsCancelled()) {\n const finalMessages = buildFinalMessages({\n baseMessages,\n completedAssistantMessages,\n currentAssistantMessageId,\n contentParts,\n });\n onEnd(finalMessages, 'done');\n }\n } catch (err) {\n if (!getIsCancelled()) {\n const finalMessages = buildFinalMessages({\n baseMessages,\n completedAssistantMessages,\n currentAssistantMessageId,\n contentParts,\n });\n onEnd(finalMessages, 'error', err instanceof Error ? err : new Error(String(err)));\n }\n }\n}\n"]}
1
+ {"version":3,"file":"consumeOpenAIStream.js","sourceRoot":"../../../../../src","sources":["adapters/openai/helpers/consumeOpenAIStream.ts"],"names":[],"mappings":"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","sourcesContent":["import type {TAssistantMessage, TChatMessage, TMessageContentUnion} from '../../../types';\nimport type {OpenAIStreamEventLike} from '../types';\n\nimport {applyContentUpdate} from './applyContentUpdate';\nimport {buildFinalMessages} from './buildFinalMessages';\nimport {contentPartsToMessageContent} from './contentPartsToMessageContent';\nimport {getOpenAIMessageItemIdFromOutputItemAdded} from './getOpenAIMessageItemIdFromOutputItemAdded';\nimport {getStreamEndResult} from './getStreamEndResult';\nimport {getStreamEventContentUpdate} from './getStreamEventContentUpdate';\nimport {isStreamEndOrErrorEvent} from './isStreamEndOrErrorEvent';\n\nfunction waitForNextTask(): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, 0));\n}\n\nfunction shouldYieldAfterUpdate(update: ReturnType<typeof getStreamEventContentUpdate>): boolean {\n return update?.kind === 'tool_add' && (update.status ?? 'loading') === 'loading';\n}\n\nexport type ConsumeStreamCallbacks = {\n baseMessages: TChatMessage[];\n getAssistantMessageId: () => string;\n /** When the API sends `response.output_item.added` for a message, exposes OpenAI item id (e.g. msg-cc-…) for reactions. */\n onAssistantMessageIdResolved?: (previousId: string, openaiItemId: string) => void;\n onContentUpdate: (messageId: string, content: TAssistantMessage['content']) => void;\n onEnd: (finalMessages: TChatMessage[], status: 'done' | 'error', error?: Error) => void;\n getIsCancelled: () => boolean;\n};\n\n/** Consumes stream, invokes callbacks. State/setters live in caller. */\n\nexport async function consumeOpenAIStream(\n stream: AsyncIterable<OpenAIStreamEventLike>,\n callbacks: ConsumeStreamCallbacks,\n): Promise<void> {\n const {\n baseMessages,\n getAssistantMessageId,\n onAssistantMessageIdResolved,\n onContentUpdate,\n onEnd,\n getIsCancelled,\n } = callbacks;\n\n let currentAssistantMessageId = getAssistantMessageId();\n let assistantMessageItemIdResolved = false;\n let contentParts: TMessageContentUnion[] = [];\n\n const applyContentToCurrentMessage = (parts: TMessageContentUnion[]) => {\n if (getIsCancelled()) return;\n onContentUpdate(currentAssistantMessageId, contentPartsToMessageContent(parts));\n };\n\n const resolveAssistantMessageId = (openaiMessageItemId: string) => {\n if (assistantMessageItemIdResolved) return;\n assistantMessageItemIdResolved = true;\n if (openaiMessageItemId === currentAssistantMessageId) return;\n\n const previousId = currentAssistantMessageId;\n currentAssistantMessageId = openaiMessageItemId;\n if (!getIsCancelled()) {\n onAssistantMessageIdResolved?.(previousId, openaiMessageItemId);\n }\n };\n\n try {\n for await (const event of stream) {\n if (getIsCancelled()) return;\n\n const openaiMessageItemId = getOpenAIMessageItemIdFromOutputItemAdded(event);\n if (openaiMessageItemId) {\n resolveAssistantMessageId(openaiMessageItemId);\n continue;\n }\n\n if (isStreamEndOrErrorEvent(event)) {\n const finalMessages = buildFinalMessages({\n baseMessages,\n currentAssistantMessageId,\n contentParts,\n });\n const endResult = getStreamEndResult(event);\n if (getIsCancelled()) return;\n onEnd(\n finalMessages,\n endResult.status,\n endResult.status === 'error' ? endResult.error : undefined,\n );\n return;\n }\n\n const update = getStreamEventContentUpdate(event);\n if (!update) continue;\n\n const nextParts = applyContentUpdate(contentParts, update);\n if (nextParts !== null) {\n contentParts = nextParts;\n applyContentToCurrentMessage(contentParts);\n if (shouldYieldAfterUpdate(update)) {\n await waitForNextTask();\n if (getIsCancelled()) return;\n }\n }\n }\n\n if (!getIsCancelled()) {\n const finalMessages = buildFinalMessages({\n baseMessages,\n currentAssistantMessageId,\n contentParts,\n });\n onEnd(finalMessages, 'done');\n }\n } catch (err) {\n if (!getIsCancelled()) {\n const finalMessages = buildFinalMessages({\n baseMessages,\n currentAssistantMessageId,\n contentParts,\n });\n onEnd(finalMessages, 'error', err instanceof Error ? err : new Error(String(err)));\n }\n }\n}\n"]}
@@ -2,14 +2,5 @@ import type { TChatMessage } from "../../types/index.js";
2
2
  import type { OpenAIStreamAdapterOptions, OpenAIStreamAdapterResult, OpenAIStreamSource } from "./types/index.js";
3
3
  import { OpenAIResponseLike } from "./types/openAiTypes.js";
4
4
  export type { OpenAIStreamAdapterOptions, OpenAIStreamAdapterResult, OpenAIStreamAdapterStatus, OpenAIStreamSource, } from "./types/index.js";
5
- /**
6
- * When the stream splits the reply into two consecutive assistant messages, the same tool call can
7
- * appear in both. Moves matching tool parts into the earlier segment and drops the duplicate from
8
- * the later one so the UI does not show orphaned repeats.
9
- *
10
- * @param messages - Chat messages (possibly with duplicate tool parts across adjacent assistants).
11
- * @returns A shallow-copied message list with orphan duplicates collapsed.
12
- */
13
- export declare function collapseOrphanToolBetweenAssistantSegments(messages: TChatMessage[]): TChatMessage[];
14
5
  export declare function useOpenAIResponsesAdapter(response: OpenAIResponseLike | null): TChatMessage[];
15
6
  export declare function useOpenAIStreamAdapter(stream: OpenAIStreamSource | null, options?: OpenAIStreamAdapterOptions): OpenAIStreamAdapterResult;
@@ -1,65 +1,13 @@
1
1
  import { __asyncGenerator, __asyncValues, __await } from "tslib";
2
2
  import { useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { consumeOpenAIStream } from "./helpers/consumeOpenAIStream.js";
4
- import { contentPartsToMessageContent } from "./helpers/contentPartsToMessageContent.js";
5
4
  import { fetchResponseToStreamEvents } from "./helpers/fetchResponseToStreamEvents.js";
6
5
  import { isFetchResponse } from "./helpers/isFetchResponse.js";
7
6
  import { openAIResponseToMessages } from "./helpers/openAIResponseToMessages.js";
8
7
  /** Single non-streaming response → TChatMessage[]. For streaming use useOpenAIStreamAdapter. */
9
- function isToolPart(p) {
10
- return p.type === 'tool';
11
- }
12
- /**
13
- * When the stream splits the reply into two consecutive assistant messages, the same tool call can
14
- * appear in both. Moves matching tool parts into the earlier segment and drops the duplicate from
15
- * the later one so the UI does not show orphaned repeats.
16
- *
17
- * @param messages - Chat messages (possibly with duplicate tool parts across adjacent assistants).
18
- * @returns A shallow-copied message list with orphan duplicates collapsed.
19
- */
20
- export function collapseOrphanToolBetweenAssistantSegments(messages) {
21
- const result = messages.map((m) => m.role === 'assistant' ? Object.assign({}, m) : m);
22
- for (let i = result.length - 1; i >= 1; i--) {
23
- const next = result[i];
24
- const prev = result[i - 1];
25
- if (prev.role !== 'assistant' || next.role !== 'assistant') {
26
- continue;
27
- }
28
- const nextContent = next.content;
29
- if (!Array.isArray(nextContent)) {
30
- continue;
31
- }
32
- const prevContent = prev.content;
33
- if (!Array.isArray(prevContent)) {
34
- continue;
35
- }
36
- const updatedNextParts = [...nextContent];
37
- let updatedPrevParts = [...prevContent];
38
- let changed = false;
39
- for (let t = updatedNextParts.length - 1; t >= 0; t--) {
40
- const part = updatedNextParts[t];
41
- if (!isToolPart(part) || !part.id) {
42
- continue;
43
- }
44
- const pIdx = updatedPrevParts.findIndex((p) => isToolPart(p) && p.id === part.id);
45
- if (pIdx < 0) {
46
- continue;
47
- }
48
- updatedPrevParts = updatedPrevParts.map((p, j) => (j === pIdx ? part : p));
49
- updatedNextParts.splice(t, 1);
50
- changed = true;
51
- }
52
- if (!changed) {
53
- continue;
54
- }
55
- result[i - 1] = Object.assign(Object.assign({}, prev), { content: contentPartsToMessageContent(updatedPrevParts) });
56
- result[i] = Object.assign(Object.assign({}, next), { content: contentPartsToMessageContent(updatedNextParts) });
57
- }
58
- return result;
59
- }
60
8
  /**
61
9
  * Index of the assistant message that should receive streamed content for `messageId`.
62
- * Uses an exact id match when present; otherwise the last assistant (current streaming segment),
10
+ * Uses an exact id match when present; otherwise the last assistant (current streaming message),
63
11
  * which covers events that reference an id before our list has caught up.
64
12
  *
65
13
  * @param messages - Current chat messages while streaming.
@@ -161,11 +109,11 @@ export function useOpenAIStreamAdapter(stream, options = {}) {
161
109
  },
162
110
  };
163
111
  const baseMessages = initialMessagesRef.current;
164
- const getAssistantMessageId = (index) => index === 0 ? assistantMessageId : `${assistantMessageId}-${index}`;
112
+ const getAssistantMessageId = () => assistantMessageId;
165
113
  setMessages([
166
114
  ...baseMessages,
167
115
  {
168
- id: getAssistantMessageId(0),
116
+ id: getAssistantMessageId(),
169
117
  role: 'assistant',
170
118
  content: '',
171
119
  },
@@ -187,12 +135,12 @@ export function useOpenAIStreamAdapter(stream, options = {}) {
187
135
  onAssistantMessageIdResolved: (previousId, openaiItemId) => {
188
136
  if (cancelled)
189
137
  return;
190
- setMessages((prev) => collapseOrphanToolBetweenAssistantSegments(prev.map((msg) => {
138
+ setMessages((prev) => prev.map((msg) => {
191
139
  if (msg.id === previousId && msg.role === 'assistant') {
192
140
  return Object.assign(Object.assign({}, msg), { id: openaiItemId });
193
141
  }
194
142
  return msg;
195
- })));
143
+ }));
196
144
  },
197
145
  onContentUpdate: (messageId, content) => {
198
146
  if (cancelled)
@@ -200,34 +148,21 @@ export function useOpenAIStreamAdapter(stream, options = {}) {
200
148
  setMessages((prev) => {
201
149
  const targetIndex = resolveStreamContentTargetIndex(prev, messageId);
202
150
  if (targetIndex < 0) {
203
- return collapseOrphanToolBetweenAssistantSegments(prev);
151
+ return prev;
204
152
  }
205
- const mapped = prev.map((msg, i) => {
153
+ return prev.map((msg, i) => {
206
154
  if (i !== targetIndex) {
207
155
  return msg;
208
156
  }
209
157
  return Object.assign(Object.assign({}, msg), { content });
210
158
  });
211
- return collapseOrphanToolBetweenAssistantSegments(mapped);
212
159
  });
213
160
  },
214
- onNewMessage: (messageId) => {
215
- if (cancelled)
216
- return;
217
- setMessages((prev) => [
218
- ...prev,
219
- {
220
- id: messageId,
221
- role: 'assistant',
222
- content: '',
223
- },
224
- ]);
225
- },
226
161
  onEnd: (finalMessages, s, err) => {
227
162
  var _a;
228
163
  setStatus(s === 'done' ? 'ready' : s);
229
164
  setError(err !== null && err !== void 0 ? err : null);
230
- const normalized = injectUsageMetadata(collapseOrphanToolBetweenAssistantSegments(finalMessages));
165
+ const normalized = injectUsageMetadata(finalMessages);
231
166
  if (!cancelled) {
232
167
  setMessages(normalized);
233
168
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useOpenAIResponsesAdapter.js","sourceRoot":"../../../../src","sources":["adapters/openai/useOpenAIResponsesAdapter.ts"],"names":[],"mappings":"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","sourcesContent":["import {useEffect, useMemo, useRef, useState} from 'react';\n\nimport type {TAssistantMessage, TChatMessage, TMessageContentUnion} from '../../types';\n\nimport {type ConsumeStreamCallbacks, consumeOpenAIStream} from './helpers/consumeOpenAIStream';\nimport {contentPartsToMessageContent} from './helpers/contentPartsToMessageContent';\nimport {fetchResponseToStreamEvents} from './helpers/fetchResponseToStreamEvents';\nimport {isFetchResponse} from './helpers/isFetchResponse';\nimport {openAIResponseToMessages} from './helpers/openAIResponseToMessages';\nimport type {\n OpenAIStreamAdapterOptions,\n OpenAIStreamAdapterResult,\n OpenAIStreamSource,\n} from './types';\nimport {OpenAIResponseLike, OpenAIStreamEventLike} from './types/openAiTypes';\n\nexport type {\n OpenAIStreamAdapterOptions,\n OpenAIStreamAdapterResult,\n OpenAIStreamAdapterStatus,\n OpenAIStreamSource,\n} from './types';\n\n/** Single non-streaming response → TChatMessage[]. For streaming use useOpenAIStreamAdapter. */\n\nfunction isToolPart(p: TMessageContentUnion): p is Extract<TMessageContentUnion, {type: 'tool'}> {\n return p.type === 'tool';\n}\n\n/**\n * When the stream splits the reply into two consecutive assistant messages, the same tool call can\n * appear in both. Moves matching tool parts into the earlier segment and drops the duplicate from\n * the later one so the UI does not show orphaned repeats.\n *\n * @param messages - Chat messages (possibly with duplicate tool parts across adjacent assistants).\n * @returns A shallow-copied message list with orphan duplicates collapsed.\n */\nexport function collapseOrphanToolBetweenAssistantSegments(\n messages: TChatMessage[],\n): TChatMessage[] {\n const result = messages.map((m) =>\n m.role === 'assistant' ? ({...m} as TAssistantMessage) : m,\n );\n\n for (let i = result.length - 1; i >= 1; i--) {\n const next = result[i] as TAssistantMessage;\n const prev = result[i - 1] as TAssistantMessage;\n if (prev.role !== 'assistant' || next.role !== 'assistant') {\n continue;\n }\n\n const nextContent = next.content;\n if (!Array.isArray(nextContent)) {\n continue;\n }\n\n const prevContent = prev.content;\n if (!Array.isArray(prevContent)) {\n continue;\n }\n\n const updatedNextParts = [...nextContent] as TMessageContentUnion[];\n let updatedPrevParts = [...prevContent] as TMessageContentUnion[];\n let changed = false;\n\n for (let t = updatedNextParts.length - 1; t >= 0; t--) {\n const part = updatedNextParts[t];\n if (!isToolPart(part) || !part.id) {\n continue;\n }\n const pIdx = updatedPrevParts.findIndex((p) => isToolPart(p) && p.id === part.id);\n if (pIdx < 0) {\n continue;\n }\n updatedPrevParts = updatedPrevParts.map((p, j) => (j === pIdx ? part : p));\n updatedNextParts.splice(t, 1);\n changed = true;\n }\n\n if (!changed) {\n continue;\n }\n\n result[i - 1] = {\n ...prev,\n content: contentPartsToMessageContent(updatedPrevParts),\n };\n result[i] = {\n ...next,\n content: contentPartsToMessageContent(updatedNextParts),\n };\n }\n\n return result;\n}\n\n/**\n * Index of the assistant message that should receive streamed content for `messageId`.\n * Uses an exact id match when present; otherwise the last assistant (current streaming segment),\n * which covers events that reference an id before our list has caught up.\n *\n * @param messages - Current chat messages while streaming.\n * @param messageId - Target assistant id from the stream event.\n * @returns Index of the assistant to update, or `-1` if there is no assistant.\n */\nfunction resolveStreamContentTargetIndex(messages: TChatMessage[], messageId: string): number {\n const byId = messages.findIndex((m) => m.id === messageId && m.role === 'assistant');\n if (byId >= 0) {\n return byId;\n }\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === 'assistant') {\n return i;\n }\n }\n return -1;\n}\n\nfunction getResponseIdFromEvent(event: OpenAIStreamEventLike): string | null {\n const response = (event as {response?: unknown}).response;\n if (!response || typeof response !== 'object') {\n return null;\n }\n\n const id = (response as {id?: unknown}).id;\n return typeof id === 'string' ? id : null;\n}\n\nexport function useOpenAIResponsesAdapter(response: OpenAIResponseLike | null): TChatMessage[] {\n return useMemo(() => openAIResponseToMessages(response), [response]);\n}\n\nexport function useOpenAIStreamAdapter(\n stream: OpenAIStreamSource | null,\n options: OpenAIStreamAdapterOptions = {},\n): OpenAIStreamAdapterResult {\n const {\n initialMessages = [],\n assistantMessageId: optionId,\n onStreamEnd,\n trackTokenUsage = false,\n } = options;\n const onStreamEndRef = useRef(onStreamEnd);\n onStreamEndRef.current = onStreamEnd;\n const initialMessagesRef = useRef(initialMessages);\n initialMessagesRef.current = initialMessages;\n\n // messageId → output_tokens from response.completed event\n const usageMapRef = useRef<Map<string, number>>(new Map());\n\n const [messages, setMessages] = useState<TChatMessage[]>(initialMessages);\n const [status, setStatus] = useState<OpenAIStreamAdapterResult['status']>('ready');\n const [error, setError] = useState<Error | null>(null);\n const [responseId, setResponseId] = useState<string | null>(null);\n\n const assistantMessageId = useMemo(() => optionId ?? `assistant-${Date.now()}`, [optionId]);\n\n useEffect(() => {\n if (!stream) {\n setStatus('ready');\n setError(null);\n setResponseId(null);\n return undefined;\n }\n\n const sourceStream: AsyncIterable<OpenAIStreamEventLike> = isFetchResponse(stream)\n ? fetchResponseToStreamEvents(stream)\n : stream;\n\n let cancelled = false;\n setStatus('streaming');\n setError(null);\n setResponseId(null);\n usageMapRef.current = new Map();\n\n const streamToConsume: AsyncIterable<OpenAIStreamEventLike> = {\n async *[Symbol.asyncIterator]() {\n for await (const event of sourceStream) {\n const nextResponseId = getResponseIdFromEvent(event);\n if (!cancelled && nextResponseId) {\n setResponseId((prev) => (prev === nextResponseId ? prev : nextResponseId));\n }\n\n // Extract token usage from response.completed (only when opt-in)\n if (trackTokenUsage) {\n const e = event as Record<string, unknown>;\n const eventType =\n (e.type as string | undefined) ?? (e.event as string | undefined);\n if (eventType === 'response.completed' && e.response) {\n const response = e.response as {\n usage?: {output_tokens?: number};\n output?: Array<{id?: string; type?: string}>;\n };\n const tokens = response.usage?.output_tokens ?? 0;\n if (tokens > 0) {\n (response.output ?? []).forEach(({id, type}) => {\n if (id && type === 'message') {\n usageMapRef.current.set(id, tokens);\n }\n });\n }\n }\n }\n\n yield event;\n }\n },\n };\n\n const baseMessages = initialMessagesRef.current;\n const getAssistantMessageId = (index: number) =>\n index === 0 ? assistantMessageId : `${assistantMessageId}-${index}`;\n\n setMessages([\n ...baseMessages,\n {\n id: getAssistantMessageId(0),\n role: 'assistant',\n content: '',\n } as TAssistantMessage,\n ]);\n\n const injectUsageMetadata = (msgs: TChatMessage[]): TChatMessage[] => {\n if (usageMapRef.current.size === 0) return msgs;\n return msgs.map((msg) => {\n if (msg.role === 'assistant' && msg.id && usageMapRef.current.has(msg.id)) {\n return {\n ...msg,\n metadata: {\n ...msg.metadata,\n outputTokens: usageMapRef.current.get(msg.id),\n },\n } as TAssistantMessage;\n }\n return msg;\n });\n };\n\n const callbacks: ConsumeStreamCallbacks = {\n baseMessages,\n getAssistantMessageId,\n // Provisional id from getAssistantMessageId → OpenAI item id once the stream provides it.\n onAssistantMessageIdResolved: (previousId, openaiItemId) => {\n if (cancelled) return;\n setMessages((prev) =>\n collapseOrphanToolBetweenAssistantSegments(\n prev.map((msg): TChatMessage => {\n if (msg.id === previousId && msg.role === 'assistant') {\n return {...msg, id: openaiItemId} as TAssistantMessage;\n }\n return msg;\n }),\n ),\n );\n },\n onContentUpdate: (messageId, content) => {\n if (cancelled) return;\n setMessages((prev) => {\n const targetIndex = resolveStreamContentTargetIndex(prev, messageId);\n if (targetIndex < 0) {\n return collapseOrphanToolBetweenAssistantSegments(prev);\n }\n const mapped = prev.map((msg, i) => {\n if (i !== targetIndex) {\n return msg;\n }\n return {...msg, content} as TAssistantMessage;\n });\n return collapseOrphanToolBetweenAssistantSegments(mapped);\n });\n },\n onNewMessage: (messageId) => {\n if (cancelled) return;\n setMessages((prev) => [\n ...prev,\n {\n id: messageId,\n role: 'assistant',\n content: '',\n } as TAssistantMessage,\n ]);\n },\n onEnd: (finalMessages, s, err) => {\n setStatus(s === 'done' ? 'ready' : s);\n setError(err ?? null);\n const normalized = injectUsageMetadata(\n collapseOrphanToolBetweenAssistantSegments(finalMessages),\n );\n if (!cancelled) {\n setMessages(normalized);\n }\n onStreamEndRef.current?.(normalized);\n },\n getIsCancelled: () => cancelled,\n };\n\n consumeOpenAIStream(streamToConsume, callbacks);\n\n return () => {\n cancelled = true;\n };\n }, [stream, assistantMessageId]);\n\n return {messages, status, error, responseId};\n}\n"]}
1
+ {"version":3,"file":"useOpenAIResponsesAdapter.js","sourceRoot":"../../../../src","sources":["adapters/openai/useOpenAIResponsesAdapter.ts"],"names":[],"mappings":"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","sourcesContent":["import {useEffect, useMemo, useRef, useState} from 'react';\n\nimport type {TAssistantMessage, TChatMessage} from '../../types';\n\nimport {type ConsumeStreamCallbacks, consumeOpenAIStream} from './helpers/consumeOpenAIStream';\nimport {fetchResponseToStreamEvents} from './helpers/fetchResponseToStreamEvents';\nimport {isFetchResponse} from './helpers/isFetchResponse';\nimport {openAIResponseToMessages} from './helpers/openAIResponseToMessages';\nimport type {\n OpenAIStreamAdapterOptions,\n OpenAIStreamAdapterResult,\n OpenAIStreamSource,\n} from './types';\nimport {OpenAIResponseLike, OpenAIStreamEventLike} from './types/openAiTypes';\n\nexport type {\n OpenAIStreamAdapterOptions,\n OpenAIStreamAdapterResult,\n OpenAIStreamAdapterStatus,\n OpenAIStreamSource,\n} from './types';\n\n/** Single non-streaming response → TChatMessage[]. For streaming use useOpenAIStreamAdapter. */\n\n/**\n * Index of the assistant message that should receive streamed content for `messageId`.\n * Uses an exact id match when present; otherwise the last assistant (current streaming message),\n * which covers events that reference an id before our list has caught up.\n *\n * @param messages - Current chat messages while streaming.\n * @param messageId - Target assistant id from the stream event.\n * @returns Index of the assistant to update, or `-1` if there is no assistant.\n */\nfunction resolveStreamContentTargetIndex(messages: TChatMessage[], messageId: string): number {\n const byId = messages.findIndex((m) => m.id === messageId && m.role === 'assistant');\n if (byId >= 0) {\n return byId;\n }\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === 'assistant') {\n return i;\n }\n }\n return -1;\n}\n\nfunction getResponseIdFromEvent(event: OpenAIStreamEventLike): string | null {\n const response = (event as {response?: unknown}).response;\n if (!response || typeof response !== 'object') {\n return null;\n }\n\n const id = (response as {id?: unknown}).id;\n return typeof id === 'string' ? id : null;\n}\n\nexport function useOpenAIResponsesAdapter(response: OpenAIResponseLike | null): TChatMessage[] {\n return useMemo(() => openAIResponseToMessages(response), [response]);\n}\n\nexport function useOpenAIStreamAdapter(\n stream: OpenAIStreamSource | null,\n options: OpenAIStreamAdapterOptions = {},\n): OpenAIStreamAdapterResult {\n const {\n initialMessages = [],\n assistantMessageId: optionId,\n onStreamEnd,\n trackTokenUsage = false,\n } = options;\n const onStreamEndRef = useRef(onStreamEnd);\n onStreamEndRef.current = onStreamEnd;\n const initialMessagesRef = useRef(initialMessages);\n initialMessagesRef.current = initialMessages;\n\n // messageId → output_tokens from response.completed event\n const usageMapRef = useRef<Map<string, number>>(new Map());\n\n const [messages, setMessages] = useState<TChatMessage[]>(initialMessages);\n const [status, setStatus] = useState<OpenAIStreamAdapterResult['status']>('ready');\n const [error, setError] = useState<Error | null>(null);\n const [responseId, setResponseId] = useState<string | null>(null);\n\n const assistantMessageId = useMemo(() => optionId ?? `assistant-${Date.now()}`, [optionId]);\n\n useEffect(() => {\n if (!stream) {\n setStatus('ready');\n setError(null);\n setResponseId(null);\n return undefined;\n }\n\n const sourceStream: AsyncIterable<OpenAIStreamEventLike> = isFetchResponse(stream)\n ? fetchResponseToStreamEvents(stream)\n : stream;\n\n let cancelled = false;\n setStatus('streaming');\n setError(null);\n setResponseId(null);\n usageMapRef.current = new Map();\n\n const streamToConsume: AsyncIterable<OpenAIStreamEventLike> = {\n async *[Symbol.asyncIterator]() {\n for await (const event of sourceStream) {\n const nextResponseId = getResponseIdFromEvent(event);\n if (!cancelled && nextResponseId) {\n setResponseId((prev) => (prev === nextResponseId ? prev : nextResponseId));\n }\n\n // Extract token usage from response.completed (only when opt-in)\n if (trackTokenUsage) {\n const e = event as Record<string, unknown>;\n const eventType =\n (e.type as string | undefined) ?? (e.event as string | undefined);\n if (eventType === 'response.completed' && e.response) {\n const response = e.response as {\n usage?: {output_tokens?: number};\n output?: Array<{id?: string; type?: string}>;\n };\n const tokens = response.usage?.output_tokens ?? 0;\n if (tokens > 0) {\n (response.output ?? []).forEach(({id, type}) => {\n if (id && type === 'message') {\n usageMapRef.current.set(id, tokens);\n }\n });\n }\n }\n }\n\n yield event;\n }\n },\n };\n\n const baseMessages = initialMessagesRef.current;\n const getAssistantMessageId = () => assistantMessageId;\n\n setMessages([\n ...baseMessages,\n {\n id: getAssistantMessageId(),\n role: 'assistant',\n content: '',\n } as TAssistantMessage,\n ]);\n\n const injectUsageMetadata = (msgs: TChatMessage[]): TChatMessage[] => {\n if (usageMapRef.current.size === 0) return msgs;\n return msgs.map((msg) => {\n if (msg.role === 'assistant' && msg.id && usageMapRef.current.has(msg.id)) {\n return {\n ...msg,\n metadata: {\n ...msg.metadata,\n outputTokens: usageMapRef.current.get(msg.id),\n },\n } as TAssistantMessage;\n }\n return msg;\n });\n };\n\n const callbacks: ConsumeStreamCallbacks = {\n baseMessages,\n getAssistantMessageId,\n // Provisional id from getAssistantMessageId → OpenAI item id once the stream provides it.\n onAssistantMessageIdResolved: (previousId, openaiItemId) => {\n if (cancelled) return;\n setMessages((prev) =>\n prev.map((msg): TChatMessage => {\n if (msg.id === previousId && msg.role === 'assistant') {\n return {...msg, id: openaiItemId} as TAssistantMessage;\n }\n return msg;\n }),\n );\n },\n onContentUpdate: (messageId, content) => {\n if (cancelled) return;\n setMessages((prev) => {\n const targetIndex = resolveStreamContentTargetIndex(prev, messageId);\n if (targetIndex < 0) {\n return prev;\n }\n return prev.map((msg, i) => {\n if (i !== targetIndex) {\n return msg;\n }\n return {...msg, content} as TAssistantMessage;\n });\n });\n },\n onEnd: (finalMessages, s, err) => {\n setStatus(s === 'done' ? 'ready' : s);\n setError(err ?? null);\n const normalized = injectUsageMetadata(finalMessages);\n if (!cancelled) {\n setMessages(normalized);\n }\n onStreamEndRef.current?.(normalized);\n },\n getIsCancelled: () => cancelled,\n };\n\n consumeOpenAIStream(streamToConsume, callbacks);\n\n return () => {\n cancelled = true;\n };\n }, [stream, assistantMessageId]);\n\n return {messages, status, error, responseId};\n}\n"]}
@@ -12,4 +12,5 @@
12
12
  }
13
13
  .g-aikit-submit-button__spinner {
14
14
  --g-color-line-brand: var(--g-color-text-brand-contrast);
15
+ --g-spin-color: var(--g-color-text-brand-contrast);
15
16
  }
@@ -1,4 +1,4 @@
1
- export type ControlSize = 'xs' | 's' | 'm' | 'l' | 'xl';
1
+ export type ControlSize = 'xxs' | 'xs' | 's' | 'm' | 'l' | 'xl';
2
2
  /**
3
3
  * Resolves a control size: an explicit size always wins, otherwise the mobile
4
4
  * default is used in mobile mode and the desktop default elsewhere.
@@ -1 +1 @@
1
- {"version":3,"file":"useMobileControlSize.js","sourceRoot":"../../../src","sources":["hooks/useMobileControlSize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,mBAAmB,CAAC;AAI5C;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAwB,MAK/D;IACG,MAAM,EAAC,IAAI,EAAE,cAAc,EAAE,aAAa,EAAE,QAAQ,EAAC,GAAG,MAAM,CAAC;IAE/D,OAAO,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;AAC/D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAChC,IAAmB,EACnB,cAAiB,EACjB,aAAgB;IAEhB,MAAM,QAAQ,GAAG,SAAS,EAAE,CAAC;IAE7B,OAAO,wBAAwB,CAAC,EAAC,IAAI,EAAE,cAAc,EAAE,aAAa,EAAE,QAAQ,EAAC,CAAC,CAAC;AACrF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAA6B;IAC5D,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACnC,CAAC","sourcesContent":["import {useMobile} from '@gravity-ui/uikit';\n\nexport type ControlSize = 'xs' | 's' | 'm' | 'l' | 'xl';\n\n/**\n * Resolves a control size: an explicit size always wins, otherwise the mobile\n * default is used in mobile mode and the desktop default elsewhere.\n */\nexport function resolveMobileControlSize<T extends ControlSize>(params: {\n size: T | undefined;\n desktopDefault: T;\n mobileDefault: T;\n isMobile: boolean;\n}): T {\n const {size, desktopDefault, mobileDefault, isMobile} = params;\n\n return size ?? (isMobile ? mobileDefault : desktopDefault);\n}\n\n/**\n * Mobile-aware control size, driven by the uikit `MobileProvider` context.\n */\nexport function useMobileControlSize<T extends ControlSize>(\n size: T | undefined,\n desktopDefault: T,\n mobileDefault: T,\n): T {\n const isMobile = useMobile();\n\n return resolveMobileControlSize({size, desktopDefault, mobileDefault, isMobile});\n}\n\n/**\n * Icon size matching the resolved control size.\n */\nexport function getControlIconSize(size: ControlSize | undefined): number {\n return size === 'xl' ? 20 : 16;\n}\n"]}
1
+ {"version":3,"file":"useMobileControlSize.js","sourceRoot":"../../../src","sources":["hooks/useMobileControlSize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,mBAAmB,CAAC;AAM5C;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAwB,MAK/D;IACG,MAAM,EAAC,IAAI,EAAE,cAAc,EAAE,aAAa,EAAE,QAAQ,EAAC,GAAG,MAAM,CAAC;IAE/D,OAAO,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;AAC/D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAChC,IAAmB,EACnB,cAAiB,EACjB,aAAgB;IAEhB,MAAM,QAAQ,GAAG,SAAS,EAAE,CAAC;IAE7B,OAAO,wBAAwB,CAAC,EAAC,IAAI,EAAE,cAAc,EAAE,aAAa,EAAE,QAAQ,EAAC,CAAC,CAAC;AACrF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAA6B;IAC5D,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACnC,CAAC","sourcesContent":["import {useMobile} from '@gravity-ui/uikit';\n\n// `xxs` only exists on `Label`, which uikit 7.48 gained. It is listed here so that the helpers\n// stay usable with the whole `LabelProps['size']` union across the supported uikit range.\nexport type ControlSize = 'xxs' | 'xs' | 's' | 'm' | 'l' | 'xl';\n\n/**\n * Resolves a control size: an explicit size always wins, otherwise the mobile\n * default is used in mobile mode and the desktop default elsewhere.\n */\nexport function resolveMobileControlSize<T extends ControlSize>(params: {\n size: T | undefined;\n desktopDefault: T;\n mobileDefault: T;\n isMobile: boolean;\n}): T {\n const {size, desktopDefault, mobileDefault, isMobile} = params;\n\n return size ?? (isMobile ? mobileDefault : desktopDefault);\n}\n\n/**\n * Mobile-aware control size, driven by the uikit `MobileProvider` context.\n */\nexport function useMobileControlSize<T extends ControlSize>(\n size: T | undefined,\n desktopDefault: T,\n mobileDefault: T,\n): T {\n const isMobile = useMobile();\n\n return resolveMobileControlSize({size, desktopDefault, mobileDefault, isMobile});\n}\n\n/**\n * Icon size matching the resolved control size.\n */\nexport function getControlIconSize(size: ControlSize | undefined): number {\n return size === 'xl' ? 20 : 16;\n}\n"]}
@@ -1 +1 @@
1
- {"version":"2.20.1","type":"module","sideEffects":["*.css","*.scss"]}
1
+ {"version":"2.20.2-beta.1b9a51bfdf2818e8caefe25d7de3af232af29ca8.0","type":"module","sideEffects":["*.css","*.scss"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/aikit",
3
- "version": "2.20.1",
3
+ "version": "2.20.2-beta.1b9a51bfdf2818e8caefe25d7de3af232af29ca8.0",
4
4
  "description": "Gravity UI base kit for building ai assistant chats",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -745,7 +745,7 @@
745
745
  "@gravity-ui/readme-validator": "^1.3.0",
746
746
  "@gravity-ui/stylelint-config": "^4.0.1",
747
747
  "@gravity-ui/tsconfig": "^1.0.0",
748
- "@gravity-ui/uikit": "^7.25.0",
748
+ "@gravity-ui/uikit": "^7.48.3",
749
749
  "@playwright/experimental-ct-react": "^1.56.1",
750
750
  "@playwright/test": "^1.56.1",
751
751
  "@storybook/addon-docs": "^9.0.5",
@@ -1,3 +0,0 @@
1
- import { OpenAIStreamEventLike } from "../types/index.js";
2
- /** output_item.done for item.type === 'message' only; other types (MCP, tool, reasoning) return false. */
3
- export declare function isMessageOutputItemDoneEvent(event: OpenAIStreamEventLike | null | undefined): boolean;
@@ -1,22 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isMessageOutputItemDoneEvent = isMessageOutputItemDoneEvent;
4
- const isOutputItemDoneEvent_1 = require("./isOutputItemDoneEvent.js");
5
- /** output_item.done for item.type === 'message' only; other types (MCP, tool, reasoning) return false. */
6
- function isMessageOutputItemDoneEvent(event) {
7
- var _a, _b, _c;
8
- if (!(0, isOutputItemDoneEvent_1.isOutputItemDoneEvent)(event)) {
9
- return false;
10
- }
11
- const e = event;
12
- const item = (_a = e.item) !== null && _a !== void 0 ? _a : (_b = e.data) === null || _b === void 0 ? void 0 : _b.item;
13
- if (item === undefined) {
14
- if (typeof process !== 'undefined' && ((_c = process.env) === null || _c === void 0 ? void 0 : _c.NODE_ENV) !== 'production') {
15
- /* eslint-disable-next-line no-console */
16
- console.warn('[useOpenAIStreamAdapter] output_item.done event has no item; treating as message done.', event);
17
- }
18
- return true;
19
- }
20
- return item.type === 'message';
21
- }
22
- //# sourceMappingURL=isMessageOutputItemDoneEvent.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"isMessageOutputItemDoneEvent.js","sourceRoot":"../../../../../src","sources":["adapters/openai/helpers/isMessageOutputItemDoneEvent.ts"],"names":[],"mappings":";;AAMA,oEAqBC;AAzBD,sEAA8D;AAE9D,0GAA0G;AAE1G,SAAgB,4BAA4B,CACxC,KAA+C;;IAE/C,IAAI,CAAC,IAAA,6CAAqB,EAAC,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,MAAM,IAAI,GACN,MAAC,CAAC,CAAC,IAAoC,mCACvC,MAAC,CAAC,CAAC,IAA6C,0CAAE,IAAI,CAAC;IAC3D,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACrB,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,CAAA,MAAA,OAAO,CAAC,GAAG,0CAAE,QAAQ,MAAK,YAAY,EAAE,CAAC;YAC3E,yCAAyC;YACzC,OAAO,CAAC,IAAI,CACR,wFAAwF,EACxF,KAAK,CACR,CAAC;QACN,CAAC;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC;AACnC,CAAC","sourcesContent":["import {OpenAIStreamEventLike} from '../types';\n\nimport {isOutputItemDoneEvent} from './isOutputItemDoneEvent';\n\n/** output_item.done for item.type === 'message' only; other types (MCP, tool, reasoning) return false. */\n\nexport function isMessageOutputItemDoneEvent(\n event: OpenAIStreamEventLike | null | undefined,\n): boolean {\n if (!isOutputItemDoneEvent(event)) {\n return false;\n }\n const e = event as Record<string, unknown>;\n const item =\n (e.item as {type?: string} | undefined) ??\n (e.data as {item?: {type?: string}} | undefined)?.item;\n if (item === undefined) {\n if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {\n /* eslint-disable-next-line no-console */\n console.warn(\n '[useOpenAIStreamAdapter] output_item.done event has no item; treating as message done.',\n event,\n );\n }\n return true;\n }\n return item.type === 'message';\n}\n"]}
@@ -1,3 +0,0 @@
1
- import { OpenAIStreamEventLike } from "../types/index.js";
2
- /** output_item.done for item.type === 'message' only; other types (MCP, tool, reasoning) return false. */
3
- export declare function isMessageOutputItemDoneEvent(event: OpenAIStreamEventLike | null | undefined): boolean;
@@ -1,19 +0,0 @@
1
- import { isOutputItemDoneEvent } from "./isOutputItemDoneEvent.js";
2
- /** output_item.done for item.type === 'message' only; other types (MCP, tool, reasoning) return false. */
3
- export function isMessageOutputItemDoneEvent(event) {
4
- var _a, _b, _c;
5
- if (!isOutputItemDoneEvent(event)) {
6
- return false;
7
- }
8
- const e = event;
9
- const item = (_a = e.item) !== null && _a !== void 0 ? _a : (_b = e.data) === null || _b === void 0 ? void 0 : _b.item;
10
- if (item === undefined) {
11
- if (typeof process !== 'undefined' && ((_c = process.env) === null || _c === void 0 ? void 0 : _c.NODE_ENV) !== 'production') {
12
- /* eslint-disable-next-line no-console */
13
- console.warn('[useOpenAIStreamAdapter] output_item.done event has no item; treating as message done.', event);
14
- }
15
- return true;
16
- }
17
- return item.type === 'message';
18
- }
19
- //# sourceMappingURL=isMessageOutputItemDoneEvent.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"isMessageOutputItemDoneEvent.js","sourceRoot":"../../../../../src","sources":["adapters/openai/helpers/isMessageOutputItemDoneEvent.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,qBAAqB,EAAC,mCAAgC;AAE9D,0GAA0G;AAE1G,MAAM,UAAU,4BAA4B,CACxC,KAA+C;;IAE/C,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,MAAM,IAAI,GACN,MAAC,CAAC,CAAC,IAAoC,mCACvC,MAAC,CAAC,CAAC,IAA6C,0CAAE,IAAI,CAAC;IAC3D,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACrB,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,CAAA,MAAA,OAAO,CAAC,GAAG,0CAAE,QAAQ,MAAK,YAAY,EAAE,CAAC;YAC3E,yCAAyC;YACzC,OAAO,CAAC,IAAI,CACR,wFAAwF,EACxF,KAAK,CACR,CAAC;QACN,CAAC;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC;AACnC,CAAC","sourcesContent":["import {OpenAIStreamEventLike} from '../types';\n\nimport {isOutputItemDoneEvent} from './isOutputItemDoneEvent';\n\n/** output_item.done for item.type === 'message' only; other types (MCP, tool, reasoning) return false. */\n\nexport function isMessageOutputItemDoneEvent(\n event: OpenAIStreamEventLike | null | undefined,\n): boolean {\n if (!isOutputItemDoneEvent(event)) {\n return false;\n }\n const e = event as Record<string, unknown>;\n const item =\n (e.item as {type?: string} | undefined) ??\n (e.data as {item?: {type?: string}} | undefined)?.item;\n if (item === undefined) {\n if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {\n /* eslint-disable-next-line no-console */\n console.warn(\n '[useOpenAIStreamAdapter] output_item.done event has no item; treating as message done.',\n event,\n );\n }\n return true;\n }\n return item.type === 'message';\n}\n"]}