mirror of
https://github.com/onyx-dot-app/onyx.git
synced 2026-03-15 12:42:39 +00:00
Compare commits
2 Commits
v3.0.2
...
feature/in
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
453ec1c701 | ||
|
|
9460a2c93a |
@@ -44,6 +44,9 @@ from onyx.chat.models import UserKnowledgeFilePacket
|
||||
from onyx.chat.prompt_builder.answer_prompt_builder import AnswerPromptBuilder
|
||||
from onyx.chat.prompt_builder.answer_prompt_builder import default_build_system_message
|
||||
from onyx.chat.prompt_builder.answer_prompt_builder import default_build_user_message
|
||||
from onyx.chat.prompt_builder.citations_prompt import (
|
||||
compute_max_document_tokens_for_persona,
|
||||
)
|
||||
from onyx.chat.user_files.parse_user_files import parse_user_files
|
||||
from onyx.configs.chat_configs import CHAT_TARGET_CHUNK_PERCENTAGE
|
||||
from onyx.configs.chat_configs import DISABLE_LLM_CHOOSE_SEARCH
|
||||
@@ -131,13 +134,13 @@ from onyx.tools.tool_implementations.internet_search.internet_search_tool import
|
||||
INTERNET_SEARCH_RESPONSE_ID,
|
||||
)
|
||||
from onyx.tools.tool_implementations.internet_search.internet_search_tool import (
|
||||
internet_search_response_to_search_docs,
|
||||
InternetSearchTool,
|
||||
)
|
||||
from onyx.tools.tool_implementations.internet_search.internet_search_tool import (
|
||||
from onyx.tools.tool_implementations.internet_search.models import (
|
||||
InternetSearchResponse,
|
||||
)
|
||||
from onyx.tools.tool_implementations.internet_search.internet_search_tool import (
|
||||
InternetSearchTool,
|
||||
from onyx.tools.tool_implementations.internet_search.utils import (
|
||||
internet_search_response_to_search_docs,
|
||||
)
|
||||
from onyx.tools.tool_implementations.search.search_tool import (
|
||||
FINAL_CONTEXT_DOCUMENTS_ID,
|
||||
@@ -277,6 +280,9 @@ def _handle_search_tool_response_summary(
|
||||
)
|
||||
|
||||
|
||||
# TODO: this takes the entire internet search response and sends it to LLM --> not correct
|
||||
# TODO: Internet search yields first an InternetSearchResponse to populate search results
|
||||
# and then yields a list of LlmDocs that should be added to context
|
||||
def _handle_internet_search_tool_response_summary(
|
||||
packet: ToolResponse,
|
||||
db_session: Session,
|
||||
@@ -910,6 +916,16 @@ def stream_chat_message_objects(
|
||||
structured_response_format=new_msg_req.structured_response_format,
|
||||
)
|
||||
|
||||
# Temp to get a pruning config for internet search
|
||||
available_tokens = compute_max_document_tokens_for_persona(
|
||||
db_session=db_session,
|
||||
persona=persona,
|
||||
actual_user_input=message_text,
|
||||
)
|
||||
|
||||
internet_pruning_config = document_pruning_config.copy()
|
||||
internet_pruning_config.max_tokens = available_tokens
|
||||
|
||||
tool_dict = construct_tools(
|
||||
persona=persona,
|
||||
prompt_config=prompt_config,
|
||||
@@ -936,6 +952,7 @@ def stream_chat_message_objects(
|
||||
),
|
||||
internet_search_tool_config=InternetSearchToolConfig(
|
||||
answer_style_config=answer_style_config,
|
||||
document_pruning_config=internet_pruning_config,
|
||||
),
|
||||
image_generation_tool_config=ImageGenerationToolConfig(
|
||||
additional_headers=litellm_additional_headers,
|
||||
|
||||
@@ -90,7 +90,7 @@ STOP_STREAM_PAT = os.environ.get("STOP_STREAM_PAT") or None
|
||||
HARD_DELETE_CHATS = os.environ.get("HARD_DELETE_CHATS", "").lower() == "true"
|
||||
|
||||
# Internet Search
|
||||
BING_API_KEY = os.environ.get("BING_API_KEY") or None
|
||||
EXA_API_KEY = os.environ.get("EXA_API_KEY") or None
|
||||
|
||||
# Enable in-house model for detecting connector-based filtering in queries
|
||||
ENABLE_CONNECTOR_CLASSIFIER = os.environ.get("ENABLE_CONNECTOR_CLASSIFIER", False)
|
||||
|
||||
@@ -16,9 +16,9 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from onyx.auth.schemas import UserRole
|
||||
from onyx.configs.app_configs import DISABLE_AUTH
|
||||
from onyx.configs.chat_configs import BING_API_KEY
|
||||
from onyx.configs.chat_configs import CONTEXT_CHUNKS_ABOVE
|
||||
from onyx.configs.chat_configs import CONTEXT_CHUNKS_BELOW
|
||||
from onyx.configs.chat_configs import EXA_API_KEY
|
||||
from onyx.configs.constants import NotificationType
|
||||
from onyx.context.search.enums import RecencyBiasSetting
|
||||
from onyx.db.constants import SLACK_BOT_PERSONA_PREFIX
|
||||
@@ -695,7 +695,7 @@ def update_persona_visibility(
|
||||
|
||||
def validate_persona_tools(tools: list[Tool]) -> None:
|
||||
for tool in tools:
|
||||
if tool.name == "InternetSearchTool" and not BING_API_KEY:
|
||||
if tool.name == "InternetSearchTool" and not EXA_API_KEY:
|
||||
raise ValueError(
|
||||
"Bing API key not found, please contact your Onyx admin to get it added!"
|
||||
)
|
||||
|
||||
@@ -45,7 +45,7 @@ BUILT_IN_TOOLS: list[InCodeToolInfo] = [
|
||||
in_code_tool_id=ImageGenerationTool.__name__,
|
||||
display_name=ImageGenerationTool._DISPLAY_NAME,
|
||||
),
|
||||
# don't show the InternetSearchTool as an option if BING_API_KEY is not available
|
||||
# don't show the InternetSearchTool as an option if EXA_API_KEY is not available
|
||||
*(
|
||||
[
|
||||
InCodeToolInfo(
|
||||
@@ -58,7 +58,7 @@ BUILT_IN_TOOLS: list[InCodeToolInfo] = [
|
||||
display_name=InternetSearchTool._DISPLAY_NAME,
|
||||
)
|
||||
]
|
||||
if os.environ.get("BING_API_KEY")
|
||||
if os.environ.get("EXA_API_KEY")
|
||||
else []
|
||||
),
|
||||
]
|
||||
|
||||
@@ -14,7 +14,7 @@ from onyx.configs.app_configs import AZURE_DALLE_API_KEY
|
||||
from onyx.configs.app_configs import AZURE_DALLE_API_VERSION
|
||||
from onyx.configs.app_configs import AZURE_DALLE_DEPLOYMENT_NAME
|
||||
from onyx.configs.app_configs import IMAGE_MODEL_NAME
|
||||
from onyx.configs.chat_configs import BING_API_KEY
|
||||
from onyx.configs.chat_configs import EXA_API_KEY
|
||||
from onyx.configs.model_configs import GEN_AI_TEMPERATURE
|
||||
from onyx.context.search.enums import LLMEvaluationType
|
||||
from onyx.context.search.enums import OptionalSearchSetting
|
||||
@@ -124,6 +124,9 @@ class InternetSearchToolConfig(BaseModel):
|
||||
citation_config=CitationConfig(all_docs_useful=True)
|
||||
)
|
||||
)
|
||||
document_pruning_config: DocumentPruningConfig = Field(
|
||||
default_factory=DocumentPruningConfig
|
||||
)
|
||||
|
||||
|
||||
class ImageGenerationToolConfig(BaseModel):
|
||||
@@ -219,15 +222,18 @@ def construct_tools(
|
||||
if not internet_search_tool_config:
|
||||
internet_search_tool_config = InternetSearchToolConfig()
|
||||
|
||||
if not BING_API_KEY:
|
||||
if not EXA_API_KEY:
|
||||
raise ValueError(
|
||||
"Internet search tool requires a Bing API key, please contact your Onyx admin to get it added!"
|
||||
"Internet search tool requires an Exa AI API key, please contact your Onyx admin to get it added!"
|
||||
)
|
||||
tool_dict[db_tool_model.id] = [
|
||||
InternetSearchTool(
|
||||
api_key=BING_API_KEY,
|
||||
api_key=EXA_API_KEY,
|
||||
db_session=db_session,
|
||||
llm=llm,
|
||||
answer_style_config=internet_search_tool_config.answer_style_config,
|
||||
prompt_config=prompt_config,
|
||||
pruning_config=internet_search_tool_config.document_pruning_config,
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
@@ -5,15 +5,26 @@ from typing import Any
|
||||
from typing import cast
|
||||
|
||||
import httpx
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from onyx.chat.chat_utils import combine_message_chain
|
||||
from onyx.chat.models import AnswerStyleConfig
|
||||
from onyx.chat.models import LlmDoc
|
||||
from onyx.chat.models import DocumentPruningConfig
|
||||
from onyx.chat.models import PromptConfig
|
||||
from onyx.chat.prompt_builder.answer_prompt_builder import AnswerPromptBuilder
|
||||
from onyx.chat.prompt_builder.citations_prompt import compute_max_llm_input_tokens
|
||||
from onyx.configs.constants import DocumentSource
|
||||
from onyx.configs.model_configs import GEN_AI_HISTORY_CUTOFF
|
||||
from onyx.context.search.models import SearchDoc
|
||||
from onyx.configs.model_configs import GEN_AI_MODEL_FALLBACK_MAX_TOKENS
|
||||
from onyx.connectors.models import Document
|
||||
from onyx.connectors.models import TextSection
|
||||
from onyx.db.search_settings import get_current_search_settings
|
||||
from onyx.indexing.chunker import Chunker
|
||||
from onyx.indexing.embedder import DefaultIndexingEmbedder
|
||||
from onyx.indexing.embedder import embed_chunks_with_failure_handling
|
||||
from onyx.indexing.indexing_pipeline import process_image_sections
|
||||
from onyx.indexing.models import DocAwareChunk
|
||||
from onyx.indexing.models import IndexChunk
|
||||
from onyx.llm.interfaces import LLM
|
||||
from onyx.llm.models import PreviousMessage
|
||||
from onyx.llm.utils import message_to_string
|
||||
@@ -26,8 +37,9 @@ from onyx.tools.tool import Tool
|
||||
from onyx.tools.tool_implementations.internet_search.models import (
|
||||
InternetSearchResponse,
|
||||
)
|
||||
from onyx.tools.tool_implementations.internet_search.models import (
|
||||
InternetSearchResult,
|
||||
from onyx.tools.tool_implementations.internet_search.models import InternetSearchResult
|
||||
from onyx.tools.tool_implementations.internet_search.utils import (
|
||||
internet_search_chunk_to_llm_doc,
|
||||
)
|
||||
from onyx.tools.tool_implementations.search_like_tool_utils import (
|
||||
build_next_prompt_for_search_like_tool,
|
||||
@@ -37,6 +49,7 @@ from onyx.tools.tool_implementations.search_like_tool_utils import (
|
||||
)
|
||||
from onyx.utils.logger import setup_logger
|
||||
from onyx.utils.special_types import JSON_ro
|
||||
from shared_configs.enums import EmbedTextType
|
||||
|
||||
logger = setup_logger()
|
||||
|
||||
@@ -66,70 +79,45 @@ Follow Up Input:
|
||||
""".strip()
|
||||
|
||||
|
||||
def llm_doc_from_internet_search_result(result: InternetSearchResult) -> LlmDoc:
|
||||
return LlmDoc(
|
||||
document_id=result.link,
|
||||
content=result.snippet,
|
||||
blurb=result.snippet,
|
||||
semantic_identifier=result.link,
|
||||
source_type=DocumentSource.WEB,
|
||||
metadata={},
|
||||
updated_at=datetime.now(),
|
||||
link=result.link,
|
||||
source_links={0: result.link},
|
||||
match_highlights=[],
|
||||
)
|
||||
|
||||
|
||||
def internet_search_response_to_search_docs(
|
||||
internet_search_response: InternetSearchResponse,
|
||||
) -> list[SearchDoc]:
|
||||
return [
|
||||
SearchDoc(
|
||||
document_id=doc.link,
|
||||
chunk_ind=-1,
|
||||
semantic_identifier=doc.title,
|
||||
link=doc.link,
|
||||
blurb=doc.snippet,
|
||||
source_type=DocumentSource.NOT_APPLICABLE,
|
||||
boost=0,
|
||||
hidden=False,
|
||||
metadata={},
|
||||
score=None,
|
||||
match_highlights=[],
|
||||
updated_at=None,
|
||||
primary_owners=[],
|
||||
secondary_owners=[],
|
||||
is_internet=True,
|
||||
)
|
||||
for doc in internet_search_response.internet_results
|
||||
]
|
||||
|
||||
|
||||
# override_kwargs is not supported for internet search tools
|
||||
class InternetSearchTool(Tool[None]):
|
||||
_NAME = "run_internet_search"
|
||||
_DISPLAY_NAME = "Internet Search"
|
||||
_DESCRIPTION = "Perform an internet search for up-to-date information."
|
||||
|
||||
# TODO: Tool constructor sets answerstyle to all sources relevant, but that is not true for internet search
|
||||
def __init__(
|
||||
self,
|
||||
db_session: Session,
|
||||
llm: LLM,
|
||||
api_key: str,
|
||||
pruning_config: DocumentPruningConfig,
|
||||
answer_style_config: AnswerStyleConfig,
|
||||
prompt_config: PromptConfig,
|
||||
num_results: int = 10,
|
||||
) -> None:
|
||||
self.db_session = db_session
|
||||
self.llm = llm
|
||||
self.api_key = api_key
|
||||
self.pruning_config = pruning_config
|
||||
self.answer_style_config = answer_style_config
|
||||
self.prompt_config = prompt_config
|
||||
|
||||
self.host = "https://api.bing.microsoft.com/v7.0"
|
||||
self.host = "https://api.exa.ai"
|
||||
self.headers = {
|
||||
"Ocp-Apim-Subscription-Key": api_key,
|
||||
"x-api-key": api_key,
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
self.num_results = num_results
|
||||
self.client = httpx.Client()
|
||||
|
||||
max_input_tokens = compute_max_llm_input_tokens(
|
||||
llm_config=llm.config,
|
||||
)
|
||||
if max_input_tokens < 3 * GEN_AI_MODEL_FALLBACK_MAX_TOKENS:
|
||||
self.chunks_above = 0
|
||||
self.chunks_below = 0
|
||||
|
||||
self.chunks_above + self.chunks_below + 1
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
@@ -162,6 +150,8 @@ class InternetSearchTool(Tool[None]):
|
||||
},
|
||||
}
|
||||
|
||||
"""For LLMs that don't support tool calling"""
|
||||
|
||||
def check_if_needs_internet_search(
|
||||
self,
|
||||
query: str,
|
||||
@@ -211,57 +201,181 @@ class InternetSearchTool(Tool[None]):
|
||||
self, *args: ToolResponse
|
||||
) -> str | list[str | dict[str, Any]]:
|
||||
search_response = cast(InternetSearchResponse, args[0].response)
|
||||
return json.dumps(search_response.model_dump())
|
||||
return json.dumps(search_response.model_dump(), default=str)
|
||||
|
||||
def _perform_search(self, query: str) -> InternetSearchResponse:
|
||||
response = self.client.get(
|
||||
f"{self.host}/search",
|
||||
headers=self.headers,
|
||||
params={"q": query, "count": self.num_results},
|
||||
with httpx.Client(timeout=20.0) as client: # Exa search api takes ~10-15s
|
||||
response = client.post(
|
||||
f"{self.host}/search",
|
||||
headers=self.headers,
|
||||
data=json.dumps(
|
||||
{
|
||||
"query": query,
|
||||
"type": "auto",
|
||||
"numResults": self.num_results,
|
||||
"contents": {
|
||||
"text": True,
|
||||
"livecrawl": "always",
|
||||
"summary": True,
|
||||
"highlights": {
|
||||
"numSentences": 5,
|
||||
"highlightsPerUrl": 1,
|
||||
"query": "Most relevant to the question: {query}",
|
||||
},
|
||||
},
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
results = response.json()
|
||||
|
||||
# Exa always returns results (questionable)
|
||||
search_results = results["results"]
|
||||
|
||||
internet_results = []
|
||||
for result in search_results:
|
||||
try:
|
||||
# Check required fields first
|
||||
required_fields = ["title", "url", "text", "summary", "highlights"]
|
||||
missing_fields = [
|
||||
field for field in required_fields if field not in result
|
||||
]
|
||||
if missing_fields:
|
||||
logger.warning(
|
||||
f"Missing required fields in search result: {missing_fields}"
|
||||
)
|
||||
continue
|
||||
|
||||
internet_results.append(
|
||||
InternetSearchResult(
|
||||
title=result["title"],
|
||||
url=result["url"],
|
||||
published_date=result.get(
|
||||
"publishedDate", datetime.now().isoformat()
|
||||
),
|
||||
author=result.get("author"),
|
||||
score=result.get("score"),
|
||||
full_content=result["text"],
|
||||
relevant_content="\n".join(result["highlights"]),
|
||||
summary=result["summary"],
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing search result: {e}")
|
||||
continue
|
||||
|
||||
return InternetSearchResponse(
|
||||
revised_query=query,
|
||||
internet_results=internet_results,
|
||||
)
|
||||
|
||||
def embed_internet_search_results(
|
||||
self, results: InternetSearchResponse, embedder: DefaultIndexingEmbedder
|
||||
) -> list[DocAwareChunk]:
|
||||
documents: list[Document] = []
|
||||
for result in results.internet_results:
|
||||
# Create a document from the search result
|
||||
doc = Document(
|
||||
id=result.url,
|
||||
sections=[TextSection(link=result.url, text=result.full_content)],
|
||||
source=DocumentSource.NOT_APPLICABLE,
|
||||
semantic_identifier=result.title,
|
||||
metadata={
|
||||
"url": result.url,
|
||||
"published_date": result.published_date,
|
||||
"author": result.author or "Unknown",
|
||||
"score": str(result.score) if result.score else "N/A",
|
||||
},
|
||||
doc_updated_at=(
|
||||
datetime.fromisoformat(result.published_date)
|
||||
if result.published_date
|
||||
else None
|
||||
),
|
||||
title=result.title,
|
||||
)
|
||||
|
||||
documents.append(doc)
|
||||
|
||||
indexing_documents = process_image_sections(documents)
|
||||
|
||||
chunker = Chunker(
|
||||
tokenizer=embedder.embedding_model.tokenizer,
|
||||
enable_multipass=False,
|
||||
enable_contextual_rag=False,
|
||||
)
|
||||
chunks = chunker.chunk(indexing_documents)
|
||||
|
||||
chunks_with_embeddings, _ = (
|
||||
embed_chunks_with_failure_handling(
|
||||
chunks=chunks,
|
||||
embedder=embedder,
|
||||
)
|
||||
if chunks
|
||||
else ([], [])
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
return chunks_with_embeddings
|
||||
|
||||
results = response.json()
|
||||
def vector_similarity_sort(
|
||||
self, query: list[float], chunks: list[IndexChunk]
|
||||
) -> list[IndexChunk]:
|
||||
def cosine_similarity(a: list[float], b: list[float]) -> float:
|
||||
dot_product = sum(x * y for x, y in zip(a, b))
|
||||
norm_a = sum(x * x for x in a) ** 0.5
|
||||
norm_b = sum(x * x for x in b) ** 0.5
|
||||
if norm_a == 0 or norm_b == 0:
|
||||
return 0.0
|
||||
return dot_product / (norm_a * norm_b)
|
||||
|
||||
# If no hits, Bing does not include the webPages key
|
||||
search_results = (
|
||||
results["webPages"]["value"][: self.num_results]
|
||||
if "webPages" in results
|
||||
else []
|
||||
)
|
||||
# Calculate similarity scores for each chunk
|
||||
scored_chunks = []
|
||||
for chunk in chunks:
|
||||
# Use the full embedding for similarity calculation
|
||||
similarity = cosine_similarity(query, chunk.embeddings.full_embedding)
|
||||
scored_chunks.append((similarity, chunk))
|
||||
|
||||
return InternetSearchResponse(
|
||||
revised_query=query,
|
||||
internet_results=[
|
||||
InternetSearchResult(
|
||||
title=result["name"],
|
||||
link=result["url"],
|
||||
snippet=result["snippet"],
|
||||
)
|
||||
for result in search_results
|
||||
],
|
||||
)
|
||||
# Sort chunks by similarity score in descending order
|
||||
scored_chunks.sort(key=lambda x: x[0], reverse=True)
|
||||
|
||||
# Return just the chunks in order of similarity
|
||||
return [chunk for _, chunk in scored_chunks]
|
||||
|
||||
def run(
|
||||
self, override_kwargs: None = None, **kwargs: str
|
||||
) -> Generator[ToolResponse, None, None]:
|
||||
query = cast(str, kwargs["internet_search_query"])
|
||||
|
||||
query = kwargs["internet_search_query"]
|
||||
results = self._perform_search(query)
|
||||
yield ToolResponse(
|
||||
id=INTERNET_SEARCH_RESPONSE_ID,
|
||||
response=results,
|
||||
|
||||
# Yield initial search response
|
||||
yield ToolResponse(id=INTERNET_SEARCH_RESPONSE_ID, response=results)
|
||||
|
||||
search_settings = get_current_search_settings(self.db_session)
|
||||
embedder = DefaultIndexingEmbedder.from_db_search_settings(
|
||||
search_settings=search_settings
|
||||
)
|
||||
query_embedding = embedder.embedding_model.encode(
|
||||
[query], text_type=EmbedTextType.QUERY
|
||||
)[0]
|
||||
embedded_chunks = self.embed_internet_search_results(
|
||||
results,
|
||||
embedder,
|
||||
)
|
||||
|
||||
llm_docs = [
|
||||
llm_doc_from_internet_search_result(result)
|
||||
for result in results.internet_results
|
||||
]
|
||||
sorted_chunks = self.vector_similarity_sort(query_embedding, embedded_chunks)
|
||||
pruned_llm_docs = []
|
||||
token_count = 0
|
||||
for chunk in sorted_chunks:
|
||||
chunk_token_count = len(chunk.embeddings.full_embedding)
|
||||
if token_count + chunk_token_count > self.pruning_config.max_tokens:
|
||||
break
|
||||
token_count += chunk_token_count
|
||||
pruned_llm_docs.append(internet_search_chunk_to_llm_doc(chunk))
|
||||
|
||||
yield ToolResponse(
|
||||
id=FINAL_CONTEXT_DOCUMENTS_ID,
|
||||
response=llm_docs,
|
||||
response=pruned_llm_docs,
|
||||
)
|
||||
|
||||
def final_result(self, *args: ToolResponse) -> JSON_ro:
|
||||
|
||||
@@ -3,10 +3,17 @@ from pydantic import BaseModel
|
||||
|
||||
class InternetSearchResult(BaseModel):
|
||||
title: str
|
||||
link: str
|
||||
snippet: str
|
||||
url: str
|
||||
published_date: str
|
||||
author: str | None
|
||||
score: float | None
|
||||
full_content: str | None
|
||||
relevant_content: str
|
||||
summary: str
|
||||
|
||||
|
||||
class InternetSearchResponse(BaseModel):
|
||||
class InternetSearchResponse(
|
||||
BaseModel
|
||||
): # TODO: rewrite this to be closer to search tool SearchResponseSummary
|
||||
revised_query: str
|
||||
internet_results: list[InternetSearchResult]
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
from onyx.chat.models import LlmDoc
|
||||
from onyx.configs.constants import DocumentSource
|
||||
from onyx.context.search.models import SearchDoc
|
||||
from onyx.indexing.models import IndexChunk
|
||||
from onyx.tools.tool_implementations.internet_search.models import (
|
||||
InternetSearchResponse,
|
||||
)
|
||||
|
||||
|
||||
# TODO: Temp, shouldn't be feeding in chunks here I think, but that's what we're left with after pruning
|
||||
def internet_search_chunk_to_llm_doc(chunk: IndexChunk) -> LlmDoc:
|
||||
return LlmDoc(
|
||||
document_id=chunk.source_document.id,
|
||||
content=chunk.content,
|
||||
blurb=chunk.blurb,
|
||||
semantic_identifier=chunk.source_document.title,
|
||||
source_type=DocumentSource.NOT_APPLICABLE,
|
||||
metadata={},
|
||||
updated_at=chunk.source_document.doc_updated_at,
|
||||
link=chunk.source_document.id,
|
||||
source_links={0: chunk.source_document.id},
|
||||
match_highlights=[],
|
||||
)
|
||||
|
||||
|
||||
def internet_search_response_to_search_docs(
|
||||
internet_search_response: InternetSearchResponse,
|
||||
) -> list[SearchDoc]:
|
||||
return [
|
||||
SearchDoc(
|
||||
document_id=doc.url,
|
||||
chunk_ind=-1,
|
||||
semantic_identifier=doc.title,
|
||||
link=doc.url,
|
||||
blurb=doc.summary,
|
||||
source_type=DocumentSource.NOT_APPLICABLE,
|
||||
boost=0,
|
||||
hidden=False,
|
||||
metadata={},
|
||||
score=doc.score,
|
||||
match_highlights=[],
|
||||
updated_at=doc.published_date,
|
||||
primary_owners=[],
|
||||
secondary_owners=[],
|
||||
is_internet=True,
|
||||
)
|
||||
for doc in internet_search_response.internet_results
|
||||
]
|
||||
@@ -46,7 +46,7 @@ services:
|
||||
- DISABLE_GENERATIVE_AI=${DISABLE_GENERATIVE_AI:-}
|
||||
- DISABLE_LITELLM_STREAMING=${DISABLE_LITELLM_STREAMING:-}
|
||||
- LITELLM_EXTRA_HEADERS=${LITELLM_EXTRA_HEADERS:-}
|
||||
- BING_API_KEY=${BING_API_KEY:-}
|
||||
- EXA_API_KEY=${EXA_API_KEY:-}
|
||||
- DISABLE_LLM_DOC_RELEVANCE=${DISABLE_LLM_DOC_RELEVANCE:-}
|
||||
- GEN_AI_API_KEY=${GEN_AI_API_KEY:-}
|
||||
# if set, allows for the use of the token budget system
|
||||
@@ -180,7 +180,7 @@ services:
|
||||
- DISABLE_LITELLM_STREAMING=${DISABLE_LITELLM_STREAMING:-}
|
||||
- LITELLM_EXTRA_HEADERS=${LITELLM_EXTRA_HEADERS:-}
|
||||
- GEN_AI_API_KEY=${GEN_AI_API_KEY:-}
|
||||
- BING_API_KEY=${BING_API_KEY:-}
|
||||
- EXA_API_KEY=${EXA_API_KEY:-}
|
||||
# Query Options
|
||||
- DOC_TIME_DECAY=${DOC_TIME_DECAY:-} # Recency Bias for search results, decay at 1 / (1 + DOC_TIME_DECAY * x years)
|
||||
- HYBRID_ALPHA=${HYBRID_ALPHA:-} # Hybrid Search Alpha (0 for entirely keyword, 1 for entirely vector)
|
||||
|
||||
@@ -52,7 +52,7 @@ services:
|
||||
- DISABLE_GENERATIVE_AI=${DISABLE_GENERATIVE_AI:-}
|
||||
- DISABLE_LITELLM_STREAMING=${DISABLE_LITELLM_STREAMING:-}
|
||||
- LITELLM_EXTRA_HEADERS=${LITELLM_EXTRA_HEADERS:-}
|
||||
- BING_API_KEY=${BING_API_KEY:-}
|
||||
- EXA_API_KEY=${EXA_API_KEY:-}
|
||||
- DISABLE_LLM_DOC_RELEVANCE=${DISABLE_LLM_DOC_RELEVANCE:-}
|
||||
- GEN_AI_API_KEY=${GEN_AI_API_KEY:-}
|
||||
- TOKEN_BUDGET_GLOBALLY_ENABLED=${TOKEN_BUDGET_GLOBALLY_ENABLED:-}
|
||||
@@ -162,7 +162,7 @@ services:
|
||||
- DISABLE_LITELLM_STREAMING=${DISABLE_LITELLM_STREAMING:-}
|
||||
- LITELLM_EXTRA_HEADERS=${LITELLM_EXTRA_HEADERS:-}
|
||||
- GEN_AI_API_KEY=${GEN_AI_API_KEY:-}
|
||||
- BING_API_KEY=${BING_API_KEY:-}
|
||||
- EXA_API_KEY=${EXA_API_KEY:-}
|
||||
# Query Options
|
||||
- DOC_TIME_DECAY=${DOC_TIME_DECAY:-}
|
||||
- HYBRID_ALPHA=${HYBRID_ALPHA:-}
|
||||
|
||||
@@ -661,7 +661,7 @@ configMap:
|
||||
LANGUAGE_CHAT_NAMING_HINT: ""
|
||||
QA_PROMPT_OVERRIDE: ""
|
||||
# Internet Search Tool
|
||||
BING_API_KEY: ""
|
||||
EXA_API_KEY: ""
|
||||
# Don't change the NLP models unless you know what you're doing
|
||||
EMBEDDING_BATCH_SIZE: ""
|
||||
DOCUMENT_ENCODER_MODEL: ""
|
||||
|
||||
87
deployment/kubernetes/env-configmap.yaml
Normal file
87
deployment/kubernetes/env-configmap.yaml
Normal file
@@ -0,0 +1,87 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: env-configmap
|
||||
data:
|
||||
# Auth Setting, also check the secrets file
|
||||
AUTH_TYPE: "disabled" # Change this for production uses unless Onyx is only accessible behind VPN
|
||||
ENCRYPTION_KEY_SECRET: "" # This should not be specified directly in the yaml, this is just for reference
|
||||
SESSION_EXPIRE_TIME_SECONDS: "86400" # 1 Day Default
|
||||
VALID_EMAIL_DOMAINS: "" # Can be something like onyx.app, as an extra double-check
|
||||
SMTP_SERVER: "" # For sending verification emails, if unspecified then defaults to 'smtp.gmail.com'
|
||||
SMTP_PORT: "" # For sending verification emails, if unspecified then defaults to '587'
|
||||
SMTP_USER: "" # 'your-email@company.com'
|
||||
SMTP_PASS: "" # 'your-gmail-password'
|
||||
EMAIL_FROM: "" # 'your-email@company.com' SMTP_USER missing used instead
|
||||
CORS_ALLOWED_ORIGIN: ""
|
||||
# Gen AI Settings
|
||||
GEN_AI_MAX_TOKENS: ""
|
||||
QA_TIMEOUT: "60"
|
||||
MAX_CHUNKS_FED_TO_CHAT: ""
|
||||
DISABLE_LLM_DOC_RELEVANCE: ""
|
||||
DISABLE_LLM_CHOOSE_SEARCH: ""
|
||||
DISABLE_LLM_QUERY_REPHRASE: ""
|
||||
# Query Options
|
||||
DOC_TIME_DECAY: ""
|
||||
HYBRID_ALPHA: ""
|
||||
EDIT_KEYWORD_QUERY: ""
|
||||
MULTILINGUAL_QUERY_EXPANSION: ""
|
||||
LANGUAGE_HINT: ""
|
||||
LANGUAGE_CHAT_NAMING_HINT: ""
|
||||
QA_PROMPT_OVERRIDE: ""
|
||||
# Other Services
|
||||
POSTGRES_HOST: "relational-db-service"
|
||||
POSTGRES_DEFAULT_SCHEMA: ""
|
||||
VESPA_HOST: "document-index-service"
|
||||
REDIS_HOST: "redis-service"
|
||||
# Internet Search Tool
|
||||
EXA_API_KEY: ""
|
||||
# Don't change the NLP models unless you know what you're doing
|
||||
EMBEDDING_BATCH_SIZE: ""
|
||||
DOCUMENT_ENCODER_MODEL: ""
|
||||
NORMALIZE_EMBEDDINGS: ""
|
||||
ASYM_QUERY_PREFIX: ""
|
||||
ASYM_PASSAGE_PREFIX: ""
|
||||
DISABLE_RERANK_FOR_STREAMING: ""
|
||||
MODEL_SERVER_HOST: "inference-model-server-service"
|
||||
MODEL_SERVER_PORT: ""
|
||||
INDEXING_MODEL_SERVER_HOST: "indexing-model-server-service"
|
||||
MIN_THREADS_ML_MODELS: ""
|
||||
# Indexing Configs
|
||||
VESPA_SEARCHER_THREADS: ""
|
||||
ENABLED_CONNECTOR_TYPES: ""
|
||||
DISABLE_INDEX_UPDATE_ON_SWAP: ""
|
||||
DASK_JOB_CLIENT_ENABLED: ""
|
||||
CONTINUE_ON_CONNECTOR_FAILURE: ""
|
||||
EXPERIMENTAL_CHECKPOINTING_ENABLED: ""
|
||||
CONFLUENCE_CONNECTOR_LABELS_TO_SKIP: ""
|
||||
JIRA_API_VERSION: ""
|
||||
WEB_CONNECTOR_VALIDATE_URLS: ""
|
||||
GONG_CONNECTOR_START_TIME: ""
|
||||
NOTION_CONNECTOR_ENABLE_RECURSIVE_PAGE_LOOKUP: ""
|
||||
MAX_DOCUMENT_CHARS: ""
|
||||
MAX_FILE_SIZE_BYTES: ""
|
||||
# Worker Parallelism
|
||||
CELERY_WORKER_INDEXING_CONCURRENCY: ""
|
||||
CELERY_WORKER_LIGHT_CONCURRENCY: ""
|
||||
CELERY_WORKER_LIGHT_PREFETCH_MULTIPLIER: ""
|
||||
# OnyxBot SlackBot Configs
|
||||
DANSWER_BOT_DISABLE_DOCS_ONLY_ANSWER: ""
|
||||
DANSWER_BOT_DISPLAY_ERROR_MSGS: ""
|
||||
DANSWER_BOT_RESPOND_EVERY_CHANNEL: ""
|
||||
DANSWER_BOT_DISABLE_COT: "" # Currently unused
|
||||
NOTIFY_SLACKBOT_NO_ANSWER: ""
|
||||
# Logging
|
||||
# Optional Telemetry, please keep it on (nothing sensitive is collected)? <3
|
||||
# https://docs.onyx.app/more/telemetry
|
||||
DISABLE_TELEMETRY: ""
|
||||
LOG_LEVEL: ""
|
||||
LOG_ALL_MODEL_INTERACTIONS: ""
|
||||
LOG_DANSWER_MODEL_INTERACTIONS: ""
|
||||
LOG_VESPA_TIMING_INFORMATION: ""
|
||||
# Shared or Non-backend Related
|
||||
INTERNAL_URL: "http://api-server-service:80" # for web server
|
||||
WEB_DOMAIN: "http://localhost:3000" # for web server and api server
|
||||
DOMAIN: "localhost" # for nginx
|
||||
# Chat Configs
|
||||
HARD_DELETE_CHATS: ""
|
||||
@@ -518,6 +518,13 @@ export function AssistantEditor({
|
||||
.map((toolId) => Number(toolId))
|
||||
.filter((toolId) => values.enabled_tools_map[toolId]);
|
||||
|
||||
if (
|
||||
internetSearchTool &&
|
||||
values.enabled_tools_map[internetSearchTool.id]
|
||||
) {
|
||||
formikHelpers.setFieldValue("datetime_aware", true); // Auto-toggle so internet search has access to current date
|
||||
}
|
||||
|
||||
const searchToolEnabled = searchTool
|
||||
? enabledTools.includes(searchTool.id)
|
||||
: false;
|
||||
|
||||
Reference in New Issue
Block a user