巨集與變數
註冊自訂巨集、在文字中求值巨集、讀寫聊天作用域或全域變數的相關 API。
巨集
Luker 透過 macros 命名空間暴露巨集系統,並為了向後相容保留了傳統的 MacrosParser。{{user}}、{{char}}、{{lastMessage}}、{{getvar::name}} 之類的內建巨集由 core 註冊;外掛可以透過 macros.register() 加入自己的巨集。
macros.register
macros.register(name: string, options: {
handler: (ctx: MacroExecutionContext) => string,
aliases?: { alias: string, visible?: boolean }[],
category?: string,
unnamedArgs?: number | UnnamedArgDef[],
list?: boolean | { min: number, max?: number },
strictArgs?: boolean,
description?: string,
returns?: string,
returnType?: 'string' | 'integer' | 'number' | 'boolean',
displayOverride?: string,
exampleUsage?: string | string[],
delayArgResolution?: boolean,
}): MacroDefinition | null註冊一個巨集。回傳已註冊的定義;驗證失敗時回傳 null。
| 選項 | 說明 |
|---|---|
handler | 巨集本體。接收一個帶有解析後引數的執行上下文 |
aliases | 別名。每個 { alias, visible } 註冊相同的處理器 |
category | 自動完成中的分組(例如 'utility'、'character'、'time') |
unnamedArgs | 一個數量(全部必填)或一個引數定義陣列 |
list | 巨集是否接受可變引數列表 |
strictArgs | 為 false 時,arity / 類型不匹配只記錄警告而不擲出 |
delayArgResolution | 為 true 時,引數中的巢狀巨集不會被預先解析——處理器必須自行呼叫 ctx.resolve(text)。僅用於控制流類型的巨集 |
處理器上下文
處理器接收一個 MacroExecutionContext,包含:
| 欄位 | 說明 |
|---|---|
name | 呼叫時使用的巨集名 |
args | 具名引數值 |
unnamedArgs | 不具名位置型引數 |
list | 可變列表引數 |
env | 巨集求值環境(聊天、角色、persona 等) |
normalize(value) | 把值強制轉成巨集的回傳類型 |
trimContent(content, opts?) | 修剪多行區塊 |
resolve(text, opts?) | 解析 text 中的巢狀巨集 |
warn(message, error?) | 記錄一條歸屬到此巨集的警告 |
const ctx = Luker.getContext();
ctx.macros.register('myStatus', {
description: 'Returns the plugin status string.',
category: 'utility',
handler: () => 'My plugin is active.',
});
ctx.macros.register('greet', {
description: 'Greets a name.',
unnamedArgs: [
{ name: 'name', optional: false, type: 'string', description: 'Person to greet' },
],
handler: (mctx) => `Hello, ${mctx.unnamedArgs[0]}!`,
});註冊後 {{myStatus}} 和 {{greet::Bob}} 都能正常工作。
macros.registry
底層的註冊表。用於反註冊和檢視:
ctx.macros.registry.unregisterMacro('myStatus');
ctx.macros.registry.hasMacro('greet');
const def = ctx.macros.registry.getMacro('greet');內建巨集參考
下列為 core 註冊的非詳盡列表。完整覆蓋見 public/scripts/macros/definitions/ 下的原始碼。
| 巨集 | 回傳 |
|---|---|
{{user}} | 當前使用者 / persona 名稱 |
{{char}} | 當前角色名 |
{{persona}} | 當前 persona 描述 |
{{charDescription}} / {{charPersonality}} / {{charScenario}} | 角色卡欄位 |
{{charDepthPrompt}} / {{charCreatorNotes}} / {{charFirstMessage}} / {{charVersion}} | 角色卡欄位 |
{{mesExamples}} / {{mesExamplesRaw}} | 對話範例 |
{{group}} / {{groupNotMuted}} | 群組成員名稱 |
{{lastMessage}} / {{lastMessageId}} / {{lastUserMessage}} / {{lastCharMessage}} | 最近的聊天內容 |
{{firstIncludedMessageId}} / {{firstDisplayedMessageId}} | 可見視窗 |
{{lastSwipeId}} / {{currentSwipeId}} | swipe 狀態 |
{{model}} | 當前模型識別碼 |
{{maxPrompt}} / {{maxContext}} / {{maxResponse}} | token 預算 |
{{time}} / {{date}} / {{weekday}} / {{isotime}} / {{isodate}} | 本地時鐘 |
{{datetimeformat::FORMAT}} | moment.format(FORMAT) |
{{idleDuration}} / {{timeDiff}} | 時間差 |
{{getvar::name}} / {{setvar::name::value}} / {{addvar::name::value}} | 本地變數 |
{{incvar::name}} / {{decvar::name}} / {{hasvar::name}} / {{deletevar::name}} | 本地變數 |
{{pushvar::name::value}} / {{popvar::name}} | 本地變數——陣列 push / pop。缺失時自動建為 [];支援點號路徑 |
{{getglobalvar::name}} / {{setglobalvar::name::value}} / ... | 全域變數 |
{{if::cond::then::else}} / {{else::...}} / {{each::...}} | 控制流 |
{{trim}} / {{newline}} / {{space}} / {{noop}} | 空白輔助 |
{{roll::XdY}} / {{random::a,b,c}} / {{pick::a,b,c}} | 隨機 |
{{//comment}} | 註解(輸出忽略) |
{{outlet::name}} | 自訂 WI outlet 內容 |
{{isMobile}} / {{hasExtension::name}} | 環境檢查 |
{{lastGenerationType}} / {{systemPrompt}} | 流水線狀態 |
MacrosParser(已棄用)
MacrosParser.registerMacro(key: string, value: string | (nonce) => string, description?: string): void
MacrosParser.unregisterMacro(key: string): void註冊單純字串替換巨集的舊 API。會記錄棄用警告。請遷移到 macros.register({ handler }) 以取得完整功能支援,或在單次呼叫中傳 dynamicMacros 給 substituteParams。
substituteParams
substituteParams(content: string, options?: {
name1Override?: string,
name2Override?: string,
original?: string,
groupOverride?: string,
replaceCharacterCard?: boolean,
dynamicMacros?: Record<string, string | (() => string)>,
postProcessFn?: (text: string) => string,
}): string解析 content 中的所有巨集。用 dynamicMacros 為單次呼叫注入即用即拋的 0 引數巨集:
const result = ctx.substituteParams('Hello, {{user}}! Today is {{date}}.');substituteParamsExtended
substituteParamsExtended(
content: string,
additionalMacros?: Record<string, string | (() => string)>,
postProcessFn?: (text: string) => string,
): stringsubstituteParams 的便利包裝,僅為當前呼叫加入 additionalMacros:
const result = ctx.substituteParamsExtended(
'Query: {{queryText}}',
{ queryText: userInput },
);additionalMacros 不會被全域註冊——僅在這次替換中存在。
變數
有兩個作用域:本地(按聊天,持久化在 chat_metadata.variables)和全域(跨聊天,持久化在 extension_settings.variables.global)。
本地變數
context.variables.local.get(name: string, args?: object): string | number
context.variables.local.set(name: string, value: any, args?: object): any
context.variables.local.add(name: string, value: any): any
context.variables.local.inc(name: string): any
context.variables.local.dec(name: string): any
context.variables.local.del(name: string): ''
context.variables.local.has(name: string): boolean
context.variables.local.push(name: string, value: any): string | undefined
context.variables.local.pop(name: string): string | undefined| 方法 | 說明 |
|---|---|
get | 讀取變數。數字字串會自動強制成數字。不存在時回傳 '' |
set | 寫入變數。回傳該值 |
add | 兩者都是數字時做數字加法。已有值是 JSON 陣列時 push。否則作為字串串接 |
inc / dec | add(name, ±1) 的捷徑 |
del | 移除變數。回傳 '' |
has | 布林存在性檢查 |
push | 把 value 推入 name 處的 JSON 陣列。缺失時自動建為 []。對應巨集形式 {{pushvar::name::value}} |
pop | 從 name 處的 JSON 陣列彈出最後一個元素。空或缺失時為無操作。對應巨集形式 {{popvar::name}} |
上述每個方法的 name 都接受點號路徑(例如 roster.alice.hp),用於讀寫一個結構化變數內部的某片葉子。寫入類方法直接就地修改 chat_metadata.variables[root],跟巨集側的 {{setvar::roster.alice.hp::value}} 行為一致;中間節點按需自動建立。
get / set 上可選的 args 參數支援:
args.key—— 替代變數名(覆寫name)args.index—— 對儲存於變數中的 JSON list / dict 的索引 / 鍵args.as(僅 set) —— 為索引寫入強制成'string'/'number'/'boolean'
全域變數
context.variables.global.get / set / add / inc / dec / del / has與本地變數有相同的介面。跨聊天持久化。
使用範例
const ctx = Luker.getContext();
// 帶預設值讀取本地變數
const turns = Number(ctx.variables.local.get('turns_taken')) || 0;
// 自增
ctx.variables.local.inc('turns_taken');
// 檢查 + 初始化全域設定變數
if (!ctx.variables.global.has('api_endpoint')) {
ctx.variables.global.set('api_endpoint', 'https://api.example.com');
}
const endpoint = ctx.variables.global.get('api_endpoint');
// 對儲存於本地變數中的 JSON 列表做索引寫入
ctx.variables.local.set('inventory', 'sword', { index: 0, as: 'string' });
ctx.variables.local.set('inventory', 'shield', { index: 1, as: 'string' });本地 vs 全域
| 本地 | 全域 | |
|---|---|---|
| 作用域 | 單一聊天 | 所有聊天 |
| 儲存 | chat_metadata.variables | extension_settings.variables.global |
| 儲存觸發 | saveMetadataDebounced | saveSettingsDebounced |
| 用途 | 聊天特定計數器、進行中狀態 | 外掛設定、跨聊天資料 |
樓層級寫入
local / global 七件套之外,luker 在頂層另外導出一個 setVariable,支援把單次寫入掛到某一樓——這是 {{setvar::name::value}} 在文字裡寫出來效果的程式碼版等價物。
context.setVariable(
name: string,
value: any,
options?: { floor?: number },
): Promise<any>| 呼叫方式 | 效果 |
|---|---|
await ctx.setVariable(k, v) | 直接寫 chat_metadata.variables[k] = v,跟 ctx.variables.local.set(k, v) 落到同一個桶,適合在非同步程式碼裡使用 |
await ctx.setVariable(k, v, { floor: N }) | 在第 N 樓的 extra.var_ops 末尾掛一條 setvar,綁在該樓的當前 swipe 上——後續 swipe 切換 / 刪樓 / 建分支時,跟樓層一起被 variable-op-log 重放或回滾 |
帶 floor 時的幾點細節:
- 值會被強轉成字串——
extra.var_ops的格式只承載字串({{getvar}}取回的也是字串)。需要存結構化物件請改用createFloorState(見 樓層級結構化 state)。 - 是重放,不是覆寫——swipe / 刪樓 / 建分支時,rebuilder 會按當前活動 swipe 上所有 var_ops 的寫入順序,把它們觸碰過的 key 在
chat_metadata.variables上重放一遍;沒被任何 var_op 寫過的 key(world-info 副作用、slash 命令、其他擴充寫的值)會原樣保留。所以一次 floor 寫入並不直接修改儲存,而是為後續每次重放貢獻一條指令。 floor必須是有效樓層索引(0 <= floor < chat.length),越界會拋錯。- 點號路徑名同樣支援樓層級寫入——
setVariable('roster.alice.hp', 50, { floor })會在第一個.處拆分,把path透傳進 op 記錄。op.key仍然是頂層變數名,重播時把整個結構當成一個單位。
const ctx = Luker.getContext();
// 立即寫,跟 ctx.variables.local.set 落到同一個桶
await ctx.setVariable('quest_stage', 'intro');
// 綁定到最後一樓,跟著 swipe / 刪樓 / 建分支一起重放
await ctx.setVariable('hp', 42, { floor: ctx.chat.length - 1 });floor 寫入 vs createFloorState
| floor 寫入 | createFloorState | |
|---|---|---|
| 資料形狀 | 標量(字串 / 數字) | 結構化物件 |
| 桶 | 跟 {{getvar::k}} 共用 chat_metadata.variables | 獨立 namespace,不進 macro 系統 |
| 提交日誌 | variable-op-log(樓層 extra.var_ops) | 樓層結構化提交日誌(__floor_log) |
| 適合 | 跟 AI 寫的 {{setvar}} 共享儲存的可回滾標量 | CardApp / 外掛自己管理的可回滾結構化狀態 |
兩個機制走的是各自獨立的提交日誌,同一個 key 不要兩邊都寫——重建順序無保證,容易互相覆蓋。