pi-web-ui 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,7 +18,7 @@ import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentS
18
18
  import { Type } from "typebox";
19
19
  import { serializeMessage, serializeStreamingMessage, } from "./serialize.js";
20
20
  import { loadCommands, saveCommandsFile, TerminalManager, } from "./terminals.js";
21
- import { findVisionModels, transcribeImages, } from "./vision-bridge.js";
21
+ import { buildVisionBridgePrompt, findVisionModels, transcribeImages, } from "./vision-bridge.js";
22
22
  const SNAPSHOT_INTERVAL_MS = 60;
23
23
  const WIDGET_REFRESH_MS = 2000;
24
24
  const WIDGET_WIDTH = 80;
@@ -907,6 +907,8 @@ class ClientStateStore {
907
907
  disabledExtensions: s?.settings?.disabledExtensions ?? [],
908
908
  visionBridgeEnabled: s?.settings?.visionBridgeEnabled ?? true,
909
909
  visionBridgeModel: s?.settings?.visionBridgeModel ?? null,
910
+ visionBridgePromptMode: s?.settings?.visionBridgePromptMode === "replace" ? "replace" : "append",
911
+ visionBridgePrompt: s?.settings?.visionBridgePrompt ?? "",
910
912
  };
911
913
  }
912
914
  /** Persist the client's settings-panel state (partial merge). */
@@ -921,6 +923,10 @@ class ClientStateStore {
921
923
  disabledExtensions: settings.disabledExtensions ?? cur.disabledExtensions ?? [],
922
924
  visionBridgeEnabled: settings.visionBridgeEnabled ?? cur.visionBridgeEnabled ?? true,
923
925
  visionBridgeModel: settings.visionBridgeModel ?? cur.visionBridgeModel ?? null,
926
+ visionBridgePromptMode: settings.visionBridgePromptMode ??
927
+ cur.visionBridgePromptMode ??
928
+ "append",
929
+ visionBridgePrompt: settings.visionBridgePrompt ?? cur.visionBridgePrompt ?? "",
924
930
  };
925
931
  this.save();
926
932
  }
@@ -2531,6 +2537,8 @@ export class ClientSession {
2531
2537
  customSystemPrompt: this.settings.customSystemPrompt,
2532
2538
  visionBridgeEnabled: this.settings.visionBridgeEnabled,
2533
2539
  visionBridgeModel: this.settings.visionBridgeModel,
2540
+ visionBridgePromptMode: this.settings.visionBridgePromptMode,
2541
+ visionBridgePrompt: this.settings.visionBridgePrompt,
2534
2542
  visionModels: this.collectVisionModels(),
2535
2543
  disabledSkills: [...this.settings.disabledSkills],
2536
2544
  disabledExtensions: [...this.settings.disabledExtensions],
@@ -2577,6 +2585,12 @@ export class ClientSession {
2577
2585
  if (partial.visionBridgeModel !== undefined) {
2578
2586
  this.settings.visionBridgeModel = partial.visionBridgeModel ?? null;
2579
2587
  }
2588
+ if (partial.visionBridgePromptMode !== undefined) {
2589
+ this.settings.visionBridgePromptMode = partial.visionBridgePromptMode;
2590
+ }
2591
+ if (partial.visionBridgePrompt !== undefined) {
2592
+ this.settings.visionBridgePrompt = partial.visionBridgePrompt;
2593
+ }
2580
2594
  this.stateStore.saveSettings(this.clientId, this.settings);
2581
2595
  this.pushSettings();
2582
2596
  if (needsReload)
@@ -2619,6 +2633,8 @@ export class ClientSession {
2619
2633
  // Presets don't capture vision-bridge prefs — keep the current ones.
2620
2634
  visionBridgeEnabled: this.settings.visionBridgeEnabled,
2621
2635
  visionBridgeModel: this.settings.visionBridgeModel,
2636
+ visionBridgePromptMode: this.settings.visionBridgePromptMode,
2637
+ visionBridgePrompt: this.settings.visionBridgePrompt,
2622
2638
  };
2623
2639
  this.stateStore.saveSettings(this.clientId, this.settings);
2624
2640
  this.pushSettings();
@@ -2918,9 +2934,14 @@ export class ClientSession {
2918
2934
  else {
2919
2935
  // Batch hash so re-sending identical images (edit & re-ask) reuses
2920
2936
  // the transcript instead of re-burning tokens on the vision API.
2937
+ // The active transcription prompt is part of the key: changing
2938
+ // the custom prompt must invalidate cached transcripts made with
2939
+ // the old prompt.
2921
2940
  const batchHash = bridgedImages
2922
2941
  .map((b) => `${b.att.name ?? "img"}:${b.raw.slice(0, 48)}`)
2923
- .join("|");
2942
+ .join("|") +
2943
+ "::" +
2944
+ buildVisionBridgePrompt(this.settings.visionBridgePromptMode, this.settings.visionBridgePrompt);
2924
2945
  let transcript = this.visionBridgeCache.get(batchHash);
2925
2946
  if (transcript === undefined) {
2926
2947
  this.emit({
@@ -2934,7 +2955,10 @@ export class ClientSession {
2934
2955
  data: b.raw,
2935
2956
  mimeType: b.mimeType,
2936
2957
  name: b.att.name,
2937
- })), { model: chosenModel ?? undefined });
2958
+ })), {
2959
+ model: chosenModel ?? undefined,
2960
+ systemPrompt: buildVisionBridgePrompt(this.settings.visionBridgePromptMode, this.settings.visionBridgePrompt),
2961
+ });
2938
2962
  this.visionBridgeCache.set(batchHash, transcript);
