Liam Dyer nsarrazin HF Staff commited on
Commit
baa4992
·
unverified ·
1 Parent(s): fd86b16

Add error message for tools (#1163)

Browse files

* feat: add error message for tools

* lint

---------

Co-authored-by: Nathan Sarrazin <[email protected]>

src/lib/components/OpenWebSearchResults.svelte CHANGED
@@ -16,7 +16,10 @@
16
  $: lastMessage = webSearchMessages
17
  .filter((update) => update.subtype !== MessageWebSearchUpdateType.Sources)
18
  .at(-1) as MessageWebSearchUpdate;
19
- $: loading = !sources && lastMessage.subtype !== MessageWebSearchUpdateType.Error;
 
 
 
20
  </script>
21
 
22
  <details
 
16
  $: lastMessage = webSearchMessages
17
  .filter((update) => update.subtype !== MessageWebSearchUpdateType.Sources)
18
  .at(-1) as MessageWebSearchUpdate;
19
+ $: errored = webSearchMessages.some(
20
+ (update) => update.subtype === MessageWebSearchUpdateType.Error
21
+ );
22
+ $: loading = !sources && !errored;
23
  </script>
24
 
25
  <details
src/lib/components/chat/ChatMessage.svelte CHANGED
@@ -29,7 +29,11 @@
29
  type MessageWebSearchSourcesUpdate,
30
  type MessageWebSearchUpdate,
31
  } from "$lib/types/MessageUpdate";
32
- import { isMessageToolCallUpdate, isMessageToolResultUpdate } from "$lib/utils/messageUpdates";
 
 
 
 
33
  import type { ToolFront } from "$lib/types/Tool";
34
  import { base } from "$app/paths";
35
  import { useConvTreeStore } from "$lib/stores/convTree";
