@vtstech/pi-react-fallback 1.0.8 → 1.0.9
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 +128 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vtstech/pi-react-fallback",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
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.0.
|
|
17
|
+
"@vtstech/pi-shared": "1.0.9"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
20
|
"@mariozechner/pi-coding-agent": ">=0.66"
|
package/react-fallback.js
CHANGED
|
@@ -15,12 +15,72 @@ function sanitizeModelJson(text) {
|
|
|
15
15
|
text = text.replace(/\\\\\\\\/g, "\\\\");
|
|
16
16
|
return text;
|
|
17
17
|
}
|
|
18
|
-
var
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
var REACT_DIALECTS = [
|
|
19
|
+
{
|
|
20
|
+
name: "react",
|
|
21
|
+
actionTag: "Action:",
|
|
22
|
+
inputTag: "Action Input:",
|
|
23
|
+
thoughtTag: "Thought:",
|
|
24
|
+
stopTags: ["Observation:", "Thought:", "Final Answer:", "Action:"],
|
|
25
|
+
finalTag: "Final Answer:"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "function",
|
|
29
|
+
actionTag: "Function:",
|
|
30
|
+
inputTag: "Function Input:",
|
|
31
|
+
thoughtTag: "Thought:",
|
|
32
|
+
stopTags: ["Observation:", "Thought:", "Final Answer:", "Function:", "Action:"],
|
|
33
|
+
finalTag: "Final Answer:"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "tool",
|
|
37
|
+
actionTag: "Tool:",
|
|
38
|
+
inputTag: "Tool Input:",
|
|
39
|
+
thoughtTag: "Thought:",
|
|
40
|
+
stopTags: ["Observation:", "Thought:", "Final Answer:", "Tool:", "Action:"],
|
|
41
|
+
finalTag: "Final Answer:"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "call",
|
|
45
|
+
actionTag: "Call:",
|
|
46
|
+
inputTag: "Input:",
|
|
47
|
+
thoughtTag: "Thought:",
|
|
48
|
+
stopTags: ["Observation:", "Thought:", "Final Answer:", "Call:", "Action:"],
|
|
49
|
+
finalTag: "Final Answer:"
|
|
50
|
+
}
|
|
51
|
+
];
|
|
52
|
+
function buildDialectPatterns(d) {
|
|
53
|
+
const esc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
54
|
+
const aT = esc(d.actionTag);
|
|
55
|
+
const iT = esc(d.inputTag);
|
|
56
|
+
const stopAlt = d.stopTags.map(esc).join("|");
|
|
57
|
+
const tT = d.thoughtTag ? esc(d.thoughtTag) : void 0;
|
|
58
|
+
const fT = d.finalTag ? esc(d.finalTag) : void 0;
|
|
59
|
+
const thoughtRe = tT ? new RegExp(`${tT}\\s*(.*?)(?=${aT}|${fT}|$)`, "is") : void 0;
|
|
60
|
+
const actionRe = new RegExp(
|
|
61
|
+
`${aT}\\s*[\\x60"']?(\\w+)[\\x60"']?\\s*\\n?\\s*${iT}\\s*(.*?)(?=\\n\\s*(?:${stopAlt})|$)`,
|
|
62
|
+
"is"
|
|
63
|
+
);
|
|
64
|
+
const actionReSameline = new RegExp(
|
|
65
|
+
`${aT}\\s*[\\x60"']?(\\w+)[\\x60"']?\\s+${iT}\\s*(.*?)(?=\\n\\s*(?:${stopAlt})|$)`,
|
|
66
|
+
"is"
|
|
67
|
+
);
|
|
68
|
+
const actionReLoose = new RegExp(
|
|
69
|
+
`${aT}\\s*(.+?)\\n\\s*${iT}\\s*(.*?)(?=\\n\\s*(?:${stopAlt})|$)`,
|
|
70
|
+
"is"
|
|
71
|
+
);
|
|
72
|
+
const actionReParen = new RegExp(`${aT}\\s*(\\w+)\\s*\\(([^)]*)\\)`, "i");
|
|
73
|
+
const finalAnswerRe = fT ? new RegExp(`${fT}\\s*([\\s\\S]*?)$`, "i") : void 0;
|
|
74
|
+
return { thoughtRe, actionRe, actionReSameline, actionReLoose, actionReParen, finalAnswerRe, dialect: d };
|
|
75
|
+
}
|
|
76
|
+
var ALL_DIALECT_PATTERNS = REACT_DIALECTS.map(buildDialectPatterns);
|
|
77
|
+
var CLASSIC_PATTERNS = ALL_DIALECT_PATTERNS[0];
|
|
78
|
+
var THOUGHT_RE = CLASSIC_PATTERNS.thoughtRe;
|
|
79
|
+
var ACTION_RE = CLASSIC_PATTERNS.actionRe;
|
|
80
|
+
var ACTION_RE_SAMELINE = CLASSIC_PATTERNS.actionReSameline;
|
|
81
|
+
var ACTION_RE_LOOSE = CLASSIC_PATTERNS.actionReLoose;
|
|
82
|
+
var ACTION_RE_PAREN = CLASSIC_PATTERNS.actionReParen;
|
|
83
|
+
var FINAL_ANSWER_RE = CLASSIC_PATTERNS.finalAnswerRe;
|
|
24
84
|
function extractJsonArgs(rawArgs) {
|
|
25
85
|
const start = rawArgs.indexOf("{");
|
|
26
86
|
if (start === -1) return null;
|
|
@@ -56,18 +116,43 @@ function extractJsonArgs(rawArgs) {
|
|
|
56
116
|
return { input: jsonStr };
|
|
57
117
|
}
|
|
58
118
|
function parseReact(text) {
|
|
119
|
+
for (const dp of ALL_DIALECT_PATTERNS) {
|
|
120
|
+
const result = parseReactWithPatterns(text, dp);
|
|
121
|
+
if (result) return result;
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
function parseReactWithPatterns(text, dp, tightLoose = false) {
|
|
59
126
|
let thought;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
127
|
+
if (dp.thoughtRe) {
|
|
128
|
+
const thoughtMatch = dp.thoughtRe.exec(text);
|
|
129
|
+
if (thoughtMatch) thought = thoughtMatch[1].trim();
|
|
130
|
+
}
|
|
131
|
+
let match = dp.actionRe.exec(text);
|
|
132
|
+
if (!match) match = dp.actionReSameline.exec(text);
|
|
64
133
|
let looseMatch = false;
|
|
65
|
-
if (!match)
|
|
134
|
+
if (!match) {
|
|
135
|
+
const looseResult = dp.actionReLoose.exec(text);
|
|
136
|
+
if (looseResult) {
|
|
137
|
+
if (tightLoose) {
|
|
138
|
+
const candidate = looseResult[1].trim().replace(/[`"']/g, "");
|
|
139
|
+
const isToolIdentifier = /^\w+$/.test(candidate) && (candidate.includes("_") || candidate.includes("-"));
|
|
140
|
+
const isKnownTool = /^(get_weather|calculate)$/i.test(candidate);
|
|
141
|
+
if (isToolIdentifier || isKnownTool) {
|
|
142
|
+
match = looseResult;
|
|
143
|
+
looseMatch = true;
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
match = looseResult;
|
|
147
|
+
looseMatch = true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
66
151
|
let parenMatch = false;
|
|
67
|
-
if (!match) match =
|
|
152
|
+
if (!match) match = dp.actionReParen.exec(text), parenMatch = true;
|
|
68
153
|
if (match) {
|
|
69
154
|
let toolName = match[1].trim().replace(/[`"']/g, "");
|
|
70
|
-
if (looseMatch && pi.context?.session?.tools) {
|
|
155
|
+
if (looseMatch && !tightLoose && pi.context?.session?.tools) {
|
|
71
156
|
const availableTools = pi.context.session.tools || [];
|
|
72
157
|
for (const real of availableTools) {
|
|
73
158
|
const rl = real.toLowerCase().replace(/_/g, "");
|
|
@@ -115,9 +200,18 @@ function parseReact(text) {
|
|
|
115
200
|
args = extractJsonArgs(rawArgs) || { input: rawArgs };
|
|
116
201
|
}
|
|
117
202
|
let finalAnswer;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
203
|
+
if (dp.finalAnswerRe) {
|
|
204
|
+
const faMatch = dp.finalAnswerRe.exec(text);
|
|
205
|
+
if (faMatch) finalAnswer = faMatch[1].trim();
|
|
206
|
+
}
|
|
207
|
+
return { name: toolName, args, thought, finalAnswer, raw: match[0], dialect: dp.dialect.name };
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
function detectReactDialect(text) {
|
|
212
|
+
for (const dp of ALL_DIALECT_PATTERNS) {
|
|
213
|
+
const tagPattern = new RegExp(`^\\s*${dp.dialect.actionTag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*`, "im");
|
|
214
|
+
if (tagPattern.test(text)) return dp.dialect;
|
|
121
215
|
}
|
|
122
216
|
return null;
|
|
123
217
|
}
|
|
@@ -296,7 +390,7 @@ function react_fallback_temp_default(pi2) {
|
|
|
296
390
|
let reactModeEnabled = readReactConfig().enabled;
|
|
297
391
|
let stats = { bridgeCalls: 0, fuzzyMatches: 0, argNormalizations: 0, parseFailures: 0 };
|
|
298
392
|
const branding = [
|
|
299
|
-
` \u26A1 Pi ReAct Fallback Extension v1.0.
|
|
393
|
+
` \u26A1 Pi ReAct Fallback Extension v1.0.9`,
|
|
300
394
|
` Written by VTSTech`,
|
|
301
395
|
` GitHub: https://github.com/VTSTech`,
|
|
302
396
|
` Website: www.vts-tech.org`
|
|
@@ -430,15 +524,23 @@ ${argsJson}`
|
|
|
430
524
|
const lines = [branding];
|
|
431
525
|
lines.push(section("REACT PARSER TEST"));
|
|
432
526
|
lines.push(info(`Input: ${text.slice(0, 100)}${text.length > 100 ? "..." : ""}`));
|
|
527
|
+
const detectedDialect = detectReactDialect(text);
|
|
433
528
|
const reactResult = parseReact(text);
|
|
434
529
|
if (reactResult) {
|
|
435
|
-
lines.push(ok(
|
|
530
|
+
lines.push(ok(`ReAct format detected! (dialect: ${reactResult.dialect || "react"})`));
|
|
436
531
|
lines.push(info(`Tool: ${reactResult.name}`));
|
|
437
532
|
lines.push(info(`Args: ${JSON.stringify(reactResult.args)}`));
|
|
438
533
|
if (reactResult.thought) lines.push(info(`Thought: ${reactResult.thought}`));
|
|
439
534
|
if (reactResult.finalAnswer) lines.push(info(`Final Answer: ${reactResult.finalAnswer}`));
|
|
440
535
|
} else {
|
|
441
|
-
|
|
536
|
+
if (detectedDialect) {
|
|
537
|
+
lines.push(warn(`Dialect tag "${detectedDialect.actionTag}" detected but no valid tool call parsed`));
|
|
538
|
+
} else {
|
|
539
|
+
lines.push(fail("No ReAct format detected"));
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
if (detectedDialect && detectedDialect.name !== "react") {
|
|
543
|
+
lines.push(info(`Detected dialect: ${detectedDialect.name} (${detectedDialect.actionTag} / ${detectedDialect.inputTag})`));
|
|
442
544
|
}
|
|
443
545
|
try {
|
|
444
546
|
const firstBrace = text.indexOf("{");
|
|
@@ -473,13 +575,18 @@ ${argsJson}`
|
|
|
473
575
|
});
|
|
474
576
|
pi2._reactParser = {
|
|
475
577
|
parseReact,
|
|
578
|
+
parseReactWithPatterns,
|
|
579
|
+
detectReactDialect,
|
|
476
580
|
sanitizeModelJson,
|
|
477
581
|
extractToolFromJson,
|
|
478
582
|
fuzzyMatchToolName,
|
|
479
583
|
normalizeArguments,
|
|
480
|
-
looksLikeSchemaDump
|
|
584
|
+
looksLikeSchemaDump,
|
|
585
|
+
REACT_DIALECTS,
|
|
586
|
+
ALL_DIALECT_PATTERNS
|
|
481
587
|
};
|
|
482
588
|
}
|
|
483
589
|
export {
|
|
484
|
-
react_fallback_temp_default as default
|
|
590
|
+
react_fallback_temp_default as default,
|
|
591
|
+
detectReactDialect
|
|
485
592
|
};
|