2939
2963
  this.emit({
2940
2964
  type: "notice",
@@ -474,6 +474,8 @@ wss.on("connection", (ws) => {
474
474
  disabledExtensions: msg.disabledExtensions,
475
475
  visionBridgeEnabled: msg.visionBridgeEnabled,
476
476
  visionBridgeModel: msg.visionBridgeModel,
477
+ visionBridgePromptMode: msg.visionBridgePromptMode,
478
+ visionBridgePrompt: msg.visionBridgePrompt,
477
479
  });
478
480
  break;
479
481
  case "save_preset":
@@ -31,8 +31,11 @@ export function findVisionModels(runtime) {
31
31
  * Evidence-first transcription prompt, modeled on modlens' output contract:
32
32
  * full verbatim text, reading-order layout blocks, entities/relations, chart
33
33
  * axes & data. Emphasizes honesty over hallucination.
34
+ *
35
+ * Exported so the settings panel can offer a custom prompt (append to this
36
+ * default or replace it entirely).
34
37
  */
35
- const SYSTEM_PROMPT = `You are a vision bridge for a text-only language model. You receive one or more images and must transcribe them into precise, structured text evidence so another model that cannot see images can answer questions about them accurately.
38
+ export const SYSTEM_PROMPT = `You are a vision bridge for a text-only language model. You receive one or more images and must transcribe them into precise, structured text evidence so another model that cannot see images can answer questions about them accurately.
36
39
 
37
40
  Follow these rules:
38
41
  1. Transcribe ALL visible text verbatim, preserving wording, spelling, punctuation and line breaks. This is the most important part — the reader relies on your transcription, not on the image.
@@ -42,6 +45,21 @@ Follow these rules:
42
45
  5. If part of the image is too blurry/low-resolution to read, say "(读不清)" or "unclear" for that part — NEVER invent or guess content you cannot see.
43
46
  6. If there are multiple images, address them in order (图 1 / Image 1, 图 2 / Image 2, ...).
44
47
  7. Output only the transcript. No preamble, no commentary about the image itself.`;
48
+ /**
49
+ * Assemble the final vision-model system prompt from the settings-panel prefs.
50
+ * mode "append": custom text appended after the default prompt (empty custom =
51
+ * pure default). mode "replace": custom text REPLACES the default prompt, but
52
+ * an empty custom text still falls back to the default (never send an empty
53
+ * system prompt to the vision model).
54
+ */
55
+ export function buildVisionBridgePrompt(mode, custom) {
56
+ const text = custom?.trim() ?? "";
57
+ if (mode === "replace" && text)
58
+ return text;
59
+ if (text)
60
+ return `${SYSTEM_PROMPT}\n\n${text}`;
61
+ return SYSTEM_PROMPT;
62
+ }
45
63
  /** Per-batch user instruction appended after the images. */
46
64
  function buildUserPrompt(count) {
47
65
  if (count <= 1) {
@@ -77,7 +95,7 @@ export async function transcribeImages(runtime, images, options = {}) {
77
95
  : "image/png",
78
96
  }));
79
97
  const context = {
80
- systemPrompt: SYSTEM_PROMPT,
98
+ systemPrompt: options.systemPrompt ?? SYSTEM_PROMPT,
81
99
  messages: [
82
100
  {
83
101
  role: "user",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-web-ui",
3
- "version": "0.24.0",
3
+ "version": "0.25.0",
4
4
  "description": "Web chat interface for the pi coding agent, powered by the pi SDK (@earendil-works/pi-coding-agent) — one-command run, Docker/systemd/launchd deployable",
5
5
  "license": "MIT",
6
6
  "type": "module",