verifiable-thinking-mcp 0.4.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.
- package/LICENSE +21 -0
- package/README.md +339 -0
- package/package.json +75 -0
- package/src/index.ts +38 -0
- package/src/lib/cache.ts +246 -0
- package/src/lib/compression.ts +804 -0
- package/src/lib/compute/cache.ts +86 -0
- package/src/lib/compute/classifier.ts +555 -0
- package/src/lib/compute/confidence.ts +79 -0
- package/src/lib/compute/context.ts +154 -0
- package/src/lib/compute/extract.ts +200 -0
- package/src/lib/compute/filter.ts +224 -0
- package/src/lib/compute/index.ts +171 -0
- package/src/lib/compute/math.ts +247 -0
- package/src/lib/compute/patterns.ts +564 -0
- package/src/lib/compute/registry.ts +145 -0
- package/src/lib/compute/solvers/arithmetic.ts +65 -0
- package/src/lib/compute/solvers/calculus.ts +249 -0
- package/src/lib/compute/solvers/derivation-core.ts +371 -0
- package/src/lib/compute/solvers/derivation-latex.ts +160 -0
- package/src/lib/compute/solvers/derivation-mistakes.ts +1046 -0
- package/src/lib/compute/solvers/derivation-simplify.ts +451 -0
- package/src/lib/compute/solvers/derivation-transform.ts +620 -0
- package/src/lib/compute/solvers/derivation.ts +67 -0
- package/src/lib/compute/solvers/facts.ts +120 -0
- package/src/lib/compute/solvers/formula.ts +728 -0
- package/src/lib/compute/solvers/index.ts +36 -0
- package/src/lib/compute/solvers/logic.ts +422 -0
- package/src/lib/compute/solvers/probability.ts +307 -0
- package/src/lib/compute/solvers/statistics.ts +262 -0
- package/src/lib/compute/solvers/word-problems.ts +408 -0
- package/src/lib/compute/types.ts +107 -0
- package/src/lib/concepts.ts +111 -0
- package/src/lib/domain.ts +731 -0
- package/src/lib/extraction.ts +912 -0
- package/src/lib/index.ts +122 -0
- package/src/lib/judge.ts +260 -0
- package/src/lib/math/ast.ts +842 -0
- package/src/lib/math/index.ts +8 -0
- package/src/lib/math/operators.ts +171 -0
- package/src/lib/math/tokenizer.ts +477 -0
- package/src/lib/patterns.ts +200 -0
- package/src/lib/session.ts +825 -0
- package/src/lib/think/challenge.ts +323 -0
- package/src/lib/think/complexity.ts +504 -0
- package/src/lib/think/confidence-drift.ts +507 -0
- package/src/lib/think/consistency.ts +347 -0
- package/src/lib/think/guidance.ts +188 -0
- package/src/lib/think/helpers.ts +568 -0
- package/src/lib/think/hypothesis.ts +216 -0
- package/src/lib/think/index.ts +127 -0
- package/src/lib/think/prompts.ts +262 -0
- package/src/lib/think/route.ts +358 -0
- package/src/lib/think/schema.ts +98 -0
- package/src/lib/think/scratchpad-schema.ts +662 -0
- package/src/lib/think/spot-check.ts +961 -0
- package/src/lib/think/types.ts +93 -0
- package/src/lib/think/verification.ts +260 -0
- package/src/lib/tokens.ts +177 -0
- package/src/lib/verification.ts +620 -0
- package/src/prompts/index.ts +10 -0
- package/src/prompts/templates.ts +336 -0
- package/src/resources/index.ts +8 -0
- package/src/resources/sessions.ts +196 -0
- package/src/tools/compress.ts +138 -0
- package/src/tools/index.ts +5 -0
- package/src/tools/scratchpad.ts +2659 -0
- package/src/tools/sessions.ts +144 -0
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEMANTIC COMPLEXITY ROUTER v3
|
|
3
|
+
*
|
|
4
|
+
* Architecture: Linguistic feature extraction + semantic composition
|
|
5
|
+
* No hardcoded term weights; principles-driven scoring formula:
|
|
6
|
+
*
|
|
7
|
+
* complexity = verb_base × domain_weight × intensity_modifier
|
|
8
|
+
*
|
|
9
|
+
* Key improvements over v2:
|
|
10
|
+
* - "explain why" handled via semantic boosters (counterintuitive, meta-cognitive)
|
|
11
|
+
* - Probability/statistics domain properly weighted (0.80)
|
|
12
|
+
* - Intensity modifiers for quantifiers, impossibility, comparative language
|
|
13
|
+
* - Negation correction ("not difficult" → lower score)
|
|
14
|
+
* - Compositional semantics: no magic weights, principled formula
|
|
15
|
+
* - Domain detection now uses unified src/lib/domain.ts
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { getDomainWeight } from "../domain.ts";
|
|
19
|
+
|
|
20
|
+
export interface ComplexityResult {
|
|
21
|
+
score: number;
|
|
22
|
+
tier: "Low" | "Moderate" | "High" | "Very Hard" | "Almost Impossible";
|
|
23
|
+
/** Confidence bounds for calibration feedback */
|
|
24
|
+
confidence: {
|
|
25
|
+
/** Router confidence in this classification (0-1) */
|
|
26
|
+
level: number;
|
|
27
|
+
/** Is this score in the gray zone (0.28-0.72)? */
|
|
28
|
+
inGrayZone: boolean;
|
|
29
|
+
/** Distance from nearest tier boundary */
|
|
30
|
+
boundaryDistance: number;
|
|
31
|
+
};
|
|
32
|
+
explanation: {
|
|
33
|
+
verb_type: string;
|
|
34
|
+
verb_score: number;
|
|
35
|
+
domain_detected: string;
|
|
36
|
+
domain_weight: number;
|
|
37
|
+
intensity_signals: string[];
|
|
38
|
+
intensity_modifier: number;
|
|
39
|
+
};
|
|
40
|
+
reasoning: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function assessPromptComplexity(text: string): ComplexityResult {
|
|
44
|
+
const lower = text.toLowerCase();
|
|
45
|
+
|
|
46
|
+
// ===== PHASE 1: VERB EXTRACTION =====
|
|
47
|
+
// Linguistic primary: the main reasoning action requested
|
|
48
|
+
const verbHierarchy: [string, number][] = [
|
|
49
|
+
["prove or refute", 0.95],
|
|
50
|
+
["prove that", 0.95],
|
|
51
|
+
["prove", 0.95],
|
|
52
|
+
["refute", 0.95],
|
|
53
|
+
["derive the", 0.85],
|
|
54
|
+
["derive", 0.8],
|
|
55
|
+
["construct a", 0.78],
|
|
56
|
+
["construct", 0.75],
|
|
57
|
+
["design", 0.73],
|
|
58
|
+
["implement", 0.7],
|
|
59
|
+
["explain why", 0.7], // Elevated - causal reasoning requires deep understanding
|
|
60
|
+
["explain how", 0.65],
|
|
61
|
+
["explain", 0.55],
|
|
62
|
+
// Bare "why" questions - causal reasoning without "explain"
|
|
63
|
+
["why can't", 0.7],
|
|
64
|
+
["why cannot", 0.7],
|
|
65
|
+
["why doesn't", 0.65],
|
|
66
|
+
["why does", 0.62],
|
|
67
|
+
["why do", 0.62],
|
|
68
|
+
["why is", 0.6],
|
|
69
|
+
["why are", 0.6],
|
|
70
|
+
["describe", 0.45],
|
|
71
|
+
["compare", 0.48],
|
|
72
|
+
["analyze", 0.5],
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
let verb_type = "generic";
|
|
76
|
+
let verb_base = 0.4; // default if no verb detected
|
|
77
|
+
|
|
78
|
+
// Match longest verb first (sorted by specificity in the array)
|
|
79
|
+
for (const [verb, weight] of verbHierarchy) {
|
|
80
|
+
if (lower.includes(verb)) {
|
|
81
|
+
verb_type = verb;
|
|
82
|
+
verb_base = weight;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ===== PHASE 2: SEMANTIC BOOSTERS =====
|
|
88
|
+
// Patterns that elevate the base verb complexity
|
|
89
|
+
let verb_boosted = verb_base;
|
|
90
|
+
|
|
91
|
+
// Booster 1: Counterintuitive signals (indicate cognitive rigor needed)
|
|
92
|
+
const counterintuitive_keywords = [
|
|
93
|
+
"counterintuitive",
|
|
94
|
+
"paradox",
|
|
95
|
+
"paradoxical",
|
|
96
|
+
"surprising",
|
|
97
|
+
"unintuitive",
|
|
98
|
+
"confusing",
|
|
99
|
+
"why is this",
|
|
100
|
+
"why does this seem",
|
|
101
|
+
"seems wrong",
|
|
102
|
+
"trick question",
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
const has_counterintuitive = counterintuitive_keywords.some((kw) => lower.includes(kw));
|
|
106
|
+
|
|
107
|
+
if (has_counterintuitive && verb_base < 0.75) {
|
|
108
|
+
verb_boosted += 0.15;
|
|
109
|
+
verb_type += " [counterintuitive]";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Booster 2: Meta-cognitive reasoning (reasoning about reasoning/human cognition)
|
|
113
|
+
const metacognitive_patterns = [
|
|
114
|
+
/why do (people|we|humans)/,
|
|
115
|
+
/why don't (people|we|humans)/,
|
|
116
|
+
/initially guess/,
|
|
117
|
+
/systematic(ally)? (fail|error|mistake)/,
|
|
118
|
+
/cognitive bias/,
|
|
119
|
+
/intuition (vs|versus) reality/,
|
|
120
|
+
/what makes (people|us|humans)/,
|
|
121
|
+
/common mistake/,
|
|
122
|
+
/why (most|many) people/,
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
const has_metacognitive = metacognitive_patterns.some((pattern) => pattern.test(lower));
|
|
126
|
+
|
|
127
|
+
if (has_metacognitive) {
|
|
128
|
+
verb_boosted += 0.1;
|
|
129
|
+
verb_type += " [meta-cognitive]";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Booster 3: Step-by-step / rigorous requirement
|
|
133
|
+
const rigor_patterns = [
|
|
134
|
+
/step[- ]by[- ]step/,
|
|
135
|
+
/rigorously/,
|
|
136
|
+
/formally prove/,
|
|
137
|
+
/show (your|the) work/,
|
|
138
|
+
/detailed explanation/,
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
const has_rigor = rigor_patterns.some((p) => p.test(lower));
|
|
142
|
+
if (has_rigor) {
|
|
143
|
+
verb_boosted += 0.08;
|
|
144
|
+
verb_type += " [rigorous]";
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Booster 4: Trap detection (questions that look simple but aren't)
|
|
148
|
+
// Short questions with numbers and keywords that indicate deceptive simplicity
|
|
149
|
+
const trap_patterns = [
|
|
150
|
+
/\b(cost|price|pay|spend|total|together)\b.*\$?\d/i, // Bat & Ball style
|
|
151
|
+
/\$?\d+.*\b(cost|price|pay|spend|total|together)\b/i, // Reversed order
|
|
152
|
+
/how many.*\d+!/i, // Factorial problems (100!)
|
|
153
|
+
/trailing zero/i, // Number theory trap
|
|
154
|
+
/\d+\s*(ball|bat|item|object)s?\b/i, // Word problem with objects
|
|
155
|
+
/\d+\s*machines?.*\d+\s*(minutes?|widgets?)/i, // Rate problems (5 machines, 5 minutes)
|
|
156
|
+
/\d+\s*(black|white|red|blue).*socks?/i, // Pigeonhole principle
|
|
157
|
+
/minimum.*guarantee/i, // Pigeonhole phrasing
|
|
158
|
+
/clock.*hands?.*overlap/i, // Clock problems
|
|
159
|
+
/hands?.*clock.*overlap/i, // Clock problems alt
|
|
160
|
+
/average speed.*round trip/i, // Harmonic mean trap
|
|
161
|
+
/round trip.*average speed/i, // Harmonic mean trap alt
|
|
162
|
+
/\d+\s*mph.*returns?.*\d+\s*mph/i, // Speed trap
|
|
163
|
+
/doubles?.*every day.*\d+\s*days/i, // Exponential growth trap (lily pad)
|
|
164
|
+
/\d+\s*days.*cover.*lake/i, // Lily pad variant
|
|
165
|
+
/overlap.*\d+\s*hours/i, // Clock overlap
|
|
166
|
+
/\d+\s*hours.*overlap/i, // Clock overlap alt
|
|
167
|
+
/times.*hands.*overlap/i, // Clock overlap natural phrasing
|
|
168
|
+
/times.*overlap/i, // Generic overlap question
|
|
169
|
+
/wason|selection task/i, // Wason card task
|
|
170
|
+
/vowel.*even|even.*vowel/i, // Wason card rule pattern
|
|
171
|
+
/how many ways.*arrange/i, // Combinatorics
|
|
172
|
+
/arrange.*letters/i, // Anagram problems
|
|
173
|
+
/letters.*arrange/i, // Anagram variant
|
|
174
|
+
/how many ways.*letters/i, // Anagram direct question
|
|
175
|
+
/\^\d{2,}/i, // Large exponents (7^100)
|
|
176
|
+
/mod\s+\d+/i, // Modular arithmetic
|
|
177
|
+
/last digit/i, // Modular arithmetic variant
|
|
178
|
+
/\d+-chamber.*bullet/i, // Russian roulette style
|
|
179
|
+
/revolver.*bullet/i, // Russian roulette variant
|
|
180
|
+
/adjacent bullet/i, // Conditional probability setup
|
|
181
|
+
// Self-reference paradoxes
|
|
182
|
+
/this statement.*(true|false|itself)/i, // Liar paradox
|
|
183
|
+
/statement.*refer.*itself/i, // Self-reference
|
|
184
|
+
// Sample size / statistical reasoning
|
|
185
|
+
/which.*(more|less) reliable/i, // Sample size comparison
|
|
186
|
+
/survey of \d+/i, // Sample size context
|
|
187
|
+
/sample.*(size|of \d+)/i, // Sample size explicit
|
|
188
|
+
// Simpson's paradox
|
|
189
|
+
/(overall|aggregate|total).*compar/i, // Aggregation paradox
|
|
190
|
+
/hospital.*surviv/i, // Simpson's classic example
|
|
191
|
+
/(mild|severe).*(surviv|rate)/i, // Subgroup analysis
|
|
192
|
+
/better overall/i, // Simpson's phrasing
|
|
193
|
+
// Expected value / geometric distribution
|
|
194
|
+
/expected (number|value).*(flip|roll|toss|draw|trial)/i, // EV questions
|
|
195
|
+
/until you get (heads|tails|a \w+)/i, // Geometric distribution
|
|
196
|
+
/flip.*until/i, // Stopping time problems
|
|
197
|
+
// Logic meta-questions (validity judgments)
|
|
198
|
+
/\b(VALID|INVALID|UNSOUND|SOUND)\b/i, // Logic validity options
|
|
199
|
+
/valid.*argument|argument.*valid/i, // Validity question
|
|
200
|
+
// Sorites / vagueness paradoxes
|
|
201
|
+
/heap.*grain|grain.*heap/i, // Sorites paradox
|
|
202
|
+
/\d+ grain/i, // Sorites setup
|
|
203
|
+
// Counterintuitive probability problems (100 prisoners, Monty Hall style)
|
|
204
|
+
/\d+\s*prisoners?.*\d+\s*boxes/i, // 100 prisoners problem
|
|
205
|
+
/prisoners?.*boxes.*strategy/i, // Prisoners/boxes with strategy
|
|
206
|
+
/loop[\s-]*follow|cycle[\s-]*strategy/i, // Loop-following strategy
|
|
207
|
+
/survival.*probability.*strategy/i, // Strategy + survival
|
|
208
|
+
/strategy.*survival.*probability/i, // Strategy + survival alt
|
|
209
|
+
/monty hall/i, // Monty Hall problem
|
|
210
|
+
/switch.*doors?|doors?.*switch/i, // Monty Hall phrasing
|
|
211
|
+
/\d+\s*doors?.*goat/i, // Monty Hall variant
|
|
212
|
+
];
|
|
213
|
+
const has_trap_pattern = trap_patterns.some((p) => p.test(text)); // Use original text for case
|
|
214
|
+
const is_short_with_numbers = text.length < 350 && /\d/.test(text); // Increased threshold
|
|
215
|
+
|
|
216
|
+
// Some traps don't have numbers but are still traps (anagrams, combinatorics)
|
|
217
|
+
const is_numberless_trap =
|
|
218
|
+
text.length < 200 &&
|
|
219
|
+
(/arrange.*letters/i.test(text) ||
|
|
220
|
+
/permutation.*letters/i.test(text) ||
|
|
221
|
+
/how many ways/i.test(text));
|
|
222
|
+
|
|
223
|
+
// Track trap detection - will add to intensity_signals after it's declared
|
|
224
|
+
const trap_detected = (has_trap_pattern && is_short_with_numbers) || is_numberless_trap;
|
|
225
|
+
if (trap_detected && verb_base < 0.7) {
|
|
226
|
+
verb_boosted += 0.25; // Strong boost for trap questions
|
|
227
|
+
verb_type += " [trap-detected]";
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
verb_boosted = Math.min(0.99, verb_boosted); // cap at 0.99
|
|
231
|
+
|
|
232
|
+
// ===== PHASE 3: DOMAIN DETECTION =====
|
|
233
|
+
// Use unified domain detector from src/lib/domain.ts
|
|
234
|
+
const { domain: domain_name, weight: domain_weight } = getDomainWeight(text);
|
|
235
|
+
|
|
236
|
+
// ===== PHASE 4: INTENSITY MODIFIERS =====
|
|
237
|
+
// Signals that increase reasoning depth without changing domain
|
|
238
|
+
let intensity_mod = 1.0;
|
|
239
|
+
const intensity_signals: string[] = [];
|
|
240
|
+
|
|
241
|
+
// Add trap pattern to intensity signals (detected in Phase 2)
|
|
242
|
+
if (trap_detected) {
|
|
243
|
+
intensity_signals.push("trap_pattern");
|
|
244
|
+
intensity_mod *= 1.15; // Additional multiplier for traps
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Quantifiers (logical depth)
|
|
248
|
+
const quantifier_patterns = [
|
|
249
|
+
/\ball\b/,
|
|
250
|
+
/\bany\b/,
|
|
251
|
+
/\bnon-?trivial/,
|
|
252
|
+
/\bevery\b/,
|
|
253
|
+
/\bno\s+\w+\s+can\b/,
|
|
254
|
+
];
|
|
255
|
+
if (quantifier_patterns.some((p) => p.test(lower))) {
|
|
256
|
+
intensity_mod *= 1.1;
|
|
257
|
+
intensity_signals.push("quantifier");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Impossibility/negative results (open problem signals)
|
|
261
|
+
const impossibility_patterns = [
|
|
262
|
+
/cannot (achieve|scale|exceed|be solved)/,
|
|
263
|
+
/\bno\b.*\balgorithm\b/,
|
|
264
|
+
/\bimpossible to\b/,
|
|
265
|
+
/why (no|not|can't|cannot)\b/,
|
|
266
|
+
/prove.*impossible/,
|
|
267
|
+
];
|
|
268
|
+
if (impossibility_patterns.some((p) => p.test(lower))) {
|
|
269
|
+
intensity_mod *= 1.08;
|
|
270
|
+
intensity_signals.push("impossibility");
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Comparative language (optimality, bounds)
|
|
274
|
+
const comparative_patterns = [
|
|
275
|
+
/faster than/,
|
|
276
|
+
/better than/,
|
|
277
|
+
/worse than/,
|
|
278
|
+
/optimal/,
|
|
279
|
+
/upper bound/,
|
|
280
|
+
/lower bound/,
|
|
281
|
+
/at least/,
|
|
282
|
+
/at most/,
|
|
283
|
+
];
|
|
284
|
+
if (comparative_patterns.some((p) => p.test(lower))) {
|
|
285
|
+
intensity_mod *= 1.08;
|
|
286
|
+
intensity_signals.push("comparative");
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Proof requirement (even without "prove" verb)
|
|
290
|
+
const proof_patterns = [
|
|
291
|
+
/\bproof\b/,
|
|
292
|
+
/\btheorem\b/,
|
|
293
|
+
/\blemma\b/,
|
|
294
|
+
/\bcorollary\b/,
|
|
295
|
+
/show that/,
|
|
296
|
+
/demonstrate that/,
|
|
297
|
+
];
|
|
298
|
+
if (proof_patterns.some((p) => p.test(lower))) {
|
|
299
|
+
intensity_mod *= 1.05;
|
|
300
|
+
intensity_signals.push("proof_structure");
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Multi-part questions (higher cognitive load)
|
|
304
|
+
const multi_part_patterns = [
|
|
305
|
+
/give (two|three|four|\d+) (examples?|reasons?|explanations?)/i,
|
|
306
|
+
/what are (two|three|\d+) possible/i,
|
|
307
|
+
/list (two|three|\d+)/i,
|
|
308
|
+
/compare.*contrast/i,
|
|
309
|
+
/similarities.*differences/i,
|
|
310
|
+
];
|
|
311
|
+
if (multi_part_patterns.some((p) => p.test(lower))) {
|
|
312
|
+
intensity_mod *= 1.12;
|
|
313
|
+
intensity_signals.push("multi_part");
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Rule-based reasoning (Wason selection task style)
|
|
317
|
+
const rule_patterns = [
|
|
318
|
+
/rule:.*which.*flip/i,
|
|
319
|
+
/which cards? must you flip/i,
|
|
320
|
+
/test the rule/i,
|
|
321
|
+
/if.*on one side.*on (the )?other/i,
|
|
322
|
+
/must.*flip.*test/i,
|
|
323
|
+
];
|
|
324
|
+
if (rule_patterns.some((p) => p.test(lower))) {
|
|
325
|
+
intensity_mod *= 1.35; // Strong boost for logical rule verification
|
|
326
|
+
intensity_signals.push("rule_based");
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ===== PHASE 5: NEGATION CORRECTION =====
|
|
330
|
+
// Handle "not difficult", "not complex" → lower score
|
|
331
|
+
const negation_patterns = [
|
|
332
|
+
/not (difficult|complex|hard)/,
|
|
333
|
+
/not requiring/,
|
|
334
|
+
/simple (explanation|answer)/,
|
|
335
|
+
/just (explain|describe)/,
|
|
336
|
+
/briefly/,
|
|
337
|
+
];
|
|
338
|
+
if (negation_patterns.some((p) => p.test(lower))) {
|
|
339
|
+
verb_boosted *= 0.7; // 30% penalty
|
|
340
|
+
verb_type += " [simplified]";
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ===== PHASE 5b: META-QUESTION DETECTION =====
|
|
344
|
+
// Questions ABOUT psychological research, biases, or experiments should use direct path.
|
|
345
|
+
// Extended reasoning on meta-cognitive questions leads to overthinking.
|
|
346
|
+
// Example: "Which group estimates higher?" is about anchoring research, not a math problem.
|
|
347
|
+
const meta_question_patterns = [
|
|
348
|
+
/which (group|sample|population) (estimates?|guesses?|predicts?|reports?)/i, // Anchoring research
|
|
349
|
+
/research (shows?|found|demonstrates?|indicates?)/i, // Citing research findings
|
|
350
|
+
/study (found|shows?|demonstrates?)/i, // Study citations
|
|
351
|
+
/experiment (shows?|found|demonstrates?)/i, // Experiment findings
|
|
352
|
+
/psychology (research|studies?|experiments?)/i, // Psychology context
|
|
353
|
+
/cognitive (bias|heuristic|psychology)/i, // Cognitive science terms
|
|
354
|
+
/asked .*(longer|shorter|higher|lower|more|less).*asked/i, // Classic anchoring setup
|
|
355
|
+
/one group.*(another|different|second) group/i, // Between-subjects comparison
|
|
356
|
+
/participants? (were|are) (asked|shown|given)/i, // Experimental procedure
|
|
357
|
+
/what (does|do) (the|this) (research|study|experiment)/i, // Meta-question about research
|
|
358
|
+
];
|
|
359
|
+
|
|
360
|
+
const is_meta_question = meta_question_patterns.some((p) => p.test(text));
|
|
361
|
+
if (is_meta_question) {
|
|
362
|
+
// Strong penalty: meta-questions benefit from direct intuition, not extended reasoning
|
|
363
|
+
verb_boosted *= 0.6; // 40% penalty - biases toward "Low" tier (direct path)
|
|
364
|
+
verb_type += " [meta-question]";
|
|
365
|
+
intensity_signals.push("meta_question");
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ===== PHASE 6: COMPOSITE SCORING =====
|
|
369
|
+
// If we detected a high-weight domain but no explicit verb, boost the base
|
|
370
|
+
// This handles "What is X?" questions in difficult domains
|
|
371
|
+
if (verb_type === "generic" && domain_weight >= 0.7) {
|
|
372
|
+
verb_boosted = Math.max(verb_boosted, 0.55);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const composite_score = verb_boosted * domain_weight * intensity_mod;
|
|
376
|
+
const final_score = Math.min(1.0, composite_score);
|
|
377
|
+
|
|
378
|
+
// ===== PHASE 7: TIER CLASSIFICATION =====
|
|
379
|
+
let tier: ComplexityResult["tier"];
|
|
380
|
+
|
|
381
|
+
if (final_score < 0.3) {
|
|
382
|
+
tier = "Low";
|
|
383
|
+
} else if (final_score < 0.5) {
|
|
384
|
+
tier = "Moderate";
|
|
385
|
+
} else if (final_score < 0.72) {
|
|
386
|
+
tier = "High";
|
|
387
|
+
} else if (final_score < 0.85) {
|
|
388
|
+
tier = "Very Hard";
|
|
389
|
+
} else {
|
|
390
|
+
tier = "Almost Impossible";
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// ASYMMETRIC DEFAULT: When in doubt, verify
|
|
394
|
+
// Any intensity signal or trap detection → minimum Moderate
|
|
395
|
+
// Rationale: under-routing causes wrong answers, over-routing only wastes tokens
|
|
396
|
+
// EXCEPTION: meta_question signals should stay Low (direct path preferred for these)
|
|
397
|
+
const signals_excluding_meta = intensity_signals.filter((s) => s !== "meta_question");
|
|
398
|
+
if (tier === "Low" && signals_excluding_meta.length > 0) {
|
|
399
|
+
tier = "Moderate";
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// ===== CONFIDENCE CALCULATION =====
|
|
403
|
+
// Calculate confidence based on distance from tier boundaries
|
|
404
|
+
const TIER_BOUNDARIES = [0.3, 0.5, 0.72, 0.85];
|
|
405
|
+
const GRAY_ZONE_LOWER = 0.28;
|
|
406
|
+
const GRAY_ZONE_UPPER = 0.72;
|
|
407
|
+
|
|
408
|
+
// Find minimum distance to any tier boundary
|
|
409
|
+
let minBoundaryDistance = 1.0;
|
|
410
|
+
for (const boundary of TIER_BOUNDARIES) {
|
|
411
|
+
const distance = Math.abs(final_score - boundary);
|
|
412
|
+
if (distance < minBoundaryDistance) {
|
|
413
|
+
minBoundaryDistance = distance;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Confidence is higher when far from boundaries
|
|
418
|
+
// Scale: 0.5 at boundary, 0.95 when far from all boundaries
|
|
419
|
+
const confidenceLevel = Math.min(0.95, 0.5 + minBoundaryDistance * 2.5);
|
|
420
|
+
const inGrayZone = final_score >= GRAY_ZONE_LOWER && final_score <= GRAY_ZONE_UPPER;
|
|
421
|
+
|
|
422
|
+
// ===== REASONING EXPLANATION =====
|
|
423
|
+
const reasoning = `Score: ${final_score.toFixed(3)} | Tier: ${tier}
|
|
424
|
+
Verb: "${verb_type}" (base: ${verb_base.toFixed(2)} → boosted: ${verb_boosted.toFixed(2)})
|
|
425
|
+
Domain: ${domain_name} (weight: ${domain_weight.toFixed(2)})
|
|
426
|
+
Intensity: ${intensity_signals.join(", ") || "none"} (modifier: ${intensity_mod.toFixed(2)}x)
|
|
427
|
+
|
|
428
|
+
Calculation: ${verb_boosted.toFixed(2)} × ${domain_weight.toFixed(
|
|
429
|
+
2,
|
|
430
|
+
)} × ${intensity_mod.toFixed(2)} = ${final_score.toFixed(3)}`;
|
|
431
|
+
|
|
432
|
+
return {
|
|
433
|
+
score: final_score,
|
|
434
|
+
tier,
|
|
435
|
+
confidence: {
|
|
436
|
+
level: confidenceLevel,
|
|
437
|
+
inGrayZone,
|
|
438
|
+
boundaryDistance: minBoundaryDistance,
|
|
439
|
+
},
|
|
440
|
+
explanation: {
|
|
441
|
+
verb_type,
|
|
442
|
+
verb_score: verb_boosted,
|
|
443
|
+
domain_detected: domain_name,
|
|
444
|
+
domain_weight,
|
|
445
|
+
intensity_signals,
|
|
446
|
+
intensity_modifier: intensity_mod,
|
|
447
|
+
},
|
|
448
|
+
reasoning: reasoning.trim(),
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Detect if a question is trivially simple (can skip all reasoning phases)
|
|
453
|
+
* These questions should use direct LLM answer with minimal prompting
|
|
454
|
+
*/
|
|
455
|
+
export function isTrivialQuestion(question: string): boolean {
|
|
456
|
+
// Must be very short
|
|
457
|
+
if (question.length > 80) return false; // Reduced from 150
|
|
458
|
+
|
|
459
|
+
// Must have very low complexity score
|
|
460
|
+
const complexity = assessPromptComplexity(question);
|
|
461
|
+
if (complexity.score >= 0.25) return false; // Stricter than 0.3
|
|
462
|
+
|
|
463
|
+
// Exclude questions with reasoning indicators
|
|
464
|
+
const reasoningIndicators = [
|
|
465
|
+
/minimum|maximum|optimal/i,
|
|
466
|
+
/guarantee|ensure|worst case/i,
|
|
467
|
+
/how many (ways|times|steps)/i,
|
|
468
|
+
/strategy|approach|method/i,
|
|
469
|
+
/arrange|permutation|combination/i,
|
|
470
|
+
/probability|chance|likely/i,
|
|
471
|
+
/\d+.*machines?.*\d+/i, // Rate problems
|
|
472
|
+
/overlap|intersect/i,
|
|
473
|
+
];
|
|
474
|
+
if (reasoningIndicators.some((p) => p.test(question))) return false;
|
|
475
|
+
|
|
476
|
+
// Check for simple answer patterns
|
|
477
|
+
const expectsSimpleAnswer =
|
|
478
|
+
/\b(yes|no|true|false)\s*(\?|$|\.)/i.test(question) ||
|
|
479
|
+
/\b(is|are|does|do|can|will|has|have)\s+\w+.*\?/i.test(question) ||
|
|
480
|
+
/\banswer\s+(yes|no|true|false)\b/i.test(question) ||
|
|
481
|
+
/\b(valid|invalid)\s*(\?|$)/i.test(question);
|
|
482
|
+
|
|
483
|
+
// Check for simple logical patterns
|
|
484
|
+
const simpleLogic =
|
|
485
|
+
/\bif\s+.+,\s*then\b/i.test(question) ||
|
|
486
|
+
/\ball\s+\w+\s+are\s+\w+/i.test(question) ||
|
|
487
|
+
/\b(therefore|thus|so)\b/i.test(question);
|
|
488
|
+
|
|
489
|
+
return expectsSimpleAnswer || simpleLogic;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Get a minimal prompt for trivial questions
|
|
494
|
+
* Designed for direct LLM answer with no reasoning overhead
|
|
495
|
+
*/
|
|
496
|
+
export function getTrivialPrompt(question: string): {
|
|
497
|
+
system: string;
|
|
498
|
+
user: string;
|
|
499
|
+
} {
|
|
500
|
+
return {
|
|
501
|
+
system: "Answer directly. Just YES, NO, or the answer. Nothing else.",
|
|
502
|
+
user: question,
|
|
503
|
+
};
|
|
504
|
+
}
|