loopengt 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.
- loopengt/__init__.py +31 -0
- loopengt/adapters/__init__.py +1 -0
- loopengt/adapters/antigravity/__init__.py +1 -0
- loopengt/adapters/antigravity/adapter.py +55 -0
- loopengt/adapters/antigravity/commands.py +21 -0
- loopengt/adapters/base.py +51 -0
- loopengt/adapters/claude_code/__init__.py +1 -0
- loopengt/adapters/claude_code/adapter.py +55 -0
- loopengt/adapters/claude_code/commands.py +16 -0
- loopengt/adapters/codex/__init__.py +1 -0
- loopengt/adapters/codex/adapter.py +52 -0
- loopengt/adapters/codex/commands.py +16 -0
- loopengt/adapters/cursor/__init__.py +1 -0
- loopengt/adapters/cursor/adapter.py +56 -0
- loopengt/adapters/cursor/commands.py +29 -0
- loopengt/adapters/generic/__init__.py +1 -0
- loopengt/adapters/generic/terminal.py +82 -0
- loopengt/cli/__init__.py +1 -0
- loopengt/cli/commands/__init__.py +1 -0
- loopengt/cli/commands/design.py +171 -0
- loopengt/cli/commands/doctor.py +110 -0
- loopengt/cli/commands/eval.py +105 -0
- loopengt/cli/commands/init.py +131 -0
- loopengt/cli/commands/mcp_serve.py +57 -0
- loopengt/cli/commands/run.py +99 -0
- loopengt/cli/commands/template.py +145 -0
- loopengt/cli/commands/trace.py +114 -0
- loopengt/cli/formatters.py +125 -0
- loopengt/cli/main.py +66 -0
- loopengt/core/__init__.py +1 -0
- loopengt/core/evals/__init__.py +1 -0
- loopengt/core/evals/judges.py +216 -0
- loopengt/core/evals/metrics.py +119 -0
- loopengt/core/evals/regression.py +157 -0
- loopengt/core/memory/__init__.py +1 -0
- loopengt/core/memory/retrieval.py +124 -0
- loopengt/core/memory/store.py +184 -0
- loopengt/core/memory/summarizer.py +97 -0
- loopengt/core/models/__init__.py +43 -0
- loopengt/core/models/agent.py +126 -0
- loopengt/core/models/loop_spec.py +251 -0
- loopengt/core/models/policy.py +131 -0
- loopengt/core/models/state.py +271 -0
- loopengt/core/models/tool.py +105 -0
- loopengt/core/runtime/__init__.py +1 -0
- loopengt/core/runtime/checkpoint.py +152 -0
- loopengt/core/runtime/executor.py +463 -0
- loopengt/core/runtime/handoff.py +139 -0
- loopengt/core/runtime/scheduler.py +168 -0
- loopengt/core/tracing/__init__.py +1 -0
- loopengt/core/tracing/events.py +95 -0
- loopengt/core/tracing/exporters.py +158 -0
- loopengt/core/tracing/store.py +202 -0
- loopengt/mcp/__init__.py +1 -0
- loopengt/mcp/client/__init__.py +1 -0
- loopengt/mcp/client/manager.py +118 -0
- loopengt/mcp/client/tools.py +107 -0
- loopengt/mcp/server/__init__.py +1 -0
- loopengt/mcp/server/prompts.py +82 -0
- loopengt/mcp/server/resources.py +75 -0
- loopengt/mcp/server/server.py +50 -0
- loopengt/mcp/server/tools.py +214 -0
- loopengt/mcp/shared/__init__.py +1 -0
- loopengt/mcp/shared/schemas.py +91 -0
- loopengt/plugins/__init__.py +1 -0
- loopengt/plugins/base.py +90 -0
- loopengt/plugins/loader.py +130 -0
- loopengt/plugins/manifest.py +70 -0
- loopengt/plugins/registry.py +146 -0
- loopengt/prompts/LOOPENGT.md +60 -0
- loopengt/prompts/__init__.py +1 -0
- loopengt/storage/__init__.py +1 -0
- loopengt/storage/jsonl.py +84 -0
- loopengt/storage/sqlite.py +102 -0
- loopengt/templates/__init__.py +1 -0
- loopengt/templates/builtins/handoff_loop/LOOPENGS.md +10 -0
- loopengt/templates/builtins/planner_executor/LOOPENGS.md +29 -0
- loopengt/templates/builtins/research_architect/LOOPENGS.md +17 -0
- loopengt/templates/builtins/reviewer_retry/LOOPENGS.md +29 -0
- loopengt/templates/builtins/supervisor_workers/LOOPENGS.md +29 -0
- loopengt/templates/loader.py +38 -0
- loopengt/templates/registry.py +85 -0
- loopengt-0.1.0.dist-info/METADATA +275 -0
- loopengt-0.1.0.dist-info/RECORD +87 -0
- loopengt-0.1.0.dist-info/WHEEL +4 -0
- loopengt-0.1.0.dist-info/entry_points.txt +8 -0
- loopengt-0.1.0.dist-info/licenses/LICENSE +674 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""SQLite storage backend for traces and checkpoints."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import structlog
|
|
10
|
+
|
|
11
|
+
logger = structlog.get_logger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SQLiteStorage:
|
|
15
|
+
"""General-purpose async SQLite storage.
|
|
16
|
+
|
|
17
|
+
Wraps ``aiosqlite`` and provides convenience methods for common
|
|
18
|
+
operations used by the trace and checkpoint subsystems.
|
|
19
|
+
|
|
20
|
+
Usage::
|
|
21
|
+
|
|
22
|
+
storage = SQLiteStorage(Path(".loopengt/data.db"))
|
|
23
|
+
async with storage:
|
|
24
|
+
await storage.execute("INSERT INTO ...", ...)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, db_path: Path) -> None:
|
|
28
|
+
self._db_path = db_path
|
|
29
|
+
self._db: Any = None
|
|
30
|
+
self._log = logger.bind(component="sqlite", path=str(db_path))
|
|
31
|
+
|
|
32
|
+
async def __aenter__(self) -> "SQLiteStorage":
|
|
33
|
+
await self.open()
|
|
34
|
+
return self
|
|
35
|
+
|
|
36
|
+
async def __aexit__(self, *args: Any) -> None:
|
|
37
|
+
await self.close()
|
|
38
|
+
|
|
39
|
+
async def open(self) -> None:
|
|
40
|
+
"""Open the database connection."""
|
|
41
|
+
import aiosqlite
|
|
42
|
+
|
|
43
|
+
self._db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
44
|
+
self._db = await aiosqlite.connect(str(self._db_path))
|
|
45
|
+
await self._db.execute("PRAGMA journal_mode=WAL")
|
|
46
|
+
self._log.debug("sqlite.opened")
|
|
47
|
+
|
|
48
|
+
async def close(self) -> None:
|
|
49
|
+
"""Close the database connection."""
|
|
50
|
+
if self._db:
|
|
51
|
+
await self._db.close()
|
|
52
|
+
self._db = None
|
|
53
|
+
self._log.debug("sqlite.closed")
|
|
54
|
+
|
|
55
|
+
async def execute(self, sql: str, params: tuple[Any, ...] = ()) -> Any:
|
|
56
|
+
"""Execute a SQL statement."""
|
|
57
|
+
if self._db is None:
|
|
58
|
+
raise RuntimeError("Database not open. Call open() first.")
|
|
59
|
+
cursor = await self._db.execute(sql, params)
|
|
60
|
+
await self._db.commit()
|
|
61
|
+
return cursor
|
|
62
|
+
|
|
63
|
+
async def executemany(
|
|
64
|
+
self, sql: str, params_seq: list[tuple[Any, ...]]
|
|
65
|
+
) -> None:
|
|
66
|
+
"""Execute a SQL statement with multiple parameter sets."""
|
|
67
|
+
if self._db is None:
|
|
68
|
+
raise RuntimeError("Database not open. Call open() first.")
|
|
69
|
+
await self._db.executemany(sql, params_seq)
|
|
70
|
+
await self._db.commit()
|
|
71
|
+
|
|
72
|
+
async def fetchall(
|
|
73
|
+
self, sql: str, params: tuple[Any, ...] = ()
|
|
74
|
+
) -> list[dict[str, Any]]:
|
|
75
|
+
"""Execute a query and return all rows as dicts."""
|
|
76
|
+
if self._db is None:
|
|
77
|
+
raise RuntimeError("Database not open. Call open() first.")
|
|
78
|
+
cursor = await self._db.execute(sql, params)
|
|
79
|
+
rows = await cursor.fetchall()
|
|
80
|
+
columns = [desc[0] for desc in cursor.description]
|
|
81
|
+
return [dict(zip(columns, row)) for row in rows]
|
|
82
|
+
|
|
83
|
+
async def fetchone(
|
|
84
|
+
self, sql: str, params: tuple[Any, ...] = ()
|
|
85
|
+
) -> dict[str, Any] | None:
|
|
86
|
+
"""Execute a query and return the first row as a dict."""
|
|
87
|
+
if self._db is None:
|
|
88
|
+
raise RuntimeError("Database not open. Call open() first.")
|
|
89
|
+
cursor = await self._db.execute(sql, params)
|
|
90
|
+
row = await cursor.fetchone()
|
|
91
|
+
if row is None:
|
|
92
|
+
return None
|
|
93
|
+
columns = [desc[0] for desc in cursor.description]
|
|
94
|
+
return dict(zip(columns, row))
|
|
95
|
+
|
|
96
|
+
async def table_exists(self, table_name: str) -> bool:
|
|
97
|
+
"""Check whether a table exists."""
|
|
98
|
+
result = await self.fetchone(
|
|
99
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name=?",
|
|
100
|
+
(table_name,),
|
|
101
|
+
)
|
|
102
|
+
return result is not None
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Template management package."""
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Handoff Loop Pattern
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
A sequential chain where each agent processes and hands off enriched
|
|
5
|
+
context to the next agent in the pipeline.
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
- Multi-stage document processing pipelines
|
|
9
|
+
- Data transformation chains
|
|
10
|
+
- Any workflow where each stage enriches the previous output
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Planner-Executor Pattern
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
A sequential two-agent pattern where a **Planner** analyses the goal and
|
|
5
|
+
produces a structured action plan, then an **Executor** carries out each
|
|
6
|
+
step of that plan.
|
|
7
|
+
|
|
8
|
+
## When to Use
|
|
9
|
+
- Tasks with clear decomposition into planning and execution phases
|
|
10
|
+
- Code generation, refactoring, and migration tasks
|
|
11
|
+
- Document drafting and editing workflows
|
|
12
|
+
|
|
13
|
+
## Agents
|
|
14
|
+
| Agent | Role |
|
|
15
|
+
|----------|-----------------------------------------|
|
|
16
|
+
| planner | Analyses the goal, produces action plan |
|
|
17
|
+
| executor | Executes each step of the plan |
|
|
18
|
+
|
|
19
|
+
## Steps
|
|
20
|
+
1. **plan** — Planner analyses the goal and outputs a structured plan
|
|
21
|
+
2. **execute** — Executor follows the plan step-by-step
|
|
22
|
+
|
|
23
|
+
## Verification
|
|
24
|
+
- Plan must contain at least one actionable step
|
|
25
|
+
- Executor output must address all plan items
|
|
26
|
+
|
|
27
|
+
## Risks
|
|
28
|
+
- Planner may produce overly complex plans → mitigate with max_steps
|
|
29
|
+
- Executor may deviate from plan → add plan-adherence verification gate
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Research-Architect Pattern
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
A three-phase workflow: **Research** gathers context, **Synthesiser**
|
|
5
|
+
distils findings, and **Architect** produces a design based on synthesis.
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
- System design and architecture tasks
|
|
9
|
+
- Technical specification writing
|
|
10
|
+
- Literature review → analysis → recommendation workflows
|
|
11
|
+
|
|
12
|
+
## Agents
|
|
13
|
+
| Agent | Role |
|
|
14
|
+
|-------------|--------------------------------------------|
|
|
15
|
+
| researcher | Deep research on the topic |
|
|
16
|
+
| synthesiser | Distils research into key findings |
|
|
17
|
+
| architect | Designs solution based on synthesis |
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Reviewer-Retry Pattern
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
An iterative loop where a **Worker** produces output, a **Reviewer**
|
|
5
|
+
evaluates it, and the worker retries with feedback until the review passes.
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
- Code review workflows
|
|
9
|
+
- Content quality assurance
|
|
10
|
+
- Any task requiring iterative refinement based on feedback
|
|
11
|
+
|
|
12
|
+
## Agents
|
|
13
|
+
| Agent | Role |
|
|
14
|
+
|----------|-------------------------------------------|
|
|
15
|
+
| worker | Produces initial output and revisions |
|
|
16
|
+
| reviewer | Evaluates output and provides feedback |
|
|
17
|
+
|
|
18
|
+
## Steps
|
|
19
|
+
1. **produce** — Worker creates initial output
|
|
20
|
+
2. **review** — Reviewer evaluates and provides feedback
|
|
21
|
+
3. **revise** — Worker revises based on feedback (loops back to review)
|
|
22
|
+
|
|
23
|
+
## Verification
|
|
24
|
+
- Reviewer must provide explicit pass/fail verdict
|
|
25
|
+
- Max 3 revision cycles to prevent infinite loops
|
|
26
|
+
|
|
27
|
+
## Risks
|
|
28
|
+
- Infinite revision loops → enforced by max_turns policy
|
|
29
|
+
- Reviewer may be too strict → add quality_threshold stop condition
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Supervisor-Workers Pattern
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
A **Supervisor** agent analyses the goal and delegates sub-tasks to
|
|
5
|
+
specialised **Worker** agents, then aggregates results.
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
- Complex tasks requiring multiple specialisations
|
|
9
|
+
- Parallel workstreams that need coordination
|
|
10
|
+
- Tasks where different expertise is needed for different parts
|
|
11
|
+
|
|
12
|
+
## Agents
|
|
13
|
+
| Agent | Role |
|
|
14
|
+
|-------------|-----------------------------------------------|
|
|
15
|
+
| supervisor | Decomposes goal, delegates, aggregates results|
|
|
16
|
+
| researcher | Gathers information and context |
|
|
17
|
+
| implementer | Writes code or produces artifacts |
|
|
18
|
+
| tester | Validates and tests outputs |
|
|
19
|
+
|
|
20
|
+
## Steps
|
|
21
|
+
1. **decompose** — Supervisor analyses goal and creates sub-tasks
|
|
22
|
+
2. **research** — Researcher gathers relevant context
|
|
23
|
+
3. **implement** — Implementer produces the work product
|
|
24
|
+
4. **test** — Tester validates the output
|
|
25
|
+
5. **aggregate** — Supervisor combines and reviews all results
|
|
26
|
+
|
|
27
|
+
## Verification
|
|
28
|
+
- Each worker output must pass supervisor validation
|
|
29
|
+
- Final aggregate must satisfy the original goal
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Template loader — YAML to LoopSpec conversion."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import yaml
|
|
9
|
+
|
|
10
|
+
from loopengt.core.models.loop_spec import LoopSpec
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TemplateLoader:
|
|
14
|
+
"""Loads loop templates from YAML files and converts to LoopSpec.
|
|
15
|
+
|
|
16
|
+
Usage::
|
|
17
|
+
|
|
18
|
+
loader = TemplateLoader()
|
|
19
|
+
spec = loader.load(Path("loop.yaml"))
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def load(self, path: Path) -> LoopSpec:
|
|
23
|
+
"""Load a loop.yaml and validate it as a LoopSpec."""
|
|
24
|
+
raw = yaml.safe_load(path.read_text(encoding="utf-8"))
|
|
25
|
+
return LoopSpec.model_validate(raw)
|
|
26
|
+
|
|
27
|
+
def load_dict(self, data: dict[str, Any]) -> LoopSpec:
|
|
28
|
+
"""Validate a dict as a LoopSpec."""
|
|
29
|
+
return LoopSpec.model_validate(data)
|
|
30
|
+
|
|
31
|
+
def dump(self, spec: LoopSpec, path: Path) -> None:
|
|
32
|
+
"""Serialize a LoopSpec to YAML and write to a file."""
|
|
33
|
+
data = spec.model_dump(mode="json", exclude_none=True)
|
|
34
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
35
|
+
path.write_text(
|
|
36
|
+
yaml.dump(data, default_flow_style=False, sort_keys=False),
|
|
37
|
+
encoding="utf-8",
|
|
38
|
+
)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Template discovery from built-ins and plugins."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import structlog
|
|
9
|
+
|
|
10
|
+
logger = structlog.get_logger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BuiltinTemplateProvider:
|
|
14
|
+
"""Provides built-in loop templates bundled with loopengt."""
|
|
15
|
+
|
|
16
|
+
TEMPLATE_NAMES = [
|
|
17
|
+
"planner_executor",
|
|
18
|
+
"reviewer_retry",
|
|
19
|
+
"supervisor_workers",
|
|
20
|
+
"research_architect",
|
|
21
|
+
"handoff_loop",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
def list_templates(self) -> list[dict[str, str]]:
|
|
25
|
+
"""List available built-in templates."""
|
|
26
|
+
return [{"name": name, "source": "builtin"} for name in self.TEMPLATE_NAMES]
|
|
27
|
+
|
|
28
|
+
def get_template_path(self, name: str) -> Path | None:
|
|
29
|
+
"""Get the path to a built-in template directory."""
|
|
30
|
+
try:
|
|
31
|
+
from importlib.resources import files
|
|
32
|
+
|
|
33
|
+
pkg = files("loopengt.templates.builtins").joinpath(name) # type: ignore[union-attr]
|
|
34
|
+
return Path(str(pkg))
|
|
35
|
+
except Exception:
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TemplateRegistry:
|
|
40
|
+
"""Registry that discovers templates from multiple sources.
|
|
41
|
+
|
|
42
|
+
Sources:
|
|
43
|
+
1. Built-in templates (loopengt.templates.builtins)
|
|
44
|
+
2. Project-local templates (.loopengt/templates/)
|
|
45
|
+
3. Plugin-provided templates (loopengt.templates entry point)
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, local_dir: Path | None = None) -> None:
|
|
49
|
+
self._local_dir = local_dir or Path(".loopengt/templates")
|
|
50
|
+
self._builtin = BuiltinTemplateProvider()
|
|
51
|
+
self._log = logger.bind(component="template_registry")
|
|
52
|
+
|
|
53
|
+
def list_all(self) -> list[dict[str, str]]:
|
|
54
|
+
"""List all available templates from all sources."""
|
|
55
|
+
templates = self._builtin.list_templates()
|
|
56
|
+
|
|
57
|
+
# Local templates
|
|
58
|
+
if self._local_dir.exists():
|
|
59
|
+
for entry in sorted(self._local_dir.iterdir()):
|
|
60
|
+
if entry.is_dir() and (entry / "loop.yaml").exists():
|
|
61
|
+
if entry.name not in BuiltinTemplateProvider.TEMPLATE_NAMES:
|
|
62
|
+
templates.append({
|
|
63
|
+
"name": entry.name,
|
|
64
|
+
"source": "local",
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return templates
|
|
68
|
+
|
|
69
|
+
def get_template(self, name: str) -> dict[str, Any] | None:
|
|
70
|
+
"""Load a template's loop.yaml as a dict."""
|
|
71
|
+
import yaml
|
|
72
|
+
|
|
73
|
+
# Check local first
|
|
74
|
+
local_path = self._local_dir / name / "loop.yaml"
|
|
75
|
+
if local_path.exists():
|
|
76
|
+
return yaml.safe_load(local_path.read_text(encoding="utf-8")) # type: ignore[no-any-return]
|
|
77
|
+
|
|
78
|
+
# Check built-ins
|
|
79
|
+
builtin_path = self._builtin.get_template_path(name)
|
|
80
|
+
if builtin_path:
|
|
81
|
+
yaml_path = builtin_path / "loop.yaml"
|
|
82
|
+
if yaml_path.exists():
|
|
83
|
+
return yaml.safe_load(yaml_path.read_text(encoding="utf-8")) # type: ignore[no-any-return]
|
|
84
|
+
|
|
85
|
+
return None
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: loopengt
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Loop Engineering Agent — design, orchestrate, and evaluate agent loops
|
|
5
|
+
Project-URL: Homepage, https://github.com/Sriramdayal/LOOPENGT
|
|
6
|
+
Project-URL: Documentation, https://github.com/Sriramdayal/LOOPENGT/tree/main/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/Sriramdayal/LOOPENGT
|
|
8
|
+
Project-URL: Issues, https://github.com/Sriramdayal/LOOPENGT/issues
|
|
9
|
+
Author: Loop Engineering Contributors
|
|
10
|
+
License-Expression: GPL-3.0-or-later
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agent,ai,loop,mcp,orchestration
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: aiosqlite>=0.19
|
|
24
|
+
Requires-Dist: anyio>=4.0
|
|
25
|
+
Requires-Dist: pydantic<3.0,>=2.0
|
|
26
|
+
Requires-Dist: pyyaml>=6.0
|
|
27
|
+
Requires-Dist: rich>=13.0
|
|
28
|
+
Requires-Dist: structlog>=23.0
|
|
29
|
+
Requires-Dist: typer>=0.9
|
|
30
|
+
Provides-Extra: all
|
|
31
|
+
Requires-Dist: fastmcp>=0.1; extra == 'all'
|
|
32
|
+
Requires-Dist: httpx>=0.25; extra == 'all'
|
|
33
|
+
Requires-Dist: mcp>=1.0; extra == 'all'
|
|
34
|
+
Requires-Dist: mypy>=1.8; extra == 'all'
|
|
35
|
+
Requires-Dist: openai>=1.0; extra == 'all'
|
|
36
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'all'
|
|
37
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'all'
|
|
38
|
+
Requires-Dist: pytest>=7.0; extra == 'all'
|
|
39
|
+
Requires-Dist: ruff>=0.4; extra == 'all'
|
|
40
|
+
Requires-Dist: types-aiofiles>=23.0; extra == 'all'
|
|
41
|
+
Requires-Dist: types-pyyaml>=6.0; extra == 'all'
|
|
42
|
+
Provides-Extra: antigravity
|
|
43
|
+
Provides-Extra: claude-code
|
|
44
|
+
Provides-Extra: codex
|
|
45
|
+
Provides-Extra: cursor
|
|
46
|
+
Provides-Extra: dev
|
|
47
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
48
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
49
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
50
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
51
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
52
|
+
Requires-Dist: types-aiofiles>=23.0; extra == 'dev'
|
|
53
|
+
Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
|
|
54
|
+
Provides-Extra: llm
|
|
55
|
+
Requires-Dist: httpx>=0.25; extra == 'llm'
|
|
56
|
+
Requires-Dist: openai>=1.0; extra == 'llm'
|
|
57
|
+
Provides-Extra: mcp
|
|
58
|
+
Requires-Dist: fastmcp>=0.1; extra == 'mcp'
|
|
59
|
+
Requires-Dist: mcp>=1.0; extra == 'mcp'
|
|
60
|
+
Description-Content-Type: text/markdown
|
|
61
|
+
|
|
62
|
+
# LOOPENGT — Loop Engineering Agent
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
[](https://www.python.org/downloads/)
|
|
66
|
+
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
67
|
+
|
|
68
|
+
**Design, orchestrate, and evaluate agent loops** — a framework for building
|
|
69
|
+
production-grade multi-agent workflows with MCP integration and IDE-agnostic
|
|
70
|
+
plugin architecture.
|
|
71
|
+
|
|
72
|
+
## Features
|
|
73
|
+
|
|
74
|
+
- 🔄 **5 orchestration patterns** — sequential, supervisor-worker, parallel
|
|
75
|
+
fan-out, handoff, evaluator-optimizer
|
|
76
|
+
- 🧩 **Plugin architecture** — extend with custom adapters, tools, templates. Includes built-in plugins for **Cursor, Claude Code, Antigravity, and Codex**.
|
|
77
|
+
- 🧠 **Native LLM Integration** — Built-in support for OpenAI and Hugging Face Inference Endpoints for autonomous loop orchestration.
|
|
78
|
+
- 🔌 **MCP server** — expose loop tools to Cursor, Claude Code, Antigravity
|
|
79
|
+
- 📊 **Built-in tracing** — SQLite + JSONL traces with OpenTelemetry export
|
|
80
|
+
- 🧪 **Evaluation framework** — built-in metrics + LLM-as-judge
|
|
81
|
+
- 📝 **5 built-in templates** — planner-executor, reviewer-retry,
|
|
82
|
+
supervisor-workers, research-architect, handoff loop
|
|
83
|
+
- ⚡ **Async-first** — built on anyio for concurrent execution
|
|
84
|
+
|
|
85
|
+
## Quick Start
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# Create and activate a virtual environment
|
|
89
|
+
uv venv
|
|
90
|
+
.venv\Scripts\activate # On Windows
|
|
91
|
+
# source .venv/bin/activate # On macOS/Linux
|
|
92
|
+
|
|
93
|
+
# Install the base package
|
|
94
|
+
uv pip install loopengt
|
|
95
|
+
|
|
96
|
+
# With MCP support
|
|
97
|
+
uv pip install "loopengt[mcp]"
|
|
98
|
+
|
|
99
|
+
# With everything
|
|
100
|
+
uv pip install "loopengt[all]"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Initialize
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
loopengt init
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Creates a `.loopengt/` directory with configuration, templates, and prompts.
|
|
110
|
+
|
|
111
|
+
### Design a Loop
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
loopengt design "code review loop with automated testing"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Generates `loop.yaml` and `LOOP_DESIGN.md`.
|
|
118
|
+
|
|
119
|
+
### Execute
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
loopengt run loop.yaml
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Inspect Traces
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
loopengt trace <run_id>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Evaluate
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
loopengt eval <run_id>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## How to Engineer a Loop (Step-by-Step Guide)
|
|
138
|
+
|
|
139
|
+
Building an autonomous agent loop with LOOPENGT involves designing the architecture, defining the agents, and orchestrating their workflow. Here is the recommended workflow for engineering a new loop from scratch:
|
|
140
|
+
|
|
141
|
+
### Step 1: Initialize Your Workspace
|
|
142
|
+
First, set up the necessary project configurations by running:
|
|
143
|
+
```bash
|
|
144
|
+
loopengt init
|
|
145
|
+
```
|
|
146
|
+
This scaffolds a `.loopengt/` directory in your project containing configuration files, built-in templates, and prompts.
|
|
147
|
+
|
|
148
|
+
### Step 2: Set Your LLM Provider
|
|
149
|
+
Ensure your environment is configured for the LLM that will both design and run your agents. For example, to use OpenAI:
|
|
150
|
+
```bash
|
|
151
|
+
export OPENAI_API_KEY="your-api-key"
|
|
152
|
+
export LOOPENGT_LLM_PROVIDER="openai"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Step 3: Design the Loop
|
|
156
|
+
Instead of writing the YAML specification manually, use the built-in AI Architect to design the loop for you. Provide a clear goal:
|
|
157
|
+
```bash
|
|
158
|
+
loopengt design "create a content generation loop where a Researcher finds facts, a Writer drafts an article, and an Editor reviews it for quality."
|
|
159
|
+
```
|
|
160
|
+
The architect will generate two files:
|
|
161
|
+
- **`LOOP_DESIGN.md`**: A human-readable breakdown of the agents, their roles, and the orchestration strategy.
|
|
162
|
+
- **`loop.yaml`**: The executable Pydantic-serializable configuration file.
|
|
163
|
+
|
|
164
|
+
### Step 4: Refine the Configuration (Optional)
|
|
165
|
+
Open `loop.yaml` in your editor. You can manually tweak:
|
|
166
|
+
- **Agents**: Adjust system prompts, input/output schemas, or LLM settings.
|
|
167
|
+
- **Tools**: Add native functions or MCP integrations.
|
|
168
|
+
- **Policies**: Set retry limits, turn budgets, or verification gates.
|
|
169
|
+
|
|
170
|
+
### Step 5: Execute the Loop
|
|
171
|
+
Run the orchestrator using your generated specification:
|
|
172
|
+
```bash
|
|
173
|
+
loopengt run loop.yaml
|
|
174
|
+
```
|
|
175
|
+
Watch the terminal as the Executor coordinates the agents (Researcher -> Writer -> Editor) based on the chosen pattern (e.g., sequential or supervisor-worker).
|
|
176
|
+
|
|
177
|
+
### Step 6: Trace and Evaluate
|
|
178
|
+
Once the run completes, inspect the detailed execution trace to debug or review agent interactions:
|
|
179
|
+
```bash
|
|
180
|
+
# List recent runs to find the ID
|
|
181
|
+
ls .loopengt/runs/
|
|
182
|
+
|
|
183
|
+
# Inspect the trace
|
|
184
|
+
loopengt trace <run_id>
|
|
185
|
+
```
|
|
186
|
+
If you want to score the quality of the final output, run the evaluator:
|
|
187
|
+
```bash
|
|
188
|
+
loopengt eval <run_id>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Architecture
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
CLI / IDE Adapters → MCP Server → Plugin System → Core Runtime → Models
|
|
195
|
+
↓
|
|
196
|
+
Tracing & Memory → Storage
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
See [ARCHITECTURE.md](ARCHITECTURE.md) for details.
|
|
200
|
+
|
|
201
|
+
## Orchestration Patterns
|
|
202
|
+
|
|
203
|
+
| Pattern | Description | Use Case |
|
|
204
|
+
|---------|-------------|----------|
|
|
205
|
+
| `sequential` | Steps execute in order | Simple workflows |
|
|
206
|
+
| `supervisor_worker` | Supervisor delegates to workers | Complex tasks |
|
|
207
|
+
| `parallel_fan_out` | Independent steps run concurrently | Batch processing |
|
|
208
|
+
| `handoff` | Agents pass enriched context along | Pipelines |
|
|
209
|
+
| `evaluator_optimizer` | Iterative quality improvement | Code review |
|
|
210
|
+
|
|
211
|
+
## MCP Integration
|
|
212
|
+
|
|
213
|
+
Start the MCP server:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
loopengt mcp --transport stdio
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Add to Cursor (`.cursor/mcp.json`):
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"mcpServers": {
|
|
224
|
+
"loopengt": {
|
|
225
|
+
"command": "loopengt",
|
|
226
|
+
"args": ["mcp", "--transport", "stdio"]
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## CLI Commands
|
|
233
|
+
|
|
234
|
+
| Command | Description |
|
|
235
|
+
|---------|-------------|
|
|
236
|
+
| `loopengt init` | Scaffold .loopengt/ project directory |
|
|
237
|
+
| `loopengt design "goal"` | Design a loop from a goal |
|
|
238
|
+
| `loopengt run <spec>` | Execute a loop spec |
|
|
239
|
+
| `loopengt trace <id>` | Inspect execution trace |
|
|
240
|
+
| `loopengt eval <id>` | Run evaluations |
|
|
241
|
+
| `loopengt template list` | List available templates |
|
|
242
|
+
| `loopengt doctor` | Diagnose configuration |
|
|
243
|
+
| `loopengt mcp` | Start MCP server |
|
|
244
|
+
|
|
245
|
+
## Development
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
git clone https://github.com/Sriramdayal/LOOPENGT.git
|
|
249
|
+
cd LOOPENGT
|
|
250
|
+
uv pip install -e ".[dev]"
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
# Lint
|
|
254
|
+
ruff check src/ tests/
|
|
255
|
+
|
|
256
|
+
# Type check
|
|
257
|
+
mypy src/loopengt/
|
|
258
|
+
|
|
259
|
+
# Test
|
|
260
|
+
pytest tests/ -v
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Plugin Development
|
|
264
|
+
|
|
265
|
+
See [docs/plugin_dev.md](docs/plugin_dev.md) for the plugin development guide.
|
|
266
|
+
|
|
267
|
+
## Examples
|
|
268
|
+
|
|
269
|
+
Check out the `examples/` directory for programmatic usage:
|
|
270
|
+
- `examples/basic_loop.py` — A simple sequential loop setup.
|
|
271
|
+
- `examples/multi_agent_loop.py` — A complex Supervisor-Worker multi-agent orchestration.
|
|
272
|
+
|
|
273
|
+
## License
|
|
274
|
+
|
|
275
|
+
[GPL-3.0](LICENSE)
|