Skip to content

執行面板 ​

它是什麼 ​

一個可重用的記憶體內執行軌跡,供任何執行多步 LLM 任務的外掛使用。它會在聊天區旁渲染一塊滑入式面板(窄屏下改為底部抽屜),每一輪是一張可折疊的卡片,分節內容串流填充,附帶停止按鈕與匯出動作,執行進行中還會顯示一枚懸浮膠囊。

共享層位於 public/scripts/run-panel/,在 Luker context 上暴露為 runPanel。倉庫內消費方:

  • 編排器 —— public/scripts/extensions/orchestrator/run-panel/panel.js

快速上手 ​

js
import { createRunStore, createRunPanel } from '/scripts/run-panel/index.js';

const store = createRunStore();
const panel = createRunPanel({
    store,
    t: (text, ...args) => text,   // supply your plugin's translator
    idPrefix: 'luker-my-run-panel',
    modeLabels: { myTask: 'My task' },
    exportFilename: ({ runId }) => `my-run-${runId}.json`,
});
panel.init();   // call once at boot
panel.open();   // call from a menu action

這就是完整的整合方式。透過寫入 store 驅動面板:

js
const runId = store.startRun({ mode: 'myTask', stopFn: () => abortController.abort() });
const roundId = store.appendRound({ runId, round: { id: 'round-1', label: 'Round 1' } });
const sectionId = store.ensureSection({
    runId, roundId,
    section: { id: 'reasoning', kind: 'reasoning', title: 'Reasoning' },
});
store.appendToSection({ runId, roundId, sectionId, delta: 'thinking…' });
store.setSectionStatus({ runId, roundId, sectionId, status: 'done' });
store.finishRun({ runId, status: 'committed', finalText: 'Result' });

儲存 API ​

createRunStore() 返回一個隔離的 store。每次呼叫都會產生一個獨立實例,因此兩個外掛絕不會共享執行狀態或產生衝突。

方法用途
subscribe(listener)訂閱事件;返回一個取消訂閱函式。
getCurrentRun()當前執行物件,無則為 null。
startRun({ mode, chatKey, abortFn, stopFn, quiet })開始一次執行;返回 runId。若該 store 已有執行中的執行則拋出錯誤。
clearCurrentRun()丟棄當前執行並發出 run_cleared。
appendRound({ runId, round: { id, label } })新增一輪;返回 roundId。
setRoundStatus({ runId, roundId, status })更新輪次狀態。
ensureSection({ runId, roundId, section: { id, kind, title, meta } })分節不存在時建立;返回 sectionId。
appendToSection({ runId, roundId, sectionId, delta })向分節正文追加串流文字。
setSectionStatus({ runId, roundId, sectionId, status, meta })更新分節狀態。
finishRun({ runId, status, finalText, error })結束執行。
setRunMeta({ runId, tokensSpent, cost })設定執行級中繼資料。
addTokenUsage({ runId, usage })把 LLM 用量物件累加進執行中的 token 總數。

quiet: true 只填充 store 而不自動開啟面板;之後呼叫 panel.open() 即可顯示。

事件 ​

監聽器接收 { type, ... } 負載。常數從 run-panel/events.js 匯出(也可透過 context.runPanel.events 存取)。

事件負載
run_startedrunId、mode、quiet
round_appendedrunId、roundId
section_ensuredrunId、roundId、sectionId
section_appendedrunId、roundId、sectionId、delta
section_statusrunId、roundId、sectionId、status、meta
round_statusrunId、roundId、status
run_metarunId
run_finishedrunId、status
run_clearedrunId

面板選項 ​

createRunPanel(options) 返回 { init, open, destroy }。

選項預設值用途
store必需來自 createRunStore() 的 store。
t恆等函式翻譯函式 (template, ...args) => string。
idPrefixluker-run-panelDOM id 前綴;面板根為 #<idPrefix>,懸浮膠囊為 #<idPrefix>-pill。
stylesheetUrl共享的 panel.css要注入的樣式表。
kindIconsDEFAULT_KIND_ICONS分節類型到圖示的對映。
defaultCollapsedKinds[]預設折疊渲染的分節類型。
modeLabels{}mode 到徽章文字的對映;缺失時回退為原始 mode。
exportFilenamerun-<runId>-<mode>.json匯出動作的檔名。
autoOpentrue非 quiet 的 run_started 時自動開啟。
emptyStateText一段預設提示無執行時 open() 顯示的文案。
onStopnull覆寫停止處理器。

輔助函式 ​

以下便捷封裝層用於終結輪次與分節狀態。首個參數為 store 實例,因此可用於任意 store。

輔助函式用途
withRound(store, runId, { id, label }, fn)追加輪次、執行 fn(roundId),隨後將輪次標記為 done;拋出錯誤時標記為 failed。返回 fn 的結果;返回 Promise 時,在其落定後終結該輪次。
withStreamingSection(store, runId, roundId, { id, kind, title }, asyncFn)確保分節存在,將 append(delta) 函式與分節 id 傳給 asyncFn,隨後將分節標記為 done;拋出錯誤時標記為 failed。

三層 API 暴露 ​

js
// Layer 1 —— 直接 ESM import(倉庫內擴充功能)
import { createRunStore, createRunPanel } from '/scripts/run-panel/index.js';

// Layer 2 —— lukerContext 屬性
const { createRunPanel } = lukerContext.runPanel;

// Layer 3 —— getContext(第三方擴充功能)
const { createRunStore } = SillyTavern.getContext().runPanel;

參考消費方 ​

  • 編排器執行時寫入輪次與分節,面板在每次執行時自動開啟:public/scripts/extensions/orchestrator/loop-runtime.js、director-runtime.js、agenda-runtime.js、spec-runtime.js。
  • 編排器封裝層設定 i18n 與匯出檔名:public/scripts/extensions/orchestrator/run-panel/panel.js。

基於 SillyTavern 建構