react-instantsearch-core 7.44.1 → 7.46.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.
@@ -1,4 +1,4 @@
1
- /*! React InstantSearch Core 7.44.1 | © Algolia, Inc. and contributors; MIT License | https://github.com/algolia/instantsearch */
1
+ /*! React InstantSearch Core 7.46.0 | © Algolia, Inc. and contributors; MIT License | https://github.com/algolia/instantsearch */
2
2
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) :
4
4
  typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) :
@@ -24,7 +24,7 @@
24
24
 
25
25
  var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
26
26
 
27
- var version$2 = '7.44.1';
27
+ var version$2 = '7.46.0';
28
28
 
29
29
  function _define_property(obj, key, value) {
30
30
  if (key in obj) {
@@ -9249,7 +9249,7 @@
9249
9249
  });
9250
9250
  }
9251
9251
 
9252
- var version = '4.111.1';
9252
+ var version = '4.113.0';
9253
9253
 
9254
9254
  var ANONYMOUS_TOKEN_COOKIE_KEY = '_ALGOLIA';
9255
9255
  function getCookie(name) {
@@ -12444,75 +12444,98 @@
12444
12444
  return useConnector(connectBreadcrumb, props, additionalWidgetProperties);
12445
12445
  }
12446
12446
 
12447
- var tryParseJson = function tryParseJson(value) {
12448
- try {
12449
- return JSON.parse(value);
12450
- } catch (unused) {
12451
- return undefined;
12452
- }
12447
+ function startsWith(str, prefix) {
12448
+ return str.slice(0, prefix.length) === prefix;
12449
+ }
12450
+
12451
+ var isPartTool = function isPartTool(part) {
12452
+ return startsWith(part.type, 'tool-');
12453
12453
  };
12454
- var repairPartialJson = function repairPartialJson(value) {
12455
- var repaired = value.trim();
12456
- if (!repaired) {
12457
- return repaired;
12454
+ var TOOL_PART_PREFIX = 'tool-';
12455
+ /**
12456
+ * Resolves the tool a message part belongs to, from either a part type
12457
+ * (`tool-algolia_search_index`) or a bare tool name.
12458
+ *
12459
+ * Generic over the tool shape so the renderer, the loader and the connector —
12460
+ * which hold different subsets of the tool contract — all resolve names the same
12461
+ * way.
12462
+ */ var findTool = function findTool(partType, tools) {
12463
+ var toolName = startsWith(partType, TOOL_PART_PREFIX) ? partType.slice(TOOL_PART_PREFIX.length) : partType;
12464
+ if (tools[toolName]) {
12465
+ return tools[toolName];
12458
12466
  }
12459
- var inString = false;
12460
- var isEscaped = false;
12461
- var stack = [];
12462
- for(var index = 0; index < repaired.length; index++){
12463
- var char = repaired[index];
12464
- if (inString) {
12465
- if (isEscaped) {
12466
- isEscaped = false;
12467
- } else if (char === '\\') {
12468
- isEscaped = true;
12469
- } else if (char === '"') {
12470
- inString = false;
12471
- }
12472
- continue;
12473
- }
12474
- if (char === '"') {
12475
- inString = true;
12476
- continue;
12467
+ // Compatibility shim for tool names suffixed by the index name, as the Algolia
12468
+ // MCP Server does (`algolia_search_index_products`). The longest matching key
12469
+ // wins, so registering both `foo` and `foo_bar` resolves `foo_bar_products` to
12470
+ // `foo_bar` otherwise the winner would depend on registration order.
12471
+ var match;
12472
+ Object.keys(tools).forEach(function(key) {
12473
+ if (startsWith(toolName, "".concat(key, "_")) && (match === undefined || key.length > match.length)) {
12474
+ match = key;
12477
12475
  }
12478
- if (char === '{' || char === '[') {
12479
- stack.push(char);
12480
- continue;
12481
- }
12482
- if (char === '}' && stack[stack.length - 1] === '{') {
12483
- stack.pop();
12484
- continue;
12485
- }
12486
- if (char === ']' && stack[stack.length - 1] === '[') {
12487
- stack.pop();
12488
- }
12489
- }
12490
- if (inString && !isEscaped) {
12491
- repaired += '"';
12492
- }
12493
- repaired = repaired.replace(RegExp(",\\s*$", "u"), '');
12494
- if (stack.length > 0) {
12495
- repaired += stack.reverse().map(function(opening) {
12496
- return opening === '{' ? '}' : ']';
12497
- }).join('');
12498
- }
12499
- return repaired.replace(RegExp(",\\s*([}\\]])", "gu"), '$1');
12476
+ });
12477
+ return match === undefined ? undefined : tools[match];
12500
12478
  };
12501
- var parsePartialJson = function parsePartialJson(accumulatedRawJson, fallbackValue) {
12502
- var normalized = accumulatedRawJson.trim();
12503
- if (!normalized) {
12504
- return fallbackValue;
12505
- }
12506
- var directParsed = tryParseJson(normalized);
12507
- if (directParsed !== undefined) {
12508
- return directParsed;
12509
- }
12510
- var repairedParsed = tryParseJson(repairPartialJson(normalized));
12511
- if (repairedParsed !== undefined) {
12512
- return repairedParsed;
12513
- }
12514
- return fallbackValue;
12479
+
12480
+ var createRecords = function createRecords() {
12481
+ return Object.create(null);
12515
12482
  };
12483
+ var hasOwn = function hasOwn(records, objectID) {
12484
+ return Object.prototype.hasOwnProperty.call(records, objectID);
12485
+ };
12486
+ function createChatRecordsStore() {
12487
+ var records = createRecords();
12488
+ return {
12489
+ get: function get(objectID) {
12490
+ return hasOwn(records, objectID) ? records[objectID] : undefined;
12491
+ },
12492
+ has: function has(objectID) {
12493
+ return hasOwn(records, objectID);
12494
+ },
12495
+ getAll: function getAll() {
12496
+ return records;
12497
+ },
12498
+ merge: function merge(contributed) {
12499
+ contributed.forEach(function(record) {
12500
+ if (record && typeof record.objectID === 'string' && record.objectID !== '') {
12501
+ records[record.objectID] = record;
12502
+ }
12503
+ });
12504
+ },
12505
+ clear: function clear() {
12506
+ records = createRecords();
12507
+ }
12508
+ };
12509
+ }
12510
+ /**
12511
+ * Collects into `store` the records of every completed tool call in `messages`
12512
+ * whose output holds `hits`. Matching on the output rather than on a tool name
12513
+ * means any tool that fetches records contributes them by returning them.
12514
+ *
12515
+ * Idempotent, so it can re-run over the whole conversation on every update:
12516
+ * streaming deltas and a conversation restored from storage are one code path.
12517
+ */ function collectChatRecords(messages) {
12518
+ var store = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : createChatRecordsStore();
12519
+ messages === null || messages === void 0 ? void 0 : messages.forEach(function(message) {
12520
+ message.parts.forEach(function(part) {
12521
+ var _part_output;
12522
+ if (!isPartTool(part) || part.state !== 'output-available') {
12523
+ return;
12524
+ }
12525
+ var hits = ((_part_output = part.output) !== null && _part_output !== void 0 ? _part_output : {}).hits;
12526
+ if (Array.isArray(hits)) {
12527
+ store.merge(hits);
12528
+ }
12529
+ });
12530
+ });
12531
+ return store;
12532
+ }
12533
+
12534
+ function flat(arr) {
12535
+ return arr.reduce(function(acc, array) {
12536
+ return acc.concat(array);
12537
+ }, []);
12538
+ }
12516
12539
 
12517
12540
  /**
12518
12541
  * Stream parser for parsing SSE (Server-Sent Events) streams.
@@ -12693,7 +12716,17 @@
12693
12716
  var lastMessage = messages[messages.length - 1];
12694
12717
  if (lastMessage.role !== 'assistant') return false;
12695
12718
  if (!lastMessage.parts || lastMessage.parts.length === 0) return false;
12696
- var toolParts = lastMessage.parts.filter(isToolOrDynamicToolUIPart);
12719
+ // Only the last step of the message counts: earlier steps were answered
12720
+ // before the model was called again, so their resolved tool calls must not
12721
+ // continue the turn a second time.
12722
+ var lastStepStartIndex = lastMessage.parts.reduce(function(lastIndex, part, index) {
12723
+ return part.type === 'step-start' ? index : lastIndex;
12724
+ }, -1);
12725
+ var toolParts = lastMessage.parts.slice(lastStepStartIndex + 1).filter(isToolOrDynamicToolUIPart) // A provider-executed tool call is the provider's to answer, so a turn
12726
+ // made of them is already complete — continuing would resend it.
12727
+ .filter(function(part) {
12728
+ return !part.providerExecuted;
12729
+ });
12697
12730
  if (toolParts.length === 0) return false;
12698
12731
  return toolParts.every(function(part) {
12699
12732
  return part.state === 'output-available' || part.state === 'output-error';
@@ -12794,328 +12827,632 @@
12794
12827
  return undefined;
12795
12828
  }
12796
12829
 
12797
- var _computedKey$1;
12798
- var defaultGuardrailFallbackResponse = 'Sorry, we are not able to generate a response at the moment.';
12799
- _computedKey$1 = /** @internal */ '~addToolResultForMessage';
12800
- var _computedKey1$1 = _computedKey$1;
12801
12830
  /**
12802
- * Abstract base class for chat implementations.
12803
- */ var AbstractChat = /*#__PURE__*/ function() {
12804
- function AbstractChat(param) {
12805
- var _this = this;
12806
- var _param_generateId = param.generateId, generateId$1 = _param_generateId === void 0 ? generateId : _param_generateId, _param_id = param.id, id = _param_id === void 0 ? generateId$1() : _param_id, transport = param.transport, state = param.state, onError = param.onError, onToolCall = param.onToolCall, onFinish = param.onFinish, onData = param.onData, sendAutomaticallyWhen = param.sendAutomaticallyWhen, shouldRepairToolInput = param.shouldRepairToolInput;
12807
- var _this1 = this;
12808
- _class_call_check(this, AbstractChat);
12809
- _define_property(this, "conversationId", void 0);
12810
- _define_property(this, "generateId", void 0);
12811
- _define_property(this, "state", void 0);
12812
- _define_property(this, "transport", void 0);
12813
- _define_property(this, "onError", void 0);
12814
- _define_property(this, "onToolCall", void 0);
12815
- _define_property(this, "onFinish", void 0);
12816
- _define_property(this, "onData", void 0);
12817
- _define_property(this, "sendAutomaticallyWhen", void 0);
12818
- _define_property(this, "shouldRepairToolInput", void 0);
12819
- _define_property(this, "activeResponse", null);
12820
- _define_property(this, "latestResponse", null);
12821
- _define_property(this, "responsesByToolCallId", new Map());
12822
- _define_property(this, "responseByMessage", new WeakMap());
12823
- // Once tool ownership is discarded, an identifier-only result cannot prove
12824
- // which response generation submitted it. Scoped submissions remain safe.
12825
- _define_property(this, "acceptsIdentifierOnlyToolResults", true);
12826
- _define_property(this, "jobExecutor", new SerialJobExecutor());
12827
- /**
12828
- * Appends or replaces a user message to the chat list. This triggers the API call to fetch
12829
- * the assistant's response.
12830
- */ _define_property(this, "sendMessage", function(message, options) {
12831
- return _this.jobExecutor.run(function() {
12832
- // Build the user message
12833
- var userMessagePromise;
12834
- if (message) {
12835
- var messageId = message.messageId || _this.generateId();
12836
- if ('parts' in message && message.parts) {
12837
- // Full message with parts provided
12838
- userMessagePromise = Promise.resolve(_object_spread({
12839
- id: messageId,
12840
- role: 'user'
12841
- }, message));
12842
- } else if ('text' in message && message.text) {
12843
- // Build from text
12844
- var parts = [
12845
- {
12846
- type: 'text',
12847
- text: message.text
12831
+ * Reads a human-readable message from a failed HTTP response body when the
12832
+ * server returns JSON such as `{ "message": "..." }` (the shared
12833
+ * `ErrorResponse` shape used by every status code), falling back to the HTTP
12834
+ * status line when the body is empty or not parseable.
12835
+ */ function getHttpErrorMessage(response) {
12836
+ var fallback = "HTTP error: ".concat(response.status, " ").concat(response.statusText);
12837
+ return response.text().then(function(text) {
12838
+ var _tryParseErrorMessage;
12839
+ return (_tryParseErrorMessage = tryParseErrorMessage(text)) !== null && _tryParseErrorMessage !== void 0 ? _tryParseErrorMessage : fallback;
12840
+ }).catch(function() {
12841
+ return fallback;
12842
+ });
12843
+ }
12844
+ /**
12845
+ * Abstract base class for HTTP-based chat transports.
12846
+ */ var HttpChatTransport = /*#__PURE__*/ function() {
12847
+ function HttpChatTransport(param) {
12848
+ var _param_api = param.api, api = _param_api === void 0 ? '/api/chat' : _param_api, credentials = param.credentials, headers = param.headers, body = param.body, customFetch = param.fetch, prepareSendMessagesRequest = param.prepareSendMessagesRequest, prepareReconnectToStreamRequest = param.prepareReconnectToStreamRequest;
12849
+ _class_call_check(this, HttpChatTransport);
12850
+ _define_property(this, "api", void 0);
12851
+ _define_property(this, "credentials", void 0);
12852
+ _define_property(this, "headers", void 0);
12853
+ _define_property(this, "body", void 0);
12854
+ _define_property(this, "fetch", void 0);
12855
+ _define_property(this, "prepareSendMessagesRequest", void 0);
12856
+ _define_property(this, "prepareReconnectToStreamRequest", void 0);
12857
+ this.api = api;
12858
+ this.credentials = credentials;
12859
+ this.headers = headers;
12860
+ this.body = body;
12861
+ this.fetch = customFetch;
12862
+ this.prepareSendMessagesRequest = prepareSendMessagesRequest;
12863
+ this.prepareReconnectToStreamRequest = prepareReconnectToStreamRequest;
12864
+ }
12865
+ _create_class(HttpChatTransport, [
12866
+ {
12867
+ key: "sendMessages",
12868
+ value: function sendMessages(param) {
12869
+ var _this = this;
12870
+ var abortSignal = param.abortSignal, chatId = param.chatId, messages = param.messages, requestMetadata = param.requestMetadata, trigger = param.trigger, messageId = param.messageId, requestHeaders = param.headers, requestBody = param.body;
12871
+ var _this_fetch;
12872
+ var fetchFn = (_this_fetch = this.fetch) !== null && _this_fetch !== void 0 ? _this_fetch : fetch;
12873
+ // Resolve configurable values
12874
+ return Promise.all([
12875
+ resolveValue(this.credentials),
12876
+ resolveValue(this.headers),
12877
+ resolveValue(this.body)
12878
+ ]).then(function(param) {
12879
+ var _param = _sliced_to_array(param, 3), resolvedCredentials = _param[0], resolvedHeaders = _param[1], resolvedBody = _param[2];
12880
+ // Build default request options
12881
+ var api = _this.api;
12882
+ var body = _object_spread({
12883
+ id: chatId,
12884
+ messages: messages
12885
+ }, resolvedBody, requestBody);
12886
+ var headers = _object_spread({
12887
+ 'Content-Type': 'application/json'
12888
+ }, _instanceof(resolvedHeaders, Headers) ? Object.fromEntries(resolvedHeaders.entries()) : resolvedHeaders, _instanceof(requestHeaders, Headers) ? Object.fromEntries(requestHeaders.entries()) : requestHeaders);
12889
+ var credentials = resolvedCredentials;
12890
+ // Apply custom preparation if provided
12891
+ var prepareRequestBody = _object_spread({}, resolvedBody, requestBody);
12892
+ var preparePromise = _this.prepareSendMessagesRequest ? Promise.resolve(_this.prepareSendMessagesRequest({
12893
+ id: chatId,
12894
+ messages: messages,
12895
+ requestMetadata: requestMetadata,
12896
+ body: prepareRequestBody,
12897
+ credentials: resolvedCredentials,
12898
+ headers: resolvedHeaders,
12899
+ api: _this.api,
12900
+ trigger: trigger,
12901
+ messageId: messageId
12902
+ })) : Promise.resolve(null);
12903
+ return preparePromise.then(function(prepared) {
12904
+ if (prepared) {
12905
+ body = prepared.body;
12906
+ if (prepared.api) api = prepared.api;
12907
+ if (prepared.headers) {
12908
+ headers = _object_spread({
12909
+ 'Content-Type': 'application/json'
12910
+ }, _instanceof(prepared.headers, Headers) ? Object.fromEntries(prepared.headers.entries()) : prepared.headers);
12848
12911
  }
12849
- ];
12850
- // Add file parts if provided
12851
- if (message.files) {
12852
- userMessagePromise = _this.convertFilesToParts(message.files).then(function(fileParts) {
12853
- var _parts;
12854
- (_parts = parts).push.apply(_parts, _to_consumable_array(fileParts));
12855
- return {
12856
- id: messageId,
12857
- role: 'user',
12858
- parts: parts,
12859
- metadata: message.metadata
12860
- };
12861
- });
12862
- } else {
12863
- userMessagePromise = Promise.resolve({
12864
- id: messageId,
12865
- role: 'user',
12866
- parts: parts,
12867
- metadata: message.metadata
12868
- });
12912
+ if (prepared.credentials) credentials = prepared.credentials;
12869
12913
  }
12870
- } else if ('files' in message && message.files) {
12871
- // Files only
12872
- userMessagePromise = _this.convertFilesToParts(message.files).then(function(fileParts) {
12873
- return {
12874
- id: messageId,
12875
- role: 'user',
12876
- parts: fileParts,
12877
- metadata: message.metadata
12878
- };
12914
+ return fetchFn(api, {
12915
+ method: 'POST',
12916
+ headers: headers,
12917
+ body: JSON.stringify(body),
12918
+ signal: abortSignal,
12919
+ credentials: credentials
12920
+ }).then(function(response) {
12921
+ if (!response.ok) {
12922
+ return getHttpErrorMessage(response).then(function(message) {
12923
+ throw new Error(message);
12924
+ });
12925
+ }
12926
+ if (!response.body) {
12927
+ throw new Error('Response body is empty');
12928
+ }
12929
+ return _this.processResponseStream(response.body);
12879
12930
  });
12880
- } else {
12881
- userMessagePromise = Promise.resolve(undefined);
12882
- }
12883
- } else {
12884
- userMessagePromise = Promise.resolve(undefined);
12885
- }
12886
- return userMessagePromise.then(function(userMessage) {
12887
- if (userMessage) {
12888
- _this.state.pushMessage(userMessage);
12889
- }
12890
- return _this.makeRequest(_object_spread({
12891
- trigger: 'submit-message',
12892
- messageId: userMessage === null || userMessage === void 0 ? void 0 : userMessage.id
12893
- }, options));
12894
- });
12895
- });
12896
- });
12897
- /**
12898
- * Regenerate the assistant message with the provided message id.
12899
- * If no message id is provided, the last assistant message will be regenerated.
12900
- */ _define_property(this, "regenerate", function() {
12901
- var _0 = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : void 0;
12902
- var _ref = [
12903
- _0
12904
- ], _ref1 = _sliced_to_array(_ref, 1), tmp = _ref1[0], _ref2 = tmp === void 0 ? {} : tmp, messageId = _ref2.messageId, options = _object_without_properties(_ref2, [
12905
- "messageId"
12906
- ]);
12907
- return _this1.jobExecutor.run(function() {
12908
- // Find the message to regenerate from
12909
- var targetIndex = -1;
12910
- if (messageId) {
12911
- targetIndex = _this1.messages.findIndex(function(m) {
12912
- return m.id === messageId;
12913
12931
  });
12914
- } else {
12915
- // Find the last assistant message
12916
- for(var i = _this1.messages.length - 1; i >= 0; i--){
12917
- if (_this1.messages[i].role === 'assistant') {
12918
- targetIndex = i;
12919
- break;
12920
- }
12921
- }
12922
- }
12923
- if (targetIndex >= 0) {
12924
- // Remove the assistant message and all messages after it
12925
- _this1.messages = _this1.messages.slice(0, targetIndex);
12926
- }
12927
- return _this1.makeRequest(_object_spread({
12928
- trigger: 'regenerate-message',
12929
- messageId: messageId
12930
- }, options));
12931
- });
12932
- });
12933
- /**
12934
- * Attempt to resume an ongoing streaming response.
12935
- */ _define_property(this, "resumeStream", function(options) {
12936
- return _this.jobExecutor.run(function() {
12937
- if (!_this.transport) {
12938
- return Promise.reject(new Error('Transport is required for resuming stream. Please provide a transport when initializing the chat.'));
12939
- }
12940
- return _this.consume(function() {
12941
- return _this.transport.reconnectToStream(_object_spread({
12942
- chatId: _this.id
12943
- }, options));
12944
- });
12945
- });
12946
- });
12947
- /**
12948
- * Clear the error state and set the status to ready if the chat is in an error state.
12949
- */ _define_property(this, "clearError", function() {
12950
- if (_this.state.status === 'error') {
12951
- _this.setStatus({
12952
- status: 'ready',
12953
- error: undefined
12954
12932
  });
12955
12933
  }
12956
- });
12957
- /**
12958
- * Add a tool result for a tool call.
12959
- */ _define_property(this, "addToolResult", function(options) {
12960
- if (!_this.acceptsIdentifierOnlyToolResults) {
12961
- return Promise.resolve();
12962
- }
12963
- var owners = _this.responsesByToolCallId.get(options.toolCallId);
12964
- var response = (owners === null || owners === void 0 ? void 0 : owners.size) === 1 ? owners.values().next().value : undefined;
12965
- if (response) return _this.submitToolResult(response, options);
12966
- var findMatchingMessage = function findMatchingMessage() {
12967
- var matchingMessages = _this.messages.filter(function(message) {
12968
- var _message_parts;
12969
- return (_message_parts = message.parts) === null || _message_parts === void 0 ? void 0 : _message_parts.some(function(part) {
12970
- return 'toolCallId' in part && part.toolCallId === options.toolCallId;
12934
+ },
12935
+ {
12936
+ key: "reconnectToStream",
12937
+ value: function reconnectToStream(param) {
12938
+ var _this = this;
12939
+ var chatId = param.chatId, requestHeaders = param.headers, requestBody = param.body;
12940
+ var _this_fetch;
12941
+ var fetchFn = (_this_fetch = this.fetch) !== null && _this_fetch !== void 0 ? _this_fetch : fetch;
12942
+ // Resolve configurable values
12943
+ return Promise.all([
12944
+ resolveValue(this.credentials),
12945
+ resolveValue(this.headers),
12946
+ resolveValue(this.body)
12947
+ ]).then(function(param) {
12948
+ var _param = _sliced_to_array(param, 3), resolvedCredentials = _param[0], resolvedHeaders = _param[1], resolvedBody = _param[2];
12949
+ // Build default request options
12950
+ var api = _this.api;
12951
+ var headers = _object_spread({}, _instanceof(resolvedHeaders, Headers) ? Object.fromEntries(resolvedHeaders.entries()) : resolvedHeaders, _instanceof(requestHeaders, Headers) ? Object.fromEntries(requestHeaders.entries()) : requestHeaders);
12952
+ var credentials = resolvedCredentials;
12953
+ // Apply custom preparation if provided
12954
+ var prepareRequestBody = _object_spread({}, resolvedBody, requestBody);
12955
+ var preparePromise = _this.prepareReconnectToStreamRequest ? Promise.resolve(_this.prepareReconnectToStreamRequest({
12956
+ id: chatId,
12957
+ requestMetadata: undefined,
12958
+ body: prepareRequestBody,
12959
+ credentials: resolvedCredentials,
12960
+ headers: resolvedHeaders,
12961
+ api: _this.api
12962
+ })) : Promise.resolve(null);
12963
+ return preparePromise.then(function(prepared) {
12964
+ if (prepared) {
12965
+ if (prepared.api) api = prepared.api;
12966
+ if (prepared.headers) {
12967
+ headers = _instanceof(prepared.headers, Headers) ? Object.fromEntries(prepared.headers.entries()) : prepared.headers;
12968
+ }
12969
+ if (prepared.credentials) credentials = prepared.credentials;
12970
+ }
12971
+ // GET request for reconnection
12972
+ return fetchFn("".concat(api, "?chatId=").concat(chatId), {
12973
+ method: 'GET',
12974
+ headers: headers,
12975
+ credentials: credentials
12976
+ }).then(function(response) {
12977
+ if (!response.ok) {
12978
+ // 404 means no stream to reconnect to, which is not an error
12979
+ if (response.status === 404) {
12980
+ return null;
12981
+ }
12982
+ return getHttpErrorMessage(response).then(function(message) {
12983
+ throw new Error(message);
12984
+ });
12985
+ }
12986
+ if (!response.body) {
12987
+ return null;
12988
+ }
12989
+ return _this.processResponseStream(response.body);
12990
+ });
12971
12991
  });
12972
12992
  });
12973
- return matchingMessages.length === 1 ? matchingMessages[0] : undefined;
12974
- };
12975
- if (!findMatchingMessage()) return Promise.resolve();
12976
- return _this.jobExecutor.run(function() {
12977
- if (!_this.acceptsIdentifierOnlyToolResults) return Promise.resolve();
12978
- var message = findMatchingMessage();
12979
- if (!message || !_this.commit(options.toolCallId, options.output, message.id, undefined, message)) {
12980
- return Promise.resolve();
12981
- }
12982
- return _this.continueResponse();
12983
- });
12984
- });
12985
- _define_property(this, _computedKey1$1, function(message, options) {
12986
- var hasToolCall = function hasToolCall(candidate) {
12987
- var _candidate_parts;
12988
- return ((_candidate_parts = candidate.parts) === null || _candidate_parts === void 0 ? void 0 : _candidate_parts.some(function(part) {
12989
- return 'toolCallId' in part && part.toolCallId === options.toolCallId;
12990
- })) === true;
12991
- };
12992
- if (!hasToolCall(message)) {
12993
- return Promise.resolve();
12994
- }
12995
- var response = _this.responseByMessage.get(message);
12996
- var currentMessage = _this.messages.includes(message) ? message : response && !response.isRetired ? _this.messages.find(function(candidate) {
12997
- return candidate.id === message.id && _this.responseByMessage.get(candidate) === response && hasToolCall(candidate);
12998
- }) : undefined;
12999
- if (!currentMessage) return Promise.resolve();
13000
- return _this.submitToolResult(response, options, currentMessage.id, currentMessage);
13001
- });
13002
- /**
13003
- * Abort the current request immediately, keep the generated tokens if any.
13004
- */ _define_property(this, "stop", function() {
13005
- if (_this.activeResponse) {
13006
- var response = _this.activeResponse;
13007
- response.outcome = 'aborted';
13008
- _this.activeResponse = null;
13009
- response.abortController.abort();
13010
12993
  }
13011
- _this.setStatus({
13012
- status: 'ready'
13013
- });
13014
- return Promise.resolve();
13015
- });
13016
- this.conversationId = id;
13017
- this.generateId = generateId$1;
13018
- this.state = state;
13019
- this.transport = transport;
13020
- this.onError = onError;
13021
- this.onToolCall = onToolCall;
13022
- this.onFinish = onFinish;
13023
- this.onData = onData;
13024
- this.sendAutomaticallyWhen = sendAutomaticallyWhen;
13025
- this.shouldRepairToolInput = shouldRepairToolInput;
12994
+ }
12995
+ ]);
12996
+ return HttpChatTransport;
12997
+ }();
12998
+ /**
12999
+ * Default chat transport implementation using NDJSON streaming.
13000
+ */ var DefaultChatTransport = /*#__PURE__*/ function(HttpChatTransport) {
13001
+ _inherits(DefaultChatTransport, HttpChatTransport);
13002
+ function DefaultChatTransport() {
13003
+ var options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
13004
+ _class_call_check(this, DefaultChatTransport);
13005
+ return _call_super(this, DefaultChatTransport, [
13006
+ options
13007
+ ]);
13026
13008
  }
13027
- _create_class(AbstractChat, [
13028
- {
13029
- key: "id",
13030
- get: function get() {
13031
- return this.conversationId;
13032
- }
13033
- },
13034
- {
13035
- key: "status",
13036
- get: /**
13037
- * Hook status:
13038
- *
13039
- * - `submitted`: The message has been sent to the API and we're awaiting the start of the response stream.
13040
- * - `streaming`: The response is actively streaming in from the API, receiving chunks of data.
13041
- * - `ready`: The full response has been received and processed; a new user message can be submitted.
13042
- * - `error`: An error occurred during the API request, preventing successful completion.
13043
- */ function get() {
13044
- return this.state.status;
13045
- }
13046
- },
13009
+ _create_class(DefaultChatTransport, [
13047
13010
  {
13048
- key: "setStatus",
13049
- value: function setStatus(param) {
13050
- var status = param.status, error = param.error;
13051
- this.state.status = status;
13052
- if (error !== undefined) {
13053
- this.state.error = error;
13054
- }
13011
+ key: "processResponseStream",
13012
+ value: function processResponseStream(stream) {
13013
+ return parseJsonEventStream(stream);
13055
13014
  }
13056
- },
13057
- {
13058
- key: "error",
13059
- get: function get() {
13060
- return this.state.error;
13015
+ }
13016
+ ]);
13017
+ return DefaultChatTransport;
13018
+ }(HttpChatTransport);
13019
+
13020
+ var tryParseJson = function tryParseJson(value) {
13021
+ try {
13022
+ return JSON.parse(value);
13023
+ } catch (unused) {
13024
+ return undefined;
13025
+ }
13026
+ };
13027
+ var repairPartialJson = function repairPartialJson(value) {
13028
+ var repaired = value.trim();
13029
+ if (!repaired) {
13030
+ return repaired;
13031
+ }
13032
+ var inString = false;
13033
+ var isEscaped = false;
13034
+ var stack = [];
13035
+ for(var index = 0; index < repaired.length; index++){
13036
+ var char = repaired[index];
13037
+ if (inString) {
13038
+ if (isEscaped) {
13039
+ isEscaped = false;
13040
+ } else if (char === '\\') {
13041
+ isEscaped = true;
13042
+ } else if (char === '"') {
13043
+ inString = false;
13061
13044
  }
13062
- },
13063
- {
13064
- /**
13065
- * Starts a new server-side conversation thread by rotating the id sent as
13066
- * `chatId` / `id` on the next request. The InstantSearch connector calls this
13067
- * after the user clears the transcript so completions are not tied to prior
13068
- * context.
13069
- */ key: "resetConversationId",
13070
- value: function resetConversationId() {
13071
- if (this.responsesByToolCallId.size > 0 || this.messages.some(function(message) {
13072
- var _message_parts;
13073
- return (_message_parts = message.parts) === null || _message_parts === void 0 ? void 0 : _message_parts.some(function(part) {
13074
- return 'toolCallId' in part;
13075
- });
13076
- })) {
13077
- this.acceptsIdentifierOnlyToolResults = false;
13045
+ continue;
13046
+ }
13047
+ if (char === '"') {
13048
+ inString = true;
13049
+ continue;
13050
+ }
13051
+ if (char === '{' || char === '[') {
13052
+ stack.push(char);
13053
+ continue;
13054
+ }
13055
+ if (char === '}' && stack[stack.length - 1] === '{') {
13056
+ stack.pop();
13057
+ continue;
13058
+ }
13059
+ if (char === ']' && stack[stack.length - 1] === '[') {
13060
+ stack.pop();
13061
+ }
13062
+ }
13063
+ if (inString && !isEscaped) {
13064
+ repaired += '"';
13065
+ }
13066
+ repaired = repaired.replace(RegExp(",\\s*$", "u"), '');
13067
+ if (stack.length > 0) {
13068
+ repaired += stack.reverse().map(function(opening) {
13069
+ return opening === '{' ? '}' : ']';
13070
+ }).join('');
13071
+ }
13072
+ return repaired.replace(RegExp(",\\s*([}\\]])", "gu"), '$1');
13073
+ };
13074
+ var parsePartialJson = function parsePartialJson(accumulatedRawJson, fallbackValue) {
13075
+ var normalized = accumulatedRawJson.trim();
13076
+ if (!normalized) {
13077
+ return fallbackValue;
13078
+ }
13079
+ var directParsed = tryParseJson(normalized);
13080
+ if (directParsed !== undefined) {
13081
+ return directParsed;
13082
+ }
13083
+ var repairedParsed = tryParseJson(repairPartialJson(normalized));
13084
+ if (repairedParsed !== undefined) {
13085
+ return repairedParsed;
13086
+ }
13087
+ return fallbackValue;
13088
+ };
13089
+
13090
+ var _computedKey$1;
13091
+ var defaultGuardrailFallbackResponse = 'Sorry, we are not able to generate a response at the moment.';
13092
+ var TOOL_CALL_CANCELLED_ERROR_TEXT = 'The tool call was cancelled: the conversation moved on before a result was provided.';
13093
+ function getToolName(part) {
13094
+ if (part.type === 'dynamic-tool') {
13095
+ var _part_toolName;
13096
+ return (_part_toolName = part.toolName) !== null && _part_toolName !== void 0 ? _part_toolName : part.type;
13097
+ }
13098
+ return part.type.slice('tool-'.length);
13099
+ }
13100
+ function warnInvalidGlobalToolResult(toolCallId) {}
13101
+ // A tool call awaiting an output that the client owns. Provider-executed calls
13102
+ // are resolved server-side, so they are left alone.
13103
+ function isPendingToolPart(part) {
13104
+ var candidate = part;
13105
+ return typeof candidate.type === 'string' && typeof candidate.toolCallId === 'string' && (candidate.state === 'input-streaming' || candidate.state === 'input-available') && candidate.providerExecuted !== true;
13106
+ }
13107
+ // Drops the fields that only belong to the state being left behind:
13108
+ // `output-error` forbids `output`, and a committed output is not `preliminary`.
13109
+ function withTerminalToolState(part, terminalState) {
13110
+ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
13111
+ part.preliminary; // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
13112
+ part.rawOutput; // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
13113
+ part.output; // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
13114
+ part.errorText;
13115
+ var retainedPart = _object_without_properties(part, [
13116
+ "preliminary",
13117
+ "rawOutput",
13118
+ "output",
13119
+ "errorText"
13120
+ ]);
13121
+ return _object_spread({}, retainedPart, terminalState);
13122
+ }
13123
+ _computedKey$1 = /** @internal */ '~addToolResultForMessage';
13124
+ var _computedKey1$1 = _computedKey$1;
13125
+ /**
13126
+ * Abstract base class for chat implementations.
13127
+ */ var AbstractChat = /*#__PURE__*/ function() {
13128
+ function AbstractChat(param) {
13129
+ var _this = this;
13130
+ var _param_generateId = param.generateId, generateId$1 = _param_generateId === void 0 ? generateId : _param_generateId, _param_id = param.id, id = _param_id === void 0 ? generateId$1() : _param_id, transport = param.transport, state = param.state, onError = param.onError, onToolCall = param.onToolCall, onFinish = param.onFinish, onData = param.onData, sendAutomaticallyWhen = param.sendAutomaticallyWhen, shouldRepairToolInput = param.shouldRepairToolInput, resolveCancelledToolOutput = param.resolveCancelledToolOutput;
13131
+ var _this1 = this;
13132
+ _class_call_check(this, AbstractChat);
13133
+ _define_property(this, "conversationId", void 0);
13134
+ _define_property(this, "generateId", void 0);
13135
+ _define_property(this, "state", void 0);
13136
+ _define_property(this, "transport", void 0);
13137
+ _define_property(this, "onError", void 0);
13138
+ _define_property(this, "onToolCall", void 0);
13139
+ _define_property(this, "onFinish", void 0);
13140
+ _define_property(this, "onData", void 0);
13141
+ _define_property(this, "sendAutomaticallyWhen", void 0);
13142
+ _define_property(this, "shouldRepairToolInput", void 0);
13143
+ _define_property(this, "resolveCancelledToolOutput", void 0);
13144
+ _define_property(this, "activeResponse", null);
13145
+ _define_property(this, "latestResponse", null);
13146
+ _define_property(this, "responsesByToolCallId", new Map());
13147
+ _define_property(this, "responseByMessage", new WeakMap());
13148
+ // Once tool ownership is discarded, an identifier-only result cannot prove
13149
+ // which response generation submitted it. Scoped submissions remain safe.
13150
+ _define_property(this, "acceptsIdentifierOnlyToolResults", true);
13151
+ _define_property(this, "jobExecutor", new SerialJobExecutor());
13152
+ /**
13153
+ * Appends or replaces a user message to the chat list. This triggers the API call to fetch
13154
+ * the assistant's response.
13155
+ */ _define_property(this, "sendMessage", function(message, options) {
13156
+ return _this.jobExecutor.run(function() {
13157
+ // Build the user message
13158
+ var userMessagePromise;
13159
+ if (message) {
13160
+ var messageId = message.messageId || _this.generateId();
13161
+ if ('parts' in message && message.parts) {
13162
+ // Full message with parts provided
13163
+ userMessagePromise = Promise.resolve(_object_spread({
13164
+ id: messageId,
13165
+ role: 'user'
13166
+ }, message));
13167
+ } else if ('text' in message && message.text) {
13168
+ // Build from text
13169
+ var parts = [
13170
+ {
13171
+ type: 'text',
13172
+ text: message.text
13173
+ }
13174
+ ];
13175
+ // Add file parts if provided
13176
+ if (message.files) {
13177
+ userMessagePromise = _this.convertFilesToParts(message.files).then(function(fileParts) {
13178
+ var _parts;
13179
+ (_parts = parts).push.apply(_parts, _to_consumable_array(fileParts));
13180
+ return {
13181
+ id: messageId,
13182
+ role: 'user',
13183
+ parts: parts,
13184
+ metadata: message.metadata
13185
+ };
13186
+ });
13187
+ } else {
13188
+ userMessagePromise = Promise.resolve({
13189
+ id: messageId,
13190
+ role: 'user',
13191
+ parts: parts,
13192
+ metadata: message.metadata
13193
+ });
13194
+ }
13195
+ } else if ('files' in message && message.files) {
13196
+ // Files only
13197
+ userMessagePromise = _this.convertFilesToParts(message.files).then(function(fileParts) {
13198
+ return {
13199
+ id: messageId,
13200
+ role: 'user',
13201
+ parts: fileParts,
13202
+ metadata: message.metadata
13203
+ };
13204
+ });
13205
+ } else {
13206
+ userMessagePromise = Promise.resolve(undefined);
13207
+ }
13208
+ } else {
13209
+ userMessagePromise = Promise.resolve(undefined);
13078
13210
  }
13079
- this.conversationId = this.generateId();
13080
- }
13081
- },
13082
- {
13083
- key: "messages",
13084
- get: function get() {
13085
- return this.state.messages;
13086
- },
13087
- set: function set(messages) {
13088
- var _this = this;
13089
- var getToolOccurrences = function getToolOccurrences(message) {
13090
- return message.parts.filter(function(part) {
13091
- return 'toolCallId' in part;
13092
- }).map(function(part) {
13093
- return {
13094
- type: part.type,
13095
- toolCallId: part.toolCallId,
13096
- state: 'state' in part ? part.state : undefined,
13097
- input: 'input' in part ? part.input : undefined,
13098
- output: 'output' in part ? part.output : undefined,
13099
- errorText: 'errorText' in part ? part.errorText : undefined,
13100
- providerExecuted: 'providerExecuted' in part ? part.providerExecuted : undefined
13101
- };
13102
- });
13103
- };
13104
- var isEquivalentRehydration = function isEquivalentRehydration(message) {
13105
- var replacements = messages.filter(function(candidate) {
13106
- return candidate.id === message.id;
13211
+ return userMessagePromise.then(function(userMessage) {
13212
+ if (userMessage) {
13213
+ _this.state.pushMessage(userMessage);
13214
+ }
13215
+ return _this.makeRequest(_object_spread({
13216
+ trigger: 'submit-message',
13217
+ messageId: userMessage === null || userMessage === void 0 ? void 0 : userMessage.id
13218
+ }, options));
13219
+ });
13220
+ });
13221
+ });
13222
+ /**
13223
+ * Regenerate the assistant message with the provided message id.
13224
+ * If no message id is provided, the last assistant message will be regenerated.
13225
+ */ _define_property(this, "regenerate", function() {
13226
+ var _0 = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : void 0;
13227
+ var _ref = [
13228
+ _0
13229
+ ], _ref1 = _sliced_to_array(_ref, 1), tmp = _ref1[0], _ref2 = tmp === void 0 ? {} : tmp, messageId = _ref2.messageId, options = _object_without_properties(_ref2, [
13230
+ "messageId"
13231
+ ]);
13232
+ return _this1.jobExecutor.run(function() {
13233
+ // Find the message to regenerate from
13234
+ var targetIndex = -1;
13235
+ if (messageId) {
13236
+ targetIndex = _this1.messages.findIndex(function(m) {
13237
+ return m.id === messageId;
13107
13238
  });
13108
- if (replacements.length !== 1) return false;
13109
- var toolOccurrences = getToolOccurrences(message);
13110
- var replacementToolOccurrences = getToolOccurrences(replacements[0]);
13111
- return toolOccurrences.length > 0 || replacementToolOccurrences.length > 0 ? isEqual(toolOccurrences, replacementToolOccurrences) : isEqual(message.parts, replacements[0].parts);
13112
- };
13113
- var detachedResponses = new Set();
13114
- this.state.messages.forEach(function(message) {
13115
- if (!messages.includes(message)) {
13116
- var response = _this.responseByMessage.get(message);
13117
- if (response && !isEquivalentRehydration(message)) {
13118
- detachedResponses.add(response);
13239
+ } else {
13240
+ // Find the last assistant message
13241
+ for(var i = _this1.messages.length - 1; i >= 0; i--){
13242
+ if (_this1.messages[i].role === 'assistant') {
13243
+ targetIndex = i;
13244
+ break;
13245
+ }
13246
+ }
13247
+ }
13248
+ if (targetIndex >= 0) {
13249
+ // Remove the assistant message and all messages after it
13250
+ _this1.messages = _this1.messages.slice(0, targetIndex);
13251
+ }
13252
+ return _this1.makeRequest(_object_spread({
13253
+ trigger: 'regenerate-message',
13254
+ messageId: messageId
13255
+ }, options));
13256
+ });
13257
+ });
13258
+ /**
13259
+ * Attempt to resume an ongoing streaming response.
13260
+ */ _define_property(this, "resumeStream", function(options) {
13261
+ return _this.jobExecutor.run(function() {
13262
+ if (!_this.transport) {
13263
+ return Promise.reject(new Error('Transport is required for resuming stream. Please provide a transport when initializing the chat.'));
13264
+ }
13265
+ return _this.consume(function() {
13266
+ return _this.transport.reconnectToStream(_object_spread({
13267
+ chatId: _this.id
13268
+ }, options));
13269
+ });
13270
+ });
13271
+ });
13272
+ /**
13273
+ * Clear the error state and set the status to ready if the chat is in an error state.
13274
+ */ _define_property(this, "clearError", function() {
13275
+ if (_this.state.status === 'error') {
13276
+ _this.setStatus({
13277
+ status: 'ready',
13278
+ error: undefined
13279
+ });
13280
+ }
13281
+ });
13282
+ /**
13283
+ * Add a tool result for a tool call.
13284
+ */ _define_property(this, "addToolResult", function(options) {
13285
+ if (!_this.acceptsIdentifierOnlyToolResults) {
13286
+ warnInvalidGlobalToolResult(options.toolCallId);
13287
+ return Promise.resolve();
13288
+ }
13289
+ var owners = _this.responsesByToolCallId.get(options.toolCallId);
13290
+ var response = (owners === null || owners === void 0 ? void 0 : owners.size) === 1 ? owners.values().next().value : undefined;
13291
+ if (response) return _this.submitToolResult(response, options);
13292
+ var findMatchingMessage = function findMatchingMessage() {
13293
+ var matchingMessages = _this.messages.filter(function(message) {
13294
+ var _message_parts;
13295
+ return (_message_parts = message.parts) === null || _message_parts === void 0 ? void 0 : _message_parts.some(function(part) {
13296
+ return 'toolCallId' in part && part.toolCallId === options.toolCallId;
13297
+ });
13298
+ });
13299
+ return matchingMessages.length === 1 ? matchingMessages[0] : undefined;
13300
+ };
13301
+ if (!findMatchingMessage()) {
13302
+ warnInvalidGlobalToolResult(options.toolCallId);
13303
+ return Promise.resolve();
13304
+ }
13305
+ return _this.jobExecutor.run(function() {
13306
+ if (!_this.acceptsIdentifierOnlyToolResults) {
13307
+ warnInvalidGlobalToolResult(options.toolCallId);
13308
+ return Promise.resolve();
13309
+ }
13310
+ var message = findMatchingMessage();
13311
+ if (!message) {
13312
+ warnInvalidGlobalToolResult(options.toolCallId);
13313
+ return Promise.resolve();
13314
+ }
13315
+ if (!_this.commit(options.toolCallId, options.output, message.id, undefined, message)) {
13316
+ return Promise.resolve();
13317
+ }
13318
+ return _this.continueResponse();
13319
+ });
13320
+ });
13321
+ _define_property(this, _computedKey1$1, function(message, options) {
13322
+ var hasToolCall = function hasToolCall(candidate) {
13323
+ var _candidate_parts;
13324
+ return ((_candidate_parts = candidate.parts) === null || _candidate_parts === void 0 ? void 0 : _candidate_parts.some(function(part) {
13325
+ return 'toolCallId' in part && part.toolCallId === options.toolCallId;
13326
+ })) === true;
13327
+ };
13328
+ if (!hasToolCall(message)) {
13329
+ return Promise.resolve();
13330
+ }
13331
+ var response = _this.responseByMessage.get(message);
13332
+ var currentMessage = _this.messages.includes(message) ? message : response && !response.isRetired ? _this.messages.find(function(candidate) {
13333
+ return candidate.id === message.id && _this.responseByMessage.get(candidate) === response && hasToolCall(candidate);
13334
+ }) : undefined;
13335
+ if (!currentMessage) return Promise.resolve();
13336
+ return _this.submitToolResult(response, options, currentMessage.id, currentMessage);
13337
+ });
13338
+ /**
13339
+ * Abort the current request immediately, keep the generated tokens if any.
13340
+ */ _define_property(this, "stop", function() {
13341
+ if (_this.activeResponse) {
13342
+ var response = _this.activeResponse;
13343
+ response.outcome = 'aborted';
13344
+ _this.activeResponse = null;
13345
+ response.abortController.abort();
13346
+ }
13347
+ _this.setStatus({
13348
+ status: 'ready'
13349
+ });
13350
+ return Promise.resolve();
13351
+ });
13352
+ this.conversationId = id;
13353
+ this.generateId = generateId$1;
13354
+ this.state = state;
13355
+ this.transport = transport;
13356
+ this.onError = onError;
13357
+ this.onToolCall = onToolCall;
13358
+ this.onFinish = onFinish;
13359
+ this.onData = onData;
13360
+ this.sendAutomaticallyWhen = sendAutomaticallyWhen;
13361
+ this.shouldRepairToolInput = shouldRepairToolInput;
13362
+ this.resolveCancelledToolOutput = resolveCancelledToolOutput;
13363
+ }
13364
+ _create_class(AbstractChat, [
13365
+ {
13366
+ key: "id",
13367
+ get: function get() {
13368
+ return this.conversationId;
13369
+ }
13370
+ },
13371
+ {
13372
+ key: "status",
13373
+ get: /**
13374
+ * Hook status:
13375
+ *
13376
+ * - `submitted`: The message has been sent to the API and we're awaiting the start of the response stream.
13377
+ * - `streaming`: The response is actively streaming in from the API, receiving chunks of data.
13378
+ * - `ready`: The full response has been received and processed; a new user message can be submitted.
13379
+ * - `error`: An error occurred during the API request, preventing successful completion.
13380
+ */ function get() {
13381
+ return this.state.status;
13382
+ }
13383
+ },
13384
+ {
13385
+ key: "setStatus",
13386
+ value: function setStatus(param) {
13387
+ var status = param.status, error = param.error;
13388
+ this.state.status = status;
13389
+ if (error !== undefined) {
13390
+ this.state.error = error;
13391
+ }
13392
+ }
13393
+ },
13394
+ {
13395
+ key: "error",
13396
+ get: function get() {
13397
+ return this.state.error;
13398
+ }
13399
+ },
13400
+ {
13401
+ /**
13402
+ * Starts a new server-side conversation thread by rotating the id sent as
13403
+ * `chatId` / `id` on the next request. The InstantSearch connector calls this
13404
+ * after the user clears the transcript so completions are not tied to prior
13405
+ * context.
13406
+ */ key: "resetConversationId",
13407
+ value: function resetConversationId() {
13408
+ if (this.responsesByToolCallId.size > 0 || this.messages.some(function(message) {
13409
+ var _message_parts;
13410
+ return (_message_parts = message.parts) === null || _message_parts === void 0 ? void 0 : _message_parts.some(function(part) {
13411
+ return 'toolCallId' in part;
13412
+ });
13413
+ })) {
13414
+ this.acceptsIdentifierOnlyToolResults = false;
13415
+ }
13416
+ this.conversationId = this.generateId();
13417
+ }
13418
+ },
13419
+ {
13420
+ key: "messages",
13421
+ get: function get() {
13422
+ return this.state.messages;
13423
+ },
13424
+ set: function set(messages) {
13425
+ var _this = this;
13426
+ var getToolOccurrences = function getToolOccurrences(message) {
13427
+ return message.parts.filter(function(part) {
13428
+ return 'toolCallId' in part;
13429
+ }).map(function(part) {
13430
+ return {
13431
+ type: part.type,
13432
+ toolCallId: part.toolCallId,
13433
+ state: 'state' in part ? part.state : undefined,
13434
+ input: 'input' in part ? part.input : undefined,
13435
+ output: 'output' in part ? part.output : undefined,
13436
+ errorText: 'errorText' in part ? part.errorText : undefined,
13437
+ providerExecuted: 'providerExecuted' in part ? part.providerExecuted : undefined
13438
+ };
13439
+ });
13440
+ };
13441
+ var isEquivalentRehydration = function isEquivalentRehydration(message) {
13442
+ var replacements = messages.filter(function(candidate) {
13443
+ return candidate.id === message.id;
13444
+ });
13445
+ if (replacements.length !== 1) return false;
13446
+ var toolOccurrences = getToolOccurrences(message);
13447
+ var replacementToolOccurrences = getToolOccurrences(replacements[0]);
13448
+ return toolOccurrences.length > 0 || replacementToolOccurrences.length > 0 ? isEqual(toolOccurrences, replacementToolOccurrences) : isEqual(message.parts, replacements[0].parts);
13449
+ };
13450
+ var detachedResponses = new Set();
13451
+ this.state.messages.forEach(function(message) {
13452
+ if (!messages.includes(message)) {
13453
+ var response = _this.responseByMessage.get(message);
13454
+ if (response && !isEquivalentRehydration(message)) {
13455
+ detachedResponses.add(response);
13119
13456
  }
13120
13457
  }
13121
13458
  });
@@ -13208,15 +13545,8 @@
13208
13545
  if (!('state' in part) || part.state === 'output-available' && !part.preliminary || part.state === 'output-error') {
13209
13546
  return false;
13210
13547
  }
13211
- // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
13212
- part.preliminary; // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
13213
- part.rawOutput;
13214
- var committedPart = _object_without_properties(part, [
13215
- "preliminary",
13216
- "rawOutput"
13217
- ]);
13218
13548
  var updatedParts = _to_consumable_array(message.parts);
13219
- updatedParts[partIndex] = _object_spread_props(_object_spread({}, committedPart), {
13549
+ updatedParts[partIndex] = withTerminalToolState(part, {
13220
13550
  state: 'output-available',
13221
13551
  output: output
13222
13552
  });
@@ -13227,6 +13557,60 @@
13227
13557
  return true;
13228
13558
  }
13229
13559
  },
13560
+ {
13561
+ key: "getOutboundMessages",
13562
+ value: /**
13563
+ * The transcript to send, with every tool call still awaiting an output
13564
+ * reported as cancelled — a provider rejects a turn holding a tool call with
13565
+ * no matching result.
13566
+ *
13567
+ * The repair stays on this copy rather than being committed to `messages`:
13568
+ * the card is still mounted, so a result submitted after the send must still
13569
+ * land, and is sent as the real output from then on.
13570
+ */ function getOutboundMessages() {
13571
+ var _this = this;
13572
+ var outboundMessages;
13573
+ this.messages.forEach(function(message, messageIndex) {
13574
+ var repairedParts;
13575
+ message.parts.forEach(function(part, partIndex) {
13576
+ if (!isPendingToolPart(part)) return;
13577
+ repairedParts = repairedParts || _to_consumable_array(message.parts);
13578
+ repairedParts[partIndex] = withTerminalToolState(part, _this.resolveToolCallCancellation(part));
13579
+ });
13580
+ if (repairedParts) {
13581
+ outboundMessages = outboundMessages || _to_consumable_array(_this.messages);
13582
+ outboundMessages[messageIndex] = _object_spread_props(_object_spread({}, message), {
13583
+ parts: repairedParts
13584
+ });
13585
+ }
13586
+ });
13587
+ return outboundMessages || this.messages;
13588
+ }
13589
+ },
13590
+ {
13591
+ key: "resolveToolCallCancellation",
13592
+ value: function resolveToolCallCancellation(part) {
13593
+ var cancellation;
13594
+ // Repairing the outbound transcript must never fail the request build.
13595
+ try {
13596
+ var _this_resolveCancelledToolOutput, _this;
13597
+ cancellation = (_this_resolveCancelledToolOutput = (_this = this).resolveCancelledToolOutput) === null || _this_resolveCancelledToolOutput === void 0 ? void 0 : _this_resolveCancelledToolOutput.call(_this, {
13598
+ toolName: getToolName(part),
13599
+ toolCallId: part.toolCallId,
13600
+ input: 'input' in part ? part.input : undefined
13601
+ });
13602
+ } catch (unused) {
13603
+ cancellation = undefined;
13604
+ }
13605
+ return cancellation ? {
13606
+ state: 'output-available',
13607
+ output: cancellation.output
13608
+ } : {
13609
+ state: 'output-error',
13610
+ errorText: TOOL_CALL_CANCELLED_ERROR_TEXT
13611
+ };
13612
+ }
13613
+ },
13230
13614
  {
13231
13615
  key: "continueResponse",
13232
13616
  value: function continueResponse(response) {
@@ -13335,10 +13719,13 @@
13335
13719
  if (!this.transport) {
13336
13720
  return Promise.reject(new Error('Transport is required for sending messages. Please provide a transport when initializing the chat.'));
13337
13721
  }
13722
+ // Resolved as late as possible, so a tool that submits its output in the
13723
+ // meantime is sent as answered.
13724
+ var messages = this.getOutboundMessages();
13338
13725
  return this.consume(function(abortSignal) {
13339
13726
  return _this.transport.sendMessages({
13340
13727
  chatId: _this.id,
13341
- messages: _this.messages,
13728
+ messages: messages,
13342
13729
  abortSignal: abortSignal,
13343
13730
  trigger: options.trigger,
13344
13731
  messageId: options.messageId,
@@ -13476,8 +13863,14 @@
13476
13863
  allowRetired: !wasRetired
13477
13864
  });
13478
13865
  };
13866
+ // A tool call the server answers itself is server-owned, even when the
13867
+ // stream omits `providerExecuted`: nothing is left for the client to
13868
+ // submit, so the response must stop requiring a result for it. Dropping it
13869
+ // from `requiredToolCallIds` also keeps the turn from auto-continuing on
13870
+ // the server's behalf — a turn made of server-executed tool calls is
13871
+ // already complete, and resending it would repeat the whole answer.
13479
13872
  var acceptServerToolResult = function acceptServerToolResult(toolCallId) {
13480
- if (response.requiredToolCallIds.has(toolCallId)) {
13873
+ if (response.requiredToolCallIds.delete(toolCallId)) {
13481
13874
  response.resolvedToolCallIds.add(toolCallId);
13482
13875
  }
13483
13876
  };
@@ -13951,489 +14344,248 @@
13951
14344
  // Trigger onData callback
13952
14345
  if (_this.onData) {
13953
14346
  _this.onData(dataPart);
13954
- }
13955
- }
13956
- }
13957
- }
13958
- }, function() {
13959
- return Promise.all(response.returnedToolCallbacks).then(function() {
13960
- return finish();
13961
- });
13962
- }, function(error) {
13963
- return finish(error);
13964
- });
13965
- });
13966
- }
13967
- },
13968
- {
13969
- key: "handleError",
13970
- value: function handleError(error) {
13971
- var _ref = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}, _ref_updateState = _ref.updateState, updateState = _ref_updateState === void 0 ? true : _ref_updateState;
13972
- if (updateState) {
13973
- this.setStatus({
13974
- status: 'error',
13975
- error: error
13976
- });
13977
- }
13978
- if (this.onError) {
13979
- this.onError(error);
13980
- }
13981
- }
13982
- },
13983
- {
13984
- key: "convertFilesToParts",
13985
- value: function convertFilesToParts(files) {
13986
- var _this, _loop = function _loop(i) {
13987
- var file = files[i];
13988
- promises.push(_this.fileToDataUrl(file).then(function(dataUrl) {
13989
- return {
13990
- type: 'file',
13991
- mediaType: file.type,
13992
- filename: file.name,
13993
- url: dataUrl
13994
- };
13995
- }));
13996
- };
13997
- if (Array.isArray(files)) {
13998
- return Promise.resolve(files);
13999
- }
14000
- var promises = [];
14001
- for(var i = 0; i < files.length; i++)_this = this, _loop(i);
14002
- return Promise.all(promises);
14003
- }
14004
- },
14005
- {
14006
- key: "fileToDataUrl",
14007
- value: function fileToDataUrl(file) {
14008
- return new Promise(function(resolve, reject) {
14009
- var reader = new FileReader();
14010
- reader.onload = function() {
14011
- return resolve(reader.result);
14012
- };
14013
- reader.onerror = reject;
14014
- reader.readAsDataURL(file);
14015
- });
14016
- }
14017
- }
14018
- ]);
14019
- return AbstractChat;
14020
- }();
14021
-
14022
- var _computedKey, _computedKey1, _computedKey2, _computedKey3, _computedKey4, _computedKey5;
14023
- var CACHE_KEY = 'instantsearch-chat-initial-messages';
14024
- // Message history is a browser concern, and a server render constructs a Chat
14025
- // too. Reading storage there throws during rendering; the write below only
14026
- // throws into its own `catch`, so gating it just stops a pointless attempt.
14027
- function getDefaultInitialMessages(id) {
14028
- return safelyRunOnBrowser(function() {
14029
- try {
14030
- // `sessionStorage` is not available in every environment with a
14031
- // `window` (e.g. React Native), and some browsers throw on access
14032
- // when storage is disabled.
14033
- var initialMessages = sessionStorage.getItem(CACHE_KEY + (id ? "-".concat(id) : ''));
14034
- return initialMessages ? JSON.parse(initialMessages) : [];
14035
- } catch (e) {
14036
- return [];
14037
- }
14038
- }, {
14039
- fallback: function fallback() {
14040
- return [];
14041
- }
14042
- });
14043
- }
14044
- _computedKey = '~registerMessagesCallback', _computedKey1 = '~registerStatusCallback', _computedKey2 = '~registerErrorCallback';
14045
- var _computedKey6 = _computedKey, _computedKey7 = _computedKey1, _computedKey8 = _computedKey2;
14046
- var ChatState = /*#__PURE__*/ function() {
14047
- function ChatState() {
14048
- var _this = this;
14049
- var id = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : undefined, initialMessages = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : undefined, persistence = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : true;
14050
- _class_call_check(this, ChatState);
14051
- _define_property(this, "_messages", void 0);
14052
- _define_property(this, "_status", 'ready');
14053
- _define_property(this, "_error", undefined);
14054
- _define_property(this, "_messagesCallbacks", new Set());
14055
- _define_property(this, "_statusCallbacks", new Set());
14056
- _define_property(this, "_errorCallbacks", new Set());
14057
- _define_property(this, "pushMessage", function(message) {
14058
- _this._messages = _this._messages.concat(message);
14059
- _this._callMessagesCallbacks();
14060
- });
14061
- _define_property(this, "popMessage", function() {
14062
- _this._messages = _this._messages.slice(0, -1);
14063
- _this._callMessagesCallbacks();
14064
- });
14065
- _define_property(this, "replaceMessage", function(index, message) {
14066
- _this._messages = _to_consumable_array(_this._messages.slice(0, index)).concat([
14067
- // We deep clone the message here to ensure the new React Compiler (currently in RC) detects deeply nested parts/metadata changes:
14068
- _this.snapshot(message)
14069
- ], _to_consumable_array(_this._messages.slice(index + 1)));
14070
- _this._callMessagesCallbacks();
14071
- });
14072
- _define_property(this, "snapshot", function(thing) {
14073
- return JSON.parse(JSON.stringify(thing));
14074
- });
14075
- _define_property(this, _computedKey6, function(onChange) {
14076
- var callback = onChange;
14077
- _this._messagesCallbacks.add(callback);
14078
- return function() {
14079
- _this._messagesCallbacks.delete(callback);
14080
- };
14081
- });
14082
- _define_property(this, _computedKey7, function(onChange) {
14083
- _this._statusCallbacks.add(onChange);
14084
- return function() {
14085
- _this._statusCallbacks.delete(onChange);
14086
- };
14087
- });
14088
- _define_property(this, _computedKey8, function(onChange) {
14089
- _this._errorCallbacks.add(onChange);
14090
- return function() {
14091
- _this._errorCallbacks.delete(onChange);
14092
- };
14093
- });
14094
- _define_property(this, "_callMessagesCallbacks", function() {
14095
- _this._messagesCallbacks.forEach(function(callback) {
14096
- return callback();
14097
- });
14098
- });
14099
- _define_property(this, "_callStatusCallbacks", function() {
14100
- _this._statusCallbacks.forEach(function(callback) {
14101
- return callback();
14102
- });
14103
- });
14104
- _define_property(this, "_callErrorCallbacks", function() {
14105
- _this._errorCallbacks.forEach(function(callback) {
14106
- return callback();
14107
- });
14108
- });
14109
- if (initialMessages !== undefined) {
14110
- this._messages = initialMessages;
14111
- } else if (persistence) {
14112
- this._messages = getDefaultInitialMessages(id);
14113
- } else {
14114
- this._messages = [];
14115
- }
14116
- if (!persistence) {
14117
- return;
14118
- }
14119
- var saveMessagesInLocalStorage = function saveMessagesInLocalStorage() {
14120
- if (_this.status === 'ready') {
14121
- safelyRunOnBrowser(function() {
14122
- try {
14123
- sessionStorage.setItem(CACHE_KEY + (id ? "-".concat(id) : ''), JSON.stringify(_this.messages));
14124
- } catch (e) {
14125
- // Do nothing if sessionStorage is not available or full
14126
- }
14127
- });
14128
- }
14129
- };
14130
- this['~registerMessagesCallback'](saveMessagesInLocalStorage);
14131
- this['~registerStatusCallback'](saveMessagesInLocalStorage);
14132
- }
14133
- _create_class(ChatState, [
14134
- {
14135
- key: "status",
14136
- get: function get() {
14137
- return this._status;
14138
- },
14139
- set: function set(newStatus) {
14140
- this._status = newStatus;
14141
- this._callStatusCallbacks();
14142
- }
14143
- },
14144
- {
14145
- key: "error",
14146
- get: function get() {
14147
- return this._error;
14148
- },
14149
- set: function set(newError) {
14150
- this._error = newError;
14151
- this._callErrorCallbacks();
14152
- }
14153
- },
14154
- {
14155
- key: "messages",
14156
- get: function get() {
14157
- return this._messages;
14158
- },
14159
- set: function set(newMessages) {
14160
- this._messages = _to_consumable_array(newMessages);
14161
- this._callMessagesCallbacks();
14162
- }
14163
- }
14164
- ]);
14165
- return ChatState;
14166
- }();
14167
- _computedKey3 = '~registerMessagesCallback', _computedKey4 = '~registerStatusCallback', _computedKey5 = '~registerErrorCallback';
14168
- var _computedKey9 = _computedKey3, _computedKey10 = _computedKey4, _computedKey11 = _computedKey5;
14169
- var Chat = /*#__PURE__*/ function(AbstractChat) {
14170
- _inherits(Chat, AbstractChat);
14171
- function Chat(_0) {
14172
- _class_call_check(this, Chat);
14173
- var _this;
14174
- var messages = _0.messages, agentId = _0.agentId, _0_persistence = _0.persistence, persistence = _0_persistence === void 0 ? true : _0_persistence, init = _object_without_properties(_0, [
14175
- "messages",
14176
- "agentId",
14177
- "persistence"
14178
- ]);
14179
- var state = new ChatState(agentId, messages, persistence);
14180
- _this = _call_super(this, Chat, [
14181
- _object_spread_props(_object_spread({}, init), {
14182
- state: state
14183
- })
14184
- ]), _define_property(_this, "_state", void 0), _define_property(_this, _computedKey9, function(onChange) {
14185
- return _this._state['~registerMessagesCallback'](onChange);
14186
- }), _define_property(_this, _computedKey10, function(onChange) {
14187
- return _this._state['~registerStatusCallback'](onChange);
14188
- }), _define_property(_this, _computedKey11, function(onChange) {
14189
- return _this._state['~registerErrorCallback'](onChange);
14190
- });
14191
- _this._state = state;
14192
- return _this;
14193
- }
14194
- return Chat;
14195
- }(AbstractChat);
14196
-
14197
- // Centralizes the "open the chat from an entry point" behavior shared by the
14198
- // SearchBox AI button, the Autocomplete AI button, prompt suggestions, and any
14199
- // future entry point. The chat is always opened; the message is only sent when
14200
- // it is non-empty and the chat is not already processing a message.
14201
- // Returns true when a message was submitted, so callers can clear their input.
14202
- function openChat(chatRenderState) {
14203
- var _ref = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}, message = _ref.message, referer = _ref.referer, turnContext = _ref.turnContext;
14204
- var _ref1;
14205
- var _chatRenderState_setOpen;
14206
- if (!chatRenderState) {
14207
- return false;
14208
- }
14209
- var trimmed = (_ref1 = message === null || message === void 0 ? void 0 : message.trim()) !== null && _ref1 !== void 0 ? _ref1 : '';
14210
- if (!trimmed) {
14211
- if (chatRenderState.focusInput) {
14212
- chatRenderState.focusInput();
14213
- } else {
14214
- var _chatRenderState_setOpen1;
14215
- (_chatRenderState_setOpen1 = chatRenderState.setOpen) === null || _chatRenderState_setOpen1 === void 0 ? void 0 : _chatRenderState_setOpen1.call(chatRenderState, true);
14216
- }
14217
- return false;
14218
- }
14219
- (_chatRenderState_setOpen = chatRenderState.setOpen) === null || _chatRenderState_setOpen === void 0 ? void 0 : _chatRenderState_setOpen.call(chatRenderState, true);
14220
- if (isChatBusy(chatRenderState) || !chatRenderState.sendMessage) {
14221
- return false;
14222
- }
14223
- chatRenderState.sendMessage(_object_spread({
14224
- text: trimmed
14225
- }, turnContext ? {
14226
- metadata: {
14227
- turnContext: turnContext
14228
- }
14229
- } : {}), referer ? {
14230
- headers: {
14231
- 'x-algolia-referer': referer
14232
- }
14233
- } : undefined);
14234
- return true;
14235
- }
14236
- function isChatBusy(chatRenderState) {
14237
- return (chatRenderState === null || chatRenderState === void 0 ? void 0 : chatRenderState.status) === 'submitted' || (chatRenderState === null || chatRenderState === void 0 ? void 0 : chatRenderState.status) === 'streaming';
14238
- }
14239
-
14240
- var SearchIndexToolType = 'algolia_search_index';
14241
-
14242
- function flat(arr) {
14243
- return arr.reduce(function(acc, array) {
14244
- return acc.concat(array);
14245
- }, []);
14246
- }
14247
-
14248
- /**
14249
- * Reads a human-readable message from a failed HTTP response body when the
14250
- * server returns JSON such as `{ "message": "..." }` (the shared
14251
- * `ErrorResponse` shape used by every status code), falling back to the HTTP
14252
- * status line when the body is empty or not parseable.
14253
- */ function getHttpErrorMessage(response) {
14254
- var fallback = "HTTP error: ".concat(response.status, " ").concat(response.statusText);
14255
- return response.text().then(function(text) {
14256
- var _tryParseErrorMessage;
14257
- return (_tryParseErrorMessage = tryParseErrorMessage(text)) !== null && _tryParseErrorMessage !== void 0 ? _tryParseErrorMessage : fallback;
14258
- }).catch(function() {
14259
- return fallback;
14260
- });
14261
- }
14262
- /**
14263
- * Abstract base class for HTTP-based chat transports.
14264
- */ var HttpChatTransport = /*#__PURE__*/ function() {
14265
- function HttpChatTransport(param) {
14266
- var _param_api = param.api, api = _param_api === void 0 ? '/api/chat' : _param_api, credentials = param.credentials, headers = param.headers, body = param.body, customFetch = param.fetch, prepareSendMessagesRequest = param.prepareSendMessagesRequest, prepareReconnectToStreamRequest = param.prepareReconnectToStreamRequest;
14267
- _class_call_check(this, HttpChatTransport);
14268
- _define_property(this, "api", void 0);
14269
- _define_property(this, "credentials", void 0);
14270
- _define_property(this, "headers", void 0);
14271
- _define_property(this, "body", void 0);
14272
- _define_property(this, "fetch", void 0);
14273
- _define_property(this, "prepareSendMessagesRequest", void 0);
14274
- _define_property(this, "prepareReconnectToStreamRequest", void 0);
14275
- this.api = api;
14276
- this.credentials = credentials;
14277
- this.headers = headers;
14278
- this.body = body;
14279
- this.fetch = customFetch;
14280
- this.prepareSendMessagesRequest = prepareSendMessagesRequest;
14281
- this.prepareReconnectToStreamRequest = prepareReconnectToStreamRequest;
14282
- }
14283
- _create_class(HttpChatTransport, [
14284
- {
14285
- key: "sendMessages",
14286
- value: function sendMessages(param) {
14287
- var _this = this;
14288
- var abortSignal = param.abortSignal, chatId = param.chatId, messages = param.messages, requestMetadata = param.requestMetadata, trigger = param.trigger, messageId = param.messageId, requestHeaders = param.headers, requestBody = param.body;
14289
- var _this_fetch;
14290
- var fetchFn = (_this_fetch = this.fetch) !== null && _this_fetch !== void 0 ? _this_fetch : fetch;
14291
- // Resolve configurable values
14292
- return Promise.all([
14293
- resolveValue(this.credentials),
14294
- resolveValue(this.headers),
14295
- resolveValue(this.body)
14296
- ]).then(function(param) {
14297
- var _param = _sliced_to_array(param, 3), resolvedCredentials = _param[0], resolvedHeaders = _param[1], resolvedBody = _param[2];
14298
- // Build default request options
14299
- var api = _this.api;
14300
- var body = _object_spread({
14301
- id: chatId,
14302
- messages: messages
14303
- }, resolvedBody, requestBody);
14304
- var headers = _object_spread({
14305
- 'Content-Type': 'application/json'
14306
- }, _instanceof(resolvedHeaders, Headers) ? Object.fromEntries(resolvedHeaders.entries()) : resolvedHeaders, _instanceof(requestHeaders, Headers) ? Object.fromEntries(requestHeaders.entries()) : requestHeaders);
14307
- var credentials = resolvedCredentials;
14308
- // Apply custom preparation if provided
14309
- var prepareRequestBody = _object_spread({}, resolvedBody, requestBody);
14310
- var preparePromise = _this.prepareSendMessagesRequest ? Promise.resolve(_this.prepareSendMessagesRequest({
14311
- id: chatId,
14312
- messages: messages,
14313
- requestMetadata: requestMetadata,
14314
- body: prepareRequestBody,
14315
- credentials: resolvedCredentials,
14316
- headers: resolvedHeaders,
14317
- api: _this.api,
14318
- trigger: trigger,
14319
- messageId: messageId
14320
- })) : Promise.resolve(null);
14321
- return preparePromise.then(function(prepared) {
14322
- if (prepared) {
14323
- body = prepared.body;
14324
- if (prepared.api) api = prepared.api;
14325
- if (prepared.headers) {
14326
- headers = _object_spread({
14327
- 'Content-Type': 'application/json'
14328
- }, _instanceof(prepared.headers, Headers) ? Object.fromEntries(prepared.headers.entries()) : prepared.headers);
14329
- }
14330
- if (prepared.credentials) credentials = prepared.credentials;
14331
- }
14332
- return fetchFn(api, {
14333
- method: 'POST',
14334
- headers: headers,
14335
- body: JSON.stringify(body),
14336
- signal: abortSignal,
14337
- credentials: credentials
14338
- }).then(function(response) {
14339
- if (!response.ok) {
14340
- return getHttpErrorMessage(response).then(function(message) {
14341
- throw new Error(message);
14342
- });
14343
- }
14344
- if (!response.body) {
14345
- throw new Error('Response body is empty');
14346
- }
14347
- return _this.processResponseStream(response.body);
14347
+ }
14348
+ }
14349
+ }
14350
+ }
14351
+ }, function() {
14352
+ return Promise.all(response.returnedToolCallbacks).then(function() {
14353
+ return finish();
14348
14354
  });
14355
+ }, function(error) {
14356
+ return finish(error);
14349
14357
  });
14350
14358
  });
14351
14359
  }
14352
14360
  },
14353
14361
  {
14354
- key: "reconnectToStream",
14355
- value: function reconnectToStream(param) {
14356
- var _this = this;
14357
- var chatId = param.chatId, requestHeaders = param.headers, requestBody = param.body;
14358
- var _this_fetch;
14359
- var fetchFn = (_this_fetch = this.fetch) !== null && _this_fetch !== void 0 ? _this_fetch : fetch;
14360
- // Resolve configurable values
14361
- return Promise.all([
14362
- resolveValue(this.credentials),
14363
- resolveValue(this.headers),
14364
- resolveValue(this.body)
14365
- ]).then(function(param) {
14366
- var _param = _sliced_to_array(param, 3), resolvedCredentials = _param[0], resolvedHeaders = _param[1], resolvedBody = _param[2];
14367
- // Build default request options
14368
- var api = _this.api;
14369
- var headers = _object_spread({}, _instanceof(resolvedHeaders, Headers) ? Object.fromEntries(resolvedHeaders.entries()) : resolvedHeaders, _instanceof(requestHeaders, Headers) ? Object.fromEntries(requestHeaders.entries()) : requestHeaders);
14370
- var credentials = resolvedCredentials;
14371
- // Apply custom preparation if provided
14372
- var prepareRequestBody = _object_spread({}, resolvedBody, requestBody);
14373
- var preparePromise = _this.prepareReconnectToStreamRequest ? Promise.resolve(_this.prepareReconnectToStreamRequest({
14374
- id: chatId,
14375
- requestMetadata: undefined,
14376
- body: prepareRequestBody,
14377
- credentials: resolvedCredentials,
14378
- headers: resolvedHeaders,
14379
- api: _this.api
14380
- })) : Promise.resolve(null);
14381
- return preparePromise.then(function(prepared) {
14382
- if (prepared) {
14383
- if (prepared.api) api = prepared.api;
14384
- if (prepared.headers) {
14385
- headers = _instanceof(prepared.headers, Headers) ? Object.fromEntries(prepared.headers.entries()) : prepared.headers;
14386
- }
14387
- if (prepared.credentials) credentials = prepared.credentials;
14388
- }
14389
- // GET request for reconnection
14390
- return fetchFn("".concat(api, "?chatId=").concat(chatId), {
14391
- method: 'GET',
14392
- headers: headers,
14393
- credentials: credentials
14394
- }).then(function(response) {
14395
- if (!response.ok) {
14396
- // 404 means no stream to reconnect to, which is not an error
14397
- if (response.status === 404) {
14398
- return null;
14399
- }
14400
- return getHttpErrorMessage(response).then(function(message) {
14401
- throw new Error(message);
14402
- });
14403
- }
14404
- if (!response.body) {
14405
- return null;
14406
- }
14407
- return _this.processResponseStream(response.body);
14408
- });
14362
+ key: "handleError",
14363
+ value: function handleError(error) {
14364
+ var _ref = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}, _ref_updateState = _ref.updateState, updateState = _ref_updateState === void 0 ? true : _ref_updateState;
14365
+ if (updateState) {
14366
+ this.setStatus({
14367
+ status: 'error',
14368
+ error: error
14409
14369
  });
14370
+ }
14371
+ if (this.onError) {
14372
+ this.onError(error);
14373
+ }
14374
+ }
14375
+ },
14376
+ {
14377
+ key: "convertFilesToParts",
14378
+ value: function convertFilesToParts(files) {
14379
+ var _this, _loop = function _loop(i) {
14380
+ var file = files[i];
14381
+ promises.push(_this.fileToDataUrl(file).then(function(dataUrl) {
14382
+ return {
14383
+ type: 'file',
14384
+ mediaType: file.type,
14385
+ filename: file.name,
14386
+ url: dataUrl
14387
+ };
14388
+ }));
14389
+ };
14390
+ if (Array.isArray(files)) {
14391
+ return Promise.resolve(files);
14392
+ }
14393
+ var promises = [];
14394
+ for(var i = 0; i < files.length; i++)_this = this, _loop(i);
14395
+ return Promise.all(promises);
14396
+ }
14397
+ },
14398
+ {
14399
+ key: "fileToDataUrl",
14400
+ value: function fileToDataUrl(file) {
14401
+ return new Promise(function(resolve, reject) {
14402
+ var reader = new FileReader();
14403
+ reader.onload = function() {
14404
+ return resolve(reader.result);
14405
+ };
14406
+ reader.onerror = reject;
14407
+ reader.readAsDataURL(file);
14410
14408
  });
14411
14409
  }
14412
14410
  }
14413
14411
  ]);
14414
- return HttpChatTransport;
14412
+ return AbstractChat;
14415
14413
  }();
14416
- /**
14417
- * Default chat transport implementation using NDJSON streaming.
14418
- */ var DefaultChatTransport = /*#__PURE__*/ function(HttpChatTransport) {
14419
- _inherits(DefaultChatTransport, HttpChatTransport);
14420
- function DefaultChatTransport() {
14421
- var options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
14422
- _class_call_check(this, DefaultChatTransport);
14423
- return _call_super(this, DefaultChatTransport, [
14424
- options
14425
- ]);
14414
+
14415
+ var _computedKey, _computedKey1, _computedKey2, _computedKey3, _computedKey4, _computedKey5;
14416
+ var CACHE_KEY = 'instantsearch-chat-initial-messages';
14417
+ // Message history is a browser concern, and a server render constructs a Chat
14418
+ // too. Reading storage there throws during rendering; the write below only
14419
+ // throws into its own `catch`, so gating it just stops a pointless attempt.
14420
+ function getDefaultInitialMessages(id) {
14421
+ return safelyRunOnBrowser(function() {
14422
+ try {
14423
+ // `sessionStorage` is not available in every environment with a
14424
+ // `window` (e.g. React Native), and some browsers throw on access
14425
+ // when storage is disabled.
14426
+ var initialMessages = sessionStorage.getItem(CACHE_KEY + (id ? "-".concat(id) : ''));
14427
+ return initialMessages ? JSON.parse(initialMessages) : [];
14428
+ } catch (e) {
14429
+ return [];
14430
+ }
14431
+ }, {
14432
+ fallback: function fallback() {
14433
+ return [];
14434
+ }
14435
+ });
14436
+ }
14437
+ _computedKey = '~registerMessagesCallback', _computedKey1 = '~registerStatusCallback', _computedKey2 = '~registerErrorCallback';
14438
+ var _computedKey6 = _computedKey, _computedKey7 = _computedKey1, _computedKey8 = _computedKey2;
14439
+ var ChatState = /*#__PURE__*/ function() {
14440
+ function ChatState() {
14441
+ var _this = this;
14442
+ var id = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : undefined, initialMessages = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : undefined, persistence = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : true;
14443
+ _class_call_check(this, ChatState);
14444
+ _define_property(this, "_messages", void 0);
14445
+ _define_property(this, "_status", 'ready');
14446
+ _define_property(this, "_error", undefined);
14447
+ _define_property(this, "_messagesCallbacks", new Set());
14448
+ _define_property(this, "_statusCallbacks", new Set());
14449
+ _define_property(this, "_errorCallbacks", new Set());
14450
+ _define_property(this, "pushMessage", function(message) {
14451
+ _this._messages = _this._messages.concat(message);
14452
+ _this._callMessagesCallbacks();
14453
+ });
14454
+ _define_property(this, "popMessage", function() {
14455
+ _this._messages = _this._messages.slice(0, -1);
14456
+ _this._callMessagesCallbacks();
14457
+ });
14458
+ _define_property(this, "replaceMessage", function(index, message) {
14459
+ _this._messages = _to_consumable_array(_this._messages.slice(0, index)).concat([
14460
+ // We deep clone the message here to ensure the new React Compiler (currently in RC) detects deeply nested parts/metadata changes:
14461
+ _this.snapshot(message)
14462
+ ], _to_consumable_array(_this._messages.slice(index + 1)));
14463
+ _this._callMessagesCallbacks();
14464
+ });
14465
+ _define_property(this, "snapshot", function(thing) {
14466
+ return JSON.parse(JSON.stringify(thing));
14467
+ });
14468
+ _define_property(this, _computedKey6, function(onChange) {
14469
+ var callback = onChange;
14470
+ _this._messagesCallbacks.add(callback);
14471
+ return function() {
14472
+ _this._messagesCallbacks.delete(callback);
14473
+ };
14474
+ });
14475
+ _define_property(this, _computedKey7, function(onChange) {
14476
+ _this._statusCallbacks.add(onChange);
14477
+ return function() {
14478
+ _this._statusCallbacks.delete(onChange);
14479
+ };
14480
+ });
14481
+ _define_property(this, _computedKey8, function(onChange) {
14482
+ _this._errorCallbacks.add(onChange);
14483
+ return function() {
14484
+ _this._errorCallbacks.delete(onChange);
14485
+ };
14486
+ });
14487
+ _define_property(this, "_callMessagesCallbacks", function() {
14488
+ _this._messagesCallbacks.forEach(function(callback) {
14489
+ return callback();
14490
+ });
14491
+ });
14492
+ _define_property(this, "_callStatusCallbacks", function() {
14493
+ _this._statusCallbacks.forEach(function(callback) {
14494
+ return callback();
14495
+ });
14496
+ });
14497
+ _define_property(this, "_callErrorCallbacks", function() {
14498
+ _this._errorCallbacks.forEach(function(callback) {
14499
+ return callback();
14500
+ });
14501
+ });
14502
+ if (initialMessages !== undefined) {
14503
+ this._messages = initialMessages;
14504
+ } else if (persistence) {
14505
+ this._messages = getDefaultInitialMessages(id);
14506
+ } else {
14507
+ this._messages = [];
14508
+ }
14509
+ if (!persistence) {
14510
+ return;
14511
+ }
14512
+ var saveMessagesInLocalStorage = function saveMessagesInLocalStorage() {
14513
+ if (_this.status === 'ready') {
14514
+ safelyRunOnBrowser(function() {
14515
+ try {
14516
+ sessionStorage.setItem(CACHE_KEY + (id ? "-".concat(id) : ''), JSON.stringify(_this.messages));
14517
+ } catch (e) {
14518
+ // Do nothing if sessionStorage is not available or full
14519
+ }
14520
+ });
14521
+ }
14522
+ };
14523
+ this['~registerMessagesCallback'](saveMessagesInLocalStorage);
14524
+ this['~registerStatusCallback'](saveMessagesInLocalStorage);
14426
14525
  }
14427
- _create_class(DefaultChatTransport, [
14526
+ _create_class(ChatState, [
14428
14527
  {
14429
- key: "processResponseStream",
14430
- value: function processResponseStream(stream) {
14431
- return parseJsonEventStream(stream);
14528
+ key: "status",
14529
+ get: function get() {
14530
+ return this._status;
14531
+ },
14532
+ set: function set(newStatus) {
14533
+ this._status = newStatus;
14534
+ this._callStatusCallbacks();
14535
+ }
14536
+ },
14537
+ {
14538
+ key: "error",
14539
+ get: function get() {
14540
+ return this._error;
14541
+ },
14542
+ set: function set(newError) {
14543
+ this._error = newError;
14544
+ this._callErrorCallbacks();
14545
+ }
14546
+ },
14547
+ {
14548
+ key: "messages",
14549
+ get: function get() {
14550
+ return this._messages;
14551
+ },
14552
+ set: function set(newMessages) {
14553
+ this._messages = _to_consumable_array(newMessages);
14554
+ this._callMessagesCallbacks();
14432
14555
  }
14433
14556
  }
14434
14557
  ]);
14435
- return DefaultChatTransport;
14436
- }(HttpChatTransport);
14558
+ return ChatState;
14559
+ }();
14560
+ _computedKey3 = '~registerMessagesCallback', _computedKey4 = '~registerStatusCallback', _computedKey5 = '~registerErrorCallback';
14561
+ var _computedKey9 = _computedKey3, _computedKey10 = _computedKey4, _computedKey11 = _computedKey5;
14562
+ var Chat = /*#__PURE__*/ function(AbstractChat) {
14563
+ _inherits(Chat, AbstractChat);
14564
+ function Chat(_0) {
14565
+ _class_call_check(this, Chat);
14566
+ var _this;
14567
+ var messages = _0.messages, agentId = _0.agentId, _0_persistence = _0.persistence, persistence = _0_persistence === void 0 ? true : _0_persistence, init = _object_without_properties(_0, [
14568
+ "messages",
14569
+ "agentId",
14570
+ "persistence"
14571
+ ]);
14572
+ var state = new ChatState(agentId, messages, persistence);
14573
+ _this = _call_super(this, Chat, [
14574
+ _object_spread_props(_object_spread({}, init), {
14575
+ state: state
14576
+ })
14577
+ ]), _define_property(_this, "_state", void 0), _define_property(_this, _computedKey9, function(onChange) {
14578
+ return _this._state['~registerMessagesCallback'](onChange);
14579
+ }), _define_property(_this, _computedKey10, function(onChange) {
14580
+ return _this._state['~registerStatusCallback'](onChange);
14581
+ }), _define_property(_this, _computedKey11, function(onChange) {
14582
+ return _this._state['~registerErrorCallback'](onChange);
14583
+ });
14584
+ _this._state = state;
14585
+ return _this;
14586
+ }
14587
+ return Chat;
14588
+ }(AbstractChat);
14437
14589
 
14438
14590
  var withUsage$q = createDocumentationMessageGenerator({
14439
14591
  name: 'chat',
@@ -14574,6 +14726,13 @@
14574
14726
  var setFeedbackState;
14575
14727
  var hasValidatedEntryPoints = false;
14576
14728
  var agentId = 'agentId' in options ? options.agentId : undefined;
14729
+ // Collected here rather than by the tool that searched: that tool renders
14730
+ // nothing while display-results presents its records, and a record has to
14731
+ // outlive the render that produced it.
14732
+ var records = createChatRecordsStore();
14733
+ var collectRecords = function collectRecords() {
14734
+ return collectChatRecords(_chatInstance.messages, records);
14735
+ };
14577
14736
  var feedbackState = {};
14578
14737
  var _sendChatMessageFeedback;
14579
14738
  var feedbackAbortController;
@@ -14583,21 +14742,44 @@
14583
14742
  return unsubscribe();
14584
14743
  });
14585
14744
  };
14586
- // Extract suggestions from the last assistant message's data-suggestions part
14587
- var getSuggestionsFromMessages = function getSuggestionsFromMessages(messages) {
14588
- // Find the last assistant message (iterate from end)
14589
- var lastAssistantMessage = _to_consumable_array(messages).reverse().find(function(message) {
14745
+ var findSuggestionsPart = function findSuggestionsPart(message) {
14746
+ var _message_parts;
14747
+ return message === null || message === void 0 ? void 0 : (_message_parts = message.parts) === null || _message_parts === void 0 ? void 0 : _message_parts.find(function(part) {
14748
+ var _part_data;
14749
+ return 'type' in part && part.type === 'data-suggestions' && 'data' in part && Array.isArray((_part_data = part.data) === null || _part_data === void 0 ? void 0 : _part_data.suggestions);
14750
+ });
14751
+ };
14752
+ var findLastAssistantMessage = function findLastAssistantMessage(messages) {
14753
+ return _to_consumable_array(messages).reverse().find(function(message) {
14590
14754
  return message.role === 'assistant' && message.parts;
14591
14755
  });
14592
- if (!(lastAssistantMessage === null || lastAssistantMessage === void 0 ? void 0 : lastAssistantMessage.parts)) {
14593
- return undefined;
14756
+ };
14757
+ // Extract suggestions from the last assistant message's data-suggestions part
14758
+ var getSuggestionsFromMessages = function getSuggestionsFromMessages(messages) {
14759
+ var _findSuggestionsPart;
14760
+ return (_findSuggestionsPart = findSuggestionsPart(findLastAssistantMessage(messages))) === null || _findSuggestionsPart === void 0 ? void 0 : _findSuggestionsPart.data.suggestions;
14761
+ };
14762
+ // "Still coming" has to be inferred: the turn is running and has no
14763
+ // `data-suggestions` part yet. Expecting any at all needs evidence, or an
14764
+ // agent that never sends them would sit under a placeholder forever.
14765
+ var getSuggestionsStatus = function getSuggestionsStatus(messages) {
14766
+ var _lastAssistantMessage_metadata;
14767
+ var status = _chatInstance.status;
14768
+ if (status !== 'submitted' && status !== 'streaming') {
14769
+ return 'idle';
14594
14770
  }
14595
- // Find the data-suggestions part
14596
- var suggestionsPart = lastAssistantMessage.parts.find(function(part) {
14597
- var _part_data;
14598
- return 'type' in part && part.type === 'data-suggestions' && 'data' in part && Array.isArray((_part_data = part.data) === null || _part_data === void 0 ? void 0 : _part_data.suggestions);
14771
+ var lastAssistantMessage = findLastAssistantMessage(messages);
14772
+ if (findSuggestionsPart(lastAssistantMessage)) {
14773
+ return 'idle';
14774
+ }
14775
+ var declaresSuggestions = (lastAssistantMessage === null || lastAssistantMessage === void 0 ? void 0 : (_lastAssistantMessage_metadata = lastAssistantMessage.metadata) === null || _lastAssistantMessage_metadata === void 0 ? void 0 : _lastAssistantMessage_metadata.suggestionsEnabled) === true;
14776
+ if (declaresSuggestions) {
14777
+ return 'loading';
14778
+ }
14779
+ var hasSuggestionsHistory = messages.some(function(message) {
14780
+ return message !== lastAssistantMessage && message.role === 'assistant' && Boolean(findSuggestionsPart(message));
14599
14781
  });
14600
- return suggestionsPart === null || suggestionsPart === void 0 ? void 0 : suggestionsPart.data.suggestions;
14782
+ return hasSuggestionsHistory ? 'loading' : 'idle';
14601
14783
  };
14602
14784
  var setMessages = function setMessages(messagesParam) {
14603
14785
  if (typeof messagesParam === 'function') {
@@ -14614,6 +14796,7 @@
14614
14796
  // ChatState callbacks that synchronously re-render, so they must run last
14615
14797
  // for that render to see the cleared feedback and rotated conversation id.
14616
14798
  feedbackState = {};
14799
+ records.clear();
14617
14800
  _chatInstance.resetConversationId();
14618
14801
  setMessages([]);
14619
14802
  _chatInstance.clearError();
@@ -14634,6 +14817,10 @@
14634
14817
  });
14635
14818
  hasValidatedEntryPoints = true;
14636
14819
  };
14820
+ // Deferred because entry points can be registered after the chat itself:
14821
+ // React adds widgets in mount order, so a trigger further down the tree
14822
+ // only lands after this widget's `init` has run.
14823
+ var deferredValidateEntryPoints = defer(validateEntryPoints);
14637
14824
  var makeChatInstance = function makeChatInstance(instantSearchInstance) {
14638
14825
  // A caller supplied `chat` already owns its transport, so it bypasses the
14639
14826
  // connector's transport construction and validation below.
@@ -14735,20 +14922,31 @@
14735
14922
  sendAutomaticallyWhen: sendAutomaticallyWhen,
14736
14923
  transport: transport,
14737
14924
  shouldRepairToolInput: function shouldRepairToolInput(toolName) {
14738
- var tool = tools[toolName];
14739
- if (!tool && toolName.startsWith("".concat(SearchIndexToolType, "_"))) {
14740
- tool = tools[SearchIndexToolType];
14741
- }
14925
+ var tool = findTool(toolName, tools);
14742
14926
  if (!tool) return true;
14743
14927
  return Boolean(tool.streamInput);
14744
14928
  },
14929
+ resolveCancelledToolOutput: function resolveCancelledToolOutput(param) {
14930
+ var toolName = param.toolName, toolCallId = param.toolCallId, input = param.input;
14931
+ var _findTool;
14932
+ var cancelOutput = (_findTool = findTool(toolName, tools)) === null || _findTool === void 0 ? void 0 : _findTool.cancelOutput;
14933
+ if (!cancelOutput) return undefined;
14934
+ try {
14935
+ var output = cancelOutput({
14936
+ toolCallId: toolCallId,
14937
+ input: input
14938
+ });
14939
+ // `undefined` means the tool declined to provide an output.
14940
+ return output === undefined ? undefined : {
14941
+ output: output
14942
+ };
14943
+ } catch (unused) {
14944
+ return undefined;
14945
+ }
14946
+ },
14745
14947
  onToolCall: function onToolCall(param, submitToolResult) {
14746
14948
  var toolCall = param.toolCall;
14747
- var tool = tools[toolCall.toolName];
14748
- // Compatibility shim with Algolia MCP Server search tool
14749
- if (!tool && toolCall.toolName.startsWith("".concat(SearchIndexToolType, "_"))) {
14750
- tool = tools[SearchIndexToolType];
14751
- }
14949
+ var tool = findTool(toolCall.toolName, tools);
14752
14950
  if (!tool) {
14753
14951
  return submitToolResult({
14754
14952
  output: 'No tool implemented for "'.concat(toolCall.toolName, '".'),
@@ -14779,8 +14977,9 @@
14779
14977
  init: function init(initOptions) {
14780
14978
  var _this = this;
14781
14979
  var instantSearchInstance = initOptions.instantSearchInstance;
14782
- validateEntryPoints(instantSearchInstance);
14980
+ deferredValidateEntryPoints(instantSearchInstance);
14783
14981
  open = normalizedPersistence.open ? readPersistedOpen(type) : false;
14982
+ records.clear();
14784
14983
  _chatInstance = makeChatInstance(instantSearchInstance);
14785
14984
  var render = function render() {
14786
14985
  renderFn(_object_spread_props(_object_spread({}, _this.getWidgetRenderState(initOptions)), {
@@ -14846,11 +15045,28 @@
14846
15045
  _chatInstance.messages = initialMessages;
14847
15046
  }
14848
15047
  });
15048
+ // Sibling entry points read `status` through the shared `renderState` to
15049
+ // disable themselves, so a transition has to escape this widget's own
15050
+ // render. Message deltas deliberately don't: they stay local to keep
15051
+ // streaming cheap. The `status` setter notifies on every write, hence
15052
+ // the comparison.
15053
+ var lastStatus = _chatInstance.status;
15054
+ var renderOnStatusChange = function renderOnStatusChange() {
15055
+ var statusChanged = _chatInstance.status !== lastStatus;
15056
+ lastStatus = _chatInstance.status;
15057
+ render();
15058
+ if (statusChanged) {
15059
+ initOptions.instantSearchInstance.scheduleRender();
15060
+ }
15061
+ };
14849
15062
  safelyRunOnBrowser(function() {
14850
15063
  chatSubscriptionUnsubscribers = [
14851
15064
  _chatInstance['~registerErrorCallback'](render),
15065
+ // Before `render`, so a delta's records are collected by the time
15066
+ // the tools of that delta render.
15067
+ _chatInstance['~registerMessagesCallback'](collectRecords),
14852
15068
  _chatInstance['~registerMessagesCallback'](render),
14853
- _chatInstance['~registerStatusCallback'](render)
15069
+ _chatInstance['~registerStatusCallback'](renderOnStatusChange)
14854
15070
  ];
14855
15071
  });
14856
15072
  // Resuming and sending reach the network, which a server render must
@@ -14901,6 +15117,9 @@
14901
15117
  function applyFilters(params) {
14902
15118
  return updateStateFromSearchToolInput(params, helper);
14903
15119
  }
15120
+ // A restored or server-rendered conversation never emitted the messages
15121
+ // callback above; collecting is idempotent, so covering it here is free.
15122
+ collectRecords();
14904
15123
  var insightsEventContext = {
14905
15124
  agentId: agentId,
14906
15125
  instantSearchStatus: instantSearchInstance.status
@@ -14913,7 +15132,8 @@
14913
15132
  '~addToolResultForMessage': _chatInstance['~addToolResultForMessage'],
14914
15133
  applyFilters: applyFilters,
14915
15134
  sendEvent: sendEvent,
14916
- insightsEventContext: insightsEventContext
15135
+ insightsEventContext: insightsEventContext,
15136
+ records: records
14917
15137
  });
14918
15138
  toolsWithAddToolResult[key] = toolWithAddToolResult;
14919
15139
  });
@@ -14956,8 +15176,10 @@
14956
15176
  '~isOpenStatePersistenceEnabled': normalizedPersistence.open,
14957
15177
  setMessages: setMessages,
14958
15178
  suggestions: getSuggestionsFromMessages(_chatInstance.messages),
15179
+ suggestionsStatus: getSuggestionsStatus(_chatInstance.messages),
14959
15180
  clearMessages: clearMessages,
14960
15181
  tools: toolsWithAddToolResult,
15182
+ records: records,
14961
15183
  sendChatMessageFeedback: _sendChatMessageFeedback,
14962
15184
  feedbackState: feedbackState,
14963
15185
  widgetParams: widgetParams,
@@ -14976,6 +15198,7 @@
14976
15198
  return renderState;
14977
15199
  },
14978
15200
  dispose: function dispose() {
15201
+ deferredValidateEntryPoints.cancel();
14979
15202
  feedbackAbortController === null || feedbackAbortController === void 0 ? void 0 : feedbackAbortController.abort();
14980
15203
  unsubscribeChatCallbacks();
14981
15204
  unmountFn();
@@ -15057,44 +15280,30 @@
15057
15280
  });
15058
15281
  }
15059
15282
 
15060
- function buildEndpoint(param) {
15061
- var appId = param.appId, agentId = param.agentId;
15062
- return "https://".concat(appId, ".algolia.net/agent-studio/1/agents/").concat(agentId, "/tasks");
15283
+ function isHeaders$1(headers) {
15284
+ return !Array.isArray(headers) && 'entries' in headers && typeof headers.entries === 'function';
15063
15285
  }
15064
- function resolveEndpoint(params) {
15065
- if (params.transport) {
15066
- return {
15067
- endpoint: params.transport.api,
15068
- headers: params.transport.headers || {},
15069
- prepareSendMessagesRequest: params.transport.prepareSendMessagesRequest
15070
- };
15286
+ function headersToRecord$1(headers) {
15287
+ if (!headers) {
15288
+ return {};
15071
15289
  }
15072
- if (!params.appId || !params.apiKey || !params.agentId) {
15073
- throw new Error('[tasks] Either `transport` or `{ appId, apiKey, agentId }` is required.');
15290
+ if (isHeaders$1(headers)) {
15291
+ return Object.fromEntries(headers.entries());
15074
15292
  }
15075
- var headers = {
15076
- 'x-algolia-application-id': params.appId,
15077
- 'x-algolia-api-key': params.apiKey
15078
- };
15079
- if (params.algoliaAgent) {
15080
- headers['x-algolia-agent'] = "".concat(params.algoliaAgent, "; tasks");
15293
+ if (Array.isArray(headers)) {
15294
+ return Object.fromEntries(headers);
15081
15295
  }
15082
- return {
15083
- endpoint: buildEndpoint({
15084
- appId: params.appId,
15085
- agentId: params.agentId
15086
- }),
15087
- headers: headers
15088
- };
15296
+ return headers;
15089
15297
  }
15090
-
15091
- function buildTaskPayload(param) {
15092
- var task = param.task, input = param.input, prepareRequest = param.prepareRequest;
15093
- var payload = {
15094
- task: task,
15095
- input: input
15096
- };
15097
- return prepareRequest ? prepareRequest(payload).body : payload;
15298
+ function withJsonContentType(headers) {
15299
+ var merged = _object_spread({}, headersToRecord$1(headers));
15300
+ Object.keys(merged).forEach(function(name) {
15301
+ if (name.toLowerCase() === 'content-type') {
15302
+ delete merged[name];
15303
+ }
15304
+ });
15305
+ merged['Content-Type'] = 'application/json';
15306
+ return merged;
15098
15307
  }
15099
15308
  function withStreamParam(url) {
15100
15309
  return url.includes('?') ? "".concat(url, "&stream=true") : "".concat(url, "?stream=true");
@@ -15102,6 +15311,40 @@
15102
15311
  function resolveStreamedOutput(data, previous) {
15103
15312
  return typeof data === 'string' ? parsePartialJson(data, previous) : data;
15104
15313
  }
15314
+ function createTaskPreparationContext(context) {
15315
+ function hideProperty(key) {
15316
+ var value = context[key];
15317
+ Object.defineProperty(context, key, {
15318
+ configurable: true,
15319
+ enumerable: false,
15320
+ get: function get() {
15321
+ return value;
15322
+ },
15323
+ set: function set(nextValue) {
15324
+ Reflect.deleteProperty(context, key);
15325
+ Object.defineProperty(context, key, {
15326
+ configurable: true,
15327
+ enumerable: true,
15328
+ value: nextValue,
15329
+ writable: true
15330
+ });
15331
+ }
15332
+ });
15333
+ }
15334
+ // Rich metadata stays out of legacy body spreads until assigned as payload.
15335
+ hideProperty('stream');
15336
+ hideProperty('body');
15337
+ hideProperty('credentials');
15338
+ hideProperty('headers');
15339
+ hideProperty('api');
15340
+ return context;
15341
+ }
15342
+ function unwrap(envelope) {
15343
+ if ((typeof envelope === "undefined" ? "undefined" : _type_of(envelope)) === 'object' && envelope !== null && 'output' in envelope) {
15344
+ return envelope.output;
15345
+ }
15346
+ return undefined;
15347
+ }
15105
15348
  function consumeTaskStream(body, onData) {
15106
15349
  return new Promise(function(resolve, reject) {
15107
15350
  var chunkStream = parseJsonEventStream(body);
@@ -15110,10 +15353,6 @@
15110
15353
  if (!chunk) {
15111
15354
  return;
15112
15355
  }
15113
- // A terminal `error` event aborts the task: reject rather than let the
15114
- // stream close and resolve the last partial snapshot as a success.
15115
- // Throwing here lets `processStream` release the reader and stop
15116
- // consuming; the rejection propagates to the caller's `.catch`.
15117
15356
  if (chunk.type === 'error') {
15118
15357
  throw new Error(chunk.errorText || 'Task stream error');
15119
15358
  }
@@ -15129,48 +15368,207 @@
15129
15368
  }, reject);
15130
15369
  });
15131
15370
  }
15132
- function fetchTask(param) {
15133
- var endpoint = param.endpoint, headers = param.headers, payload = param.payload, onData = param.onData, _param_stream = param.stream, stream = _param_stream === void 0 ? true : _param_stream;
15134
- return fetch(stream ? withStreamParam(endpoint) : endpoint, {
15135
- method: 'POST',
15136
- headers: _object_spread_props(_object_spread({}, headers), {
15137
- 'Content-Type': 'application/json'
15138
- }),
15139
- body: JSON.stringify(payload)
15140
- }).then(function(response) {
15141
- var _response_headers_get, _response_headers;
15142
- if (!response.ok) {
15143
- throw new Error("HTTP error ".concat(response.status));
15144
- }
15145
- var contentType = ((_response_headers = response.headers) === null || _response_headers === void 0 ? void 0 : (_response_headers_get = _response_headers.get) === null || _response_headers_get === void 0 ? void 0 : _response_headers_get.call(_response_headers, 'content-type')) || '';
15146
- if (stream && response.body && contentType.includes('text/event-stream')) {
15147
- return consumeTaskStream(response.body, onData);
15371
+ /** Default HTTP transport for named Tasks requests and task-output streams. */ var DefaultTaskTransport = /*#__PURE__*/ function() {
15372
+ function DefaultTaskTransport() {
15373
+ var _ref = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}, _ref_api = _ref.api, api = _ref_api === void 0 ? '/api/tasks' : _ref_api, credentials = _ref.credentials, headers = _ref.headers, body = _ref.body, customFetch = _ref.fetch, prepareSendMessagesRequest = _ref.prepareSendMessagesRequest;
15374
+ _class_call_check(this, DefaultTaskTransport);
15375
+ _define_property(this, "api", void 0);
15376
+ _define_property(this, "credentials", void 0);
15377
+ _define_property(this, "headers", void 0);
15378
+ _define_property(this, "body", void 0);
15379
+ _define_property(this, "fetch", void 0);
15380
+ _define_property(this, "prepareSendMessagesRequest", void 0);
15381
+ this.api = api;
15382
+ this.credentials = credentials;
15383
+ this.headers = headers;
15384
+ this.body = body;
15385
+ this.fetch = customFetch;
15386
+ this.prepareSendMessagesRequest = prepareSendMessagesRequest;
15387
+ }
15388
+ _create_class(DefaultTaskTransport, [
15389
+ {
15390
+ key: "sendTask",
15391
+ value: function sendTask(param) {
15392
+ var task = param.task, input = param.input, stream = param.stream, onData = param.onData;
15393
+ return this.sendTaskRequest({
15394
+ task: task,
15395
+ input: input,
15396
+ stream: stream,
15397
+ onData: onData ? function(data) {
15398
+ return onData(unwrap(data));
15399
+ } : undefined
15400
+ }).then(unwrap);
15401
+ }
15402
+ },
15403
+ {
15404
+ /** @internal */ key: "sendTaskRequest",
15405
+ value: function sendTaskRequest(param) {
15406
+ var _this = this;
15407
+ var task = param.task, input = param.input, stream = param.stream, onData = param.onData;
15408
+ var _this_fetch;
15409
+ var fetchFn = (_this_fetch = this.fetch) !== null && _this_fetch !== void 0 ? _this_fetch : fetch;
15410
+ return Promise.all([
15411
+ resolveValue(this.credentials),
15412
+ resolveValue(this.headers),
15413
+ resolveValue(this.body)
15414
+ ]).then(function(param) {
15415
+ var _param = _sliced_to_array(param, 3), resolvedCredentials = _param[0], resolvedHeaders = _param[1], resolvedBody = _param[2];
15416
+ var api = _this.api;
15417
+ var credentials = resolvedCredentials;
15418
+ var headers = withJsonContentType(resolvedHeaders);
15419
+ var body = _object_spread({
15420
+ task: task,
15421
+ input: input
15422
+ }, resolvedBody);
15423
+ var preparedBody = resolvedBody ? _object_spread({}, resolvedBody) : undefined;
15424
+ var preparePromise = _this.prepareSendMessagesRequest ? Promise.resolve(_this.prepareSendMessagesRequest(createTaskPreparationContext({
15425
+ task: task,
15426
+ input: input,
15427
+ stream: stream,
15428
+ body: preparedBody,
15429
+ credentials: resolvedCredentials,
15430
+ headers: resolvedHeaders,
15431
+ api: _this.api
15432
+ }))) : Promise.resolve(null);
15433
+ return preparePromise.then(function(prepared) {
15434
+ if (prepared) {
15435
+ body = prepared.body;
15436
+ if (prepared.api) {
15437
+ api = prepared.api;
15438
+ }
15439
+ if (prepared.credentials) {
15440
+ credentials = prepared.credentials;
15441
+ }
15442
+ if (prepared.headers) {
15443
+ headers = withJsonContentType(prepared.headers);
15444
+ }
15445
+ }
15446
+ var request = {
15447
+ method: 'POST',
15448
+ headers: headers,
15449
+ body: JSON.stringify(body)
15450
+ };
15451
+ if (credentials !== undefined) {
15452
+ request.credentials = credentials;
15453
+ }
15454
+ return fetchFn(stream ? withStreamParam(api) : api, request).then(function(response) {
15455
+ var _response_headers_get, _response_headers;
15456
+ if (!response.ok) {
15457
+ throw new Error("HTTP error ".concat(response.status));
15458
+ }
15459
+ var contentType = ((_response_headers = response.headers) === null || _response_headers === void 0 ? void 0 : (_response_headers_get = _response_headers.get) === null || _response_headers_get === void 0 ? void 0 : _response_headers_get.call(_response_headers, 'content-type')) || '';
15460
+ if (stream && response.body && contentType.includes('text/event-stream')) {
15461
+ return consumeTaskStream(response.body, onData);
15462
+ }
15463
+ return response.json();
15464
+ });
15465
+ });
15466
+ });
15467
+ }
15148
15468
  }
15149
- return response.json();
15469
+ ]);
15470
+ return DefaultTaskTransport;
15471
+ }();
15472
+
15473
+ function buildEndpoint(param) {
15474
+ var appId = param.appId, agentId = param.agentId;
15475
+ return "https://".concat(appId, ".algolia.net/agent-studio/1/agents/").concat(agentId, "/tasks");
15476
+ }
15477
+ function isHeaders(headers) {
15478
+ return !Array.isArray(headers) && 'entries' in headers && typeof headers.entries === 'function';
15479
+ }
15480
+ function headersToRecord(headers) {
15481
+ if (!headers) {
15482
+ return {};
15483
+ }
15484
+ if (isHeaders(headers)) {
15485
+ return Object.fromEntries(headers.entries());
15486
+ }
15487
+ if (Array.isArray(headers)) {
15488
+ return Object.fromEntries(headers);
15489
+ }
15490
+ return _object_spread({}, headers);
15491
+ }
15492
+ function mergeProtectedHeaders(headers, protectedHeaders) {
15493
+ var merged = headersToRecord(headers);
15494
+ Object.entries(protectedHeaders).forEach(function(param) {
15495
+ var _param = _sliced_to_array(param, 2), protectedName = _param[0], value = _param[1];
15496
+ Object.keys(merged).forEach(function(name) {
15497
+ if (name.toLowerCase() === protectedName.toLowerCase()) {
15498
+ delete merged[name];
15499
+ }
15500
+ });
15501
+ merged[protectedName] = value;
15150
15502
  });
15503
+ return merged;
15151
15504
  }
15152
- function unwrap(envelope) {
15153
- return envelope === null || envelope === void 0 ? void 0 : envelope.output;
15505
+ /** @internal */ function createTaskTransport(param) {
15506
+ var _param_transport = param.transport, transport = _param_transport === void 0 ? {} : _param_transport, appId = param.appId, apiKey = param.apiKey, agentId = param.agentId, algoliaAgent = param.algoliaAgent;
15507
+ var _transport_api;
15508
+ if (!agentId) {
15509
+ return new DefaultTaskTransport(transport);
15510
+ }
15511
+ if (!appId || !apiKey) {
15512
+ throw new Error('[tasks] `appId` and `apiKey` are required when `agentId` is provided.');
15513
+ }
15514
+ var protectedHeaders = {
15515
+ 'x-algolia-application-id': appId,
15516
+ 'x-algolia-api-key': apiKey
15517
+ };
15518
+ if (algoliaAgent) {
15519
+ protectedHeaders['x-algolia-agent'] = "".concat(algoliaAgent, "; tasks");
15520
+ }
15521
+ var originalPrepare = transport.prepareSendMessagesRequest;
15522
+ var prepareSendMessagesRequest = originalPrepare ? function(request) {
15523
+ return Promise.resolve(originalPrepare(request)).then(function(prepared) {
15524
+ return _object_spread_props(_object_spread({}, prepared), {
15525
+ headers: prepared.headers ? mergeProtectedHeaders(prepared.headers, protectedHeaders) : undefined
15526
+ });
15527
+ });
15528
+ } : undefined;
15529
+ return new DefaultTaskTransport(_object_spread_props(_object_spread({}, transport), {
15530
+ api: (_transport_api = transport.api) !== null && _transport_api !== void 0 ? _transport_api : buildEndpoint({
15531
+ appId: appId,
15532
+ agentId: agentId
15533
+ }),
15534
+ headers: function headers() {
15535
+ return Promise.resolve(resolveValue(transport.headers)).then(function(headers) {
15536
+ return mergeProtectedHeaders(headers, protectedHeaders);
15537
+ });
15538
+ },
15539
+ prepareSendMessagesRequest: prepareSendMessagesRequest
15540
+ }));
15154
15541
  }
15155
- function createTaskRunner(param) {
15156
- var endpoint = param.endpoint, headers = param.headers, task = param.task, _param_stream = param.stream, stream = _param_stream === void 0 ? true : _param_stream, prepareRequest = param.prepareRequest;
15542
+
15543
+ function createTaskRunner(options) {
15544
+ var task = options.task, _options_stream = options.stream, stream = _options_stream === void 0 ? true : _options_stream;
15545
+ var transport;
15546
+ if (options.transport !== undefined) {
15547
+ transport = options.transport;
15548
+ } else {
15549
+ var prepareRequest = options.prepareRequest;
15550
+ transport = new DefaultTaskTransport({
15551
+ api: options.endpoint,
15552
+ headers: options.headers,
15553
+ fetch: options.fetch,
15554
+ prepareSendMessagesRequest: prepareRequest ? function(param) {
15555
+ var requestTask = param.task, input = param.input;
15556
+ return prepareRequest({
15557
+ task: requestTask,
15558
+ input: input
15559
+ });
15560
+ } : undefined
15561
+ });
15562
+ }
15157
15563
  return {
15158
- submit: function submit(variables) {
15564
+ submit: function submit(input) {
15159
15565
  var onData = (arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}).onData;
15160
- var payload = buildTaskPayload({
15566
+ return transport.sendTask({
15161
15567
  task: task,
15162
- input: variables,
15163
- prepareRequest: prepareRequest
15164
- });
15165
- return fetchTask({
15166
- endpoint: endpoint,
15167
- headers: headers,
15168
- payload: payload,
15568
+ input: input,
15169
15569
  stream: stream,
15170
- onData: onData ? function(partial) {
15171
- return onData(unwrap(partial));
15172
- } : undefined
15173
- }).then(unwrap);
15570
+ onData: onData
15571
+ });
15174
15572
  }
15175
15573
  };
15176
15574
  }
@@ -15257,31 +15655,28 @@
15257
15655
  $$type: 'ais.tasks',
15258
15656
  init: function init(initOptions) {
15259
15657
  var instantSearchInstance = initOptions.instantSearchInstance;
15260
- if (transport) {
15261
- var resolved = resolveEndpoint({
15262
- transport: transport
15263
- });
15264
- runner = createTaskRunner({
15265
- endpoint: resolved.endpoint,
15266
- headers: resolved.headers,
15267
- task: task,
15268
- stream: stream,
15269
- prepareRequest: resolved.prepareSendMessagesRequest
15270
- });
15271
- } else {
15658
+ if (agentId) {
15272
15659
  var _getAppIdAndApiKey = _sliced_to_array(getAppIdAndApiKey(instantSearchInstance.client), 2), appId = _getAppIdAndApiKey[0], apiKey = _getAppIdAndApiKey[1];
15273
15660
  if (!appId || !apiKey) {
15274
15661
  throw new Error(withUsage$p('Could not extract Algolia credentials from the search client.'));
15275
15662
  }
15276
- var resolved1 = resolveEndpoint({
15663
+ var taskTransport = createTaskTransport({
15664
+ transport: transport,
15277
15665
  appId: appId,
15278
15666
  apiKey: apiKey,
15279
15667
  agentId: agentId,
15280
15668
  algoliaAgent: getAlgoliaAgent(instantSearchInstance.client)
15281
15669
  });
15282
15670
  runner = createTaskRunner({
15283
- endpoint: resolved1.endpoint,
15284
- headers: resolved1.headers,
15671
+ transport: taskTransport,
15672
+ task: task,
15673
+ stream: stream
15674
+ });
15675
+ } else {
15676
+ runner = createTaskRunner({
15677
+ transport: createTaskTransport({
15678
+ transport: transport
15679
+ }),
15285
15680
  task: task,
15286
15681
  stream: stream
15287
15682
  });
@@ -15308,6 +15703,49 @@
15308
15703
  };
15309
15704
  };
15310
15705
 
15706
+ // Centralizes the "open the chat from an entry point" behavior shared by the
15707
+ // SearchBox AI button, the Autocomplete AI button, prompt suggestions, and any
15708
+ // future entry point. The chat is always opened; the message is only sent when
15709
+ // it is non-empty and the chat is not already processing a message.
15710
+ // Returns true when a message was submitted, so callers can clear their input.
15711
+ function openChat(chatRenderState) {
15712
+ var _ref = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}, message = _ref.message, referer = _ref.referer, turnContext = _ref.turnContext;
15713
+ var _ref1;
15714
+ var _chatRenderState_setOpen;
15715
+ if (!chatRenderState) {
15716
+ return false;
15717
+ }
15718
+ var trimmed = (_ref1 = message === null || message === void 0 ? void 0 : message.trim()) !== null && _ref1 !== void 0 ? _ref1 : '';
15719
+ if (!trimmed) {
15720
+ if (chatRenderState.focusInput) {
15721
+ chatRenderState.focusInput();
15722
+ } else {
15723
+ var _chatRenderState_setOpen1;
15724
+ (_chatRenderState_setOpen1 = chatRenderState.setOpen) === null || _chatRenderState_setOpen1 === void 0 ? void 0 : _chatRenderState_setOpen1.call(chatRenderState, true);
15725
+ }
15726
+ return false;
15727
+ }
15728
+ (_chatRenderState_setOpen = chatRenderState.setOpen) === null || _chatRenderState_setOpen === void 0 ? void 0 : _chatRenderState_setOpen.call(chatRenderState, true);
15729
+ if (isChatBusy(chatRenderState) || !chatRenderState.sendMessage) {
15730
+ return false;
15731
+ }
15732
+ chatRenderState.sendMessage(_object_spread({
15733
+ text: trimmed
15734
+ }, turnContext ? {
15735
+ metadata: {
15736
+ turnContext: turnContext
15737
+ }
15738
+ } : {}), referer ? {
15739
+ headers: {
15740
+ 'x-algolia-referer': referer
15741
+ }
15742
+ } : undefined);
15743
+ return true;
15744
+ }
15745
+ function isChatBusy(chatRenderState) {
15746
+ return (chatRenderState === null || chatRenderState === void 0 ? void 0 : chatRenderState.status) === 'submitted' || (chatRenderState === null || chatRenderState === void 0 ? void 0 : chatRenderState.status) === 'streaming';
15747
+ }
15748
+
15311
15749
  var withUsage$o = createDocumentationMessageGenerator({
15312
15750
  name: 'prompt-suggestions',
15313
15751
  connector: true
@@ -15549,14 +15987,24 @@
15549
15987
  if (!latestRenderOptions) return;
15550
15988
  renderOutward(latestRenderOptions);
15551
15989
  };
15552
- var tasksWidget = connectTasks(handleInnerRender, noop)(_object_spread_props(_object_spread({}, transport ? {
15553
- transport: transport
15554
- } : {
15555
- agentId: agentId
15556
- }), {
15557
- task: configurationId,
15558
- stream: true
15559
- }));
15990
+ var tasksParams;
15991
+ if (agentId) {
15992
+ tasksParams = {
15993
+ agentId: agentId,
15994
+ transport: transport,
15995
+ task: configurationId,
15996
+ stream: true
15997
+ };
15998
+ } else if (transport) {
15999
+ tasksParams = {
16000
+ transport: transport,
16001
+ task: configurationId,
16002
+ stream: true
16003
+ };
16004
+ } else {
16005
+ throw new Error(withUsage$o('The `agentId` option is required unless a custom `transport` is provided.'));
16006
+ }
16007
+ var tasksWidget = connectTasks(handleInnerRender, noop)(tasksParams);
15560
16008
  return {
15561
16009
  $$type: 'ais.promptSuggestions',
15562
16010
  init: function init(initOptions) {
@@ -15629,6 +16077,10 @@
15629
16077
  return function(widgetParams) {
15630
16078
  var params = widgetParams !== null && widgetParams !== void 0 ? widgetParams : {};
15631
16079
  var lastOptions = null;
16080
+ function openChatFromTrigger(options) {
16081
+ if (!lastOptions) return false;
16082
+ return openChat(getChatRenderState(lastOptions), options);
16083
+ }
15632
16084
  function toggleOpen() {
15633
16085
  if (!lastOptions) return;
15634
16086
  var chatState = getChatRenderState(lastOptions);
@@ -15665,6 +16117,8 @@
15665
16117
  return {
15666
16118
  open: (_ref = chatState === null || chatState === void 0 ? void 0 : chatState.open) !== null && _ref !== void 0 ? _ref : false,
15667
16119
  toggleOpen: toggleOpen,
16120
+ openChat: openChatFromTrigger,
16121
+ isChatBusy: chatState ? !chatState.sendMessage || isChatBusy(chatState) : false,
15668
16122
  widgetParams: params
15669
16123
  };
15670
16124
  },