Skip to content

Commit 6a9d67b

Browse files
committed
refac
1 parent ebb7ce2 commit 6a9d67b

3 files changed

Lines changed: 102 additions & 6 deletions

File tree

backend/open_webui/utils/middleware.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2557,19 +2557,33 @@ async def tool_function(**kwargs):
25572557
# so system terminals work even when no other tools are selected)
25582558
if terminal_id:
25592559
try:
2560-
terminal_tools = await get_terminal_tools(
2560+
terminal_tools, system_prompt = await get_terminal_tools(
25612561
request,
25622562
terminal_id,
25632563
user,
25642564
extra_params,
25652565
)
25662566
if terminal_tools:
25672567
tools_dict = {**tools_dict, **terminal_tools}
2568+
if system_prompt:
2569+
form_data['messages'] = add_or_update_system_message(
2570+
system_prompt,
2571+
form_data['messages'],
2572+
append=True,
2573+
)
25682574
except Exception as e:
25692575
log.exception(e)
25702576

25712577
if direct_tool_servers:
25722578
for tool_server in direct_tool_servers:
2579+
system_prompt = tool_server.pop('system_prompt', None)
2580+
if system_prompt:
2581+
form_data['messages'] = add_or_update_system_message(
2582+
system_prompt,
2583+
form_data['messages'],
2584+
append=True,
2585+
)
2586+
25732587
tool_specs = tool_server.pop('specs', [])
25742588

25752589
for tool in tool_specs:

backend/open_webui/utils/tools.py

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,43 @@ async def get_terminal_cwd(
828828
return None
829829

830830

831+
async def get_terminal_system_prompt(
832+
base_url: str,
833+
headers: dict,
834+
cookies: Optional[dict] = None,
835+
) -> Optional[str]:
836+
"""Fetch the system prompt from a terminal server.
837+
838+
Checks ``/api/config`` for the ``system`` feature flag first;
839+
only fetches ``/system`` if the flag is present. Returns *None*
840+
silently when the server doesn't support the endpoint.
841+
"""
842+
base = base_url.rstrip('/')
843+
try:
844+
async with aiohttp.ClientSession(
845+
timeout=aiohttp.ClientTimeout(total=3),
846+
trust_env=True,
847+
) as session:
848+
# 1. Check feature flag
849+
async with session.get(f'{base}/api/config') as resp:
850+
if resp.status != 200:
851+
return None
852+
config = await resp.json()
853+
if not config.get('features', {}).get('system'):
854+
return None
855+
856+
# 2. Fetch system prompt
857+
async with session.get(
858+
f'{base}/system', headers=headers, cookies=cookies or {}
859+
) as resp:
860+
if resp.status == 200:
861+
data = await resp.json()
862+
return data.get('prompt')
863+
except Exception as e:
864+
log.debug(f'Failed to fetch terminal system prompt: {e}')
865+
return None
866+
867+
831868
async def set_terminal_servers(request: Request):
832869
"""Load and cache OpenAPI specs from all TERMINAL_SERVER_CONNECTIONS."""
833870
connections = request.app.state.config.TERMINAL_SERVER_CONNECTIONS or []
@@ -867,6 +904,25 @@ async def set_terminal_servers(request: Request):
867904

868905
request.app.state.TERMINAL_SERVERS = await get_tool_servers_data(server_configs)
869906

907+
# Fetch system prompts concurrently (runs at cache time, not per-request)
908+
connections_by_id = {c.get('id'): c for c in connections if c.get('id')}
909+
910+
async def _fetch_system_prompt(server):
911+
connection = connections_by_id.get(server.get('id'))
912+
if not connection:
913+
return
914+
headers = {}
915+
if connection.get('auth_type', 'bearer') == 'bearer':
916+
headers['Authorization'] = f'Bearer {connection.get("key", "")}'
917+
prompt = await get_terminal_system_prompt(server['url'], headers)
918+
if prompt:
919+
server['system_prompt'] = prompt
920+
921+
await asyncio.gather(
922+
*[_fetch_system_prompt(s) for s in request.app.state.TERMINAL_SERVERS],
923+
return_exceptions=True,
924+
)
925+
870926
if request.app.state.redis is not None:
871927
await request.app.state.redis.set('terminal_servers', json.dumps(request.app.state.TERMINAL_SERVERS))
872928

@@ -894,7 +950,7 @@ async def get_terminal_tools(
894950
terminal_id: str,
895951
user: UserModel,
896952
extra_params: dict,
897-
) -> dict[str, dict]:
953+
) -> tuple[dict[str, dict], Optional[str]]:
898954
"""Resolve tools for a terminal server identified by terminal_id.
899955
900956
- Finds the connection in TERMINAL_SERVER_CONNECTIONS
@@ -941,14 +997,14 @@ async def get_terminal_tools(
941997
headers['Authorization'] = f'Bearer {oauth_token.get("access_token", "")}'
942998
# auth_type == "none": no Authorization header
943999

1000+
system_prompt = server_data.get('system_prompt')
9441001
terminal_cwd = await get_terminal_cwd(connection.get('url', ''), headers, cookies)
9451002

9461003
tools_dict = {}
9471004
for spec in specs:
9481005
function_name = spec['name']
949-
950-
# Inject CWD into run_command description
9511006
tool_spec = clean_openai_tool_schema(spec)
1007+
9521008
if function_name == 'run_command' and terminal_cwd:
9531009
tool_spec['description'] = (
9541010
tool_spec.get('description', '') + f'\n\nThe current working directory is: {terminal_cwd}'
@@ -977,7 +1033,7 @@ async def tool_function(**kwargs):
9771033
'type': 'terminal',
9781034
}
9791035

980-
return tools_dict
1036+
return tools_dict, system_prompt
9811037

9821038

9831039
async def get_tool_server_data(url: str, headers: Optional[dict]) -> Dict[str, Any]:

src/lib/apis/index.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,12 +392,38 @@ export const getToolServersData = async (servers: object[]) => {
392392
specs: convertOpenApiToToolPayload(res)
393393
};
394394

395-
return {
395+
const result: Record<string, any> = {
396396
url: server?.url,
397397
openapi: openapi,
398398
info: info,
399399
specs: specs
400400
};
401+
402+
// Fetch system prompt if the server supports it
403+
try {
404+
const baseUrl = (server?.url ?? '').replace(/\/$/, '');
405+
const configRes = await fetch(`${baseUrl}/api/config`);
406+
if (configRes.ok) {
407+
const config = await configRes.json();
408+
if (config?.features?.system) {
409+
const headers: Record<string, string> = {};
410+
if (toolServerToken) {
411+
headers['Authorization'] = `Bearer ${toolServerToken}`;
412+
}
413+
const systemRes = await fetch(`${baseUrl}/system`, { headers });
414+
if (systemRes.ok) {
415+
const systemData = await systemRes.json();
416+
if (systemData?.prompt) {
417+
result.system_prompt = systemData.prompt;
418+
}
419+
}
420+
}
421+
}
422+
} catch (e) {
423+
// Server doesn't support /system — that's fine
424+
}
425+
426+
return result;
401427
} else if (error) {
402428
return {
403429
error,

0 commit comments

Comments
 (0)