From eb26e963c70f2db8bb39e480e783e82b97f37725 Mon Sep 17 00:00:00 2001 From: Diana Gromova <16adianay@gmail.com> Date: Wed, 2 Sep 2026 19:02:08 +0400 Subject: [PATCH 1/7] Add Chat - Custom AI Assistant demo --- .../CustomAIAssistant/jQuery/ai-service.js | 80 ++++ .../CustomAIAssistant/jQuery/chat-router.js | 272 ++++++++++++++ .../Chat/CustomAIAssistant/jQuery/data.js | 87 +++++ .../CustomAIAssistant/jQuery/form-commands.js | 89 +++++ .../CustomAIAssistant/jQuery/grid-commands.js | 286 ++++++++++++++ .../Chat/CustomAIAssistant/jQuery/index.html | 36 ++ .../Chat/CustomAIAssistant/jQuery/index.js | 349 ++++++++++++++++++ .../Chat/CustomAIAssistant/jQuery/styles.css | 68 ++++ apps/demos/menuMeta.json | 29 ++ 9 files changed, 1296 insertions(+) create mode 100644 apps/demos/Demos/Chat/CustomAIAssistant/jQuery/ai-service.js create mode 100644 apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js create mode 100644 apps/demos/Demos/Chat/CustomAIAssistant/jQuery/data.js create mode 100644 apps/demos/Demos/Chat/CustomAIAssistant/jQuery/form-commands.js create mode 100644 apps/demos/Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js create mode 100644 apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.html create mode 100644 apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.js create mode 100644 apps/demos/Demos/Chat/CustomAIAssistant/jQuery/styles.css diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/ai-service.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/ai-service.js new file mode 100644 index 000000000000..844db2d9c648 --- /dev/null +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/ai-service.js @@ -0,0 +1,80 @@ +function createAiIntegration() { + const aiService = new AzureOpenAI({ + dangerouslyAllowBrowser: true, + deployment, + endpoint, + apiVersion, + apiKey, + }); + + async function getAIResponse(messages, signal) { + const params = { + messages, + model: deployment, + max_completion_tokens: 1000, + temperature: 0, + }; + + const response = await aiService.chat.completions.create(params, { + signal, + }); + + return response.choices[0].message?.content; + } + + async function getAIResponseRecursive(messages, signal) { + return getAIResponse(messages, signal).catch(async (error) => { + if (!error.message.includes("Connection error")) { + throw error; + } + + DevExpress.ui.notify({ + message: + "Our demo AI service reached a temporary request limit. Retrying in 30 seconds.", + width: "auto", + type: "error", + displayTime: 5000, + }); + + await new Promise((resolve) => setTimeout(resolve, 30000)); + + return getAIResponseRecursive(messages, signal); + }); + } + + return new DevExpress.aiIntegration.AIIntegration({ + sendRequest({ prompt, data }) { + const isValidRequest = JSON.stringify(prompt.user).length < 20000; + if (!isValidRequest) { + return { + promise: Promise.reject( + new ChatCommandError( + "❌ This message is too long for me to process. Please shorten it and try again.", + ), + ), + abort: () => {}, + }; + } + const controller = new AbortController(); + const signal = controller.signal; + + const isSmartPasteRequest = Array.isArray(data?.fields); + const system = isSmartPasteRequest + ? `${prompt.system ?? ""} IMPORTANT: reply on a SINGLE line with no line breaks of any kind - use ";;;" as the only separator between fields.` + : (prompt.system ?? ""); + + const aiPrompt = [ + { role: "system", content: system }, + { role: "user", content: prompt.user }, + ]; + const promise = getAIResponseRecursive(aiPrompt, signal); + + return { + promise, + abort: () => { + controller.abort(); + }, + }; + }, + }); +} diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js new file mode 100644 index 000000000000..0a6fde992ad8 --- /dev/null +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js @@ -0,0 +1,272 @@ +class ChatCommandError extends Error {} + +function extractJson(text) { + const match = text.match(/\{[\s\S]*\}/); + + try { + return JSON.parse(match[0]); + } catch { + throw new ChatCommandError( + "❌ I received an unexpected response from the AI service. Please rephrase your request and try again.", + ); + } +} + +function executeAiCommand(text, aiIntegration) { + return new Promise((resolve, reject) => { + aiIntegration.execute( + { text }, + { + onComplete: (finalResponse) => { + try { + resolve(extractJson(finalResponse)); + } catch (error) { + reject(error); + } + }, + onError: reject, + }, + ); + }); +} + +function buildGridSystemPrompt(columnNames) { + return [ + "You control a task DataGrid on this page.", + "This page ALSO has a separate employee/customer profile form (fields like name, title/prefix, " + + "position, state, birth date) that is handled elsewhere - it is NOT part of this grid.", + "Figure out what the user's request is about and translate ONLY the part that is clearly about " + + "the task grid into the matching commands described below.", + "Do NOT create a grid action just because a value could technically fit a text column (e.g. " + + '"Subject"). If the request is about the profile form (e.g. mentions a person\'s name, title, ' + + 'job position, state, or birth date), leave that part out of "actions" entirely - even if no ' + + "other part of the request is grid-related.", + "", + buildGridPromptSection(columnNames), + "", + "Respond with STRICT JSON only, no code fences, no explanations, matching this schema:", + JSON.stringify(buildGridResponseSchema()), + "", + 'If the request has nothing to do with the grid, respond with "actions": [].', + ].join("\n"); +} + +const FIELD_OR_VALUE_NOT_FOUND_MESSAGE = + "❌ No field or column exists with such a name, or the entered value is invalid. Please check the name and value and try again."; + +const MAX_USER_MESSAGE_LENGTH = 2000; +const ROUTER_TARGETS = new Set(["form", "grid", "mixed", "none"]); +const FORM_ACTION_TYPES = new Set(["clear_field", "clear_all", "smart_paste"]); + +function buildFormActionPromptSection(form) { + const fieldList = getFormFieldOptions(form) + .map((f) => `${f.dataField} (${f.label})`) + .join(", "); + + return [ + `Form fields (dataField and label): ${fieldList}.`, + 'If the request is about the form, also set "formAction" to one of:', + '- {"type": "clear_field", "field": ""} to clear one specific field.', + '- {"type": "clear_all"} to clear/reset the whole form.', + '- {"type": "smart_paste"} to fill in form data from the request text.', + 'Set "formAction" to null if the request is not about the form.', + ].join("\n"); +} + +async function classifyRequest(text, aiIntegration, form) { + if (!aiIntegration) { + return { target: "mixed", formAction: null }; + } + + const prompt = [ + "Decide which UI area should handle the user's request.", + "Return STRICT JSON only, without markdown fences.", + 'Format: {"target": "form" | "grid" | "mixed" | "none", "formAction": | null, "reason": "short explanation" }', + "Rules:", + "- Use form for profile/customer form updates, field clearing, or smart-paste style data entry.", + "- Use grid for sorting, filtering, showing/hiding columns, or other DataGrid tasks.", + "- Use mixed when the request clearly asks for both a form change and a grid change together.", + "- Use none when the request is unrelated to both areas.", + "If you are not confident, return mixed.", + "", + buildFormActionPromptSection(form), + "", + `User request: "${text}"`, + ].join("\n"); + + try { + const parsed = await executeAiCommand(prompt, aiIntegration); + const target = String(parsed?.target ?? "mixed") + .trim() + .toLowerCase(); + const rawFormAction = parsed?.formAction; + const formAction = + rawFormAction && FORM_ACTION_TYPES.has(rawFormAction.type) + ? rawFormAction + : null; + + return { + target: ROUTER_TARGETS.has(target) ? target : "mixed", + formAction, + }; + } catch { + return { target: "mixed", formAction: null }; + } +} + +function buildGridResultsPromise(gridInstance, aiIntegration, text) { + const columnNames = getGridColumnNames(gridInstance); + const prompt = `${buildGridSystemPrompt(columnNames)}\n\nUser request: "${text}"`; + + return executeAiCommand(prompt, aiIntegration) + .then((parsed) => { + + const actions = Array.isArray(parsed.actions) ? parsed.actions : []; + + if (actions.length === 0) { + gridInstance?.endCustomLoading(); + return { results: [], error: null }; + } + + try { + return { + results: applyGridActions(gridInstance, actions, text), + error: null, + }; + } finally { + gridInstance?.endCustomLoading(); + } + }) + .catch((error) => { + gridInstance?.endCustomLoading(); + return { results: [], error }; + }); +} + +function buildFormResultsPromise(form, formAction, text) { + const clearResult = applyFormClearAction(form, formAction); + if (clearResult) { + return Promise.resolve({ results: [clearResult], error: null }); + } + + return applyFormSmartPaste(form, text) + .then((result) => ({ results: [result], error: null })) + .catch((error) => ({ results: [], error })); +} + +function formatFailures(failed) { + return failed.map((message) => `❌ ${message}`).join("\n"); +} + +function formatSucceeded(succeeded) { + return succeeded.map((message) => `✅ Done. ${message}`).join("\n"); +} + +function joinSucceededOrThrow(results, fallbackError) { + const succeeded = results + .filter((r) => r.status === "success") + .map((r) => r.message); + const failed = results + .filter((r) => r.status === "failure") + .map((r) => r.message); + + if (succeeded.length === 0) { + throw failed.length > 0 + ? new ChatCommandError(formatFailures(failed)) + : (fallbackError ?? new ChatCommandError(FIELD_OR_VALUE_NOT_FOUND_MESSAGE)); + } + + return failed.length > 0 + ? `${formatSucceeded(succeeded)}\n${formatFailures(failed)}` + : formatSucceeded(succeeded); +} + +async function runCommand(text, { form, gridInstance, aiIntegration }) { + if (text.length > MAX_USER_MESSAGE_LENGTH) { + return Promise.reject( + new ChatCommandError( + "❌ This message is too long for me to process. Please shorten it and try again.", + ), + ); + } + + const { target, formAction } = await classifyRequest( + text, + aiIntegration, + form, + ); + + if (target === "form") { + const { results: formResults, error: formError } = + await buildFormResultsPromise(form, formAction, text); + + return joinSucceededOrThrow(formResults, formError); + } + + if (target === "grid") { + gridInstance?.beginCustomLoading(); + + const { results: gridResults, error: gridError } = + await buildGridResultsPromise(gridInstance, aiIntegration, text); + + return joinSucceededOrThrow(gridResults, gridError); + } + + const [ + { results: formResults, error: formError }, + { results: gridResults, error: gridError }, + ] = await Promise.all([ + buildFormResultsPromise(form, formAction, text), + buildGridResultsPromise(gridInstance, aiIntegration, text), + ]); + + if (gridError) { + console.warn( + "DataGrid AI request failed, but the Form request may have succeeded:", + gridError, + ); + } + + if (formError) { + console.warn( + "Form AI request failed, but the DataGrid request may have succeeded:", + formError, + ); + } + + return joinSucceededOrThrow( + [...formResults, ...gridResults], + gridError ?? formError, + ); +} + +function reportAiResult(promise, pushMessage) { + return promise + .then((message) => { + pushMessage({ + author: { id: "ai", name: "AI Assistant" }, + text: message, + }); + }) + .catch((error) => { + const text = + error instanceof ChatCommandError + ? error.message + : "❌ I couldn't reach the AI service. Please check your connection and try again."; + + pushMessage({ + author: { id: "ai", name: "AI Assistant" }, + text, + }); + }); +} + +function routeMessage( + text, + { form, gridInstance, aiIntegration, pushMessage }, +) { + return reportAiResult( + runCommand(text, { form, gridInstance, aiIntegration }), + pushMessage, + ); +} diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/data.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/data.js new file mode 100644 index 000000000000..bc5c56febde6 --- /dev/null +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/data.js @@ -0,0 +1,87 @@ +const CLASSES = { + clearChatButton: "ai-chat-clear-button", +}; + +const deployment = "demo-mini"; +const apiVersion = "2024-02-01"; +const endpoint = "https://public-api.devexpress.com/demo-openai"; +const apiKey = "DEMO"; + +const emptyViewMessage = "How can I help with this page?"; +const emptyViewPrompt = + "Update employee Form fields.\nFilter or sort tasks, display or hide DataGrid columns, or clear all filters and sorting." + +const titles = ["Mr.", "Mrs.", "Ms."]; +const colors = { High: "#F1BBBC", Normal: "#F9E2AE", Low: "#9FD89F" }; +const states = ["California", "New York", "Texas"]; +const positions = [ + "CEO", + "Sales Assistant", + "CMO", + "Manager", + "Designer", + "Developer", +]; + +const employee = { + ID: 1, + Prefix: "Mr.", + FirstName: "John", + LastName: "Heart", + Position: "CEO", + State: "California", + BirthDate: "1964/03/16", +}; + +const tasks = [ + { + ID: 5, + Subject: "Choose between PPO and HMO Health Plan", + StartDate: "2026/02/15", + DueDate: "2026/04/15", + Status: "In Progress", + Priority: "Low", + Completion: 75, + EmployeeID: 1, + }, + { + ID: 6, + Subject: "Google AdWords Strategy", + StartDate: "2026/02/16", + DueDate: "2026/02/28", + Status: "Completed", + Priority: "High", + Completion: 100, + EmployeeID: 1, + }, + { + ID: 7, + Subject: "New Brochures", + StartDate: "2026/02/17", + DueDate: "2026/02/24", + Status: "Completed", + Priority: "Normal", + Completion: 100, + EmployeeID: 1, + }, + { + ID: 22, + Subject: "Update NDA Agreement", + StartDate: "2026/03/14", + DueDate: "2026/03/16", + Status: "Completed", + Priority: "High", + Completion: 100, + EmployeeID: 1, + }, + { + ID: 52, + Subject: "Review Product Recall Report by Engineering Team", + StartDate: "2026/05/17", + DueDate: "2026/05/20", + Status: "Completed", + Priority: "High", + Completion: 100, + EmployeeID: 1, + }, +]; diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/form-commands.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/form-commands.js new file mode 100644 index 000000000000..9be2a171b732 --- /dev/null +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/form-commands.js @@ -0,0 +1,89 @@ +const SMART_PASTE_TIMEOUT_MS = 30000; + +function getFormFieldOptions(form) { + return (form.option("items") || []) + .filter((item) => item.dataField) + .map((item) => ({ + dataField: item.dataField, + label: item.label?.text ?? item.dataField, + })); +} + +function applyFormClearAction(form, formAction) { + if (!formAction || formAction.type === "smart_paste") return null; + + if (formAction.type === "clear_all") { + try { + form.clear(); + return { status: "success", message: "Cleared all Form fields." }; + } catch { + return { + status: "failure", + message: "I couldn't clear the form.", + }; + } + } + + if (formAction.type === "clear_field") { + const isKnownField = getFormFieldOptions(form).some( + (f) => f.dataField === formAction.field, + ); + + if (!isKnownField) { + return { + status: "failure", + message: `I couldn't find a field named "${formAction.field}" to clear.`, + }; + } + + form.updateData(formAction.field, null); + + return { status: "success", message: `Cleared ${formAction.field}.` }; + } + + return null; +} + +function applyFormSmartPaste(form, text) { + return new Promise((resolve) => { + let settled = false; + + const finish = (result) => { + if (settled) return; + settled = true; + clearTimeout(timeoutId); + form.off("smartPasted", handleSmartPasted); + resolve(result); + }; + + const handleSmartPasted = (e) => { + const fieldCount = Object.keys(e.aiResult ?? {}).length; + finish( + fieldCount > 0 + ? { status: "success", message: "Updated the form." } + : { + status: "failure", + message: "I couldn't find any Form fields matching the request.", + }, + ); + }; + + const timeoutId = setTimeout(() => { + finish({ + status: "failure", + message: "I couldn't process your request. Please try rephrasing it.", + }); + }, SMART_PASTE_TIMEOUT_MS); + + form.on("smartPasted", handleSmartPasted); + + try { + form.smartPaste(text); + } catch { + finish({ + status: "failure", + message: "I couldn't process your request. Please try rephrasing it.", + }); + } + }); +} diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js new file mode 100644 index 000000000000..5005da420a71 --- /dev/null +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js @@ -0,0 +1,286 @@ +const gridCommands = { + filterValue: { + description: + "Apply a filter to a single column. Pass column (dataField), operator, and value. " + + 'Supported operators: "=", "<>", "<", "<=", ">", ">=", "contains", "notcontains", "startswith", "endswith", "anyof". ' + + 'Date values must be in "YYYY-MM-DDTHH:mm:ss" format (e.g. "2024-05-10T00:00:00"). ' + + 'The "Completion" column is a boolean (task completed or not): use operator "=" with value true for ' + + "completed tasks, or value false for tasks that are not completed. " + + "To filter a date column by a year and/or month (the same thing the grid's own header filter does " + + 'when you pick a year then a month), use operator "anyof" with value as an array of one or more ' + + 'strings in "YYYY" (whole year, e.g. "2023") or "YYYY/M" (whole month, month is 1-12 with no leading ' + + 'zero, e.g. "2023/5" for May 2023) format, e.g. {"column": "DueDate", "operator": "anyof", "value": ' + + '["2023/5"]} for "May 2023". Only use "anyof" when the year is known; if the year is missing and ' + + 'cannot be inferred from elsewhere in the request (e.g. plain "in May" with no year anywhere), do ' + + "not guess it - omit this action entirely instead of adding it with a made-up year.", + schema: { + type: "object", + properties: { + column: { type: "string" }, + operator: { + type: "string", + enum: [ + "=", + "<>", + "<", + "<=", + ">", + ">=", + "contains", + "notcontains", + "startswith", + "endswith", + "anyof", + ], + }, + value: { + anyOf: [ + { type: ["string", "number", "boolean"] }, + { type: "array", items: { type: "string" } }, + ], + }, + }, + required: ["column", "operator", "value"], + }, + execute(grid, args, rawText) { + const { column, failure } = getColumnOrFail(grid, args.column); + if (failure) return failure; + + const caption = column.caption ?? args.column; + let { value } = args; + + if (args.column === "Completion" && typeof value !== "boolean") { + const normalized = String(value).trim().toLowerCase(); + value = + value === 100 || + ["true", "completed", "yes", "100"].includes(normalized); + } + + if ( + (column.dataType === "date" || column.dataType === "datetime") && + typeof value === "string" + ) { + const parsedDate = new Date(value); + if (!Number.isNaN(parsedDate.getTime())) { + value = parsedDate; + } + } + + if (args.operator === "anyof" && Array.isArray(value)) { + const mentionedYears = new Set( + String(rawText ?? "").match(/\b\d{4}\b/g), + ); + const hasUngroundedYear = value.some( + (entry) => !mentionedYears.has(String(entry).split("/")[0]), + ); + + if (hasUngroundedYear) { + return { + status: "failure", + message: `No field or column exists with such a name, or the entered value is invalid.`, + }; + } + } + + try { + grid.option("filterValue", [args.column, args.operator, value]); + return { + status: "success", + message: `Filtered by "${caption}".`, + }; + } catch { + return { + status: "failure", + message: `I couldn't apply that filter to "${caption}". Check that the value matches the column's type.`, + }; + } + }, + }, + + clearFilter: { + description: "Clear all filters on the grid.", + schema: { type: "object", properties: {} }, + execute(grid) { + try { + grid.clearFilter(); + return { status: "success", message: "Filter cleared." }; + } catch { + return { + status: "failure", + message: "I couldn't clear the DataGrid's filters.", + }; + } + }, + }, + + sorting: { + description: + 'Sort a column ascending or descending. Pass sortOrder "none" to remove sorting from this column only.', + schema: { + type: "object", + properties: { + column: { type: "string" }, + sortOrder: { type: "string", enum: ["asc", "desc", "none"] }, + }, + required: ["column", "sortOrder"], + }, + execute(grid, args) { + const { column, failure } = getColumnOrFail(grid, args.column); + if (failure) return failure; + + const caption = column.caption ?? args.column; + + try { + grid.columnOption( + args.column, + "sortOrder", + args.sortOrder === "none" ? undefined : args.sortOrder, + ); + + const message = + args.sortOrder === "none" + ? `Cleared sorting on "${caption}".` + : `Sorted by "${caption}" (${args.sortOrder === "asc" ? "ascending" : "descending"}).`; + + return { status: "success", message }; + } catch { + return { + status: "failure", + message: `I couldn't sort by "${caption}".`, + }; + } + }, + }, + + clearSorting: { + description: "Remove sorting from all columns.", + schema: { type: "object", properties: {} }, + execute(grid) { + try { + grid.clearSorting(); + return { status: "success", message: "Sorting cleared." }; + } catch { + return { + status: "failure", + message: "I couldn't clear the DataGrid's sorting.", + }; + } + }, + }, + + columnsVisibility: { + description: "Show or hide a column.", + schema: { + type: "object", + properties: { + column: { type: "string" }, + visible: { type: "boolean" }, + }, + required: ["column", "visible"], + }, + execute(grid, args) { + const { column, failure } = getColumnOrFail(grid, args.column); + if (failure) return failure; + + const caption = column.caption ?? args.column; + + try { + grid.columnOption(args.column, "visible", args.visible); + return { + status: "success", + message: args.visible + ? `Showed column "${caption}".` + : `Hid column "${caption}".`, + }; + } catch { + return { + status: "failure", + message: `I couldn't change the visibility of "${caption}".`, + }; + } + }, + }, +}; + +function getColumnOrFail(grid, columnName) { + const column = grid.columnOption(columnName); + + if (!column) { + return { + column: null, + failure: { + status: "failure", + message: `I couldn't find a DataGrid column named "${columnName}".`, + }, + }; + } + + return { column, failure: null }; +} + +function buildGridResponseSchema() { + const branches = Object.entries(gridCommands).map(([name, cmd]) => ({ + type: "object", + properties: { + name: { type: "string", enum: [name] }, + args: cmd.schema, + }, + required: ["name", "args"], + })); + + return { + type: "object", + properties: { + actions: { + type: "array", + description: "List of grid commands to execute, in order.", + items: { anyOf: branches }, + }, + }, + required: ["actions"], + }; +} + +function buildGridPromptSection(columnNames) { + const commandDescriptions = Object.entries(gridCommands) + .map(([name, cmd]) => `- "${name}": ${cmd.description}`) + .join("\n"); + + return [ + 'GRID: translate any part of the request that affects the task grid into one or more grid commands (the "actions" array).', + `Available columns (dataField): ${columnNames.join(", ")}.`, + "CRITICAL RULE: a column mentioned in the request must clearly correspond to one of the available " + + 'columns above (matching by meaning is fine, e.g. "due date" -> "DueDate"). If it does not - even ' + + "if it superficially looks like it could be a column name - you must NOT invent or substitute the " + + "closest-sounding available column. Instead, still emit the action using the column name exactly " + + "as written in the request, so the app can report that the column wasn't found - never replace it " + + "with a different, existing column just to make the action valid.", + 'Example: request "filter the ZXQ column by foo" - ZXQ matches no available column, so emit ' + + '{"column": "ZXQ", ...} as-is (it will correctly fail as "column not found") - do NOT emit an ' + + 'action for "Subject" or any other real column instead.', + 'The "Completion" column is a boolean: true means the task is completed, false means it is not. ' + + 'To filter for "completed" tasks, use {"column": "Completion", "operator": "=", "value": true}. ' + + 'To filter for "not completed" tasks, use {"column": "Completion", "operator": "=", "value": false}.', + "Available grid commands:", + commandDescriptions, + ].join("\n"); +} + +function getGridColumnNames(gridInstance) { + return gridInstance.getVisibleColumns().map((col) => col.dataField); +} + +function applyGridActions(grid, actions, rawText) { + return actions.map((action) => { + const command = gridCommands[action.name]; + + if (!command) { + return { + status: "failure", + message: `I don't know how to do "${action.name}".`, + }; + } + + return command.execute(grid, action.args ?? {}, rawText); + }); +} diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.html b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.html new file mode 100644 index 000000000000..11eef21e09c0 --- /dev/null +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.html @@ -0,0 +1,36 @@ + + + + DevExtreme Demo + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+ + diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.js new file mode 100644 index 000000000000..7dbefc978170 --- /dev/null +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.js @@ -0,0 +1,349 @@ +$(function () { + DevExpress.config({ + editorStylingMode: "filled", + }); + + DevExpress.config({ + floatingActionButtonConfig: { + position: { + my: "right bottom", + at: "right bottom", + of: "#grid-container", + offset: "-16 -16", + }, + }, + }); + + DevExpress.localization.loadMessages({ + en: { + "dxChat-textareaPlaceholder": "Enter a prompt...", + }, + }); + + let chatInstance; + let popupInstance; + let clearButtonInstance; + let formInstance; + let gridInstance; + let toastInstance; + + toastInstance = $("#toast") + .dxToast({ + displayTime: 600, + closeOnClick: true, + message: "Form data is saved.", + type: "success", + position: { + of: "#form-container", + at: "bottom center", + my: "bottom center", + offset: "0 -20", + }, + }) + .dxToast("instance"); + + function pushMessage(message) { + chatInstance + .getDataSource() + .store() + .push([ + { + type: "insert", + data: { + id: Date.now() + Math.random(), + timestamp: new Date(), + ...message, + }, + }, + ]); + } + + function setDisabled(disabled) { + chatInstance.option("disabled", disabled); + } + + function updateClearButtonState() { + const items = chatInstance.getDataSource().items(); + clearButtonInstance?.option("disabled", items.length === 0); + } + + function clearChat() { + const store = chatInstance.getDataSource().store(); + + store.clear(); + chatInstance.getDataSource().reload(); + updateClearButtonState(); + } + + function handleUserMessage(message) { + setDisabled(true); + + const finish = () => { + setDisabled(false); + updateClearButtonState(); + }; + + const routed = routeMessage(message.text, { + form: formInstance, + gridInstance, + aiIntegration, + pushMessage, + }); + + routed.finally(finish); + } + + popupInstance = $("#aiAssistantContainer") + .dxPopup({ + title: "AI Assistant", + width: 400, + height: "90%", + dragEnabled: true, + resizeEnabled: true, + showCloseButton: true, + shading: false, + visible: false, + onHiding() { + fabBtn.option("visible", true); + }, + onShowing() { + fabBtn.option("visible", false); + }, + position: { + my: "right top", + at: "right top", + of: ".demo-container", + offset: "-20 20", + }, + toolbarItems: [ + { + widget: "dxButton", + toolbar: "top", + location: "after", + cssClass: CLASSES.clearChatButton, + options: { + icon: "clearhistory", + disabled: true, + hint: "Clear chat", + onClick: () => clearChat(), + onInitialized: (e) => { + clearButtonInstance = e.component; + }, + }, + }, + ], + contentTemplate($container) { + const $chatContainer = $("
") + .addClass("ai-chat-content") + .appendTo($container); + + $chatContainer.dxChat({ + height: "100%", + showAvatar: false, + width: "auto", + dataSource: { + store: new DevExpress.data.ArrayStore({ key: "id" }), + }, + reloadOnChange: true, + user: { id: "user" }, + showUserName: false, + speechToTextEnabled: true, + suggestions: { + items: [ + { text: "Show Completed Tasks", prompt: "Show Completed Tasks" }, + { + text: "Change State to Texas", + prompt: "Change State to Texas", + }, + ], + onItemClick(e) { + const { prompt } = e.itemData; + const message = { + id: Date.now() + Math.random(), + timestamp: new Date(), + author: { id: "user" }, + text: prompt, + }; + + pushMessage(message); + handleUserMessage(message); + }, + }, + emptyViewTemplate(_data, container) { + const $icon = $("
").addClass( + "dx-chat-messagelist-empty-image dx-ai-chat__empty-image", + ); + const $message = $("
") + .addClass("ai-chat-empty-message") + .text(emptyViewMessage); + const $prompt = $("
") + .addClass("ai-chat-empty-prompt") + .html(emptyViewPrompt); + + $(container).append($icon).append($message).append($prompt); + }, + onMessageEntered(e) { + handleUserMessage(e.message); + }, + onInitialized(e) { + chatInstance = e.component; + }, + }); + }, + }) + .dxPopup("instance"); + + const aiIntegration = createAiIntegration(); + + formInstance = $("#form-container") + .dxForm({ + formData: employee, + colCount: 3, + labelLocation: "top", + onOptionChanged: (e) => { + if (e.name === "isDirty") { + const saveButton = formInstance.getButton("Save"); + saveButton.option("disabled", !e.value); + } + }, + aiIntegration, + items: [ + { + dataField: "Prefix", + label: { text: "Title" }, + editorType: "dxSelectBox", + editorOptions: { items: titles, searchEnabled: true }, + aiOptions: { + instruction: + "Only fill this field with one of the allowed values (Mr., Mrs., Ms.) if a " + + "title is explicitly mentioned in the text. Never use this field for any part " + + "of a person's name.", + }, + }, + { + dataField: "FirstName", + label: { text: "First Name" }, + aiOptions: { + instruction: + "Only fill this field if the text clearly refers to a person's given name. " + + "Never use grid/task-related words like Subject, Priority, Status, Due Date, " + + "Completion, or generic verbs like sort/filter/show as a name.", + }, + }, + { + dataField: "LastName", + label: { text: "Last Name" }, + aiOptions: { + instruction: + "If the text gives a full person name (e.g. 'customer name', 'employee name') " + + "without separately labeled first/last names, use only the first word as First " + + "Name and the rest of the name as Last Name.", + }, + }, + { + dataField: "Position", + editorType: "dxSelectBox", + editorOptions: { items: positions, searchEnabled: true }, + aiOptions: { + instruction: + "Only fill this field with one of the allowed job position values if the text " + + "explicitly refers to the employee's own job title/role.", + }, + }, + { + dataField: "State", + editorType: "dxSelectBox", + editorOptions: { items: states, searchEnabled: true }, + aiOptions: { + instruction: + "Only fill this field with one of the allowed US state values if the text " + + "explicitly refers to the employee's home/office state", + }, + }, + { + dataField: "BirthDate", + editorType: "dxDateBox", + editorOptions: { displayFormat: "M/d/yyyy" }, + aiOptions: { + instruction: + "Only fill this field if the text explicitly refers to the employee's own birth " + + "date or date of birth.", + }, + }, + { + itemType: "button", + name: "Save", + colSpan: 3, + cssClass: "save-button", + buttonOptions: { + text: "Save", + type: "default", + disabled: true, + useSubmitBehavior: true, + width: "120px", + onClick: () => { + toastInstance.show(); + }, + }, + }, + ], + }) + .dxForm("instance"); + + gridInstance = $("#grid-container") + .dxDataGrid({ + dataSource: tasks, + keyExpr: "ID", + showBorders: true, + filterRow: { visible: true }, + headerFilter: { visible: true }, + filterSyncEnabled: true, + columns: [ + { dataField: "Subject", width: 250 }, + { dataField: "StartDate", dataType: "date" }, + { dataField: "DueDate", dataType: "date" }, + { + dataField: "Priority", + caption: "Priority", + cellTemplate: (container, options) => { + $("
") + .css({ + background: colors[options.value], + borderRadius: "24px", + padding: "2px 8px", + display: "inline-block", + textAlign: "center", + }) + .text(options.value) + .appendTo(container); + }, + }, + { + dataField: "Completion", + caption: "Completed", + alignment: "center", + dataType: "boolean", + calculateCellValue: (rowData) => rowData.Completion === 100, + calculateFilterExpression(filterValue, selectedFilterOperation) { + const wantsCompleted = + selectedFilterOperation === "<>" ? !filterValue : !!filterValue; + const rawCompletion = (rowData) => rowData.Completion; + return wantsCompleted + ? [rawCompletion, "=", 100] + : [rawCompletion, "<", 100]; + }, + }, + ], + }) + .dxDataGrid("instance"); + + let fabBtn = $("#ai-fab") + .dxSpeedDialAction({ + icon: "sparkle", + label: "AI Assistant", + onClick: function (e) { + popupInstance.toggle(); + }, + }) + .dxSpeedDialAction("instance"); +}); diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/styles.css b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/styles.css new file mode 100644 index 000000000000..2031731a987e --- /dev/null +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/styles.css @@ -0,0 +1,68 @@ +.dx-chat { + max-width: 900px; + border-radius: 0; + min-height: 20em; +} + +#grid-container { + margin-top: 20px; + min-height: 360px; +} + +#form-container { + border: 1px solid #e0e0e0; + padding: 16px; +} + +.dx-color-scheme-blue-light #form-container { + background-color: #fff; +} + +.demo-container { + margin: 20px; + height: 556px; +} + +.ai-chat-empty-icon { + font-size: 32px; + color: #337ab7; + text-align: center; + margin-bottom: 12px; +} + +.ai-chat-empty-message { + font-size: 18px; + font-weight: 400; + text-align: center; + color: #242424; + margin-bottom: 8px; +} + +.ai-chat-empty-prompt { + font-size: 14px; + color: #888; + text-align: center; + padding: 0 24px; + white-space: pre-line; +} + +.ai-chat-clear-button { + margin-right: 8px; +} + +.dx-popup-content { + padding: 0; +} + +.dx-chat-messagebox { + border-top-color: transparent; +} + +.dx-chat-messagelist-empty-view, +.dx-chat-messagebox { + padding-top: 0; +} + +.dx-layout-manager .dx-field-item.save-button { + padding-bottom: 0; +} diff --git a/apps/demos/menuMeta.json b/apps/demos/menuMeta.json index 72e77fc3bcc3..4e910a8cc810 100644 --- a/apps/demos/menuMeta.json +++ b/apps/demos/menuMeta.json @@ -139,6 +139,21 @@ "DemoType": "Web" } ] + }, + { + "Name": "Custom AI Assistant", + "Equivalents": "AI, Chatbot, Assistant", + "Demos": [ + { + "Title": "Custom AI Assistant", + "Name": "CustomAIAssistant", + "Widget": "Chat", + "Badge": "AI", + "TopLevel": true, + "Modules": "openai", + "DemoType": "Web" + } + ] } ] }, @@ -4444,6 +4459,20 @@ } ] }, + { + "Name": "Custom AI Assistant", + "Equivalents": "AI, Chatbot, Assistant", + "Demos": [ + { + "Title": "Custom AI Assistant", + "Name": "CustomAIAssistant", + "Widget": "Chat", + "Badge": "AI", + "Modules": "openai", + "DemoType": "Web" + } + ] + }, { "Name": "AI and Chatbot Integration", "Equivalents": "AI, Chatbot, Assistant", From c77a2dd3f266dd210325c92536c9adffd4bd0a26 Mon Sep 17 00:00:00 2001 From: Diana Gromova <16adianay@gmail.com> Date: Thu, 3 Sep 2026 16:32:29 +0400 Subject: [PATCH 2/7] Fixed failed tests - Add aria-label to CheckBoxes - Add etalons - Fux quotes --- .../CustomAIAssistant/jQuery/ai-service.js | 14 +- .../CustomAIAssistant/jQuery/chat-router.js | 110 +++++------ .../Chat/CustomAIAssistant/jQuery/data.js | 94 +++++----- .../CustomAIAssistant/jQuery/form-commands.js | 30 +-- .../CustomAIAssistant/jQuery/grid-commands.js | 78 ++++---- .../Chat/CustomAIAssistant/jQuery/index.html | 2 +- .../Chat/CustomAIAssistant/jQuery/index.js | 171 +++++++++--------- apps/demos/eslint.config.mjs | 12 ++ ...-CustomAIAssistant (fluent.blue.light).png | Bin 0 -> 70581 bytes ...ustomAIAssistant (material.blue.light).png | Bin 0 -> 68270 bytes 10 files changed, 264 insertions(+), 247 deletions(-) create mode 100644 apps/demos/testing/etalons/Chat-CustomAIAssistant (fluent.blue.light).png create mode 100644 apps/demos/testing/etalons/Chat-CustomAIAssistant (material.blue.light).png diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/ai-service.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/ai-service.js index 844db2d9c648..5571107b6ff5 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/ai-service.js +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/ai-service.js @@ -24,15 +24,15 @@ function createAiIntegration() { async function getAIResponseRecursive(messages, signal) { return getAIResponse(messages, signal).catch(async (error) => { - if (!error.message.includes("Connection error")) { + if (!error.message.includes('Connection error')) { throw error; } DevExpress.ui.notify({ message: - "Our demo AI service reached a temporary request limit. Retrying in 30 seconds.", - width: "auto", - type: "error", + 'Our demo AI service reached a temporary request limit. Retrying in 30 seconds.', + width: 'auto', + type: 'error', displayTime: 5000, }); @@ -49,7 +49,7 @@ function createAiIntegration() { return { promise: Promise.reject( new ChatCommandError( - "❌ This message is too long for me to process. Please shorten it and try again.", + '❌ This message is too long for me to process. Please shorten it and try again.', ), ), abort: () => {}, @@ -60,8 +60,8 @@ function createAiIntegration() { const isSmartPasteRequest = Array.isArray(data?.fields); const system = isSmartPasteRequest - ? `${prompt.system ?? ""} IMPORTANT: reply on a SINGLE line with no line breaks of any kind - use ";;;" as the only separator between fields.` - : (prompt.system ?? ""); + ? `${prompt.system ?? ''} IMPORTANT: reply on a SINGLE line with no line breaks of any kind - use ';;;' as the only separator between fields.` + : (prompt.system ?? ''); const aiPrompt = [ { role: "system", content: system }, diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js index 0a6fde992ad8..958e0ff849a7 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js @@ -7,7 +7,7 @@ function extractJson(text) { return JSON.parse(match[0]); } catch { throw new ChatCommandError( - "❌ I received an unexpected response from the AI service. Please rephrase your request and try again.", + '❌ I received an unexpected response from the AI service. Please rephrase your request and try again.', ); } } @@ -32,71 +32,71 @@ function executeAiCommand(text, aiIntegration) { function buildGridSystemPrompt(columnNames) { return [ - "You control a task DataGrid on this page.", - "This page ALSO has a separate employee/customer profile form (fields like name, title/prefix, " + - "position, state, birth date) that is handled elsewhere - it is NOT part of this grid.", - "Figure out what the user's request is about and translate ONLY the part that is clearly about " + - "the task grid into the matching commands described below.", - "Do NOT create a grid action just because a value could technically fit a text column (e.g. " + - '"Subject"). If the request is about the profile form (e.g. mentions a person\'s name, title, ' + - 'job position, state, or birth date), leave that part out of "actions" entirely - even if no ' + - "other part of the request is grid-related.", - "", + 'You control a task DataGrid on this page.', + 'This page ALSO has a separate employee/customer profile form (fields like name, title/prefix, ' + + 'position, state, birth date) that is handled elsewhere - it is NOT part of this grid.', + 'Figure out what the user\'s request is about and translate ONLY the part that is clearly about ' + + 'the task grid into the matching commands described below.', + 'Do NOT create a grid action just because a value could technically fit a text column (e.g. ' + + "'Subject'). If the request is about the profile form (e.g. mentions a person's name, title, " + + "job position, state, or birth date), leave that part out of 'actions' entirely - even if no " + + 'other part of the request is grid-related.', + '', buildGridPromptSection(columnNames), - "", - "Respond with STRICT JSON only, no code fences, no explanations, matching this schema:", + '', + 'Respond with STRICT JSON only, no code fences, no explanations, matching this schema:', JSON.stringify(buildGridResponseSchema()), - "", - 'If the request has nothing to do with the grid, respond with "actions": [].', - ].join("\n"); + '', + "If the request has nothing to do with the grid, respond with 'actions': [].", + ].join('\n'); } const FIELD_OR_VALUE_NOT_FOUND_MESSAGE = - "❌ No field or column exists with such a name, or the entered value is invalid. Please check the name and value and try again."; + '❌ No field or column exists with such a name, or the entered value is invalid. Please check the name and value and try again.'; const MAX_USER_MESSAGE_LENGTH = 2000; -const ROUTER_TARGETS = new Set(["form", "grid", "mixed", "none"]); -const FORM_ACTION_TYPES = new Set(["clear_field", "clear_all", "smart_paste"]); +const ROUTER_TARGETS = new Set(['form', 'grid', 'mixed', 'none']); +const FORM_ACTION_TYPES = new Set(['clear_field', 'clear_all', 'smart_paste']); function buildFormActionPromptSection(form) { const fieldList = getFormFieldOptions(form) .map((f) => `${f.dataField} (${f.label})`) - .join(", "); + .join(', '); return [ `Form fields (dataField and label): ${fieldList}.`, - 'If the request is about the form, also set "formAction" to one of:', - '- {"type": "clear_field", "field": ""} to clear one specific field.', - '- {"type": "clear_all"} to clear/reset the whole form.', - '- {"type": "smart_paste"} to fill in form data from the request text.', - 'Set "formAction" to null if the request is not about the form.', - ].join("\n"); + "If the request is about the form, also set 'formAction' to one of:", + "- {'type': 'clear_field', 'field': ''} to clear one specific field.", + "- {'type': 'clear_all'} to clear/reset the whole form.", + "- {'type': 'smart_paste'} to fill in form data from the request text.", + "Set 'formAction' to null if the request is not about the form.", + ].join('\n'); } async function classifyRequest(text, aiIntegration, form) { if (!aiIntegration) { - return { target: "mixed", formAction: null }; + return { target: 'mixed', formAction: null }; } const prompt = [ - "Decide which UI area should handle the user's request.", - "Return STRICT JSON only, without markdown fences.", - 'Format: {"target": "form" | "grid" | "mixed" | "none", "formAction": | null, "reason": "short explanation" }', - "Rules:", - "- Use form for profile/customer form updates, field clearing, or smart-paste style data entry.", - "- Use grid for sorting, filtering, showing/hiding columns, or other DataGrid tasks.", - "- Use mixed when the request clearly asks for both a form change and a grid change together.", - "- Use none when the request is unrelated to both areas.", - "If you are not confident, return mixed.", - "", + 'Decide which UI area should handle the user\'s request.', + 'Return STRICT JSON only, without markdown fences.', + "Format: {'target': 'form' | 'grid' | 'mixed' | 'none', 'formAction': | null, 'reason': 'short explanation' }", + 'Rules:', + '- Use form for profile/customer form updates, field clearing, or smart-paste style data entry.', + '- Use grid for sorting, filtering, showing/hiding columns, or other DataGrid tasks.', + '- Use mixed when the request clearly asks for both a form change and a grid change together.', + '- Use none when the request is unrelated to both areas.', + 'If you are not confident, return mixed.', + '', buildFormActionPromptSection(form), - "", - `User request: "${text}"`, - ].join("\n"); + '', + `User request: '${text}'`, + ].join('\n'); try { const parsed = await executeAiCommand(prompt, aiIntegration); - const target = String(parsed?.target ?? "mixed") + const target = String(parsed?.target ?? 'mixed') .trim() .toLowerCase(); const rawFormAction = parsed?.formAction; @@ -106,17 +106,17 @@ async function classifyRequest(text, aiIntegration, form) { : null; return { - target: ROUTER_TARGETS.has(target) ? target : "mixed", + target: ROUTER_TARGETS.has(target) ? target : 'mixed', formAction, }; } catch { - return { target: "mixed", formAction: null }; + return { target: 'mixed', formAction: null }; } } function buildGridResultsPromise(gridInstance, aiIntegration, text) { const columnNames = getGridColumnNames(gridInstance); - const prompt = `${buildGridSystemPrompt(columnNames)}\n\nUser request: "${text}"`; + const prompt = `${buildGridSystemPrompt(columnNames)}\n\nUser request: '${text}'`; return executeAiCommand(prompt, aiIntegration) .then((parsed) => { @@ -155,19 +155,19 @@ function buildFormResultsPromise(form, formAction, text) { } function formatFailures(failed) { - return failed.map((message) => `❌ ${message}`).join("\n"); + return failed.map((message) => `❌ ${message}`).join('\n'); } function formatSucceeded(succeeded) { - return succeeded.map((message) => `✅ Done. ${message}`).join("\n"); + return succeeded.map((message) => `✅ Done. ${message}`).join('\n'); } function joinSucceededOrThrow(results, fallbackError) { const succeeded = results - .filter((r) => r.status === "success") + .filter((r) => r.status === 'success') .map((r) => r.message); const failed = results - .filter((r) => r.status === "failure") + .filter((r) => r.status === 'failure') .map((r) => r.message); if (succeeded.length === 0) { @@ -185,7 +185,7 @@ async function runCommand(text, { form, gridInstance, aiIntegration }) { if (text.length > MAX_USER_MESSAGE_LENGTH) { return Promise.reject( new ChatCommandError( - "❌ This message is too long for me to process. Please shorten it and try again.", + '❌ This message is too long for me to process. Please shorten it and try again.', ), ); } @@ -196,14 +196,14 @@ async function runCommand(text, { form, gridInstance, aiIntegration }) { form, ); - if (target === "form") { + if (target === 'form') { const { results: formResults, error: formError } = await buildFormResultsPromise(form, formAction, text); return joinSucceededOrThrow(formResults, formError); } - if (target === "grid") { + if (target === 'grid') { gridInstance?.beginCustomLoading(); const { results: gridResults, error: gridError } = @@ -222,14 +222,14 @@ async function runCommand(text, { form, gridInstance, aiIntegration }) { if (gridError) { console.warn( - "DataGrid AI request failed, but the Form request may have succeeded:", + 'DataGrid AI request failed, but the Form request may have succeeded:', gridError, ); } if (formError) { console.warn( - "Form AI request failed, but the DataGrid request may have succeeded:", + 'Form AI request failed, but the DataGrid request may have succeeded:', formError, ); } @@ -244,7 +244,7 @@ function reportAiResult(promise, pushMessage) { return promise .then((message) => { pushMessage({ - author: { id: "ai", name: "AI Assistant" }, + author: { id: 'ai', name: 'AI Assistant' }, text: message, }); }) @@ -252,7 +252,7 @@ function reportAiResult(promise, pushMessage) { const text = error instanceof ChatCommandError ? error.message - : "❌ I couldn't reach the AI service. Please check your connection and try again."; + : '❌ I couldn\'t reach the AI service. Please check your connection and try again.'; pushMessage({ author: { id: "ai", name: "AI Assistant" }, diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/data.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/data.js index bc5c56febde6..c0a181887d6f 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/data.js +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/data.js @@ -1,86 +1,86 @@ const CLASSES = { - clearChatButton: "ai-chat-clear-button", + clearChatButton: 'ai-chat-clear-button', }; -const deployment = "demo-mini"; -const apiVersion = "2024-02-01"; -const endpoint = "https://public-api.devexpress.com/demo-openai"; -const apiKey = "DEMO"; +const deployment = 'demo-mini'; +const apiVersion = '2024-02-01'; +const endpoint = 'https://public-api.devexpress.com/demo-openai'; +const apiKey = 'DEMO'; -const emptyViewMessage = "How can I help with this page?"; +const emptyViewMessage = 'How can I help with this page?'; const emptyViewPrompt = - "Update employee Form fields.\nFilter or sort tasks, display or hide DataGrid columns, or clear all filters and sorting." + 'Update employee Form fields.\nFilter or sort tasks, display or hide DataGrid columns, or clear all filters and sorting.'; -const titles = ["Mr.", "Mrs.", "Ms."]; -const colors = { High: "#F1BBBC", Normal: "#F9E2AE", Low: "#9FD89F" }; -const states = ["California", "New York", "Texas"]; +const titles = ['Mr.', 'Mrs.', 'Ms.']; +const colors = { High: '#F1BBBC', Normal: '#F9E2AE', Low: '#9FD89F' }; +const states = ['California', 'New York', 'Texas']; const positions = [ - "CEO", - "Sales Assistant", - "CMO", - "Manager", - "Designer", - "Developer", + 'CEO', + 'Sales Assistant', + 'CMO', + 'Manager', + 'Designer', + 'Developer', ]; const employee = { ID: 1, - Prefix: "Mr.", - FirstName: "John", - LastName: "Heart", - Position: "CEO", - State: "California", - BirthDate: "1964/03/16", + Prefix: 'Mr.', + FirstName: 'John', + LastName: 'Heart', + Position: 'CEO', + State: 'California', + BirthDate: '1964/03/16', }; const tasks = [ { ID: 5, - Subject: "Choose between PPO and HMO Health Plan", - StartDate: "2026/02/15", - DueDate: "2026/04/15", - Status: "In Progress", - Priority: "Low", + Subject: 'Choose between PPO and HMO Health Plan', + StartDate: '2026/02/15', + DueDate: '2026/04/15', + Status: 'In Progress', + Priority: 'Low', Completion: 75, EmployeeID: 1, }, { ID: 6, - Subject: "Google AdWords Strategy", - StartDate: "2026/02/16", - DueDate: "2026/02/28", - Status: "Completed", - Priority: "High", + Subject: 'Google AdWords Strategy', + StartDate: '2026/02/16', + DueDate: '2026/02/28', + Status: 'Completed', + Priority: 'High', Completion: 100, EmployeeID: 1, }, { ID: 7, - Subject: "New Brochures", - StartDate: "2026/02/17", - DueDate: "2026/02/24", - Status: "Completed", - Priority: "Normal", + Subject: 'New Brochures', + StartDate: '2026/02/17', + DueDate: '2026/02/24', + Status: 'Completed', + Priority: 'Normal', Completion: 100, EmployeeID: 1, }, { ID: 22, - Subject: "Update NDA Agreement", - StartDate: "2026/03/14", - DueDate: "2026/03/16", - Status: "Completed", - Priority: "High", + Subject: 'Update NDA Agreement', + StartDate: '2026/03/14', + DueDate: '2026/03/16', + Status: 'Completed', + Priority: 'High', Completion: 100, EmployeeID: 1, }, { ID: 52, - Subject: "Review Product Recall Report by Engineering Team", - StartDate: "2026/05/17", - DueDate: "2026/05/20", - Status: "Completed", - Priority: "High", + Subject: 'Review Product Recall Report by Engineering Team', + StartDate: '2026/05/17', + DueDate: '2026/05/20', + Status: 'Completed', + Priority: 'High', Completion: 100, EmployeeID: 1, }, diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/form-commands.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/form-commands.js index 9be2a171b732..4d7e58ce75f9 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/form-commands.js +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/form-commands.js @@ -1,7 +1,7 @@ const SMART_PASTE_TIMEOUT_MS = 30000; function getFormFieldOptions(form) { - return (form.option("items") || []) + return (form.option('items') || []) .filter((item) => item.dataField) .map((item) => ({ dataField: item.dataField, @@ -10,35 +10,35 @@ function getFormFieldOptions(form) { } function applyFormClearAction(form, formAction) { - if (!formAction || formAction.type === "smart_paste") return null; + if (!formAction || formAction.type === 'smart_paste') return null; - if (formAction.type === "clear_all") { + if (formAction.type === 'clear_all') { try { form.clear(); - return { status: "success", message: "Cleared all Form fields." }; + return { status: 'success', message: 'Cleared all Form fields.' }; } catch { return { - status: "failure", + status: 'failure', message: "I couldn't clear the form.", }; } } - if (formAction.type === "clear_field") { + if (formAction.type === 'clear_field') { const isKnownField = getFormFieldOptions(form).some( (f) => f.dataField === formAction.field, ); if (!isKnownField) { return { - status: "failure", - message: `I couldn't find a field named "${formAction.field}" to clear.`, + status: 'failure', + message: `I couldn't find a field named '${formAction.field}' to clear.`, }; } form.updateData(formAction.field, null); - return { status: "success", message: `Cleared ${formAction.field}.` }; + return { status: 'success', message: `Cleared ${formAction.field}.` }; } return null; @@ -52,7 +52,7 @@ function applyFormSmartPaste(form, text) { if (settled) return; settled = true; clearTimeout(timeoutId); - form.off("smartPasted", handleSmartPasted); + form.off('smartPasted', handleSmartPasted); resolve(result); }; @@ -60,9 +60,9 @@ function applyFormSmartPaste(form, text) { const fieldCount = Object.keys(e.aiResult ?? {}).length; finish( fieldCount > 0 - ? { status: "success", message: "Updated the form." } + ? { status: 'success', message: 'Updated the form.' } : { - status: "failure", + status: 'failure', message: "I couldn't find any Form fields matching the request.", }, ); @@ -70,18 +70,18 @@ function applyFormSmartPaste(form, text) { const timeoutId = setTimeout(() => { finish({ - status: "failure", + status: 'failure', message: "I couldn't process your request. Please try rephrasing it.", }); }, SMART_PASTE_TIMEOUT_MS); - form.on("smartPasted", handleSmartPasted); + form.on('smartPasted', handleSmartPasted); try { form.smartPaste(text); } catch { finish({ - status: "failure", + status: 'failure', message: "I couldn't process your request. Please try rephrasing it.", }); } diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js index 5005da420a71..08b74e4e24e7 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js @@ -1,18 +1,18 @@ const gridCommands = { filterValue: { description: - "Apply a filter to a single column. Pass column (dataField), operator, and value. " + + 'Apply a filter to a single column. Pass column (dataField), operator, and value. ' + 'Supported operators: "=", "<>", "<", "<=", ">", ">=", "contains", "notcontains", "startswith", "endswith", "anyof". ' + 'Date values must be in "YYYY-MM-DDTHH:mm:ss" format (e.g. "2024-05-10T00:00:00"). ' + 'The "Completion" column is a boolean (task completed or not): use operator "=" with value true for ' + - "completed tasks, or value false for tasks that are not completed. " + + 'completed tasks, or value false for tasks that are not completed. ' + "To filter a date column by a year and/or month (the same thing the grid's own header filter does " + 'when you pick a year then a month), use operator "anyof" with value as an array of one or more ' + 'strings in "YYYY" (whole year, e.g. "2023") or "YYYY/M" (whole month, month is 1-12 with no leading ' + 'zero, e.g. "2023/5" for May 2023) format, e.g. {"column": "DueDate", "operator": "anyof", "value": ' + '["2023/5"]} for "May 2023". Only use "anyof" when the year is known; if the year is missing and ' + 'cannot be inferred from elsewhere in the request (e.g. plain "in May" with no year anywhere), do ' + - "not guess it - omit this action entirely instead of adding it with a made-up year.", + 'not guess it - omit this action entirely instead of adding it with a made-up year.', schema: { type: "object", properties: { @@ -49,16 +49,16 @@ const gridCommands = { const caption = column.caption ?? args.column; let { value } = args; - if (args.column === "Completion" && typeof value !== "boolean") { + if (args.column === 'Completion' && typeof value !== 'boolean') { const normalized = String(value).trim().toLowerCase(); value = value === 100 || - ["true", "completed", "yes", "100"].includes(normalized); + ['true', 'completed', 'yes', '100'].includes(normalized); } if ( - (column.dataType === "date" || column.dataType === "datetime") && - typeof value === "string" + (column.dataType === 'date' || column.dataType === 'datetime') && + typeof value === 'string' ) { const parsedDate = new Date(value); if (!Number.isNaN(parsedDate.getTime())) { @@ -66,7 +66,7 @@ const gridCommands = { } } - if (args.operator === "anyof" && Array.isArray(value)) { + if (args.operator === 'anyof' && Array.isArray(value)) { const mentionedYears = new Set( String(rawText ?? "").match(/\b\d{4}\b/g), ); @@ -76,22 +76,22 @@ const gridCommands = { if (hasUngroundedYear) { return { - status: "failure", - message: `No field or column exists with such a name, or the entered value is invalid.`, + status: 'failure', + message: 'No field or column exists with such a name, or the entered value is invalid.', }; } } try { - grid.option("filterValue", [args.column, args.operator, value]); + grid.option('filterValue', [args.column, args.operator, value]); return { - status: "success", - message: `Filtered by "${caption}".`, + status: 'success', + message: `Filtered by '${caption}'.`, }; } catch { return { - status: "failure", - message: `I couldn't apply that filter to "${caption}". Check that the value matches the column's type.`, + status: 'failure', + message: `I couldn't apply that filter to '${caption}'. Check that the value matches the column's type.`, }; } }, @@ -103,10 +103,10 @@ const gridCommands = { execute(grid) { try { grid.clearFilter(); - return { status: "success", message: "Filter cleared." }; + return { status: 'success', message: 'Filter cleared.' }; } catch { return { - status: "failure", + status: 'failure', message: "I couldn't clear the DataGrid's filters.", }; } @@ -133,20 +133,20 @@ const gridCommands = { try { grid.columnOption( args.column, - "sortOrder", - args.sortOrder === "none" ? undefined : args.sortOrder, + 'sortOrder', + args.sortOrder === 'none' ? undefined : args.sortOrder, ); const message = - args.sortOrder === "none" - ? `Cleared sorting on "${caption}".` - : `Sorted by "${caption}" (${args.sortOrder === "asc" ? "ascending" : "descending"}).`; + args.sortOrder === 'none' + ? `Cleared sorting on '${caption}'.` + : `Sorted by '${caption}' (${args.sortOrder === 'asc' ? 'ascending' : 'descending'}).`; return { status: "success", message }; } catch { return { status: "failure", - message: `I couldn't sort by "${caption}".`, + message: `I couldn't sort by '${caption}'.`, }; } }, @@ -158,10 +158,10 @@ const gridCommands = { execute(grid) { try { grid.clearSorting(); - return { status: "success", message: "Sorting cleared." }; + return { status: 'success', message: 'Sorting cleared.' }; } catch { return { - status: "failure", + status: 'failure', message: "I couldn't clear the DataGrid's sorting.", }; } @@ -187,15 +187,15 @@ const gridCommands = { try { grid.columnOption(args.column, "visible", args.visible); return { - status: "success", + status: 'success', message: args.visible - ? `Showed column "${caption}".` - : `Hid column "${caption}".`, + ? `Showed column '${caption}'.` + : `Hid column '${caption}'.`, }; } catch { return { - status: "failure", - message: `I couldn't change the visibility of "${caption}".`, + status: 'failure', + message: `I couldn't change the visibility of '${caption}'.`, }; } }, @@ -209,8 +209,8 @@ function getColumnOrFail(grid, columnName) { return { column: null, failure: { - status: "failure", - message: `I couldn't find a DataGrid column named "${columnName}".`, + status: 'failure', + message: `I couldn't find a DataGrid column named '${columnName}'.`, }, }; } @@ -243,12 +243,12 @@ function buildGridResponseSchema() { function buildGridPromptSection(columnNames) { const commandDescriptions = Object.entries(gridCommands) - .map(([name, cmd]) => `- "${name}": ${cmd.description}`) + .map(([name, cmd]) => `- '${name}': ${cmd.description}`) .join("\n"); return [ 'GRID: translate any part of the request that affects the task grid into one or more grid commands (the "actions" array).', - `Available columns (dataField): ${columnNames.join(", ")}.`, + `Available columns (dataField): ${columnNames.join(', ')}.`, "CRITICAL RULE: a column mentioned in the request must clearly correspond to one of the available " + 'columns above (matching by meaning is fine, e.g. "due date" -> "DueDate"). If it does not - even ' + "if it superficially looks like it could be a column name - you must NOT invent or substitute the " + @@ -257,10 +257,10 @@ function buildGridPromptSection(columnNames) { "with a different, existing column just to make the action valid.", 'Example: request "filter the ZXQ column by foo" - ZXQ matches no available column, so emit ' + '{"column": "ZXQ", ...} as-is (it will correctly fail as "column not found") - do NOT emit an ' + - 'action for "Subject" or any other real column instead.', + "action for 'Subject' or any other real column instead.", 'The "Completion" column is a boolean: true means the task is completed, false means it is not. ' + - 'To filter for "completed" tasks, use {"column": "Completion", "operator": "=", "value": true}. ' + - 'To filter for "not completed" tasks, use {"column": "Completion", "operator": "=", "value": false}.', + "To filter for 'completed' tasks, use {'column': 'Completion', 'operator': '=', 'value': true}. " + + "To filter for 'not completed' tasks, use {'column': 'Completion', 'operator': '=', 'value': false}.", "Available grid commands:", commandDescriptions, ].join("\n"); @@ -276,8 +276,8 @@ function applyGridActions(grid, actions, rawText) { if (!command) { return { - status: "failure", - message: `I don't know how to do "${action.name}".`, + status: 'failure', + message: `I don't know how to do '${action.name}'.`, }; } diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.html b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.html index 11eef21e09c0..af15e1c5f4c6 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.html +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.html @@ -11,7 +11,7 @@ diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.js index 7dbefc978170..c223bc12c155 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.js +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.js @@ -1,22 +1,22 @@ $(function () { DevExpress.config({ - editorStylingMode: "filled", + editorStylingMode: 'filled', }); DevExpress.config({ floatingActionButtonConfig: { position: { - my: "right bottom", - at: "right bottom", - of: "#grid-container", - offset: "-16 -16", + my: 'right bottom', + at: 'right bottom', + of: '#grid-container', + offset: '-16 -16', }, }, }); DevExpress.localization.loadMessages({ en: { - "dxChat-textareaPlaceholder": "Enter a prompt...", + 'dxChat-textareaPlaceholder': 'Enter a prompt...', }, }); @@ -27,20 +27,20 @@ $(function () { let gridInstance; let toastInstance; - toastInstance = $("#toast") + toastInstance = $('#toast') .dxToast({ displayTime: 600, closeOnClick: true, - message: "Form data is saved.", - type: "success", + message: 'Form data is saved.', + type: 'success', position: { - of: "#form-container", - at: "bottom center", - my: "bottom center", - offset: "0 -20", + of: '#form-container', + at: 'bottom center', + my: 'bottom center', + offset: '0 -20', }, }) - .dxToast("instance"); + .dxToast('instance'); function pushMessage(message) { chatInstance @@ -48,7 +48,7 @@ $(function () { .store() .push([ { - type: "insert", + type: 'insert', data: { id: Date.now() + Math.random(), timestamp: new Date(), @@ -59,12 +59,12 @@ $(function () { } function setDisabled(disabled) { - chatInstance.option("disabled", disabled); + chatInstance.option('disabled', disabled); } function updateClearButtonState() { const items = chatInstance.getDataSource().items(); - clearButtonInstance?.option("disabled", items.length === 0); + clearButtonInstance?.option('disabled', items.length === 0); } function clearChat() { @@ -93,38 +93,38 @@ $(function () { routed.finally(finish); } - popupInstance = $("#aiAssistantContainer") + popupInstance = $('#aiAssistantContainer') .dxPopup({ - title: "AI Assistant", + title: 'AI Assistant', width: 400, - height: "90%", + height: '90%', dragEnabled: true, resizeEnabled: true, showCloseButton: true, shading: false, visible: false, onHiding() { - fabBtn.option("visible", true); + fabBtn.option('visible', true); }, onShowing() { - fabBtn.option("visible", false); + fabBtn.option('visible', false); }, position: { - my: "right top", - at: "right top", - of: ".demo-container", - offset: "-20 20", + my: 'right top', + at: 'right top', + of: '.demo-container', + offset: '-20 20', }, toolbarItems: [ { - widget: "dxButton", - toolbar: "top", - location: "after", + widget: 'dxButton', + toolbar: 'top', + location: 'after', cssClass: CLASSES.clearChatButton, options: { - icon: "clearhistory", + icon: 'clearhistory', disabled: true, - hint: "Clear chat", + hint: 'Clear chat', onClick: () => clearChat(), onInitialized: (e) => { clearButtonInstance = e.component; @@ -133,27 +133,27 @@ $(function () { }, ], contentTemplate($container) { - const $chatContainer = $("
") - .addClass("ai-chat-content") + const $chatContainer = $('
') + .addClass('ai-chat-content') .appendTo($container); $chatContainer.dxChat({ - height: "100%", + height: '100%', showAvatar: false, - width: "auto", + width: 'auto', dataSource: { - store: new DevExpress.data.ArrayStore({ key: "id" }), + store: new DevExpress.data.ArrayStore({ key: 'id' }), }, reloadOnChange: true, - user: { id: "user" }, + user: { id: 'user' }, showUserName: false, speechToTextEnabled: true, suggestions: { items: [ - { text: "Show Completed Tasks", prompt: "Show Completed Tasks" }, + { text: 'Show Completed Tasks', prompt: 'Show Completed Tasks' }, { - text: "Change State to Texas", - prompt: "Change State to Texas", + text: 'Change State to Texas', + prompt: 'Change State to Texas', }, ], onItemClick(e) { @@ -161,7 +161,7 @@ $(function () { const message = { id: Date.now() + Math.random(), timestamp: new Date(), - author: { id: "user" }, + author: { id: 'user' }, text: prompt, }; @@ -170,14 +170,14 @@ $(function () { }, }, emptyViewTemplate(_data, container) { - const $icon = $("
").addClass( - "dx-chat-messagelist-empty-image dx-ai-chat__empty-image", + const $icon = $('
').addClass( + 'dx-chat-messagelist-empty-image dx-ai-chat__empty-image', ); - const $message = $("
") - .addClass("ai-chat-empty-message") + const $message = $('
') + .addClass('ai-chat-empty-message') .text(emptyViewMessage); - const $prompt = $("
") - .addClass("ai-chat-empty-prompt") + const $prompt = $('
') + .addClass('ai-chat-empty-prompt') .html(emptyViewPrompt); $(container).append($icon).append($message).append($prompt); @@ -191,32 +191,32 @@ $(function () { }); }, }) - .dxPopup("instance"); + .dxPopup('instance'); const aiIntegration = createAiIntegration(); - formInstance = $("#form-container") + formInstance = $('#form-container') .dxForm({ formData: employee, colCount: 3, - labelLocation: "top", + labelLocation: 'top', onOptionChanged: (e) => { - if (e.name === "isDirty") { - const saveButton = formInstance.getButton("Save"); - saveButton.option("disabled", !e.value); + if (e.name === 'isDirty') { + const saveButton = formInstance.getButton('Save'); + saveButton.option('disabled', !e.value); } }, aiIntegration, items: [ { - dataField: "Prefix", - label: { text: "Title" }, - editorType: "dxSelectBox", + dataField: 'Prefix', + label: { text: 'Title' }, + editorType: 'dxSelectBox', editorOptions: { items: titles, searchEnabled: true }, aiOptions: { instruction: - "Only fill this field with one of the allowed values (Mr., Mrs., Ms.) if a " + - "title is explicitly mentioned in the text. Never use this field for any part " + + 'Only fill this field with one of the allowed values (Mr., Mrs., Ms.) if a ' + + 'title is explicitly mentioned in the text. Never use this field for any part ' + "of a person's name.", }, }, @@ -288,62 +288,67 @@ $(function () { }, ], }) - .dxForm("instance"); + .dxForm('instance'); - gridInstance = $("#grid-container") + gridInstance = $('#grid-container') .dxDataGrid({ dataSource: tasks, - keyExpr: "ID", + keyExpr: 'ID', showBorders: true, filterRow: { visible: true }, headerFilter: { visible: true }, filterSyncEnabled: true, columns: [ - { dataField: "Subject", width: 250 }, - { dataField: "StartDate", dataType: "date" }, - { dataField: "DueDate", dataType: "date" }, + { dataField: 'Subject', width: 250 }, + { dataField: 'StartDate', dataType: 'date' }, + { dataField: 'DueDate', dataType: 'date' }, { - dataField: "Priority", - caption: "Priority", + dataField: 'Priority', + caption: 'Priority', cellTemplate: (container, options) => { - $("
") + $('
') .css({ background: colors[options.value], - borderRadius: "24px", - padding: "2px 8px", - display: "inline-block", - textAlign: "center", + borderRadius: '24px', + padding: '2px 8px', + display: 'inline-block', + textAlign: 'center', }) .text(options.value) .appendTo(container); }, }, { - dataField: "Completion", - caption: "Completed", - alignment: "center", - dataType: "boolean", + dataField: 'Completion', + caption: 'Completed', + alignment: 'center', + dataType: 'boolean', + editorOptions: { + elementAttr: { + 'aria-label': 'Completed', + }, + }, calculateCellValue: (rowData) => rowData.Completion === 100, calculateFilterExpression(filterValue, selectedFilterOperation) { const wantsCompleted = - selectedFilterOperation === "<>" ? !filterValue : !!filterValue; + selectedFilterOperation === '<>' ? !filterValue : !!filterValue; const rawCompletion = (rowData) => rowData.Completion; return wantsCompleted - ? [rawCompletion, "=", 100] - : [rawCompletion, "<", 100]; + ? [rawCompletion, '=', 100] + : [rawCompletion, '<', 100]; }, }, ], }) - .dxDataGrid("instance"); + .dxDataGrid('instance'); - let fabBtn = $("#ai-fab") + let fabBtn = $('#ai-fab') .dxSpeedDialAction({ - icon: "sparkle", - label: "AI Assistant", + icon: 'sparkle', + label: 'AI Assistant', onClick: function (e) { popupInstance.toggle(); }, }) - .dxSpeedDialAction("instance"); + .dxSpeedDialAction('instance'); }); diff --git a/apps/demos/eslint.config.mjs b/apps/demos/eslint.config.mjs index 393ce938ab60..142fb68f8dc6 100644 --- a/apps/demos/eslint.config.mjs +++ b/apps/demos/eslint.config.mjs @@ -316,6 +316,18 @@ export default [ 'no-unused-vars': 0, }, }, + { + files: [ + 'Demos/Chat/CustomAIAssistant/jQuery/ai-service.js', + 'Demos/Chat/CustomAIAssistant/jQuery/chat-router.js', + 'Demos/Chat/CustomAIAssistant/jQuery/form-commands.js', + 'Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js', + ], + rules: { + 'no-undef': 0, + 'no-unused-vars': 0, + }, + }, // React demos { diff --git a/apps/demos/testing/etalons/Chat-CustomAIAssistant (fluent.blue.light).png b/apps/demos/testing/etalons/Chat-CustomAIAssistant (fluent.blue.light).png new file mode 100644 index 0000000000000000000000000000000000000000..9bcb47a4fe71b02a67bf7e043f1f9c284dcc6e53 GIT binary patch literal 70581 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 literal 0 HcmV?d00001 diff --git a/apps/demos/testing/etalons/Chat-CustomAIAssistant (material.blue.light).png b/apps/demos/testing/etalons/Chat-CustomAIAssistant (material.blue.light).png new file mode 100644 index 0000000000000000000000000000000000000000..2413725f1106d4b2cb2862b4f0021a45c8d6063e GIT binary patch literal 68270 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 literal 0 HcmV?d00001 From 55b5ceeae1fb812a85961ec5bdb4d6cc004a807d Mon Sep 17 00:00:00 2001 From: Diana Gromova <16adianay@gmail.com> Date: Thu, 3 Sep 2026 17:15:15 +0400 Subject: [PATCH 3/7] Update CSP config --- apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.html | 2 +- apps/demos/utils/server/csp-server.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.html b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.html index af15e1c5f4c6..11eef21e09c0 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.html +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.html @@ -11,7 +11,7 @@ diff --git a/apps/demos/utils/server/csp-server.js b/apps/demos/utils/server/csp-server.js index 5b2243644fd6..1994de2cafe6 100644 --- a/apps/demos/utils/server/csp-server.js +++ b/apps/demos/utils/server/csp-server.js @@ -140,6 +140,9 @@ const CSP_DEMO_ALLOWLIST = { 'Chat/AIAndChatbotIntegration': { 'connect-src': DEMO_OPENAI_CONNECT_SRC, }, + 'Chat/CustomAIAssistant': { + 'connect-src': DEMO_OPENAI_CONNECT_SRC, + }, 'Chat/MessageStreaming': { 'connect-src': DEMO_OPENAI_CONNECT_SRC, }, @@ -210,6 +213,9 @@ const CSP_FRAMEWORK_ALLOWLIST = { 'Chat/AIAndChatbotIntegration': { 'script-src': ["'unsafe-inline'"], }, + 'Chat/CustomAIAssistant': { + 'script-src': ["'unsafe-inline'"], + }, 'Chat/MessageStreaming': { 'script-src': ["'unsafe-inline'"], }, From c028a8ab85b8914bb91538fcef2783ebb00b9a1a Mon Sep 17 00:00:00 2001 From: Diana Gromova <16adianay@gmail.com> Date: Thu, 3 Sep 2026 17:43:26 +0400 Subject: [PATCH 4/7] Fix lint errors --- .../CustomAIAssistant/jQuery/ai-service.js | 4 +- .../CustomAIAssistant/jQuery/chat-router.js | 3 +- .../CustomAIAssistant/jQuery/form-commands.js | 8 +- .../CustomAIAssistant/jQuery/grid-commands.js | 108 +++++++++--------- .../Chat/CustomAIAssistant/jQuery/index.js | 58 +++++----- 5 files changed, 89 insertions(+), 92 deletions(-) diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/ai-service.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/ai-service.js index 5571107b6ff5..be820653865d 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/ai-service.js +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/ai-service.js @@ -64,8 +64,8 @@ function createAiIntegration() { : (prompt.system ?? ''); const aiPrompt = [ - { role: "system", content: system }, - { role: "user", content: prompt.user }, + { role: 'system', content: system }, + { role: 'user', content: prompt.user }, ]; const promise = getAIResponseRecursive(aiPrompt, signal); diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js index 958e0ff849a7..5999d359c59a 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js @@ -120,7 +120,6 @@ function buildGridResultsPromise(gridInstance, aiIntegration, text) { return executeAiCommand(prompt, aiIntegration) .then((parsed) => { - const actions = Array.isArray(parsed.actions) ? parsed.actions : []; if (actions.length === 0) { @@ -255,7 +254,7 @@ function reportAiResult(promise, pushMessage) { : '❌ I couldn\'t reach the AI service. Please check your connection and try again.'; pushMessage({ - author: { id: "ai", name: "AI Assistant" }, + author: { id: 'ai', name: 'AI Assistant' }, text, }); }); diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/form-commands.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/form-commands.js index 4d7e58ce75f9..10ae5ff75828 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/form-commands.js +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/form-commands.js @@ -60,11 +60,11 @@ function applyFormSmartPaste(form, text) { const fieldCount = Object.keys(e.aiResult ?? {}).length; finish( fieldCount > 0 - ? { status: 'success', message: 'Updated the form.' } + ? { status: 'success', message: 'Updated the form.' } : { - status: 'failure', - message: "I couldn't find any Form fields matching the request.", - }, + status: 'failure', + message: "I couldn't find any Form fields matching the request.", + }, ); }; diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js index 08b74e4e24e7..59d65e82d152 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js @@ -14,33 +14,33 @@ const gridCommands = { 'cannot be inferred from elsewhere in the request (e.g. plain "in May" with no year anywhere), do ' + 'not guess it - omit this action entirely instead of adding it with a made-up year.', schema: { - type: "object", + type: 'object', properties: { - column: { type: "string" }, + column: { type: 'string' }, operator: { - type: "string", + type: 'string', enum: [ - "=", - "<>", - "<", - "<=", - ">", - ">=", - "contains", - "notcontains", - "startswith", - "endswith", - "anyof", + '=', + '<>', + '<', + '<=', + '>', + '>=', + 'contains', + 'notcontains', + 'startswith', + 'endswith', + 'anyof', ], }, value: { anyOf: [ - { type: ["string", "number", "boolean"] }, - { type: "array", items: { type: "string" } }, + { type: ['string', 'number', 'boolean'] }, + { type: 'array', items: { type: 'string' } }, ], }, }, - required: ["column", "operator", "value"], + required: ['column', 'operator', 'value'], }, execute(grid, args, rawText) { const { column, failure } = getColumnOrFail(grid, args.column); @@ -68,13 +68,13 @@ const gridCommands = { if (args.operator === 'anyof' && Array.isArray(value)) { const mentionedYears = new Set( - String(rawText ?? "").match(/\b\d{4}\b/g), + String(rawText ?? '').match(/\b\d{4}\b/g), ); - const hasUngroundedYear = value.some( - (entry) => !mentionedYears.has(String(entry).split("/")[0]), + const hasUnrecognizedYear = value.some( + (entry) => !mentionedYears.has(String(entry).split('/')[0]), ); - if (hasUngroundedYear) { + if (hasUnrecognizedYear) { return { status: 'failure', message: 'No field or column exists with such a name, or the entered value is invalid.', @@ -98,8 +98,8 @@ const gridCommands = { }, clearFilter: { - description: "Clear all filters on the grid.", - schema: { type: "object", properties: {} }, + description: 'Clear all filters on the grid.', + schema: { type: 'object', properties: {} }, execute(grid) { try { grid.clearFilter(); @@ -117,12 +117,12 @@ const gridCommands = { description: 'Sort a column ascending or descending. Pass sortOrder "none" to remove sorting from this column only.', schema: { - type: "object", + type: 'object', properties: { - column: { type: "string" }, - sortOrder: { type: "string", enum: ["asc", "desc", "none"] }, + column: { type: 'string' }, + sortOrder: { type: 'string', enum: ['asc', 'desc', 'none'] }, }, - required: ["column", "sortOrder"], + required: ['column', 'sortOrder'], }, execute(grid, args) { const { column, failure } = getColumnOrFail(grid, args.column); @@ -142,10 +142,10 @@ const gridCommands = { ? `Cleared sorting on '${caption}'.` : `Sorted by '${caption}' (${args.sortOrder === 'asc' ? 'ascending' : 'descending'}).`; - return { status: "success", message }; + return { status: 'success', message }; } catch { return { - status: "failure", + status: 'failure', message: `I couldn't sort by '${caption}'.`, }; } @@ -153,8 +153,8 @@ const gridCommands = { }, clearSorting: { - description: "Remove sorting from all columns.", - schema: { type: "object", properties: {} }, + description: 'Remove sorting from all columns.', + schema: { type: 'object', properties: {} }, execute(grid) { try { grid.clearSorting(); @@ -169,14 +169,14 @@ const gridCommands = { }, columnsVisibility: { - description: "Show or hide a column.", + description: 'Show or hide a column.', schema: { - type: "object", + type: 'object', properties: { - column: { type: "string" }, - visible: { type: "boolean" }, + column: { type: 'string' }, + visible: { type: 'boolean' }, }, - required: ["column", "visible"], + required: ['column', 'visible'], }, execute(grid, args) { const { column, failure } = getColumnOrFail(grid, args.column); @@ -185,7 +185,7 @@ const gridCommands = { const caption = column.caption ?? args.column; try { - grid.columnOption(args.column, "visible", args.visible); + grid.columnOption(args.column, 'visible', args.visible); return { status: 'success', message: args.visible @@ -219,51 +219,51 @@ function getColumnOrFail(grid, columnName) { } function buildGridResponseSchema() { - const branches = Object.entries(gridCommands).map(([name, cmd]) => ({ - type: "object", + const branches = Object.entries(gridCommands).map(([name, command]) => ({ + type: 'object', properties: { - name: { type: "string", enum: [name] }, - args: cmd.schema, + name: { type: 'string', enum: [name] }, + args: command.schema, }, - required: ["name", "args"], + required: ['name', 'args'], })); return { - type: "object", + type: 'object', properties: { actions: { - type: "array", - description: "List of grid commands to execute, in order.", + type: 'array', + description: 'List of grid commands to execute, in order.', items: { anyOf: branches }, }, }, - required: ["actions"], + required: ['actions'], }; } function buildGridPromptSection(columnNames) { const commandDescriptions = Object.entries(gridCommands) - .map(([name, cmd]) => `- '${name}': ${cmd.description}`) - .join("\n"); + .map(([name, command]) => `- '${name}': ${command.description}`) + .join('\n'); return [ 'GRID: translate any part of the request that affects the task grid into one or more grid commands (the "actions" array).', `Available columns (dataField): ${columnNames.join(', ')}.`, - "CRITICAL RULE: a column mentioned in the request must clearly correspond to one of the available " + + 'CRITICAL RULE: a column mentioned in the request must clearly correspond to one of the available ' + 'columns above (matching by meaning is fine, e.g. "due date" -> "DueDate"). If it does not - even ' + - "if it superficially looks like it could be a column name - you must NOT invent or substitute the " + - "closest-sounding available column. Instead, still emit the action using the column name exactly " + + 'if it superficially looks like it could be a column name - you must NOT invent or substitute the ' + + 'closest-sounding available column. Instead, still emit the action using the column name exactly ' + "as written in the request, so the app can report that the column wasn't found - never replace it " + - "with a different, existing column just to make the action valid.", + 'with a different, existing column just to make the action valid.', 'Example: request "filter the ZXQ column by foo" - ZXQ matches no available column, so emit ' + '{"column": "ZXQ", ...} as-is (it will correctly fail as "column not found") - do NOT emit an ' + "action for 'Subject' or any other real column instead.", 'The "Completion" column is a boolean: true means the task is completed, false means it is not. ' + "To filter for 'completed' tasks, use {'column': 'Completion', 'operator': '=', 'value': true}. " + "To filter for 'not completed' tasks, use {'column': 'Completion', 'operator': '=', 'value': false}.", - "Available grid commands:", + 'Available grid commands:', commandDescriptions, - ].join("\n"); + ].join('\n'); } function getGridColumnNames(gridInstance) { diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.js index c223bc12c155..91b472d76d85 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.js +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.js @@ -1,4 +1,4 @@ -$(function () { +$(() => { DevExpress.config({ editorStylingMode: 'filled', }); @@ -21,13 +21,11 @@ $(function () { }); let chatInstance; - let popupInstance; let clearButtonInstance; let formInstance; let gridInstance; - let toastInstance; - toastInstance = $('#toast') + const toastInstance = $('#toast') .dxToast({ displayTime: 600, closeOnClick: true, @@ -93,7 +91,7 @@ $(function () { routed.finally(finish); } - popupInstance = $('#aiAssistantContainer') + const popupInstance = $('#aiAssistantContainer') .dxPopup({ title: 'AI Assistant', width: 400, @@ -221,66 +219,66 @@ $(function () { }, }, { - dataField: "FirstName", - label: { text: "First Name" }, + dataField: 'FirstName', + label: { text: 'First Name' }, aiOptions: { instruction: "Only fill this field if the text clearly refers to a person's given name. " + - "Never use grid/task-related words like Subject, Priority, Status, Due Date, " + - "Completion, or generic verbs like sort/filter/show as a name.", + 'Never use grid/task-related words like Subject, Priority, Status, Due Date, ' + + 'Completion, or generic verbs like sort/filter/show as a name.', }, }, { - dataField: "LastName", - label: { text: "Last Name" }, + dataField: 'LastName', + label: { text: 'Last Name' }, aiOptions: { instruction: "If the text gives a full person name (e.g. 'customer name', 'employee name') " + - "without separately labeled first/last names, use only the first word as First " + - "Name and the rest of the name as Last Name.", + 'without separately labeled first/last names, use only the first word as First ' + + 'Name and the rest of the name as Last Name.', }, }, { - dataField: "Position", - editorType: "dxSelectBox", + dataField: 'Position', + editorType: 'dxSelectBox', editorOptions: { items: positions, searchEnabled: true }, aiOptions: { instruction: - "Only fill this field with one of the allowed job position values if the text " + + 'Only fill this field with one of the allowed job position values if the text ' + "explicitly refers to the employee's own job title/role.", }, }, { - dataField: "State", - editorType: "dxSelectBox", + dataField: 'State', + editorType: 'dxSelectBox', editorOptions: { items: states, searchEnabled: true }, aiOptions: { instruction: - "Only fill this field with one of the allowed US state values if the text " + + 'Only fill this field with one of the allowed US state values if the text ' + "explicitly refers to the employee's home/office state", }, }, { - dataField: "BirthDate", - editorType: "dxDateBox", - editorOptions: { displayFormat: "M/d/yyyy" }, + dataField: 'BirthDate', + editorType: 'dxDateBox', + editorOptions: { displayFormat: 'M/d/yyyy' }, aiOptions: { instruction: "Only fill this field if the text explicitly refers to the employee's own birth " + - "date or date of birth.", + 'date or date of birth.', }, }, { - itemType: "button", - name: "Save", + itemType: 'button', + name: 'Save', colSpan: 3, - cssClass: "save-button", + cssClass: 'save-button', buttonOptions: { - text: "Save", - type: "default", + text: 'Save', + type: 'default', disabled: true, useSubmitBehavior: true, - width: "120px", + width: '120px', onClick: () => { toastInstance.show(); }, @@ -346,7 +344,7 @@ $(function () { .dxSpeedDialAction({ icon: 'sparkle', label: 'AI Assistant', - onClick: function (e) { + onClick() { popupInstance.toggle(); }, }) From 8c00e93482149fc096f36790f5c5f81d28c9df29 Mon Sep 17 00:00:00 2001 From: Diana Gromova <16adianay@gmail.com> Date: Thu, 3 Sep 2026 18:47:11 +0400 Subject: [PATCH 5/7] Fix Copilot suggestions --- .../Demos/Chat/CustomAIAssistant/jQuery/chat-router.js | 6 ++++++ .../Demos/Chat/CustomAIAssistant/jQuery/form-commands.js | 6 ++++++ .../Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js | 2 +- apps/demos/Demos/Chat/CustomAIAssistant/jQuery/styles.css | 4 ++-- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js index 5999d359c59a..58dcfd252a1e 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js @@ -195,6 +195,12 @@ async function runCommand(text, { form, gridInstance, aiIntegration }) { form, ); + if (target === "none") { + throw new ChatCommandError( + "❌ This request doesn't appear to be related to Form or DataGrid. Please try rephrasing it.", + ); + } + if (target === 'form') { const { results: formResults, error: formError } = await buildFormResultsPromise(form, formAction, text); diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/form-commands.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/form-commands.js index 10ae5ff75828..91fe4319b0d9 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/form-commands.js +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/form-commands.js @@ -47,6 +47,7 @@ function applyFormClearAction(form, formAction) { function applyFormSmartPaste(form, text) { return new Promise((resolve) => { let settled = false; + let timedOut = false; const finish = (result) => { if (settled) return; @@ -57,6 +58,10 @@ function applyFormSmartPaste(form, text) { }; const handleSmartPasted = (e) => { + if (timedOut) { + return; + } + const fieldCount = Object.keys(e.aiResult ?? {}).length; finish( fieldCount > 0 @@ -69,6 +74,7 @@ function applyFormSmartPaste(form, text) { }; const timeoutId = setTimeout(() => { + timedOut = true; finish({ status: 'failure', message: "I couldn't process your request. Please try rephrasing it.", diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js index 59d65e82d152..410449e9820e 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/grid-commands.js @@ -267,7 +267,7 @@ function buildGridPromptSection(columnNames) { } function getGridColumnNames(gridInstance) { - return gridInstance.getVisibleColumns().map((col) => col.dataField); + return gridInstance.option('columns').map((col) => col.dataField); } function applyGridActions(grid, actions, rawText) { diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/styles.css b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/styles.css index 2031731a987e..67dc0dc46149 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/styles.css +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/styles.css @@ -34,7 +34,7 @@ font-size: 18px; font-weight: 400; text-align: center; - color: #242424; + color: var(--dx-color-text); margin-bottom: 8px; } @@ -50,7 +50,7 @@ margin-right: 8px; } -.dx-popup-content { +#aiAssistantContainer .dx-popup-content { padding: 0; } From 9b7361869e77cc15ff890f816ea61d44b76d6792 Mon Sep 17 00:00:00 2001 From: Diana Gromova <16adianay@gmail.com> Date: Thu, 3 Sep 2026 18:55:10 +0400 Subject: [PATCH 6/7] Add a custom class to a popup wrapper --- apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.js | 3 +++ apps/demos/Demos/Chat/CustomAIAssistant/jQuery/styles.css | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.js index 91b472d76d85..9f505d5c4a8f 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.js +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/index.js @@ -94,6 +94,9 @@ $(() => { const popupInstance = $('#aiAssistantContainer') .dxPopup({ title: 'AI Assistant', + wrapperAttr: { + class: 'chat-popup', + }, width: 400, height: '90%', dragEnabled: true, diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/styles.css b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/styles.css index 67dc0dc46149..9f2f499ed6f9 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/styles.css +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/styles.css @@ -50,7 +50,7 @@ margin-right: 8px; } -#aiAssistantContainer .dx-popup-content { +.chat-popup .dx-popup-content { padding: 0; } From 8ce4563921fd44f6a445d87256ec5dc662db488a Mon Sep 17 00:00:00 2001 From: Diana Gromova <16adianay@gmail.com> Date: Thu, 3 Sep 2026 19:15:28 +0400 Subject: [PATCH 7/7] Update chat-router.js --- apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js index 58dcfd252a1e..b47be23dd6d1 100644 --- a/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js +++ b/apps/demos/Demos/Chat/CustomAIAssistant/jQuery/chat-router.js @@ -195,7 +195,7 @@ async function runCommand(text, { form, gridInstance, aiIntegration }) { form, ); - if (target === "none") { + if (target === 'none') { throw new ChatCommandError( "❌ This request doesn't appear to be related to Form or DataGrid. Please try rephrasing it.", );