goupilew nsarrazin HF Staff commited on
Commit
eabf2bf
·
unverified ·
1 Parent(s): 8a9d27a

[feat] Add support for endpoint web sources (#1544)

Browse files

* [feat] Add support for endpoint web sources

* chores: lint

---------

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

src/lib/components/chat/ChatMessage.svelte CHANGED
@@ -23,10 +23,12 @@
23
 
24
  import OpenWebSearchResults from "../OpenWebSearchResults.svelte";
25
  import {
 
26
  MessageWebSearchUpdateType,
27
  type MessageToolUpdate,
28
  type MessageWebSearchSourcesUpdate,
29
  type MessageWebSearchUpdate,
 
30
  } from "$lib/types/MessageUpdate";
31
  import { base } from "$app/paths";
32
  import { useConvTreeStore } from "$lib/stores/convTree";
@@ -149,6 +151,10 @@
149
  $: searchUpdates = (message.updates?.filter(({ type }) => type === "webSearch") ??
150
  []) as MessageWebSearchUpdate[];
151
 
 
 
 
 
152
  // filter all updates with type === "tool" then group them by uuid field
153
 
154
  $: toolUpdates = message.updates
@@ -314,7 +320,29 @@
314
  {/each}
315
  </div>
316
  {/if}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  </div>
 
318
  {#if !loading && (message.content || toolUpdates)}
319
  <div
320
  class="absolute -bottom-4 right-0 flex max-md:transition-all md:group-hover:visible md:group-hover:opacity-100
 
23
 
24
  import OpenWebSearchResults from "../OpenWebSearchResults.svelte";
25
  import {
26
+ MessageUpdateType,
27
  MessageWebSearchUpdateType,
28
  type MessageToolUpdate,
29
  type MessageWebSearchSourcesUpdate,
30
  type MessageWebSearchUpdate,
31
+ type MessageFinalAnswerUpdate,
32
  } from "$lib/types/MessageUpdate";
33
  import { base } from "$app/paths";
34
  import { useConvTreeStore } from "$lib/stores/convTree";
 
151
  $: searchUpdates = (message.updates?.filter(({ type }) => type === "webSearch") ??
152
  []) as MessageWebSearchUpdate[];
153
 
154
+ $: messageFinalAnswer = message.updates?.find(
155
+ ({ type }) => type === MessageUpdateType.FinalAnswer
156
+ ) as MessageFinalAnswerUpdate;
157
+
158
  // filter all updates with type === "tool" then group them by uuid field
159
 
160
  $: toolUpdates = message.updates
 
320
  {/each}
321
  </div>
322
  {/if}
323
+
324
+ <!-- Endpoint web sources -->
325
+ {#if messageFinalAnswer?.webSources && messageFinalAnswer.webSources.length}
326
+ <div class="mt-4 flex flex-wrap items-center gap-x-2 gap-y-1.5 text-sm">
327
+ <div class="text-gray-400">Sources:</div>
328
+ {#each messageFinalAnswer.webSources as { uri, title }}
329
+ <a
330
+ class="flex items-center gap-2 whitespace-nowrap rounded-lg border bg-white px-2 py-1.5 leading-none hover:border-gray-300 dark:border-gray-800 dark:bg-gray-900 dark:hover:border-gray-700"
331
+ href={uri}
332
+ target="_blank"
333
+ >
334
+ <img
335
+ class="h-3.5 w-3.5 rounded"
336
+ src="https://www.google.com/s2/favicons?sz=64&domain_url={new URL(uri).hostname}"
337
+ alt="{title} favicon"
338
+ />
339
+ <div>{title}</div>
340
+ </a>
341
+ {/each}
342
+ </div>
343
+ {/if}
344
  </div>
345
+
346
  {#if !loading && (message.content || toolUpdates)}
347
  <div
348
  class="absolute -bottom-4 right-0 flex max-md:transition-all md:group-hover:visible md:group-hover:opacity-100
src/lib/server/endpoints/endpoints.ts CHANGED
@@ -48,13 +48,14 @@ export interface EndpointParameters {
48
  interface CommonEndpoint {
49
  weight: number;
50
  }
51
- type TextGenerationStreamOutputWithTools = TextGenerationStreamOutput & {
52
  token: TextGenerationStreamToken & { toolCalls?: ToolCall[] };
 
53
  };
54
  // type signature for the endpoint
55
  export type Endpoint = (
56
  params: EndpointParameters
57
- ) => Promise<AsyncGenerator<TextGenerationStreamOutputWithTools, void, void>>;
58
 
59
  // generator function that takes in parameters for defining the endpoint and return the endpoint
60
  export type EndpointGenerator<T extends CommonEndpoint> = (parameters: T) => Endpoint;
 
48
  interface CommonEndpoint {
49
  weight: number;
50
  }
51
+ export type TextGenerationStreamOutputWithToolsAndWebSources = TextGenerationStreamOutput & {
52
  token: TextGenerationStreamToken & { toolCalls?: ToolCall[] };
53
+ webSources?: { uri: string; title: string }[];
54
  };
55
  // type signature for the endpoint
56
  export type Endpoint = (
57
  params: EndpointParameters
58
+ ) => Promise<AsyncGenerator<TextGenerationStreamOutputWithToolsAndWebSources, void, void>>;
59
 
60
  // generator function that takes in parameters for defining the endpoint and return the endpoint
61
  export type EndpointGenerator<T extends CommonEndpoint> = (parameters: T) => Endpoint;
src/lib/server/endpoints/google/endpointVertex.ts CHANGED
@@ -5,10 +5,9 @@ import {
5
  type Content,
6
  type TextPart,
7
  } from "@google-cloud/vertexai";
8
- import type { Endpoint } from "../endpoints";
9
  import { z } from "zod";
10
  import type { Message } from "$lib/types/Message";
11
- import type { TextGenerationStreamOutput } from "@huggingface/inference";
12
  import { createImageProcessorOptionsValidator, makeImageProcessor } from "../images";
13
  import { createDocumentProcessorOptionsValidator, makeDocumentProcessor } from "../document";
14
 
@@ -170,6 +169,8 @@ export function endpointVertex(input: z.input<typeof endpointVertexParametersSch
170
  return (async function* () {
171
  let generatedText = "";
172
 
 
 
173
  for await (const data of result.stream) {
174
  if (!data?.candidates?.length) break; // Handle case where no candidates are present
175
 
@@ -183,9 +184,29 @@ export function endpointVertex(input: z.input<typeof endpointVertexParametersSch
183
 
184
  const isLastChunk = !!candidate.finishReason;
185
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  const content = firstPart.text;
187
  generatedText += content;
188
- const output: TextGenerationStreamOutput = {
189
  token: {
190
  id: tokenId++,
191
  text: content,
@@ -194,6 +215,7 @@ export function endpointVertex(input: z.input<typeof endpointVertexParametersSch
194
  },
195
  generated_text: isLastChunk ? generatedText : null,
196
  details: null,
 
197
  };
198
  yield output;
199
 
 
5
  type Content,
6
  type TextPart,
7
  } from "@google-cloud/vertexai";
8
+ import type { Endpoint, TextGenerationStreamOutputWithToolsAndWebSources } from "../endpoints";
9
  import { z } from "zod";
10
  import type { Message } from "$lib/types/Message";
 
11
  import { createImageProcessorOptionsValidator, makeImageProcessor } from "../images";
12
  import { createDocumentProcessorOptionsValidator, makeDocumentProcessor } from "../document";
13
 
 
169
  return (async function* () {
170
  let generatedText = "";
171
 
172
+ const webSources = [];
173
+
174
  for await (const data of result.stream) {
175
  if (!data?.candidates?.length) break; // Handle case where no candidates are present
176
 
 
184
 
185
  const isLastChunk = !!candidate.finishReason;
186
 
187
+ const candidateWebSources = candidate.groundingMetadata?.groundingChunks
188
+ ?.map((chunk) => {
189
+ const uri = chunk.web?.uri ?? chunk.retrievedContext?.uri;
190
+ const title = chunk.web?.title ?? chunk.retrievedContext?.title;
191
+
192
+ if (!uri || !title) {
193
+ return null;
194
+ }
195
+
196
+ return {
197
+ uri,
198
+ title,
199
+ };
200
+ })
201
+ .filter((source) => source !== null);
202
+
203
+ if (candidateWebSources) {
204
+ webSources.push(...candidateWebSources);
205
+ }
206
+
207
  const content = firstPart.text;
208
  generatedText += content;
209
+ const output: TextGenerationStreamOutputWithToolsAndWebSources = {
210
  token: {
211
  id: tokenId++,
212
  text: content,
 
215
  },
216
  generated_text: isLastChunk ? generatedText : null,
217
  details: null,
218
+ webSources,
219
  };
220
  yield output;
221
 
src/lib/server/textGeneration/generate.ts CHANGED
@@ -33,7 +33,12 @@ export async function* generate(
33
  text = text.slice(0, text.length - stopToken.length);
34
  }
35
 
36
- yield { type: MessageUpdateType.FinalAnswer, text, interrupted };
 
 
 
 
 
37
  continue;
38
  }
39
 
 
33
  text = text.slice(0, text.length - stopToken.length);
34
  }
35
 
36
+ yield {
37
+ type: MessageUpdateType.FinalAnswer,
38
+ text,
39
+ interrupted,
40
+ webSources: output.webSources,
41
+ };
42
  continue;
43
  }
44
 
src/lib/types/MessageUpdate.ts CHANGED
@@ -124,4 +124,5 @@ export interface MessageFinalAnswerUpdate {
124
  type: MessageUpdateType.FinalAnswer;
125
  text: string;
126
  interrupted: boolean;
 
127
  }
 
124
  type: MessageUpdateType.FinalAnswer;
125
  text: string;
126
  interrupted: boolean;
127
+ webSources?: { uri: string; title: string }[];
128
  }