mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-18 04:43:55 +00:00
refac
This commit is contained in:
parent
8c56361ee0
commit
92e64c1d59
8 changed files with 77 additions and 12 deletions
|
|
@ -2058,6 +2058,14 @@ ENABLE_FOLDERS = os.getenv('ENABLE_FOLDERS', 'True').lower() == 'true'
|
|||
|
||||
FOLDER_MAX_FILE_COUNT = os.getenv('FOLDER_MAX_FILE_COUNT', '')
|
||||
|
||||
SKILL_CONTENT_MAX_CHARS = int(os.environ['SKILL_CONTENT_MAX_CHARS']) if os.getenv('SKILL_CONTENT_MAX_CHARS') else None
|
||||
if SKILL_CONTENT_MAX_CHARS is not None and SKILL_CONTENT_MAX_CHARS <= 0:
|
||||
raise ValueError('SKILL_CONTENT_MAX_CHARS must be a positive integer or unset')
|
||||
|
||||
SKILL_PREVIEW_MAX_CHARS = int(os.environ['SKILL_PREVIEW_MAX_CHARS']) if os.getenv('SKILL_PREVIEW_MAX_CHARS') else None
|
||||
if SKILL_PREVIEW_MAX_CHARS is not None and SKILL_PREVIEW_MAX_CHARS <= 0:
|
||||
raise ValueError('SKILL_PREVIEW_MAX_CHARS must be a positive integer or unset')
|
||||
|
||||
ENABLE_CHANNELS = os.getenv('ENABLE_CHANNELS', 'False').lower() == 'true'
|
||||
|
||||
ENABLE_ENCRYPTED_CHANNELS = os.getenv('ENABLE_ENCRYPTED_CHANNELS', 'False').lower() == 'true'
|
||||
|
|
@ -2892,6 +2900,8 @@ ENABLE_LDAP_GROUP_CREATION = os.getenv('ENABLE_LDAP_GROUP_CREATION', 'False').lo
|
|||
LDAP_ATTRIBUTE_FOR_GROUPS = os.getenv('LDAP_ATTRIBUTE_FOR_GROUPS', 'memberOf')
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
'skills.content_max_chars': SKILL_CONTENT_MAX_CHARS,
|
||||
'skills.preview_max_chars': SKILL_PREVIEW_MAX_CHARS,
|
||||
'direct.enable': ENABLE_DIRECT_CONNECTIONS,
|
||||
'direct.integrations.enable': ENABLE_DIRECT_INTEGRATIONS,
|
||||
'ollama.enable': ENABLE_OLLAMA_API,
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ from open_webui.utils.json_codec import JSONCodec
|
|||
from open_webui.utils.mfa import MFA_CONFIG_KEYS, MfaConfigForm, get_mfa_config, limit_account, update_mfa_config
|
||||
from open_webui.utils.misc import parse_duration, validate_email_format
|
||||
from open_webui.utils.rate_limit import RateLimiter
|
||||
from pydantic import BaseModel, StrictStr, field_validator
|
||||
from pydantic import BaseModel, Field, StrictStr, field_validator
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
|
|
@ -134,6 +134,8 @@ ADMIN_CONFIG_KEYS = {
|
|||
'ENABLE_CALENDAR': 'calendar.enable',
|
||||
'ENABLE_MEMORIES': 'memories.enable',
|
||||
'ENABLE_MEMORY_SYSTEM_CONTEXT': 'memories.system_context.enable',
|
||||
'SKILL_CONTENT_MAX_CHARS': 'skills.content_max_chars',
|
||||
'SKILL_PREVIEW_MAX_CHARS': 'skills.preview_max_chars',
|
||||
'ENABLE_NOTES': 'notes.enable',
|
||||
'ENABLE_USER_WEBHOOKS': 'ui.enable_user_webhooks',
|
||||
'ENABLE_USER_STATUS': 'users.enable_status',
|
||||
|
|
@ -1185,6 +1187,8 @@ class AdminConfig(MfaConfigForm):
|
|||
ENABLE_CALENDAR: bool
|
||||
ENABLE_MEMORIES: bool
|
||||
ENABLE_MEMORY_SYSTEM_CONTEXT: bool
|
||||
SKILL_CONTENT_MAX_CHARS: int | None = Field(default=None, gt=0, strict=True)
|
||||
SKILL_PREVIEW_MAX_CHARS: int | None = Field(default=None, gt=0, strict=True)
|
||||
ENABLE_NOTES: bool
|
||||
ENABLE_USER_WEBHOOKS: bool
|
||||
ENABLE_USER_STATUS: bool
|
||||
|
|
@ -1233,6 +1237,10 @@ async def update_admin_config(request: Request, form_data: AdminConfig, user=Dep
|
|||
updates.pop('apps.enable', None)
|
||||
if 'I18N' not in form_data.model_fields_set:
|
||||
updates.pop('ui.i18n', None)
|
||||
if 'SKILL_CONTENT_MAX_CHARS' not in form_data.model_fields_set:
|
||||
updates.pop('skills.content_max_chars', None)
|
||||
if 'SKILL_PREVIEW_MAX_CHARS' not in form_data.model_fields_set:
|
||||
updates.pop('skills.preview_max_chars', None)
|
||||
updates['ui.default_interface_settings'] = form_data.DEFAULT_INTERFACE_SETTINGS or {}
|
||||
updates['folders.max_file_count'] = int(form_data.FOLDER_MAX_FILE_COUNT) if form_data.FOLDER_MAX_FILE_COUNT else ''
|
||||
updates['automations.max_count'] = int(form_data.AUTOMATION_MAX_COUNT) if form_data.AUTOMATION_MAX_COUNT else ''
|
||||
|
|
|
|||
|
|
@ -3550,7 +3550,7 @@ async def view_skill(
|
|||
{
|
||||
'id': skill.id,
|
||||
'name': snapshot['name'],
|
||||
**skill_overview(snapshot['content'], file_summaries(snapshot['data']['files'])),
|
||||
**await skill_overview(snapshot['content'], file_summaries(snapshot['data']['files'])),
|
||||
},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
|
@ -3573,7 +3573,7 @@ async def read_skill_file(
|
|||
:param id: Skill ID.
|
||||
:param path: Relative path within the skill.
|
||||
:param offset: Character offset for paging text.
|
||||
:param max_chars: Maximum characters to return, up to 20000 (default 10000).
|
||||
:param max_chars: Maximum characters to return, capped by the admin skill content limit (default 10000).
|
||||
"""
|
||||
try:
|
||||
from types import SimpleNamespace
|
||||
|
|
@ -3627,7 +3627,7 @@ async def read_skill_file(
|
|||
return JSONCodec.dumps(
|
||||
{
|
||||
'path': path,
|
||||
**skill_content_page(file['content'], offset, max_chars),
|
||||
**await skill_content_page(file['content'], offset, max_chars),
|
||||
},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2531,7 +2531,7 @@ async def process_chat_payload(request, form_data, user, metadata, model):
|
|||
continue
|
||||
skill_versions[skill.id] = version_id
|
||||
skill_tools_enabled = use_builtin_tools and model_builtin_tools.get('skills', True)
|
||||
overview = skill_overview(
|
||||
overview = await skill_overview(
|
||||
snapshot['content'],
|
||||
[f['path'] for f in snapshot['data']['files'] if f['path'] != 'SKILL.md'],
|
||||
tools_enabled=skill_tools_enabled,
|
||||
|
|
|
|||
|
|
@ -31,9 +31,15 @@ SKILL_GREP_EXCERPT_MAX_CHARS = 1_200
|
|||
SKILL_GREP_LOCATIONS_MAX_CHARS = 2_000
|
||||
|
||||
|
||||
def skill_content_page(content: str, offset: int = 0, max_chars: int = SKILL_CONTENT_MAX_CHARS) -> dict:
|
||||
async def skill_content_page(
|
||||
content: str, offset: int = 0, max_chars: int = SKILL_CONTENT_MAX_CHARS, *, limit: int | None = None
|
||||
) -> dict:
|
||||
if limit is None:
|
||||
from open_webui.models.config import Config
|
||||
|
||||
limit = await Config.get('skills.content_max_chars') or SKILL_CONTENT_MAX_CHARS
|
||||
offset = max(0, offset)
|
||||
end = offset + min(SKILL_CONTENT_MAX_CHARS, max(1, max_chars))
|
||||
end = offset + min(limit, max(1, max_chars))
|
||||
return {'content': content[offset:end], 'next_offset': end if end < len(content) else None}
|
||||
|
||||
|
||||
|
|
@ -196,16 +202,21 @@ def grep_skill_content(files: list[dict], pattern: str, case_insensitive: bool =
|
|||
return serialized
|
||||
|
||||
|
||||
def skill_overview(content: str, files: list, field: str = 'files', tools_enabled: bool = True) -> dict:
|
||||
async def skill_overview(content: str, files: list, field: str = 'files', tools_enabled: bool = True) -> dict:
|
||||
if not tools_enabled:
|
||||
return {
|
||||
'content': content[:SKILL_INJECTION_MAX_CHARS],
|
||||
'next_offset': SKILL_INJECTION_MAX_CHARS if len(content) > SKILL_INJECTION_MAX_CHARS else None,
|
||||
**bounded_skill_manifest(files, field),
|
||||
}
|
||||
large = len(content) > SKILL_CONTENT_MAX_CHARS
|
||||
from open_webui.models.config import Config
|
||||
|
||||
limits = await Config.get_many('skills.content_max_chars', 'skills.preview_max_chars')
|
||||
content_limit = limits.get('skills.content_max_chars') or SKILL_CONTENT_MAX_CHARS
|
||||
preview_limit = limits.get('skills.preview_max_chars') or SKILL_PREVIEW_MAX_CHARS
|
||||
large = len(content) > content_limit
|
||||
return {
|
||||
**skill_content_page(content, max_chars=SKILL_PREVIEW_MAX_CHARS if large else SKILL_CONTENT_MAX_CHARS),
|
||||
**await skill_content_page(content, max_chars=preview_limit if large else content_limit, limit=content_limit),
|
||||
**bounded_skill_manifest(files, field, SKILL_OVERVIEW_LIST_MAX_CHARS),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -287,10 +287,10 @@ async def get_terminal_skill(
|
|||
'description': skill.get('description'),
|
||||
'directory': directory,
|
||||
**(
|
||||
skill_overview(skill['content'], resources, 'resources', tools_enabled)
|
||||
await skill_overview(skill['content'], resources, 'resources', tools_enabled)
|
||||
if overview
|
||||
else {
|
||||
**skill_content_page(skill['content'], offset, max_chars),
|
||||
**await skill_content_page(skill['content'], offset, max_chars),
|
||||
**bounded_skill_manifest(resources, 'resources'),
|
||||
}
|
||||
),
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@
|
|||
const cleaned = entriesToI18n(uiI18nEntries);
|
||||
const res = await updateAdminConfig(localStorage.token, {
|
||||
...adminConfig,
|
||||
SKILL_CONTENT_MAX_CHARS: adminConfig.SKILL_CONTENT_MAX_CHARS ?? null,
|
||||
SKILL_PREVIEW_MAX_CHARS: adminConfig.SKILL_PREVIEW_MAX_CHARS ?? null,
|
||||
DEFAULT_INTERFACE_SETTINGS: defaultInterfaceSettings,
|
||||
I18N: cleaned
|
||||
});
|
||||
|
|
@ -337,6 +339,36 @@
|
|||
/>
|
||||
</AdminSettingRow>
|
||||
{/if}
|
||||
<AdminSettingField
|
||||
forId="skill-content-max-chars"
|
||||
label={$i18n.t('settings.admin.general.skillContentMaxChars.label')}
|
||||
description={$i18n.t('settings.admin.general.skillContentMaxChars.description')}
|
||||
>
|
||||
<input
|
||||
id="skill-content-max-chars"
|
||||
class={inputClass}
|
||||
type="number"
|
||||
min="1"
|
||||
step="1"
|
||||
placeholder={$i18n.t('Default')}
|
||||
bind:value={adminConfig.SKILL_CONTENT_MAX_CHARS}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField
|
||||
forId="skill-preview-max-chars"
|
||||
label={$i18n.t('settings.admin.general.skillPreviewMaxChars.label')}
|
||||
description={$i18n.t('settings.admin.general.skillPreviewMaxChars.description')}
|
||||
>
|
||||
<input
|
||||
id="skill-preview-max-chars"
|
||||
class={inputClass}
|
||||
type="number"
|
||||
min="1"
|
||||
step="1"
|
||||
placeholder={$i18n.t('Default')}
|
||||
bind:value={adminConfig.SKILL_PREVIEW_MAX_CHARS}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('settings.admin.general.notes.label')}
|
||||
description={$i18n.t('settings.admin.general.notes.description')}
|
||||
|
|
|
|||
|
|
@ -2761,6 +2761,10 @@
|
|||
"settings.admin.general.modelResponseMode.description": "Choose where model responses to root-level channel mentions are posted.",
|
||||
"settings.admin.general.modelResponseMode.label": "Model Response Mode",
|
||||
"settings.admin.general.notes.description": "Allow users to create and manage notes.",
|
||||
"settings.admin.general.skillContentMaxChars.label": "Skill Read Limit",
|
||||
"settings.admin.general.skillContentMaxChars.description": "Maximum characters per skill read. Longer instructions open as a preview. Leave empty to use the default.",
|
||||
"settings.admin.general.skillPreviewMaxChars.label": "Skill Preview Length",
|
||||
"settings.admin.general.skillPreviewMaxChars.description": "Characters shown when instructions exceed the read limit. Cannot exceed that limit. Leave empty to use the default.",
|
||||
"settings.admin.general.notes.label": "Notes",
|
||||
"settings.admin.general.responseWatermark.description": "Append a watermark to assistant responses when configured.",
|
||||
"settings.admin.general.responseWatermark.label": "Response Watermark",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue