@assistant-ui/react 0.4.8 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.js CHANGED
@@ -53,11 +53,13 @@ __export(src_exports, {
53
53
  ThreadWelcome: () => thread_welcome_default,
54
54
  UserActionBar: () => user_action_bar_default,
55
55
  UserMessage: () => user_message_default,
56
+ fromCoreMessage: () => fromCoreMessage,
56
57
  fromCoreMessages: () => fromCoreMessages,
57
58
  fromLanguageModelMessages: () => fromLanguageModelMessages,
58
59
  fromLanguageModelTools: () => fromLanguageModelTools,
59
60
  makeAssistantTool: () => makeAssistantTool,
60
61
  makeAssistantToolUI: () => makeAssistantToolUI,
62
+ toCoreMessage: () => toCoreMessage,
61
63
  toCoreMessages: () => toCoreMessages,
62
64
  toLanguageModelMessages: () => toLanguageModelMessages,
63
65
  toLanguageModelTools: () => toLanguageModelTools,
@@ -397,13 +399,17 @@ var ThreadProvider = ({
397
399
  (0, import_react4.useCallback)(
398
400
  (thread) => {
399
401
  const onThreadUpdate = () => {
400
- context.useThread.setState(
401
- Object.freeze({
402
- isRunning: context.useThreadRuntime.getState().isRunning
403
- }),
404
- true
405
- );
406
- context.useThreadMessages.setState(thread.messages, true);
402
+ if (thread.isRunning !== context.useThread.getState().isRunning) {
403
+ context.useThread.setState(
404
+ Object.freeze({
405
+ isRunning: thread.isRunning
406
+ }),
407
+ true
408
+ );
409
+ }
410
+ if (thread.messages !== context.useThreadMessages.getState()) {
411
+ context.useThreadMessages.setState(thread.messages, true);
412
+ }
407
413
  };
408
414
  onThreadUpdate();
409
415
  return thread.subscribe(onThreadUpdate);
@@ -1256,12 +1262,35 @@ var import_react38 = require("react");
1256
1262
  var import_react34 = require("react");
1257
1263
  var import_zustand10 = require("zustand");
1258
1264
  var import_jsx_runtime13 = require("react/jsx-runtime");
1259
- var DONE_STATUS = { type: "done" };
1265
+ var COMPLETE_STATUS = {
1266
+ type: "complete"
1267
+ };
1268
+ var toContentPartStatus = (message, partIndex, part) => {
1269
+ if (message.role !== "assistant") return COMPLETE_STATUS;
1270
+ const isLastPart = partIndex === Math.max(0, message.content.length - 1);
1271
+ if (part.type !== "tool-call") {
1272
+ if ("reason" in message.status && message.status.reason === "tool-calls" && isLastPart)
1273
+ throw new Error(
1274
+ "Encountered unexpected requires-action status. This is likely an internal bug in assistant-ui."
1275
+ );
1276
+ return isLastPart ? message.status : COMPLETE_STATUS;
1277
+ }
1278
+ if (!!part.result) {
1279
+ return COMPLETE_STATUS;
1280
+ }
1281
+ return message.status;
1282
+ };
1283
+ var EMPTY_CONTENT = Object.freeze({ type: "text", text: "" });
1260
1284
  var syncContentPart = ({ message }, useContentPart, partIndex) => {
1261
- const part = message.content[partIndex];
1262
- if (!part) return;
1263
- const messageStatus = message.role === "assistant" ? message.status : DONE_STATUS;
1264
- const status = partIndex === message.content.length - 1 ? messageStatus : DONE_STATUS;
1285
+ let part = message.content[partIndex];
1286
+ if (!part) {
1287
+ if (message.content.length === 0 && partIndex === 0) {
1288
+ part = EMPTY_CONTENT;
1289
+ } else {
1290
+ return;
1291
+ }
1292
+ }
1293
+ const status = toContentPartStatus(message, partIndex, part);
1265
1294
  const currentState = useContentPart.getState();
1266
1295
  if (currentState.part === part && currentState.status === status) return;
1267
1296
  useContentPart.setState(
@@ -1386,7 +1415,7 @@ var ContentPartPrimitiveText = (0, import_react36.forwardRef)(({ smooth = true,
1386
1415
  part: { text }
1387
1416
  } = useContentPartText();
1388
1417
  const smoothText = useSmooth(text, smooth);
1389
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_primitive4.Primitive.span, { "data-status": status, ...rest, ref: forwardedRef, children: smoothText });
1418
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_primitive4.Primitive.span, { "data-status": status.type, ...rest, ref: forwardedRef, children: smoothText });
1390
1419
  });
1391
1420
  ContentPartPrimitiveText.displayName = "ContentPartPrimitive.Text";
1392
1421
 
@@ -1414,7 +1443,7 @@ ContentPartPrimitiveDisplay.displayName = "ContentPartPrimitive.Display";
1414
1443
  // src/primitives/contentPart/ContentPartInProgress.tsx
1415
1444
  var ContentPartPrimitiveInProgress = ({ children }) => {
1416
1445
  const { useContentPart } = useContentPartContext();
1417
- const isInProgress = useContentPart((c) => c.status.type === "in_progress");
1446
+ const isInProgress = useContentPart((c) => c.status.type === "running");
1418
1447
  return isInProgress ? children : null;
1419
1448
  };
1420
1449
  ContentPartPrimitiveInProgress.displayName = "ContentPartPrimitive.InProgress";
@@ -1453,10 +1482,16 @@ var MessageContentPartComponent = ({
1453
1482
  const type = part.type;
1454
1483
  switch (type) {
1455
1484
  case "text":
1485
+ if (status.type === "requires-action")
1486
+ throw new Error("Encountered unexpected requires-action status");
1456
1487
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Text2, { part, status });
1457
1488
  case "image":
1489
+ if (status.type === "requires-action")
1490
+ throw new Error("Encountered unexpected requires-action status");
1458
1491
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Image2, { part, status });
1459
1492
  case "ui":
1493
+ if (status.type === "requires-action")
1494
+ throw new Error("Encountered unexpected requires-action status");
1460
1495
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(UI, { part, status });
1461
1496
  case "tool-call": {
1462
1497
  const Tool = by_name[part.toolName] || Fallback2;
@@ -1486,7 +1521,7 @@ var MessagePrimitiveContent = ({
1486
1521
  components
1487
1522
  }) => {
1488
1523
  const { useMessage } = useMessageContext();
1489
- const contentLength = useMessage((s) => s.message.content.length);
1524
+ const contentLength = useMessage((s) => s.message.content.length) || 1;
1490
1525
  return new Array(contentLength).fill(null).map((_, idx) => {
1491
1526
  const partIndex = idx;
1492
1527
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
@@ -1783,7 +1818,7 @@ var useThreadViewportAutoScroll = ({
1783
1818
  const div = divRef.current;
1784
1819
  if (!div) return;
1785
1820
  const isAtBottom = useViewport.getState().isAtBottom;
1786
- const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
1821
+ const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight + 1;
1787
1822
  if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
1788
1823
  } else {
1789
1824
  if (newIsAtBottom) {
@@ -2019,6 +2054,40 @@ var ThreadPrimitiveSuggestion = createActionButton(
2019
2054
  // src/runtimes/local/useLocalRuntime.tsx
2020
2055
  var import_react54 = require("react");
2021
2056
 
2057
+ // src/runtimes/core/BaseAssistantRuntime.tsx
2058
+ var BaseAssistantRuntime = class {
2059
+ constructor(_thread) {
2060
+ this._thread = _thread;
2061
+ this._thread = _thread;
2062
+ }
2063
+ get thread() {
2064
+ return this._thread;
2065
+ }
2066
+ set thread(thread) {
2067
+ this._thread = thread;
2068
+ this.subscriptionHandler();
2069
+ }
2070
+ _subscriptions = /* @__PURE__ */ new Set();
2071
+ subscribe(callback) {
2072
+ this._subscriptions.add(callback);
2073
+ return () => this._subscriptions.delete(callback);
2074
+ }
2075
+ subscriptionHandler = () => {
2076
+ for (const callback of this._subscriptions) callback();
2077
+ };
2078
+ };
2079
+
2080
+ // src/internal.ts
2081
+ var internal_exports = {};
2082
+ __export(internal_exports, {
2083
+ BaseAssistantRuntime: () => BaseAssistantRuntime,
2084
+ MessageRepository: () => MessageRepository,
2085
+ ProxyConfigProvider: () => ProxyConfigProvider,
2086
+ TooltipIconButton: () => TooltipIconButton,
2087
+ generateId: () => generateId,
2088
+ useSmooth: () => useSmooth
2089
+ });
2090
+
2022
2091
  // src/utils/idUtils.tsx
2023
2092
  var import_non_secure = require("nanoid/non-secure");
2024
2093
  var generateId = (0, import_non_secure.customAlphabet)(
@@ -2028,6 +2097,54 @@ var generateId = (0, import_non_secure.customAlphabet)(
2028
2097
  var optimisticPrefix = "__optimistic__";
2029
2098
  var generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
2030
2099
 
2100
+ // src/runtimes/edge/converters/fromCoreMessage.ts
2101
+ var fromCoreMessages = (message) => {
2102
+ return message.map((message2) => fromCoreMessage(message2));
2103
+ };
2104
+ var fromCoreMessage = (message, {
2105
+ id = generateId(),
2106
+ status = { type: "complete", reason: "unknown" }
2107
+ } = {}) => {
2108
+ const commonProps = {
2109
+ id,
2110
+ createdAt: /* @__PURE__ */ new Date()
2111
+ };
2112
+ const role = message.role;
2113
+ switch (role) {
2114
+ case "assistant":
2115
+ return {
2116
+ ...commonProps,
2117
+ role,
2118
+ content: message.content.map((part) => {
2119
+ if (part.type === "tool-call") {
2120
+ return {
2121
+ ...part,
2122
+ argsText: JSON.stringify(part.args)
2123
+ };
2124
+ }
2125
+ return part;
2126
+ }),
2127
+ status
2128
+ };
2129
+ case "user":
2130
+ return {
2131
+ ...commonProps,
2132
+ role,
2133
+ content: message.content
2134
+ };
2135
+ case "system":
2136
+ return {
2137
+ ...commonProps,
2138
+ role,
2139
+ content: message.content
2140
+ };
2141
+ default: {
2142
+ const unsupportedRole = role;
2143
+ throw new Error(`Unknown message role: ${unsupportedRole}`);
2144
+ }
2145
+ }
2146
+ };
2147
+
2031
2148
  // src/runtimes/utils/MessageRepository.tsx
2032
2149
  var findHead = (message) => {
2033
2150
  if (message.next) return findHead(message.next);
@@ -2118,12 +2235,13 @@ var MessageRepository = class {
2118
2235
  do {
2119
2236
  optimisticId = generateOptimisticId();
2120
2237
  } while (this.messages.has(optimisticId));
2121
- this.addOrUpdateMessage(parentId, {
2122
- ...message,
2123
- id: optimisticId,
2124
- createdAt: /* @__PURE__ */ new Date(),
2125
- ...message.role === "assistant" ? { status: "in_progress" } : void 0
2126
- });
2238
+ this.addOrUpdateMessage(
2239
+ parentId,
2240
+ fromCoreMessage(message, {
2241
+ id: optimisticId,
2242
+ status: { type: "running" }
2243
+ })
2244
+ );
2127
2245
  return optimisticId;
2128
2246
  }
2129
2247
  deleteMessage(messageId, replacementId) {
@@ -2190,40 +2308,6 @@ var MessageRepository = class {
2190
2308
  }
2191
2309
  };
2192
2310
 
2193
- // src/runtimes/core/BaseAssistantRuntime.tsx
2194
- var BaseAssistantRuntime = class {
2195
- constructor(_thread) {
2196
- this._thread = _thread;
2197
- this._thread = _thread;
2198
- }
2199
- get thread() {
2200
- return this._thread;
2201
- }
2202
- set thread(thread) {
2203
- this._thread = thread;
2204
- this.subscriptionHandler();
2205
- }
2206
- _subscriptions = /* @__PURE__ */ new Set();
2207
- subscribe(callback) {
2208
- this._subscriptions.add(callback);
2209
- return () => this._subscriptions.delete(callback);
2210
- }
2211
- subscriptionHandler = () => {
2212
- for (const callback of this._subscriptions) callback();
2213
- };
2214
- };
2215
-
2216
- // src/internal.ts
2217
- var internal_exports = {};
2218
- __export(internal_exports, {
2219
- BaseAssistantRuntime: () => BaseAssistantRuntime,
2220
- MessageRepository: () => MessageRepository,
2221
- ProxyConfigProvider: () => ProxyConfigProvider,
2222
- TooltipIconButton: () => TooltipIconButton,
2223
- generateId: () => generateId,
2224
- useSmooth: () => useSmooth
2225
- });
2226
-
2227
2311
  // src/ui/base/tooltip-icon-button.tsx
2228
2312
  var import_react52 = require("react");
2229
2313
 
@@ -2538,35 +2622,43 @@ var fromLanguageModelMessages = (lm, { mergeRoundtrips }) => {
2538
2622
  return messages;
2539
2623
  };
2540
2624
 
2541
- // src/runtimes/edge/converters/fromCoreMessage.ts
2542
- var fromCoreMessages = (message) => {
2543
- return message.map((message2) => {
2544
- return {
2545
- id: generateId(),
2546
- createdAt: /* @__PURE__ */ new Date(),
2547
- ...message2.role === "assistant" ? {
2548
- status: { type: "done" }
2549
- } : void 0,
2550
- ...message2
2551
- };
2552
- });
2553
- };
2554
-
2555
2625
  // src/runtimes/edge/converters/toCoreMessages.ts
2556
2626
  var toCoreMessages = (message) => {
2557
- return message.map((message2) => {
2558
- return {
2559
- role: message2.role,
2560
- content: message2.content.map((part) => {
2561
- if (part.type === "ui") throw new Error("UI parts are not supported");
2562
- if (part.type === "tool-call") {
2563
- const { argsText, ...rest } = part;
2564
- return rest;
2565
- }
2566
- return part;
2567
- })
2568
- };
2569
- });
2627
+ return message.map(toCoreMessage);
2628
+ };
2629
+ var toCoreMessage = (message) => {
2630
+ const role = message.role;
2631
+ switch (role) {
2632
+ case "assistant":
2633
+ return {
2634
+ role,
2635
+ content: message.content.map((part) => {
2636
+ if (part.type === "ui") throw new Error("UI parts are not supported");
2637
+ if (part.type === "tool-call") {
2638
+ const { argsText, ...rest } = part;
2639
+ return rest;
2640
+ }
2641
+ return part;
2642
+ })
2643
+ };
2644
+ case "user":
2645
+ return {
2646
+ role,
2647
+ content: message.content.map((part) => {
2648
+ if (part.type === "ui") throw new Error("UI parts are not supported");
2649
+ return part;
2650
+ })
2651
+ };
2652
+ case "system":
2653
+ return {
2654
+ role,
2655
+ content: message.content
2656
+ };
2657
+ default: {
2658
+ const unsupportedRole = role;
2659
+ throw new Error(`Unknown message role: ${unsupportedRole}`);
2660
+ }
2661
+ }
2570
2662
  };
2571
2663
 
2572
2664
  // src/runtimes/edge/converters/fromLanguageModelTools.ts
@@ -3036,9 +3128,10 @@ var parsePartialJson = (json) => {
3036
3128
  };
3037
3129
 
3038
3130
  // src/runtimes/edge/streams/runResultStream.ts
3039
- function runResultStream(initialContent) {
3131
+ function runResultStream() {
3040
3132
  let message = {
3041
- content: initialContent
3133
+ content: [],
3134
+ status: { type: "running" }
3042
3135
  };
3043
3136
  const currentToolCall = { toolCallId: "", argsText: "" };
3044
3137
  return new TransformStream({
@@ -3086,7 +3179,13 @@ function runResultStream(initialContent) {
3086
3179
  break;
3087
3180
  }
3088
3181
  case "error": {
3089
- throw chunk.error;
3182
+ if (chunk.error instanceof Error && chunk.error.name === "AbortError") {
3183
+ message = appendOrUpdateCancel(message);
3184
+ controller.enqueue(message);
3185
+ break;
3186
+ } else {
3187
+ throw chunk.error;
3188
+ }
3090
3189
  }
3091
3190
  default: {
3092
3191
  const unhandledType = chunkType;
@@ -3160,11 +3259,42 @@ var appendOrUpdateToolResult = (message, toolCallId, toolName, result) => {
3160
3259
  };
3161
3260
  var appendOrUpdateFinish = (message, chunk) => {
3162
3261
  const { type, ...rest } = chunk;
3262
+ return {
3263
+ ...message,
3264
+ status: getStatus(chunk),
3265
+ roundtrips: [
3266
+ ...message.roundtrips ?? [],
3267
+ {
3268
+ logprobs: rest.logprobs,
3269
+ usage: rest.usage
3270
+ }
3271
+ ]
3272
+ };
3273
+ };
3274
+ var getStatus = (chunk) => {
3275
+ if (chunk.finishReason === "tool-calls") {
3276
+ return {
3277
+ type: "requires-action",
3278
+ reason: "tool-calls"
3279
+ };
3280
+ } else if (chunk.finishReason === "stop" || chunk.finishReason === "unknown") {
3281
+ return {
3282
+ type: "complete",
3283
+ reason: chunk.finishReason
3284
+ };
3285
+ } else {
3286
+ return {
3287
+ type: "incomplete",
3288
+ reason: chunk.finishReason
3289
+ };
3290
+ }
3291
+ };
3292
+ var appendOrUpdateCancel = (message) => {
3163
3293
  return {
3164
3294
  ...message,
3165
3295
  status: {
3166
- type: "done",
3167
- ...rest
3296
+ type: "incomplete",
3297
+ reason: "cancelled"
3168
3298
  }
3169
3299
  };
3170
3300
  };
@@ -3264,7 +3394,7 @@ var EdgeChatAdapter = class {
3264
3394
  constructor(options) {
3265
3395
  this.options = options;
3266
3396
  }
3267
- async roundtrip(initialContent, { messages, abortSignal, config, onUpdate }) {
3397
+ async run({ messages, abortSignal, config, onUpdate }) {
3268
3398
  const result = await fetch(this.options.api, {
3269
3399
  method: "POST",
3270
3400
  headers: {
@@ -3284,46 +3414,14 @@ var EdgeChatAdapter = class {
3284
3414
  `Edge runtime returned status ${result.status}: ${await result.text()}`
3285
3415
  );
3286
3416
  }
3287
- const stream = result.body.pipeThrough(new TextDecoderStream()).pipeThrough(chunkByLineStream()).pipeThrough(assistantDecoderStream()).pipeThrough(toolResultStream(config.tools)).pipeThrough(runResultStream(initialContent));
3288
- let message;
3417
+ const stream = result.body.pipeThrough(new TextDecoderStream()).pipeThrough(chunkByLineStream()).pipeThrough(assistantDecoderStream()).pipeThrough(toolResultStream(config.tools)).pipeThrough(runResultStream());
3289
3418
  let update;
3290
3419
  for await (update of asAsyncIterable(stream)) {
3291
- message = onUpdate(update);
3420
+ onUpdate(update);
3292
3421
  }
3293
3422
  if (update === void 0)
3294
3423
  throw new Error("No data received from Edge Runtime");
3295
- return [message, update];
3296
- }
3297
- async run({ messages, abortSignal, config, onUpdate }) {
3298
- let roundtripAllowance = this.options.maxToolRoundtrips ?? 1;
3299
- let usage = {
3300
- promptTokens: 0,
3301
- completionTokens: 0
3302
- };
3303
- let result;
3304
- let assistantMessage;
3305
- do {
3306
- [assistantMessage, result] = await this.roundtrip(result?.content ?? [], {
3307
- messages: assistantMessage ? [...messages, assistantMessage] : messages,
3308
- abortSignal,
3309
- config,
3310
- onUpdate
3311
- });
3312
- if (result.status?.type === "done") {
3313
- usage.promptTokens += result.status.usage?.promptTokens ?? 0;
3314
- usage.completionTokens += result.status.usage?.completionTokens ?? 0;
3315
- }
3316
- } while (result.status?.type === "done" && result.status.finishReason === "tool-calls" && result.content.every((c) => c.type !== "tool-call" || !!c.result) && roundtripAllowance-- > 0);
3317
- if (result.status?.type === "done" && usage.promptTokens > 0) {
3318
- result = {
3319
- ...result,
3320
- status: {
3321
- ...result.status,
3322
- usage
3323
- }
3324
- };
3325
- }
3326
- return result;
3424
+ return update;
3327
3425
  }
3328
3426
  };
3329
3427
 
@@ -3336,30 +3434,10 @@ var useEdgeRuntime = ({
3336
3434
  return useLocalRuntime(adapter, { initialMessages });
3337
3435
  };
3338
3436
 
3339
- // src/runtimes/local/LocalRuntime.tsx
3340
- var LocalRuntime = class extends BaseAssistantRuntime {
3341
- _proxyConfigProvider;
3342
- constructor(adapter, options) {
3343
- const proxyConfigProvider = new ProxyConfigProvider();
3344
- super(new LocalThreadRuntime(proxyConfigProvider, adapter, options));
3345
- this._proxyConfigProvider = proxyConfigProvider;
3346
- }
3347
- set adapter(adapter) {
3348
- this.thread.adapter = adapter;
3349
- }
3350
- registerModelConfigProvider(provider) {
3351
- return this._proxyConfigProvider.registerModelConfigProvider(provider);
3352
- }
3353
- switchToThread(threadId) {
3354
- if (threadId) {
3355
- throw new Error("LocalRuntime does not yet support switching threads");
3356
- }
3357
- return this.thread = new LocalThreadRuntime(
3358
- this._proxyConfigProvider,
3359
- this.thread.adapter
3360
- );
3361
- }
3362
- };
3437
+ // src/runtimes/local/shouldContinue.tsx
3438
+ var shouldContinue = (result) => result.status?.type === "requires-action" && result.status.reason === "tool-calls" && result.content.every((c) => c.type !== "tool-call" || !!c.result);
3439
+
3440
+ // src/runtimes/local/LocalThreadRuntime.tsx
3363
3441
  var CAPABILITIES = Object.freeze({
3364
3442
  edit: true,
3365
3443
  reload: true,
@@ -3370,6 +3448,7 @@ var LocalThreadRuntime = class {
3370
3448
  constructor(configProvider, adapter, options) {
3371
3449
  this.configProvider = configProvider;
3372
3450
  this.adapter = adapter;
3451
+ this.options = options;
3373
3452
  if (options?.initialMessages) {
3374
3453
  let parentId = null;
3375
3454
  const messages = fromCoreMessages(options.initialMessages);
@@ -3413,27 +3492,51 @@ var LocalThreadRuntime = class {
3413
3492
  }
3414
3493
  async startRun(parentId) {
3415
3494
  this.repository.resetHead(parentId);
3416
- const messages = this.repository.getMessages();
3495
+ const id = generateId();
3417
3496
  let message = {
3418
- id: generateId(),
3497
+ id,
3419
3498
  role: "assistant",
3420
- status: { type: "in_progress" },
3421
- content: [{ type: "text", text: "" }],
3499
+ status: { type: "running" },
3500
+ content: [],
3422
3501
  createdAt: /* @__PURE__ */ new Date()
3423
3502
  };
3503
+ do {
3504
+ message = await this.performRoundtrip(parentId, message);
3505
+ } while (shouldContinue(message));
3506
+ }
3507
+ async performRoundtrip(parentId, message) {
3508
+ const messages = this.repository.getMessages();
3424
3509
  this.abortController?.abort();
3425
3510
  this.abortController = new AbortController();
3426
- this.repository.addOrUpdateMessage(parentId, { ...message });
3427
- this.notifySubscribers();
3511
+ const initialContent = message.content;
3512
+ const initialRoundtrips = message.roundtrips;
3428
3513
  const updateMessage = (m) => {
3429
3514
  message = {
3430
3515
  ...message,
3431
- ...m
3516
+ ...m.content ? { content: [...initialContent, ...m.content ?? []] } : void 0,
3517
+ status: m.status ?? message.status,
3518
+ ...m.roundtrips?.length ? { roundtrips: [...initialRoundtrips ?? [], ...m.roundtrips] } : void 0
3432
3519
  };
3433
3520
  this.repository.addOrUpdateMessage(parentId, message);
3434
3521
  this.notifySubscribers();
3435
- return message;
3436
3522
  };
3523
+ const maxToolRoundtrips = this.options?.maxToolRoundtrips ?? 1;
3524
+ const toolRoundtrips = message.roundtrips?.length ?? 0;
3525
+ if (toolRoundtrips > maxToolRoundtrips) {
3526
+ updateMessage({
3527
+ status: {
3528
+ type: "incomplete",
3529
+ reason: "tool-calls"
3530
+ }
3531
+ });
3532
+ return message;
3533
+ } else {
3534
+ updateMessage({
3535
+ status: {
3536
+ type: "running"
3537
+ }
3538
+ });
3539
+ }
3437
3540
  try {
3438
3541
  const result = await this.adapter.run({
3439
3542
  messages,
@@ -3441,21 +3544,29 @@ var LocalThreadRuntime = class {
3441
3544
  config: this.configProvider.getModelConfig(),
3442
3545
  onUpdate: updateMessage
3443
3546
  });
3444
- if (result.status?.type === "in_progress")
3547
+ if (result.status?.type === "running")
3445
3548
  throw new Error(
3446
- "Unexpected in_progress status returned from ChatModelAdapter"
3549
+ "Unexpected running status returned from ChatModelAdapter"
3447
3550
  );
3448
3551
  this.abortController = null;
3449
- updateMessage({ status: { type: "done" }, ...result });
3450
- this.repository.addOrUpdateMessage(parentId, { ...message });
3451
- } catch (e) {
3452
- const isAbortError = e instanceof Error && e.name === "AbortError";
3453
- this.abortController = null;
3454
3552
  updateMessage({
3455
- status: isAbortError ? { type: "cancelled" } : { type: "error", error: e }
3553
+ status: { type: "complete", reason: "unknown" },
3554
+ ...result
3456
3555
  });
3457
- if (!isAbortError) throw e;
3556
+ } catch (e) {
3557
+ this.abortController = null;
3558
+ if (e instanceof Error && e.name === "AbortError") {
3559
+ updateMessage({
3560
+ status: { type: "incomplete", reason: "cancelled" }
3561
+ });
3562
+ } else {
3563
+ updateMessage({
3564
+ status: { type: "incomplete", reason: "error", error: e }
3565
+ });
3566
+ throw e;
3567
+ }
3458
3568
  }
3569
+ return message;
3459
3570
  }
3460
3571
  cancelRun() {
3461
3572
  if (!this.abortController) return;
@@ -3470,14 +3581,16 @@ var LocalThreadRuntime = class {
3470
3581
  return () => this._subscriptions.delete(callback);
3471
3582
  }
3472
3583
  addToolResult({ messageId, toolCallId, result }) {
3473
- const { parentId, message } = this.repository.getMessage(messageId);
3584
+ let { parentId, message } = this.repository.getMessage(messageId);
3474
3585
  if (message.role !== "assistant")
3475
3586
  throw new Error("Tried to add tool result to non-assistant message");
3587
+ let added = false;
3476
3588
  let found = false;
3477
3589
  const newContent = message.content.map((c) => {
3478
3590
  if (c.type !== "tool-call") return c;
3479
3591
  if (c.toolCallId !== toolCallId) return c;
3480
3592
  found = true;
3593
+ if (!c.result) added = true;
3481
3594
  return {
3482
3595
  ...c,
3483
3596
  result
@@ -3485,10 +3598,39 @@ var LocalThreadRuntime = class {
3485
3598
  });
3486
3599
  if (!found)
3487
3600
  throw new Error("Tried to add tool result to non-existing tool call");
3488
- this.repository.addOrUpdateMessage(parentId, {
3601
+ message = {
3489
3602
  ...message,
3490
3603
  content: newContent
3491
- });
3604
+ };
3605
+ this.repository.addOrUpdateMessage(parentId, message);
3606
+ if (added && shouldContinue(message)) {
3607
+ this.performRoundtrip(parentId, message);
3608
+ }
3609
+ }
3610
+ };
3611
+
3612
+ // src/runtimes/local/LocalRuntime.tsx
3613
+ var LocalRuntime = class extends BaseAssistantRuntime {
3614
+ _proxyConfigProvider;
3615
+ constructor(adapter, options) {
3616
+ const proxyConfigProvider = new ProxyConfigProvider();
3617
+ super(new LocalThreadRuntime(proxyConfigProvider, adapter, options));
3618
+ this._proxyConfigProvider = proxyConfigProvider;
3619
+ }
3620
+ set adapter(adapter) {
3621
+ this.thread.adapter = adapter;
3622
+ }
3623
+ registerModelConfigProvider(provider) {
3624
+ return this._proxyConfigProvider.registerModelConfigProvider(provider);
3625
+ }
3626
+ switchToThread(threadId) {
3627
+ if (threadId) {
3628
+ throw new Error("LocalRuntime does not yet support switching threads");
3629
+ }
3630
+ return this.thread = new LocalThreadRuntime(
3631
+ this._proxyConfigProvider,
3632
+ this.thread.adapter
3633
+ );
3492
3634
  }
3493
3635
  };
3494
3636
 
@@ -3683,7 +3825,7 @@ var Text = ({ status }) => {
3683
3825
  {
3684
3826
  className: (0, import_classnames2.default)(
3685
3827
  "aui-text",
3686
- status.type === "in_progress" && "aui-text-in-progress"
3828
+ status.type === "running" && "aui-text-in-progress"
3687
3829
  ),
3688
3830
  children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(contentPart_exports.Text, {})
3689
3831
  }
@@ -4210,11 +4352,13 @@ var assistant_modal_default = Object.assign(AssistantModal, exports12);
4210
4352
  ThreadWelcome,
4211
4353
  UserActionBar,
4212
4354
  UserMessage,
4355
+ fromCoreMessage,
4213
4356
  fromCoreMessages,
4214
4357
  fromLanguageModelMessages,
4215
4358
  fromLanguageModelTools,
4216
4359
  makeAssistantTool,
4217
4360
  makeAssistantToolUI,
4361
+ toCoreMessage,
4218
4362
  toCoreMessages,
4219
4363
  toLanguageModelMessages,
4220
4364
  toLanguageModelTools,