| .. | ||
| icon.lua | ||
| plugin.lua | ||
| README.md | ||
Assistant Plugin Reference
This folder keeps the Lua manifest (plugin.lua) that defines a custom assistant. Treat it as the single source of truth for how AI Studio renders your assistant UI and builds the submitted prompt.
Structure
ASSISTANTis the root table. It must containTitle,Description,SystemPrompt,SubmitText,AllowProfiles, and the nestedUIdefinition.UI.Typeis always"FORM"andUI.Childrenis a list of component tables.- Each component table declares
Type, an optionalChildrenarray, and aPropstable that feeds the component’s parameters.
Supported types (matching the Blazor UI components):
TEXT_AREA: any user input field withName,Label,UserPrompt,PrefillText,IsSingleLine,ReadOnly.DROPDOWN: selects between variants;Propsmust includeName,Label,Default,Items, and optionallyValueTypeplusUserPrompt.SWITCH: boolean option; requiresName,Label,Value,LabelOn,LabelOff, and may includeUserPrompt.COLOR_PICKER: color input based onMudColorPicker; requiresName,Label, and may includePlaceholder,ShowAlpha,ShowToolbar,ShowModeSwitch,PickerVariant,UserPrompt,Class,Style.PROVIDER_SELECTION/PROFILE_SELECTION: hooks into the shared provider/profile selectors.WEB_CONTENT_READER: rendersReadWebContent; includeName,UserPrompt,Preselect,PreselectContentCleanerAgent.FILE_CONTENT_READER: rendersReadFileContent; includeName,UserPrompt.IMAGE: embeds a static illustration;Propsmust includeSrcplus optionallyAltandCaption.Srccan be an HTTP/HTTPS URL, adata:URI, or a plugin-relative path (plugin://assets/your-image.png). The runtime will convert plugin-relative paths intodata:URLs (base64).HEADING,TEXT,LIST: descriptive helpers.
Images referenced via the plugin:// scheme must exist in the plugin directory (e.g., assets/example.png). Drop the file there and point Src at it. The component will read the file at runtime, encode it as Base64, and render it inside the assistant UI.
Prompt Assembly
Each component exposes a UserPrompt string. When the assistant runs, AssistantDynamic iterates over RootComponent.Children and, for each component that has a prompt, emits:
context:
<UserPrompt>
---
user prompt:
<value extracted from the component>
For switches the “value” is the boolean true/false; for readers it is the fetched/selected content; for color pickers it is the selected color text (for example #FFAA00 or rgba(...), depending on the picker mode). Always provide a meaningful UserPrompt so the final concatenated prompt remains coherent from the LLM’s perspective.
Advanced: BuildPrompt (optional)
If you want full control over prompt composition, define ASSISTANT.BuildPrompt as a Lua function. When present, AI Studio calls it and uses its return value as the final user prompt. The default prompt assembly is skipped.
Contract
ASSISTANT.BuildPrompt(input)must return a string.- If the function is missing, returns
nil, or returns a non-string, AI Studio falls back to the default prompt assembly. - Errors in the function are caught and logged, then fall back to the default prompt assembly.
Input table shape
The function receives a single input table with:
input.fields: values keyed by componentName- Text area, dropdown, and readers are strings
- Switch is a boolean
- Color picker is the selected color as a string
input.meta: per-component metadata keyed by componentNameType(string, e.g.TEXT_AREA,DROPDOWN,SWITCH,COLOR_PICKER)Label(string, when provided)UserPrompt(string, when provided)
input.profile: selected profile dataId,Name,NeedToKnow,Actions,Num- When no profile is selected, values match the built-in "Use no profile" entry
Table shapes (quick reference)
input = {
fields = {
["<Name>"] = "<string|boolean>",
...
},
meta = {
["<Name>"] = {
Type = "<TEXT_AREA|DROPDOWN|SWITCH|WEB_CONTENT_READER|FILE_CONTENT_READER|COLOR_PICKER>",
Label = "<string?>",
UserPrompt = "<string?>"
},
...
},
profile = {
Name = "<string>",
NeedToKnow = "<string>",
Actions = "<string>",
Num = <number>
}
}
Using meta inside BuildPrompt
input.meta is useful when you want to dynamically build the prompt based on component type or reuse existing UI text (labels/user prompts).
Example: iterate all fields with labels and include their values
ASSISTANT.BuildPrompt = function(input)
local parts = {}
for name, value in pairs(input.fields) do
local meta = input.meta[name]
if meta and meta.Label and value ~= "" then
table.insert(parts, meta.Label .. ": " .. tostring(value))
end
end
return table.concat(parts, "\n")
end
Example: handle types differently
ASSISTANT.BuildPrompt = function(input)
local parts = {}
for name, meta in pairs(input.meta) do
local value = input.fields[name]
if meta.Type == "SWITCH" then
table.insert(parts, name .. ": " .. tostring(value))
elseif meta.Type == "COLOR_PICKER" and value and value ~= "" then
table.insert(parts, name .. ": " .. value)
elseif value and value ~= "" then
table.insert(parts, name .. ": " .. value)
end
end
return table.concat(parts, "\n")
end
COLOR_PICKER reference
- Use
Type = "COLOR_PICKER"to render a MudBlazor color picker. - Required props:
Name: unique state key used in prompt assembly andBuildPrompt(input.fields).Label: visible field label.
- Optional props:
Placeholder: default color hex string (e.g.#FF10FF) or initial hint text.ShowAlpha: defaults totrue; enables alpha channel editing.ShowToolbar: defaults totrue; shows picker/grid/palette toolbar.ShowModeSwitch: defaults totrue; allows switching between HEX/RGB(A)/HSL modes.PickerVariant: one ofDIALOG,INLINE,STATIC; invalid or omitted values fall back toSTATIC.UserPrompt: prompt context text for the selected color.Class,Style: forwarded to the rendered component for layout/styling.
Example:
{
["Type"] = "COLOR_PICKER",
["Props"] = {
["Name"] = "accentColor",
["Label"] = "Accent color",
["Placeholder"] = "#FFAA00",
["ShowAlpha"] = false,
["ShowToolbar"] = true,
["ShowModeSwitch"] = true,
["PickerVariant"] = "STATIC",
["UserPrompt"] = "Use this as the accent color for the generated design."
}
}
Using profile inside BuildPrompt
Profiles are optional user context (e.g., "NeedToKnow" and "Actions"). You can inject this directly into the user prompt if you want the LLM to always see it.
Example:
ASSISTANT.BuildPrompt = function(input)
local parts = {}
if input.profile and input.profile.NeedToKnow ~= "" then
table.insert(parts, "User context:")
table.insert(parts, input.profile.NeedToKnow)
table.insert(parts, "")
end
table.insert(parts, input.fields.Main or "")
return table.concat(parts, "\n")
end
Included lua libraries
- Basic Functions Library
- Coroutine Manipulation Library
- String Manipulation Library
- Table Manipulation Library
- Mathematical Functions Library
- Bitwise Operations Library
Logging helpers (assistant plugins only)
The assistant runtime exposes basic logging helpers to Lua. Use them to debug custom prompt building.
LogDebug(message)LogInfo(message)LogWarn(message)LogError(message)
Example:
ASSISTANT.BuildPrompt = function(input)
LogInfo("BuildPrompt called")
return input.fields.Text or ""
end
Date/time helpers (assistant plugins only)
Use these when you need timestamps inside Lua.
DateTime(format)returns a table with date/time parts plus a formatted string.formatis optional; default isyyyy-MM-dd HH:mm:ss(ISO 8601-like).formattedcontains the date in your desired format (e.g.dd.MM.yyyy HH:mm) or the default.- Members:
year,month,day,hour,minute,second,millisecond,formatted.
Timestamp()returns a UTC timestamp in ISO-8601 format (O/ round-trip), e.g.2026-03-02T21:15:30.1234567Z.
Example:
local dt = DateTime("yyyy-MM-dd HH:mm:ss")
LogInfo(dt.formatted)
LogInfo(Timestamp())
LogInfo(dt.day .. "." .. dt.month .. "." .. dt.year)
Example: simple custom prompt
ASSISTANT.BuildPrompt = function(input)
local f = input.fields
return "Topic: " .. (f.Topic or "") .. "\nDetails:\n" .. (f.Details or "")
end
Example: structured prompt (similar to Coding assistant)
ASSISTANT.BuildPrompt = function(input)
local f = input.fields
local parts = {}
if (f.Code or "") ~= "" then
table.insert(parts, "I have the following code:")
table.insert(parts, "```")
table.insert(parts, f.Code)
table.insert(parts, "```")
table.insert(parts, "")
end
if (f.CompilerMessages or "") ~= "" then
table.insert(parts, "I have the following compiler messages:")
table.insert(parts, "```")
table.insert(parts, f.CompilerMessages)
table.insert(parts, "```")
table.insert(parts, "")
end
table.insert(parts, "My questions are:")
table.insert(parts, f.Questions or "")
return table.concat(parts, "\n")
end
Tips
- Give every component a unique
Name— it’s used to track state. - Keep in mind that components and their properties are case-sensitive (e.g. if you write
["Type"] = "heading"instead of["Type"] = "HEADING"the component will not be registered). Always copy-paste the component from theplugin.luamanifest to avoid this. - When you expect default content (e.g., a textarea with instructions), keep
UserPromptbut also setPrefillTextso the user starts with a hint. - If you need extra explanatory text (before or after the interactive controls), use
TEXTorHEADINGcomponents. - Keep
Preselect/PreselectContentCleanerAgentflags inWEB_CONTENT_READERto simplify the initial UI for the user.