theagentrouter-core 0.1.0a1__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.
- theagentrouter_core-0.1.0a1/.github/ISSUE_TEMPLATE/bug_report.yml +38 -0
- theagentrouter_core-0.1.0a1/.github/ISSUE_TEMPLATE/config.yml +5 -0
- theagentrouter_core-0.1.0a1/.github/ISSUE_TEMPLATE/feature_request.yml +15 -0
- theagentrouter_core-0.1.0a1/.github/dependabot.yml +10 -0
- theagentrouter_core-0.1.0a1/.github/pull_request_template.md +8 -0
- theagentrouter_core-0.1.0a1/.github/workflows/ci.yml +74 -0
- theagentrouter_core-0.1.0a1/.github/workflows/local-gateway-build.yml +77 -0
- theagentrouter_core-0.1.0a1/.github/workflows/release.yml +96 -0
- theagentrouter_core-0.1.0a1/.gitignore +15 -0
- theagentrouter_core-0.1.0a1/.pre-commit-config.yaml +7 -0
- theagentrouter_core-0.1.0a1/AGENTS.md +58 -0
- theagentrouter_core-0.1.0a1/CHANGELOG.md +85 -0
- theagentrouter_core-0.1.0a1/CLAUDE.md +11 -0
- theagentrouter_core-0.1.0a1/CODE_OF_CONDUCT.md +128 -0
- theagentrouter_core-0.1.0a1/CONTRIBUTING.md +28 -0
- theagentrouter_core-0.1.0a1/DEVELOPMENT.md +177 -0
- theagentrouter_core-0.1.0a1/LICENSE +202 -0
- theagentrouter_core-0.1.0a1/Makefile +35 -0
- theagentrouter_core-0.1.0a1/PKG-INFO +201 -0
- theagentrouter_core-0.1.0a1/README.md +164 -0
- theagentrouter_core-0.1.0a1/SECURITY.md +21 -0
- theagentrouter_core-0.1.0a1/docs/README.md +13 -0
- theagentrouter_core-0.1.0a1/docs/coming-from-litellm.md +90 -0
- theagentrouter_core-0.1.0a1/docs/frameworks.md +99 -0
- theagentrouter_core-0.1.0a1/docs/images/how-it-works.svg +53 -0
- theagentrouter_core-0.1.0a1/docs/install.md +108 -0
- theagentrouter_core-0.1.0a1/docs/local-gateway.md +113 -0
- theagentrouter_core-0.1.0a1/docs/usage.md +313 -0
- theagentrouter_core-0.1.0a1/examples/00_two_liner.py +12 -0
- theagentrouter_core-0.1.0a1/examples/01_hello_world.py +10 -0
- theagentrouter_core-0.1.0a1/examples/02_multi_provider.py +22 -0
- theagentrouter_core-0.1.0a1/examples/03_streaming.py +13 -0
- theagentrouter_core-0.1.0a1/examples/04_tool_use.py +33 -0
- theagentrouter_core-0.1.0a1/examples/05_async.py +24 -0
- theagentrouter_core-0.1.0a1/examples/06_structured_output.py +21 -0
- theagentrouter_core-0.1.0a1/examples/07_fallback.py +21 -0
- theagentrouter_core-0.1.0a1/examples/08_retry.py +16 -0
- theagentrouter_core-0.1.0a1/examples/09_model_alias.py +18 -0
- theagentrouter_core-0.1.0a1/examples/10_share_traffic.py +19 -0
- theagentrouter_core-0.1.0a1/examples/11_from_litellm.py +52 -0
- theagentrouter_core-0.1.0a1/examples/12_litellm_router.py +20 -0
- theagentrouter_core-0.1.0a1/examples/13_handling_errors.py +20 -0
- theagentrouter_core-0.1.0a1/examples/14_cost.py +17 -0
- theagentrouter_core-0.1.0a1/examples/15_through_a_gateway.py +23 -0
- theagentrouter_core-0.1.0a1/examples/16_adk_agent.py +43 -0
- theagentrouter_core-0.1.0a1/examples/17_openai_agents.py +34 -0
- theagentrouter_core-0.1.0a1/examples/18_local_gateway.py +21 -0
- theagentrouter_core-0.1.0a1/examples/19_embeddings.py +22 -0
- theagentrouter_core-0.1.0a1/examples/20_request_limit.py +20 -0
- theagentrouter_core-0.1.0a1/examples/README.md +31 -0
- theagentrouter_core-0.1.0a1/llms-full.txt +1726 -0
- theagentrouter_core-0.1.0a1/llms.txt +47 -0
- theagentrouter_core-0.1.0a1/local-gateway/LICENSE +202 -0
- theagentrouter_core-0.1.0a1/local-gateway/README.md +18 -0
- theagentrouter_core-0.1.0a1/local-gateway/aigw-release.json +9 -0
- theagentrouter_core-0.1.0a1/local-gateway/hatch_build.py +74 -0
- theagentrouter_core-0.1.0a1/local-gateway/pyproject.toml +23 -0
- theagentrouter_core-0.1.0a1/local-gateway/src/theagentrouter_local_gateway/__init__.py +33 -0
- theagentrouter_core-0.1.0a1/packages/theagentrouter/LICENSE +202 -0
- theagentrouter_core-0.1.0a1/packages/theagentrouter/README.md +13 -0
- theagentrouter_core-0.1.0a1/packages/theagentrouter/pyproject.toml +41 -0
- theagentrouter_core-0.1.0a1/pyproject.toml +76 -0
- theagentrouter_core-0.1.0a1/scripts/build_llms.py +103 -0
- theagentrouter_core-0.1.0a1/scripts/check_release.py +61 -0
- theagentrouter_core-0.1.0a1/scripts/check_test_report.py +14 -0
- theagentrouter_core-0.1.0a1/scripts/fetch_aigw.py +44 -0
- theagentrouter_core-0.1.0a1/scripts/smoke_adk_real.py +85 -0
- theagentrouter_core-0.1.0a1/scripts/smoke_agents_real.py +83 -0
- theagentrouter_core-0.1.0a1/scripts/smoke_local_gateway.py +116 -0
- theagentrouter_core-0.1.0a1/scripts/smoke_real.py +102 -0
- theagentrouter_core-0.1.0a1/scripts/smoke_routes.py +114 -0
- theagentrouter_core-0.1.0a1/scripts/surface.py +83 -0
- theagentrouter_core-0.1.0a1/scripts/sync_models.py +78 -0
- theagentrouter_core-0.1.0a1/skills/migrate-from-litellm/SKILL.md +96 -0
- theagentrouter_core-0.1.0a1/skills/theagentrouter/SKILL.md +72 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/__init__.py +100 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/anthropic_format.py +397 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/compat/__init__.py +17 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/compat/_callbacks.py +166 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/compat/litellm.py +297 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/embeddings.py +124 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/exceptions.py +155 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/local_gateway.py +774 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/model_catalog.json +3530 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/models.py +138 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/openai_format.py +208 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/py.typed +0 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/responses.py +171 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/routes.py +250 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/routing.py +270 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/tokens.py +326 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/transport.py +263 -0
- theagentrouter_core-0.1.0a1/src/theagentrouter/types.py +214 -0
- theagentrouter_core-0.1.0a1/surface.txt +121 -0
- theagentrouter_core-0.1.0a1/tests/__init__.py +0 -0
- theagentrouter_core-0.1.0a1/tests/conftest.py +3 -0
- theagentrouter_core-0.1.0a1/tests/fake_provider.py +423 -0
- theagentrouter_core-0.1.0a1/tests/test_adk.py +141 -0
- theagentrouter_core-0.1.0a1/tests/test_embeddings.py +90 -0
- theagentrouter_core-0.1.0a1/tests/test_llms.py +24 -0
- theagentrouter_core-0.1.0a1/tests/test_local_gateway.py +442 -0
- theagentrouter_core-0.1.0a1/tests/test_minimum.py +237 -0
- theagentrouter_core-0.1.0a1/tests/test_openai_agents.py +75 -0
- theagentrouter_core-0.1.0a1/tests/test_release_regressions.py +409 -0
- theagentrouter_core-0.1.0a1/tests/test_release_tools.py +93 -0
- theagentrouter_core-0.1.0a1/tests/test_routes.py +418 -0
- theagentrouter_core-0.1.0a1/tests/test_sdk.py +277 -0
- theagentrouter_core-0.1.0a1/tests/test_surface.py +28 -0
- theagentrouter_core-0.1.0a1/tests/test_tokens.py +142 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something does not work as described
|
|
3
|
+
labels: [bug]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: what
|
|
7
|
+
attributes:
|
|
8
|
+
label: What happened
|
|
9
|
+
description: The smallest code that shows the problem, and the full error.
|
|
10
|
+
render: python
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
- type: input
|
|
14
|
+
id: model
|
|
15
|
+
attributes:
|
|
16
|
+
label: Model name
|
|
17
|
+
placeholder: anthropic/claude-sonnet-4-5
|
|
18
|
+
validations:
|
|
19
|
+
required: true
|
|
20
|
+
- type: dropdown
|
|
21
|
+
id: route
|
|
22
|
+
attributes:
|
|
23
|
+
label: Where the call goes
|
|
24
|
+
options: [Direct to the provider, Through a gateway (AGENTROUTER_BASE_URL), api_base on the call]
|
|
25
|
+
validations:
|
|
26
|
+
required: true
|
|
27
|
+
- type: input
|
|
28
|
+
id: version
|
|
29
|
+
attributes:
|
|
30
|
+
label: theagentrouter version
|
|
31
|
+
placeholder: python -c "import theagentrouter; print(theagentrouter.__version__)"
|
|
32
|
+
validations:
|
|
33
|
+
required: true
|
|
34
|
+
- type: input
|
|
35
|
+
id: framework
|
|
36
|
+
attributes:
|
|
37
|
+
label: Agent framework, if any
|
|
38
|
+
placeholder: Google ADK 2.10.0
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Suggest a change
|
|
3
|
+
labels: [enhancement]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: need
|
|
7
|
+
attributes:
|
|
8
|
+
label: What you are trying to do
|
|
9
|
+
description: Describe the job, not the solution.
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: idea
|
|
14
|
+
attributes:
|
|
15
|
+
label: What you would like the SDK to do
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
## What this changes
|
|
2
|
+
|
|
3
|
+
## Checklist
|
|
4
|
+
|
|
5
|
+
- [ ] Tests added or updated (`pytest`)
|
|
6
|
+
- [ ] `ruff check .` and `ruff format --check .` pass
|
|
7
|
+
- [ ] If the public API changed on purpose: `python scripts/surface.py --write`, and the reason is above
|
|
8
|
+
- [ ] `CHANGELOG.md` updated under "Unreleased", if users will notice
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_call:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
strategy:
|
|
16
|
+
fail-fast: false
|
|
17
|
+
matrix:
|
|
18
|
+
python: ["3.10", "3.11", "3.12", "3.13"]
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python }}
|
|
24
|
+
- run: pip install -e ".[dev]"
|
|
25
|
+
- run: ruff check .
|
|
26
|
+
- run: ruff format --check .
|
|
27
|
+
- run: pytest
|
|
28
|
+
- run: python scripts/check_release.py
|
|
29
|
+
|
|
30
|
+
adk:
|
|
31
|
+
# A real Google ADK agent on this SDK, in an environment without LiteLLM.
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
- uses: actions/setup-python@v5
|
|
36
|
+
with:
|
|
37
|
+
python-version: "3.12"
|
|
38
|
+
- run: pip install -e ".[test]" google-adk
|
|
39
|
+
- run: python -c "import importlib.util as u, sys; sys.exit(u.find_spec('litellm') is not None)"
|
|
40
|
+
- run: pytest tests/test_adk.py --junitxml=adk-tests.xml
|
|
41
|
+
- run: python scripts/check_test_report.py adk-tests.xml
|
|
42
|
+
|
|
43
|
+
openai-agents:
|
|
44
|
+
# A real OpenAI Agents SDK agent on this SDK, in an environment without LiteLLM.
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v4
|
|
48
|
+
- uses: actions/setup-python@v5
|
|
49
|
+
with:
|
|
50
|
+
python-version: "3.12"
|
|
51
|
+
- run: pip install -e ".[test]" openai-agents
|
|
52
|
+
- run: python -c "import importlib.util as u, sys; sys.exit(u.find_spec('litellm') is not None)"
|
|
53
|
+
- run: pytest tests/test_openai_agents.py --junitxml=agents-tests.xml
|
|
54
|
+
- run: python scripts/check_test_report.py agents-tests.xml
|
|
55
|
+
|
|
56
|
+
build:
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/checkout@v4
|
|
60
|
+
- uses: actions/setup-python@v5
|
|
61
|
+
with:
|
|
62
|
+
python-version: "3.12"
|
|
63
|
+
- run: pip install build twine
|
|
64
|
+
- run: python -m build # theagentrouter-core
|
|
65
|
+
- run: python -m build packages/theagentrouter --outdir dist # theagentrouter, no code
|
|
66
|
+
- run: python -m twine check --strict dist/*
|
|
67
|
+
- uses: actions/upload-artifact@v4
|
|
68
|
+
with:
|
|
69
|
+
name: dist
|
|
70
|
+
path: dist/
|
|
71
|
+
if-no-files-found: error
|
|
72
|
+
|
|
73
|
+
local-gateway:
|
|
74
|
+
uses: ./.github/workflows/local-gateway-build.yml
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
name: Local gateway packages
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
contents: read
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
timeout-minutes: 30
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
include:
|
|
17
|
+
- os: macos-14
|
|
18
|
+
target: darwin-arm64
|
|
19
|
+
- os: ubuntu-24.04
|
|
20
|
+
target: linux-amd64
|
|
21
|
+
- os: ubuntu-24.04-arm
|
|
22
|
+
target: linux-arm64
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
- uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.12"
|
|
28
|
+
- run: pip install build twine pytest pytest-asyncio
|
|
29
|
+
- run: python scripts/check_release.py
|
|
30
|
+
- run: python scripts/fetch_aigw.py
|
|
31
|
+
- run: python -m build --wheel
|
|
32
|
+
- run: python -m build --wheel local-gateway --outdir local-dist
|
|
33
|
+
- run: python -m twine check --strict local-dist/*
|
|
34
|
+
- name: Install built packages
|
|
35
|
+
run: pip install dist/*.whl local-dist/*.whl
|
|
36
|
+
- name: PyPI companion wheel size limit
|
|
37
|
+
run: |
|
|
38
|
+
python - <<'PY'
|
|
39
|
+
from pathlib import Path
|
|
40
|
+
for wheel in Path("local-dist").glob("*.whl"):
|
|
41
|
+
assert wheel.stat().st_size <= 100 * 1024 * 1024, f"{wheel} exceeds PyPI's default file limit"
|
|
42
|
+
PY
|
|
43
|
+
- run: python -m pip check
|
|
44
|
+
# Every test that starts the real aigw: the gateway itself, routes (fallbacks, retries,
|
|
45
|
+
# request limits) and embeddings. None may skip here.
|
|
46
|
+
- run: pytest tests/test_local_gateway.py tests/test_routes.py tests/test_embeddings.py --junitxml=local-gateway-tests.xml
|
|
47
|
+
- run: python scripts/check_test_report.py local-gateway-tests.xml
|
|
48
|
+
- uses: actions/upload-artifact@v4
|
|
49
|
+
with:
|
|
50
|
+
name: dist-local-${{ matrix.target }}
|
|
51
|
+
path: local-dist/*.whl
|
|
52
|
+
compression-level: 0
|
|
53
|
+
if-no-files-found: error
|
|
54
|
+
|
|
55
|
+
# Where no platform wheel fits, pip installs this one, so `pip install theagentrouter` never fails.
|
|
56
|
+
fallback:
|
|
57
|
+
runs-on: ubuntu-24.04
|
|
58
|
+
timeout-minutes: 10
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v4
|
|
61
|
+
- uses: actions/setup-python@v5
|
|
62
|
+
with:
|
|
63
|
+
python-version: "3.12"
|
|
64
|
+
- run: pip install build twine
|
|
65
|
+
- run: AGENTROUTER_NO_BINARY_WHEEL=1 python -m build --wheel local-gateway --outdir local-dist
|
|
66
|
+
- run: python -m twine check --strict local-dist/*
|
|
67
|
+
- name: The fallback wheel has no program, and the SDK sees no local gateway
|
|
68
|
+
run: |
|
|
69
|
+
test "$(ls local-dist)" = "theagentrouter_local_gateway-$(python scripts/check_release.py --print-version)-py3-none-any.whl"
|
|
70
|
+
if unzip -l local-dist/*.whl | grep -q "/bin/"; then echo "the fallback wheel contains a program"; exit 1; fi
|
|
71
|
+
pip install . local-dist/*.whl
|
|
72
|
+
python -c "import theagentrouter.local_gateway as g; assert not g.installed()"
|
|
73
|
+
- uses: actions/upload-artifact@v4
|
|
74
|
+
with:
|
|
75
|
+
name: dist-local-any
|
|
76
|
+
path: local-dist/*.whl
|
|
77
|
+
if-no-files-found: error
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
checks:
|
|
12
|
+
uses: ./.github/workflows/ci.yml
|
|
13
|
+
|
|
14
|
+
verify:
|
|
15
|
+
needs: checks
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
- run: python scripts/check_release.py --tag "$GITHUB_REF_NAME"
|
|
23
|
+
- uses: actions/download-artifact@v4
|
|
24
|
+
with:
|
|
25
|
+
name: dist
|
|
26
|
+
path: dist/
|
|
27
|
+
- uses: actions/download-artifact@v4
|
|
28
|
+
with:
|
|
29
|
+
pattern: dist-local-*
|
|
30
|
+
path: companion-dist/
|
|
31
|
+
merge-multiple: true
|
|
32
|
+
- name: Verify SDK and all companion wheels are present
|
|
33
|
+
run: |
|
|
34
|
+
python - <<'PY'
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
files = list(Path("dist").glob("*")) + list(Path("companion-dist").glob("*"))
|
|
37
|
+
# darwin-arm64, linux-amd64, linux-arm64, and the py3-none-any fallback with no program
|
|
38
|
+
assert len(list(Path("companion-dist").glob("theagentrouter_local_gateway-*.whl"))) == 4
|
|
39
|
+
assert len(list(Path("companion-dist").glob("theagentrouter_local_gateway-*-py3-none-any.whl"))) == 1
|
|
40
|
+
for name in ("theagentrouter_core", "theagentrouter"):
|
|
41
|
+
assert len(list(Path("dist").glob(f"{name}-[0-9]*.whl"))) == 1, name
|
|
42
|
+
assert len(list(Path("dist").glob(f"{name}-[0-9]*.tar.gz"))) == 1, name
|
|
43
|
+
assert len(files) == 8
|
|
44
|
+
PY
|
|
45
|
+
|
|
46
|
+
# One job and one GitHub environment per package. PyPI accepts only one pending
|
|
47
|
+
# trusted publisher per owner, repository, workflow and environment, so each
|
|
48
|
+
# package needs its own environment until its project exists on PyPI.
|
|
49
|
+
# Each package goes up after the ones it depends on: the gateway, the SDK, then theagentrouter.
|
|
50
|
+
publish-local-gateway:
|
|
51
|
+
needs: verify
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
environment: pypi-local-gateway
|
|
54
|
+
permissions:
|
|
55
|
+
id-token: write # PyPI trusted publishing; no token stored anywhere
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/download-artifact@v4
|
|
58
|
+
with:
|
|
59
|
+
pattern: dist-local-*
|
|
60
|
+
path: companion-dist/
|
|
61
|
+
merge-multiple: true
|
|
62
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
63
|
+
with:
|
|
64
|
+
packages-dir: companion-dist/
|
|
65
|
+
|
|
66
|
+
publish-core:
|
|
67
|
+
needs: publish-local-gateway
|
|
68
|
+
runs-on: ubuntu-latest
|
|
69
|
+
environment: pypi-core
|
|
70
|
+
permissions:
|
|
71
|
+
id-token: write
|
|
72
|
+
steps:
|
|
73
|
+
- uses: actions/download-artifact@v4
|
|
74
|
+
with:
|
|
75
|
+
name: dist
|
|
76
|
+
path: dist/
|
|
77
|
+
- run: rm dist/theagentrouter-[0-9]*
|
|
78
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
79
|
+
with:
|
|
80
|
+
packages-dir: dist/
|
|
81
|
+
|
|
82
|
+
publish-theagentrouter:
|
|
83
|
+
needs: publish-core
|
|
84
|
+
runs-on: ubuntu-latest
|
|
85
|
+
environment: pypi
|
|
86
|
+
permissions:
|
|
87
|
+
id-token: write
|
|
88
|
+
steps:
|
|
89
|
+
- uses: actions/download-artifact@v4
|
|
90
|
+
with:
|
|
91
|
+
name: dist
|
|
92
|
+
path: dist/
|
|
93
|
+
- run: rm dist/theagentrouter_core-*
|
|
94
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
95
|
+
with:
|
|
96
|
+
packages-dir: dist/
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.egg-info/
|
|
3
|
+
.venv/
|
|
4
|
+
dist/
|
|
5
|
+
local-dist/
|
|
6
|
+
build/
|
|
7
|
+
.pytest_cache/
|
|
8
|
+
.venv-adk/
|
|
9
|
+
.venv-agents/
|
|
10
|
+
|
|
11
|
+
# Local provider keys for scripts/smoke_*.py. Never committed.
|
|
12
|
+
.env
|
|
13
|
+
|
|
14
|
+
# The aigw program for the local-gateway package; put there by `make local-gateway`
|
|
15
|
+
local-gateway/src/theagentrouter_local_gateway/bin/
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Instructions for coding agents (and people) working in this repository. `CLAUDE.md` points here.
|
|
4
|
+
|
|
5
|
+
## What this is
|
|
6
|
+
|
|
7
|
+
`theagentrouter` (imported as `theagentrouter`) is an open-source Python library. Agent frameworks use it to call AI models. It keeps LiteLLM's way of calling models (`completion`, `provider/model` names, response fields, error names) in a small install whose only dependency is `httpx`.
|
|
8
|
+
|
|
9
|
+
It speaks two request formats: OpenAI Chat Completions and Anthropic Messages. With no gateway, it calls providers that already speak one of them. With a gateway set, it sends every call there in the OpenAI format, and the gateway translates.
|
|
10
|
+
|
|
11
|
+
## Layout
|
|
12
|
+
|
|
13
|
+
| Path | What it holds |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `src/theagentrouter/__init__.py` | Public API: `completion`, `acompletion`, model lookups |
|
|
16
|
+
| `src/theagentrouter/routing.py` | Where a call goes: `api_base`, a gateway you set, or the local gateway. Never a provider directly; settings from the environment and `AGENTROUTER_HOME/config.json` |
|
|
17
|
+
| `src/theagentrouter/routes.py` | Routes: names of your own with fallbacks and retries (`route`, `Retry`), stored in `AGENTROUTER_HOME/routes.json` and served by the local gateway |
|
|
18
|
+
| `src/theagentrouter/local_gateway.py` | The local gateway: which providers the environment has, the `aigw` config, starting, finding and stopping `aigw` |
|
|
19
|
+
| `local-gateway/` | The `theagentrouter-local-gateway` package: the `aigw` program, one wheel per platform |
|
|
20
|
+
| `packages/theagentrouter/` | The `theagentrouter` package: no code; installs `theagentrouter-core` (the root package) and the local gateway |
|
|
21
|
+
| `src/theagentrouter/openai_format.py` | Build OpenAI requests; read responses and SSE streams |
|
|
22
|
+
| `src/theagentrouter/anthropic_format.py` | Convert OpenAI-style messages and tools to Anthropic Messages; read responses and streams back |
|
|
23
|
+
| `src/theagentrouter/transport.py` | HTTP with `httpx`, sync and async, streaming, error mapping |
|
|
24
|
+
| `src/theagentrouter/exceptions.py` | LiteLLM's error names; `from_http` maps HTTP errors to them |
|
|
25
|
+
| `src/theagentrouter/types.py` | LiteLLM-shaped response types; readable by attribute and as dicts |
|
|
26
|
+
| `src/theagentrouter/models.py`, `model_catalog.json` | Model prices and limits from tetrateio/agent-router-models (bundled; never fetched at run time) |
|
|
27
|
+
| `src/theagentrouter/compat/` | The LiteLLM-compatible module and `install_as_litellm()` |
|
|
28
|
+
| `surface.txt` | The recorded public surface |
|
|
29
|
+
| `tests/` | Tests against a local fake provider; `test_adk.py` and `test_openai_agents.py` run real Google ADK and OpenAI Agents SDK agents |
|
|
30
|
+
| `scripts/` | `surface.py`, `sync_models.py`, `smoke_real.py`, `smoke_adk_real.py`, `smoke_local_gateway.py`, `smoke_routes.py` |
|
|
31
|
+
|
|
32
|
+
## Rules
|
|
33
|
+
|
|
34
|
+
1. **Keep LiteLLM's API.** Same function names, arguments, response fields and error names. A framework that works with LiteLLM must work after `install_as_litellm()`.
|
|
35
|
+
2. **No provider code beyond the two formats.** The SDK never calls a provider; a new provider is one row in `local_gateway.PROVIDERS` (its `aigw` schema, address and key variables), which the local gateway's config is written from. Anything that needs translation belongs in the gateway, not here.
|
|
36
|
+
3. **One dependency.** Do not add runtime dependencies without a strong reason; `httpx` is the only one.
|
|
37
|
+
4. **The public surface is a contract.** `tests/test_surface.py` fails when the surface changes. After a deliberate change, run `python scripts/surface.py --write` and say why in the change description. The Enterprise SDK checks itself against `surface.txt`: it must contain all of it, unchanged.
|
|
38
|
+
5. **Never refer to the Enterprise package.** The open-source SDK must not import, mention or look for the Enterprise package (`tetrate-agentrouter`, imported as `tetrate.agentrouter`). A test checks this.
|
|
39
|
+
6. **Settings come from the environment, then `AGENTROUTER_HOME/config.json`.** Do not add a second place to read them.
|
|
40
|
+
7. **No secrets in tests or examples.** Tests use the fake provider in `tests/fake_provider.py`. Real-provider checks live in `scripts/smoke_real.py` and read keys from the environment.
|
|
41
|
+
|
|
42
|
+
## Commands
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
|
|
46
|
+
.venv/bin/pytest # all tests, no keys needed
|
|
47
|
+
.venv/bin/ruff check . && .venv/bin/ruff format --check .
|
|
48
|
+
.venv/bin/python scripts/surface.py --write # after a deliberate API change
|
|
49
|
+
.venv/bin/python scripts/sync_models.py # refresh the model table
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`DEVELOPMENT.md` has the details, including the framework tests and the release steps. User docs are in `docs/`: keep `install.md`, `usage.md`, `coming-from-litellm.md` and `frameworks.md` in step with the code, especially the LiteLLM lists when `compat/litellm.py` or `transport.IGNORED` changes.
|
|
53
|
+
|
|
54
|
+
## Before you finish a change
|
|
55
|
+
|
|
56
|
+
- `pytest`, `ruff check` and `ruff format --check` pass.
|
|
57
|
+
- New behavior has a test against the fake provider.
|
|
58
|
+
- `CHANGELOG.md` has a line under "Unreleased" for anything a user would notice.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are listed here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow [Semantic Versioning](https://semver.org/).
|
|
4
|
+
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
### Added (2026-10-12)
|
|
8
|
+
|
|
9
|
+
- After an SDK upgrade, the next call restarts a local gateway that the old version started. `state.json` records the SDK's and the local gateway's versions.
|
|
10
|
+
|
|
11
|
+
### Fixed (2026-10-12)
|
|
12
|
+
|
|
13
|
+
- `pip install theagentrouter` no longer fails where the local gateway has no wheel (Intel macOS, macOS 12 and older, Alpine, Linux before glibc 2.36, Windows). A `py3-none-any` local gateway wheel with no program is published next to the platform wheels; pip picks it only where none fits, and the SDK then runs without a local gateway. The macOS wheel needs macOS 13 or newer; the docs said 12.
|
|
14
|
+
- Two programs defining routes at the same time no longer lose one: the route file is locked while it is changed and written in one step.
|
|
15
|
+
- Removing a route named like a model (`openai/gpt-4o-mini`) restarts the local gateway on the next call; the route's fallbacks, retries and limit stayed active before.
|
|
16
|
+
- With `AGENTROUTER_AUTOSTART=0`, a route works when the running gateway already has it.
|
|
17
|
+
- Claude: `cache_control` on content parts, on a whole message (LiteLLM's form) and on tools reaches Anthropic, so prompt caching works; the system prompt goes as text blocks when one is marked. Cache reads show in `usage.prompt_tokens_details.cached_tokens`.
|
|
18
|
+
- Claude: streamed thinking arrives as `delta.reasoning_content`, and responses and rebuilt streams carry `thinking_blocks` with their signatures, as LiteLLM's do. Streamed, each fragment of thinking text comes in its own chunk and the signature alone in a last chunk, LiteLLM's shape, so Google ADK puts the block back together unchanged. Sent back with a tool result, the blocks go to Anthropic first in the turn, unchanged, which Anthropic requires when thinking is on.
|
|
19
|
+
- `stream_chunk_builder` rebuilds each choice on its own; with `n=2` the two answers were joined into one.
|
|
20
|
+
- `acompletion`, `aembedding` and `aresponses` no longer block the event loop while the local gateway starts.
|
|
21
|
+
- `completion_cost` works on a `responses()` reply (it read only Chat Completions usage).
|
|
22
|
+
- `validate_environment` for Bedrock asks for `AWS_SECRET_ACCESS_KEY` and `AWS_REGION` too, as the local gateway does.
|
|
23
|
+
- The release workflow runs the route and embedding tests against the real `aigw`, not only the local gateway tests.
|
|
24
|
+
|
|
25
|
+
### Changed (2026-10-12)
|
|
26
|
+
|
|
27
|
+
- The bundled model list is read from `tetrateio/agent-router-models` (the SDK pointed at a `theagentrouter/` copy that does not exist) and refreshed: 228 models. DeepInfra models are keyed `deepinfra/<model>`; they were `deepinfra/deepinfra/<model>`, so lookups by their usual name found nothing.
|
|
28
|
+
- **The local gateway chooses its port at each start** instead of always using 1975, so Tetrate Desktop or another `aigw run` on 1975 no longer blocks it. SDK programs find it through `state.json`. When `aigw`'s fixed internal ports (1061, 18000) are taken, the error names the program holding them.
|
|
29
|
+
- **The SDK talks only to a gateway.** It no longer calls OpenAI, Anthropic, Gemini, Groq, Mistral, DeepSeek or Together directly: every call goes to the local gateway (Agent Router on this computer), to a gateway set with `AGENTROUTER_BASE_URL`, or to an endpoint named with `api_base=` (LiteLLM's argument). Claude goes to the gateway in Anthropic's Messages format, so prompt caching and thinking keep working.
|
|
30
|
+
- **Three packages.** `theagentrouter` installs the SDK and, on macOS 13+ (Apple chips) and Linux (x86-64, Arm), the local gateway; it has no code of its own. `theagentrouter-core` is the SDK alone, for a remote gateway (servers, containers, serverless, CI). `theagentrouter-local-gateway` holds `aigw`. All import as `import theagentrouter`. `theagentrouter[local-gateway]` still works.
|
|
31
|
+
- `api_key=` without `api_base=` warns and has no effect: the local gateway reads provider keys from the environment.
|
|
32
|
+
- `_hidden_params["route"]` is `local gateway`, `gateway` or `api_base` (was `direct` for calls to providers).
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- The rest of what agent frameworks call from LiteLLM (counted across 20 frameworks that import it): OpenAI and Anthropic model names work without a provider, as in LiteLLM (`gpt-4o-mini`, `claude-haiku-4-5`); a bare `gemini-…` is refused with the reason, since LiteLLM sends it to Vertex AI; `responses` and `aresponses` for OpenAI models, through the gateway's `/v1/responses`; `stream_chunk_builder`; `validate_environment`; `success_callback`, `failure_callback` and `callbacks` in `theagentrouter.compat.litellm`, calling your functions and loggers after each call and stream; `model_list`, `cost_calculator`, and `ChatCompletionDeltaToolCall`, `CostPerToken` and `ChatCompletionMessageToolCall` under `types.utils`.
|
|
37
|
+
- A route defined in Python warns when `AGENTROUTER_BASE_URL` is set: routes program the local gateway only.
|
|
38
|
+
- `token_counter`, with LiteLLM's arguments, also under `theagentrouter.compat.litellm`. OpenAI and Anthropic are counted by the gateway (`/responses/input_tokens`, `/messages/count_tokens`); everything else is estimated with LiteLLM's rules, using tiktoken from the new `tokens` extra or about four characters a token without it. `AGENTROUTER_TOKEN_COUNT=estimate` always estimates.
|
|
39
|
+
- Embeddings: `theagentrouter.embedding` and `aembedding`, also under `theagentrouter.compat.litellm`, with LiteLLM's arguments and `EmbeddingResponse`, priced from the model list. Direct for OpenAI-format providers, through a gateway for the rest; Anthropic has none.
|
|
40
|
+
- Request limits on routes: `route(..., limit=Limit(requests, per="minute"))`, enforced by the local gateway with Envoy's local rate limit. Requests, not tokens: counting tokens needs Redis, which `aigw run` does not have.
|
|
41
|
+
- Routes: `theagentrouter.route(name, primary, fallbacks, retry)`, with `Retry`, `list_routes` and `remove_route`. A route is a model name of your own; the local gateway does its retries and fallbacks, with the models at priority 0, 1, …, as Agent Router Enterprise does. Models in one tier share the traffic.
|
|
42
|
+
- LiteLLM's `Router` in `theagentrouter.compat.litellm`, for `model_list`, `fallbacks` and `num_retries`, served by routes.
|
|
43
|
+
- Responses from a route carry `served_by` (read from the reply's model name) and `route_name` in `_hidden_params`, and are priced as the model that answered.
|
|
44
|
+
- The local gateway writes a backend at `127.0.0.1` as `localhost`, and waits after starting until Envoy has looked up every route's backends.
|
|
45
|
+
- Numbered examples, `examples/00_two_liner.py` to `examples/20_request_limit.py`, with an index.
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- The local gateway uses `aigw` v1.2.0 (was v1.1.0). Request limits need it: v1.1.0 is built on Envoy Gateway v1.8.1, whose rate-limit schema makes `aigw run` reject every `rateLimit` (`requests` declared int32 with a maximum of 4294967295); Envoy Gateway v1.9 fixed it.
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
|
|
53
|
+
- The warning for `fallbacks` and `num_retries` now points to routes.
|
|
54
|
+
|
|
55
|
+
## [0.1.0a1] - 2026-10-07
|
|
56
|
+
|
|
57
|
+
### Added
|
|
58
|
+
|
|
59
|
+
- `completion` and `acompletion` with LiteLLM's arguments, streaming and tool calls.
|
|
60
|
+
- Direct calls with no gateway to OpenAI, Anthropic, Gemini, Groq, Mistral, DeepSeek and Together, using the key in the environment.
|
|
61
|
+
- Anthropic Messages support: LiteLLM-style messages and tools are converted, and replies and streams are converted back.
|
|
62
|
+
- Gateway mode: with `AGENTROUTER_BASE_URL` set, every call goes to that gateway in the OpenAI format.
|
|
63
|
+
- Settings from `AGENTROUTER_HOME/config.json` when the environment does not set them.
|
|
64
|
+
- LiteLLM's error names, mapped from provider HTTP errors.
|
|
65
|
+
- `get_model_info`, `model_cost`, `completion_cost` and capability lookups, from a snapshot of tetrateio/agent-router-models.
|
|
66
|
+
- `theagentrouter.compat.litellm` and `install_as_litellm()`, so Google ADK's `LiteLlm` class and the OpenAI Agents SDK's `LitellmModel` work without LiteLLM installed.
|
|
67
|
+
- A warning, once per argument, when a LiteLLM-only argument such as `fallbacks` or `num_retries` is passed and ignored.
|
|
68
|
+
- `usage.prompt_tokens_details`, `usage.completion_tokens_details`, `choice.logprobs`, `delta.refusal` and `model_copy()` on responses, as in LiteLLM.
|
|
69
|
+
- Documentation: install, usage, coming from LiteLLM, and agent frameworks, under `docs/`.
|
|
70
|
+
- The local gateway: `pip install "theagentrouter[local-gateway]"` installs Agent Router's `aigw` (package `theagentrouter-local-gateway`). The first call that needs it, such as a `bedrock/` model, starts it in the background with the providers whose keys are in the environment. While it runs, calls to its configured providers go through it, except direct-provider calls with an explicit `api_key`. `AGENTROUTER_AUTOSTART=0` turns starting off.
|
|
71
|
+
|
|
72
|
+
### Fixed
|
|
73
|
+
|
|
74
|
+
- An explicit provider `api_key` is honored even while the local gateway runs. Direct providers bypass it for that call.
|
|
75
|
+
- Anthropic maps `parallel_tool_calls=False` to `disable_parallel_tool_use`. OpenAI-only parameters that cannot be translated raise `UnsupportedParamsError`; parameter discovery reports the supported set.
|
|
76
|
+
- OpenAI responses retain refusal, reasoning, log probabilities and cached/reasoning token details, including streams.
|
|
77
|
+
- Provider error events in streams raise the matching SDK exception with the requested model and provider.
|
|
78
|
+
- Preparing a request leaves the caller's tool-call history unchanged.
|
|
79
|
+
- Releases build, verify and publish the SDK and three companion wheels together. The companion uses checksum-pinned upstream binaries; required integration tests cannot silently skip.
|
|
80
|
+
|
|
81
|
+
### Changed
|
|
82
|
+
|
|
83
|
+
- A provider that needs a gateway now says how to install the local gateway, instead of only asking for `AGENTROUTER_BASE_URL`.
|
|
84
|
+
- With a gateway set, a model name with no provider, such as `claude-sonnet-5`, goes to the gateway unchanged instead of being refused.
|
|
85
|
+
- The local gateway receives the full `provider/model` name, as any gateway does, and routes it with one rule per model. No SDK-only header is sent.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Read [AGENTS.md](AGENTS.md): it holds the instructions for this repository, for every coding agent.
|
|
4
|
+
|
|
5
|
+
Reminders that matter most:
|
|
6
|
+
|
|
7
|
+
- `tests/test_surface.py` guards the public API. A failing surface test after an intended change means: run `python scripts/surface.py --write`, and explain the change.
|
|
8
|
+
- Never add a reference to the Enterprise package (`tetrate-agentrouter` / `tetrate.agentrouter`) here; the open-source SDK must not know the Enterprise SDK exists.
|
|
9
|
+
- The local gateway (`src/theagentrouter/local_gateway.py`, package in `local-gateway/`) has end-to-end tests that are skipped unless the package is installed. Before finishing a change to routing or the gateway, run `make local-gateway AIGW=/path/to/aigw`, then `pytest` — a skipped run proves nothing about the gateway. With keys in `.env`, also run `scripts/smoke_local_gateway.py`.
|
|
10
|
+
- Never commit the `aigw` program (`local-gateway/src/theagentrouter_local_gateway/bin/`, git-ignored), and never write a key value into the gateway's config: it holds `${VAR}` references that `aigw` fills in from its environment.
|
|
11
|
+
- Through the local gateway, the SDK sends the full `provider/model` name, as to any gateway; never add an SDK-only header. The gateway's config has one rule per model that passes the provider its own name, spread over routes of at most 15 rules (Gateway API's limit is 16 and `aigw` adds one). Anthropic goes in its own Messages format to `/anthropic/v1/messages`, because `aigw` does not translate OpenAI to Anthropic. Details in `docs/local-gateway.md`.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
+
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
+
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
+
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity
|
|
10
|
+
and orientation.
|
|
11
|
+
|
|
12
|
+
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
+
diverse, inclusive, and healthy community.
|
|
14
|
+
|
|
15
|
+
## Our Standards
|
|
16
|
+
|
|
17
|
+
Examples of behavior that contributes to a positive environment for our
|
|
18
|
+
community include:
|
|
19
|
+
|
|
20
|
+
* Demonstrating empathy and kindness toward other people
|
|
21
|
+
* Being respectful of differing opinions, viewpoints, and experiences
|
|
22
|
+
* Giving and gracefully accepting constructive feedback
|
|
23
|
+
* Accepting responsibility and apologizing to those affected by our mistakes,
|
|
24
|
+
and learning from the experience
|
|
25
|
+
* Focusing on what is best not just for us as individuals, but for the
|
|
26
|
+
overall community
|
|
27
|
+
|
|
28
|
+
Examples of unacceptable behavior include:
|
|
29
|
+
|
|
30
|
+
* The use of sexualized language or imagery, and sexual attention or
|
|
31
|
+
advances of any kind
|
|
32
|
+
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
33
|
+
* Public or private harassment
|
|
34
|
+
* Publishing others' private information, such as a physical or email
|
|
35
|
+
address, without their explicit permission
|
|
36
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
|
37
|
+
professional setting
|
|
38
|
+
|
|
39
|
+
## Enforcement Responsibilities
|
|
40
|
+
|
|
41
|
+
Community leaders are responsible for clarifying and enforcing our standards of
|
|
42
|
+
acceptable behavior and will take appropriate and fair corrective action in
|
|
43
|
+
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
44
|
+
or harmful.
|
|
45
|
+
|
|
46
|
+
Community leaders have the right and responsibility to remove, edit, or reject
|
|
47
|
+
comments, commits, code, wiki edits, issues, and other contributions that are
|
|
48
|
+
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
|
49
|
+
decisions when appropriate.
|
|
50
|
+
|
|
51
|
+
## Scope
|
|
52
|
+
|
|
53
|
+
This Code of Conduct applies within all community spaces, and also applies when
|
|
54
|
+
an individual is officially representing the community in public spaces.
|
|
55
|
+
Examples of representing our community include using an official e-mail address,
|
|
56
|
+
posting via an official social media account, or acting as an appointed
|
|
57
|
+
representative at an online or offline event.
|
|
58
|
+
|
|
59
|
+
## Enforcement
|
|
60
|
+
|
|
61
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
62
|
+
reported to the community leaders responsible for enforcement at
|
|
63
|
+
[TODO: add the Tetrate contact address before publishing].
|
|
64
|
+
All complaints will be reviewed and investigated promptly and fairly.
|
|
65
|
+
|
|
66
|
+
All community leaders are obligated to respect the privacy and security of the
|
|
67
|
+
reporter of any incident.
|
|
68
|
+
|
|
69
|
+
## Enforcement Guidelines
|
|
70
|
+
|
|
71
|
+
Community leaders will follow these Community Impact Guidelines in determining
|
|
72
|
+
the consequences for any action they deem in violation of this Code of Conduct:
|
|
73
|
+
|
|
74
|
+
### 1. Correction
|
|
75
|
+
|
|
76
|
+
**Community Impact**: Use of inappropriate language or other behavior deemed
|
|
77
|
+
unprofessional or unwelcome in the community.
|
|
78
|
+
|
|
79
|
+
**Consequence**: A private, written warning from community leaders, providing
|
|
80
|
+
clarity around the nature of the violation and an explanation of why the
|
|
81
|
+
behavior was inappropriate. A public apology may be requested.
|
|
82
|
+
|
|
83
|
+
### 2. Warning
|
|
84
|
+
|
|
85
|
+
**Community Impact**: A violation through a single incident or series
|
|
86
|
+
of actions.
|
|
87
|
+
|
|
88
|
+
**Consequence**: A warning with consequences for continued behavior. No
|
|
89
|
+
interaction with the people involved, including unsolicited interaction with
|
|
90
|
+
those enforcing the Code of Conduct, for a specified period of time. This
|
|
91
|
+
includes avoiding interactions in community spaces as well as external channels
|
|
92
|
+
like social media. Violating these terms may lead to a temporary or
|
|
93
|
+
permanent ban.
|
|
94
|
+
|
|
95
|
+
### 3. Temporary Ban
|
|
96
|
+
|
|
97
|
+
**Community Impact**: A serious violation of community standards, including
|
|
98
|
+
sustained inappropriate behavior.
|
|
99
|
+
|
|
100
|
+
**Consequence**: A temporary ban from any sort of interaction or public
|
|
101
|
+
communication with the community for a specified period of time. No public or
|
|
102
|
+
private interaction with the people involved, including unsolicited interaction
|
|
103
|
+
with those enforcing the Code of Conduct, is allowed during this period.
|
|
104
|
+
Violating these terms may lead to a permanent ban.
|
|
105
|
+
|
|
106
|
+
### 4. Permanent Ban
|
|
107
|
+
|
|
108
|
+
**Community Impact**: Demonstrating a pattern of violation of community
|
|
109
|
+
standards, including sustained inappropriate behavior, harassment of an
|
|
110
|
+
individual, or aggression toward or disparagement of classes of individuals.
|
|
111
|
+
|
|
112
|
+
**Consequence**: A permanent ban from any sort of public interaction within
|
|
113
|
+
the community.
|
|
114
|
+
|
|
115
|
+
## Attribution
|
|
116
|
+
|
|
117
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
118
|
+
version 2.0, available at
|
|
119
|
+
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
|
120
|
+
|
|
121
|
+
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
|
122
|
+
enforcement ladder](https://github.com/mozilla/diversity).
|
|
123
|
+
|
|
124
|
+
[homepage]: https://www.contributor-covenant.org
|
|
125
|
+
|
|
126
|
+
For answers to common questions about this code of conduct, see the FAQ at
|
|
127
|
+
https://www.contributor-covenant.org/faq. Translations are available at
|
|
128
|
+
https://www.contributor-covenant.org/translations.
|