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 |
}
|