Internet Search Tool (#1666)

---------

Co-authored-by: Weves <chrisweaver101@gmail.com>
This commit is contained in:
rashad-danswer
2024-07-06 18:01:24 -07:00
committed by GitHub
parent e06f8a0a4b
commit 146f85936b
36 changed files with 718 additions and 115 deletions

View File

@@ -53,6 +53,10 @@ function findImageGenerationTool(tools: ToolSnapshot[]) {
return tools.find((tool) => tool.in_code_tool_id === "ImageGenerationTool");
}
function findInternetSearchTool(tools: ToolSnapshot[]) {
return tools.find((tool) => tool.in_code_tool_id === "InternetSearchTool");
}
function SubLabel({ children }: { children: string | JSX.Element }) {
return <div className="text-sm text-subtle mb-2">{children}</div>;
}
@@ -150,16 +154,20 @@ export function AssistantEditor({
const imageGenerationTool = providerSupportingImageGenerationExists
? findImageGenerationTool(tools)
: undefined;
const internetSearchTool = findInternetSearchTool(tools);
const customTools = tools.filter(
(tool) =>
tool.in_code_tool_id !== searchTool?.in_code_tool_id &&
tool.in_code_tool_id !== imageGenerationTool?.in_code_tool_id
tool.in_code_tool_id !== imageGenerationTool?.in_code_tool_id &&
tool.in_code_tool_id !== internetSearchTool?.in_code_tool_id
);
const availableTools = [
...customTools,
...(searchTool ? [searchTool] : []),
...(imageGenerationTool ? [imageGenerationTool] : []),
...(internetSearchTool ? [internetSearchTool] : []),
];
const enabledToolsMap: { [key: number]: boolean } = {};
availableTools.forEach((tool) => {
@@ -666,6 +674,17 @@ export function AssistantEditor({
</>
)}
{internetSearchTool && (
<BooleanFormField
noPadding
name={`enabled_tools_map.${internetSearchTool.id}`}
label={internetSearchTool.display_name}
onChange={() => {
toggleToolInValues(internetSearchTool.id);
}}
/>
)}
{customTools.length > 0 && (
<>
{customTools.map((tool) => (

View File

@@ -1,6 +1,6 @@
import { Bubble } from "@/components/Bubble";
import { ToolSnapshot } from "@/lib/tools/interfaces";
import { FiImage, FiSearch } from "react-icons/fi";
import { FiImage, FiSearch, FiGlobe } from "react-icons/fi";
export function ToolsDisplay({ tools }: { tools: ToolSnapshot[] }) {
return (
@@ -15,6 +15,9 @@ export function ToolsDisplay({ tools }: { tools: ToolSnapshot[] }) {
} else if (tool.name === "ImageGenerationTool") {
toolName = "Image Generation";
toolIcon = <FiImage className="mr-1 my-auto" />;
} else if (tool.name === "InternetSearchTool") {
toolName = "Internet Search";
toolIcon = <FiGlobe className="mr-1 my-auto" />;
}
return (

View File

@@ -10,6 +10,7 @@ import {
DocumentMetadataBlock,
buildDocumentSummaryDisplay,
} from "@/components/search/DocumentDisplay";
import { InternetSearchIcon } from "@/components/InternetSearchIcon";
interface DocumentDisplayProps {
document: DanswerDocument;
@@ -30,24 +31,25 @@ export function ChatDocumentDisplay({
setPopup,
tokenLimitReached,
}: DocumentDisplayProps) {
// Consider reintroducing null scored docs in the future
if (document.score === null) {
return null;
}
const isInternet = document.is_internet;
return (
<div key={document.semantic_identifier} className="text-sm px-3">
<div className="flex relative w-full overflow-y-visible">
<a
className={
"rounded-lg flex font-bold flex-shrink truncate " +
"rounded-lg flex font-bold flex-shrink truncate items-center " +
(document.link ? "" : "pointer-events-none")
}
href={document.link}
target="_blank"
rel="noopener noreferrer"
>
<SourceIcon sourceType={document.source_type} iconSize={18} />
{isInternet ? (
<InternetSearchIcon url={document.link} />
) : (
<SourceIcon sourceType={document.source_type} iconSize={18} />
)}
<p className="overflow-hidden text-ellipsis mx-2 my-auto text-sm ">
{document.semantic_identifier || document.document_id}
</p>
@@ -73,29 +75,16 @@ export function ChatDocumentDisplay({
/>
</div>
)}
<div
className={`
text-xs
text-emphasis
bg-hover
rounded
p-0.5
w-fit
my-auto
select-none
my-auto
mr-2`}
>
{Math.abs(document.score).toFixed(2)}
</div>
</div>
)}
<DocumentSelector
isSelected={isSelected}
handleSelect={() => handleSelect(document.document_id)}
isDisabled={tokenLimitReached && !isSelected}
/>
{!isInternet && (
<DocumentSelector
isSelected={isSelected}
handleSelect={() => handleSelect(document.document_id)}
isDisabled={tokenLimitReached && !isSelected}
/>
)}
</div>
<div>
<div className="mt-1">

View File

@@ -530,7 +530,11 @@ export function checkAnyAssistantHasSearch(
}
export function personaIncludesRetrieval(selectedPersona: Persona) {
return selectedPersona.num_chunks !== 0;
return selectedPersona.tools.some(
(tool) =>
tool.in_code_tool_id &&
["SearchTool", "InternetSearchTool"].includes(tool.in_code_tool_id)
);
}
const PARAMS_TO_SKIP = [

View File

@@ -10,6 +10,7 @@ import {
FiChevronRight,
FiChevronLeft,
FiTool,
FiGlobe,
} from "react-icons/fi";
import { FeedbackType } from "../types";
import { useEffect, useRef, useState } from "react";
@@ -25,6 +26,7 @@ import { ChatFileType, FileDescriptor, ToolCallMetadata } from "../interfaces";
import {
IMAGE_GENERATION_TOOL_NAME,
SEARCH_TOOL_NAME,
INTERNET_SEARCH_TOOL_NAME,
} from "../tools/constants";
import { ToolRunDisplay } from "../tools/ToolRunningAnimation";
import { Hoverable } from "@/components/Hoverable";
@@ -39,11 +41,12 @@ import Prism from "prismjs";
import "prismjs/themes/prism-tomorrow.css";
import "./custom-code-styles.css";
import { Persona } from "@/app/admin/assistants/interfaces";
import { Button } from "@tremor/react";
import { AssistantIcon } from "@/components/assistants/AssistantIcon";
import { InternetSearchIcon } from "@/components/InternetSearchIcon";
const TOOLS_WITH_CUSTOM_HANDLING = [
SEARCH_TOOL_NAME,
INTERNET_SEARCH_TOOL_NAME,
IMAGE_GENERATION_TOOL_NAME,
];
@@ -149,6 +152,9 @@ export const AIMessage = ({
content = trimIncompleteCodeSection(content);
}
const danswerSearchToolEnabledForPersona = currentPersona.tools.some(
(tool) => tool.in_code_tool_id === SEARCH_TOOL_NAME
);
const shouldShowLoader =
!toolCall || (toolCall.tool_name === SEARCH_TOOL_NAME && !content);
const defaultLoader = shouldShowLoader ? (
@@ -200,36 +206,37 @@ export const AIMessage = ({
</div>
<div className="w-message-xs 2xl:w-message-sm 3xl:w-message-default break-words mt-1 ml-8">
{(!toolCall || toolCall.tool_name === SEARCH_TOOL_NAME) && (
<>
{query !== undefined &&
handleShowRetrieved !== undefined &&
isCurrentlyShowingRetrieved !== undefined &&
!retrievalDisabled && (
<div className="my-1">
<SearchSummary
query={query}
hasDocs={hasDocs || false}
messageId={messageId}
isCurrentlyShowingRetrieved={
isCurrentlyShowingRetrieved
}
handleShowRetrieved={handleShowRetrieved}
handleSearchQueryEdit={handleSearchQueryEdit}
/>
</div>
)}
{handleForceSearch &&
content &&
query === undefined &&
!hasDocs &&
!retrievalDisabled && (
<div className="my-1">
<SkippedSearch handleForceSearch={handleForceSearch} />
</div>
)}
</>
)}
{(!toolCall || toolCall.tool_name === SEARCH_TOOL_NAME) &&
danswerSearchToolEnabledForPersona && (
<>
{query !== undefined &&
handleShowRetrieved !== undefined &&
isCurrentlyShowingRetrieved !== undefined &&
!retrievalDisabled && (
<div className="my-1">
<SearchSummary
query={query}
hasDocs={hasDocs || false}
messageId={messageId}
isCurrentlyShowingRetrieved={
isCurrentlyShowingRetrieved
}
handleShowRetrieved={handleShowRetrieved}
handleSearchQueryEdit={handleSearchQueryEdit}
/>
</div>
)}
{handleForceSearch &&
content &&
query === undefined &&
!hasDocs &&
!retrievalDisabled && (
<div className="my-1">
<SkippedSearch handleForceSearch={handleForceSearch} />
</div>
)}
</>
)}
{toolCall &&
!TOOLS_WITH_CUSTOM_HANDLING.includes(toolCall.tool_name) && (
@@ -258,6 +265,20 @@ export const AIMessage = ({
</div>
)}
{toolCall && toolCall.tool_name === INTERNET_SEARCH_TOOL_NAME && (
<div className="my-2">
<ToolRunDisplay
toolName={
toolCall.tool_result
? `Searched the internet`
: `Searching the internet`
}
toolLogo={<FiGlobe size={15} className="my-auto mr-1" />}
isRunning={!toolCall.tool_result}
/>
</div>
)}
{content ? (
<>
<FileDisplay files={files || []} />
@@ -317,12 +338,16 @@ export const AIMessage = ({
.filter(([_, document]) => document.semantic_identifier)
.map(([citationKey, document], ind) => {
const display = (
<div className="max-w-350 text-ellipsis flex text-sm border border-border py-1 px-2 rounded flex">
<div className="max-w-350 text-ellipsis text-sm border border-border py-1 px-2 rounded flex">
<div className="mr-1 my-auto">
<SourceIcon
sourceType={document.source_type}
iconSize={16}
/>
{document.is_internet ? (
<InternetSearchIcon url={document.link} />
) : (
<SourceIcon
sourceType={document.source_type}
iconSize={16}
/>
)}
</div>
[{citationKey}] {document!.semantic_identifier}
</div>

View File

@@ -1,2 +1,3 @@
export const SEARCH_TOOL_NAME = "run_search";
export const INTERNET_SEARCH_TOOL_NAME = "run_internet_search";
export const IMAGE_GENERATION_TOOL_NAME = "run_image_generation";

View File

@@ -0,0 +1,9 @@
export function InternetSearchIcon({ url }: { url: string }) {
return (
<img
className="rounded-full w-[18px] h-[18px]"
src={`https://www.google.com/s2/favicons?sz=128&domain=${url}`}
alt="favicon"
/>
);
}

View File

@@ -11,6 +11,7 @@ export const SearchType = {
SEMANTIC: "semantic",
KEYWORD: "keyword",
AUTOMATIC: "automatic",
INTERNET: "internet",
};
export type SearchType = (typeof SearchType)[keyof typeof SearchType];
@@ -48,6 +49,7 @@ export interface DanswerDocument {
metadata: { [key: string]: string };
updated_at: string | null;
db_doc_id?: number;
is_internet: boolean;
}
export interface DocumentInfoPacket {

View File

@@ -39,7 +39,6 @@ import {
import { ValidSources } from "./types";
import { SourceCategory, SourceMetadata } from "./search/interfaces";
import { Persona } from "@/app/admin/assistants/interfaces";
import internal from "stream";
interface PartialSourceMetadata {
icon: React.FC<{ size?: number; className?: string }>;
@@ -232,6 +231,11 @@ const SOURCE_METADATA_MAP: SourceMap = {
displayName: "Google Storage",
category: SourceCategory.AppConnection,
},
not_applicable: {
icon: GlobeIcon,
displayName: "Internet",
category: SourceCategory.ImportedKnowledge,
},
};
function fillSourceMetadata(

View File

@@ -1,6 +1,7 @@
export interface ToolSnapshot {
id: number;
name: string;
display_name: string;
description: string;
// only specified for Custom Tools. OpenAPI schema which represents

View File

@@ -63,7 +63,8 @@ export type ValidSources =
| "s3"
| "r2"
| "google_cloud_storage"
| "oci_storage";
| "oci_storage"
| "not_applicable";
export type ValidInputTypes = "load_state" | "poll" | "event";
export type ValidStatuses =