@gravity-ui/aikit 2.3.0 → 2.4.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 (56) hide show
  1. package/build/cjs/components/organisms/AssistantMessage/defaultMessageTypeRegistry.js +6 -0
  2. package/build/cjs/components/organisms/AssistantMessage/defaultMessageTypeRegistry.js.map +1 -1
  3. package/build/cjs/hooks/index.d.ts +2 -0
  4. package/build/cjs/hooks/index.js +2 -0
  5. package/build/cjs/hooks/index.js.map +1 -1
  6. package/build/cjs/hooks/useToolResultContinuation.d.ts +31 -0
  7. package/build/cjs/hooks/useToolResultContinuation.js +91 -0
  8. package/build/cjs/hooks/useToolResultContinuation.js.map +1 -0
  9. package/build/cjs/hooks/useToolset.d.ts +24 -0
  10. package/build/cjs/hooks/useToolset.js +26 -0
  11. package/build/cjs/hooks/useToolset.js.map +1 -0
  12. package/build/cjs/package.json +1 -1
  13. package/build/cjs/utils/index.d.ts +1 -0
  14. package/build/cjs/utils/index.js +1 -0
  15. package/build/cjs/utils/index.js.map +1 -1
  16. package/build/cjs/utils/messageTypeRegistry.d.ts +8 -1
  17. package/build/cjs/utils/messageTypeRegistry.js +14 -2
  18. package/build/cjs/utils/messageTypeRegistry.js.map +1 -1
  19. package/build/cjs/utils/toolset/i18n/en.json +3 -0
  20. package/build/cjs/utils/toolset/i18n/index.d.ts +13 -0
  21. package/build/cjs/utils/toolset/i18n/index.js +10 -0
  22. package/build/cjs/utils/toolset/i18n/index.js.map +1 -0
  23. package/build/cjs/utils/toolset/i18n/ru.json +3 -0
  24. package/build/cjs/utils/toolset/index.d.ts +142 -0
  25. package/build/cjs/utils/toolset/index.js +183 -0
  26. package/build/cjs/utils/toolset/index.js.map +1 -0
  27. package/build/esm/components/organisms/AssistantMessage/defaultMessageTypeRegistry.js +6 -0
  28. package/build/esm/components/organisms/AssistantMessage/defaultMessageTypeRegistry.js.map +1 -1
  29. package/build/esm/hooks/index.d.ts +2 -0
  30. package/build/esm/hooks/index.js +2 -0
  31. package/build/esm/hooks/index.js.map +1 -1
  32. package/build/esm/hooks/useToolResultContinuation.d.ts +31 -0
  33. package/build/esm/hooks/useToolResultContinuation.js +87 -0
  34. package/build/esm/hooks/useToolResultContinuation.js.map +1 -0
  35. package/build/esm/hooks/useToolset.d.ts +24 -0
  36. package/build/esm/hooks/useToolset.js +23 -0
  37. package/build/esm/hooks/useToolset.js.map +1 -0
  38. package/build/esm/package.json +1 -1
  39. package/build/esm/utils/index.d.ts +1 -0
  40. package/build/esm/utils/index.js +1 -0
  41. package/build/esm/utils/index.js.map +1 -1
  42. package/build/esm/utils/messageTypeRegistry.d.ts +8 -1
  43. package/build/esm/utils/messageTypeRegistry.js +14 -2
  44. package/build/esm/utils/messageTypeRegistry.js.map +1 -1
  45. package/build/esm/utils/toolset/i18n/en.json +3 -0
  46. package/build/esm/utils/toolset/i18n/index.d.ts +13 -0
  47. package/build/esm/utils/toolset/i18n/index.js +6 -0
  48. package/build/esm/utils/toolset/i18n/index.js.map +1 -0
  49. package/build/esm/utils/toolset/i18n/ru.json +3 -0
  50. package/build/esm/utils/toolset/index.d.ts +142 -0
  51. package/build/esm/utils/toolset/index.js +176 -0
  52. package/build/esm/utils/toolset/index.js.map +1 -0
  53. package/docs/GENUI.md +627 -0
  54. package/docs/HOOKS.md +65 -11
  55. package/llms.txt +2 -1
  56. package/package.json +11 -1
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defineTool = defineTool;
4
+ exports.createToolset = createToolset;
5
+ exports.toolsetToOpenAIDefinitions = toolsetToOpenAIDefinitions;
6
+ exports.createToolsetRenderer = createToolsetRenderer;
7
+ exports.applyToolResult = applyToolResult;
8
+ const jsx_runtime_1 = require("react/jsx-runtime");
9
+ const ToolMessage_1 = require("../../components/organisms/ToolMessage/index.js");
10
+ const messageTypeRegistry_1 = require("../messageTypeRegistry.js");
11
+ const i18n_1 = require("./i18n/index.js");
12
+ /**
13
+ * Wrap a typed tool definition into an erased runtime entry that the
14
+ * toolset renderer can dispatch by `toolName`. The literal `name` is
15
+ * preserved as a type parameter so `createToolset` can derive its keys.
16
+ */
17
+ function defineTool(definition) {
18
+ return {
19
+ name: definition.name,
20
+ description: definition.description,
21
+ parameters: definition.parameters,
22
+ validate: definition.schema.validate,
23
+ Renderer: definition.component,
24
+ execute: ({ args, result, toolCallId }) => definition.execute
25
+ ? definition.execute({
26
+ args: args,
27
+ result: result,
28
+ toolCallId,
29
+ })
30
+ : result,
31
+ };
32
+ }
33
+ /**
34
+ * Build a `Toolset` from a list of `defineTool(...)` results. Keys are
35
+ * derived from `definition.name`, so the call site cannot drift between
36
+ * a literal-object key and the embedded `name`. The return type narrows
37
+ * its keys to the union of literal tool names, giving name-autocomplete
38
+ * on lookups. Throws on duplicates.
39
+ */
40
+ function createToolset(...tools) {
41
+ const result = {};
42
+ for (const tool of tools) {
43
+ if (Object.prototype.hasOwnProperty.call(result, tool.name)) {
44
+ throw new Error(`createToolset: duplicate tool name "${tool.name}"`);
45
+ }
46
+ result[tool.name] = tool;
47
+ }
48
+ return result;
49
+ }
50
+ /**
51
+ * Map a `Toolset` to the OpenAI `tools[]` shape so chat clients can pass
52
+ * tool definitions to the model without reimplementing the conversion.
53
+ */
54
+ function toolsetToOpenAIDefinitions(toolset) {
55
+ return Object.values(toolset).map((tool) => ({
56
+ type: 'function',
57
+ function: {
58
+ name: tool.name,
59
+ description: tool.description,
60
+ parameters: tool.parameters,
61
+ },
62
+ }));
63
+ }
64
+ const OUTCOME_KEYS = new Set(['status', 'result', 'error']);
65
+ function isToolExecutionOutcome(value) {
66
+ if (!value || typeof value !== 'object')
67
+ return false;
68
+ const status = value.status;
69
+ if (status !== 'success' && status !== 'error' && status !== 'cancelled')
70
+ return false;
71
+ // A bare TResult may coincidentally carry a `status` field. Require all
72
+ // own keys to be in {status, result, error} so e.g. `{status: 'success',
73
+ // id: '123'}` is treated as a result, not a (result-less) outcome.
74
+ for (const key of Object.keys(value)) {
75
+ if (!OUTCOME_KEYS.has(key))
76
+ return false;
77
+ }
78
+ return true;
79
+ }
80
+ function normalizeOutcome(value) {
81
+ if (isToolExecutionOutcome(value))
82
+ return value;
83
+ return { status: 'success', result: value };
84
+ }
85
+ /**
86
+ * Build a `MessageRendererRegistry` whose `tool` renderer dispatches
87
+ * by `toolName` into the provided toolset. Unknown tools and invalid
88
+ * args fall back to a generic `<ToolMessage status="error" />`. Errors
89
+ * thrown inside `execute` are surfaced via an `error` outcome.
90
+ */
91
+ function createToolsetRenderer(toolset, options) {
92
+ var _a;
93
+ const { onToolResult } = options;
94
+ const registry = Object.assign({}, ((_a = options.registry) !== null && _a !== void 0 ? _a : (0, messageTypeRegistry_1.createMessageRendererRegistry)()));
95
+ const toolRenderer = {
96
+ component: ({ part }) => {
97
+ const toolPart = part.data;
98
+ const toolDef = toolset[toolPart.toolName];
99
+ if (!toolDef) {
100
+ return ((0, jsx_runtime_1.jsx)(ToolMessage_1.ToolMessage, { toolName: toolPart.toolName, status: "error", expandable: true, initialExpanded: true, bodyContent: (0, i18n_1.i18n)('fallback-unknown-tool', {
101
+ toolName: toolPart.toolName,
102
+ }) }));
103
+ }
104
+ const validation = toolDef.validate(toolPart.args);
105
+ if (!validation.success) {
106
+ return ((0, jsx_runtime_1.jsx)(ToolMessage_1.ToolMessage, { toolName: toolPart.toolName, status: "error", expandable: true, initialExpanded: true, bodyContent: validation.error.message }));
107
+ }
108
+ const submitResult = (result) => {
109
+ Promise.resolve()
110
+ .then(() => toolDef.execute({
111
+ args: validation.data,
112
+ result,
113
+ toolCallId: toolPart.toolCallId,
114
+ }))
115
+ .then((raw) => normalizeOutcome(raw))
116
+ .catch((err) => ({
117
+ status: 'error',
118
+ error: { message: err instanceof Error ? err.message : String(err) },
119
+ }))
120
+ .then((outcome) => {
121
+ onToolResult({
122
+ toolCallId: toolPart.toolCallId,
123
+ toolName: toolPart.toolName,
124
+ status: outcome.status,
125
+ result: outcome.result,
126
+ error: 'error' in outcome ? outcome.error : undefined,
127
+ });
128
+ });
129
+ };
130
+ const { Renderer } = toolDef;
131
+ return ((0, jsx_runtime_1.jsx)(Renderer, { args: validation.data, result: toolPart.result, submitResult: submitResult }));
132
+ },
133
+ };
134
+ (0, messageTypeRegistry_1.registerMessageRenderer)(registry, 'tool', toolRenderer);
135
+ return registry;
136
+ }
137
+ function isToolPartContent(part) {
138
+ return part.type === 'tool' && typeof part.data === 'object' && part.data !== null;
139
+ }
140
+ function toContentParts(content) {
141
+ if (typeof content === 'string') {
142
+ return content ? [{ type: 'text', data: { text: content } }] : [];
143
+ }
144
+ return Array.isArray(content) ? content : [content];
145
+ }
146
+ /**
147
+ * Merge a tool result into the matching `tool` part of the chat history.
148
+ * Honors `event.status` so the part reflects success / error / cancelled.
149
+ * For backward compatibility, a missing `status` is treated as `'success'`.
150
+ * Returns the original array reference when nothing matched, so React state
151
+ * setters can skip needless updates.
152
+ */
153
+ function applyToolResult(messages, event) {
154
+ var _a;
155
+ let changed = false;
156
+ const status = (_a = event.status) !== null && _a !== void 0 ? _a : 'success';
157
+ const isError = status === 'error';
158
+ const next = messages.map((msg) => {
159
+ if (msg.role !== 'assistant')
160
+ return msg;
161
+ const parts = toContentParts(msg.content);
162
+ const hasMatch = parts.some((part) => isToolPartContent(part) && part.data.toolCallId === event.toolCallId);
163
+ if (!hasMatch)
164
+ return msg;
165
+ changed = true;
166
+ const updatedParts = parts.map((part) => {
167
+ var _a;
168
+ if (!isToolPartContent(part) || part.data.toolCallId !== event.toolCallId) {
169
+ return part;
170
+ }
171
+ const nextData = Object.assign(Object.assign({}, part.data), { status, result: event.result });
172
+ if (isError && ((_a = event.error) === null || _a === void 0 ? void 0 : _a.message)) {
173
+ nextData.bodyContent = event.error.message;
174
+ nextData.expandable = true;
175
+ nextData.initialExpanded = true;
176
+ }
177
+ return Object.assign(Object.assign({}, part), { data: nextData });
178
+ });
179
+ return Object.assign(Object.assign({}, msg), { content: updatedParts });
180
+ });
181
+ return changed ? next : messages;
182
+ }
183
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"../../../../src","sources":["utils/toolset/index.tsx"],"names":[],"mappings":"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","sourcesContent":["import type {ComponentType} from 'react';\n\nimport {ToolMessage} from '../../components/organisms/ToolMessage';\nimport type {\n TAssistantMessage,\n TChatMessage,\n TMessageContent,\n ToolMessageContentData,\n} from '../../types/messages';\nimport {\n type MessageRenderer,\n type MessageRendererRegistry,\n createMessageRendererRegistry,\n registerMessageRenderer,\n} from '../messageTypeRegistry';\n\nimport {i18n} from './i18n';\n\nexport type ToolSchemaResult<TArgs> =\n | {success: true; data: TArgs}\n | {success: false; error: {message: string}};\n\nexport type ToolSchema<TArgs> = {\n validate: (input: unknown) => ToolSchemaResult<TArgs>;\n};\n\nexport type ToolComponentProps<TArgs, TResult> = {\n args: TArgs;\n result?: TResult;\n submitResult: (result: TResult) => void;\n};\n\nexport type ToolExecutionStatus = 'success' | 'error' | 'cancelled';\n\n/**\n * Discriminated outcome returned by a tool's `execute` callback.\n * A tool may also return a bare `TResult`; it is then treated as\n * `{status: 'success', result}` to keep simple cases boilerplate-free.\n */\nexport type ToolExecutionOutcome<TResult> =\n | {status: 'success'; result: TResult}\n | {status: 'error'; result?: TResult; error?: {message: string}}\n | {status: 'cancelled'; result?: TResult};\n\nexport type ToolDefinition<TArgs, TResult, TName extends string = string> = {\n name: TName;\n description: string;\n parameters: Record<string, unknown>;\n schema: ToolSchema<TArgs>;\n component: ComponentType<ToolComponentProps<TArgs, TResult>>;\n /**\n * Run after the user submits a result inside the tool component.\n * Return:\n * - `TResult` (or a Promise of one) for a successful execution;\n * - a `ToolExecutionOutcome<TResult>` to explicitly report `error`\n * or `cancelled`. A thrown error is surfaced as `{status: 'error'}`.\n */\n execute?: (params: {\n args: TArgs;\n result: TResult;\n toolCallId: string;\n }) =>\n | TResult\n | ToolExecutionOutcome<TResult>\n | Promise<TResult | ToolExecutionOutcome<TResult>>;\n};\n\nexport type RuntimeToolDefinition<TName extends string = string> = {\n name: TName;\n description: string;\n parameters: Record<string, unknown>;\n validate: (input: unknown) => ToolSchemaResult<unknown>;\n /**\n * Rendered as `<Renderer ... />` JSX. Hooks placed at the top of this\n * component work normally because it is a real React component, not a\n * function called inside another render path.\n */\n Renderer: ComponentType<ToolComponentProps<unknown, unknown>>;\n execute: (params: {\n args: unknown;\n result: unknown;\n toolCallId: string;\n }) =>\n | unknown\n | ToolExecutionOutcome<unknown>\n | Promise<unknown | ToolExecutionOutcome<unknown>>;\n};\n\nexport type Toolset = Record<string, RuntimeToolDefinition>;\n\nexport type ToolPartContentData<TArgs = unknown, TResult = unknown> = ToolMessageContentData & {\n toolCallId: string;\n args?: TArgs;\n result?: TResult;\n};\n\nexport type ToolPartContent<TArgs = unknown, TResult = unknown> = TMessageContent<\n 'tool',\n ToolPartContentData<TArgs, TResult>\n>;\n\nexport type ToolsetResultEvent = {\n toolCallId: string;\n toolName: string;\n status: ToolExecutionStatus;\n result: unknown;\n error?: {message: string};\n};\n\n/**\n * Wrap a typed tool definition into an erased runtime entry that the\n * toolset renderer can dispatch by `toolName`. The literal `name` is\n * preserved as a type parameter so `createToolset` can derive its keys.\n */\nexport function defineTool<TArgs, TResult, const TName extends string>(\n definition: ToolDefinition<TArgs, TResult, TName>,\n): RuntimeToolDefinition<TName> {\n return {\n name: definition.name,\n description: definition.description,\n parameters: definition.parameters,\n validate: definition.schema.validate as (input: unknown) => ToolSchemaResult<unknown>,\n Renderer: definition.component as ComponentType<ToolComponentProps<unknown, unknown>>,\n execute: ({args, result, toolCallId}) =>\n definition.execute\n ? definition.execute({\n args: args as TArgs,\n result: result as TResult,\n toolCallId,\n })\n : result,\n };\n}\n\n/**\n * Build a `Toolset` from a list of `defineTool(...)` results. Keys are\n * derived from `definition.name`, so the call site cannot drift between\n * a literal-object key and the embedded `name`. The return type narrows\n * its keys to the union of literal tool names, giving name-autocomplete\n * on lookups. Throws on duplicates.\n */\nexport function createToolset<const T extends readonly RuntimeToolDefinition<string>[]>(\n ...tools: T\n): {[K in T[number]['name']]: RuntimeToolDefinition<T[number]['name']>} {\n const result: Record<string, RuntimeToolDefinition> = {};\n for (const tool of tools) {\n if (Object.prototype.hasOwnProperty.call(result, tool.name)) {\n throw new Error(`createToolset: duplicate tool name \"${tool.name}\"`);\n }\n result[tool.name] = tool;\n }\n return result as {[K in T[number]['name']]: RuntimeToolDefinition<T[number]['name']>};\n}\n\n/**\n * Map a `Toolset` to the OpenAI `tools[]` shape so chat clients can pass\n * tool definitions to the model without reimplementing the conversion.\n */\nexport function toolsetToOpenAIDefinitions(toolset: Toolset): Array<{\n type: 'function';\n function: {name: string; description: string; parameters: Record<string, unknown>};\n}> {\n return Object.values(toolset).map((tool) => ({\n type: 'function' as const,\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n },\n }));\n}\n\nexport type CreateToolsetRendererOptions = {\n onToolResult: (event: ToolsetResultEvent) => void;\n /**\n * Existing registry whose entries should be preserved. A shallow copy\n * is taken; the input reference is never mutated.\n */\n registry?: MessageRendererRegistry;\n};\n\nconst OUTCOME_KEYS = new Set(['status', 'result', 'error']);\n\nfunction isToolExecutionOutcome<TResult>(value: unknown): value is ToolExecutionOutcome<TResult> {\n if (!value || typeof value !== 'object') return false;\n const status = (value as {status?: unknown}).status;\n if (status !== 'success' && status !== 'error' && status !== 'cancelled') return false;\n // A bare TResult may coincidentally carry a `status` field. Require all\n // own keys to be in {status, result, error} so e.g. `{status: 'success',\n // id: '123'}` is treated as a result, not a (result-less) outcome.\n for (const key of Object.keys(value)) {\n if (!OUTCOME_KEYS.has(key)) return false;\n }\n return true;\n}\n\nfunction normalizeOutcome<TResult>(value: unknown): ToolExecutionOutcome<TResult> {\n if (isToolExecutionOutcome<TResult>(value)) return value;\n return {status: 'success', result: value as TResult};\n}\n\n/**\n * Build a `MessageRendererRegistry` whose `tool` renderer dispatches\n * by `toolName` into the provided toolset. Unknown tools and invalid\n * args fall back to a generic `<ToolMessage status=\"error\" />`. Errors\n * thrown inside `execute` are surfaced via an `error` outcome.\n */\nexport function createToolsetRenderer(\n toolset: Toolset,\n options: CreateToolsetRendererOptions,\n): MessageRendererRegistry {\n const {onToolResult} = options;\n const registry: MessageRendererRegistry = {\n ...(options.registry ?? createMessageRendererRegistry()),\n };\n\n const toolRenderer: MessageRenderer<ToolPartContent> = {\n component: ({part}) => {\n const toolPart = part.data;\n const toolDef = toolset[toolPart.toolName];\n\n if (!toolDef) {\n return (\n <ToolMessage\n toolName={toolPart.toolName}\n status=\"error\"\n expandable\n initialExpanded\n bodyContent={i18n('fallback-unknown-tool', {\n toolName: toolPart.toolName,\n })}\n />\n );\n }\n\n const validation = toolDef.validate(toolPart.args);\n if (!validation.success) {\n return (\n <ToolMessage\n toolName={toolPart.toolName}\n status=\"error\"\n expandable\n initialExpanded\n bodyContent={validation.error.message}\n />\n );\n }\n\n const submitResult = (result: unknown) => {\n Promise.resolve()\n .then(() =>\n toolDef.execute({\n args: validation.data,\n result,\n toolCallId: toolPart.toolCallId,\n }),\n )\n .then((raw) => normalizeOutcome(raw))\n .catch(\n (err): ToolExecutionOutcome<unknown> => ({\n status: 'error',\n error: {message: err instanceof Error ? err.message : String(err)},\n }),\n )\n .then((outcome) => {\n onToolResult({\n toolCallId: toolPart.toolCallId,\n toolName: toolPart.toolName,\n status: outcome.status,\n result: outcome.result,\n error: 'error' in outcome ? outcome.error : undefined,\n });\n });\n };\n\n const {Renderer} = toolDef;\n return (\n <Renderer\n args={validation.data}\n result={toolPart.result}\n submitResult={submitResult}\n />\n );\n },\n };\n\n registerMessageRenderer<ToolPartContent>(registry, 'tool', toolRenderer);\n\n return registry;\n}\n\nfunction isToolPartContent(part: TMessageContent): part is ToolPartContent {\n return part.type === 'tool' && typeof part.data === 'object' && part.data !== null;\n}\n\nfunction toContentParts<TCustom extends TMessageContent>(\n content: TAssistantMessage<TCustom>['content'],\n): TMessageContent[] {\n if (typeof content === 'string') {\n return content ? [{type: 'text', data: {text: content}}] : [];\n }\n return Array.isArray(content) ? (content as TMessageContent[]) : [content as TMessageContent];\n}\n\n/**\n * Merge a tool result into the matching `tool` part of the chat history.\n * Honors `event.status` so the part reflects success / error / cancelled.\n * For backward compatibility, a missing `status` is treated as `'success'`.\n * Returns the original array reference when nothing matched, so React state\n * setters can skip needless updates.\n */\nexport function applyToolResult<TCustom extends TMessageContent = never>(\n messages: TChatMessage<TCustom>[],\n event: ToolsetResultEvent,\n): TChatMessage<TCustom>[] {\n let changed = false;\n const status: ToolExecutionStatus = event.status ?? 'success';\n const isError = status === 'error';\n\n const next = messages.map((msg): TChatMessage<TCustom> => {\n if (msg.role !== 'assistant') return msg;\n\n const parts = toContentParts<TCustom>(msg.content);\n const hasMatch = parts.some(\n (part) => isToolPartContent(part) && part.data.toolCallId === event.toolCallId,\n );\n if (!hasMatch) return msg;\n\n changed = true;\n const updatedParts = parts.map((part) => {\n if (!isToolPartContent(part) || part.data.toolCallId !== event.toolCallId) {\n return part;\n }\n const nextData: ToolPartContentData = {\n ...part.data,\n status,\n result: event.result,\n };\n if (isError && event.error?.message) {\n nextData.bodyContent = event.error.message;\n nextData.expandable = true;\n nextData.initialExpanded = true;\n }\n return {\n ...part,\n data: nextData,\n };\n });\n\n return {\n ...msg,\n content: updatedParts as TAssistantMessage<TCustom>['content'],\n };\n });\n\n return changed ? next : messages;\n}\n"]}
@@ -7,12 +7,18 @@ export function createDefaultMessageRegistry(transformOptions, shouldParseIncomp
7
7
  const registry = createMessageRendererRegistry();
8
8
  registerMessageRenderer(registry, 'text', {
9
9
  component: ({ part }) => (_jsx(MarkdownRenderer, { content: part.data.text, transformOptions: transformOptions, shouldParseIncompleteMarkdown: shouldParseIncompleteMarkdown })),
10
+ }, {
11
+ isDefault: true,
10
12
  });
11
13
  registerMessageRenderer(registry, 'tool', {
12
14
  component: ({ part }) => _jsx(ToolMessage, Object.assign({}, part.data)),
15
+ }, {
16
+ isDefault: true,
13
17
  });
14
18
  registerMessageRenderer(registry, 'thinking', {
15
19
  component: ({ part }) => _jsx(ThinkingMessage, Object.assign({}, part.data)),
20
+ }, {
21
+ isDefault: true,
16
22
  });
17
23
  return registry;
18
24
  }
