Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
[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
|
52 |
token: TextGenerationStreamToken & { toolCalls?: ToolCall[] };
|
|
|
53 |
};
|
54 |
// type signature for the endpoint
|
55 |
export type Endpoint = (
|
56 |
params: EndpointParameters
|
57 |
-
) => Promise<AsyncGenerator<
|
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:
|
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 {
|
|
|
|
|
|
|
|
|
|
|
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 |
}
|