From e9f05b6cd76a53523ac98b60352e7a732b305db8 Mon Sep 17 00:00:00 2001 From: dannyl1u Date: Thu, 30 Jan 2025 20:48:06 -0800 Subject: [PATCH 01/20] feat: delete message responses --- .../components/chat/Messages/Message.svelte | 1 + .../chat/Messages/ResponseMessage.svelte | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/lib/components/chat/Messages/Message.svelte b/src/lib/components/chat/Messages/Message.svelte index 8c14fda0f..c15e7c4ec 100644 --- a/src/lib/components/chat/Messages/Message.svelte +++ b/src/lib/components/chat/Messages/Message.svelte @@ -78,6 +78,7 @@ {rateMessage} {actionMessage} {submitMessage} + {deleteMessage} {continueResponse} {regenerateResponse} {addMessages} diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte index d6b31e6a0..14d280560 100644 --- a/src/lib/components/chat/Messages/ResponseMessage.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage.svelte @@ -114,6 +114,8 @@ export let saveMessage: Function; export let rateMessage: Function; export let actionMessage: Function; + export let deleteMessage: Function; + export let submitMessage: Function; export let continueResponse: Function; @@ -461,6 +463,10 @@ feedbackLoading = false; }; + const deleteMessageHandler = async () => { + deleteMessage(message.id); + }; + $: if (!edit) { (async () => { await tick(); @@ -1102,6 +1108,35 @@ {/if} {#if isLastMessage} + {#if siblings.length > 1} + + + + {/if} - - {/if} + + {/if} + {#if isLastMessage} {#each model?.actions ?? [] as action} From af10f87075f0059ac00940bbd9c42f9d7c3971fd Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 31 Jan 2025 23:49:25 -0800 Subject: [PATCH 14/20] refac: textarea --- src/lib/components/common/Textarea.svelte | 83 +++++++++-------------- 1 file changed, 32 insertions(+), 51 deletions(-) diff --git a/src/lib/components/common/Textarea.svelte b/src/lib/components/common/Textarea.svelte index bcd5b4d75..227834d4a 100644 --- a/src/lib/components/common/Textarea.svelte +++ b/src/lib/components/common/Textarea.svelte @@ -3,68 +3,49 @@ export let value = ''; export let placeholder = ''; + export let rows = 1; + export let required = false; export let className = - 'w-full rounded-lg px-3 py-2 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none resize-none h-full'; - - export let onKeydown: Function = () => {}; + 'w-full rounded-lg px-3 py-2 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none h-full'; let textareaElement; - $: if (textareaElement) { - if (textareaElement.innerText !== value && value !== '') { - textareaElement.innerText = value ?? ''; - } - } - // Adjust height on mount and after setting the element. onMount(async () => { await tick(); + resize(); + + requestAnimationFrame(() => { + // setInterveal to cehck until textareaElement is set + const interval = setInterval(() => { + if (textareaElement) { + clearInterval(interval); + resize(); + } + }, 100); + }); }); - // Handle paste event to ensure only plaintext is pasted - function handlePaste(event: ClipboardEvent) { - event.preventDefault(); // Prevent the default paste action - const clipboardData = event.clipboardData?.getData('text/plain'); // Get plaintext from clipboard - - // Insert plaintext into the textarea - document.execCommand('insertText', false, clipboardData); - } + const resize = () => { + if (textareaElement) { + textareaElement.style.height = ''; + textareaElement.style.height = `${textareaElement.scrollHeight}px`; + } + }; -
{ - const text = textareaElement.innerText; - if (text === '\n') { - value = ''; - return; - } - - value = text; + bind:value + {placeholder} + class={className} + style="field-sizing: content;" + {rows} + {required} + on:input={(e) => { + resize(); + }} + on:focus={() => { + resize(); }} - on:paste={handlePaste} - on:keydown={onKeydown} - data-placeholder={placeholder} /> - - From 3502e09d9f66437c3ea7ecb0ebac5488820d8128 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 31 Jan 2025 23:50:58 -0800 Subject: [PATCH 15/20] fix: image prompt gen template --- backend/open_webui/routers/tasks.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/open_webui/routers/tasks.py b/backend/open_webui/routers/tasks.py index 299ec5326..f56a0232d 100644 --- a/backend/open_webui/routers/tasks.py +++ b/backend/open_webui/routers/tasks.py @@ -90,6 +90,10 @@ async def update_task_config( form_data.TITLE_GENERATION_PROMPT_TEMPLATE ) + request.app.state.config.IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE = ( + form_data.IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE + ) + request.app.state.config.ENABLE_AUTOCOMPLETE_GENERATION = ( form_data.ENABLE_AUTOCOMPLETE_GENERATION ) From 642a093d0274eb6e0c706e8eb4b04188573f7a95 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sat, 1 Feb 2025 21:01:06 -0800 Subject: [PATCH 16/20] refac: tool calls --- backend/open_webui/config.py | 23 +++- backend/open_webui/utils/middleware.py | 108 ++++++++++-------- .../admin/Settings/Interface.svelte | 17 ++- svelte.config.js | 12 +- 4 files changed, 103 insertions(+), 57 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index d60f3d436..88d38db23 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -1283,7 +1283,28 @@ TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE = PersistentConfig( ) -DEFAULT_TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE = """Available Tools: {{TOOLS}}\nReturn an empty string if no tools match the query. If a function tool matches, construct and return a JSON object in the format {\"name\": \"functionName\", \"parameters\": {\"requiredFunctionParamKey\": \"requiredFunctionParamValue\"}} using the appropriate tool and its parameters. Only return the object and limit the response to the JSON object without additional text.""" +DEFAULT_TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE = """Available Tools: {{TOOLS}} + +Your task is to choose and return the correct tool(s) from the list of available tools based on the query. Follow these guidelines: + +- Return only the JSON object, without any additional text or explanation. + +- If no tools match the query, return an empty array: + { + "tool_calls": [] + } + +- If one or more tools match the query, construct a JSON response containing a "tool_calls" array with objects that include: + - "name": The tool's name. + - "parameters": A dictionary of required parameters and their corresponding values. + +The format for the JSON response is strictly: +{ + "tool_calls": [ + {"name": "toolName1", "parameters": {"key1": "value1"}}, + {"name": "toolName2", "parameters": {"key2": "value2"}} + ] +}""" DEFAULT_EMOJI_GENERATION_PROMPT_TEMPLATE = """Your task is to reflect the speaker's likely facial expression through a fitting emoji. Interpret emotions from the message and reflect their facial expression using fitting, diverse emojis (e.g., 😊, 😢, 😡, 😱). diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index b821e11a5..5105c8bb4 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -270,60 +270,70 @@ async def chat_completion_tools_handler( result = json.loads(content) - tool_function_name = result.get("name", None) - if tool_function_name not in tools: - return body, {} + async def tool_call_handler(tool_call): + log.debug(f"{tool_call=}") - tool_function_params = result.get("parameters", {}) + tool_function_name = tool_call.get("name", None) + if tool_function_name not in tools: + return body, {} - try: - required_params = ( - tools[tool_function_name] - .get("spec", {}) - .get("parameters", {}) - .get("required", []) - ) - tool_function = tools[tool_function_name]["callable"] - tool_function_params = { - k: v - for k, v in tool_function_params.items() - if k in required_params - } - tool_output = await tool_function(**tool_function_params) + tool_function_params = tool_call.get("parameters", {}) - except Exception as e: - tool_output = str(e) - - if isinstance(tool_output, str): - if tools[tool_function_name]["citation"]: - sources.append( - { - "source": { - "name": f"TOOL:{tools[tool_function_name]['toolkit_id']}/{tool_function_name}" - }, - "document": [tool_output], - "metadata": [ - { - "source": f"TOOL:{tools[tool_function_name]['toolkit_id']}/{tool_function_name}" - } - ], - } - ) - else: - sources.append( - { - "source": {}, - "document": [tool_output], - "metadata": [ - { - "source": f"TOOL:{tools[tool_function_name]['toolkit_id']}/{tool_function_name}" - } - ], - } + try: + required_params = ( + tools[tool_function_name] + .get("spec", {}) + .get("parameters", {}) + .get("required", []) ) + tool_function = tools[tool_function_name]["callable"] + tool_function_params = { + k: v + for k, v in tool_function_params.items() + if k in required_params + } + tool_output = await tool_function(**tool_function_params) - if tools[tool_function_name]["file_handler"]: - skip_files = True + except Exception as e: + tool_output = str(e) + + if isinstance(tool_output, str): + if tools[tool_function_name]["citation"]: + sources.append( + { + "source": { + "name": f"TOOL:{tools[tool_function_name]['toolkit_id']}/{tool_function_name}" + }, + "document": [tool_output], + "metadata": [ + { + "source": f"TOOL:{tools[tool_function_name]['toolkit_id']}/{tool_function_name}" + } + ], + } + ) + else: + sources.append( + { + "source": {}, + "document": [tool_output], + "metadata": [ + { + "source": f"TOOL:{tools[tool_function_name]['toolkit_id']}/{tool_function_name}" + } + ], + } + ) + + if tools[tool_function_name]["file_handler"]: + skip_files = True + + # check if "tool_calls" in result + if result.get("tool_calls"): + for tool_call in result.get("tool_calls"): + await tool_call_handler(tool_call) + else: + await tool_call_handler(result) except Exception as e: log.exception(f"Error: {e}") diff --git a/src/lib/components/admin/Settings/Interface.svelte b/src/lib/components/admin/Settings/Interface.svelte index 055acbf80..332d02c5a 100644 --- a/src/lib/components/admin/Settings/Interface.svelte +++ b/src/lib/components/admin/Settings/Interface.svelte @@ -31,7 +31,8 @@ ENABLE_TAGS_GENERATION: true, ENABLE_SEARCH_QUERY_GENERATION: true, ENABLE_RETRIEVAL_QUERY_GENERATION: true, - QUERY_GENERATION_PROMPT_TEMPLATE: '' + QUERY_GENERATION_PROMPT_TEMPLATE: '', + TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE: '' }; let promptSuggestions = []; @@ -251,6 +252,20 @@
+
+
{$i18n.t('Tools Function Calling Prompt')}
+ + +