entroplain 0.2.0 → 0.2.1
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/DEPLOY.md +41 -0
- package/README.md +478 -476
- package/dist/entroplain-0.2.2-py3-none-any.whl +0 -0
- package/dist/entroplain-0.2.2.tar.gz +0 -0
- package/dist/entroplain-0.2.3-py3-none-any.whl +0 -0
- package/dist/entroplain-0.2.3.tar.gz +0 -0
- package/docs/AGENT_USAGE.md +178 -178
- package/docs/USAGE.md +302 -302
- package/entroplain/__init__.py +5 -3
- package/entroplain/cost_tracker.py +231 -231
- package/entroplain/dashboard.py +480 -368
- package/entroplain/monitor.py +390 -390
- package/entroplain/providers.py +626 -626
- package/entroplain/proxy.py +561 -349
- package/entroplain/shared_state.py +72 -0
- package/package.json +47 -46
- package/pyproject.toml +1 -1
- package/scripts/setup.bat +89 -0
- package/scripts/setup.sh +98 -0
- package/test_nvidia.py +56 -56
- package/test_proxy.py +16 -16
- package/vercel.json +6 -0
- package/website/README.md +14 -0
- package/website/app/globals.css +88 -0
- package/website/app/layout.tsx +34 -0
- package/website/app/page.tsx +537 -0
- package/website/package-lock.json +520 -0
- package/website/package.json +25 -0
- package/website/tsconfig.json +40 -0
- package/website/vercel.json +3 -0
- package/dist/entroplain-0.2.0-py3-none-any.whl +0 -0
- package/dist/entroplain-0.2.0.tar.gz +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared state for real-time data sharing between proxy and dashboard.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
from typing import Dict, Any, List, Callable, Optional
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class SharedState:
|
|
14
|
+
"""
|
|
15
|
+
Shared state between proxy and dashboard.
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
state = SharedState()
|
|
19
|
+
|
|
20
|
+
# In proxy:
|
|
21
|
+
await state.update({
|
|
22
|
+
"trajectory": [...],
|
|
23
|
+
"token_count": 10,
|
|
24
|
+
...
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
# In dashboard:
|
|
28
|
+
data = await state.get_update()
|
|
29
|
+
"""
|
|
30
|
+
_data: Dict[str, Any] = field(default_factory=dict)
|
|
31
|
+
_subscribers: List[Callable] = field(default_factory=list)
|
|
32
|
+
_lock: asyncio.Lock = field(default_factory=asyncio.Lock)
|
|
33
|
+
_last_update: Optional[datetime] = None
|
|
34
|
+
|
|
35
|
+
async def update(self, data: Dict[str, Any]) -> None:
|
|
36
|
+
"""Update state and notify subscribers."""
|
|
37
|
+
async with self._lock:
|
|
38
|
+
self._data = data
|
|
39
|
+
self._last_update = datetime.utcnow()
|
|
40
|
+
|
|
41
|
+
# Notify all subscribers
|
|
42
|
+
for callback in self._subscribers:
|
|
43
|
+
try:
|
|
44
|
+
await callback(data)
|
|
45
|
+
except Exception:
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
async def get(self) -> Dict[str, Any]:
|
|
49
|
+
"""Get current state."""
|
|
50
|
+
async with self._lock:
|
|
51
|
+
return self._data.copy()
|
|
52
|
+
|
|
53
|
+
def subscribe(self, callback: Callable) -> None:
|
|
54
|
+
"""Subscribe to state updates."""
|
|
55
|
+
self._subscribers.append(callback)
|
|
56
|
+
|
|
57
|
+
def unsubscribe(self, callback: Callable) -> None:
|
|
58
|
+
"""Unsubscribe from state updates."""
|
|
59
|
+
if callback in self._subscribers:
|
|
60
|
+
self._subscribers.remove(callback)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# Global shared state instance
|
|
64
|
+
_shared_state: Optional[SharedState] = None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_shared_state() -> SharedState:
|
|
68
|
+
"""Get or create the global shared state."""
|
|
69
|
+
global _shared_state
|
|
70
|
+
if _shared_state is None:
|
|
71
|
+
_shared_state = SharedState()
|
|
72
|
+
return _shared_state
|
package/package.json
CHANGED
|
@@ -1,46 +1,47 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "entroplain",
|
|
3
|
-
"version": "0.2.
|
|
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
|
-
"cost-tracking",
|
|
21
|
-
"dashboard"
|
|
22
|
-
],
|
|
23
|
-
"author": "Entroplain Contributors",
|
|
24
|
-
"license": "MIT",
|
|
25
|
-
"repository": {
|
|
26
|
-
"type": "git",
|
|
27
|
-
"url": "https://github.com/entroplain/entroplain.git"
|
|
28
|
-
},
|
|
29
|
-
"bugs": {
|
|
30
|
-
"url": "https://github.com/entroplain/entroplain/issues"
|
|
31
|
-
},
|
|
32
|
-
"homepage": "https://github.com/entroplain/entroplain#readme",
|
|
33
|
-
"devDependencies": {
|
|
34
|
-
"@types/jest": "^29.5.0",
|
|
35
|
-
"@types/node": "^20.0.0",
|
|
36
|
-
"jest": "^29.5.0",
|
|
37
|
-
"ts-jest": "^29.1.0",
|
|
38
|
-
"typescript": "^5.0.0"
|
|
39
|
-
},
|
|
40
|
-
"dependencies": {
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "entroplain",
|
|
3
|
+
"version": "0.2.1",
|
|
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
|
+
"cost-tracking",
|
|
21
|
+
"dashboard"
|
|
22
|
+
],
|
|
23
|
+
"author": "Entroplain Contributors",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/entroplain/entroplain.git"
|
|
28
|
+
},
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/entroplain/entroplain/issues"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/entroplain/entroplain#readme",
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/jest": "^29.5.0",
|
|
35
|
+
"@types/node": "^20.0.0",
|
|
36
|
+
"jest": "^29.5.0",
|
|
37
|
+
"ts-jest": "^29.1.0",
|
|
38
|
+
"typescript": "^5.0.0"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"entroplain": "^0.2.0",
|
|
42
|
+
"openai": "^4.0.0"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18.0.0"
|
|
46
|
+
}
|
|
47
|
+
}
|
package/pyproject.toml
CHANGED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
REM Entroplain Setup Script for Windows
|
|
3
|
+
REM Run from PowerShell or Command Prompt
|
|
4
|
+
|
|
5
|
+
echo ==============================================================
|
|
6
|
+
echo ENTROPAIN SETUP - Windows
|
|
7
|
+
echo ==============================================================
|
|
8
|
+
|
|
9
|
+
REM Check Python
|
|
10
|
+
python --version >nul 2>&1
|
|
11
|
+
if errorlevel 1 (
|
|
12
|
+
echo X Python is required but not installed.
|
|
13
|
+
echo Download from: https://www.python.org/downloads/
|
|
14
|
+
echo Make sure to check "Add Python to PATH" during installation.
|
|
15
|
+
exit /b 1
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
for /f "tokens=2" %%i in ('python --version 2^>^&1') do set PYTHON_VERSION=%%i
|
|
19
|
+
echo + Python %PYTHON_VERSION% found
|
|
20
|
+
|
|
21
|
+
REM Install entroplain
|
|
22
|
+
echo.
|
|
23
|
+
echo Installing entroplain...
|
|
24
|
+
pip install --upgrade entroplain
|
|
25
|
+
|
|
26
|
+
REM Verify installation
|
|
27
|
+
python -c "import entroplain" 2>nul
|
|
28
|
+
if errorlevel 1 (
|
|
29
|
+
echo X Installation failed
|
|
30
|
+
exit /b 1
|
|
31
|
+
)
|
|
32
|
+
echo + entroplain installed successfully
|
|
33
|
+
|
|
34
|
+
REM Check for API keys
|
|
35
|
+
echo.
|
|
36
|
+
echo ==============================================================
|
|
37
|
+
echo API KEY SETUP
|
|
38
|
+
echo ==============================================================
|
|
39
|
+
|
|
40
|
+
set PROVIDERS=
|
|
41
|
+
if defined OPENAI_API_KEY (
|
|
42
|
+
echo + OPENAI_API_KEY is set
|
|
43
|
+
set PROVIDERS=%PROVIDERS% openai
|
|
44
|
+
)
|
|
45
|
+
if defined ANTHROPIC_API_KEY (
|
|
46
|
+
echo + ANTHROPIC_API_KEY is set
|
|
47
|
+
set PROVIDERS=%PROVIDERS% anthropic
|
|
48
|
+
)
|
|
49
|
+
if defined NVIDIA_API_KEY (
|
|
50
|
+
echo + NVIDIA_API_KEY is set
|
|
51
|
+
set PROVIDERS=%PROVIDERS% nvidia
|
|
52
|
+
)
|
|
53
|
+
if defined GOOGLE_API_KEY (
|
|
54
|
+
echo + GOOGLE_API_KEY is set
|
|
55
|
+
set PROVIDERS=%PROVIDERS% google
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if "%PROVIDERS%"=="" (
|
|
59
|
+
echo.
|
|
60
|
+
echo ! No API keys found. Set at least one:
|
|
61
|
+
echo.
|
|
62
|
+
echo set OPENAI_API_KEY=your-key
|
|
63
|
+
echo set ANTHROPIC_API_KEY=your-key
|
|
64
|
+
echo set NVIDIA_API_KEY=your-key
|
|
65
|
+
echo set GOOGLE_API_KEY=your-key
|
|
66
|
+
echo.
|
|
67
|
+
echo To make permanent, add to System Environment Variables:
|
|
68
|
+
echo - Search "Environment Variables" in Start menu
|
|
69
|
+
echo - Edit "User variables" or "System variables"
|
|
70
|
+
echo - Add new variable with name and key
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
echo.
|
|
74
|
+
echo ==============================================================
|
|
75
|
+
echo QUICK START
|
|
76
|
+
echo ==============================================================
|
|
77
|
+
echo.
|
|
78
|
+
echo REM Analyze entropy:
|
|
79
|
+
echo entroplain analyze "Your prompt here"
|
|
80
|
+
echo.
|
|
81
|
+
echo REM Stream with early exit:
|
|
82
|
+
echo entroplain stream --exit-on-converge "Your prompt here"
|
|
83
|
+
echo.
|
|
84
|
+
echo REM Start proxy with dashboard:
|
|
85
|
+
echo entroplain-proxy --port 8765 --provider openai
|
|
86
|
+
echo REM Then open http://localhost:8765/dashboard
|
|
87
|
+
echo.
|
|
88
|
+
echo ==============================================================
|
|
89
|
+
echo Setup complete!
|
package/scripts/setup.sh
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Entroplain Setup Script for macOS/Linux
|
|
3
|
+
# Usage: curl -sSL https://raw.githubusercontent.com/entroplain/entroplain/main/scripts/setup.sh | bash
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
echo "=============================================================="
|
|
8
|
+
echo " ENTROPAIN SETUP - macOS/Linux"
|
|
9
|
+
echo "=============================================================="
|
|
10
|
+
|
|
11
|
+
# Colors
|
|
12
|
+
GREEN='\033[0;32m'
|
|
13
|
+
BLUE='\033[0;34m'
|
|
14
|
+
NC='\033[0m'
|
|
15
|
+
|
|
16
|
+
# Check Python
|
|
17
|
+
if ! command -v python3 &> /dev/null; then
|
|
18
|
+
echo "❌ Python 3 is required but not installed."
|
|
19
|
+
echo " Install with: brew install python3 (macOS) or sudo apt install python3 (Linux)"
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
PYTHON_VERSION=$(python3 --version 2>&1 | awk '{print $2}')
|
|
24
|
+
echo -e "${GREEN}✓${NC} Python $PYTHON_VERSION found"
|
|
25
|
+
|
|
26
|
+
# Check pip
|
|
27
|
+
if ! command -v pip3 &> /dev/null; then
|
|
28
|
+
echo "❌ pip3 is required but not installed."
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# Install entroplain
|
|
33
|
+
echo ""
|
|
34
|
+
echo -e "${BLUE}Installing entroplain...${NC}"
|
|
35
|
+
pip3 install --upgrade entroplain
|
|
36
|
+
|
|
37
|
+
# Verify installation
|
|
38
|
+
if python3 -c "import entroplain" 2>/dev/null; then
|
|
39
|
+
echo -e "${GREEN}✓${NC} entroplain installed successfully"
|
|
40
|
+
else
|
|
41
|
+
echo "❌ Installation failed"
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Check for API keys
|
|
46
|
+
echo ""
|
|
47
|
+
echo "=============================================================="
|
|
48
|
+
echo " API KEY SETUP"
|
|
49
|
+
echo "=============================================================="
|
|
50
|
+
|
|
51
|
+
PROVIDERS=""
|
|
52
|
+
if [ -n "$OPENAI_API_KEY" ]; then
|
|
53
|
+
echo -e "${GREEN}✓${NC} OPENAI_API_KEY is set"
|
|
54
|
+
PROVIDERS="$PROVIDERS openai"
|
|
55
|
+
fi
|
|
56
|
+
if [ -n "$ANTHROPIC_API_KEY" ]; then
|
|
57
|
+
echo -e "${GREEN}✓${NC} ANTHROPIC_API_KEY is set"
|
|
58
|
+
PROVIDERS="$PROVIDERS anthropic"
|
|
59
|
+
fi
|
|
60
|
+
if [ -n "$NVIDIA_API_KEY" ]; then
|
|
61
|
+
echo -e "${GREEN}✓${NC} NVIDIA_API_KEY is set"
|
|
62
|
+
PROVIDERS="$PROVIDERS nvidia"
|
|
63
|
+
fi
|
|
64
|
+
if [ -n "$GOOGLE_API_KEY" ]; then
|
|
65
|
+
echo -e "${GREEN}✓${NC} GOOGLE_API_KEY is set"
|
|
66
|
+
PROVIDERS="$PROVIDERS google"
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
if [ -z "$PROVIDERS" ]; then
|
|
70
|
+
echo ""
|
|
71
|
+
echo "⚠️ No API keys found. Set at least one:"
|
|
72
|
+
echo ""
|
|
73
|
+
echo " export OPENAI_API_KEY='your-key'"
|
|
74
|
+
echo " export ANTHROPIC_API_KEY='your-key'"
|
|
75
|
+
echo " export NVIDIA_API_KEY='your-key'"
|
|
76
|
+
echo " export GOOGLE_API_KEY='your-key'"
|
|
77
|
+
echo ""
|
|
78
|
+
echo " Add to ~/.bashrc or ~/.zshrc to persist"
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# Usage examples
|
|
82
|
+
echo ""
|
|
83
|
+
echo "=============================================================="
|
|
84
|
+
echo " QUICK START"
|
|
85
|
+
echo "=============================================================="
|
|
86
|
+
echo ""
|
|
87
|
+
echo " # Analyze entropy:"
|
|
88
|
+
echo " entroplain analyze \"Your prompt here\""
|
|
89
|
+
echo ""
|
|
90
|
+
echo " # Stream with early exit:"
|
|
91
|
+
echo " entroplain stream --exit-on-converge \"Your prompt here\""
|
|
92
|
+
echo ""
|
|
93
|
+
echo " # Start proxy with dashboard:"
|
|
94
|
+
echo " entroplain-proxy --port 8765 --provider openai"
|
|
95
|
+
echo " # Then open http://localhost:8765/dashboard"
|
|
96
|
+
echo ""
|
|
97
|
+
echo "=============================================================="
|
|
98
|
+
echo -e "${GREEN}Setup complete!${NC}"
|
package/test_nvidia.py
CHANGED
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
"""Test the proxy with NVIDIA API."""
|
|
2
|
-
|
|
3
|
-
import requests
|
|
4
|
-
import json
|
|
5
|
-
import os
|
|
6
|
-
|
|
7
|
-
# Get API key from environment
|
|
8
|
-
api_key = os.environ.get("NVIDIA_API_KEY", "")
|
|
9
|
-
|
|
10
|
-
if not api_key:
|
|
11
|
-
print("ERROR: NVIDIA_API_KEY not set")
|
|
12
|
-
exit(1)
|
|
13
|
-
|
|
14
|
-
# Make request through proxy
|
|
15
|
-
response = requests.post(
|
|
16
|
-
"http://localhost:8767/v1/chat/completions",
|
|
17
|
-
headers={
|
|
18
|
-
"Content-Type": "application/json",
|
|
19
|
-
"Authorization": f"Bearer {api_key}"
|
|
20
|
-
},
|
|
21
|
-
json={
|
|
22
|
-
"model": "meta/llama-3.1-70b-instruct",
|
|
23
|
-
"messages": [{"role": "user", "content": "What is 2+2? Just answer the number."}],
|
|
24
|
-
"max_tokens": 50,
|
|
25
|
-
"temperature": 0.1,
|
|
26
|
-
"stream": True
|
|
27
|
-
},
|
|
28
|
-
stream=True
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
print(f"Status: {response.status_code}")
|
|
32
|
-
print("Streaming response:")
|
|
33
|
-
print("-" * 40)
|
|
34
|
-
|
|
35
|
-
for line in response.iter_lines():
|
|
36
|
-
if line:
|
|
37
|
-
line = line.decode('utf-8')
|
|
38
|
-
if line.startswith("data: "):
|
|
39
|
-
data = line[6:]
|
|
40
|
-
if data == "[DONE]":
|
|
41
|
-
print("\n[DONE]")
|
|
42
|
-
break
|
|
43
|
-
try:
|
|
44
|
-
chunk = json.loads(data)
|
|
45
|
-
if chunk.get("choices"):
|
|
46
|
-
delta = chunk["choices"][0].get("delta", {})
|
|
47
|
-
if delta.get("content"):
|
|
48
|
-
print(delta["content"], end="", flush=True)
|
|
49
|
-
except json.JSONDecodeError:
|
|
50
|
-
pass
|
|
51
|
-
|
|
52
|
-
print("\n" + "-" * 40)
|
|
53
|
-
|
|
54
|
-
# Check proxy health
|
|
55
|
-
health = requests.get("http://localhost:8767/health")
|
|
56
|
-
print(f"\nProxy stats: {health.json()}")
|
|
1
|
+
"""Test the proxy with NVIDIA API."""
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
# Get API key from environment
|
|
8
|
+
api_key = os.environ.get("NVIDIA_API_KEY", "")
|
|
9
|
+
|
|
10
|
+
if not api_key:
|
|
11
|
+
print("ERROR: NVIDIA_API_KEY not set")
|
|
12
|
+
exit(1)
|
|
13
|
+
|
|
14
|
+
# Make request through proxy
|
|
15
|
+
response = requests.post(
|
|
16
|
+
"http://localhost:8767/v1/chat/completions",
|
|
17
|
+
headers={
|
|
18
|
+
"Content-Type": "application/json",
|
|
19
|
+
"Authorization": f"Bearer {api_key}"
|
|
20
|
+
},
|
|
21
|
+
json={
|
|
22
|
+
"model": "meta/llama-3.1-70b-instruct",
|
|
23
|
+
"messages": [{"role": "user", "content": "What is 2+2? Just answer the number."}],
|
|
24
|
+
"max_tokens": 50,
|
|
25
|
+
"temperature": 0.1,
|
|
26
|
+
"stream": True
|
|
27
|
+
},
|
|
28
|
+
stream=True
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
print(f"Status: {response.status_code}")
|
|
32
|
+
print("Streaming response:")
|
|
33
|
+
print("-" * 40)
|
|
34
|
+
|
|
35
|
+
for line in response.iter_lines():
|
|
36
|
+
if line:
|
|
37
|
+
line = line.decode('utf-8')
|
|
38
|
+
if line.startswith("data: "):
|
|
39
|
+
data = line[6:]
|
|
40
|
+
if data == "[DONE]":
|
|
41
|
+
print("\n[DONE]")
|
|
42
|
+
break
|
|
43
|
+
try:
|
|
44
|
+
chunk = json.loads(data)
|
|
45
|
+
if chunk.get("choices"):
|
|
46
|
+
delta = chunk["choices"][0].get("delta", {})
|
|
47
|
+
if delta.get("content"):
|
|
48
|
+
print(delta["content"], end="", flush=True)
|
|
49
|
+
except json.JSONDecodeError:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
print("\n" + "-" * 40)
|
|
53
|
+
|
|
54
|
+
# Check proxy health
|
|
55
|
+
health = requests.get("http://localhost:8767/health")
|
|
56
|
+
print(f"\nProxy stats: {health.json()}")
|
package/test_proxy.py
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
"""Test the proxy with a real API call."""
|
|
2
|
-
|
|
3
|
-
import requests
|
|
4
|
-
import json
|
|
5
|
-
|
|
6
|
-
# Test health endpoint
|
|
7
|
-
try:
|
|
8
|
-
response = requests.get("http://localhost:8765/health")
|
|
9
|
-
print(f"Health check: {response.status_code}")
|
|
10
|
-
print(response.json())
|
|
11
|
-
except Exception as e:
|
|
12
|
-
print(f"Proxy not running: {e}")
|
|
13
|
-
print("\nTo test the proxy, run:")
|
|
14
|
-
print(" entroplain-proxy --port 8765")
|
|
15
|
-
print("\nThen in another terminal:")
|
|
16
|
-
print(" python test_proxy.py")
|
|
1
|
+
"""Test the proxy with a real API call."""
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
# Test health endpoint
|
|
7
|
+
try:
|
|
8
|
+
response = requests.get("http://localhost:8765/health")
|
|
9
|
+
print(f"Health check: {response.status_code}")
|
|
10
|
+
print(response.json())
|
|
11
|
+
except Exception as e:
|
|
12
|
+
print(f"Proxy not running: {e}")
|
|
13
|
+
print("\nTo test the proxy, run:")
|
|
14
|
+
print(" entroplain-proxy --port 8765")
|
|
15
|
+
print("\nThen in another terminal:")
|
|
16
|
+
print(" python test_proxy.py")
|
package/vercel.json
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--bg: #ffffff;
|
|
5
|
+
--bg-dark: #0a0a0a;
|
|
6
|
+
--fg: #171717;
|
|
7
|
+
--fg-muted: #666666;
|
|
8
|
+
--accent: #4ade80;
|
|
9
|
+
--accent-hover: #22c55e;
|
|
10
|
+
--border: rgba(0, 0, 0, 0.08);
|
|
11
|
+
--card: #fafafa;
|
|
12
|
+
--code-bg: #1e1e1e;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
* {
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
margin: 0;
|
|
18
|
+
padding: 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
html {
|
|
22
|
+
scroll-behavior: smooth;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
body {
|
|
26
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
27
|
+
background: var(--bg);
|
|
28
|
+
color: var(--fg);
|
|
29
|
+
line-height: 1.5;
|
|
30
|
+
-webkit-font-smoothing: antialiased;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* Vercel-style shadow-as-border */
|
|
34
|
+
.border-shadow {
|
|
35
|
+
box-shadow: rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.card-shadow {
|
|
39
|
+
box-shadow:
|
|
40
|
+
rgba(0, 0, 0, 0.08) 0px 0px 0px 1px,
|
|
41
|
+
rgba(0, 0, 0, 0.04) 0px 2px 2px,
|
|
42
|
+
rgba(0, 0, 0, 0.04) 0px 8px 8px -8px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* Code blocks */
|
|
46
|
+
code, pre {
|
|
47
|
+
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
pre {
|
|
51
|
+
background: var(--code-bg);
|
|
52
|
+
border-radius: 8px;
|
|
53
|
+
padding: 16px;
|
|
54
|
+
overflow-x: auto;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
code {
|
|
58
|
+
color: #e0e0e0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Tight tracking for headings */
|
|
62
|
+
h1, h2, h3 {
|
|
63
|
+
letter-spacing: -0.02em;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* Selection */
|
|
67
|
+
::selection {
|
|
68
|
+
background: rgba(74, 222, 128, 0.2);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* Scrollbar */
|
|
72
|
+
::-webkit-scrollbar {
|
|
73
|
+
width: 8px;
|
|
74
|
+
height: 8px;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
::-webkit-scrollbar-track {
|
|
78
|
+
background: transparent;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
::-webkit-scrollbar-thumb {
|
|
82
|
+
background: rgba(0, 0, 0, 0.2);
|
|
83
|
+
border-radius: 4px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
::-webkit-scrollbar-thumb:hover {
|
|
87
|
+
background: rgba(0, 0, 0, 0.3);
|
|
88
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Metadata } from 'next'
|
|
2
|
+
import { Analytics } from '@vercel/analytics/next'
|
|
3
|
+
import { SpeedInsights } from '@vercel/speed-insights/next'
|
|
4
|
+
import './globals.css'
|
|
5
|
+
|
|
6
|
+
export const metadata: Metadata = {
|
|
7
|
+
title: 'Entroplain - Early Exit for Efficient Agent Reasoning',
|
|
8
|
+
description: 'Stop wasting tokens. Entroplain detects when LLMs have confidently answered and exits early, saving up to 50% on API costs.',
|
|
9
|
+
openGraph: {
|
|
10
|
+
title: 'Entroplain - Early Exit for Efficient Agent Reasoning',
|
|
11
|
+
description: 'Stop wasting tokens. Detect confident answers and exit early.',
|
|
12
|
+
type: 'website',
|
|
13
|
+
},
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function RootLayout({
|
|
17
|
+
children,
|
|
18
|
+
}: {
|
|
19
|
+
children: React.ReactNode
|
|
20
|
+
}) {
|
|
21
|
+
return (
|
|
22
|
+
<html lang="en">
|
|
23
|
+
<head>
|
|
24
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
25
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
|
|
26
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
|
27
|
+
</head>
|
|
28
|
+
<body style={{ margin: 0, padding: 0 }}>{children}
|
|
29
|
+
<Analytics />
|
|
30
|
+
<SpeedInsights />
|
|
31
|
+
</body>
|
|
32
|
+
</html>
|
|
33
|
+
)
|
|
34
|
+
}
|