diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 75b30f184e..2ea869f59e 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -2844,7 +2844,6 @@ async def process_chat_payload(request, form_data, user, metadata, model): ) available_skills = [] terminal_skills = [] - view_skill_ids = [] if is_note_chat: note_id = (chat.meta or {}).get('note_id') note = await Notes.get_note_by_id(note_id) if note_id else None @@ -2931,7 +2930,6 @@ async def process_chat_payload(request, form_data, user, metadata, model): append=True, ) else: - view_skill_ids.append(skill.id) skill_manifest += ( f'\n{skill.id}\n{skill.name}\n' f'{skill.description or ""}\n\n' @@ -2973,7 +2971,6 @@ async def process_chat_payload(request, form_data, user, metadata, model): append=True, ) else: - view_skill_ids.append(sid) skill_manifest += format_terminal_skill_manifest_entry(skill) if skill_manifest: @@ -3221,7 +3218,7 @@ async def process_chat_payload(request, form_data, user, metadata, model): { **extra_params, '__event_emitter__': event_emitter, - '__skill_ids__': view_skill_ids, + '__skill_ids__': sorted(resolved_skill_ids), }, features, model, diff --git a/backend/open_webui/utils/skill_files.py b/backend/open_webui/utils/skill_files.py index 109a49e067..54b71815b0 100644 --- a/backend/open_webui/utils/skill_files.py +++ b/backend/open_webui/utils/skill_files.py @@ -162,8 +162,12 @@ def grep_skill_content(files: list[dict], pattern: str, case_insensitive: bool = } candidates = [*result['matches'], entry] candidate = { - **result, 'matches': candidates, 'truncated': True, 'notice': notice, - 'omitted_matches': [], 'omitted_matches_truncated': False, + **result, + 'matches': candidates, + 'truncated': True, + 'notice': notice, + 'omitted_matches': [], + 'omitted_matches_truncated': False, } if ( len(candidates) <= max_results @@ -175,10 +179,18 @@ def grep_skill_content(files: list[dict], pattern: str, case_insensitive: bool = continue accepting = False result.update(truncated=True, notice=notice, omitted_matches=[], omitted_matches_truncated=False) - locations = [*result['omitted_matches'], { - 'path': file['path'], 'offset': start, 'section': heading['title'] if heading else None, - }] - if len(locations) <= 30 and len(JSONCodec.dumps(locations, ensure_ascii=False)) <= SKILL_GREP_LOCATIONS_MAX_CHARS: + locations = [ + *result['omitted_matches'], + { + 'path': file['path'], + 'offset': start, + 'section': heading['title'] if heading else None, + }, + ] + if ( + len(locations) <= 30 + and len(JSONCodec.dumps(locations, ensure_ascii=False)) <= SKILL_GREP_LOCATIONS_MAX_CHARS + ): result['omitted_matches'] = locations else: accepting_locations = False @@ -225,9 +237,7 @@ def format_skill_content(page: dict, skill_id: str, tools_enabled: bool) -> str: if page['next_offset'] is not None: content += '\nSkill instructions truncated.' if tools_enabled: - content += ( - f'\nread_skill_file(id={json.dumps(skill_id)}, path="SKILL.md", offset={page["next_offset"]}).' - ) + content += f'\nread_skill_file(id={json.dumps(skill_id)}, path="SKILL.md", offset={page["next_offset"]}).' return content diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index 7e30e190f6..18184f6293 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -849,8 +849,13 @@ async def get_builtin_tools( ) # Skills tools - overview, targeted search, and bounded reads of relevant passages. + skill_ids = sorted(set(extra_params.get('__skill_ids__') or [])) + workspace_skill_ids = [skill_id for skill_id in skill_ids if not skill_id.startswith('terminal:')] if is_builtin_tool_enabled('skills'): - builtin_functions.extend([view_skill, read_skill_file, grep_skill_files, update_skill_files]) + if skill_ids: + builtin_functions.extend([view_skill, read_skill_file, grep_skill_files]) + if workspace_skill_ids: + builtin_functions.append(update_skill_files) if user.get('role') == 'admin' or await has_permission( user.get('id', ''), 'workspace.skills', await Config.get('user.permissions') ): @@ -907,6 +912,10 @@ async def get_builtin_tools( ) spec = get_builtin_tool_spec(func) + if func.__name__ in {'view_skill', 'read_skill_file', 'grep_skill_files'}: + spec['parameters']['properties']['id']['enum'] = skill_ids + elif func.__name__ == 'update_skill_files': + spec['parameters']['properties']['id']['enum'] = workspace_skill_ids if func.__name__ == 'delegate_task' and not config.get('subagents.background_enabled'): parameters = spec.get('parameters', {}) parameters.get('properties', {}).pop('background', None)