mirror of
https://github.com/open-webui/open-webui.git
synced 2026-08-28 05:27:35 +00:00
refac
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
This commit is contained in:
parent
e8fda1c7a0
commit
11e61b69eb
3 changed files with 56 additions and 39 deletions
|
|
@ -1078,6 +1078,28 @@ SENTENCE_TRANSFORMERS_CROSS_ENCODER_SIGMOID_ACTIVATION_FUNCTION = (
|
|||
os.getenv('SENTENCE_TRANSFORMERS_CROSS_ENCODER_SIGMOID_ACTIVATION_FUNCTION', 'True').lower() == 'true'
|
||||
)
|
||||
|
||||
####################################
|
||||
# KNOWLEDGE TOOLS
|
||||
####################################
|
||||
|
||||
|
||||
def _int_env(name: str, default: int) -> int:
|
||||
try:
|
||||
return max(int(os.getenv(name) or default), 1)
|
||||
except (ValueError, TypeError):
|
||||
return default
|
||||
|
||||
|
||||
# Total output of a single kb_exec call, whatever the command.
|
||||
KB_EXEC_MAX_OUTPUT_CHARS = _int_env('KB_EXEC_MAX_OUTPUT_CHARS', 30_000)
|
||||
# Files a single kb_exec grep may scan before it asks for a narrower scope.
|
||||
KB_EXEC_MAX_GREP_FILES = _int_env('KB_EXEC_MAX_GREP_FILES', 200)
|
||||
# Matching lines returned by kb_exec grep and grep_knowledge_files.
|
||||
KNOWLEDGE_GREP_MAX_MATCHES = _int_env('KNOWLEDGE_GREP_MAX_MATCHES', 50)
|
||||
# Characters returned by view_file / view_knowledge_file.
|
||||
VIEW_FILE_MAX_CHARS = _int_env('VIEW_FILE_MAX_CHARS', 100_000)
|
||||
VIEW_FILE_DEFAULT_MAX_CHARS = _int_env('VIEW_FILE_DEFAULT_MAX_CHARS', 10_000)
|
||||
|
||||
####################################
|
||||
# TOOLS/FUNCTIONS PIP OPTIONS
|
||||
####################################
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@ from typing import Literal, Optional
|
|||
from fastapi import HTTPException, Request
|
||||
|
||||
from open_webui.config import RAG_EMBEDDING_QUERY_PREFIX
|
||||
from open_webui.env import (
|
||||
KNOWLEDGE_GREP_MAX_MATCHES,
|
||||
VIEW_FILE_DEFAULT_MAX_CHARS,
|
||||
VIEW_FILE_MAX_CHARS,
|
||||
)
|
||||
from open_webui.models.channels import Channel, ChannelMember, Channels
|
||||
from open_webui.models.chats import Chats
|
||||
from open_webui.models.config import Config
|
||||
|
|
@ -2157,12 +2162,6 @@ async def search_knowledge_files(
|
|||
return json.dumps({'error': str(e)})
|
||||
|
||||
|
||||
# Hard cap for view_file / view_knowledge_file output
|
||||
MAX_VIEW_FILE_CHARS = 100_000
|
||||
DEFAULT_VIEW_FILE_MAX_CHARS = 10_000
|
||||
MAX_GREP_RESULTS = 50
|
||||
|
||||
|
||||
async def _get_accessible_chat_files(
|
||||
files: Optional[list[dict]],
|
||||
user: dict,
|
||||
|
|
@ -2229,7 +2228,7 @@ def _grep_file_models(
|
|||
if matches(line):
|
||||
file_matches += 1
|
||||
total_matches += 1
|
||||
if not count_only and len(results) < MAX_GREP_RESULTS:
|
||||
if not count_only and len(results) < KNOWLEDGE_GREP_MAX_MATCHES:
|
||||
results.append(f'{file.id} {file.filename}:{i}: {line}')
|
||||
|
||||
if file_matches > 0 and count_only:
|
||||
|
|
@ -2244,8 +2243,8 @@ def _grep_file_models(
|
|||
return f'No matches for "{pattern}"'
|
||||
|
||||
output = '\n'.join(results)
|
||||
if total_matches > MAX_GREP_RESULTS:
|
||||
output += f'\n[{MAX_GREP_RESULTS} of {total_matches} matches shown — use file_id to narrow]'
|
||||
if total_matches > KNOWLEDGE_GREP_MAX_MATCHES:
|
||||
output += f'\n[{KNOWLEDGE_GREP_MAX_MATCHES} of {total_matches} matches shown — use file_id to narrow]'
|
||||
return output
|
||||
|
||||
|
||||
|
|
@ -2589,7 +2588,7 @@ async def grep_knowledge_files(
|
|||
async def view_file(
|
||||
file_id: str,
|
||||
offset: int = 0,
|
||||
max_chars: int = DEFAULT_VIEW_FILE_MAX_CHARS,
|
||||
max_chars: int = VIEW_FILE_DEFAULT_MAX_CHARS,
|
||||
line_numbers: bool = False,
|
||||
start_line: Optional[int] = None,
|
||||
end_line: Optional[int] = None,
|
||||
|
|
@ -2602,7 +2601,7 @@ async def view_file(
|
|||
|
||||
:param file_id: The ID of the file to retrieve
|
||||
:param offset: Character offset to start reading from (default: 0)
|
||||
:param max_chars: Maximum characters to return (default: 10000, hard cap: 100000)
|
||||
:param max_chars: Maximum characters to return (a server-side hard cap applies)
|
||||
:param line_numbers: If true, prefix each line with its 1-indexed line number
|
||||
:param start_line: Optional 1-indexed start line (overrides offset/max_chars when set)
|
||||
:param end_line: Optional 1-indexed end line (inclusive)
|
||||
|
|
@ -2624,10 +2623,10 @@ async def view_file(
|
|||
try:
|
||||
max_chars = int(max_chars)
|
||||
except ValueError:
|
||||
max_chars = DEFAULT_VIEW_FILE_MAX_CHARS
|
||||
max_chars = VIEW_FILE_DEFAULT_MAX_CHARS
|
||||
|
||||
# Enforce hard cap
|
||||
max_chars = min(max(max_chars, 1), MAX_VIEW_FILE_CHARS)
|
||||
max_chars = min(max(max_chars, 1), VIEW_FILE_MAX_CHARS)
|
||||
offset = max(offset, 0)
|
||||
|
||||
try:
|
||||
|
|
@ -2705,7 +2704,7 @@ async def view_file(
|
|||
async def view_knowledge_file(
|
||||
file_id: str,
|
||||
offset: int = 0,
|
||||
max_chars: int = DEFAULT_VIEW_FILE_MAX_CHARS,
|
||||
max_chars: int = VIEW_FILE_DEFAULT_MAX_CHARS,
|
||||
line_numbers: bool = False,
|
||||
start_line: Optional[int] = None,
|
||||
end_line: Optional[int] = None,
|
||||
|
|
@ -2717,7 +2716,7 @@ async def view_knowledge_file(
|
|||
|
||||
:param file_id: The ID of the file to retrieve
|
||||
:param offset: Character offset to start reading from (default: 0)
|
||||
:param max_chars: Maximum characters to return (default: 10000, hard cap: 100000)
|
||||
:param max_chars: Maximum characters to return (a server-side hard cap applies)
|
||||
:param line_numbers: If true, prefix each line with its 1-indexed line number
|
||||
:param start_line: Optional 1-indexed start line (overrides offset/max_chars when set)
|
||||
:param end_line: Optional 1-indexed end line (inclusive)
|
||||
|
|
@ -2739,10 +2738,10 @@ async def view_knowledge_file(
|
|||
try:
|
||||
max_chars = int(max_chars)
|
||||
except ValueError:
|
||||
max_chars = DEFAULT_VIEW_FILE_MAX_CHARS
|
||||
max_chars = VIEW_FILE_DEFAULT_MAX_CHARS
|
||||
|
||||
# Enforce hard cap
|
||||
max_chars = min(max(max_chars, 1), MAX_VIEW_FILE_CHARS)
|
||||
max_chars = min(max(max_chars, 1), VIEW_FILE_MAX_CHARS)
|
||||
offset = max(offset, 0)
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -19,15 +19,16 @@ from typing import Optional
|
|||
import regex
|
||||
from fastapi import Request
|
||||
|
||||
from open_webui.env import (
|
||||
KB_EXEC_MAX_GREP_FILES,
|
||||
KB_EXEC_MAX_OUTPUT_CHARS,
|
||||
KNOWLEDGE_GREP_MAX_MATCHES,
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Limits
|
||||
MAX_CAT_CHARS = 100_000
|
||||
DEFAULT_CAT_CHARS = 10_000
|
||||
MAX_GREP_FILES = 200
|
||||
DEFAULT_HEAD_LINES = 10
|
||||
DEFAULT_TAIL_LINES = 10
|
||||
MAX_GREP_MATCHES = 50
|
||||
|
||||
# Matching time allowed per tool call. Backtracking cost is exponential in the length of the
|
||||
# matched text, so capping the pattern or the line does not bound it.
|
||||
|
|
@ -627,21 +628,10 @@ async def _kb_cat(args: list[str], flags: set[str], user: dict, model_knowledge:
|
|||
return resolved['error']
|
||||
|
||||
content = resolved['content']
|
||||
show_numbers = 'n' in flags
|
||||
|
||||
if len(content) > MAX_CAT_CHARS:
|
||||
content = content[:MAX_CAT_CHARS]
|
||||
truncated = True
|
||||
else:
|
||||
truncated = False
|
||||
|
||||
if show_numbers:
|
||||
if 'n' in flags:
|
||||
lines = content.split('\n')
|
||||
content = '\n'.join(f'{i}: {line}' for i, line in enumerate(lines, 1))
|
||||
|
||||
if truncated:
|
||||
content += f'\n[truncated at {MAX_CAT_CHARS:,} chars — use head/tail/sed/grep to navigate]'
|
||||
|
||||
return content
|
||||
|
||||
|
||||
|
|
@ -790,7 +780,7 @@ async def _kb_grep(
|
|||
if ext_filter:
|
||||
accessible = [f for f in accessible if f['filename'].endswith(f'.{ext_filter}')]
|
||||
|
||||
if len(accessible) > MAX_GREP_FILES:
|
||||
if len(accessible) > KB_EXEC_MAX_GREP_FILES:
|
||||
return f'Too many files ({len(accessible)}). Scope your search: grep "{pattern}" docs/ or grep "{pattern}" *.py'
|
||||
|
||||
from open_webui.models.files import Files
|
||||
|
|
@ -822,7 +812,7 @@ async def _kb_grep(
|
|||
|
||||
if not count_only and not filenames_only:
|
||||
for line_num, line_text in file_matches:
|
||||
if len(results) < MAX_GREP_MATCHES:
|
||||
if len(results) < KNOWLEDGE_GREP_MAX_MATCHES:
|
||||
results.append(f'{file_info["id"]} {file_info["filename"]}:{line_num}: {line_text.rstrip()}')
|
||||
|
||||
if count_only:
|
||||
|
|
@ -841,8 +831,8 @@ async def _kb_grep(
|
|||
return f'No matches for "{pattern}" across {len(accessible)} files'
|
||||
|
||||
output = '\n'.join(results)
|
||||
if total_matches > MAX_GREP_MATCHES:
|
||||
output += f'\n[showing {MAX_GREP_MATCHES} of {total_matches} matches]'
|
||||
if total_matches > KNOWLEDGE_GREP_MAX_MATCHES:
|
||||
output += f'\n[showing {KNOWLEDGE_GREP_MAX_MATCHES} of {total_matches} matches]'
|
||||
return output
|
||||
|
||||
|
||||
|
|
@ -1181,7 +1171,13 @@ async def kb_exec(
|
|||
|
||||
# One budget for the whole command: a per-search budget would multiply by segment count.
|
||||
with match_budget():
|
||||
return await _execute_pipeline(segments, __user__, __model_knowledge__)
|
||||
output = await _execute_pipeline(segments, __user__, __model_knowledge__)
|
||||
if len(output) > KB_EXEC_MAX_OUTPUT_CHARS:
|
||||
output = output[:KB_EXEC_MAX_OUTPUT_CHARS] + (
|
||||
f'\n[output truncated at {KB_EXEC_MAX_OUTPUT_CHARS:,} chars'
|
||||
' — narrow the command with a path, glob, head/tail/sed or grep]'
|
||||
)
|
||||
return output
|
||||
except Exception as e:
|
||||
log.exception(f'kb_exec error: {e}')
|
||||
return f'Error: {e}'
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue