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,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local Compute Engine
|
|
3
|
+
*
|
|
4
|
+
* Solves computable problems instantly without LLM calls.
|
|
5
|
+
* Why ask an LLM to compute 17+28 when TypeScript can do it in 0.001ms?
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* 1. Classifier: Fast bitmask routing (~0.01ms)
|
|
9
|
+
* 2. Registry: Auto-discovered solvers by type
|
|
10
|
+
* 3. Cache: LRU cache for repeated questions
|
|
11
|
+
*
|
|
12
|
+
* Capabilities:
|
|
13
|
+
* - Arithmetic: basic math expressions
|
|
14
|
+
* - Formulas: pythagorean, quadratic, fibonacci, logarithms, factorial
|
|
15
|
+
* - Word problems: "twice as many", "half of", "sum of X and Y"
|
|
16
|
+
* - Multi-step word problems: entity extraction + dependency resolution
|
|
17
|
+
* - Multi-expression extraction: finds ALL computable parts in text
|
|
18
|
+
* - Context injection: returns augmented prompt with computed values
|
|
19
|
+
* - LRU caching: avoids recomputing identical questions
|
|
20
|
+
* - Confidence scoring: returns likelihood of successful local compute
|
|
21
|
+
* - Domain filtering: filters computations by domain relevance
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
// Re-export cache utilities
|
|
25
|
+
export { clearCache, computeCache, getCacheStats } from "./cache.ts";
|
|
26
|
+
// Re-export classifier
|
|
27
|
+
export {
|
|
28
|
+
type ClassifierResult,
|
|
29
|
+
classifyQuestion,
|
|
30
|
+
describeMask,
|
|
31
|
+
SolverGroup,
|
|
32
|
+
type SolverMask,
|
|
33
|
+
SolverType,
|
|
34
|
+
shouldTrySolver,
|
|
35
|
+
} from "./classifier.ts";
|
|
36
|
+
// Re-export confidence
|
|
37
|
+
export { computeConfidence, isLikelyComputable } from "./confidence.ts";
|
|
38
|
+
// Re-export context-aware compute
|
|
39
|
+
export {
|
|
40
|
+
type ContextAwareInput,
|
|
41
|
+
type ContextAwareResult,
|
|
42
|
+
computeWithContext,
|
|
43
|
+
contextAwareCompute,
|
|
44
|
+
wouldKeepComputation,
|
|
45
|
+
} from "./context.ts";
|
|
46
|
+
|
|
47
|
+
// Re-export extraction
|
|
48
|
+
export { computeAndReplace, extractAndCompute } from "./extract.ts";
|
|
49
|
+
// Re-export filtering
|
|
50
|
+
export {
|
|
51
|
+
type FilterResult,
|
|
52
|
+
filterByDomainRelevance,
|
|
53
|
+
filterByMask,
|
|
54
|
+
isMethodRelevant,
|
|
55
|
+
methodToSolverType,
|
|
56
|
+
} from "./filter.ts";
|
|
57
|
+
// Re-export math helpers (for testing)
|
|
58
|
+
export {
|
|
59
|
+
combinations,
|
|
60
|
+
factorial,
|
|
61
|
+
fibonacci,
|
|
62
|
+
formatResult,
|
|
63
|
+
gcd,
|
|
64
|
+
isPrime,
|
|
65
|
+
lcm,
|
|
66
|
+
normalizeUnicodeSuperscripts,
|
|
67
|
+
type ParseResult,
|
|
68
|
+
permutations,
|
|
69
|
+
safeEvaluate,
|
|
70
|
+
} from "./math.ts";
|
|
71
|
+
// Re-export registry
|
|
72
|
+
export {
|
|
73
|
+
getRegistryStats,
|
|
74
|
+
getSolvers,
|
|
75
|
+
getSolversForMask,
|
|
76
|
+
registerSolver,
|
|
77
|
+
runSolvers,
|
|
78
|
+
type Solver,
|
|
79
|
+
} from "./registry.ts";
|
|
80
|
+
// Re-export solvers (for direct access / testing)
|
|
81
|
+
export {
|
|
82
|
+
canonicalizeExpression,
|
|
83
|
+
type DerivationErrorExplanation,
|
|
84
|
+
type DerivationLatexOptions,
|
|
85
|
+
type DerivationResult,
|
|
86
|
+
type DetectedMistake,
|
|
87
|
+
derivationTextToLatex,
|
|
88
|
+
derivationToLatex,
|
|
89
|
+
detectCommonMistakes,
|
|
90
|
+
detectCommonMistakesFromText,
|
|
91
|
+
explainDerivationError,
|
|
92
|
+
type MistakeDetectionResult,
|
|
93
|
+
type MistakeType,
|
|
94
|
+
type NextStepSuggestion,
|
|
95
|
+
type SimplificationPath,
|
|
96
|
+
type SimplificationStep,
|
|
97
|
+
type SimplifiedStep,
|
|
98
|
+
type SimplifyDerivationResult,
|
|
99
|
+
simplifyDerivation,
|
|
100
|
+
simplifyDerivationText,
|
|
101
|
+
suggestNextStep,
|
|
102
|
+
suggestNextStepFromText,
|
|
103
|
+
suggestSimplificationPath,
|
|
104
|
+
tryArithmetic,
|
|
105
|
+
tryCalculus,
|
|
106
|
+
tryCRTProblem,
|
|
107
|
+
tryDerivation,
|
|
108
|
+
tryFormula,
|
|
109
|
+
tryLogic,
|
|
110
|
+
tryMathFacts,
|
|
111
|
+
tryMultiStepWordProblem,
|
|
112
|
+
tryProbability,
|
|
113
|
+
trySimplifyToConstant,
|
|
114
|
+
tryWordProblem,
|
|
115
|
+
verifyDerivationSteps,
|
|
116
|
+
} from "./solvers/index.ts";
|
|
117
|
+
// Re-export types
|
|
118
|
+
export type {
|
|
119
|
+
AugmentedResult,
|
|
120
|
+
CacheStats,
|
|
121
|
+
ComputeConfidence,
|
|
122
|
+
ComputeResult,
|
|
123
|
+
ExtractedComputation,
|
|
124
|
+
} from "./types.ts";
|
|
125
|
+
|
|
126
|
+
// Import for main function
|
|
127
|
+
import { computeCache } from "./cache.ts";
|
|
128
|
+
import { classifyQuestion } from "./classifier.ts";
|
|
129
|
+
import { runSolvers } from "./registry.ts";
|
|
130
|
+
import type { ComputeResult } from "./types.ts";
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Try to solve a question locally without LLM
|
|
134
|
+
* Returns immediately if computable, otherwise returns { solved: false }
|
|
135
|
+
*
|
|
136
|
+
* Flow:
|
|
137
|
+
* 1. Check cache (instant return if hit)
|
|
138
|
+
* 2. Classify question → bitmask of likely solver types
|
|
139
|
+
* 3. Run matching solvers in priority order
|
|
140
|
+
* 4. Cache successful results
|
|
141
|
+
*
|
|
142
|
+
* @param text - The question to solve
|
|
143
|
+
* @param useCache - Whether to use LRU cache (default: true)
|
|
144
|
+
*/
|
|
145
|
+
export function tryLocalCompute(text: string, useCache = true): ComputeResult {
|
|
146
|
+
// Check cache first
|
|
147
|
+
if (useCache) {
|
|
148
|
+
const cached = computeCache.get(text);
|
|
149
|
+
if (cached) {
|
|
150
|
+
return { ...cached, time_ms: 0 }; // Cache hit is instant
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Classify question to get solver mask
|
|
155
|
+
const { mask, lower } = classifyQuestion(text);
|
|
156
|
+
|
|
157
|
+
// No solvers match → quick exit
|
|
158
|
+
if (mask === 0) {
|
|
159
|
+
return { solved: false, confidence: 0 };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Run matching solvers
|
|
163
|
+
const result = runSolvers(text, lower, mask);
|
|
164
|
+
|
|
165
|
+
// Cache successful results
|
|
166
|
+
if (result.solved && useCache) {
|
|
167
|
+
computeCache.set(text, result);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure math helper functions
|
|
3
|
+
* No regex, no side effects - just computation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** Calculate combinations C(n,k) = n! / (k! * (n-k)!) without factorial overflow */
|
|
7
|
+
export function combinations(n: number, k: number): number {
|
|
8
|
+
if (k > n - k) k = n - k; // Optimization: C(n,k) = C(n,n-k)
|
|
9
|
+
let result = 1;
|
|
10
|
+
for (let i = 0; i < k; i++) {
|
|
11
|
+
result = (result * (n - i)) / (i + 1);
|
|
12
|
+
}
|
|
13
|
+
return Math.round(result);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Calculate permutations P(n,k) = n! / (n-k)! */
|
|
17
|
+
export function permutations(n: number, k: number): number {
|
|
18
|
+
let result = 1;
|
|
19
|
+
for (let i = 0; i < k; i++) {
|
|
20
|
+
result *= n - i;
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Calculate nth Fibonacci number (1-indexed) */
|
|
26
|
+
export function fibonacci(n: number): number {
|
|
27
|
+
if (n <= 0) return 0;
|
|
28
|
+
if (n <= 2) return 1;
|
|
29
|
+
|
|
30
|
+
let a = 1,
|
|
31
|
+
b = 1;
|
|
32
|
+
for (let i = 3; i <= n; i++) {
|
|
33
|
+
[a, b] = [b, a + b];
|
|
34
|
+
}
|
|
35
|
+
return b;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Calculate factorial n! */
|
|
39
|
+
export function factorial(n: number): number {
|
|
40
|
+
if (n <= 1) return 1;
|
|
41
|
+
let result = 1;
|
|
42
|
+
for (let i = 2; i <= n; i++) {
|
|
43
|
+
result *= i;
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Calculate greatest common divisor using Euclidean algorithm */
|
|
49
|
+
export function gcd(a: number, b: number): number {
|
|
50
|
+
a = Math.abs(a);
|
|
51
|
+
b = Math.abs(b);
|
|
52
|
+
while (b) {
|
|
53
|
+
[a, b] = [b, a % b];
|
|
54
|
+
}
|
|
55
|
+
return a;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Calculate least common multiple */
|
|
59
|
+
export function lcm(a: number, b: number): number {
|
|
60
|
+
return Math.abs(a * b) / gcd(a, b);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Check if n is prime using 6k+-1 optimization */
|
|
64
|
+
export function isPrime(n: number): boolean {
|
|
65
|
+
if (n < 2) return false;
|
|
66
|
+
if (n === 2) return true;
|
|
67
|
+
if (n % 2 === 0) return false;
|
|
68
|
+
if (n === 3) return true;
|
|
69
|
+
if (n % 3 === 0) return false;
|
|
70
|
+
|
|
71
|
+
// Check 6k+-1 up to sqrt(n)
|
|
72
|
+
const sqrt = Math.sqrt(n);
|
|
73
|
+
for (let i = 5; i <= sqrt; i += 6) {
|
|
74
|
+
if (n % i === 0 || n % (i + 2) === 0) return false;
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Format a number result: integer if whole, otherwise fixed to 6 decimals */
|
|
80
|
+
export function formatResult(n: number): number {
|
|
81
|
+
return Number.isInteger(n) ? n : +n.toFixed(6);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// =============================================================================
|
|
85
|
+
// SAFE EXPRESSION PARSER
|
|
86
|
+
// Recursive descent parser - no eval/Function, pure math operations only
|
|
87
|
+
// Grammar:
|
|
88
|
+
// expr → term (('+' | '-') term)*
|
|
89
|
+
// term → factor (('*' | '/') factor)*
|
|
90
|
+
// factor → base ('^' factor)? // Right-associative exponentiation
|
|
91
|
+
// base → '-' base | primary
|
|
92
|
+
// primary → NUMBER | '(' expr ')'
|
|
93
|
+
// =============================================================================
|
|
94
|
+
|
|
95
|
+
export interface ParseResult {
|
|
96
|
+
success: boolean;
|
|
97
|
+
value?: number;
|
|
98
|
+
error?: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Safe arithmetic expression parser
|
|
103
|
+
* Supports: +, -, *, /, ^, (), negative numbers, decimals
|
|
104
|
+
* NO eval, NO Function constructor - pure recursive descent
|
|
105
|
+
*/
|
|
106
|
+
export function safeEvaluate(expression: string): ParseResult {
|
|
107
|
+
// Remove whitespace and normalize
|
|
108
|
+
const input = expression.replace(/\s+/g, "").replace(/\*\*/g, "^");
|
|
109
|
+
|
|
110
|
+
// Quick validation - only allow safe characters
|
|
111
|
+
if (!/^[\d+\-*/^().]+$/.test(input)) {
|
|
112
|
+
return { success: false, error: "Invalid characters" };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Check for empty or invalid patterns
|
|
116
|
+
// Note: We allow +- or -- (e.g., 5+-3 = 5+(-3), --5 = 5)
|
|
117
|
+
// But reject: **, //, */, /*, etc.
|
|
118
|
+
if (input === "" || /\(\)|[*/^]{2}|^[*/^]|[+\-*/^(]$/.test(input)) {
|
|
119
|
+
return { success: false, error: "Invalid expression" };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let pos = 0;
|
|
123
|
+
|
|
124
|
+
function peek(): string {
|
|
125
|
+
return input[pos] || "";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function consume(): string {
|
|
129
|
+
return input[pos++] || "";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function parseNumber(): number | null {
|
|
133
|
+
const start = pos;
|
|
134
|
+
// Handle leading negative that's part of the number (handled in base, but also here for safety)
|
|
135
|
+
if (peek() === "-") consume();
|
|
136
|
+
// Integer part
|
|
137
|
+
while (/\d/.test(peek())) consume();
|
|
138
|
+
// Decimal part
|
|
139
|
+
if (peek() === ".") {
|
|
140
|
+
consume();
|
|
141
|
+
while (/\d/.test(peek())) consume();
|
|
142
|
+
}
|
|
143
|
+
const numStr = input.slice(start, pos);
|
|
144
|
+
if (numStr === "" || numStr === "-" || numStr === ".") {
|
|
145
|
+
pos = start;
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
return parseFloat(numStr);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function parsePrimary(): number | null {
|
|
152
|
+
if (peek() === "(") {
|
|
153
|
+
consume(); // '('
|
|
154
|
+
const result = parseExpr();
|
|
155
|
+
if (result === null) return null;
|
|
156
|
+
if (peek() !== ")") return null;
|
|
157
|
+
consume(); // ')'
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
return parseNumber();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function parseBase(): number | null {
|
|
164
|
+
if (peek() === "-") {
|
|
165
|
+
consume();
|
|
166
|
+
const val = parseBase(); // Recursive for multiple negatives: --5
|
|
167
|
+
return val === null ? null : -val;
|
|
168
|
+
}
|
|
169
|
+
return parsePrimary();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function parseFactor(): number | null {
|
|
173
|
+
const left = parseBase();
|
|
174
|
+
if (left === null) return null;
|
|
175
|
+
if (peek() === "^") {
|
|
176
|
+
consume();
|
|
177
|
+
const right = parseFactor(); // Right-associative: 2^3^2 = 2^(3^2) = 512
|
|
178
|
+
if (right === null) return null;
|
|
179
|
+
return left ** right;
|
|
180
|
+
}
|
|
181
|
+
return left;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function parseTerm(): number | null {
|
|
185
|
+
let left = parseFactor();
|
|
186
|
+
if (left === null) return null;
|
|
187
|
+
while (peek() === "*" || peek() === "/") {
|
|
188
|
+
const op = consume();
|
|
189
|
+
const right = parseFactor();
|
|
190
|
+
if (right === null) return null;
|
|
191
|
+
if (op === "*") {
|
|
192
|
+
left = left * right;
|
|
193
|
+
} else {
|
|
194
|
+
if (right === 0) return null; // Division by zero
|
|
195
|
+
left = left / right;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return left;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function parseExpr(): number | null {
|
|
202
|
+
let left = parseTerm();
|
|
203
|
+
if (left === null) return null;
|
|
204
|
+
while (peek() === "+" || peek() === "-") {
|
|
205
|
+
const op = consume();
|
|
206
|
+
const right = parseTerm();
|
|
207
|
+
if (right === null) return null;
|
|
208
|
+
left = op === "+" ? left + right : left - right;
|
|
209
|
+
}
|
|
210
|
+
return left;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
const result = parseExpr();
|
|
215
|
+
// Ensure we consumed the entire input
|
|
216
|
+
if (result === null || pos !== input.length) {
|
|
217
|
+
return { success: false, error: "Parse error" };
|
|
218
|
+
}
|
|
219
|
+
if (!Number.isFinite(result)) {
|
|
220
|
+
return { success: false, error: "Result not finite" };
|
|
221
|
+
}
|
|
222
|
+
return { success: true, value: result };
|
|
223
|
+
} catch {
|
|
224
|
+
return { success: false, error: "Parse error" };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** Normalize Unicode superscripts to ^n notation */
|
|
229
|
+
export function normalizeUnicodeSuperscripts(text: string): string {
|
|
230
|
+
const superscriptMap: Record<string, string> = {
|
|
231
|
+
"\u2070": "^0",
|
|
232
|
+
"\u00B9": "^1",
|
|
233
|
+
"\u00B2": "^2",
|
|
234
|
+
"\u00B3": "^3",
|
|
235
|
+
"\u2074": "^4",
|
|
236
|
+
"\u2075": "^5",
|
|
237
|
+
"\u2076": "^6",
|
|
238
|
+
"\u2077": "^7",
|
|
239
|
+
"\u2078": "^8",
|
|
240
|
+
"\u2079": "^9",
|
|
241
|
+
};
|
|
242
|
+
let result = text;
|
|
243
|
+
for (const [sup, repl] of Object.entries(superscriptMap)) {
|
|
244
|
+
result = result.replaceAll(sup, repl);
|
|
245
|
+
}
|
|
246
|
+
return result;
|
|
247
|
+
}
|