@@ -287,6 +291,7 @@
287
  {#each Object.values(toolUpdates) as tool}
288
  {#if tool.length}
289
  {@const toolName = tool.find(isMessageToolCallUpdate)?.call.name}
 
290
  {@const toolDone = tool.some(isMessageToolResultUpdate)}
291
  {#if toolName && toolName !== "websearch"}
292
  <details
@@ -301,7 +306,7 @@
301
  >
302
  <svg
303
  class="absolute inset-0 text-purple-500/40 transition-opacity"
304
- class:invisible={toolDone}
305
  width="22"
306
  height="22"
307
  viewBox="0 0 38 38"
@@ -321,7 +326,7 @@
321
  </div>
322
 
323
  <span>
324
- {toolDone ? "Called" : "Calling"} tool
325
  <span class="font-semibold"
326
  >{availableTools.find((el) => toolHasName(toolName, el))?.displayName}</span
327
  >
@@ -341,6 +346,12 @@
341
  </li>
342
  {/each}
343
  </ul>
 
 
 
 
 
 
344
  {/if}
345
  {/each}
346
  </details>
 
29
  type MessageWebSearchSourcesUpdate,
30
  type MessageWebSearchUpdate,
31
  } from "$lib/types/MessageUpdate";
32
+ import {
33
+ isMessageToolCallUpdate,
34
+ isMessageToolResultUpdate,
35
+ isMessageToolErrorUpdate,
36
+ } from "$lib/utils/messageUpdates";
37
  import type { ToolFront } from "$lib/types/Tool";
38
  import { base } from "$app/paths";
39
  import { useConvTreeStore } from "$lib/stores/convTree";
 
291
  {#each Object.values(toolUpdates) as tool}
292
  {#if tool.length}
293
  {@const toolName = tool.find(isMessageToolCallUpdate)?.call.name}
294
+ {@const toolError = tool.some(isMessageToolErrorUpdate)}
295
  {@const toolDone = tool.some(isMessageToolResultUpdate)}
296
  {#if toolName && toolName !== "websearch"}
297
  <details
 
306
  >
307
  <svg
308
  class="absolute inset-0 text-purple-500/40 transition-opacity"
309
+ class:invisible={toolDone || toolError}
310
  width="22"
311
  height="22"
312
  viewBox="0 0 38 38"
 
326
  </div>
327
 
328
  <span>
329
+ {toolError ? "Error calling" : toolDone ? "Called" : "Calling"} tool
330
  <span class="font-semibold"
331
  >{availableTools.find((el) => toolHasName(toolName, el))?.displayName}</span
332
  >
 
346
  </li>
347
  {/each}
348
  </ul>
349
+ {:else if toolUpdate.subtype === MessageToolUpdateType.Error}
350
+ <div class="mt-1 flex items-center gap-2 opacity-80">
351
+ <h3 class="text-sm">Error</h3>
352
+ <div class="h-px flex-1 bg-gradient-to-r from-gray-500/20" />
353
+ </div>
354
+ <p class="text-sm">{toolUpdate.message}</p>
355
  {/if}
356
  {/each}
357
  </details>
src/lib/server/textGeneration/tools.ts CHANGED
@@ -69,19 +69,38 @@ async function* runTool(
69
  call,
70
  };
71
  try {
72
- const toolResult = yield* tool.call(call.parameters, {
73
- conv,
74
- messages,
75
- preprompt,
76
- assistant,
77
- });
78
- yield {
79
- type: MessageUpdateType.Tool,
80
- subtype: MessageToolUpdateType.Result,
81
- uuid,
82
- result: { ...toolResult, call } as ToolResult,
83
- };
84
- return { ...toolResult, call } as ToolResult;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  } catch (cause) {
86
  console.error(Error(`Failed while running tool ${call.name}`), { cause });
87
  return {
 
69
  call,
70
  };
71
  try {
72
+ try {
73
+ const toolResult = yield* tool.call(call.parameters, {
74
+ conv,
75
+ messages,
76
+ preprompt,
77
+ assistant,
78
+ });
79
+ if (toolResult.status === ToolResultStatus.Error) {
80
+ yield {
81
+ type: MessageUpdateType.Tool,
82
+ subtype: MessageToolUpdateType.Error,
83
+ uuid,
84
+ message: toolResult.message,
85
+ };
86
+ } else {
87
+ yield {
88
+ type: MessageUpdateType.Tool,
89
+ subtype: MessageToolUpdateType.Result,
90
+ uuid,
91
+ result: { ...toolResult, call } as ToolResult,
92
+ };
93
+ }
94
+
95
+ return { ...toolResult, call } as ToolResult;
96
+ } catch (e) {
97
+ yield {
98
+ type: MessageUpdateType.Tool,
99
+ subtype: MessageToolUpdateType.Error,
100
+ uuid,
101
+ message: e instanceof Error ? e.message : String(e),
102
+ };
103
+ }
104
  } catch (cause) {
105
  console.error(Error(`Failed while running tool ${call.name}`), { cause });
106
  return {
src/lib/server/tools/index.ts CHANGED
@@ -2,7 +2,7 @@ import type { Assistant } from "$lib/types/Assistant";
2
  import type { Conversation } from "$lib/types/Conversation";
3
  import type { Message } from "$lib/types/Message";
4
  import type { MessageUpdate } from "$lib/types/MessageUpdate";
5
- import type { Tool, ToolResult } from "$lib/types/Tool";
6
 
7
  import calculator from "./calculator";
8
  import directlyAnswer from "./directlyAnswer";
@@ -19,11 +19,17 @@ export interface BackendToolContext {
19
  assistant?: Pick<Assistant, "rag" | "dynamicPrompt" | "generateSettings">;
20
  }
21
 
 
 
 
 
 
 
22
  export interface BackendTool extends Tool {
23
  call(
24
  params: Record<string, string | number | boolean>,
25
  context: BackendToolContext
26
- ): AsyncGenerator<MessageUpdate, Omit<ToolResult, "call">, undefined>;
27
  }
28
 
29
  export const allTools: BackendTool[] = [
 
2
  import type { Conversation } from "$lib/types/Conversation";
3
  import type { Message } from "$lib/types/Message";
4
  import type { MessageUpdate } from "$lib/types/MessageUpdate";
5
+ import type { Tool, ToolResultError, ToolResultSuccess } from "$lib/types/Tool";
6
 
7
  import calculator from "./calculator";
8
  import directlyAnswer from "./directlyAnswer";
 
19
  assistant?: Pick<Assistant, "rag" | "dynamicPrompt" | "generateSettings">;
20
  }
21
 
22
+ // typescript can't narrow a discriminated union after applying a generic like Omit to it
23
+ // so we have to define the omitted types and create a new union
24
+ type ToolResultSuccessOmitted = Omit<ToolResultSuccess, "call">;
25
+ type ToolResultErrorOmitted = Omit<ToolResultError, "call">;
26
+ type ToolResultOmitted = ToolResultSuccessOmitted | ToolResultErrorOmitted;
27
+
28
  export interface BackendTool extends Tool {
29
  call(
30
  params: Record<string, string | number | boolean>,
31
  context: BackendToolContext
32
+ ): AsyncGenerator<MessageUpdate, ToolResultOmitted, undefined>;
33
  }
34
 
35
  export const allTools: BackendTool[] = [
src/lib/types/MessageUpdate.ts CHANGED
@@ -74,6 +74,8 @@ export enum MessageToolUpdateType {
74
  Call = "call",
75
  /** The result of a tool call */
76
  Result = "result",
 
 
77
  }
78
  interface MessageToolBaseUpdate<TSubType extends MessageToolUpdateType> {
79
  type: MessageUpdateType.Tool;
@@ -87,7 +89,13 @@ export interface MessageToolResultUpdate
87
  extends MessageToolBaseUpdate<MessageToolUpdateType.Result> {
88
  result: ToolResult;
89
  }
90
- export type MessageToolUpdate = MessageToolCallUpdate | MessageToolResultUpdate;
 
 
 
 
 
 
91
 
92
  // Everything else
93
  export interface MessageTitleUpdate {
 
74
  Call = "call",
75
  /** The result of a tool call */
76
  Result = "result",
77
+ /** Error while running tool */
78
+ Error = "error",
79
  }
80
  interface MessageToolBaseUpdate<TSubType extends MessageToolUpdateType> {
81
  type: MessageUpdateType.Tool;
 
89
  extends MessageToolBaseUpdate<MessageToolUpdateType.Result> {
90
  result: ToolResult;
91
  }
92
+ export interface MessageToolErrorUpdate extends MessageToolBaseUpdate<MessageToolUpdateType.Error> {
93
+ message: string;
94
+ }
95
+ export type MessageToolUpdate =
96
+ | MessageToolCallUpdate
97
+ | MessageToolResultUpdate
98
+ | MessageToolErrorUpdate;
99
 
100
  // Everything else
101
  export interface MessageTitleUpdate {
src/lib/types/Tool.ts CHANGED
@@ -31,13 +31,13 @@ export enum ToolResultStatus {
31
  Success = "success",
32
  Error = "error",
33
  }
34
- interface ToolResultSuccess {
35
  status: ToolResultStatus.Success;
36
  call: ToolCall;
37
  outputs: Record<string, unknown>[];
38
  display?: boolean;
39
  }
40
- interface ToolResultError {
41
  status: ToolResultStatus.Error;
42
  call: ToolCall;
43
  message: string;
 
31
  Success = "success",
32
  Error = "error",
33
  }
34
+ export interface ToolResultSuccess {
35
  status: ToolResultStatus.Success;
36
  call: ToolCall;
37
  outputs: Record<string, unknown>[];
38
  display?: boolean;
39
  }
40
+ export interface ToolResultError {
41
  status: ToolResultStatus.Error;
42
  call: ToolCall;
43
  message: string;
src/lib/utils/messageUpdates.ts CHANGED
@@ -11,6 +11,8 @@ import {
11
  type MessageWebSearchSourcesUpdate,
12
  type MessageWebSearchErrorUpdate,
13
  MessageWebSearchUpdateType,
 
 
14
  } from "$lib/types/MessageUpdate";
15
 
16
  export const isMessageWebSearchUpdate = (update: MessageUpdate): update is MessageWebSearchUpdate =>
@@ -32,8 +34,12 @@ export const isMessageToolUpdate = (update: MessageUpdate): update is MessageToo
32
  update.type === MessageUpdateType.Tool;
33
  export const isMessageToolCallUpdate = (update: MessageUpdate): update is MessageToolCallUpdate =>
34
  isMessageToolUpdate(update) && update.subtype === MessageToolUpdateType.Call;
35
- export const isMessageToolResultUpdate = (update: MessageUpdate): update is MessageToolCallUpdate =>
 
 
36
  isMessageToolUpdate(update) && update.subtype === MessageToolUpdateType.Result;
 
 
37
 
38
  type MessageUpdateRequestOptions = {
39
  base: string;
 
11
  type MessageWebSearchSourcesUpdate,
12
  type MessageWebSearchErrorUpdate,
13
  MessageWebSearchUpdateType,
14
+ type MessageToolErrorUpdate,
15
+ type MessageToolResultUpdate,
16
  } from "$lib/types/MessageUpdate";
17
 
18
  export const isMessageWebSearchUpdate = (update: MessageUpdate): update is MessageWebSearchUpdate =>
 
34
  update.type === MessageUpdateType.Tool;
35
  export const isMessageToolCallUpdate = (update: MessageUpdate): update is MessageToolCallUpdate =>
36
  isMessageToolUpdate(update) && update.subtype === MessageToolUpdateType.Call;
37
+ export const isMessageToolResultUpdate = (
38
+ update: MessageUpdate
39
+ ): update is MessageToolResultUpdate =>
40
  isMessageToolUpdate(update) && update.subtype === MessageToolUpdateType.Result;
41
+ export const isMessageToolErrorUpdate = (update: MessageUpdate): update is MessageToolErrorUpdate =>
42
+ isMessageToolUpdate(update) && update.subtype === MessageToolUpdateType.Error;
43
 
44
  type MessageUpdateRequestOptions = {
45
  base: string;