@@ -1 +1 @@
1
- {"version":3,"file":"defaultMessageTypeRegistry.js","sourceRoot":"../../../../../src","sources":["components/organisms/AssistantMessage/defaultMessageTypeRegistry.tsx"],"names":[],"mappings":";AAOA,OAAO,EAEH,6BAA6B,EAC7B,uBAAuB,GAC1B,8CAA2C;AAC5C,OAAO,EAAC,gBAAgB,EAAC,8CAAqC;AAC9D,OAAO,EAAC,eAAe,EAAC,oCAA2B;AACnD,OAAO,EAAC,WAAW,EAAC,gCAAuB;AAE3C,MAAM,UAAU,4BAA4B,CACxC,gBAA8B,EAC9B,6BAAuC;IAEvC,MAAM,QAAQ,GAAG,6BAA6B,EAAE,CAAC;IAEjD,uBAAuB,CAAqB,QAAQ,EAAE,MAAM,EAAE;QAC1D,SAAS,EAAE,CAAC,EAAC,IAAI,EAAC,EAAE,EAAE,CAAC,CACnB,KAAC,gBAAgB,IACb,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EACvB,gBAAgB,EAAE,gBAAgB,EAClC,6BAA6B,EAAE,6BAA6B,GAC9D,CACL;KACJ,CAAC,CAAC;IAEH,uBAAuB,CAAqB,QAAQ,EAAE,MAAM,EAAE;QAC1D,SAAS,EAAE,CAAC,EAAC,IAAI,EAAC,EAAE,EAAE,CAAC,KAAC,WAAW,oBAAK,IAAI,CAAC,IAAI,EAAI;KACxD,CAAC,CAAC;IAEH,uBAAuB,CAAyB,QAAQ,EAAE,UAAU,EAAE;QAClE,SAAS,EAAE,CAAC,EAAC,IAAI,EAAC,EAAE,EAAE,CAAC,KAAC,eAAe,oBAAK,IAAI,CAAC,IAAI,EAAI;KAC5D,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC;AACpB,CAAC","sourcesContent":["import type {OptionsType} from '@diplodoc/transform/lib/typings';\n\nimport type {\n TextMessageContent,\n ThinkingMessageContent,\n ToolMessageContent,\n} from '../../../types/messages';\nimport {\n type MessageRendererRegistry,\n createMessageRendererRegistry,\n registerMessageRenderer,\n} from '../../../utils/messageTypeRegistry';\nimport {MarkdownRenderer} from '../../atoms/MarkdownRenderer';\nimport {ThinkingMessage} from '../ThinkingMessage';\nimport {ToolMessage} from '../ToolMessage';\n\nexport function createDefaultMessageRegistry(\n transformOptions?: OptionsType,\n shouldParseIncompleteMarkdown?: boolean,\n): MessageRendererRegistry {\n const registry = createMessageRendererRegistry();\n\n registerMessageRenderer<TextMessageContent>(registry, 'text', {\n component: ({part}) => (\n <MarkdownRenderer\n content={part.data.text}\n transformOptions={transformOptions}\n shouldParseIncompleteMarkdown={shouldParseIncompleteMarkdown}\n />\n ),\n });\n\n registerMessageRenderer<ToolMessageContent>(registry, 'tool', {\n component: ({part}) => <ToolMessage {...part.data} />,\n });\n\n registerMessageRenderer<ThinkingMessageContent>(registry, 'thinking', {\n component: ({part}) => <ThinkingMessage {...part.data} />,\n });\n\n return registry;\n}\n"]}
1
+ {"version":3,"file":"defaultMessageTypeRegistry.js","sourceRoot":"../../../../../src","sources":["components/organisms/AssistantMessage/defaultMessageTypeRegistry.tsx"],"names":[],"mappings":";AAOA,OAAO,EAEH,6BAA6B,EAC7B,uBAAuB,GAC1B,8CAA2C;AAC5C,OAAO,EAAC,gBAAgB,EAAC,8CAAqC;AAC9D,OAAO,EAAC,eAAe,EAAC,oCAA2B;AACnD,OAAO,EAAC,WAAW,EAAC,gCAAuB;AAE3C,MAAM,UAAU,4BAA4B,CACxC,gBAA8B,EAC9B,6BAAuC;IAEvC,MAAM,QAAQ,GAAG,6BAA6B,EAAE,CAAC;IAEjD,uBAAuB,CACnB,QAAQ,EACR,MAAM,EACN;QACI,SAAS,EAAE,CAAC,EAAC,IAAI,EAAC,EAAE,EAAE,CAAC,CACnB,KAAC,gBAAgB,IACb,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EACvB,gBAAgB,EAAE,gBAAgB,EAClC,6BAA6B,EAAE,6BAA6B,GAC9D,CACL;KACJ,EACD;QACI,SAAS,EAAE,IAAI;KAClB,CACJ,CAAC;IAEF,uBAAuB,CACnB,QAAQ,EACR,MAAM,EACN;QACI,SAAS,EAAE,CAAC,EAAC,IAAI,EAAC,EAAE,EAAE,CAAC,KAAC,WAAW,oBAAK,IAAI,CAAC,IAAI,EAAI;KACxD,EACD;QACI,SAAS,EAAE,IAAI;KAClB,CACJ,CAAC;IAEF,uBAAuB,CACnB,QAAQ,EACR,UAAU,EACV;QACI,SAAS,EAAE,CAAC,EAAC,IAAI,EAAC,EAAE,EAAE,CAAC,KAAC,eAAe,oBAAK,IAAI,CAAC,IAAI,EAAI;KAC5D,EACD;QACI,SAAS,EAAE,IAAI;KAClB,CACJ,CAAC;IAEF,OAAO,QAAQ,CAAC;AACpB,CAAC","sourcesContent":["import type {OptionsType} from '@diplodoc/transform/lib/typings';\n\nimport type {\n TextMessageContent,\n ThinkingMessageContent,\n ToolMessageContent,\n} from '../../../types/messages';\nimport {\n type MessageRendererRegistry,\n createMessageRendererRegistry,\n registerMessageRenderer,\n} from '../../../utils/messageTypeRegistry';\nimport {MarkdownRenderer} from '../../atoms/MarkdownRenderer';\nimport {ThinkingMessage} from '../ThinkingMessage';\nimport {ToolMessage} from '../ToolMessage';\n\nexport function createDefaultMessageRegistry(\n transformOptions?: OptionsType,\n shouldParseIncompleteMarkdown?: boolean,\n): MessageRendererRegistry {\n const registry = createMessageRendererRegistry();\n\n registerMessageRenderer<TextMessageContent>(\n registry,\n 'text',\n {\n component: ({part}) => (\n <MarkdownRenderer\n content={part.data.text}\n transformOptions={transformOptions}\n shouldParseIncompleteMarkdown={shouldParseIncompleteMarkdown}\n />\n ),\n },\n {\n isDefault: true,\n },\n );\n\n registerMessageRenderer<ToolMessageContent>(\n registry,\n 'tool',\n {\n component: ({part}) => <ToolMessage {...part.data} />,\n },\n {\n isDefault: true,\n },\n );\n\n registerMessageRenderer<ThinkingMessageContent>(\n registry,\n 'thinking',\n {\n component: ({part}) => <ThinkingMessage {...part.data} />,\n },\n {\n isDefault: true,\n },\n );\n\n return registry;\n}\n"]}
@@ -5,3 +5,5 @@ export * from "./useScrollPreservation.js";
5
5
  export * from "./useAutoCollapseOnSuccess.js";
