From 6d91a168beb849a1f7cdbca92884a6d96285709b Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 22 Oct 2025 16:56:02 -0500 Subject: [PATCH 01/10] refine llm configs --- src/lib/helpers/enums.js | 30 ++- src/lib/helpers/types/agentTypes.js | 4 + src/lib/helpers/types/commonTypes.js | 11 +- src/lib/scss/custom/pages/_agent.scss | 7 + src/lib/services/llm-provider-service.js | 15 +- .../agent-components/agent-llm-config.svelte | 209 +++-------------- .../audio-transcription-config.svelte | 119 ++++++++++ .../llm-configs/chat-config.svelte | 217 ++++++++++++++++++ .../llm-configs/image-edit-config.svelte | 119 ++++++++++ .../image-generation-config.svelte | 119 ++++++++++ .../llm-configs/realtime-config.svelte | 119 ++++++++++ .../page/agent/[agentId]/agent-tabs.svelte | 2 +- .../page/instruction/testing/+page.svelte | 2 +- 13 files changed, 782 insertions(+), 191 deletions(-) create mode 100644 src/routes/page/agent/[agentId]/agent-components/llm-configs/audio-transcription-config.svelte create mode 100644 src/routes/page/agent/[agentId]/agent-components/llm-configs/chat-config.svelte create mode 100644 src/routes/page/agent/[agentId]/agent-components/llm-configs/image-edit-config.svelte create mode 100644 src/routes/page/agent/[agentId]/agent-components/llm-configs/image-generation-config.svelte create mode 100644 src/routes/page/agent/[agentId]/agent-components/llm-configs/realtime-config.svelte diff --git a/src/lib/helpers/enums.js b/src/lib/helpers/enums.js index d3b1ef6a..71580fbe 100644 --- a/src/lib/helpers/enums.js +++ b/src/lib/helpers/enums.js @@ -203,14 +203,34 @@ const globalEvent = { export const GlobalEvent = Object.freeze(globalEvent); const llmModelType = { - Text: 1, - Chat: 2, - Image: 3, - Embedding: 4, - Audio: 5 + All: "All", + Text: "Text", + Chat: "Chat", + Image: "Image", + Embedding: "Embedding", + Audio: "Audio", + Realtime: "Realtime", + Web: "Web" }; export const LlmModelType = Object.freeze(llmModelType); +const llmModelCapability = { + All: "All", + Text: "Text", + Chat: "Chat", + ImageReading: "ImageReading", + ImageGeneration: "ImageGeneration", + ImageEdit: "ImageEdit", + ImageVariation: "ImageVariation", + Embedding: "Embedding", + AudioTranscription: "AudioTranscription", + AudioGeneration: "AudioGeneration", + Realtime: "Realtime", + WebSearch: "WebSearch", + PdfReading: "PdfReading" +}; +export const LlmModelCapability = Object.freeze(llmModelCapability); + const reasoningEffortLevel = { Minimal: "minimal", Low: "low", diff --git a/src/lib/helpers/types/agentTypes.js b/src/lib/helpers/types/agentTypes.js index 8046926b..34c4bbb4 100644 --- a/src/lib/helpers/types/agentTypes.js +++ b/src/lib/helpers/types/agentTypes.js @@ -18,6 +18,10 @@ * @property {number} max_recursion_depth * @property {number?} [max_output_tokens] * @property {string?} [reasoning_effort_level] + * @property {any} [image_generation] + * @property {any} [image_edit] + * @property {any} [audio_transcription] + * @property {any} [realtime] */ diff --git a/src/lib/helpers/types/commonTypes.js b/src/lib/helpers/types/commonTypes.js index 038079b5..a97df90b 100644 --- a/src/lib/helpers/types/commonTypes.js +++ b/src/lib/helpers/types/commonTypes.js @@ -39,10 +39,13 @@ */ /** - * @typedef {Object} LlmConfigOption - * @property {number?} [type] + * @typedef {Object} LlmConfigFilter + * @property {string[]?} [providers] + * @property {string[]?} [modelIds] + * @property {string[]?} [modelNames] + * @property {string[]?} [modelTypes] + * @property {string[]?} [modelCapabilities] * @property {boolean?} [multiModal] - * @property {boolean?} [imageGeneration] */ /** @@ -55,6 +58,8 @@ * @typedef {Object} LlmModelSetting * @property {string} name * @property {string} type + * @property {string[]} capabilities + * @property {boolean} multiModal * @property {any} reasoning */ diff --git a/src/lib/scss/custom/pages/_agent.scss b/src/lib/scss/custom/pages/_agent.scss index 15ef802d..f0b4fe0a 100644 --- a/src/lib/scss/custom/pages/_agent.scss +++ b/src/lib/scss/custom/pages/_agent.scss @@ -143,6 +143,12 @@ } } +.agent-config-container { + padding: 10px; + border: 1px dashed var(--bs-primary); + border-radius: 5px; +} + .agent-utility-container { display: flex; flex-direction: column; @@ -150,6 +156,7 @@ max-height: 500px; overflow-y: auto; scrollbar-width: thin; + padding: 0px 10px; .merge-utility { display: flex; diff --git a/src/lib/services/llm-provider-service.js b/src/lib/services/llm-provider-service.js index 7d15c608..5dbb6749 100644 --- a/src/lib/services/llm-provider-service.js +++ b/src/lib/services/llm-provider-service.js @@ -1,6 +1,7 @@ import { endpoints } from './api-endpoints.js'; import { replaceUrl } from '$lib/helpers/http.js'; import axios from 'axios'; +import qs from 'qs'; /** * Get provider list @@ -26,19 +27,15 @@ export async function getLlmProviderModels(provider) { /** * Get llm configs - * @param {import('$commonTypes').LlmConfigOption?} [options] + * @param {import('$commonTypes').LlmConfigFilter?} [filter] * @returns {Promise} */ -export async function getLlmConfigs(options = null) { +export async function getLlmConfigs(filter = null) { const url = endpoints.llmConfigsUrl; + const params = filter != null ? { filter: filter } : null; const response = await axios.get(url, { - params: { - options: options - }, - paramsSerializer: { - dots: true, - indexes: null, - } + params: params, + paramsSerializer: (params) => qs.stringify(params, { encode: false, allowDots: true, arrayFormat: "indices" }) }); return response.data; } \ No newline at end of file diff --git a/src/routes/page/agent/[agentId]/agent-components/agent-llm-config.svelte b/src/routes/page/agent/[agentId]/agent-components/agent-llm-config.svelte index 439a683e..a68e90cb 100644 --- a/src/routes/page/agent/[agentId]/agent-components/agent-llm-config.svelte +++ b/src/routes/page/agent/[agentId]/agent-components/agent-llm-config.svelte @@ -1,9 +1,12 @@
-
LLM Config
+
LLM Configurations
- {#if agent.llm_config?.is_inherit} - Inherited - {/if} -
- -
- -
- changeProvider(e)}> - {#each providers as option} - - {/each} - -
-
- -
- -
- changeModel(e)}> - {#each models as option} - - {/each} - -
-
- -
- -
- validateIntegerInput(e)} - on:change={e => changeMaxRecursiveDepth(e)} - /> -
-
- -
- -
- validateIntegerInput(e)} - on:change={e => changeMaxOutputToken(e)} - /> -
- {#if isReasoningModel} -
- -
- changeReasoningEffortLevel(e)}> - {#each reasonLevelOptions as option} - - {/each} - -
+
+ + + + +
- {/if} \ No newline at end of file diff --git a/src/routes/page/agent/[agentId]/agent-components/llm-configs/audio-transcription-config.svelte b/src/routes/page/agent/[agentId]/agent-components/llm-configs/audio-transcription-config.svelte new file mode 100644 index 00000000..81690a09 --- /dev/null +++ b/src/routes/page/agent/[agentId]/agent-components/llm-configs/audio-transcription-config.svelte @@ -0,0 +1,119 @@ + + +
+
+
Audio Transcription
+
+ +
+ +
+ changeProvider(e)}> + {#each providers as option} + + {/each} + +
+
+ +
+ +
+ changeModel(e)}> + {#each models as option} + + {/each} + +
+
+
\ No newline at end of file diff --git a/src/routes/page/agent/[agentId]/agent-components/llm-configs/chat-config.svelte b/src/routes/page/agent/[agentId]/agent-components/llm-configs/chat-config.svelte new file mode 100644 index 00000000..866d44b8 --- /dev/null +++ b/src/routes/page/agent/[agentId]/agent-components/llm-configs/chat-config.svelte @@ -0,0 +1,217 @@ + + +
+
+
Chat
+ {#if agent.llm_config?.is_inherit} +
+ Inherited +
+ {/if} +
+ +
+ +
+ changeProvider(e)}> + {#each providers as option} + + {/each} + +
+
+ +
+ +
+ changeModel(e)}> + {#each models as option} + + {/each} + +
+
+ +
+ +
+ validateIntegerInput(e)} + on:change={e => changeMaxRecursiveDepth(e)} + /> +
+
+ +
+ +
+ validateIntegerInput(e)} + on:change={e => changeMaxOutputToken(e)} + /> +
+
+ + {#if isReasoningModel} +
+ +
+ changeReasoningEffortLevel(e)}> + {#each reasonLevelOptions as option} + + {/each} + +
+
+ {/if} +
\ No newline at end of file diff --git a/src/routes/page/agent/[agentId]/agent-components/llm-configs/image-edit-config.svelte b/src/routes/page/agent/[agentId]/agent-components/llm-configs/image-edit-config.svelte new file mode 100644 index 00000000..58960330 --- /dev/null +++ b/src/routes/page/agent/[agentId]/agent-components/llm-configs/image-edit-config.svelte @@ -0,0 +1,119 @@ + + +
+
+
Image Edit
+
+ +
+ +
+ changeProvider(e)}> + {#each providers as option} + + {/each} + +
+
+ +
+ +
+ changeModel(e)}> + {#each models as option} + + {/each} + +
+
+
\ No newline at end of file diff --git a/src/routes/page/agent/[agentId]/agent-components/llm-configs/image-generation-config.svelte b/src/routes/page/agent/[agentId]/agent-components/llm-configs/image-generation-config.svelte new file mode 100644 index 00000000..1daeaf43 --- /dev/null +++ b/src/routes/page/agent/[agentId]/agent-components/llm-configs/image-generation-config.svelte @@ -0,0 +1,119 @@ + + +
+
+
Image Generation
+
+ +
+ +
+ changeProvider(e)}> + {#each providers as option} + + {/each} + +
+
+ +
+ +
+ changeModel(e)}> + {#each models as option} + + {/each} + +
+
+
\ No newline at end of file diff --git a/src/routes/page/agent/[agentId]/agent-components/llm-configs/realtime-config.svelte b/src/routes/page/agent/[agentId]/agent-components/llm-configs/realtime-config.svelte new file mode 100644 index 00000000..72d820cf --- /dev/null +++ b/src/routes/page/agent/[agentId]/agent-components/llm-configs/realtime-config.svelte @@ -0,0 +1,119 @@ + + +
+
+
Realtime
+
+ +
+ +
+ changeProvider(e)}> + {#each providers as option} + + {/each} + +
+
+ +
+ +
+ changeModel(e)}> + {#each models as option} + + {/each} + +
+
+
\ No newline at end of file diff --git a/src/routes/page/agent/[agentId]/agent-tabs.svelte b/src/routes/page/agent/[agentId]/agent-tabs.svelte index 25a9fa2c..ac799eb9 100644 --- a/src/routes/page/agent/[agentId]/agent-tabs.svelte +++ b/src/routes/page/agent/[agentId]/agent-tabs.svelte @@ -48,7 +48,7 @@ /** @type {any[]}*/ let tabs = [ - { name: 'agent-llm-config', displayText: 'LLm Config' }, + { name: 'agent-llm-config', displayText: 'LLm Configs' }, { name: 'agent-routing-rule', displayText: 'Routing' }, { name: 'agent-utility', displayText: 'Utilities' }, { name: 'agent-knowledgebase', displayText: 'Knowledge Base' }, diff --git a/src/routes/page/instruction/testing/+page.svelte b/src/routes/page/instruction/testing/+page.svelte index eb43dec3..66f287b2 100644 --- a/src/routes/page/instruction/testing/+page.svelte +++ b/src/routes/page/instruction/testing/+page.svelte @@ -72,7 +72,7 @@ isLoading = true; const pagedAgents = await getAgents({ pager: { page: 1, size: 1000, count: 0 } }); agents = pagedAgents.items || []; - llmConfigs = await getLlmConfigs({ type: LlmModelType.Chat }); + llmConfigs = await getLlmConfigs({ modelTypes: [LlmModelType.Chat] }); } catch { agents = []; } finally { From 49c0ac547258e0d7b99dd4a12ed691b70af42dc1 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Thu, 23 Oct 2025 19:46:29 -0500 Subject: [PATCH 02/10] clean code --- .../llm-configs/audio-transcription-config.svelte | 3 --- .../agent-components/llm-configs/image-edit-config.svelte | 3 --- .../llm-configs/image-generation-config.svelte | 3 --- .../agent-components/llm-configs/realtime-config.svelte | 3 --- 4 files changed, 12 deletions(-) diff --git a/src/routes/page/agent/[agentId]/agent-components/llm-configs/audio-transcription-config.svelte b/src/routes/page/agent/[agentId]/agent-components/llm-configs/audio-transcription-config.svelte index 81690a09..986a58a3 100644 --- a/src/routes/page/agent/[agentId]/agent-components/llm-configs/audio-transcription-config.svelte +++ b/src/routes/page/agent/[agentId]/agent-components/llm-configs/audio-transcription-config.svelte @@ -63,12 +63,10 @@ if (!!!provider) { models = []; config.model = null; - config.reasoning_effort_level = null; handleAgentChange(); return; } - config.is_inherit = false; models = getLlmModels(provider); config.model = models[0]?.name; handleAgentChange(); @@ -76,7 +74,6 @@ /** @param {any} e */ function changeModel(e) { - config.is_inherit = false; config.model = e.target.value || null; handleAgentChange(); } diff --git a/src/routes/page/agent/[agentId]/agent-components/llm-configs/image-edit-config.svelte b/src/routes/page/agent/[agentId]/agent-components/llm-configs/image-edit-config.svelte index 58960330..bbcbe195 100644 --- a/src/routes/page/agent/[agentId]/agent-components/llm-configs/image-edit-config.svelte +++ b/src/routes/page/agent/[agentId]/agent-components/llm-configs/image-edit-config.svelte @@ -63,12 +63,10 @@ if (!!!provider) { models = []; config.model = null; - config.reasoning_effort_level = null; handleAgentChange(); return; } - config.is_inherit = false; models = getLlmModels(provider); config.model = models[0]?.name; handleAgentChange(); @@ -76,7 +74,6 @@ /** @param {any} e */ function changeModel(e) { - config.is_inherit = false; config.model = e.target.value || null; handleAgentChange(); } diff --git a/src/routes/page/agent/[agentId]/agent-components/llm-configs/image-generation-config.svelte b/src/routes/page/agent/[agentId]/agent-components/llm-configs/image-generation-config.svelte index 1daeaf43..f11f9656 100644 --- a/src/routes/page/agent/[agentId]/agent-components/llm-configs/image-generation-config.svelte +++ b/src/routes/page/agent/[agentId]/agent-components/llm-configs/image-generation-config.svelte @@ -63,12 +63,10 @@ if (!!!provider) { models = []; config.model = null; - config.reasoning_effort_level = null; handleAgentChange(); return; } - config.is_inherit = false; models = getLlmModels(provider); config.model = models[0]?.name; handleAgentChange(); @@ -76,7 +74,6 @@ /** @param {any} e */ function changeModel(e) { - config.is_inherit = false; config.model = e.target.value || null; handleAgentChange(); } diff --git a/src/routes/page/agent/[agentId]/agent-components/llm-configs/realtime-config.svelte b/src/routes/page/agent/[agentId]/agent-components/llm-configs/realtime-config.svelte index 72d820cf..12ebf444 100644 --- a/src/routes/page/agent/[agentId]/agent-components/llm-configs/realtime-config.svelte +++ b/src/routes/page/agent/[agentId]/agent-components/llm-configs/realtime-config.svelte @@ -63,12 +63,10 @@ if (!!!provider) { models = []; config.model = null; - config.reasoning_effort_level = null; handleAgentChange(); return; } - config.is_inherit = false; models = getLlmModels(provider); config.model = models[0]?.name; handleAgentChange(); @@ -76,7 +74,6 @@ /** @param {any} e */ function changeModel(e) { - config.is_inherit = false; config.model = e.target.value || null; handleAgentChange(); } From dc8faac039b343ef22d932ff3ad03e9eeb3665e7 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 27 Oct 2025 17:33:05 -0500 Subject: [PATCH 03/10] add code script page --- src/lib/helpers/enums.js | 6 + src/lib/helpers/http.js | 3 +- src/lib/helpers/types/agentTypes.js | 28 ++ src/lib/services/agent-service.js | 33 ++ src/lib/services/api-endpoints.js | 4 + .../page/agent/code-scripts/+page.svelte | 433 ++++++++++++++++++ src/routes/page/conversation/+page.svelte | 2 - svelte.config.js | 1 + 8 files changed, 507 insertions(+), 3 deletions(-) create mode 100644 src/routes/page/agent/code-scripts/+page.svelte diff --git a/src/lib/helpers/enums.js b/src/lib/helpers/enums.js index 71580fbe..7f70fcc4 100644 --- a/src/lib/helpers/enums.js +++ b/src/lib/helpers/enums.js @@ -77,6 +77,12 @@ const agentType = { }; export const AgentType = Object.freeze(agentType); +const agentCodeScriptType = { + Src: 'src', + Test: 'test' +}; +export const AgentCodeScriptType = Object.freeze(agentCodeScriptType); + const routingMode = { Eager: "eager", Lazy: "lazy" diff --git a/src/lib/helpers/http.js b/src/lib/helpers/http.js index ac0435ab..8e470866 100644 --- a/src/lib/helpers/http.js +++ b/src/lib/helpers/http.js @@ -74,7 +74,8 @@ function skipLoader(config) { new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/create', 'g'), new RegExp('http(s*)://(.*?)/knowledge/document/(.*?)/page', 'g'), new RegExp('http(s*)://(.*?)/users', 'g'), - new RegExp('http(s*)://(.*?)/instruct/chat-completion', 'g') + new RegExp('http(s*)://(.*?)/instruct/chat-completion', 'g'), + new RegExp('http(s*)://(.*?)/agent/(.*?)/code-scripts', 'g') ]; /** @type {RegExp[]} */ diff --git a/src/lib/helpers/types/agentTypes.js b/src/lib/helpers/types/agentTypes.js index 34c4bbb4..28e4a1b3 100644 --- a/src/lib/helpers/types/agentTypes.js +++ b/src/lib/helpers/types/agentTypes.js @@ -109,6 +109,34 @@ * @property {string} [direct_agent_id] - Run task directly in this agent. */ +/** + * @typedef {Object} AgentCodeScriptFilter + * @property {string[]?} [scriptNames] + * @property {string[]?} [scriptTypes] + */ + + +/** + * @typedef {Object} AgentCodeScriptViewModel + * @property {string?} [uid] + * @property {string} name + * @property {string} content + * @property {string} script_type + */ + +/** + * @typedef {Object} AgentCodeScriptUpdateOptions + * @property {boolean?} [delete_if_not_included] + * @property {boolean?} [is_upsert] + */ + +/** + * @typedef {Object} AgentCodeScriptUpdateModel + * @property {AgentCodeScriptViewModel[]?} [code_scripts] + * @property {AgentCodeScriptUpdateOptions?} [options] + */ + + /** * @typedef {Object} ChannelInstruction * @property {string} [uid] diff --git a/src/lib/services/agent-service.js b/src/lib/services/agent-service.js index e0786c91..3ab51a65 100644 --- a/src/lib/services/agent-service.js +++ b/src/lib/services/agent-service.js @@ -1,5 +1,6 @@ import { endpoints } from '$lib/services/api-endpoints.js'; import axios from 'axios'; +import qs from 'qs'; /** * Get agent settings @@ -121,3 +122,35 @@ export async function getAgentLabels() { const response = await axios.get(url); return response.data; } + + +/** + * Get agent code scripts + * @param {string} agentId + * @param {import('$agentTypes').AgentCodeScriptFilter?} filter + * @returns {Promise} + */ +export async function getAgentCodeScripts(agentId, filter = null) { + const url = endpoints.agentCodeScriptListUrl.replace("{agentId}", agentId); + const response = await axios.get(url, { + params: { + ...filter + }, + paramsSerializer: (params) => qs.stringify(params, { encode: false, allowDots: true, arrayFormat: "indices" }) + }); + return response.data; +} + +/** + * Update agent code scripts + * @param {string} agentId + * @param {import('$agentTypes').AgentCodeScriptUpdateModel} update + * @returns {Promise} + */ +export async function updateAgentCodeScripts(agentId, update) { + const url = endpoints.agentCodeScriptUpdateUrl.replace("{agentId}", agentId); + const response = await axios.post(url, { + ...update + }); + return response.data; +} \ No newline at end of file diff --git a/src/lib/services/api-endpoints.js b/src/lib/services/api-endpoints.js index 12ab5ec3..1ee3cc59 100644 --- a/src/lib/services/api-endpoints.js +++ b/src/lib/services/api-endpoints.js @@ -37,6 +37,10 @@ export const endpoints = { agentUtilityOptionsUrl: `${host}/agent/utility/options`, agentRuleOptionsUrl: `${host}/rule/triggers`, agentLabelsUrl: `${host}/agent/labels`, + + // agent code script: + agentCodeScriptListUrl: `${host}/agent/{agentId}/code-scripts`, + agentCodeScriptUpdateUrl: `${host}/agent/{agentId}/code-scripts`, // agent task agentTaskListUrl: `${host}/agent/tasks`, diff --git a/src/routes/page/agent/code-scripts/+page.svelte b/src/routes/page/agent/code-scripts/+page.svelte new file mode 100644 index 00000000..55dc1924 --- /dev/null +++ b/src/routes/page/agent/code-scripts/+page.svelte @@ -0,0 +1,433 @@ + + + + + + + + + + +
Agent
+ changeScriptContent(e, srcScriptObj.selectedScript?.uid, AgentCodeScriptType.Src)} + placeholder="Enter your content" + /> + {/if} + +
+
+
+ + + + + +
+
+ Test scripts +
+ {#if !!selectedAgentId} + + +
addScript(AgentCodeScriptType.Test)} + > + +
+ {/if} +
+ +
+ + + {#if testScriptObj.scripts.length > 0} + + {#each testScriptObj.scripts as item, idx (idx) } + selectScript(item.uid, AgentCodeScriptType.Test)} + onDelete={() => deleteScript(item.uid, AgentCodeScriptType.Test)} + onInput={() => {}} + /> + {/each} + + changeScriptContent(e, testScriptObj.selectedScript?.uid, AgentCodeScriptType.Test)} + placeholder="Enter your content" + /> + {/if} + + +
+
+ + +
+ + +
+
+{/if} \ No newline at end of file diff --git a/src/routes/page/conversation/+page.svelte b/src/routes/page/conversation/+page.svelte index 265f7750..6b1908dd 100644 --- a/src/routes/page/conversation/+page.svelte +++ b/src/routes/page/conversation/+page.svelte @@ -28,9 +28,7 @@ getPagingQueryParams, setUrlQueryParams, goToUrl, - convertTimeRange - } from '$lib/helpers/utils/common'; diff --git a/svelte.config.js b/svelte.config.js index 7f8a0383..f6f17a39 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -50,6 +50,7 @@ const config = { "/page/agent", "/page/agent/router", "/page/agent/evaluator", + "/page/agent/code-scripts", "/page/agent/reporting/[reportType]", "/page/agent/[agentId]", "/page/agent/[agentId]/build", From 67c16aace91a5fe4584d528aa4d00e709a8b28da Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Mon, 27 Oct 2025 22:43:10 -0500 Subject: [PATCH 04/10] add code editor --- package-lock.json | 99 ++++++++++++-- package.json | 7 + src/lib/scss/custom/pages/_agent.scss | 13 ++ .../page/agent/code-scripts/+page.svelte | 124 +++++++++++++----- vite.config.js | 10 ++ 5 files changed, 207 insertions(+), 46 deletions(-) diff --git a/package-lock.json b/package-lock.json index d00fdd51..7a7d36c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,12 @@ "name": "botsharp-ui", "version": "1.0.0", "dependencies": { + "@codemirror/commands": "^6.10.0", + "@codemirror/lang-python": "^6.2.1", + "@codemirror/language": "^6.11.3", + "@codemirror/state": "^6.5.2", + "@codemirror/theme-one-dark": "^6.1.3", + "@codemirror/view": "^6.38.6", "@ernane/svelte-star-rating": "^1.1.4", "@fullcalendar/common": "^5.11.5", "@microsoft/signalr": "^8.0.0", @@ -30,6 +36,7 @@ "overlayscrollbars-svelte": "^0.5.2", "qs": "^6.14.0", "svelte-awesome-color-picker": "^2.4.7", + "svelte-codemirror-editor": "^1.4.1", "svelte-collapse": "^0.1.2", "svelte-file-dropzone": "^2.0.2", "svelte-flatpickr": "^3.3.3", @@ -100,9 +107,9 @@ } }, "node_modules/@codemirror/commands": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.6.0.tgz", - "integrity": "sha512-qnY+b7j1UNcTS31Eenuc/5YJB6gQOzkUoNmJQc0rznwqSRpeaWWpjkWy2C/MPTcePpsKJEM26hXrOXl1+nceXg==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.0.tgz", + "integrity": "sha512-2xUIc5mHXQzT16JnyOFkh8PvfeXuIut3pslWGfsGOhxP/lpgRm9HOl/mpzLErgt5mXDovqA0d11P21gofRLb9w==", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.4.0", @@ -119,10 +126,22 @@ "@lezer/json": "^1.0.0" } }, + "node_modules/@codemirror/lang-python": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-python/-/lang-python-6.2.1.tgz", + "integrity": "sha512-IRjC8RUBhn9mGR9ywecNhB51yePWCGgvHfY1lWN/Mrp3cKuHr0isDKia+9HnvhiWNnMpbGhWrkhuWOc09exRyw==", + "dependencies": { + "@codemirror/autocomplete": "^6.3.2", + "@codemirror/language": "^6.8.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.1", + "@lezer/python": "^1.1.4" + } + }, "node_modules/@codemirror/language": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.2.tgz", - "integrity": "sha512-kgbTYTo0Au6dCSc/TFy7fK3fpJmgHDv1sG1KNQKJXVi+xBTEeBPY/M30YXiU6mMXeH+YIDLsbrT4ZwNRdtF+SA==", + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.3.tgz", + "integrity": "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==", "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", @@ -153,16 +172,31 @@ } }, "node_modules/@codemirror/state": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz", - "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", + "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/theme-one-dark": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz", + "integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/highlight": "^1.0.0" + } }, "node_modules/@codemirror/view": { - "version": "6.29.1", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.29.1.tgz", - "integrity": "sha512-7r+DlO/QFwPqKp73uq5mmrS4TuLPUVotbNOKYzN3OLP5ScrOVXcm4g13/48b6ZXGhdmzMinzFYqH0vo+qihIkQ==", + "version": "6.38.6", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.6.tgz", + "integrity": "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==", "dependencies": { - "@codemirror/state": "^6.4.0", + "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } @@ -845,6 +879,21 @@ "@lezer/common": "^1.0.0" } }, + "node_modules/@lezer/python": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/@lezer/python/-/python-1.1.18.tgz", + "integrity": "sha512-31FiUrU7z9+d/ElGQLJFXl+dKOdx0jALlP3KEOsGTex8mvj+SoE1FgItcHWK/axkxCHGUSpqIHt6JAWfWu9Rhg==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==" + }, "node_modules/@microsoft/signalr": { "version": "8.0.7", "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-8.0.7.tgz", @@ -1620,6 +1669,21 @@ "periscopic": "^3.1.0" } }, + "node_modules/codemirror": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", + "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", + "peer": true, + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, "node_modules/codemirror-wrapped-line-indent": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/codemirror-wrapped-line-indent/-/codemirror-wrapped-line-indent-1.0.8.tgz", @@ -3941,6 +4005,15 @@ "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0" } }, + "node_modules/svelte-codemirror-editor": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/svelte-codemirror-editor/-/svelte-codemirror-editor-1.4.1.tgz", + "integrity": "sha512-Pv350iro0Y/AZTT/y2OLaonheQqAwl50Hdfipa2Jv1Z04TSP5kPUyxQnRjqxeRW7DXOX9s5Nd11tHdBl9iYSzw==", + "peerDependencies": { + "codemirror": "^6.0.0", + "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, "node_modules/svelte-collapse": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/svelte-collapse/-/svelte-collapse-0.1.2.tgz", diff --git a/package.json b/package.json index bef3fca2..3c8ea925 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,12 @@ }, "type": "module", "dependencies": { + "@codemirror/commands": "^6.10.0", + "@codemirror/lang-python": "^6.2.1", + "@codemirror/language": "^6.11.3", + "@codemirror/state": "^6.5.2", + "@codemirror/theme-one-dark": "^6.1.3", + "@codemirror/view": "^6.38.6", "@ernane/svelte-star-rating": "^1.1.4", "@fullcalendar/common": "^5.11.5", "@microsoft/signalr": "^8.0.0", @@ -59,6 +65,7 @@ "overlayscrollbars-svelte": "^0.5.2", "qs": "^6.14.0", "svelte-awesome-color-picker": "^2.4.7", + "svelte-codemirror-editor": "^1.4.1", "svelte-collapse": "^0.1.2", "svelte-file-dropzone": "^2.0.2", "svelte-flatpickr": "^3.3.3", diff --git a/src/lib/scss/custom/pages/_agent.scss b/src/lib/scss/custom/pages/_agent.scss index f0b4fe0a..c5396a2b 100644 --- a/src/lib/scss/custom/pages/_agent.scss +++ b/src/lib/scss/custom/pages/_agent.scss @@ -273,4 +273,17 @@ &.show { opacity: 1 !important; } +} + +.code-editor { + max-height: 500px; + overflow-y: auto; + scrollbar-width: thin; + resize: none; + + .cm-editor { + min-width: 1000px; + overflow-x: auto; + scrollbar-width: thin; + } } \ No newline at end of file diff --git a/src/routes/page/agent/code-scripts/+page.svelte b/src/routes/page/agent/code-scripts/+page.svelte index 55dc1924..dea3afd6 100644 --- a/src/routes/page/agent/code-scripts/+page.svelte +++ b/src/routes/page/agent/code-scripts/+page.svelte @@ -2,7 +2,14 @@ import { onMount } from 'svelte'; import { _ } from 'svelte-i18n'; import { v4 as uuidv4 } from 'uuid'; - import { Button, Card, CardBody, Col, Input, Row } from '@sveltestrap/sveltestrap'; + import { Button, Card, CardBody, Col, Row, Tooltip } from '@sveltestrap/sveltestrap'; + import CodeMirror from "svelte-codemirror-editor"; + import { indentUnit, indentOnInput, indentService } from "@codemirror/language"; + import { keymap } from "@codemirror/view"; + import { defaultKeymap, history, indentWithTab, historyKeymap } from "@codemirror/commands"; + import { EditorState } from "@codemirror/state"; + import { python } from "@codemirror/lang-python"; + import { oneDark } from "@codemirror/theme-one-dark"; import Breadcrumb from '$lib/common/Breadcrumb.svelte'; import HeadTitle from '$lib/common/HeadTitle.svelte'; import LoadingToComplete from '$lib/common/LoadingToComplete.svelte'; @@ -11,7 +18,38 @@ import NavItem from '$lib/common/nav-bar/NavItem.svelte'; import { getAgentCodeScripts, getAgentOptions, updateAgentCodeScripts } from '$lib/services/agent-service'; import { AgentCodeScriptType } from '$lib/helpers/enums'; - + + const defaultScript = `# Python Demo +def greet(name): + print(f"Hello, {name}!") + +if __name__ == "__main__": + greet('AI') +`; + + const extensions = [ + python(), + indentUnit.of(" "), + EditorState.tabSize.of(4), + indentOnInput(), + indentService.of((context, pos) => { + const prevLine = pos > 0 ? context.state.doc.lineAt(pos - 1) : null; + if (prevLine) { + const prevText = prevLine.text; + const match = prevText.match(/^(\s*)/); + const baseIndent = match ? match[1].length : 0; + + // Check if previous line ends with : (control structure) + if (prevText.trimEnd().endsWith(':')) { + return baseIndent + 4; + } + return baseIndent; + } + return 0; + }), + history(), + keymap.of([...defaultKeymap, ...historyKeymap, indentWithTab]) + ]; /** @type {boolean} */ let isLoading = false; @@ -76,14 +114,14 @@ selectedAgentId = selectedValues.length > 0 ? selectedValues[0] : null; if (!!selectedAgentId) { - loadAgentCodeScripts(selectedAgentId); + initAgentCodeScripts(selectedAgentId); } else { refreshScriptObj([]); } } /** @param {string} agentId */ - function loadAgentCodeScripts(agentId) { + function initAgentCodeScripts(agentId) { return new Promise((resolve, reject) => { getAgentCodeScripts(agentId).then(res => { refreshScriptObj(res); @@ -99,15 +137,19 @@ */ function refreshScriptObj(scripts) { const srcs = scripts?.filter(x => x.script_type === AgentCodeScriptType.Src)?.map(x => ({ ...x, uid: uuidv4() })) || []; + const foundSrc = srcs.find(x => x.name === srcScriptObj?.selectedScript?.name); srcScriptObj = { + ...srcScriptObj, scripts: srcs, - selectedScript: srcs.length > 0 ? srcs[0] : null + selectedScript: foundSrc ? foundSrc : srcs.length > 0 ? srcs[0] : null }; const tests = scripts?.filter(x => x.script_type === AgentCodeScriptType.Test)?.map(x => ({ ...x, uid: uuidv4() })) || []; + const foundTest = srcs.find(x => x.name === srcScriptObj?.selectedScript?.name); testScriptObj = { + ...testScriptObj, scripts: tests, - selectedScript: tests.length > 0 ? tests[0] : null + selectedScript: foundTest ? foundTest : tests.length > 0 ? tests[0] : null }; } @@ -121,8 +163,8 @@ ...srcScriptObj.scripts, { uid: uuidv4(), - name: '', - content: '', + name: `src_${srcScriptObj.scripts.length}.py`, + content: defaultScript, script_type: AgentCodeScriptType.Src } ]; @@ -136,8 +178,8 @@ ...testScriptObj.scripts, { uid: uuidv4(), - name: '', - content: '', + name: `test_${testScriptObj.scripts.length}.py`, + content: defaultScript, script_type: AgentCodeScriptType.Test } ]; @@ -190,7 +232,6 @@ */ function changeScriptContent(e, uid, scriptType) { let obj = null; - if (scriptType === AgentCodeScriptType.Src) { obj = srcScriptObj; } else if (scriptType === AgentCodeScriptType.Test) { @@ -199,7 +240,7 @@ const found = obj?.scripts?.find(x => x.uid === uid); if (found) { - found.content = e.target.value; + found.content = e.detail; } } @@ -214,7 +255,8 @@ const uniqueTestScripts = testScriptObj.scripts.filter(x => x.name?.trim()).filter((script, index, self) => index === self.findIndex(s => s.name === script.name) ); - const scripts = [...uniqueSrcScripts, ...uniqueTestScripts].map(x => ({...x, name: x.name.trim() })); + const scripts = [...uniqueSrcScripts, ...uniqueTestScripts].map(x => ({...x, name: x.name.trim() })) + .filter(x => x.name.endsWith('.py')); const update = { code_scripts: scripts, @@ -251,7 +293,7 @@ if (!selectedAgentId) { return; } - loadAgentCodeScripts(selectedAgentId); + initAgentCodeScripts(selectedAgentId); } @@ -287,6 +329,14 @@
Source scripts
+
+
+ +
+ +
Support python only
+
+
{#if !!selectedAgentId} @@ -331,15 +381,15 @@ /> {/each} - changeScriptContent(e, srcScriptObj.selectedScript?.uid, AgentCodeScriptType.Src)} - placeholder="Enter your content" - /> +
+ changeScriptContent(e, srcScriptObj.selectedScript?.uid, AgentCodeScriptType.Src)} + /> +
{/if} @@ -354,6 +404,14 @@
Test scripts
+
+
+ +
+ +
Support python only
+
+
{#if !!selectedAgentId} @@ -398,15 +456,15 @@ /> {/each} - changeScriptContent(e, testScriptObj.selectedScript?.uid, AgentCodeScriptType.Test)} - placeholder="Enter your content" - /> +
+ changeScriptContent(e, testScriptObj.selectedScript?.uid, AgentCodeScriptType.Test)} + /> +
{/if} @@ -430,4 +488,4 @@
-{/if} \ No newline at end of file +{/if} diff --git a/vite.config.js b/vite.config.js index a35bc1b7..ccc8194f 100644 --- a/vite.config.js +++ b/vite.config.js @@ -15,6 +15,16 @@ export default defineConfig({ "svelte-select", 'lodash.get', 'lodash.isequal', 'lodash.clonedeep' ], + exclude: [ + "svelte-codemirror-editor", + "codemirror", + "@codemirror/lang-python", + "@codemirror/view", + "@codemirror/state", + "@codemirror/language", + "@codemirror/commands", + "@codemirror/theme-one-dark" + ] }, server: { port: 5015, From fc6843e6fa4282bb83018b1799f7732d32c077ea Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 28 Oct 2025 10:56:22 -0500 Subject: [PATCH 05/10] minor change --- src/lib/services/agent-service.js | 7 +++++-- src/routes/page/agent/+page.svelte | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lib/services/agent-service.js b/src/lib/services/agent-service.js index 3ab51a65..d4d87e7b 100644 --- a/src/lib/services/agent-service.js +++ b/src/lib/services/agent-service.js @@ -115,11 +115,14 @@ export async function getAgentRuleOptions() { /** * Get agent labels + * @param {number?} [size] * @returns {Promise} */ -export async function getAgentLabels() { +export async function getAgentLabels(size = null) { const url = endpoints.agentLabelsUrl; - const response = await axios.get(url); + const response = await axios.get(url, { + params: { size: size } + }); return response.data; } diff --git a/src/routes/page/agent/+page.svelte b/src/routes/page/agent/+page.svelte index 6f658ddd..232607d2 100644 --- a/src/routes/page/agent/+page.svelte +++ b/src/routes/page/agent/+page.svelte @@ -123,7 +123,7 @@ } function getAgentLabelOptions() { - return getAgentLabels().then(res => { + return getAgentLabels(3000).then(res => { agentLabelOptions = res?.map(x => ({ label: x, value: x })) || []; }).catch(() => { agentLabelOptions = []; From e2c4c5f4acec4e5bf3853bbd6aec5b74854c8e15 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 28 Oct 2025 17:16:50 -0500 Subject: [PATCH 06/10] add vector search param --- src/lib/helpers/types/knowledgeTypes.js | 6 ++++++ src/routes/page/knowledge-base/question-answer/+page.svelte | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib/helpers/types/knowledgeTypes.js b/src/lib/helpers/types/knowledgeTypes.js index f0ede318..5d105db1 100644 --- a/src/lib/helpers/types/knowledgeTypes.js +++ b/src/lib/helpers/types/knowledgeTypes.js @@ -16,6 +16,7 @@ * @property {number} [confidence] - Confidence. * @property {boolean} [with_vector] - Include vector or not. * @property {VectorFilterGroup[]} [filter_groups] - Search filter groups. + * @property {VectorSearchParam} [search_param] - Search params. */ /** @@ -28,6 +29,11 @@ * @property {VectorSort?} [order_by] - Sort by. */ +/** + * @typedef {Object} VectorSearchParam + * @property {boolean?} [exact_search] - Exact search or not. + */ + /** * @typedef {Object} VectorFilterGroup * @property {string} [logical_operator] - The logical operator. diff --git a/src/routes/page/knowledge-base/question-answer/+page.svelte b/src/routes/page/knowledge-base/question-answer/+page.svelte index a96cc893..dec9e19c 100644 --- a/src/routes/page/knowledge-base/question-answer/+page.svelte +++ b/src/routes/page/knowledge-base/question-answer/+page.svelte @@ -202,11 +202,13 @@ searchDone = true; }); } else { + /** @type {import('$knowledgeTypes').SearchKnowledgeRequest} */ const params = { text: util.trim(text), confidence: Number(validateConfidenceNumber(confidence)), with_vector: enableVector, - filter_groups: innerSearchGroups + filter_groups: innerSearchGroups, + search_param: { exact_search: false } }; searchVectorKnowledge(selectedCollection, params).then(res => { From b4374543d12faaf3d7a090cf485ec0962a6bd7c7 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Tue, 28 Oct 2025 22:45:41 -0500 Subject: [PATCH 07/10] add exact search --- .../page/instruction/testing/+page.svelte | 2 +- .../knowledge-base/documents/+page.svelte | 45 ++++++++++++++++--- .../question-answer/+page.svelte | 43 +++++++++++++++--- 3 files changed, 77 insertions(+), 13 deletions(-) diff --git a/src/routes/page/instruction/testing/+page.svelte b/src/routes/page/instruction/testing/+page.svelte index 66f287b2..3a02b213 100644 --- a/src/routes/page/instruction/testing/+page.svelte +++ b/src/routes/page/instruction/testing/+page.svelte @@ -193,7 +193,7 @@ bind:value={text} on:keydown={(e) => pressKey(e)} /> -
+
{#if elapsedTime} {`Elapsed time: ${elapsedTime}`} diff --git a/src/routes/page/knowledge-base/documents/+page.svelte b/src/routes/page/knowledge-base/documents/+page.svelte index c5a6d8c0..5469f3a1 100644 --- a/src/routes/page/knowledge-base/documents/+page.svelte +++ b/src/routes/page/knowledge-base/documents/+page.svelte @@ -50,6 +50,7 @@ const duration = 2000; const maxLength = 4096; const step = 0.1; + const searchLimit = 10; const enableVector = true; const collectionType = KnowledgeCollectionType.Document; @@ -58,6 +59,7 @@ let successText = "Done"; let errorText = "Error"; let confidence = '0.5'; + let elapsedTime = ''; /** @type {string} */ let selectedCollection; @@ -113,6 +115,7 @@ let textSearch = false; let isAdvSearchOn = false; let disableSearchBtn = false; + let isExactSearch = false; /** @type {any} */ let docUploadrCmp; @@ -135,10 +138,11 @@ sort: null }; - $: disabled = isLoading || isLoadingMore || isSearching; + $: disableBase = isLoading || isLoadingMore || isSearching; + $: disabled = !selectedCollection || disableBase; $: { disableSearchBtn = false; - if (isSearching || isLoadingMore) { + if (!selectedCollection || isSearching || isLoadingMore) { disableSearchBtn = true; } else if (textSearch && searchItems.length > 0) { disableSearchBtn = false; @@ -184,6 +188,8 @@ isSearching = true; searchDone = false; isFromSearch = false; + elapsedTime = ''; + const start = new Date(); innerSearchGroups = buildSearchFilterGroups(searchItems); innerSort = buildSearchSort(sortField, sortOrder); @@ -200,13 +206,18 @@ }).finally(() => { isSearching = false; searchDone = true; + const gap = new Date().getTime() - start.getTime(); + elapsedTime = `${(gap / 1000).toFixed(3)}s`; }); } else { + /** @type {import('$knowledgeTypes').SearchKnowledgeRequest} */ const params = { text: util.trim(text), + limit: searchLimit, confidence: Number(validateConfidenceNumber(confidence)), with_vector: enableVector, - filter_groups: innerSearchGroups + filter_groups: innerSearchGroups, + search_param: { exact_search: isExactSearch } }; searchVectorKnowledge(selectedCollection, params).then(res => { @@ -217,6 +228,8 @@ isSearching = false; searchDone = true; nextId = null; + const gap = new Date().getTime() - start.getTime(); + elapsedTime = `${(gap / 1000).toFixed(3)}s`; }); } } @@ -260,12 +273,14 @@ } function resetStates() { + elapsedTime = ''; text = ""; nextId = null; isSearching = false; searchDone = false; isFromSearch = false; textSearch = false; + isExactSearch = false; selectedOperator = 'or'; innerSearchGroups = []; sortField = ''; @@ -938,8 +953,13 @@ bind:value={text} on:keydown={(e) => pressKey(e)} /> -
- {text?.length || 0}/{maxLength} +
+
+ {#if elapsedTime} + {`Elapsed time: ${elapsedTime}`} + {/if} +
+
{text?.length || 0}/{maxLength}
+ {#if !textSearch} +
+
+ +
+
+ {/if}