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
|
-
})), {
|
|
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",
|
package/dist/server/index.js
CHANGED
|
@@ -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.
|
|
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",
|