babelagent 0.1.0__tar.gz
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.
- babelagent-0.1.0/.github/workflows/ci.yml +39 -0
- babelagent-0.1.0/.github/workflows/docs.yml +47 -0
- babelagent-0.1.0/.github/workflows/publish.yml +27 -0
- babelagent-0.1.0/.gitignore +34 -0
- babelagent-0.1.0/CHANGELOG.md +61 -0
- babelagent-0.1.0/CITATION.cff +22 -0
- babelagent-0.1.0/LICENSE +21 -0
- babelagent-0.1.0/PKG-INFO +135 -0
- babelagent-0.1.0/README.md +83 -0
- babelagent-0.1.0/brand/extra.css +135 -0
- babelagent-0.1.0/docs/SECURITY.md +62 -0
- babelagent-0.1.0/docs/STORY.md +247 -0
- babelagent-0.1.0/docs/a2a.md +68 -0
- babelagent-0.1.0/docs/adapters.md +78 -0
- babelagent-0.1.0/docs/api.md +45 -0
- babelagent-0.1.0/docs/changelog.md +1 -0
- babelagent-0.1.0/docs/index.md +83 -0
- babelagent-0.1.0/docs/posts/babelagent-the-problem.md +33 -0
- babelagent-0.1.0/docs/quickstart.md +96 -0
- babelagent-0.1.0/docs/stylesheets/extra.css +135 -0
- babelagent-0.1.0/mkdocs.yml +81 -0
- babelagent-0.1.0/pyproject.toml +85 -0
- babelagent-0.1.0/src/babelagent/__init__.py +87 -0
- babelagent-0.1.0/src/babelagent/__main__.py +6 -0
- babelagent-0.1.0/src/babelagent/adapters/__init__.py +22 -0
- babelagent-0.1.0/src/babelagent/adapters/a2a_agent.py +143 -0
- babelagent-0.1.0/src/babelagent/adapters/auto.py +137 -0
- babelagent-0.1.0/src/babelagent/adapters/base.py +104 -0
- babelagent-0.1.0/src/babelagent/adapters/callable_agent.py +52 -0
- babelagent-0.1.0/src/babelagent/adapters/frameworks/__init__.py +81 -0
- babelagent-0.1.0/src/babelagent/adapters/http_agent.py +278 -0
- babelagent-0.1.0/src/babelagent/adapters/llm_agent.py +117 -0
- babelagent-0.1.0/src/babelagent/adapters/mcp_agent.py +57 -0
- babelagent-0.1.0/src/babelagent/config.py +42 -0
- babelagent-0.1.0/src/babelagent/core/__init__.py +47 -0
- babelagent-0.1.0/src/babelagent/core/agent.py +74 -0
- babelagent-0.1.0/src/babelagent/core/errors.py +39 -0
- babelagent-0.1.0/src/babelagent/core/grade.py +85 -0
- babelagent-0.1.0/src/babelagent/core/graph.py +164 -0
- babelagent-0.1.0/src/babelagent/core/message.py +47 -0
- babelagent-0.1.0/src/babelagent/core/node.py +50 -0
- babelagent-0.1.0/src/babelagent/core/scheduler.py +252 -0
- babelagent-0.1.0/src/babelagent/core/topology.py +88 -0
- babelagent-0.1.0/src/babelagent/io/__init__.py +3 -0
- babelagent-0.1.0/src/babelagent/io/_demo_assets.py +45 -0
- babelagent-0.1.0/src/babelagent/io/cli.py +104 -0
- babelagent-0.1.0/src/babelagent/io/doctor.py +52 -0
- babelagent-0.1.0/src/babelagent/io/mcp.py +68 -0
- babelagent-0.1.0/src/babelagent/io/rest.py +233 -0
- babelagent-0.1.0/src/babelagent/py.typed +0 -0
- babelagent-0.1.0/tests/security/test_hardening.py +348 -0
- babelagent-0.1.0/tests/unit/test_a2a.py +80 -0
- babelagent-0.1.0/tests/unit/test_adapt.py +76 -0
- babelagent-0.1.0/tests/unit/test_demo.py +26 -0
- babelagent-0.1.0/tests/unit/test_graph.py +105 -0
- babelagent-0.1.0/tests/unit/test_mcp_server.py +22 -0
- babelagent-0.1.0/tests/unit/test_rest.py +62 -0
- babelagent-0.1.0/tests/unit/test_topology.py +71 -0
- babelagent-0.1.0/tests/unit/test_version.py +27 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ci-${{ github.ref }}
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
gate:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
strategy:
|
|
19
|
+
matrix:
|
|
20
|
+
python-version: ["3.11", "3.12"]
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
with:
|
|
24
|
+
persist-credentials: false
|
|
25
|
+
- uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: ${{ matrix.python-version }}
|
|
28
|
+
- name: Install (with serve + mcp + dev extras)
|
|
29
|
+
run: |
|
|
30
|
+
python -m pip install --upgrade pip
|
|
31
|
+
pip install -e ".[serve,mcp,dev]"
|
|
32
|
+
- name: Lint
|
|
33
|
+
run: ruff check src tests
|
|
34
|
+
- name: Type check
|
|
35
|
+
run: mypy src
|
|
36
|
+
- name: Test
|
|
37
|
+
run: pytest -q
|
|
38
|
+
- name: Smoke (key-free demo)
|
|
39
|
+
run: babelagent demo
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- "docs/**"
|
|
8
|
+
- "src/**"
|
|
9
|
+
- "mkdocs.yml"
|
|
10
|
+
- "CHANGELOG.md"
|
|
11
|
+
- ".github/workflows/docs.yml"
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
pages: write
|
|
17
|
+
id-token: write
|
|
18
|
+
|
|
19
|
+
# Allow one concurrent deploy; don't cancel an in-progress production deploy.
|
|
20
|
+
concurrency:
|
|
21
|
+
group: pages
|
|
22
|
+
cancel-in-progress: false
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
build:
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
- uses: actions/setup-python@v5
|
|
30
|
+
with:
|
|
31
|
+
python-version: "3.12"
|
|
32
|
+
- name: Install (package for autodoc + mkdocs)
|
|
33
|
+
run: pip install -e . mkdocs-material "mkdocstrings[python]"
|
|
34
|
+
- run: mkdocs build --strict
|
|
35
|
+
- uses: actions/upload-pages-artifact@v3
|
|
36
|
+
with:
|
|
37
|
+
path: site
|
|
38
|
+
|
|
39
|
+
deploy:
|
|
40
|
+
needs: build
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
environment:
|
|
43
|
+
name: github-pages
|
|
44
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
45
|
+
steps:
|
|
46
|
+
- id: deployment
|
|
47
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
# Releases to PyPI via OIDC Trusted Publishing (no token secrets in the repo).
|
|
4
|
+
# Configured pending publisher: repo amitpatole/babelagent, workflow publish.yml.
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
tags: ["v*"]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
pypi:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
id-token: write # mint the OIDC token PyPI verifies against the publisher
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
with:
|
|
20
|
+
persist-credentials: false
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.12"
|
|
24
|
+
- run: pip install --upgrade pip build
|
|
25
|
+
- run: python -m build
|
|
26
|
+
- name: Publish to PyPI (Trusted Publishing)
|
|
27
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
*.egg
|
|
9
|
+
|
|
10
|
+
# Virtual envs
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
env/
|
|
14
|
+
|
|
15
|
+
# Tooling caches
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
.mypy_cache/
|
|
18
|
+
.ruff_cache/
|
|
19
|
+
.coverage
|
|
20
|
+
htmlcov/
|
|
21
|
+
|
|
22
|
+
# Docs build
|
|
23
|
+
site/
|
|
24
|
+
|
|
25
|
+
# OS
|
|
26
|
+
.DS_Store
|
|
27
|
+
|
|
28
|
+
# Security cadence — NEVER commit residuals / red-team notes / PoCs (local only)
|
|
29
|
+
.security-local/
|
|
30
|
+
docs/SECURITY_RESIDUALS.md
|
|
31
|
+
|
|
32
|
+
# Local env / secrets (creds live in ~/.config, never here)
|
|
33
|
+
.env
|
|
34
|
+
*.local.toml
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Babelagent are documented here. Format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/); this project uses semantic versioning.
|
|
5
|
+
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
### Security
|
|
9
|
+
- Completed the full security cadence over the REST / MCP / A2A / HTTP surface: a 3-surface audit and
|
|
10
|
+
**four adversarial red-team rounds** (final round found nothing new). Fixed and regression-pinned:
|
|
11
|
+
attacker-controlled `deadline_s` (DoS), SSRF (allowlist + NAT64/6to4/Teredo normalization, no
|
|
12
|
+
redirects, response caps, connect-time re-guard), keyword/positional argument injection from
|
|
13
|
+
untrusted upstream output, `_safe` depth+width DoS, trace exception-message leakage, REST DNS-rebind
|
|
14
|
+
(Host pinning), slowloris (body + connection limits), and a hard wall-clock guillotine defeating
|
|
15
|
+
cancellation-swallowing agents. See `docs/SECURITY.md` for the posture, deployment guidance, and the
|
|
16
|
+
documented residuals. 22 security regression tests in `tests/security/`.
|
|
17
|
+
|
|
18
|
+
### Renamed
|
|
19
|
+
- Project renamed from the working title **BYOA** to **Babelagent** (import + distribution name
|
|
20
|
+
`babelagent`). The framing is now a neutral communication layer that lets heterogeneous agents talk
|
|
21
|
+
to each other (agent-to-agent), not a factory/assembly line.
|
|
22
|
+
- API vocabulary re-themed: `Factory` → `Graph`, `Line` → `CompiledGraph`, `Blueprint` → `Topology`,
|
|
23
|
+
`Station` → `Node` (merged with the old graph-node wrapper), `Part` → `Message`, `Product` →
|
|
24
|
+
`Result`, check outcome `Result` → `Grade`; `.station()` → `.node()`.
|
|
25
|
+
|
|
26
|
+
### Added — core graph engine
|
|
27
|
+
- `Graph` builder with **linear chaining** (`.node(...).node(...)`) that compiles to a **DAG core**
|
|
28
|
+
supporting `after=` dependencies, fan-out, parallel branches, and fan-in `join`.
|
|
29
|
+
- Barrier policies for fan-in: `all`, `k_of_n` (with `k`), and `optional`.
|
|
30
|
+
- Async scheduler with bounded concurrency, per-node + per-run timeouts, reactive readiness, and
|
|
31
|
+
failure propagation (unsatisfiable barriers skip downstream nodes).
|
|
32
|
+
- Native, dependency-free quality types: `Verdict` (pass/warn/fail), `Grade`, `Check`, and `GateMode`
|
|
33
|
+
(off/warn/strict) for optional per-node gating.
|
|
34
|
+
- `Message` / `Result` envelopes; `Topology` validation (cycle + dangling-dep detection).
|
|
35
|
+
|
|
36
|
+
### Added — adapters + `adapt()`
|
|
37
|
+
- **`adapt()`** on-the-fly adapter creator: normalizes callables, HTTP/OpenAPI endpoints, MCP tools,
|
|
38
|
+
framework agents (LangChain / CrewAI / AutoGen), and LLM providers into the uniform `Agent` interface.
|
|
39
|
+
- `CallableAgent`, `HttpAgent` (+ `from_openapi`, basic SSRF guard), `LLM` provider adapter (lazy,
|
|
40
|
+
behind extras, defaults to the latest Claude model).
|
|
41
|
+
- Extensible registry: `register_adapter(...)` and the `babelagent.adapters` entry-point group.
|
|
42
|
+
|
|
43
|
+
### Added — A2A adapter
|
|
44
|
+
- **`A2AAgent` / `A2ARef`**: consume a remote Agent2Agent (A2A) agent as a node via the
|
|
45
|
+
`message/send` JSON-RPC method, with Agent Card discovery. Built on httpx (base wheel, no extra),
|
|
46
|
+
SSRF-guarded. `adapt(A2ARef(url))` wires a remote agent into the graph. This is the "consume the
|
|
47
|
+
protocols, don't compete with them" position: a remote A2A agent becomes just another `Agent`.
|
|
48
|
+
|
|
49
|
+
### Added — interfaces
|
|
50
|
+
- `babelagent` CLI (Typer): `demo`, `doctor`, `inspect`, `serve`, `mcp`, `version`.
|
|
51
|
+
- **REST service** (`babelagent.io.rest`, `serve` extra): serve a graph over HTTP. Hardened from
|
|
52
|
+
birth — constant-time bearer auth, zero-config on loopback but fail-closed (refuses to bind a
|
|
53
|
+
non-loopback host without a token), request-body size cap enforced before buffering, a concurrency
|
|
54
|
+
semaphore, deadline-bounded runs, and sanitized errors. Endpoints: `GET /health`, `GET /graph`,
|
|
55
|
+
`POST /run`.
|
|
56
|
+
- **MCP server** (`babelagent.io.mcp`, `mcp` extra): expose a graph as MCP tools (`run_graph`,
|
|
57
|
+
`graph_topology`) so any MCP client can run a whole graph. Works across MCP SDK 2.x (`MCPServer`)
|
|
58
|
+
and 1.x (`FastMCP`).
|
|
59
|
+
- Key-free `babelagent demo`: a broken agent gated to FAIL, then a fixed one producing a PASS result.
|
|
60
|
+
|
|
61
|
+
[Unreleased]: https://example.invalid/babelagent/compare
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
title: Babelagent
|
|
3
|
+
message: "If you use Babelagent, please cite it as below."
|
|
4
|
+
type: software
|
|
5
|
+
authors:
|
|
6
|
+
- family-names: Patole
|
|
7
|
+
given-names: Amit
|
|
8
|
+
email: amit.patole@gmail.com
|
|
9
|
+
repository-code: "https://github.com/amitpatole/babelagent"
|
|
10
|
+
url: "https://amitpatole.github.io/babelagent/"
|
|
11
|
+
abstract: >-
|
|
12
|
+
The Babel that lets AI agents understand each other: a neutral, framework-agnostic
|
|
13
|
+
layer where heterogeneous agents (callables, HTTP/OpenAPI, MCP, framework agents,
|
|
14
|
+
LLMs) share one interface, collaborate agent-to-agent, and are graded at each hop.
|
|
15
|
+
keywords:
|
|
16
|
+
- ai
|
|
17
|
+
- agents
|
|
18
|
+
- a2a
|
|
19
|
+
- interoperability
|
|
20
|
+
- multi-agent
|
|
21
|
+
license: MIT
|
|
22
|
+
version: 0.1.0
|
babelagent-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Amit Patole
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: babelagent
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The Babel that lets AI agents understand each other: a neutral layer where heterogeneous agents (callables, HTTP/OpenAPI, MCP, framework agents, LLMs) share one interface, collaborate (A2A), and get graded at each hop.
|
|
5
|
+
Author-email: Amit Patole <amit.patole@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: a2a,adapters,agents,ai,dag,interoperability,multi-agent,orchestration
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Requires-Dist: httpx>=0.27
|
|
18
|
+
Requires-Dist: platformdirs>=4.0
|
|
19
|
+
Requires-Dist: pydantic-settings>=2.2
|
|
20
|
+
Requires-Dist: pydantic>=2.6
|
|
21
|
+
Requires-Dist: typer>=0.12
|
|
22
|
+
Provides-Extra: agentsensory
|
|
23
|
+
Requires-Dist: agentsensory>=0.1; extra == 'agentsensory'
|
|
24
|
+
Provides-Extra: all
|
|
25
|
+
Requires-Dist: anthropic>=0.40; extra == 'all'
|
|
26
|
+
Requires-Dist: fastapi>=0.110; extra == 'all'
|
|
27
|
+
Requires-Dist: langchain-core>=0.3; extra == 'all'
|
|
28
|
+
Requires-Dist: mcp>=1.2; extra == 'all'
|
|
29
|
+
Requires-Dist: ollama>=0.3; extra == 'all'
|
|
30
|
+
Requires-Dist: openai>=1.0; extra == 'all'
|
|
31
|
+
Requires-Dist: python-multipart>=0.0.9; extra == 'all'
|
|
32
|
+
Requires-Dist: uvicorn[standard]>=0.29; extra == 'all'
|
|
33
|
+
Provides-Extra: cloud
|
|
34
|
+
Requires-Dist: anthropic>=0.40; extra == 'cloud'
|
|
35
|
+
Requires-Dist: openai>=1.0; extra == 'cloud'
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
39
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
41
|
+
Provides-Extra: frameworks
|
|
42
|
+
Requires-Dist: langchain-core>=0.3; extra == 'frameworks'
|
|
43
|
+
Provides-Extra: mcp
|
|
44
|
+
Requires-Dist: mcp>=1.2; extra == 'mcp'
|
|
45
|
+
Provides-Extra: ollama
|
|
46
|
+
Requires-Dist: ollama>=0.3; extra == 'ollama'
|
|
47
|
+
Provides-Extra: serve
|
|
48
|
+
Requires-Dist: fastapi>=0.110; extra == 'serve'
|
|
49
|
+
Requires-Dist: python-multipart>=0.0.9; extra == 'serve'
|
|
50
|
+
Requires-Dist: uvicorn[standard]>=0.29; extra == 'serve'
|
|
51
|
+
Description-Content-Type: text/markdown
|
|
52
|
+
|
|
53
|
+
# Babelagent
|
|
54
|
+
|
|
55
|
+
> The Babel that lets AI agents understand each other. One shared tongue for agents that were never meant to talk.
|
|
56
|
+
|
|
57
|
+
Every agent framework has its own idea of what an agent is. LangChain has Runnables with `.invoke`,
|
|
58
|
+
CrewAI has Crews with `.kickoff`, AutoGen has agents with `.generate_reply`, an LLM has
|
|
59
|
+
`messages.create`, a microservice has an HTTP route. None of them agree on a shape, so connecting any
|
|
60
|
+
two means writing glue.
|
|
61
|
+
|
|
62
|
+
Babelagent is the neutral layer in between. You bring your own agents, whatever they are (a callable,
|
|
63
|
+
an HTTP/OpenAPI endpoint, an MCP tool, a framework agent, or an LLM), and it wraps each one in a single
|
|
64
|
+
shared interface so they can exchange messages and collaborate on a task, **agent-to-agent (A2A)**.
|
|
65
|
+
The value is not any single adapter. It is that once something is adapted, it works with everything
|
|
66
|
+
else you have adapted.
|
|
67
|
+
|
|
68
|
+
The headline is **`adapt()`**: hand Babelagent almost anything and it builds the connector on the fly.
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
import asyncio
|
|
72
|
+
from babelagent import Graph
|
|
73
|
+
|
|
74
|
+
async def main():
|
|
75
|
+
graph = (
|
|
76
|
+
Graph()
|
|
77
|
+
.node("clean", str.strip)
|
|
78
|
+
.node("shout", str.upper)
|
|
79
|
+
.node("exclaim", lambda s: s + "!")
|
|
80
|
+
)
|
|
81
|
+
result = await graph.run(" hello ")
|
|
82
|
+
print(result.output) # "HELLO!"
|
|
83
|
+
|
|
84
|
+
asyncio.run(main())
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Let different agents work in parallel and hand their results to each other, with barrier policies on
|
|
88
|
+
the join:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
g = Graph()
|
|
92
|
+
g.node("src", lambda text: text)
|
|
93
|
+
g.node("summary", adapt(langchain_agent), after=["src"]) # a LangChain agent
|
|
94
|
+
g.node("labels", adapt("https://api.example.com/classify"), after=["src"]) # an HTTP service
|
|
95
|
+
g.join("merge", after=["summary", "labels"], agent=combine, barrier="all")
|
|
96
|
+
result = await g.run(document)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Concepts
|
|
100
|
+
|
|
101
|
+
| Concept | What it is |
|
|
102
|
+
|---|---|
|
|
103
|
+
| **Graph** | Builds the network of agents (linear chaining **and** a DAG API) |
|
|
104
|
+
| **CompiledGraph** | The validated, runnable graph; `await graph.run(payload)` |
|
|
105
|
+
| **Node** | One participant: an agent plus an optional quality check/gate |
|
|
106
|
+
| **Agent** | The uniform `async run(message, ctx) -> message` interface |
|
|
107
|
+
| **`adapt()`** | Turns any brought object into an Agent, inferring the adapter |
|
|
108
|
+
| **Message / Result** | The envelope agents exchange / the final output + run trace |
|
|
109
|
+
| **Barrier** | Fan-in join policy: `all` · `k_of_n` · `optional` |
|
|
110
|
+
|
|
111
|
+
## Install
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
pip install babelagent # light base (callables + HTTP)
|
|
115
|
+
pip install "babelagent[mcp]" # MCP tools
|
|
116
|
+
pip install "babelagent[cloud]" # Anthropic / OpenAI
|
|
117
|
+
pip install "babelagent[ollama]" # local models
|
|
118
|
+
pip install "babelagent[frameworks]" # LangChain / CrewAI / AutoGen
|
|
119
|
+
pip install "babelagent[serve]" # REST service
|
|
120
|
+
pip install "babelagent[all]"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Try it (no API key)
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
babelagent demo # a broken agent (gated FAIL) then a fixed one (PASS)
|
|
127
|
+
babelagent doctor # which adapter families are available
|
|
128
|
+
babelagent inspect # print a graph's topology as JSON
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Status
|
|
132
|
+
|
|
133
|
+
Alpha, in active development. Local-only for now. MIT licensed.
|
|
134
|
+
|
|
135
|
+
— amitpatole
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Babelagent
|
|
2
|
+
|
|
3
|
+
> The Babel that lets AI agents understand each other. One shared tongue for agents that were never meant to talk.
|
|
4
|
+
|
|
5
|
+
Every agent framework has its own idea of what an agent is. LangChain has Runnables with `.invoke`,
|
|
6
|
+
CrewAI has Crews with `.kickoff`, AutoGen has agents with `.generate_reply`, an LLM has
|
|
7
|
+
`messages.create`, a microservice has an HTTP route. None of them agree on a shape, so connecting any
|
|
8
|
+
two means writing glue.
|
|
9
|
+
|
|
10
|
+
Babelagent is the neutral layer in between. You bring your own agents, whatever they are (a callable,
|
|
11
|
+
an HTTP/OpenAPI endpoint, an MCP tool, a framework agent, or an LLM), and it wraps each one in a single
|
|
12
|
+
shared interface so they can exchange messages and collaborate on a task, **agent-to-agent (A2A)**.
|
|
13
|
+
The value is not any single adapter. It is that once something is adapted, it works with everything
|
|
14
|
+
else you have adapted.
|
|
15
|
+
|
|
16
|
+
The headline is **`adapt()`**: hand Babelagent almost anything and it builds the connector on the fly.
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
import asyncio
|
|
20
|
+
from babelagent import Graph
|
|
21
|
+
|
|
22
|
+
async def main():
|
|
23
|
+
graph = (
|
|
24
|
+
Graph()
|
|
25
|
+
.node("clean", str.strip)
|
|
26
|
+
.node("shout", str.upper)
|
|
27
|
+
.node("exclaim", lambda s: s + "!")
|
|
28
|
+
)
|
|
29
|
+
result = await graph.run(" hello ")
|
|
30
|
+
print(result.output) # "HELLO!"
|
|
31
|
+
|
|
32
|
+
asyncio.run(main())
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Let different agents work in parallel and hand their results to each other, with barrier policies on
|
|
36
|
+
the join:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
g = Graph()
|
|
40
|
+
g.node("src", lambda text: text)
|
|
41
|
+
g.node("summary", adapt(langchain_agent), after=["src"]) # a LangChain agent
|
|
42
|
+
g.node("labels", adapt("https://api.example.com/classify"), after=["src"]) # an HTTP service
|
|
43
|
+
g.join("merge", after=["summary", "labels"], agent=combine, barrier="all")
|
|
44
|
+
result = await g.run(document)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Concepts
|
|
48
|
+
|
|
49
|
+
| Concept | What it is |
|
|
50
|
+
|---|---|
|
|
51
|
+
| **Graph** | Builds the network of agents (linear chaining **and** a DAG API) |
|
|
52
|
+
| **CompiledGraph** | The validated, runnable graph; `await graph.run(payload)` |
|
|
53
|
+
| **Node** | One participant: an agent plus an optional quality check/gate |
|
|
54
|
+
| **Agent** | The uniform `async run(message, ctx) -> message` interface |
|
|
55
|
+
| **`adapt()`** | Turns any brought object into an Agent, inferring the adapter |
|
|
56
|
+
| **Message / Result** | The envelope agents exchange / the final output + run trace |
|
|
57
|
+
| **Barrier** | Fan-in join policy: `all` · `k_of_n` · `optional` |
|
|
58
|
+
|
|
59
|
+
## Install
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install babelagent # light base (callables + HTTP)
|
|
63
|
+
pip install "babelagent[mcp]" # MCP tools
|
|
64
|
+
pip install "babelagent[cloud]" # Anthropic / OpenAI
|
|
65
|
+
pip install "babelagent[ollama]" # local models
|
|
66
|
+
pip install "babelagent[frameworks]" # LangChain / CrewAI / AutoGen
|
|
67
|
+
pip install "babelagent[serve]" # REST service
|
|
68
|
+
pip install "babelagent[all]"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Try it (no API key)
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
babelagent demo # a broken agent (gated FAIL) then a fixed one (PASS)
|
|
75
|
+
babelagent doctor # which adapter families are available
|
|
76
|
+
babelagent inspect # print a graph's topology as JSON
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Status
|
|
80
|
+
|
|
81
|
+
Alpha, in active development. Local-only for now. MIT licensed.
|
|
82
|
+
|
|
83
|
+
— amitpatole
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
Trio Design System — "Warm Paper" · MkDocs Material override
|
|
3
|
+
Light-first, printed-document calm. No gradients, no glows. One accent.
|
|
4
|
+
CANONICAL FILE — copied into each repo's docs/stylesheets/extra.css.
|
|
5
|
+
Per-repo accent: Babelagent (bridge) desaturated teal.
|
|
6
|
+
============================================================================ */
|
|
7
|
+
@import url('https://fonts.googleapis.com/css2?family=Source+Serif+4:opsz,wght@8..60,400;8..60,500;8..60,600;8..60,700&display=swap');
|
|
8
|
+
|
|
9
|
+
:root {
|
|
10
|
+
/* ---- Warm Paper tokens ---- */
|
|
11
|
+
--wp-bg: #f7f5f1;
|
|
12
|
+
--wp-surface: #fffdf9;
|
|
13
|
+
--wp-panel: #efece4;
|
|
14
|
+
--wp-ink: #1b1a17;
|
|
15
|
+
--wp-ink-muted: #6b6862;
|
|
16
|
+
--wp-ink-faint: #918d84;
|
|
17
|
+
--wp-rule: #e3ded4;
|
|
18
|
+
|
|
19
|
+
/* ---- Accent — Babelagent (bridge) ---- */
|
|
20
|
+
--wp-accent: #3f8a86;
|
|
21
|
+
--wp-accent-ink: #2a615d;
|
|
22
|
+
|
|
23
|
+
--wp-serif: "Source Serif 4", Georgia, "Times New Roman", serif;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* ---- Map tokens onto Material's light scheme ---- */
|
|
27
|
+
[data-md-color-scheme="default"] {
|
|
28
|
+
--md-default-bg-color: var(--wp-bg);
|
|
29
|
+
--md-default-fg-color: var(--wp-ink);
|
|
30
|
+
--md-default-fg-color--light: var(--wp-ink-muted);
|
|
31
|
+
--md-default-fg-color--lighter: var(--wp-ink-faint);
|
|
32
|
+
--md-default-fg-color--lightest: var(--wp-rule);
|
|
33
|
+
|
|
34
|
+
--md-primary-fg-color: var(--wp-bg); /* header = paper, not a color bar */
|
|
35
|
+
--md-primary-fg-color--light: var(--wp-bg);
|
|
36
|
+
--md-primary-fg-color--dark: var(--wp-bg);
|
|
37
|
+
--md-primary-bg-color: var(--wp-ink); /* header text = ink */
|
|
38
|
+
--md-primary-bg-color--light: var(--wp-ink-muted);
|
|
39
|
+
|
|
40
|
+
--md-accent-fg-color: var(--wp-accent-ink);
|
|
41
|
+
--md-accent-fg-color--transparent: color-mix(in srgb, var(--wp-accent) 12%, transparent);
|
|
42
|
+
|
|
43
|
+
--md-typeset-color: var(--wp-ink);
|
|
44
|
+
--md-typeset-a-color: var(--wp-accent-ink);
|
|
45
|
+
|
|
46
|
+
--md-code-bg-color: var(--wp-panel);
|
|
47
|
+
--md-code-fg-color: var(--wp-ink);
|
|
48
|
+
--md-footer-bg-color: var(--wp-surface);
|
|
49
|
+
--md-footer-bg-color--dark: var(--wp-surface);
|
|
50
|
+
--md-footer-fg-color: var(--wp-ink-muted);
|
|
51
|
+
--md-footer-fg-color--light: var(--wp-ink-faint);
|
|
52
|
+
|
|
53
|
+
color-scheme: light;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* ---- Header: paper with a hairline, no heavy bar ---- */
|
|
57
|
+
.md-header {
|
|
58
|
+
color: var(--wp-ink);
|
|
59
|
+
border-bottom: 1px solid var(--wp-rule);
|
|
60
|
+
box-shadow: none;
|
|
61
|
+
}
|
|
62
|
+
.md-header--shadow { box-shadow: none; }
|
|
63
|
+
.md-tabs {
|
|
64
|
+
background: var(--wp-bg);
|
|
65
|
+
color: var(--wp-ink);
|
|
66
|
+
border-bottom: 1px solid var(--wp-rule);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* ---- Serif display for headings; tight, never heavy ---- */
|
|
70
|
+
.md-typeset h1, .md-typeset h2, .md-typeset h3,
|
|
71
|
+
.md-content__inner > h1, .md-nav__title {
|
|
72
|
+
font-family: var(--wp-serif);
|
|
73
|
+
letter-spacing: -.01em;
|
|
74
|
+
font-weight: 600;
|
|
75
|
+
color: var(--wp-ink);
|
|
76
|
+
}
|
|
77
|
+
.md-typeset h1 { font-weight: 600; }
|
|
78
|
+
|
|
79
|
+
/* The page H1 gets a quiet accent hairline underneath — the single accent gesture */
|
|
80
|
+
.md-typeset h1 {
|
|
81
|
+
padding-bottom: .35rem;
|
|
82
|
+
border-bottom: 1px solid var(--wp-rule);
|
|
83
|
+
position: relative;
|
|
84
|
+
}
|
|
85
|
+
.md-typeset h1::after {
|
|
86
|
+
content: ""; position: absolute; left: 0; bottom: -1px;
|
|
87
|
+
width: 2.2rem; height: 1px; background: var(--wp-accent);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* ---- Links: accent-ink, underline on hover only ---- */
|
|
91
|
+
.md-typeset a { color: var(--wp-accent-ink); text-decoration: none; }
|
|
92
|
+
.md-typeset a:hover { text-decoration: underline; text-underline-offset: 3px; }
|
|
93
|
+
|
|
94
|
+
/* ---- Code: quiet panel, hairline, no neon ---- */
|
|
95
|
+
.md-typeset pre > code,
|
|
96
|
+
.md-typeset .highlight { border-radius: 8px; }
|
|
97
|
+
.md-typeset pre { border: 1px solid var(--wp-rule); border-radius: 8px; }
|
|
98
|
+
.md-typeset code { background: var(--wp-panel); }
|
|
99
|
+
|
|
100
|
+
/* ---- Tables & admonitions: hairlines, soft fills ---- */
|
|
101
|
+
.md-typeset table:not([class]) {
|
|
102
|
+
border: 1px solid var(--wp-rule); border-radius: 8px; overflow: hidden;
|
|
103
|
+
}
|
|
104
|
+
.md-typeset table:not([class]) th {
|
|
105
|
+
background: var(--wp-panel); color: var(--wp-ink); font-weight: 600;
|
|
106
|
+
}
|
|
107
|
+
.md-typeset .admonition, .md-typeset details {
|
|
108
|
+
border: 1px solid var(--wp-rule); border-left-width: 2px;
|
|
109
|
+
border-left-color: var(--wp-accent); box-shadow: none; background: var(--wp-surface);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* ---- Buttons / primary CTA ---- */
|
|
113
|
+
.md-typeset .md-button--primary {
|
|
114
|
+
background: var(--wp-accent-ink); border-color: var(--wp-accent-ink); color: var(--wp-surface);
|
|
115
|
+
}
|
|
116
|
+
.md-typeset .md-button--primary:hover {
|
|
117
|
+
background: var(--wp-ink); border-color: var(--wp-ink);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* ---- Search ---- */
|
|
121
|
+
.md-search__form { background: var(--wp-surface); border: 1px solid var(--wp-rule); }
|
|
122
|
+
.md-search__input::placeholder { color: var(--wp-ink-faint); }
|
|
123
|
+
|
|
124
|
+
/* ---- Maker's mark: quiet "— amitpatole" signature in the footer ---- */
|
|
125
|
+
.md-footer-meta { border-top: 1px solid var(--wp-rule); }
|
|
126
|
+
.md-footer-meta__inner::after {
|
|
127
|
+
content: "— amitpatole";
|
|
128
|
+
font-family: var(--wp-serif); font-style: italic; font-size: .8rem;
|
|
129
|
+
color: var(--wp-accent-ink); margin-left: auto; padding: 0 .6rem;
|
|
130
|
+
align-self: center; white-space: nowrap;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* ---- Tighten the overall feel ---- */
|
|
134
|
+
.md-grid { max-width: 64rem; } /* comfortable measure */
|
|
135
|
+
.md-typeset { line-height: 1.62; }
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Security posture
|
|
2
|
+
|
|
3
|
+
Babelagent runs code and calls services you bring, and its REST/MCP interfaces accept untrusted input,
|
|
4
|
+
so security is treated as part of "done." The current surface has been through a full audit and four
|
|
5
|
+
adversarial red-team rounds (the last found nothing new). This page states what is hardened, how to
|
|
6
|
+
deploy safely, and the residual risks no audit removes.
|
|
7
|
+
|
|
8
|
+
## What is hardened
|
|
9
|
+
|
|
10
|
+
**Network input (REST service, `babelagent[serve]`).**
|
|
11
|
+
- Bearer-token auth compared in constant time (`hmac.compare_digest`). Zero-config on loopback;
|
|
12
|
+
**fail-closed** on any non-loopback bind without a token (`serve()` refuses to start).
|
|
13
|
+
- Anti-DNS-rebind **Host-header pinning** on loopback binds (a browser cannot rebind `attacker.com`
|
|
14
|
+
to `127.0.0.1` and drive `/run`). Fails closed on a missing Host.
|
|
15
|
+
- Request-body size cap enforced **before** buffering; a body-phase idle/slowloris timeout; a
|
|
16
|
+
concurrency semaphore; `limit_concurrency` + `timeout_keep_alive` on uvicorn.
|
|
17
|
+
- Caller-supplied `deadline_s` is validated (finite, positive) and **clamped** to the server ceiling
|
|
18
|
+
(it can only lower the run budget, never raise it), backed by a hard wall-clock guillotine on the run.
|
|
19
|
+
- Errors are sanitized: internal exception messages are redacted from the returned trace.
|
|
20
|
+
|
|
21
|
+
**Outbound requests (HTTP / OpenAPI / A2A adapters).**
|
|
22
|
+
- SSRF allowlist: only public global-unicast targets. Blocks loopback/private/link-local/reserved/
|
|
23
|
+
multicast/unspecified, and normalizes IPv4-mapped, IPv4-compatible, NAT64, 6to4, and Teredo IPv6
|
|
24
|
+
so an internal IPv4 cannot be smuggled through an IPv6 literal.
|
|
25
|
+
- Redirects are never followed; the guard is re-run at connect time; upstream responses are streamed
|
|
26
|
+
with a size cap so a hostile agent cannot exhaust memory.
|
|
27
|
+
- Secrets: API keys and tokens come only from the environment; never hard-coded, never serialized into
|
|
28
|
+
a topology or returned in a response.
|
|
29
|
+
|
|
30
|
+
**Execution / resources.**
|
|
31
|
+
- No `eval`/`exec`/`pickle`/`shell=True` on untrusted input. The OpenAPI path only reads a schema.
|
|
32
|
+
- The scheduler bounds concurrency; response serialization is bounded by depth **and** a total-node
|
|
33
|
+
budget (so a wide or shared-reference structure cannot fan out to exponential work).
|
|
34
|
+
- Untrusted upstream output cannot inject keyword/positional "flag" arguments into a callable node
|
|
35
|
+
(payload spreads into a callable only when it exactly matches the callable's required parameters).
|
|
36
|
+
- Entry-point adapter discovery can be disabled with `BABELAGENT_NO_PLUGINS=1`.
|
|
37
|
+
|
|
38
|
+
## Deploying safely
|
|
39
|
+
|
|
40
|
+
- **Front a routable deployment with a reverse proxy** (nginx `client_header_timeout`, an ALB idle
|
|
41
|
+
timeout, etc.). uvicorn has no header-receive timeout, so a slow-headers Slowloris is only fully
|
|
42
|
+
mitigated by a fronting proxy. Always set a token (`BABELAGENT_API_TOKEN`) for non-loopback binds.
|
|
43
|
+
- **Front untrusted outbound targets with an egress allowlist / proxy.** The SSRF guard blocks internal
|
|
44
|
+
targets, but a host under attacker DNS control can still rebind between validation and connect; an
|
|
45
|
+
egress allowlist closes that residual.
|
|
46
|
+
- **Do not run untrusted, blocking, or CPU-bound *sync* callables on a shared server.** A blocked
|
|
47
|
+
worker thread cannot be force-killed (a Python limitation); use an async agent or an out-of-process
|
|
48
|
+
agent for such work.
|
|
49
|
+
|
|
50
|
+
## Documented residuals (not eliminated by any audit)
|
|
51
|
+
|
|
52
|
+
- A blocked/CPU-bound **sync** callable's worker thread cannot be interrupted; the run still reports the
|
|
53
|
+
timeout, but the thread lingers.
|
|
54
|
+
- **DNS-rebinding** of an outbound target is not fully closed without an egress allowlist/proxy.
|
|
55
|
+
- **Header-phase Slowloris** requires a fronting reverse proxy.
|
|
56
|
+
- **Brought agents and entry-point plugins run code** by design; trust your dependency set and the
|
|
57
|
+
agents you plug in. LLM provider SDKs dial trusted first-party endpoints.
|
|
58
|
+
- The usual unknowns: third-party dependencies and the kernel/OS.
|
|
59
|
+
|
|
60
|
+
## Reporting
|
|
61
|
+
|
|
62
|
+
This project is in local development. Once public, report vulnerabilities privately to the maintainer.
|