mirror of
https://github.com/open-webui/open-webui.git
synced 2026-08-28 08:25:01 -06:00
refac
This commit is contained in:
@@ -105,6 +105,10 @@ async def proxy_terminal(
|
||||
target_url += f'?{request.query_params}'
|
||||
|
||||
headers = {'X-User-Id': user.id}
|
||||
# Forward per-session cwd tracking header
|
||||
session_id = request.headers.get('x-session-id')
|
||||
if session_id:
|
||||
headers['X-Session-Id'] = session_id
|
||||
cookies = {}
|
||||
auth_type = connection.get('auth_type', 'bearer')
|
||||
|
||||
|
||||
@@ -1009,6 +1009,13 @@ async def get_terminal_tools(
|
||||
# auth_type == "none": no Authorization header
|
||||
|
||||
system_prompt = server_data.get('system_prompt')
|
||||
|
||||
# Use chat_id as the per-session key for cwd tracking
|
||||
metadata = extra_params.get('__metadata__', {})
|
||||
session_id = metadata.get('chat_id')
|
||||
if session_id:
|
||||
headers['X-Session-Id'] = session_id
|
||||
|
||||
terminal_cwd = await get_terminal_cwd(connection.get('url', ''), headers, cookies)
|
||||
|
||||
tools_dict = {}
|
||||
|
||||
@@ -454,7 +454,8 @@ export const executeToolServer = async (
|
||||
url: string,
|
||||
name: string,
|
||||
params: Record<string, any>,
|
||||
serverData: { openapi: any; info: any; specs: any }
|
||||
serverData: { openapi: any; info: any; specs: any },
|
||||
sessionId?: string
|
||||
) => {
|
||||
let error = null;
|
||||
|
||||
@@ -531,6 +532,7 @@ export const executeToolServer = async (
|
||||
'Content-Type': 'application/json',
|
||||
...(token && { authorization: `Bearer ${token}` })
|
||||
};
|
||||
if (sessionId) headers['X-Session-Id'] = sessionId;
|
||||
|
||||
const requestOptions: RequestInit = {
|
||||
method: httpMethod.toUpperCase(),
|
||||
|
||||
@@ -45,11 +45,11 @@ export const getTerminalConfig = async (
|
||||
return res.json().catch(() => null);
|
||||
};
|
||||
|
||||
export const getCwd = async (baseUrl: string, apiKey: string): Promise<string | null> => {
|
||||
export const getCwd = async (baseUrl: string, apiKey: string, sessionId?: string): Promise<string | null> => {
|
||||
const url = `${baseUrl.replace(/\/$/, '')}/files/cwd`;
|
||||
const res = await fetch(url, {
|
||||
headers: { Authorization: `Bearer ${apiKey}` }
|
||||
}).catch(() => null);
|
||||
const headers: Record<string, string> = { Authorization: `Bearer ${apiKey}` };
|
||||
if (sessionId) headers['X-Session-Id'] = sessionId;
|
||||
const res = await fetch(url, { headers }).catch(() => null);
|
||||
if (!res || !res.ok) return null;
|
||||
const json = await res.json().catch(() => null);
|
||||
return json?.cwd ?? null;
|
||||
@@ -218,15 +218,18 @@ export const deleteEntry = async (
|
||||
export const setCwd = async (
|
||||
baseUrl: string,
|
||||
apiKey: string,
|
||||
path: string
|
||||
path: string,
|
||||
sessionId?: string
|
||||
): Promise<{ cwd: string } | null> => {
|
||||
const url = `${baseUrl.replace(/\/$/, '')}/files/cwd`;
|
||||
const headers: Record<string, string> = {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
if (sessionId) headers['X-Session-Id'] = sessionId;
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
headers,
|
||||
body: JSON.stringify({ path })
|
||||
})
|
||||
.then(async (res) => {
|
||||
|
||||
@@ -362,7 +362,7 @@
|
||||
onClose={() => showControls.set(false)}
|
||||
/>
|
||||
{:else if activeTab === 'files' && $selectedTerminalId}
|
||||
<FileNav onAttach={handleTerminalAttach} />
|
||||
<FileNav onAttach={handleTerminalAttach} {chatId} />
|
||||
{:else if activeTab === 'files' && codeInterpreterEnabled}
|
||||
<PyodideFileNav />
|
||||
{:else}
|
||||
@@ -513,7 +513,7 @@
|
||||
onClose={() => showControls.set(false)}
|
||||
/>
|
||||
{:else if activeTab === 'files' && $selectedTerminalId}
|
||||
<FileNav onAttach={handleTerminalAttach} overlay={dragged} />
|
||||
<FileNav onAttach={handleTerminalAttach} overlay={dragged} {chatId} />
|
||||
{:else if activeTab === 'files' && codeInterpreterEnabled}
|
||||
<PyodideFileNav overlay={dragged} />
|
||||
{:else}
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
|
||||
export let onAttach: ((blob: Blob, name: string, contentType: string) => void) | null = null;
|
||||
export let overlay = false;
|
||||
export let chatId: string | null = null;
|
||||
|
||||
// ── Terminal panel state ────────────────────────────────────────────
|
||||
let terminalExpanded = false;
|
||||
@@ -215,30 +216,48 @@
|
||||
return url ? { url, key } : null;
|
||||
};
|
||||
|
||||
// Detect terminal changes — the explicit store references ensure
|
||||
// Detect terminal or chat changes — the explicit store references ensure
|
||||
// Svelte re-runs this block when any of them update.
|
||||
// The `mounted` flag prevents the initial run from racing with onMount.
|
||||
let prevTerminalUrl = '';
|
||||
let prevChatId = chatId;
|
||||
let mounted = false;
|
||||
$: {
|
||||
($selectedTerminalId, $terminalServers, $settings);
|
||||
const terminal = getTerminal();
|
||||
selectedTerminal = terminal;
|
||||
|
||||
if (terminal && terminal.url !== prevTerminalUrl) {
|
||||
prevTerminalUrl = terminal.url;
|
||||
loading = true;
|
||||
error = null;
|
||||
entries = [];
|
||||
(async () => {
|
||||
// Discover server features (terminal enabled/disabled)
|
||||
const config = await getTerminalConfig(terminal.url, terminal.key);
|
||||
terminalEnabled = config?.features?.terminal !== false;
|
||||
const chatChanged = chatId !== prevChatId;
|
||||
const oldChatId = prevChatId;
|
||||
if (chatChanged) prevChatId = chatId;
|
||||
|
||||
const rawCwd = await getCwd(terminal.url, terminal.key);
|
||||
const cwd = rawCwd ? normalizePath(rawCwd) : null;
|
||||
const dir = cwd ? (cwd.endsWith('/') ? cwd : cwd + '/') : '/';
|
||||
savedPath = dir;
|
||||
loadDir(dir);
|
||||
})();
|
||||
const terminalChanged = terminal && terminal.url !== prevTerminalUrl;
|
||||
if (terminalChanged) prevTerminalUrl = terminal.url;
|
||||
|
||||
if (mounted && terminal) {
|
||||
if (chatChanged && chatId && !oldChatId) {
|
||||
// Chat just got created (null → real ID): persist the current
|
||||
// browsed path as the new session's cwd — don't re-fetch.
|
||||
setCwd(terminal.url, terminal.key, savedPath, chatId);
|
||||
} else if (terminalChanged || chatChanged) {
|
||||
// Terminal switched, new chat started, or switched between
|
||||
// existing chats — re-fetch the session cwd.
|
||||
loading = true;
|
||||
error = null;
|
||||
entries = [];
|
||||
(async () => {
|
||||
if (terminalChanged) {
|
||||
const config = await getTerminalConfig(terminal.url, terminal.key);
|
||||
terminalEnabled = config?.features?.terminal !== false;
|
||||
}
|
||||
|
||||
const rawCwd = await getCwd(terminal.url, terminal.key, chatId ?? undefined);
|
||||
const cwd = rawCwd ? normalizePath(rawCwd) : null;
|
||||
const dir = cwd ? (cwd.endsWith('/') ? cwd : cwd + '/') : '/';
|
||||
savedPath = dir;
|
||||
loadDir(dir);
|
||||
})();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,7 +335,7 @@
|
||||
loading = false;
|
||||
|
||||
// Set working directory on the terminal server (fire-and-forget)
|
||||
setCwd(terminal.url, terminal.key, path);
|
||||
setCwd(terminal.url, terminal.key, path, chatId ?? undefined);
|
||||
|
||||
if (result === null) {
|
||||
error =
|
||||
@@ -736,14 +755,22 @@
|
||||
|
||||
if (!handledDisplayFile) {
|
||||
loading = true;
|
||||
if (savedPath === '/') {
|
||||
const rawCwd = await getCwd(terminal.url, terminal.key);
|
||||
|
||||
// Discover server features on initial mount
|
||||
const config = await getTerminalConfig(terminal.url, terminal.key);
|
||||
terminalEnabled = config?.features?.terminal !== false;
|
||||
|
||||
if (chatId || savedPath === '/') {
|
||||
// Fetch session-specific cwd from the server (or global default for new chats)
|
||||
const rawCwd = await getCwd(terminal.url, terminal.key, chatId ?? undefined);
|
||||
const cwd = rawCwd ? normalizePath(rawCwd) : null;
|
||||
if (cwd) savedPath = cwd.endsWith('/') ? cwd : cwd + '/';
|
||||
}
|
||||
loadDir(savedPath);
|
||||
}
|
||||
|
||||
mounted = true;
|
||||
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Shift') shiftKey = true;
|
||||
};
|
||||
@@ -1368,6 +1395,7 @@
|
||||
overlay={overlay || isDraggingHandle}
|
||||
bind:connected={terminalConnected}
|
||||
bind:connecting={terminalConnecting}
|
||||
{chatId}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let overlay = false;
|
||||
export let chatId: string | null = null;
|
||||
|
||||
let terminalEl: HTMLDivElement;
|
||||
let term: Terminal | null = null;
|
||||
@@ -67,9 +68,11 @@
|
||||
authToken = apiKey;
|
||||
|
||||
// Create session
|
||||
const createHeaders: Record<string, string> = { Authorization: `Bearer ${apiKey}` };
|
||||
if (chatId) createHeaders['X-Session-Id'] = chatId;
|
||||
const res = await fetch(`${base}/api/terminals`, {
|
||||
method: 'POST',
|
||||
headers: { Authorization: `Bearer ${apiKey}` }
|
||||
headers: createHeaders
|
||||
});
|
||||
if (!res.ok) throw new Error(`Failed to create session: ${res.status}`);
|
||||
const session = await res.json();
|
||||
@@ -83,9 +86,11 @@
|
||||
authToken = token;
|
||||
|
||||
// Create session via proxy
|
||||
const proxyHeaders: Record<string, string> = { Authorization: `Bearer ${token}` };
|
||||
if (chatId) proxyHeaders['X-Session-Id'] = chatId;
|
||||
const res = await fetch(`${base}/terminals/${info.serverId}/api/terminals`, {
|
||||
method: 'POST',
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
headers: proxyHeaders
|
||||
});
|
||||
if (!res.ok) throw new Error(`Failed to create session: ${res.status}`);
|
||||
const session = await res.json();
|
||||
|
||||
@@ -375,7 +375,7 @@
|
||||
return { toolServer, toolServerData, token };
|
||||
};
|
||||
|
||||
const executeTool = async (data, cb) => {
|
||||
const executeTool = async (data, cb, chatId) => {
|
||||
const { toolServer, toolServerData, token } = resolveToolServer(data.server?.url);
|
||||
|
||||
console.log('executeTool', data, toolServer);
|
||||
@@ -386,7 +386,8 @@
|
||||
toolServer.url,
|
||||
data?.name,
|
||||
data?.params,
|
||||
toolServerData
|
||||
toolServerData,
|
||||
chatId
|
||||
);
|
||||
|
||||
console.log('executeToolServer', res);
|
||||
@@ -485,7 +486,7 @@
|
||||
executePythonAsWorker(data.id, data.code, cb, data.files || []);
|
||||
} else if (type === 'execute:tool') {
|
||||
console.log('execute:tool', data);
|
||||
executeTool(data, cb);
|
||||
executeTool(data, cb, event.chat_id);
|
||||
} else if (type === 'request:chat:completion') {
|
||||
console.log(data, $socket.id);
|
||||
const { session_id, channel, form_data, model } = data;
|
||||
|
||||
Reference in New Issue
Block a user