@vtstech/pi-react-fallback 1.1.1 → 1.1.3
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.
- package/package.json +2 -2
- package/react-fallback.js +23 -378
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vtstech/pi-react-fallback",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "ReAct fallback extension for Pi Coding Agent",
|
|
5
5
|
"main": "react-fallback.js",
|
|
6
6
|
"keywords": ["pi-extensions"],
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"url": "https://github.com/VTSTech/pi-coding-agent"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@vtstech/pi-shared": "1.1.
|
|
17
|
+
"@vtstech/pi-shared": "1.1.3"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
20
|
"@mariozechner/pi-coding-agent": ">=0.66"
|
package/react-fallback.js
CHANGED
|
@@ -4,373 +4,19 @@ import * as fs from "node:fs";
|
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { section, ok, fail, warn, info } from "@vtstech/pi-shared/format";
|
|
6
6
|
import { EXTENSION_VERSION } from "@vtstech/pi-shared/ollama";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
{
|
|
21
|
-
name: "react",
|
|
22
|
-
actionTag: "Action:",
|
|
23
|
-
inputTag: "Action Input:",
|
|
24
|
-
thoughtTag: "Thought:",
|
|
25
|
-
stopTags: ["Observation:", "Thought:", "Final Answer:", "Action:"],
|
|
26
|
-
finalTag: "Final Answer:"
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
name: "function",
|
|
30
|
-
actionTag: "Function:",
|
|
31
|
-
inputTag: "Function Input:",
|
|
32
|
-
thoughtTag: "Thought:",
|
|
33
|
-
stopTags: ["Observation:", "Thought:", "Final Answer:", "Function:", "Action:"],
|
|
34
|
-
finalTag: "Final Answer:"
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
name: "tool",
|
|
38
|
-
actionTag: "Tool:",
|
|
39
|
-
inputTag: "Tool Input:",
|
|
40
|
-
thoughtTag: "Thought:",
|
|
41
|
-
stopTags: ["Observation:", "Thought:", "Final Answer:", "Tool:", "Action:"],
|
|
42
|
-
finalTag: "Final Answer:"
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
name: "call",
|
|
46
|
-
actionTag: "Call:",
|
|
47
|
-
inputTag: "Input:",
|
|
48
|
-
thoughtTag: "Thought:",
|
|
49
|
-
stopTags: ["Observation:", "Thought:", "Final Answer:", "Call:", "Action:"],
|
|
50
|
-
finalTag: "Final Answer:"
|
|
51
|
-
}
|
|
52
|
-
];
|
|
53
|
-
function buildDialectPatterns(d) {
|
|
54
|
-
const esc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
55
|
-
const aT = esc(d.actionTag);
|
|
56
|
-
const iT = esc(d.inputTag);
|
|
57
|
-
const stopAlt = d.stopTags.map(esc).join("|");
|
|
58
|
-
const tT = d.thoughtTag ? esc(d.thoughtTag) : void 0;
|
|
59
|
-
const fT = d.finalTag ? esc(d.finalTag) : void 0;
|
|
60
|
-
const thoughtRe = tT ? new RegExp(`${tT}\\s*(.*?)(?=${aT}|${fT}|$)`, "is") : void 0;
|
|
61
|
-
const actionRe = new RegExp(
|
|
62
|
-
`${aT}\\s*[\\x60"']?(\\w+)[\\x60"']?\\s*\\n?\\s*${iT}\\s*(.*?)(?=\\n\\s*(?:${stopAlt})|$)`,
|
|
63
|
-
"is"
|
|
64
|
-
);
|
|
65
|
-
const actionReSameline = new RegExp(
|
|
66
|
-
`${aT}\\s*[\\x60"']?(\\w+)[\\x60"']?\\s+${iT}\\s*(.*?)(?=\\n\\s*(?:${stopAlt})|$)`,
|
|
67
|
-
"is"
|
|
68
|
-
);
|
|
69
|
-
const actionReLoose = new RegExp(
|
|
70
|
-
`${aT}\\s*(.+?)\\n\\s*${iT}\\s*(.*?)(?=\\n\\s*(?:${stopAlt})|$)`,
|
|
71
|
-
"is"
|
|
72
|
-
);
|
|
73
|
-
const actionReParen = new RegExp(`${aT}\\s*(\\w+)\\s*\\(([^)]*)\\)`, "i");
|
|
74
|
-
const finalAnswerRe = fT ? new RegExp(`${fT}\\s*([\\s\\S]*?)$`, "i") : void 0;
|
|
75
|
-
return { thoughtRe, actionRe, actionReSameline, actionReLoose, actionReParen, finalAnswerRe, dialect: d };
|
|
76
|
-
}
|
|
77
|
-
var ALL_DIALECT_PATTERNS = REACT_DIALECTS.map(buildDialectPatterns);
|
|
78
|
-
var CLASSIC_PATTERNS = ALL_DIALECT_PATTERNS[0];
|
|
79
|
-
var THOUGHT_RE = CLASSIC_PATTERNS.thoughtRe;
|
|
80
|
-
var ACTION_RE = CLASSIC_PATTERNS.actionRe;
|
|
81
|
-
var ACTION_RE_SAMELINE = CLASSIC_PATTERNS.actionReSameline;
|
|
82
|
-
var ACTION_RE_LOOSE = CLASSIC_PATTERNS.actionReLoose;
|
|
83
|
-
var ACTION_RE_PAREN = CLASSIC_PATTERNS.actionReParen;
|
|
84
|
-
var FINAL_ANSWER_RE = CLASSIC_PATTERNS.finalAnswerRe;
|
|
85
|
-
function extractJsonArgs(rawArgs) {
|
|
86
|
-
const start = rawArgs.indexOf("{");
|
|
87
|
-
if (start === -1) return null;
|
|
88
|
-
let depth = 0;
|
|
89
|
-
let end = -1;
|
|
90
|
-
for (let i = start; i < rawArgs.length; i++) {
|
|
91
|
-
if (rawArgs[i] === "{") depth++;
|
|
92
|
-
else if (rawArgs[i] === "}") {
|
|
93
|
-
depth--;
|
|
94
|
-
if (depth === 0) {
|
|
95
|
-
end = i;
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
if (end === -1) return null;
|
|
101
|
-
const jsonStr = rawArgs.slice(start, end + 1);
|
|
102
|
-
try {
|
|
103
|
-
const parsed = JSON.parse(jsonStr);
|
|
104
|
-
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : { input: String(parsed) };
|
|
105
|
-
} catch {
|
|
106
|
-
}
|
|
107
|
-
try {
|
|
108
|
-
const sanitized = sanitizeModelJson(jsonStr);
|
|
109
|
-
const parsed = JSON.parse(sanitized);
|
|
110
|
-
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : { input: String(parsed) };
|
|
111
|
-
} catch {
|
|
112
|
-
}
|
|
113
|
-
const exprMatch = jsonStr.match(/['"]expression['"]:\s*['"]([^'"]+)['"]/);
|
|
114
|
-
if (exprMatch) return { expression: exprMatch[1] };
|
|
115
|
-
const cmdMatch = jsonStr.match(/['"]command['"]:\s*['"]([^'"]+)['"]/);
|
|
116
|
-
if (cmdMatch) return { command: cmdMatch[1] };
|
|
117
|
-
return { input: jsonStr };
|
|
118
|
-
}
|
|
119
|
-
function parseReact(text) {
|
|
120
|
-
for (const dp of ALL_DIALECT_PATTERNS) {
|
|
121
|
-
const result = parseReactWithPatterns(text, dp);
|
|
122
|
-
if (result) return result;
|
|
123
|
-
}
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
function parseReactWithPatterns(text, dp, tightLoose = false) {
|
|
127
|
-
let thought;
|
|
128
|
-
if (dp.thoughtRe) {
|
|
129
|
-
const thoughtMatch = dp.thoughtRe.exec(text);
|
|
130
|
-
if (thoughtMatch) thought = thoughtMatch[1].trim();
|
|
131
|
-
}
|
|
132
|
-
let match = dp.actionRe.exec(text);
|
|
133
|
-
if (!match) match = dp.actionReSameline.exec(text);
|
|
134
|
-
let looseMatch = false;
|
|
135
|
-
if (!match) {
|
|
136
|
-
const looseResult = dp.actionReLoose.exec(text);
|
|
137
|
-
if (looseResult) {
|
|
138
|
-
if (tightLoose) {
|
|
139
|
-
const candidate = looseResult[1].trim().replace(/[`"']/g, "");
|
|
140
|
-
const isToolIdentifier = /^\w+$/.test(candidate) && (candidate.includes("_") || candidate.includes("-"));
|
|
141
|
-
const isKnownTool = /^(get_weather|calculate)$/i.test(candidate);
|
|
142
|
-
if (isToolIdentifier || isKnownTool) {
|
|
143
|
-
match = looseResult;
|
|
144
|
-
looseMatch = true;
|
|
145
|
-
}
|
|
146
|
-
} else {
|
|
147
|
-
match = looseResult;
|
|
148
|
-
looseMatch = true;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
let parenMatch = false;
|
|
153
|
-
if (!match) match = dp.actionReParen.exec(text), parenMatch = true;
|
|
154
|
-
if (match) {
|
|
155
|
-
let toolName = match[1].trim().replace(/[`"']/g, "");
|
|
156
|
-
if (looseMatch && !tightLoose && pi.context?.session?.tools) {
|
|
157
|
-
const availableTools = pi.context.session.tools || [];
|
|
158
|
-
for (const real of availableTools) {
|
|
159
|
-
const rl = real.toLowerCase().replace(/_/g, "");
|
|
160
|
-
if (toolName.toLowerCase().includes(rl)) {
|
|
161
|
-
toolName = real;
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
if (toolName.includes(" ")) {
|
|
166
|
-
const words = toolName.split(/\s+/);
|
|
167
|
-
for (const w of words) {
|
|
168
|
-
const wc = w.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
169
|
-
if (wc.length < 3) continue;
|
|
170
|
-
for (const real of availableTools) {
|
|
171
|
-
const rl = real.toLowerCase().replace(/_/g, "");
|
|
172
|
-
if (rl.includes(wc.toLowerCase())) {
|
|
173
|
-
toolName = real;
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
if (!toolName.includes(" ")) break;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
const rawArgs = match[2].trim().replace(/^```\w*\s*/gm, "").replace(/```\s*$/gm, "").trim();
|
|
182
|
-
let args;
|
|
183
|
-
if (parenMatch && rawArgs && !rawArgs.startsWith("{")) {
|
|
184
|
-
const pairs = rawArgs.match(/(\w+)\s*:\s*("[^"]*"|'[^']*'|\S+)/g);
|
|
185
|
-
if (pairs) {
|
|
186
|
-
const obj = {};
|
|
187
|
-
for (const p of pairs) {
|
|
188
|
-
const colonIdx = p.indexOf(":");
|
|
189
|
-
const key = p.slice(0, colonIdx).trim();
|
|
190
|
-
let val = p.slice(colonIdx + 1).trim();
|
|
191
|
-
if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
|
|
192
|
-
val = val.slice(1, -1);
|
|
193
|
-
}
|
|
194
|
-
obj[key] = val;
|
|
195
|
-
}
|
|
196
|
-
args = obj;
|
|
197
|
-
} else {
|
|
198
|
-
args = { input: rawArgs };
|
|
199
|
-
}
|
|
200
|
-
} else {
|
|
201
|
-
args = extractJsonArgs(rawArgs) || { input: rawArgs };
|
|
202
|
-
}
|
|
203
|
-
let finalAnswer;
|
|
204
|
-
if (dp.finalAnswerRe) {
|
|
205
|
-
const faMatch = dp.finalAnswerRe.exec(text);
|
|
206
|
-
if (faMatch) finalAnswer = faMatch[1].trim();
|
|
207
|
-
}
|
|
208
|
-
return { name: toolName, args, thought, finalAnswer, raw: match[0], dialect: dp.dialect.name };
|
|
209
|
-
}
|
|
210
|
-
return null;
|
|
211
|
-
}
|
|
212
|
-
function detectReactDialect(text) {
|
|
213
|
-
for (const dp of ALL_DIALECT_PATTERNS) {
|
|
214
|
-
const tagPattern = new RegExp(`^\\s*${dp.dialect.actionTag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*`, "im");
|
|
215
|
-
if (tagPattern.test(text)) return dp.dialect;
|
|
216
|
-
}
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
function extractToolFromJson(obj) {
|
|
220
|
-
if (!obj || typeof obj !== "object") return null;
|
|
221
|
-
let name = obj.name || obj.function || obj.tool || obj.action;
|
|
222
|
-
let args = obj.arguments || obj.parameters || obj.args || obj.actionInput || {};
|
|
223
|
-
if (!name) {
|
|
224
|
-
for (const key of Object.keys(obj)) {
|
|
225
|
-
const kl = key.toLowerCase();
|
|
226
|
-
if (kl === "action" && typeof obj[key] === "string") {
|
|
227
|
-
name = obj[key];
|
|
228
|
-
}
|
|
229
|
-
if (kl === "action input" || kl === "actioninput" || kl === "action_input") {
|
|
230
|
-
const val = obj[key];
|
|
231
|
-
if (typeof val === "object" && val !== null) args = val;
|
|
232
|
-
else if (val) args = { input: val };
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
if (!name) {
|
|
237
|
-
const argToTool = { expression: "calculator", command: "shell" };
|
|
238
|
-
const nonToolKeys = /* @__PURE__ */ new Set(["response", "method", "answer", "result", "explanation", "output", "text"]);
|
|
239
|
-
const objKeys = Object.keys(obj);
|
|
240
|
-
if (!objKeys.some((k) => nonToolKeys.has(k))) {
|
|
241
|
-
for (const key of objKeys) {
|
|
242
|
-
if (key in argToTool) {
|
|
243
|
-
name = argToTool[key];
|
|
244
|
-
args = obj;
|
|
245
|
-
break;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
if (!name || typeof args !== "object" || args === null) return null;
|
|
251
|
-
return { name, args };
|
|
252
|
-
}
|
|
253
|
-
var WORD_MAPPINGS = {
|
|
254
|
-
calculate: ["calculator"],
|
|
255
|
-
calc: ["calculator"],
|
|
256
|
-
math: ["calculator"],
|
|
257
|
-
compute: ["calculator"],
|
|
258
|
-
eval: ["calculator"],
|
|
259
|
-
expression: ["calculator"],
|
|
260
|
-
power: ["calculator"],
|
|
261
|
-
pow: ["calculator"],
|
|
262
|
-
sqrt: ["calculator"],
|
|
263
|
-
python: ["shell"],
|
|
264
|
-
repl: ["shell"],
|
|
265
|
-
code: ["shell"],
|
|
266
|
-
execute: ["shell"],
|
|
267
|
-
shell: ["bash"],
|
|
268
|
-
bash: ["bash"],
|
|
269
|
-
cmd: ["bash"],
|
|
270
|
-
command: ["bash"],
|
|
271
|
-
ls: ["bash"],
|
|
272
|
-
cat: ["bash"],
|
|
273
|
-
echo: ["bash"],
|
|
274
|
-
grep: ["bash"],
|
|
275
|
-
read: ["read"],
|
|
276
|
-
write: ["write"],
|
|
277
|
-
file: ["read"],
|
|
278
|
-
weather: ["get_weather"],
|
|
279
|
-
search: ["bash"]
|
|
280
|
-
};
|
|
281
|
-
function fuzzyMatchToolName(hallucinated, availableTools) {
|
|
282
|
-
const lower = hallucinated.toLowerCase().replace(/_/g, "");
|
|
283
|
-
if (availableTools.includes(hallucinated)) return hallucinated;
|
|
284
|
-
for (const real of availableTools) {
|
|
285
|
-
const rl = real.toLowerCase().replace(/_/g, "");
|
|
286
|
-
if (rl === lower || rl.includes(lower) || lower.includes(rl)) return real;
|
|
287
|
-
}
|
|
288
|
-
for (const [keyword, hints] of Object.entries(WORD_MAPPINGS)) {
|
|
289
|
-
if (lower.includes(keyword)) {
|
|
290
|
-
for (const hint of hints) {
|
|
291
|
-
for (const real of availableTools) {
|
|
292
|
-
if (real.includes(hint) || real === hint) return real;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
if (lower.length >= 4) {
|
|
298
|
-
for (const real of availableTools) {
|
|
299
|
-
const rl = real.toLowerCase();
|
|
300
|
-
if (rl.length >= 4 && rl.slice(0, 4) === lower.slice(0, 4)) return real;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
return null;
|
|
304
|
-
}
|
|
305
|
-
var ARG_ALIASES = {
|
|
306
|
-
expression: ["expr", "exp", "formula", "calculation", "math"],
|
|
307
|
-
file_path: ["path", "filepath", "file", "filename", "location"],
|
|
308
|
-
content: ["text", "data", "body", "value"],
|
|
309
|
-
command: ["cmd", "shell", "script", "exec"],
|
|
310
|
-
url: ["uri", "link", "endpoint", "address"],
|
|
311
|
-
query: ["search", "term", "keywords", "q"],
|
|
312
|
-
input: ["value", "arg", "parameter"],
|
|
313
|
-
timeout: ["time_limit", "max_time", "seconds"]
|
|
314
|
-
};
|
|
315
|
-
function normalizeArguments(args, expectedParams) {
|
|
316
|
-
if (!args || typeof args !== "object") return args;
|
|
317
|
-
const expectedSet = new Set(expectedParams.map((p) => p.toLowerCase()));
|
|
318
|
-
const normalized = {};
|
|
319
|
-
const powerParts = {};
|
|
320
|
-
for (const [key, value] of Object.entries(args)) {
|
|
321
|
-
const keyLower = key.toLowerCase().replace(/-/g, "_");
|
|
322
|
-
let targetParam = null;
|
|
323
|
-
for (const param of expectedParams) {
|
|
324
|
-
if (param.toLowerCase() === keyLower) {
|
|
325
|
-
targetParam = param;
|
|
326
|
-
break;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
if (!targetParam) {
|
|
330
|
-
for (const [canonical, aliases] of Object.entries(ARG_ALIASES)) {
|
|
331
|
-
if (aliases.includes(keyLower) && expectedSet.has(canonical.toLowerCase())) {
|
|
332
|
-
targetParam = canonical;
|
|
333
|
-
break;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
if (!targetParam) {
|
|
338
|
-
for (const param of expectedParams) {
|
|
339
|
-
if (keyLower.includes(param.toLowerCase()) || keyLower.startsWith(param.toLowerCase())) {
|
|
340
|
-
targetParam = param;
|
|
341
|
-
break;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
if (["base", "value", "x"].includes(keyLower) || ["exponent", "power", "n", "p", "exp"].includes(keyLower)) {
|
|
346
|
-
powerParts[keyLower] = value;
|
|
347
|
-
continue;
|
|
348
|
-
}
|
|
349
|
-
const finalKey = targetParam || key;
|
|
350
|
-
if (!(finalKey in normalized)) normalized[finalKey] = value;
|
|
351
|
-
}
|
|
352
|
-
if (powerParts && expectedSet.has("expression")) {
|
|
353
|
-
const base = powerParts.base ?? powerParts.value ?? powerParts.x;
|
|
354
|
-
const exp = powerParts.exponent ?? powerParts.power ?? powerParts.n ?? powerParts.p ?? powerParts.exp;
|
|
355
|
-
if (base !== void 0 && exp !== void 0) normalized.expression = `${base} ** ${exp}`;
|
|
356
|
-
else if (base !== void 0) normalized.expression = String(base);
|
|
357
|
-
}
|
|
358
|
-
return normalized;
|
|
359
|
-
}
|
|
360
|
-
function looksLikeSchemaDump(text) {
|
|
361
|
-
if (!text) return false;
|
|
362
|
-
const indicators = [
|
|
363
|
-
'{"function <nil>',
|
|
364
|
-
'"type":"function"',
|
|
365
|
-
'"parameters":{"type":"object"',
|
|
366
|
-
'[{"type":',
|
|
367
|
-
'"required":',
|
|
368
|
-
'"properties":'
|
|
369
|
-
];
|
|
370
|
-
const lower = text.toLowerCase();
|
|
371
|
-
const matches = indicators.filter((i) => lower.includes(i.toLowerCase())).length;
|
|
372
|
-
return matches >= 2;
|
|
373
|
-
}
|
|
7
|
+
import {
|
|
8
|
+
sanitizeModelJson,
|
|
9
|
+
extractToolFromJson,
|
|
10
|
+
parseReact,
|
|
11
|
+
parseReactWithPatterns,
|
|
12
|
+
detectReactDialect,
|
|
13
|
+
fuzzyMatchToolName,
|
|
14
|
+
normalizeArguments,
|
|
15
|
+
looksLikeSchemaDump,
|
|
16
|
+
REACT_DIALECTS,
|
|
17
|
+
ALL_DIALECT_PATTERNS,
|
|
18
|
+
FINAL_ANSWER_RE
|
|
19
|
+
} from "@vtstech/pi-shared/react-parser";
|
|
374
20
|
var REACT_CONFIG_PATH = path.join(os.homedir(), ".pi", "agent", "react-mode.json");
|
|
375
21
|
function readReactConfig() {
|
|
376
22
|
try {
|
|
@@ -387,7 +33,7 @@ function writeReactConfig(config) {
|
|
|
387
33
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
388
34
|
fs.writeFileSync(REACT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
389
35
|
}
|
|
390
|
-
function react_fallback_temp_default(
|
|
36
|
+
function react_fallback_temp_default(pi) {
|
|
391
37
|
let reactModeEnabled = readReactConfig().enabled;
|
|
392
38
|
let stats = { bridgeCalls: 0, fuzzyMatches: 0, argNormalizations: 0, parseFailures: 0 };
|
|
393
39
|
const branding = [
|
|
@@ -397,7 +43,7 @@ function react_fallback_temp_default(pi2) {
|
|
|
397
43
|
` Website: www.vts-tech.org`
|
|
398
44
|
].join("\n");
|
|
399
45
|
function registerBridgeTool() {
|
|
400
|
-
|
|
46
|
+
pi.registerTool({
|
|
401
47
|
name: "tool_call",
|
|
402
48
|
label: "Universal Tool Call",
|
|
403
49
|
description: `Universal tool call bridge. Use this to call any available tool by specifying its name and arguments as JSON.
|
|
@@ -434,7 +80,7 @@ The bridge will match your tool name (fuzzy matching supported) and normalize ar
|
|
|
434
80
|
} catch {
|
|
435
81
|
args = { input: argsStr };
|
|
436
82
|
}
|
|
437
|
-
const allTools =
|
|
83
|
+
const allTools = pi.getAllTools();
|
|
438
84
|
let targetToolName = null;
|
|
439
85
|
if (allTools.includes(requestedName)) {
|
|
440
86
|
targetToolName = requestedName;
|
|
@@ -468,7 +114,7 @@ ${argsJson}`
|
|
|
468
114
|
if (reactModeEnabled) {
|
|
469
115
|
registerBridgeTool();
|
|
470
116
|
}
|
|
471
|
-
|
|
117
|
+
pi.on("context", (event) => {
|
|
472
118
|
if (!reactModeEnabled) return;
|
|
473
119
|
const model = event.messages;
|
|
474
120
|
for (let i = model.length - 1; i >= 0; i--) {
|
|
@@ -482,7 +128,7 @@ ${argsJson}`
|
|
|
482
128
|
}
|
|
483
129
|
}
|
|
484
130
|
});
|
|
485
|
-
|
|
131
|
+
pi.registerCommand("react-mode", {
|
|
486
132
|
description: "Toggle ReAct fallback mode for models without native tool calling",
|
|
487
133
|
handler: async (_args, ctx) => {
|
|
488
134
|
reactModeEnabled = !reactModeEnabled;
|
|
@@ -507,14 +153,14 @@ ${argsJson}`
|
|
|
507
153
|
lines.push(info("Run /reload to remove the tool from the current model"));
|
|
508
154
|
}
|
|
509
155
|
const report = lines.join("\n");
|
|
510
|
-
|
|
156
|
+
pi.sendMessage({
|
|
511
157
|
customType: "react-mode-report",
|
|
512
158
|
content: report,
|
|
513
159
|
display: { type: "content", content: report }
|
|
514
160
|
});
|
|
515
161
|
}
|
|
516
162
|
});
|
|
517
|
-
|
|
163
|
+
pi.registerCommand("react-parse", {
|
|
518
164
|
description: "Test the ReAct parser against a text input: /react-parse <text>",
|
|
519
165
|
handler: async (args, ctx) => {
|
|
520
166
|
const text = args.trim();
|
|
@@ -568,14 +214,14 @@ ${argsJson}`
|
|
|
568
214
|
const fa = faMatch[1].trim();
|
|
569
215
|
lines.push(ok(`Final Answer: ${fa}`));
|
|
570
216
|
}
|
|
571
|
-
|
|
217
|
+
pi.sendMessage({
|
|
572
218
|
customType: "react-parse-report",
|
|
573
219
|
content: lines.join("\n"),
|
|
574
220
|
display: { type: "content", content: lines.join("\n") }
|
|
575
221
|
});
|
|
576
222
|
}
|
|
577
223
|
});
|
|
578
|
-
|
|
224
|
+
pi._reactParser = {
|
|
579
225
|
parseReact,
|
|
580
226
|
parseReactWithPatterns,
|
|
581
227
|
detectReactDialect,
|
|
@@ -589,6 +235,5 @@ ${argsJson}`
|
|
|
589
235
|
};
|
|
590
236
|
}
|
|
591
237
|
export {
|
|
592
|
-
react_fallback_temp_default as default
|
|
593
|
-
detectReactDialect
|
|
238
|
+
react_fallback_temp_default as default
|
|
594
239
|
};
|