mirror of
https://github.com/open-webui/open-webui.git
synced 2026-09-11 22:52:54 +00:00
feat: sort flags for kb_exec file listings (#29840)
kb_exec silently ignored ls -t. It accepted the flag, dropped it and returned the same undefined database order as a plain ls, so the model believed it had a newest-first list when it did not. With a few thousand files in a knowledge base there was no way to ask what changed recently without reading the whole listing and comparing dates by eye. ls, tree and find now sort their file lines by name by default, so the same knowledge base always lists the same way. -t sorts newest first, -S largest first and -r reverses, combinable like -at or -tr, matching the flags the model already knows from a shell. Directories keep their existing name order and stay grouped first. No query changes: the timestamps and sizes were already loaded for the date and size columns.
This commit is contained in:
parent
a253bf0c32
commit
1b67da7004
1 changed files with 21 additions and 6 deletions
|
|
@ -587,7 +587,7 @@ async def _kb_ls(args: list[str], flags: set[str], user: dict, model_knowledge:
|
|||
if flat_mode:
|
||||
# Flat mode: build full tree (legitimate use)
|
||||
tree = await _build_directory_tree(kb_id)
|
||||
for f in tree['files']:
|
||||
for f in _sort_files(tree['files'], flags):
|
||||
lines.append(f' {f["id"]} {f["path"]} {_fmt_size(f)} {_fmt_date(f)}')
|
||||
lines.append('')
|
||||
continue
|
||||
|
|
@ -610,7 +610,7 @@ async def _kb_ls(args: list[str], flags: set[str], user: dict, model_knowledge:
|
|||
# Show files at this level (filter from accessible files)
|
||||
accessible = await _get_accessible_files(user, model_knowledge, knowledge_id=kb_id)
|
||||
dir_files = [f for f in accessible if f['directory_id'] == target_dir_id]
|
||||
for f in dir_files:
|
||||
for f in _sort_files(dir_files, flags):
|
||||
lines.append(f' {f["id"]} {f["filename"]} {_fmt_size(f)} {_fmt_date(f)}')
|
||||
|
||||
if not subdirs and not dir_files:
|
||||
|
|
@ -619,13 +619,24 @@ async def _kb_ls(args: list[str], flags: set[str], user: dict, model_knowledge:
|
|||
|
||||
if direct_files and not target_kb_id and not dir_path:
|
||||
lines.append('Attached Files:')
|
||||
for f in direct_files:
|
||||
for f in _sort_files(direct_files, flags):
|
||||
lines.append(f' {f["id"]} {f["filename"]} {_fmt_size(f)} {_fmt_date(f)}')
|
||||
lines.append('')
|
||||
|
||||
return '\n'.join(lines).rstrip()
|
||||
|
||||
|
||||
def _sort_files(files: list[dict], flags: set[str]) -> list[dict]:
|
||||
"""ls-style ordering: by name or path, -t newest first, -S largest first, -r reverses."""
|
||||
if 't' in flags:
|
||||
files = sorted(files, key=lambda f: f.get('updated_at') or 0, reverse=True)
|
||||
elif 'S' in flags:
|
||||
files = sorted(files, key=lambda f: f.get('size') or 0, reverse=True)
|
||||
else:
|
||||
files = sorted(files, key=lambda f: f.get('path') or f['filename'])
|
||||
return files[::-1] if 'r' in flags else files
|
||||
|
||||
|
||||
def _fmt_size(f: dict) -> str:
|
||||
return f'{f["size"]:,} bytes' if f.get('size') else ''
|
||||
|
||||
|
|
@ -893,7 +904,7 @@ async def _kb_find(args: list[str], flags: set[str], user: dict, model_knowledge
|
|||
return f'No files matching "{pattern}"{scope_str}'
|
||||
|
||||
lines = []
|
||||
for f in matched:
|
||||
for f in _sort_files(matched, flags):
|
||||
kb_info = f' ({f["knowledge_name"]})' if f.get('knowledge_name') else ''
|
||||
lines.append(f'{f["id"]} {f["filename"]}{kb_info}')
|
||||
return '\n'.join(lines)
|
||||
|
|
@ -1065,7 +1076,7 @@ async def _kb_tree(args: list[str], flags: set[str], user: dict, model_knowledge
|
|||
def _render_tree(parent_id, prefix=' '):
|
||||
items = []
|
||||
subdirs = _get_subdirs(tree, parent_id)
|
||||
files = _get_files_in_dir(tree, parent_id)
|
||||
files = _sort_files(_get_files_in_dir(tree, parent_id), flags)
|
||||
entries = [('dir', d) for d in subdirs] + [('file', f) for f in files]
|
||||
|
||||
for idx, (etype, entry) in enumerate(entries):
|
||||
|
|
@ -1090,7 +1101,7 @@ async def _kb_tree(args: list[str], flags: set[str], user: dict, model_knowledge
|
|||
|
||||
if direct_files and not dir_scope:
|
||||
output.append('Attached Files:')
|
||||
for idx, f in enumerate(direct_files):
|
||||
for idx, f in enumerate(_sort_files(direct_files, flags)):
|
||||
connector = '└── ' if idx == len(direct_files) - 1 else '├── '
|
||||
output.append(f' {connector}{f["filename"]}')
|
||||
output.append(f'\n 0 directories, {len(direct_files)} files')
|
||||
|
|
@ -1158,6 +1169,9 @@ async def kb_exec(
|
|||
ls — list root files and directories
|
||||
ls docs/ — list contents of a directory
|
||||
ls -a — flat list of all files with full paths
|
||||
ls -t — newest modified first
|
||||
ls -S — largest first
|
||||
ls -r — reverse file order
|
||||
tree — recursive directory tree view
|
||||
tree docs/ — subtree from a directory
|
||||
cat -n <file> — read file with line numbers
|
||||
|
|
@ -1172,6 +1186,7 @@ async def kb_exec(
|
|||
grep "text" *.py — filter by extension
|
||||
find "*.md" — find files by glob
|
||||
find docs/ "*.md" — find within a directory
|
||||
find -t "*.md", tree -t — same sort flags as ls
|
||||
wc <file> — line/word/char counts
|
||||
stat <file> — file metadata
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue