entroplain 0.1.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/CONTRIBUTING.md +103 -0
- package/LICENSE +21 -0
- package/README.md +389 -0
- package/dist/entroplain-0.1.0-py3-none-any.whl +0 -0
- package/dist/entroplain-0.1.0.tar.gz +0 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/monitor.d.ts.map +1 -0
- package/dist/types.d.ts.map +1 -0
- package/docs/USAGE.md +302 -0
- package/entroplain/__init__.py +30 -0
- package/entroplain/cli.py +152 -0
- package/entroplain/hooks.py +183 -0
- package/entroplain/monitor.py +272 -0
- package/entroplain/providers.py +626 -0
- package/examples.md +40 -0
- package/package.json +44 -0
- package/pyproject.toml +85 -0
- package/src/hooks.ts +130 -0
- package/src/index.ts +9 -0
- package/src/monitor.ts +252 -0
- package/src/types.ts +58 -0
- package/tests/test_functional.py +303 -0
- package/tests/test_monitor.py +165 -0
- package/tsconfig.json +19 -0
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "entroplain",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Entropy-based early exit for efficient agent reasoning",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"test": "jest",
|
|
10
|
+
"lint": "eslint src/",
|
|
11
|
+
"prepublishOnly": "npm run build"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"llm",
|
|
15
|
+
"agent",
|
|
16
|
+
"entropy",
|
|
17
|
+
"early-exit",
|
|
18
|
+
"reasoning",
|
|
19
|
+
"efficiency"
|
|
20
|
+
],
|
|
21
|
+
"author": "Entroplain Contributors",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/entroplain/entroplain.git"
|
|
26
|
+
},
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/entroplain/entroplain/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/entroplain/entroplain#readme",
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/jest": "^29.5.0",
|
|
33
|
+
"@types/node": "^20.0.0",
|
|
34
|
+
"jest": "^29.5.0",
|
|
35
|
+
"ts-jest": "^29.1.0",
|
|
36
|
+
"typescript": "^5.0.0"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"openai": "^4.0.0"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=18.0.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
package/pyproject.toml
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "entroplain"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Entropy-based early exit for efficient agent reasoning"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Entroplain Contributors"}
|
|
13
|
+
]
|
|
14
|
+
keywords = ["llm", "agent", "entropy", "early-exit", "efficiency", "reasoning"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.8",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
26
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
27
|
+
]
|
|
28
|
+
requires-python = ">=3.8"
|
|
29
|
+
dependencies = [
|
|
30
|
+
"typing-extensions>=4.0.0;python_version<'3.10'",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
openai = ["openai>=1.0.0"]
|
|
35
|
+
anthropic = ["anthropic>=0.25.0"]
|
|
36
|
+
google = ["google-generativeai>=0.3.0"]
|
|
37
|
+
nvidia = ["requests>=2.28.0", "aiohttp>=3.8.0"]
|
|
38
|
+
ollama = ["requests>=2.28.0", "aiohttp>=3.8.0"]
|
|
39
|
+
llama-cpp = ["llama-cpp-python>=0.2.0"]
|
|
40
|
+
all = [
|
|
41
|
+
"openai>=1.0.0",
|
|
42
|
+
"anthropic>=0.25.0",
|
|
43
|
+
"google-generativeai>=0.3.0",
|
|
44
|
+
"requests>=2.28.0",
|
|
45
|
+
"aiohttp>=3.8.0",
|
|
46
|
+
"llama-cpp-python>=0.2.0",
|
|
47
|
+
]
|
|
48
|
+
dev = [
|
|
49
|
+
"pytest>=7.0.0",
|
|
50
|
+
"pytest-asyncio>=0.21.0",
|
|
51
|
+
"black>=23.0.0",
|
|
52
|
+
"isort>=5.0.0",
|
|
53
|
+
"mypy>=1.0.0",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
[project.urls]
|
|
57
|
+
Homepage = "https://github.com/entroplain/entroplain"
|
|
58
|
+
Documentation = "https://github.com/entroplain/entroplain#readme"
|
|
59
|
+
Repository = "https://github.com/entroplain/entroplain.git"
|
|
60
|
+
Issues = "https://github.com/entroplain/entroplain/issues"
|
|
61
|
+
|
|
62
|
+
[project.scripts]
|
|
63
|
+
entroplain = "entroplain.cli:main"
|
|
64
|
+
|
|
65
|
+
[tool.setuptools.packages.find]
|
|
66
|
+
where = ["."]
|
|
67
|
+
include = ["entroplain*"]
|
|
68
|
+
|
|
69
|
+
[tool.black]
|
|
70
|
+
line-length = 100
|
|
71
|
+
target-version = ["py38", "py39", "py310", "py311", "py312"]
|
|
72
|
+
|
|
73
|
+
[tool.isort]
|
|
74
|
+
profile = "black"
|
|
75
|
+
line_length = 100
|
|
76
|
+
|
|
77
|
+
[tool.mypy]
|
|
78
|
+
python_version = "3.8"
|
|
79
|
+
warn_return_any = true
|
|
80
|
+
warn_unused_configs = true
|
|
81
|
+
disallow_untyped_defs = true
|
|
82
|
+
|
|
83
|
+
[tool.pytest.ini_options]
|
|
84
|
+
asyncio_mode = "auto"
|
|
85
|
+
testpaths = ["tests"]
|
package/src/hooks.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks for agent framework integration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { EntropyMonitor } from './monitor';
|
|
6
|
+
import { EntropyPoint, MonitorStats } from './types';
|
|
7
|
+
|
|
8
|
+
// Global monitor instance
|
|
9
|
+
let monitor: EntropyMonitor | null = null;
|
|
10
|
+
let config: Record<string, unknown> = {};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Initialize entropy hooks
|
|
14
|
+
*/
|
|
15
|
+
export function initHooks(userConfig: Record<string, unknown> = {}): void {
|
|
16
|
+
config = userConfig;
|
|
17
|
+
monitor = new EntropyMonitor({
|
|
18
|
+
entropyThreshold: (config.entropyThreshold as number) ?? 0.15,
|
|
19
|
+
minValleys: (config.minValleys as number) ?? 2,
|
|
20
|
+
velocityThreshold: (config.velocityThreshold as number) ?? 0.05,
|
|
21
|
+
minTokens: (config.minTokens as number) ?? 50,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Hook to track entropy for each token
|
|
27
|
+
*/
|
|
28
|
+
export function trackEntropy(token: string, entropy: number): {
|
|
29
|
+
token: string;
|
|
30
|
+
entropy: number;
|
|
31
|
+
index: number;
|
|
32
|
+
isValley: boolean;
|
|
33
|
+
velocity: number;
|
|
34
|
+
shouldExit: boolean;
|
|
35
|
+
stats: MonitorStats;
|
|
36
|
+
} {
|
|
37
|
+
if (!monitor) {
|
|
38
|
+
initHooks();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const point = monitor!.track(token, entropy);
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
token,
|
|
45
|
+
entropy,
|
|
46
|
+
index: point.index,
|
|
47
|
+
isValley: point.isValley,
|
|
48
|
+
velocity: point.velocity,
|
|
49
|
+
shouldExit: monitor!.shouldExit(),
|
|
50
|
+
stats: monitor!.getStats(),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Hook to check if reasoning has converged
|
|
56
|
+
*/
|
|
57
|
+
export function earlyExit(): boolean {
|
|
58
|
+
if (!monitor) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
return monitor.shouldExit();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Reset the global monitor state
|
|
66
|
+
*/
|
|
67
|
+
export function resetHooks(): void {
|
|
68
|
+
if (monitor) {
|
|
69
|
+
monitor.reset();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get the current monitor instance
|
|
75
|
+
*/
|
|
76
|
+
export function getMonitor(): EntropyMonitor | null {
|
|
77
|
+
return monitor;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Class-based hook for frameworks that prefer class instances
|
|
82
|
+
*/
|
|
83
|
+
export class EntropyHook {
|
|
84
|
+
private monitor: EntropyMonitor;
|
|
85
|
+
private config: Record<string, unknown>;
|
|
86
|
+
|
|
87
|
+
constructor(userConfig: Record<string, unknown> = {}) {
|
|
88
|
+
this.config = userConfig;
|
|
89
|
+
this.monitor = new EntropyMonitor({
|
|
90
|
+
entropyThreshold: (this.config.entropyThreshold as number) ?? 0.15,
|
|
91
|
+
minValleys: (this.config.minValleys as number) ?? 2,
|
|
92
|
+
velocityThreshold: (this.config.velocityThreshold as number) ?? 0.05,
|
|
93
|
+
minTokens: (this.config.minTokens as number) ?? 50,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
onToken(token: string, entropy: number): {
|
|
98
|
+
token: string;
|
|
99
|
+
entropy: number;
|
|
100
|
+
index: number;
|
|
101
|
+
isValley: boolean;
|
|
102
|
+
velocity: number;
|
|
103
|
+
shouldExit: boolean;
|
|
104
|
+
stats: MonitorStats;
|
|
105
|
+
} {
|
|
106
|
+
const point = this.monitor.track(token, entropy);
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
token,
|
|
110
|
+
entropy,
|
|
111
|
+
index: point.index,
|
|
112
|
+
isValley: point.isValley,
|
|
113
|
+
velocity: point.velocity,
|
|
114
|
+
shouldExit: this.monitor.shouldExit(),
|
|
115
|
+
stats: this.monitor.getStats(),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
shouldExit(): boolean {
|
|
120
|
+
return this.monitor.shouldExit();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
reset(): void {
|
|
124
|
+
this.monitor.reset();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
getStats(): MonitorStats {
|
|
128
|
+
return this.monitor.getStats();
|
|
129
|
+
}
|
|
130
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entroplain — Entropy-based early exit for efficient agent reasoning
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { EntropyMonitor, calculateEntropy } from './monitor';
|
|
8
|
+
export { EntropyHook, trackEntropy, earlyExit } from './hooks';
|
|
9
|
+
export * from './types';
|
package/src/monitor.ts
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entropy Monitor — Core entropy tracking and early exit logic
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { EntropyPoint, MonitorConfig, MonitorStats, ExitCondition } from './types';
|
|
6
|
+
|
|
7
|
+
export class EntropyMonitor {
|
|
8
|
+
private config: MonitorConfig;
|
|
9
|
+
private trajectory: EntropyPoint[] = [];
|
|
10
|
+
private valleys: EntropyPoint[] = [];
|
|
11
|
+
private index: number = 0;
|
|
12
|
+
|
|
13
|
+
constructor(options: Partial<MonitorConfig> = {}) {
|
|
14
|
+
this.config = {
|
|
15
|
+
entropyThreshold: options.entropyThreshold ?? 0.15,
|
|
16
|
+
minValleys: options.minValleys ?? 2,
|
|
17
|
+
velocityThreshold: options.velocityThreshold ?? 0.05,
|
|
18
|
+
minTokens: options.minTokens ?? 50,
|
|
19
|
+
valleyWindow: options.valleyWindow ?? 5,
|
|
20
|
+
plateauThreshold: options.plateauThreshold ?? 3,
|
|
21
|
+
exitCondition: options.exitCondition ?? 'combined',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Calculate Shannon entropy from log probabilities
|
|
27
|
+
*/
|
|
28
|
+
calculateEntropy(logprobs: number[], fromProbs: boolean = false): number {
|
|
29
|
+
if (!logprobs || logprobs.length === 0) {
|
|
30
|
+
return 0.0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let entropy = 0.0;
|
|
34
|
+
for (const lp of logprobs) {
|
|
35
|
+
const prob = fromProbs ? lp : Math.exp(lp);
|
|
36
|
+
if (prob > 0) {
|
|
37
|
+
entropy -= prob * Math.log2(prob + 1e-10);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return entropy;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Track a token and its entropy value
|
|
46
|
+
*/
|
|
47
|
+
track(token: string, entropy: number): EntropyPoint {
|
|
48
|
+
const point: EntropyPoint = {
|
|
49
|
+
index: this.index,
|
|
50
|
+
token,
|
|
51
|
+
entropy,
|
|
52
|
+
isValley: false,
|
|
53
|
+
velocity: 0.0,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Calculate velocity
|
|
57
|
+
if (this.trajectory.length > 0) {
|
|
58
|
+
const prev = this.trajectory[this.trajectory.length - 1];
|
|
59
|
+
point.velocity = Math.abs(entropy - prev.entropy);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Detect valley (local minimum)
|
|
63
|
+
if (this.trajectory.length >= 2) {
|
|
64
|
+
const prev2 = this.trajectory[this.trajectory.length - 2];
|
|
65
|
+
const prev1 = this.trajectory[this.trajectory.length - 1];
|
|
66
|
+
if (prev1.entropy < prev2.entropy && prev1.entropy < entropy) {
|
|
67
|
+
prev1.isValley = true;
|
|
68
|
+
this.valleys.push(prev1);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
this.trajectory.push(point);
|
|
73
|
+
this.index++;
|
|
74
|
+
|
|
75
|
+
return point;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get all entropy valleys (local minima)
|
|
80
|
+
*/
|
|
81
|
+
getValleys(): Array<[number, number]> {
|
|
82
|
+
return this.valleys.map(v => [v.index, v.entropy]);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get current entropy velocity
|
|
87
|
+
*/
|
|
88
|
+
getVelocity(): number {
|
|
89
|
+
if (this.trajectory.length < 2) {
|
|
90
|
+
return 0.0;
|
|
91
|
+
}
|
|
92
|
+
return this.trajectory[this.trajectory.length - 1].velocity;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get mean entropy over the trajectory
|
|
97
|
+
*/
|
|
98
|
+
getMeanEntropy(): number {
|
|
99
|
+
if (this.trajectory.length === 0) {
|
|
100
|
+
return 0.0;
|
|
101
|
+
}
|
|
102
|
+
const sum = this.trajectory.reduce((acc, p) => acc + p.entropy, 0);
|
|
103
|
+
return sum / this.trajectory.length;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get the number of detected valleys
|
|
108
|
+
*/
|
|
109
|
+
getValleyCount(): number {
|
|
110
|
+
return this.valleys.length;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Check if valley count has plateaued
|
|
115
|
+
*/
|
|
116
|
+
isValleysPlateau(): boolean {
|
|
117
|
+
if (this.valleys.length < this.config.minValleys) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (this.valleys.length < this.config.plateauThreshold) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const recent = this.valleys.slice(-this.config.plateauThreshold);
|
|
126
|
+
const spacings: number[] = [];
|
|
127
|
+
for (let i = 0; i < recent.length - 1; i++) {
|
|
128
|
+
spacings.push(recent[i + 1].index - recent[i].index);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (spacings.length === 0) return false;
|
|
132
|
+
|
|
133
|
+
const meanSpacing = spacings.reduce((a, b) => a + b, 0) / spacings.length;
|
|
134
|
+
const variance = spacings.reduce((acc, s) => acc + (s - meanSpacing) ** 2, 0) / spacings.length;
|
|
135
|
+
|
|
136
|
+
return variance < 10;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Check if current entropy is below threshold
|
|
141
|
+
*/
|
|
142
|
+
isEntropyLow(): boolean {
|
|
143
|
+
if (this.trajectory.length === 0) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
return this.trajectory[this.trajectory.length - 1].entropy < this.config.entropyThreshold;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Check if velocity is below threshold
|
|
151
|
+
*/
|
|
152
|
+
isVelocityStable(): boolean {
|
|
153
|
+
return this.getVelocity() < this.config.velocityThreshold;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Determine if reasoning has converged
|
|
158
|
+
*/
|
|
159
|
+
shouldExit(): boolean {
|
|
160
|
+
// Always require minimum tokens
|
|
161
|
+
if (this.trajectory.length < this.config.minTokens) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Always require minimum valleys
|
|
166
|
+
if (this.valleys.length < this.config.minValleys) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
switch (this.config.exitCondition) {
|
|
171
|
+
case 'valleys_plateau':
|
|
172
|
+
return this.isValleysPlateau();
|
|
173
|
+
case 'entropy_drop':
|
|
174
|
+
return this.isEntropyLow();
|
|
175
|
+
case 'velocity_zero':
|
|
176
|
+
return this.isVelocityStable();
|
|
177
|
+
case 'combined':
|
|
178
|
+
return (this.isEntropyLow() || this.isValleysPlateau()) && this.isVelocityStable();
|
|
179
|
+
default:
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Alias for shouldExit()
|
|
186
|
+
*/
|
|
187
|
+
isConverged(): boolean {
|
|
188
|
+
return this.shouldExit();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get full entropy trajectory
|
|
193
|
+
*/
|
|
194
|
+
getTrajectory(): number[] {
|
|
195
|
+
return this.trajectory.map(p => p.entropy);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Get all tracked tokens
|
|
200
|
+
*/
|
|
201
|
+
getTokens(): string[] {
|
|
202
|
+
return this.trajectory.map(p => p.token);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Get summary statistics
|
|
207
|
+
*/
|
|
208
|
+
getStats(): MonitorStats {
|
|
209
|
+
if (this.trajectory.length === 0) {
|
|
210
|
+
return {
|
|
211
|
+
tokenCount: 0,
|
|
212
|
+
valleyCount: 0,
|
|
213
|
+
meanEntropy: 0,
|
|
214
|
+
minEntropy: 0,
|
|
215
|
+
maxEntropy: 0,
|
|
216
|
+
currentEntropy: 0,
|
|
217
|
+
currentVelocity: 0,
|
|
218
|
+
isConverged: false,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const entropies = this.trajectory.map(p => p.entropy);
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
tokenCount: this.trajectory.length,
|
|
226
|
+
valleyCount: this.valleys.length,
|
|
227
|
+
meanEntropy: this.getMeanEntropy(),
|
|
228
|
+
minEntropy: Math.min(...entropies),
|
|
229
|
+
maxEntropy: Math.max(...entropies),
|
|
230
|
+
currentEntropy: entropies[entropies.length - 1],
|
|
231
|
+
currentVelocity: this.getVelocity(),
|
|
232
|
+
isConverged: this.shouldExit(),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Clear all tracked data
|
|
238
|
+
*/
|
|
239
|
+
reset(): void {
|
|
240
|
+
this.trajectory = [];
|
|
241
|
+
this.valleys = [];
|
|
242
|
+
this.index = 0;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Standalone function to calculate Shannon entropy
|
|
248
|
+
*/
|
|
249
|
+
export function calculateEntropy(logprobs: number[], fromProbs: boolean = false): number {
|
|
250
|
+
const monitor = new EntropyMonitor();
|
|
251
|
+
return monitor.calculateEntropy(logprobs, fromProbs);
|
|
252
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for Entroplain
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface EntropyPoint {
|
|
6
|
+
index: number;
|
|
7
|
+
token: string;
|
|
8
|
+
entropy: number;
|
|
9
|
+
isValley: boolean;
|
|
10
|
+
velocity: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface MonitorConfig {
|
|
14
|
+
entropyThreshold: number;
|
|
15
|
+
minValleys: number;
|
|
16
|
+
velocityThreshold: number;
|
|
17
|
+
minTokens: number;
|
|
18
|
+
valleyWindow: number;
|
|
19
|
+
plateauThreshold: number;
|
|
20
|
+
exitCondition: ExitCondition;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type ExitCondition =
|
|
24
|
+
| 'valleys_plateau'
|
|
25
|
+
| 'entropy_drop'
|
|
26
|
+
| 'velocity_zero'
|
|
27
|
+
| 'combined';
|
|
28
|
+
|
|
29
|
+
export interface MonitorStats {
|
|
30
|
+
tokenCount: number;
|
|
31
|
+
valleyCount: number;
|
|
32
|
+
meanEntropy: number;
|
|
33
|
+
minEntropy: number;
|
|
34
|
+
maxEntropy: number;
|
|
35
|
+
currentEntropy: number;
|
|
36
|
+
currentVelocity: number;
|
|
37
|
+
isConverged: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface TokenWithEntropy {
|
|
41
|
+
token: string;
|
|
42
|
+
entropy: number;
|
|
43
|
+
logprob: number;
|
|
44
|
+
topLogprobs: Array<{ token: string; logprob: number }>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ProviderConfig {
|
|
48
|
+
apiKey?: string;
|
|
49
|
+
baseUrl?: string;
|
|
50
|
+
model?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface StreamOptions {
|
|
54
|
+
model?: string;
|
|
55
|
+
messages?: Array<{ role: string; content: string }>;
|
|
56
|
+
maxTokens?: number;
|
|
57
|
+
topLogprobs?: number;
|
|
58
|
+
}
|