7.1 KiB
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.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. 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
input.meta: per-component metadata keyed by componentNameType(string, e.g.TEXT_AREA,DROPDOWN,SWITCH)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>",
Label = "<string?>",
UserPrompt = "<string?>"
},
...
},
profile = {
Id = "<string guid>",
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 value and value ~= "" then
table.insert(parts, name .. ": " .. value)
end
end
return table.concat(parts, "\n")
end
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
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.