6
6
  export * from "./useAutoCollapseOnCancelled.js";
7
7
  export * from "./useFileUploadStore.js";
8
+ export * from "./useToolset.js";
9
+ export * from "./useToolResultContinuation.js";
@@ -5,4 +5,6 @@ export * from "./useScrollPreservation.js";
5
5
  export * from "./useAutoCollapseOnSuccess.js";
6
6
  export * from "./useAutoCollapseOnCancelled.js";
7
7
  export * from "./useFileUploadStore.js";
8
+ export * from "./useToolset.js";
9
+ export * from "./useToolResultContinuation.js";
8
10
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"../../../src","sources":["hooks/index.ts"],"names":[],"mappings":"AAAA,4CAAmC;AACnC,oCAAiC;AACjC,oCAAiC;AACjC,2CAAwC;AACxC,8CAA2C;AAC3C,gDAA6C;AAC7C,wCAAqC","sourcesContent":["export * from './useDateFormatter';\nexport * from './useToolMessage';\nexport * from './useSmartScroll';\nexport * from './useScrollPreservation';\nexport * from './useAutoCollapseOnSuccess';\nexport * from './useAutoCollapseOnCancelled';\nexport * from './useFileUploadStore';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"../../../src","sources":["hooks/index.ts"],"names":[],"mappings":"AAAA,4CAAmC;AACnC,oCAAiC;AACjC,oCAAiC;AACjC,2CAAwC;AACxC,8CAA2C;AAC3C,gDAA6C;AAC7C,wCAAqC;AACrC,gCAA6B;AAC7B,+CAA4C","sourcesContent":["export * from './useDateFormatter';\nexport * from './useToolMessage';\nexport * from './useSmartScroll';\nexport * from './useScrollPreservation';\nexport * from './useAutoCollapseOnSuccess';\nexport * from './useAutoCollapseOnCancelled';\nexport * from './useFileUploadStore';\nexport * from './useToolset';\nexport * from './useToolResultContinuation';\n"]}
@@ -0,0 +1,31 @@
1
+ import type { TChatMessage, TMessageContent } from "../types/messages.js";
2
+ import { type ToolExecutionStatus } from "../utils/toolset/index.js";
3
+ export type ToolSettledEvent<TCustom extends TMessageContent = never> = {
4
+ toolCallId: string;
5
+ toolName: string;
6
+ status: ToolExecutionStatus;
7
+ messages: TChatMessage<TCustom>[];
8
+ };
9
+ export type UseToolResultContinuationOptions<TCustom extends TMessageContent = never> = {
10
+ messages: TChatMessage<TCustom>[];
11
+ /**
12
+ * Called once per tool that transitioned from a pending status
13
+ * (`waitingConfirmation`, `waitingSubmission`, `loading`) to a terminal
14
+ * one (`success`, `error`, `cancelled`). Typical use: forward the
15
+ * updated transcript to the model.
16
+ */
17
+ onSettled: (event: ToolSettledEvent<TCustom>) => void;
18
+ };
19
+ /**
20
+ * Pure: returns events for every tool whose status was pending in `prev`
21
+ * and is terminal in `next`. Tools that are already terminal in `prev`
22
+ * (replay/restore) do not produce events.
23
+ */
24
+ export declare function findNewlySettledTools<TCustom extends TMessageContent = never>(prev: TChatMessage<TCustom>[], next: TChatMessage<TCustom>[]): ToolSettledEvent<TCustom>[];
25
+ /**
26
+ * Observe `messages` and fire `onSettled` once whenever a tool part
27
+ * transitions from pending to terminal. Tools already terminal on the
28
+ * first observed snapshot do not fire — replay/restore is intentionally
29
+ * silent. Pair with `useToolset` to forward results to the model.
30
+ */
31
+ export declare function useToolResultContinuation<TCustom extends TMessageContent = never>(options: UseToolResultContinuationOptions<TCustom>): void;
@@ -0,0 +1,87 @@
1
+ import { useEffect, useRef } from 'react';
2
+ const PENDING_STATUSES = new Set([
3
+ 'waitingConfirmation',
4
+ 'waitingSubmission',
5
+ 'loading',
6
+ ]);
7
+ const TERMINAL_STATUSES = new Set([
8
+ 'success',
9
+ 'error',
10
+ 'cancelled',
11
+ ]);
12
+ function isPending(status) {
13
+ return status !== undefined && PENDING_STATUSES.has(status);
14
+ }
15
+ function isTerminal(status) {
16
+ return status !== undefined && TERMINAL_STATUSES.has(status);
17
+ }
18
+ function isToolPartContent(part) {
19
+ return (part.type === 'tool' &&
20
+ typeof part.data === 'object' &&
21
+ part.data !== null &&
22
+ 'toolCallId' in part.data);
23
+ }
24
+ function toContentParts(content) {
25
+ if (typeof content === 'string')
26
+ return [];
27
+ return Array.isArray(content) ? content : [content];
28
+ }
29
+ function collectToolPartsByCallId(messages) {
30
+ const map = new Map();
31
+ for (const msg of messages) {
32
+ if (msg.role !== 'assistant')
33
+ continue;
34
+ for (const part of toContentParts(msg.content)) {
35
+ if (!part || typeof part !== 'object')
36
+ continue;
37
+ if (!isToolPartContent(part))
38
+ continue;
39
+ map.set(part.data.toolCallId, part.data);
40
+ }
41
+ }
42
+ return map;
43
+ }
44
+ /**
45
+ * Pure: returns events for every tool whose status was pending in `prev`
46
+ * and is terminal in `next`. Tools that are already terminal in `prev`
47
+ * (replay/restore) do not produce events.
48
+ */
49
+ export function findNewlySettledTools(prev, next) {
50
+ var _a;
51
+ const prevParts = collectToolPartsByCallId(prev);
52
+ const events = [];
53
+ for (const [toolCallId, data] of collectToolPartsByCallId(next)) {
54
+ const prevStatus = (_a = prevParts.get(toolCallId)) === null || _a === void 0 ? void 0 : _a.status;
55
+ if (isPending(prevStatus) && isTerminal(data.status)) {
56
+ events.push({
57
+ toolCallId,
58
+ toolName: data.toolName,
59
+ status: data.status,
60
+ messages: next,
61
+ });
62
+ }
63
+ }
64
+ return events;
65
+ }
66
+ /**
67
+ * Observe `messages` and fire `onSettled` once whenever a tool part
68
+ * transitions from pending to terminal. Tools already terminal on the
69
+ * first observed snapshot do not fire — replay/restore is intentionally
70
+ * silent. Pair with `useToolset` to forward results to the model.
71
+ */
72
+ export function useToolResultContinuation(options) {
73
+ const { messages, onSettled } = options;
74
+ const onSettledRef = useRef(onSettled);
75
+ useEffect(() => {
76
+ onSettledRef.current = onSettled;
77
+ });
78
+ const prevMessagesRef = useRef(messages);
79
+ useEffect(() => {
80
+ const events = findNewlySettledTools(prevMessagesRef.current, messages);
81
+ prevMessagesRef.current = messages;
82
+ for (const event of events) {
83
+ onSettledRef.current(event);
84
+ }
85
+ }, [messages]);
86
+ }
87
+ //# sourceMappingURL=useToolResultContinuation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useToolResultContinuation.js","sourceRoot":"../../../src","sources":["hooks/useToolResultContinuation.ts"],"names":[],"mappings":"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","sourcesContent":["import {useEffect, useRef} from 'react';\n\nimport type {TChatMessage, TMessageContent} from '../types/messages';\nimport type {TToolStatus} from '../types/tool';\nimport {\n type ToolExecutionStatus,\n type ToolPartContent,\n type ToolPartContentData,\n} from '../utils/toolset';\n\nexport type ToolSettledEvent<TCustom extends TMessageContent = never> = {\n toolCallId: string;\n toolName: string;\n status: ToolExecutionStatus;\n messages: TChatMessage<TCustom>[];\n};\n\nexport type UseToolResultContinuationOptions<TCustom extends TMessageContent = never> = {\n messages: TChatMessage<TCustom>[];\n /**\n * Called once per tool that transitioned from a pending status\n * (`waitingConfirmation`, `waitingSubmission`, `loading`) to a terminal\n * one (`success`, `error`, `cancelled`). Typical use: forward the\n * updated transcript to the model.\n */\n onSettled: (event: ToolSettledEvent<TCustom>) => void;\n};\n\nconst PENDING_STATUSES: ReadonlySet<TToolStatus> = new Set<TToolStatus>([\n 'waitingConfirmation',\n 'waitingSubmission',\n 'loading',\n]);\n\nconst TERMINAL_STATUSES: ReadonlySet<TToolStatus> = new Set<TToolStatus>([\n 'success',\n 'error',\n 'cancelled',\n]);\n\nfunction isPending(status: TToolStatus | undefined): boolean {\n return status !== undefined && PENDING_STATUSES.has(status);\n}\n\nfunction isTerminal(status: TToolStatus | undefined): status is ToolExecutionStatus {\n return status !== undefined && TERMINAL_STATUSES.has(status);\n}\n\nfunction isToolPartContent(part: TMessageContent): part is ToolPartContent {\n return (\n part.type === 'tool' &&\n typeof part.data === 'object' &&\n part.data !== null &&\n 'toolCallId' in part.data\n );\n}\n\nfunction toContentParts<TCustom extends TMessageContent>(\n content: TChatMessage<TCustom>['content'],\n): TMessageContent[] {\n if (typeof content === 'string') return [];\n return Array.isArray(content) ? content : [content];\n}\n\nfunction collectToolPartsByCallId<TCustom extends TMessageContent>(\n messages: TChatMessage<TCustom>[],\n): Map<string, ToolPartContentData> {\n const map = new Map<string, ToolPartContentData>();\n for (const msg of messages) {\n if (msg.role !== 'assistant') continue;\n for (const part of toContentParts(msg.content)) {\n if (!part || typeof part !== 'object') continue;\n if (!isToolPartContent(part)) continue;\n map.set(part.data.toolCallId, part.data);\n }\n }\n return map;\n}\n\n/**\n * Pure: returns events for every tool whose status was pending in `prev`\n * and is terminal in `next`. Tools that are already terminal in `prev`\n * (replay/restore) do not produce events.\n */\nexport function findNewlySettledTools<TCustom extends TMessageContent = never>(\n prev: TChatMessage<TCustom>[],\n next: TChatMessage<TCustom>[],\n): ToolSettledEvent<TCustom>[] {\n const prevParts = collectToolPartsByCallId(prev);\n const events: ToolSettledEvent<TCustom>[] = [];\n for (const [toolCallId, data] of collectToolPartsByCallId(next)) {\n const prevStatus = prevParts.get(toolCallId)?.status;\n if (isPending(prevStatus) && isTerminal(data.status)) {\n events.push({\n toolCallId,\n toolName: data.toolName,\n status: data.status,\n messages: next,\n });\n }\n }\n return events;\n}\n\n/**\n * Observe `messages` and fire `onSettled` once whenever a tool part\n * transitions from pending to terminal. Tools already terminal on the\n * first observed snapshot do not fire — replay/restore is intentionally\n * silent. Pair with `useToolset` to forward results to the model.\n */\nexport function useToolResultContinuation<TCustom extends TMessageContent = never>(\n options: UseToolResultContinuationOptions<TCustom>,\n): void {\n const {messages, onSettled} = options;\n\n const onSettledRef = useRef(onSettled);\n useEffect(() => {\n onSettledRef.current = onSettled;\n });\n\n const prevMessagesRef = useRef(messages);\n\n useEffect(() => {\n const events = findNewlySettledTools(prevMessagesRef.current, messages);\n prevMessagesRef.current = messages;\n for (const event of events) {\n onSettledRef.current(event);\n }\n }, [messages]);\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import type { Dispatch, SetStateAction } from 'react';
2
+ import type { TChatMessage, TMessageContent } from "../types/messages.js";
3
+ import type { MessageRendererRegistry } from "../utils/messageTypeRegistry.js";
4
+ import { type Toolset, type ToolsetResultEvent } from "../utils/toolset/index.js";
5
+ export type UseToolsetOptions<TCustom extends TMessageContent = never> = {
6
+ toolset: Toolset;
7
+ setMessages: Dispatch<SetStateAction<TChatMessage<TCustom>[]>>;
8
+ /** Existing registry to extend instead of starting from a fresh one. */
9
+ registry?: MessageRendererRegistry;
10
+ };
11
+ export type UseToolsetReturn = {
12
+ messageRendererRegistry: MessageRendererRegistry;
13
+ handleToolResult: (event: ToolsetResultEvent) => void;
14
+ };
15
+ /**
16
+ * Wire a toolset into the chat: returns a `MessageRendererRegistry` that
17
+ * renders tool parts via the toolset and a `handleToolResult` callback
18
+ * that merges results into history. To react to tool completion (e.g.
19
+ * forward to the model), pair with `useToolResultContinuation`.
20
+ *
21
+ * Pass the same `toolset` reference across renders (e.g. via `useMemo` or
22
+ * `createToolset`) so the registry stays stable.
23
+ */
24
+ export declare function useToolset<TCustom extends TMessageContent = never>(options: UseToolsetOptions<TCustom>): UseToolsetReturn;
@@ -0,0 +1,23 @@
1
+ import { useCallback, useMemo } from 'react';
2
+ import { applyToolResult, createToolsetRenderer, } from "../utils/toolset/index.js";
3
+ /**
4
+ * Wire a toolset into the chat: returns a `MessageRendererRegistry` that
5
+ * renders tool parts via the toolset and a `handleToolResult` callback
6
+ * that merges results into history. To react to tool completion (e.g.
7
+ * forward to the model), pair with `useToolResultContinuation`.
8
+ *
9
+ * Pass the same `toolset` reference across renders (e.g. via `useMemo` or
10
+ * `createToolset`) so the registry stays stable.
11
+ */
12
+ export function useToolset(options) {
13
+ const { toolset, setMessages, registry } = options;
14
+ const handleToolResult = useCallback((event) => {
15
+ setMessages((prev) => applyToolResult(prev, event));
16
+ }, [setMessages]);
17
+ const messageRendererRegistry = useMemo(() => createToolsetRenderer(toolset, {
18
+ onToolResult: handleToolResult,
19
+ registry,
20
+ }), [toolset, handleToolResult, registry]);
21
+ return { messageRendererRegistry, handleToolResult };
22
+ }
23
+ //# sourceMappingURL=useToolset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useToolset.js","sourceRoot":"../../../src","sources":["hooks/useToolset.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAE,OAAO,EAAC,MAAM,OAAO,CAAC;AAK3C,OAAO,EAGH,eAAe,EACf,qBAAqB,GACxB,kCAAyB;AAc1B;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CACtB,OAAmC;IAEnC,MAAM,EAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAC,GAAG,OAAO,CAAC;IAEjD,MAAM,gBAAgB,GAAG,WAAW,CAChC,CAAC,KAAyB,EAAE,EAAE;QAC1B,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IACxD,CAAC,EACD,CAAC,WAAW,CAAC,CAChB,CAAC;IAEF,MAAM,uBAAuB,GAAG,OAAO,CACnC,GAAG,EAAE,CACD,qBAAqB,CAAC,OAAO,EAAE;QAC3B,YAAY,EAAE,gBAAgB;QAC9B,QAAQ;KACX,CAAC,EACN,CAAC,OAAO,EAAE,gBAAgB,EAAE,QAAQ,CAAC,CACxC,CAAC;IAEF,OAAO,EAAC,uBAAuB,EAAE,gBAAgB,EAAC,CAAC;AACvD,CAAC","sourcesContent":["import {useCallback, useMemo} from 'react';\nimport type {Dispatch, SetStateAction} from 'react';\n\nimport type {TChatMessage, TMessageContent} from '../types/messages';\nimport type {MessageRendererRegistry} from '../utils/messageTypeRegistry';\nimport {\n type Toolset,\n type ToolsetResultEvent,\n applyToolResult,\n createToolsetRenderer,\n} from '../utils/toolset';\n\nexport type UseToolsetOptions<TCustom extends TMessageContent = never> = {\n toolset: Toolset;\n setMessages: Dispatch<SetStateAction<TChatMessage<TCustom>[]>>;\n /** Existing registry to extend instead of starting from a fresh one. */\n registry?: MessageRendererRegistry;\n};\n\nexport type UseToolsetReturn = {\n messageRendererRegistry: MessageRendererRegistry;\n handleToolResult: (event: ToolsetResultEvent) => void;\n};\n\n/**\n * Wire a toolset into the chat: returns a `MessageRendererRegistry` that\n * renders tool parts via the toolset and a `handleToolResult` callback\n * that merges results into history. To react to tool completion (e.g.\n * forward to the model), pair with `useToolResultContinuation`.\n *\n * Pass the same `toolset` reference across renders (e.g. via `useMemo` or\n * `createToolset`) so the registry stays stable.\n */\nexport function useToolset<TCustom extends TMessageContent = never>(\n options: UseToolsetOptions<TCustom>,\n): UseToolsetReturn {\n const {toolset, setMessages, registry} = options;\n\n const handleToolResult = useCallback(\n (event: ToolsetResultEvent) => {\n setMessages((prev) => applyToolResult(prev, event));\n },\n [setMessages],\n );\n\n const messageRendererRegistry = useMemo(\n () =>\n createToolsetRenderer(toolset, {\n onToolResult: handleToolResult,\n registry,\n }),\n [toolset, handleToolResult, registry],\n );\n\n return {messageRendererRegistry, handleToolResult};\n}\n"]}
@@ -1 +1 @@
1
- {"version":"2.3.0","type":"module","sideEffects":["*.css","*.scss"]}
1
+ {"version":"2.4.0","type":"module","sideEffects":["*.css","*.scss"]}
@@ -3,3 +3,4 @@ export * from "./messageUtils.js";
3
3
  export * from "./validation.js";
4
4
  export * from "./messageTypeRegistry.js";
5
5
  export * from "./clipboardUtils.js";
6
+ export * from "./toolset/index.js";
@@ -4,4 +4,5 @@ export * from "./messageUtils.js";
4
4
  export * from "./validation.js";
5
5
  export * from "./messageTypeRegistry.js";
6
6
  export * from "./clipboardUtils.js";
7
+ export * from "./toolset/index.js";
7
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"../../../src","sources":["utils/index.ts"],"names":[],"mappings":"AAAA,mBAAmB;AACnB,+BAA4B;AAC5B,kCAA+B;AAC/B,gCAA6B;AAC7B,yCAAsC;AACtC,oCAAiC","sourcesContent":["// Export utilities\nexport * from './chatUtils';\nexport * from './messageUtils';\nexport * from './validation';\nexport * from './messageTypeRegistry';\nexport * from './clipboardUtils';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"../../../src","sources":["utils/index.ts"],"names":[],"mappings":"AAAA,mBAAmB;AACnB,+BAA4B;AAC5B,kCAA+B;AAC/B,gCAA6B;AAC7B,yCAAsC;AACtC,oCAAiC;AACjC,mCAA0B","sourcesContent":["// Export utilities\nexport * from './chatUtils';\nexport * from './messageUtils';\nexport * from './validation';\nexport * from './messageTypeRegistry';\nexport * from './clipboardUtils';\nexport * from './toolset';\n"]}
@@ -8,6 +8,13 @@ export type MessageRenderer<TContent extends TMessageContent = TMessageContent>
8
8
  };
9
9
  export type MessageRendererRegistry = Record<string, MessageRenderer>;
10
10
  export declare function createMessageRendererRegistry(): MessageRendererRegistry;
11
- export declare function registerMessageRenderer<TContent extends TMessageContent>(registry: MessageRendererRegistry, contentType: TContent['type'], renderer: MessageRenderer<TContent>): MessageRendererRegistry;
11
+ export type RegisterMessageRendererOptions = {
12
+ /**
13
+ * Marks a renderer as built-in. Replacing built-in renderers is expected
14
+ * when consumers customize the default registry, so it does not warn.
15
+ */
16
+ isDefault?: boolean;
17
+ };
18
+ export declare function registerMessageRenderer<TContent extends TMessageContent>(registry: MessageRendererRegistry, contentType: TContent['type'], renderer: MessageRenderer<TContent>, options?: RegisterMessageRendererOptions): MessageRendererRegistry;
12
19
  export declare function getMessageRenderer(registry: MessageRendererRegistry, contentType: string): React.ComponentType<MessageContentComponentProps> | undefined;
13
20
  export declare function mergeMessageRendererRegistries(defaultRegistry: MessageRendererRegistry, customRegistry: MessageRendererRegistry): MessageRendererRegistry;
@@ -1,9 +1,21 @@
1
+ const defaultRenderers = new WeakSet();
1
2
  export function createMessageRendererRegistry() {
2
3
  return {};
3
4
  }
4
- export function registerMessageRenderer(registry, contentType, renderer) {
5
+ export function registerMessageRenderer(registry, contentType, renderer, options) {
6
+ const existingRenderer = registry[contentType];
7
+ if (process.env.NODE_ENV !== 'production' &&
8
+ existingRenderer &&
9
+ !defaultRenderers.has(existingRenderer)) {
10
+ // eslint-disable-next-line no-console
11
+ console.warn(`registerMessageRenderer: overwriting existing renderer for content type "${contentType}"`);
12
+ }
13
+ const registeredRenderer = renderer;
14
+ if (options === null || options === void 0 ? void 0 : options.isDefault) {
15
+ defaultRenderers.add(registeredRenderer);
16
+ }
5
17
  Object.assign(registry, {
6
- [contentType]: renderer,
18
+ [contentType]: registeredRenderer,
7
19
  });
8
20
  return registry;
9
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"messageTypeRegistry.js","sourceRoot":"../../../src","sources":["utils/messageTypeRegistry.ts"],"names":[],"mappings":"AAcA,MAAM,UAAU,6BAA6B;IACzC,OAAO,EAAE,CAAC;AACd,CAAC;AAED,MAAM,UAAU,uBAAuB,CACnC,QAAiC,EACjC,WAA6B,EAC7B,QAAmC;IAEnC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE;QACpB,CAAC,WAAW,CAAC,EAAE,QAAsC;KACxD,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAC9B,QAAiC,EACjC,WAAmB;;IAEnB,OAAO,MAAA,QAAQ,CAAC,WAAW,CAAC,0CAAE,SAAS,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,8BAA8B,CAC1C,eAAwC,EACxC,cAAuC;IAEvC,uCACO,eAAe,GACf,cAAc,EACnB;AACN,CAAC","sourcesContent":["import type React from 'react';\n\nimport type {TMessageContent} from '../types/messages';\n\nexport type MessageContentComponentProps<TContent extends TMessageContent = TMessageContent> = {\n part: TContent;\n};\n\nexport type MessageRenderer<TContent extends TMessageContent = TMessageContent> = {\n component: React.ComponentType<MessageContentComponentProps<TContent>>;\n};\n\nexport type MessageRendererRegistry = Record<string, MessageRenderer>;\n\nexport function createMessageRendererRegistry(): MessageRendererRegistry {\n return {};\n}\n\nexport function registerMessageRenderer<TContent extends TMessageContent>(\n registry: MessageRendererRegistry,\n contentType: TContent['type'],\n renderer: MessageRenderer<TContent>,\n): MessageRendererRegistry {\n Object.assign(registry, {\n [contentType]: renderer as unknown as MessageRenderer,\n });\n return registry;\n}\n\nexport function getMessageRenderer(\n registry: MessageRendererRegistry,\n contentType: string,\n): React.ComponentType<MessageContentComponentProps> | undefined {\n return registry[contentType]?.component;\n}\n\nexport function mergeMessageRendererRegistries(\n defaultRegistry: MessageRendererRegistry,\n customRegistry: MessageRendererRegistry,\n): MessageRendererRegistry {\n return {\n ...defaultRegistry,\n ...customRegistry,\n };\n}\n"]}
1
+ {"version":3,"file":"messageTypeRegistry.js","sourceRoot":"../../../src","sources":["utils/messageTypeRegistry.ts"],"names":[],"mappings":"AAcA,MAAM,gBAAgB,GAAG,IAAI,OAAO,EAAmB,CAAC;AAExD,MAAM,UAAU,6BAA6B;IACzC,OAAO,EAAE,CAAC;AACd,CAAC;AAUD,MAAM,UAAU,uBAAuB,CACnC,QAAiC,EACjC,WAA6B,EAC7B,QAAmC,EACnC,OAAwC;IAExC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC/C,IACI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;QACrC,gBAAgB;QAChB,CAAC,gBAAgB,CAAC,GAAG,CAAC,gBAAgB,CAAC,EACzC,CAAC;QACC,sCAAsC;QACtC,OAAO,CAAC,IAAI,CACR,4EAA4E,WAAW,GAAG,CAC7F,CAAC;IACN,CAAC;IACD,MAAM,kBAAkB,GAAG,QAAsC,CAAC;IAClE,IAAI,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,SAAS,EAAE,CAAC;QACrB,gBAAgB,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAC7C,CAAC;IACD,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE;QACpB,CAAC,WAAW,CAAC,EAAE,kBAAkB;KACpC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAC9B,QAAiC,EACjC,WAAmB;;IAEnB,OAAO,MAAA,QAAQ,CAAC,WAAW,CAAC,0CAAE,SAAS,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,8BAA8B,CAC1C,eAAwC,EACxC,cAAuC;IAEvC,uCACO,eAAe,GACf,cAAc,EACnB;AACN,CAAC","sourcesContent":["import type React from 'react';\n\nimport type {TMessageContent} from '../types/messages';\n\nexport type MessageContentComponentProps<TContent extends TMessageContent = TMessageContent> = {\n part: TContent;\n};\n\nexport type MessageRenderer<TContent extends TMessageContent = TMessageContent> = {\n component: React.ComponentType<MessageContentComponentProps<TContent>>;\n};\n\nexport type MessageRendererRegistry = Record<string, MessageRenderer>;\n\nconst defaultRenderers = new WeakSet<MessageRenderer>();\n\nexport function createMessageRendererRegistry(): MessageRendererRegistry {\n return {};\n}\n\nexport type RegisterMessageRendererOptions = {\n /**\n * Marks a renderer as built-in. Replacing built-in renderers is expected\n * when consumers customize the default registry, so it does not warn.\n */\n isDefault?: boolean;\n};\n\nexport function registerMessageRenderer<TContent extends TMessageContent>(\n registry: MessageRendererRegistry,\n contentType: TContent['type'],\n renderer: MessageRenderer<TContent>,\n options?: RegisterMessageRendererOptions,\n): MessageRendererRegistry {\n const existingRenderer = registry[contentType];\n if (\n process.env.NODE_ENV !== 'production' &&\n existingRenderer &&\n !defaultRenderers.has(existingRenderer)\n ) {\n // eslint-disable-next-line no-console\n console.warn(\n `registerMessageRenderer: overwriting existing renderer for content type \"${contentType}\"`,\n );\n }\n const registeredRenderer = renderer as unknown as MessageRenderer;\n if (options?.isDefault) {\n defaultRenderers.add(registeredRenderer);\n }\n Object.assign(registry, {\n [contentType]: registeredRenderer,\n });\n return registry;\n}\n\nexport function getMessageRenderer(\n registry: MessageRendererRegistry,\n contentType: string,\n): React.ComponentType<MessageContentComponentProps> | undefined {\n return registry[contentType]?.component;\n}\n\nexport function mergeMessageRendererRegistries(\n defaultRegistry: MessageRendererRegistry,\n customRegistry: MessageRendererRegistry,\n): MessageRendererRegistry {\n return {\n ...defaultRegistry,\n ...customRegistry,\n };\n}\n"]}
@@ -0,0 +1,3 @@
1
+ {
2
+ "fallback-unknown-tool": "Unknown tool: {{toolName}}"
3
+ }
@@ -0,0 +1,13 @@
1
+ export declare const i18n: ((key: "fallback-unknown-tool", params?: import("@gravity-ui/i18n").Params) => string) & {
2
+ Translation: import("react").ComponentType<{
3
+ children: (props: {
4
+ t: (key: "fallback-unknown-tool", params?: import("@gravity-ui/i18n").Params) => string;
5
+ }) => React.ReactNode;
6
+ }>;
7
+ useTranslation: () => {
8
+ t: (key: "fallback-unknown-tool", params?: import("@gravity-ui/i18n").Params) => string;
9
+ };
10
+ keysetData: {
11
+ "g-aikit-Toolset": Record<"fallback-unknown-tool", import("@gravity-ui/i18n").KeyData>;
12
+ };
13
+ };
@@ -0,0 +1,6 @@
1
+ import { addComponentKeysets } from '@gravity-ui/uikit/i18n';
2
+ import { NAMESPACE } from "../../cn.js";
3
+ import en from "./en.json";
4
+ import ru from "./ru.json";
5
+ export const i18n = addComponentKeysets({ en, ru }, `${NAMESPACE}Toolset`);
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"../../../../../src","sources":["utils/toolset/i18n/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,mBAAmB,EAAC,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAC,SAAS,EAAC,oBAAiB;AAEnC,OAAO,EAAE,kBAAkB;AAC3B,OAAO,EAAE,kBAAkB;AAE3B,MAAM,CAAC,MAAM,IAAI,GAAG,mBAAmB,CAAC,EAAC,EAAE,EAAE,EAAE,EAAC,EAAE,GAAG,SAAS,SAAS,CAAC,CAAC","sourcesContent":["import {addComponentKeysets} from '@gravity-ui/uikit/i18n';\n\nimport {NAMESPACE} from '../../cn';\n\nimport en from './en.json';\nimport ru from './ru.json';\n\nexport const i18n = addComponentKeysets({en, ru}, `${NAMESPACE}Toolset`);\n"]}
@@ -0,0 +1,3 @@
1
+ {
2
+ "fallback-unknown-tool": "Неизвестный инструмент: {{toolName}}"
3
+ }