agentevolution 0.1.0__py3-none-any.whl
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.
- agentevolution/__init__.py +9 -0
- agentevolution/__main__.py +6 -0
- agentevolution/config.py +85 -0
- agentevolution/dashboard/__init__.py +1 -0
- agentevolution/dashboard/app.py +242 -0
- agentevolution/dashboard/static/app.js +415 -0
- agentevolution/dashboard/static/index.html +246 -0
- agentevolution/dashboard/static/style.css +1248 -0
- agentevolution/fitness/__init__.py +1 -0
- agentevolution/fitness/scorer.py +129 -0
- agentevolution/forge/__init__.py +1 -0
- agentevolution/forge/normalizer.py +53 -0
- agentevolution/forge/publisher.py +143 -0
- agentevolution/forge/schema_gen.py +175 -0
- agentevolution/gauntlet/__init__.py +1 -0
- agentevolution/gauntlet/profiler.py +58 -0
- agentevolution/gauntlet/sandbox.py +180 -0
- agentevolution/gauntlet/security.py +179 -0
- agentevolution/gauntlet/signer.py +20 -0
- agentevolution/hivemind/__init__.py +1 -0
- agentevolution/hivemind/discovery.py +111 -0
- agentevolution/hivemind/recipes.py +74 -0
- agentevolution/provenance/__init__.py +1 -0
- agentevolution/provenance/chain.py +76 -0
- agentevolution/provenance/trust.py +44 -0
- agentevolution/server.py +657 -0
- agentevolution/storage/__init__.py +1 -0
- agentevolution/storage/database.py +384 -0
- agentevolution/storage/models.py +195 -0
- agentevolution/storage/vector_store.py +140 -0
- agentevolution/utils/__init__.py +1 -0
- agentevolution/utils/hashing.py +32 -0
- agentevolution-0.1.0.dist-info/METADATA +215 -0
- agentevolution-0.1.0.dist-info/RECORD +37 -0
- agentevolution-0.1.0.dist-info/WHEEL +4 -0
- agentevolution-0.1.0.dist-info/entry_points.txt +3 -0
- agentevolution-0.1.0.dist-info/licenses/LICENSE +21 -0
agentevolution/config.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""AgentEvolution Configuration Management."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ForgeConfig(BaseModel):
|
|
8
|
+
"""Configuration for The Forge (tool publishing)."""
|
|
9
|
+
max_code_size_bytes: int = Field(default=50_000, description="Max code size in bytes")
|
|
10
|
+
max_description_length: int = Field(default=2000, description="Max description length")
|
|
11
|
+
blocked_imports: list[str] = Field(
|
|
12
|
+
default_factory=lambda: [
|
|
13
|
+
"subprocess", "shutil", "ctypes", "multiprocessing",
|
|
14
|
+
"signal", "resource", "pty", "termios",
|
|
15
|
+
],
|
|
16
|
+
description="Python imports blocked for security",
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class GauntletConfig(BaseModel):
|
|
21
|
+
"""Configuration for The Gauntlet (verification)."""
|
|
22
|
+
execution_timeout_seconds: int = Field(default=30, description="Max execution time")
|
|
23
|
+
max_memory_mb: int = Field(default=256, description="Max memory usage in MB")
|
|
24
|
+
max_output_size_bytes: int = Field(default=10_000, description="Max output size")
|
|
25
|
+
allowed_network: bool = Field(default=False, description="Allow network access in sandbox")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class HiveMindConfig(BaseModel):
|
|
29
|
+
"""Configuration for The Hive Mind (discovery)."""
|
|
30
|
+
embedding_model: str = Field(
|
|
31
|
+
default="all-MiniLM-L6-v2",
|
|
32
|
+
description="Sentence-transformers model for embeddings",
|
|
33
|
+
)
|
|
34
|
+
collection_name: str = Field(default="agentevolution_tools", description="ChromaDB collection")
|
|
35
|
+
max_results: int = Field(default=10, description="Max search results")
|
|
36
|
+
min_similarity: float = Field(default=0.3, description="Min cosine similarity threshold")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class FitnessConfig(BaseModel):
|
|
40
|
+
"""Configuration for the Fitness Engine."""
|
|
41
|
+
weight_success_rate: float = 0.35
|
|
42
|
+
weight_token_efficiency: float = 0.25
|
|
43
|
+
weight_latency: float = 0.20
|
|
44
|
+
weight_adoption: float = 0.10
|
|
45
|
+
weight_freshness: float = 0.10
|
|
46
|
+
decay_days: int = Field(default=30, description="Days before staleness decay begins")
|
|
47
|
+
delist_threshold: float = Field(default=0.2, description="Score below which tools get delisted")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class AgentEvolutionConfig(BaseModel):
|
|
51
|
+
"""Root configuration for AgentEvolution."""
|
|
52
|
+
data_dir: Path = Field(default=Path("./data"), description="Data directory")
|
|
53
|
+
db_name: str = Field(default="agentevolution.db", description="SQLite database filename")
|
|
54
|
+
host: str = Field(default="0.0.0.0", description="Server host")
|
|
55
|
+
port: int = Field(default=8080, description="Server port")
|
|
56
|
+
forge: ForgeConfig = Field(default_factory=ForgeConfig)
|
|
57
|
+
gauntlet: GauntletConfig = Field(default_factory=GauntletConfig)
|
|
58
|
+
hivemind: HiveMindConfig = Field(default_factory=HiveMindConfig)
|
|
59
|
+
fitness: FitnessConfig = Field(default_factory=FitnessConfig)
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def db_path(self) -> Path:
|
|
63
|
+
return self.data_dir / self.db_name
|
|
64
|
+
|
|
65
|
+
def ensure_dirs(self) -> None:
|
|
66
|
+
"""Create necessary directories."""
|
|
67
|
+
self.data_dir.mkdir(parents=True, exist_ok=True)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# Global default config
|
|
71
|
+
_config: AgentEvolutionConfig | None = None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def get_config() -> AgentEvolutionConfig:
|
|
75
|
+
"""Get the global configuration instance."""
|
|
76
|
+
global _config
|
|
77
|
+
if _config is None:
|
|
78
|
+
_config = AgentEvolutionConfig()
|
|
79
|
+
return _config
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def set_config(config: AgentEvolutionConfig) -> None:
|
|
83
|
+
"""Set the global configuration instance."""
|
|
84
|
+
global _config
|
|
85
|
+
_config = config
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""AgentVerse Dashboard — Web UI."""
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""AgentEvolution Dashboard — FastAPI backend serving the web UI.
|
|
2
|
+
|
|
3
|
+
Provides REST API endpoints and serves the static dashboard.
|
|
4
|
+
Run: agentevolution-dashboard (or python -m agentevolution.dashboard.app)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from fastapi import FastAPI, HTTPException
|
|
15
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
16
|
+
from fastapi.responses import HTMLResponse, FileResponse
|
|
17
|
+
from fastapi.staticfiles import StaticFiles
|
|
18
|
+
from pydantic import BaseModel
|
|
19
|
+
|
|
20
|
+
from agentevolution.config import AgentEvolutionConfig, get_config, set_config
|
|
21
|
+
from agentevolution.storage.database import Database
|
|
22
|
+
from agentevolution.storage.vector_store import VectorStore
|
|
23
|
+
from agentevolution.fitness.scorer import FitnessScorer
|
|
24
|
+
from agentevolution.storage.models import ToolStatus
|
|
25
|
+
|
|
26
|
+
logging.basicConfig(level=logging.INFO, stream=sys.stderr)
|
|
27
|
+
logger = logging.getLogger("agentevolution.dashboard")
|
|
28
|
+
|
|
29
|
+
# ─── Static files path ───
|
|
30
|
+
STATIC_DIR = Path(__file__).parent / "static"
|
|
31
|
+
|
|
32
|
+
# ─── App State ───
|
|
33
|
+
|
|
34
|
+
db: Database | None = None
|
|
35
|
+
vector_store: VectorStore | None = None
|
|
36
|
+
fitness: FitnessScorer | None = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def create_app() -> FastAPI:
|
|
40
|
+
"""Create the FastAPI dashboard application."""
|
|
41
|
+
app = FastAPI(
|
|
42
|
+
title="AgentEvolution Dashboard",
|
|
43
|
+
description="The Self-Evolving MCP Tool Ecosystem — Visual Dashboard",
|
|
44
|
+
version="0.1.0",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
app.add_middleware(
|
|
48
|
+
CORSMiddleware,
|
|
49
|
+
allow_origins=["*"],
|
|
50
|
+
allow_methods=["*"],
|
|
51
|
+
allow_headers=["*"],
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# ─── Lifecycle ───
|
|
55
|
+
|
|
56
|
+
@app.on_event("startup")
|
|
57
|
+
async def startup():
|
|
58
|
+
global db, vector_store, fitness
|
|
59
|
+
config = get_config()
|
|
60
|
+
config.ensure_dirs()
|
|
61
|
+
db = Database(config.db_path)
|
|
62
|
+
await db.connect()
|
|
63
|
+
vector_store = VectorStore(config.data_dir, config.hivemind.collection_name)
|
|
64
|
+
vector_store.connect()
|
|
65
|
+
fitness = FitnessScorer()
|
|
66
|
+
logger.info("🖥️ Dashboard connected to AgentEvolution data at %s", config.data_dir)
|
|
67
|
+
|
|
68
|
+
@app.on_event("shutdown")
|
|
69
|
+
async def shutdown():
|
|
70
|
+
if db:
|
|
71
|
+
await db.close()
|
|
72
|
+
|
|
73
|
+
# ─── Dashboard Page ───
|
|
74
|
+
|
|
75
|
+
@app.get("/", response_class=HTMLResponse)
|
|
76
|
+
async def dashboard():
|
|
77
|
+
index = STATIC_DIR / "index.html"
|
|
78
|
+
return HTMLResponse(content=index.read_text(encoding="utf-8"))
|
|
79
|
+
|
|
80
|
+
@app.get("/style.css")
|
|
81
|
+
async def css():
|
|
82
|
+
return FileResponse(STATIC_DIR / "style.css", media_type="text/css")
|
|
83
|
+
|
|
84
|
+
@app.get("/app.js")
|
|
85
|
+
async def js():
|
|
86
|
+
return FileResponse(STATIC_DIR / "app.js", media_type="application/javascript")
|
|
87
|
+
|
|
88
|
+
# ─── API Endpoints ───
|
|
89
|
+
|
|
90
|
+
@app.get("/api/stats")
|
|
91
|
+
async def get_stats():
|
|
92
|
+
"""Get ecosystem-wide statistics."""
|
|
93
|
+
all_tools = await db.list_tools(status=ToolStatus.ACTIVE, limit=1000)
|
|
94
|
+
total_uses = sum(t.total_uses for t in all_tools)
|
|
95
|
+
total_agents = len(set(t.author_agent_id for t in all_tools))
|
|
96
|
+
avg_fitness = sum(t.fitness_score for t in all_tools) / len(all_tools) if all_tools else 0
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
"total_tools": len(all_tools),
|
|
100
|
+
"total_uses": total_uses,
|
|
101
|
+
"unique_agents": total_agents,
|
|
102
|
+
"avg_fitness": round(avg_fitness, 4),
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@app.get("/api/tools")
|
|
106
|
+
async def list_tools(limit: int = 50, status: str = "active"):
|
|
107
|
+
"""List all tools with details."""
|
|
108
|
+
tool_status = ToolStatus.ACTIVE if status == "active" else None
|
|
109
|
+
tools = await db.list_tools(status=tool_status, limit=limit)
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
"tools": [
|
|
113
|
+
{
|
|
114
|
+
"id": t.id,
|
|
115
|
+
"name": t.name,
|
|
116
|
+
"description": t.description,
|
|
117
|
+
"fitness_score": t.fitness_score,
|
|
118
|
+
"trust_level": t.trust_level.value if hasattr(t.trust_level, 'value') else t.trust_level,
|
|
119
|
+
"status": t.status.value if hasattr(t.status, 'value') else t.status,
|
|
120
|
+
"total_uses": t.total_uses,
|
|
121
|
+
"successful_uses": t.successful_uses,
|
|
122
|
+
"unique_agents": t.unique_agents,
|
|
123
|
+
"tags": t.tags,
|
|
124
|
+
"version": t.version,
|
|
125
|
+
"parent_tool_id": t.parent_tool_id,
|
|
126
|
+
"author_agent_id": t.author_agent_id,
|
|
127
|
+
"avg_execution_time_ms": t.avg_execution_time_ms,
|
|
128
|
+
"content_hash": t.content_hash[:16] + "..." if t.content_hash else "",
|
|
129
|
+
"created_at": t.created_at.isoformat() if t.created_at else "",
|
|
130
|
+
}
|
|
131
|
+
for t in tools
|
|
132
|
+
],
|
|
133
|
+
"total": len(tools),
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@app.get("/api/tools/{tool_id}")
|
|
137
|
+
async def get_tool(tool_id: str):
|
|
138
|
+
"""Get full tool details including code."""
|
|
139
|
+
tool = await db.get_tool(tool_id)
|
|
140
|
+
if not tool:
|
|
141
|
+
raise HTTPException(status_code=404, detail="Tool not found")
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
"id": tool.id,
|
|
145
|
+
"name": tool.name,
|
|
146
|
+
"description": tool.description,
|
|
147
|
+
"code": tool.code,
|
|
148
|
+
"test_case": tool.test_case,
|
|
149
|
+
"input_schema": tool.input_schema,
|
|
150
|
+
"fitness_score": tool.fitness_score,
|
|
151
|
+
"trust_level": tool.trust_level.value if hasattr(tool.trust_level, 'value') else tool.trust_level,
|
|
152
|
+
"status": tool.status.value if hasattr(tool.status, 'value') else tool.status,
|
|
153
|
+
"total_uses": tool.total_uses,
|
|
154
|
+
"successful_uses": tool.successful_uses,
|
|
155
|
+
"unique_agents": tool.unique_agents,
|
|
156
|
+
"tags": tool.tags,
|
|
157
|
+
"version": tool.version,
|
|
158
|
+
"parent_tool_id": tool.parent_tool_id,
|
|
159
|
+
"content_hash": tool.content_hash,
|
|
160
|
+
"author_agent_id": tool.author_agent_id,
|
|
161
|
+
"created_at": tool.created_at.isoformat() if tool.created_at else "",
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
@app.get("/api/tools/{tool_id}/provenance")
|
|
165
|
+
async def get_provenance(tool_id: str):
|
|
166
|
+
"""Get provenance chain for a tool."""
|
|
167
|
+
chain = await db.get_provenance_chain(tool_id)
|
|
168
|
+
return {
|
|
169
|
+
"chain": [
|
|
170
|
+
{
|
|
171
|
+
"tool_id": p.tool_id,
|
|
172
|
+
"version": p.version,
|
|
173
|
+
"content_hash": p.content_hash[:16] + "...",
|
|
174
|
+
"parent_hash": (p.parent_hash[:16] + "...") if p.parent_hash else None,
|
|
175
|
+
"security_scan": p.security_scan.value if hasattr(p.security_scan, 'value') else p.security_scan,
|
|
176
|
+
"execution_time_ms": p.performance.execution_time_ms if p.performance else 0,
|
|
177
|
+
"memory_peak_mb": p.performance.memory_peak_mb if p.performance else 0,
|
|
178
|
+
"signature": p.signature[:16] + "..." if p.signature else "",
|
|
179
|
+
"created_at": p.created_at.isoformat() if p.created_at else "",
|
|
180
|
+
}
|
|
181
|
+
for p in chain
|
|
182
|
+
],
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
@app.get("/api/activity")
|
|
186
|
+
async def get_activity(limit: int = 20):
|
|
187
|
+
"""Get recent activity feed."""
|
|
188
|
+
# Get recent tools as activity
|
|
189
|
+
tools = await db.list_tools(limit=limit)
|
|
190
|
+
activities = []
|
|
191
|
+
for t in tools:
|
|
192
|
+
activities.append({
|
|
193
|
+
"type": "tool_published",
|
|
194
|
+
"tool_name": t.name,
|
|
195
|
+
"tool_id": t.id,
|
|
196
|
+
"agent_id": t.author_agent_id,
|
|
197
|
+
"fitness_score": t.fitness_score,
|
|
198
|
+
"timestamp": t.created_at.isoformat() if t.created_at else "",
|
|
199
|
+
"tags": t.tags,
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
return {"activities": sorted(activities, key=lambda a: a["timestamp"], reverse=True)}
|
|
203
|
+
|
|
204
|
+
@app.get("/api/leaderboard")
|
|
205
|
+
async def get_leaderboard(limit: int = 10):
|
|
206
|
+
"""Get top tools by fitness score."""
|
|
207
|
+
tools = await db.list_tools(status=ToolStatus.ACTIVE, limit=limit)
|
|
208
|
+
# Sort by fitness
|
|
209
|
+
tools.sort(key=lambda t: t.fitness_score, reverse=True)
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
"leaderboard": [
|
|
213
|
+
{
|
|
214
|
+
"rank": i + 1,
|
|
215
|
+
"name": t.name,
|
|
216
|
+
"id": t.id,
|
|
217
|
+
"fitness_score": t.fitness_score,
|
|
218
|
+
"total_uses": t.total_uses,
|
|
219
|
+
"trust_level": t.trust_level.value if hasattr(t.trust_level, 'value') else t.trust_level,
|
|
220
|
+
"author": t.author_agent_id,
|
|
221
|
+
}
|
|
222
|
+
for i, t in enumerate(tools[:limit])
|
|
223
|
+
],
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return app
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
# ─── CLI Entry Point ───
|
|
230
|
+
|
|
231
|
+
def main():
|
|
232
|
+
"""Run the dashboard server."""
|
|
233
|
+
import uvicorn
|
|
234
|
+
config = AgentEvolutionConfig()
|
|
235
|
+
set_config(config)
|
|
236
|
+
print("🖥️ AgentEvolution Dashboard", file=sys.stderr)
|
|
237
|
+
print(f" http://localhost:8080", file=sys.stderr)
|
|
238
|
+
uvicorn.run(create_app(), host="0.0.0.0", port=8080, log_level="info")
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
if __name__ == "__main__":
|
|
242
|
+
main()
|