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.
Files changed (109) hide show
  1. theagentrouter_core-0.1.0a1/.github/ISSUE_TEMPLATE/bug_report.yml +38 -0
  2. theagentrouter_core-0.1.0a1/.github/ISSUE_TEMPLATE/config.yml +5 -0
  3. theagentrouter_core-0.1.0a1/.github/ISSUE_TEMPLATE/feature_request.yml +15 -0
  4. theagentrouter_core-0.1.0a1/.github/dependabot.yml +10 -0
  5. theagentrouter_core-0.1.0a1/.github/pull_request_template.md +8 -0
  6. theagentrouter_core-0.1.0a1/.github/workflows/ci.yml +74 -0
  7. theagentrouter_core-0.1.0a1/.github/workflows/local-gateway-build.yml +77 -0
  8. theagentrouter_core-0.1.0a1/.github/workflows/release.yml +96 -0
  9. theagentrouter_core-0.1.0a1/.gitignore +15 -0
  10. theagentrouter_core-0.1.0a1/.pre-commit-config.yaml +7 -0
  11. theagentrouter_core-0.1.0a1/AGENTS.md +58 -0
  12. theagentrouter_core-0.1.0a1/CHANGELOG.md +85 -0
  13. theagentrouter_core-0.1.0a1/CLAUDE.md +11 -0
  14. theagentrouter_core-0.1.0a1/CODE_OF_CONDUCT.md +128 -0
  15. theagentrouter_core-0.1.0a1/CONTRIBUTING.md +28 -0
  16. theagentrouter_core-0.1.0a1/DEVELOPMENT.md +177 -0
  17. theagentrouter_core-0.1.0a1/LICENSE +202 -0
  18. theagentrouter_core-0.1.0a1/Makefile +35 -0
  19. theagentrouter_core-0.1.0a1/PKG-INFO +201 -0
  20. theagentrouter_core-0.1.0a1/README.md +164 -0
  21. theagentrouter_core-0.1.0a1/SECURITY.md +21 -0
  22. theagentrouter_core-0.1.0a1/docs/README.md +13 -0
  23. theagentrouter_core-0.1.0a1/docs/coming-from-litellm.md +90 -0
  24. theagentrouter_core-0.1.0a1/docs/frameworks.md +99 -0
  25. theagentrouter_core-0.1.0a1/docs/images/how-it-works.svg +53 -0
  26. theagentrouter_core-0.1.0a1/docs/install.md +108 -0
  27. theagentrouter_core-0.1.0a1/docs/local-gateway.md +113 -0
  28. theagentrouter_core-0.1.0a1/docs/usage.md +313 -0
  29. theagentrouter_core-0.1.0a1/examples/00_two_liner.py +12 -0
  30. theagentrouter_core-0.1.0a1/examples/01_hello_world.py +10 -0
  31. theagentrouter_core-0.1.0a1/examples/02_multi_provider.py +22 -0
  32. theagentrouter_core-0.1.0a1/examples/03_streaming.py +13 -0
  33. theagentrouter_core-0.1.0a1/examples/04_tool_use.py +33 -0
  34. theagentrouter_core-0.1.0a1/examples/05_async.py +24 -0
  35. theagentrouter_core-0.1.0a1/examples/06_structured_output.py +21 -0
  36. theagentrouter_core-0.1.0a1/examples/07_fallback.py +21 -0
  37. theagentrouter_core-0.1.0a1/examples/08_retry.py +16 -0
  38. theagentrouter_core-0.1.0a1/examples/09_model_alias.py +18 -0
  39. theagentrouter_core-0.1.0a1/examples/10_share_traffic.py +19 -0
  40. theagentrouter_core-0.1.0a1/examples/11_from_litellm.py +52 -0
  41. theagentrouter_core-0.1.0a1/examples/12_litellm_router.py +20 -0
  42. theagentrouter_core-0.1.0a1/examples/13_handling_errors.py +20 -0
  43. theagentrouter_core-0.1.0a1/examples/14_cost.py +17 -0
  44. theagentrouter_core-0.1.0a1/examples/15_through_a_gateway.py +23 -0
  45. theagentrouter_core-0.1.0a1/examples/16_adk_agent.py +43 -0
  46. theagentrouter_core-0.1.0a1/examples/17_openai_agents.py +34 -0
  47. theagentrouter_core-0.1.0a1/examples/18_local_gateway.py +21 -0
  48. theagentrouter_core-0.1.0a1/examples/19_embeddings.py +22 -0
  49. theagentrouter_core-0.1.0a1/examples/20_request_limit.py +20 -0
  50. theagentrouter_core-0.1.0a1/examples/README.md +31 -0
  51. theagentrouter_core-0.1.0a1/llms-full.txt +1726 -0
  52. theagentrouter_core-0.1.0a1/llms.txt +47 -0
  53. theagentrouter_core-0.1.0a1/local-gateway/LICENSE +202 -0
  54. theagentrouter_core-0.1.0a1/local-gateway/README.md +18 -0
  55. theagentrouter_core-0.1.0a1/local-gateway/aigw-release.json +9 -0
  56. theagentrouter_core-0.1.0a1/local-gateway/hatch_build.py +74 -0
  57. theagentrouter_core-0.1.0a1/local-gateway/pyproject.toml +23 -0
  58. theagentrouter_core-0.1.0a1/local-gateway/src/theagentrouter_local_gateway/__init__.py +33 -0
  59. theagentrouter_core-0.1.0a1/packages/theagentrouter/LICENSE +202 -0
  60. theagentrouter_core-0.1.0a1/packages/theagentrouter/README.md +13 -0
  61. theagentrouter_core-0.1.0a1/packages/theagentrouter/pyproject.toml +41 -0
  62. theagentrouter_core-0.1.0a1/pyproject.toml +76 -0
  63. theagentrouter_core-0.1.0a1/scripts/build_llms.py +103 -0
  64. theagentrouter_core-0.1.0a1/scripts/check_release.py +61 -0
  65. theagentrouter_core-0.1.0a1/scripts/check_test_report.py +14 -0
  66. theagentrouter_core-0.1.0a1/scripts/fetch_aigw.py +44 -0
  67. theagentrouter_core-0.1.0a1/scripts/smoke_adk_real.py +85 -0
  68. theagentrouter_core-0.1.0a1/scripts/smoke_agents_real.py +83 -0
  69. theagentrouter_core-0.1.0a1/scripts/smoke_local_gateway.py +116 -0
  70. theagentrouter_core-0.1.0a1/scripts/smoke_real.py +102 -0
  71. theagentrouter_core-0.1.0a1/scripts/smoke_routes.py +114 -0
  72. theagentrouter_core-0.1.0a1/scripts/surface.py +83 -0
  73. theagentrouter_core-0.1.0a1/scripts/sync_models.py +78 -0
  74. theagentrouter_core-0.1.0a1/skills/migrate-from-litellm/SKILL.md +96 -0
  75. theagentrouter_core-0.1.0a1/skills/theagentrouter/SKILL.md +72 -0
  76. theagentrouter_core-0.1.0a1/src/theagentrouter/__init__.py +100 -0
  77. theagentrouter_core-0.1.0a1/src/theagentrouter/anthropic_format.py +397 -0
  78. theagentrouter_core-0.1.0a1/src/theagentrouter/compat/__init__.py +17 -0
  79. theagentrouter_core-0.1.0a1/src/theagentrouter/compat/_callbacks.py +166 -0
  80. theagentrouter_core-0.1.0a1/src/theagentrouter/compat/litellm.py +297 -0
  81. theagentrouter_core-0.1.0a1/src/theagentrouter/embeddings.py +124 -0
  82. theagentrouter_core-0.1.0a1/src/theagentrouter/exceptions.py +155 -0
  83. theagentrouter_core-0.1.0a1/src/theagentrouter/local_gateway.py +774 -0
  84. theagentrouter_core-0.1.0a1/src/theagentrouter/model_catalog.json +3530 -0
  85. theagentrouter_core-0.1.0a1/src/theagentrouter/models.py +138 -0
  86. theagentrouter_core-0.1.0a1/src/theagentrouter/openai_format.py +208 -0
  87. theagentrouter_core-0.1.0a1/src/theagentrouter/py.typed +0 -0
  88. theagentrouter_core-0.1.0a1/src/theagentrouter/responses.py +171 -0
  89. theagentrouter_core-0.1.0a1/src/theagentrouter/routes.py +250 -0
  90. theagentrouter_core-0.1.0a1/src/theagentrouter/routing.py +270 -0
  91. theagentrouter_core-0.1.0a1/src/theagentrouter/tokens.py +326 -0
  92. theagentrouter_core-0.1.0a1/src/theagentrouter/transport.py +263 -0
  93. theagentrouter_core-0.1.0a1/src/theagentrouter/types.py +214 -0
  94. theagentrouter_core-0.1.0a1/surface.txt +121 -0
  95. theagentrouter_core-0.1.0a1/tests/__init__.py +0 -0
  96. theagentrouter_core-0.1.0a1/tests/conftest.py +3 -0
  97. theagentrouter_core-0.1.0a1/tests/fake_provider.py +423 -0
  98. theagentrouter_core-0.1.0a1/tests/test_adk.py +141 -0
  99. theagentrouter_core-0.1.0a1/tests/test_embeddings.py +90 -0
  100. theagentrouter_core-0.1.0a1/tests/test_llms.py +24 -0
  101. theagentrouter_core-0.1.0a1/tests/test_local_gateway.py +442 -0
  102. theagentrouter_core-0.1.0a1/tests/test_minimum.py +237 -0
  103. theagentrouter_core-0.1.0a1/tests/test_openai_agents.py +75 -0
  104. theagentrouter_core-0.1.0a1/tests/test_release_regressions.py +409 -0
  105. theagentrouter_core-0.1.0a1/tests/test_release_tools.py +93 -0
  106. theagentrouter_core-0.1.0a1/tests/test_routes.py +418 -0
  107. theagentrouter_core-0.1.0a1/tests/test_sdk.py +277 -0
  108. theagentrouter_core-0.1.0a1/tests/test_surface.py +28 -0
  109. 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,5 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Security problem
4
+ url: https://github.com/theagentrouter/agent-router-python/security/advisories/new
5
+ about: Report security problems privately, not as an issue.
@@ -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,10 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: pip
4
+ directory: "/"
5
+ schedule:
6
+ interval: weekly
7
+ - package-ecosystem: github-actions
8
+ directory: "/"
9
+ schedule:
10
+ interval: weekly
@@ -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,7 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.6.9
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - id: ruff-format
@@ -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.