brrragent 0.4.2__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.
@@ -0,0 +1,15 @@
1
+ __pycache__/
2
+ *.egg-info/
3
+ .venv/
4
+ .coverage
5
+ .coverage.*
6
+ .pytest_cache/
7
+ .ruff_cache/
8
+ coverage.xml
9
+ htmlcov/
10
+ build/
11
+ dist/
12
+ *.pyc
13
+ .env
14
+ .env.*
15
+ !.env.example
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bruno Volpato
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,224 @@
1
+ Metadata-Version: 2.5
2
+ Name: brrragent
3
+ Version: 0.4.2
4
+ Summary: Reusable agent loop with multi-provider routing and MCP tool calling
5
+ Project-URL: Homepage, https://github.com/bvolpato/brrragent
6
+ Project-URL: Repository, https://github.com/bvolpato/brrragent
7
+ Project-URL: Issues, https://github.com/bvolpato/brrragent/issues
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Requires-Python: >=3.12
11
+ Requires-Dist: httpx2<3,>=2.5
12
+ Requires-Dist: httpx<1,>=0.27.1
13
+ Requires-Dist: mcp<3,>=2.0.0
14
+ Requires-Dist: openai<3,>=2.45.0
15
+ Provides-Extra: gemini
16
+ Requires-Dist: google-genai<3,>=1.0.0; extra == 'gemini'
17
+ Description-Content-Type: text/markdown
18
+
19
+ # brrragent
20
+
21
+ `brrragent` is a Python package for running a synchronous model/tool loop. It
22
+ provides a common entry point for several model APIs and converts MCP tools into
23
+ the function-calling formats expected by those APIs.
24
+
25
+ ## Overview
26
+
27
+ Main components:
28
+
29
+ - `run_agent`: executes model responses and tool calls until completion.
30
+ - `McpToolCaller`: discovers, filters, routes, and executes tools across one or
31
+ more MCP servers.
32
+ - `McpServerConfig`: configures Streamable HTTP, legacy HTTP+SSE, stdio, or raw
33
+ JSON-RPC compatibility connections.
34
+ - `KeyPool`: selects API keys and removes rate-limited keys from rotation.
35
+
36
+ Model adapters cover OpenAI, OpenAI-compatible APIs, Google Gemini, OpenRouter,
37
+ and the optional Codex OAuth backend. Structured outputs, retry handling, tool
38
+ result truncation, and provider routing are handled by the package.
39
+
40
+ ## Installation
41
+
42
+ Install from PyPI:
43
+
44
+ ```bash
45
+ uv add brrragent
46
+ ```
47
+
48
+ Gemini support uses an optional dependency:
49
+
50
+ ```bash
51
+ uv add "brrragent[gemini]"
52
+ ```
53
+
54
+ Use `uv pip install` instead when installing into an existing environment.
55
+
56
+ ## Basic use
57
+
58
+ ```python
59
+ from brrragent import McpToolCaller, run_agent
60
+
61
+ with McpToolCaller(endpoint="https://mcp.example.com/mcp") as mcp:
62
+ result = run_agent(
63
+ system_prompt="Follow the request and use available tools when needed.",
64
+ user_prompt="Return a short response.",
65
+ model="openai/gpt-5.5",
66
+ mcp=mcp,
67
+ )
68
+
69
+ print(result)
70
+ ```
71
+
72
+ Provider credentials are read from the caller's environment. Common variables
73
+ are `OPENAI_API_KEY`, `OPENROUTER_API_KEY`, and `GEMINI_API_KEY`.
74
+
75
+ ## Codex OAuth
76
+
77
+ Create brrragent-owned Codex credentials with device login:
78
+
79
+ ```bash
80
+ uv run brrragent-auth login
81
+ ```
82
+
83
+ This writes direct OAuth credentials to `~/.cache/brrragent/codex-auth.json`
84
+ and Codex CLI credentials to `~/.cache/brrragent/codex-home/auth.json`, both
85
+ with `0600` permissions. Default routing does not read shared OpenCode auth or
86
+ `~/.codex/auth.json`.
87
+
88
+ Override locations with `BRRRAGENT_AUTH_PATH`, `BRRRAGENT_CODEX_HOME`, or
89
+ command flags. Existing callers can still use `BRRRAGENT_OPENCODE_AUTH_PATH`
90
+ and explicit `CODEX_HOME` values.
91
+
92
+ Use one independently issued profile per application or worker group to avoid
93
+ refresh-token rotation conflicts:
94
+
95
+ ```bash
96
+ uv run brrragent-auth login --profile content-worker
97
+ BRRRAGENT_PROFILE=content-worker uv run your-application
98
+ ```
99
+
100
+ Prefer per-call selection when one process handles multiple use cases:
101
+
102
+ ```python
103
+ from brrragent import CodexAuthConfig, run_agent
104
+
105
+ result = run_agent(
106
+ system_prompt="Return a concise answer.",
107
+ user_prompt="Summarize this input.",
108
+ model="codex/gpt-5.6-luna:medium",
109
+ codex_auth=CodexAuthConfig(profile="content-worker"),
110
+ )
111
+ ```
112
+
113
+ Explicit paths are also supported with
114
+ `CodexAuthConfig(auth_path="...", codex_home="...")`. Per-call selection is
115
+ context-local, so concurrent use cases do not need to mutate process-wide
116
+ environment variables.
117
+
118
+ Named profiles live under `~/.cache/brrragent/profiles/<profile>/`. Run device
119
+ login separately for every profile; copying an auth file also copies its
120
+ rotating refresh token and does not provide isolation. `BRRRAGENT_PROFILE_ROOT`
121
+ can move the profile directory. A named profile also takes precedence over a
122
+ generic `CODEX_HOME` value.
123
+
124
+ ## Prompt caching
125
+
126
+ Keep reusable tools and system instructions before request-specific content.
127
+ Use one versioned key per stable prompt without user IDs, timestamps, URLs, or
128
+ prompt text:
129
+
130
+ ```python
131
+ from brrragent import PromptCacheConfig
132
+
133
+ cache = PromptCacheConfig(key="workflow:v2", ttl="1h")
134
+ ```
135
+
136
+ Pass `prompt_cache=cache` to `run_agent`. brrragent maps it to supported cache
137
+ keys, explicit breakpoints, provider affinity, and Anthropic cache controls.
138
+ Gemini uses its stable system-first prefix for implicit caching. An optional
139
+ `on_usage` callback receives normalized token and cache usage for each request.
140
+
141
+ ## MCP servers
142
+
143
+ One caller can combine multiple endpoints:
144
+
145
+ ```python
146
+ from brrragent import McpToolCaller
147
+
148
+ mcp = McpToolCaller(
149
+ endpoints=[
150
+ "https://mcp.example.com/one",
151
+ "https://mcp.example.com/two",
152
+ ],
153
+ allowed_tools={"*"},
154
+ excluded_tools={"internal_*"},
155
+ )
156
+ ```
157
+
158
+ Use explicit server configuration to mix transports or prefix tool names:
159
+
160
+ ```python
161
+ from brrragent import McpServerConfig, McpToolCaller
162
+
163
+ mcp = McpToolCaller(
164
+ servers=[
165
+ McpServerConfig(
166
+ name="remote",
167
+ url="https://mcp.example.com/mcp",
168
+ tool_prefix="remote_",
169
+ ),
170
+ McpServerConfig(
171
+ name="local",
172
+ transport="stdio",
173
+ command="uvx",
174
+ args=("example-mcp-server",),
175
+ tool_prefix="local_",
176
+ ),
177
+ ]
178
+ )
179
+ ```
180
+
181
+ `transport="auto"` tries Streamable HTTP, legacy HTTP+SSE, then raw JSON-RPC
182
+ POST compatibility. Standard transports use MCP initialization, version
183
+ negotiation, persistent sessions, and clean shutdown. Duplicate visible tool
184
+ names require `tool_prefix` configuration.
185
+
186
+ HTTP auth objects in `McpServerConfig` use `httpx2.Auth`, matching MCP SDK v2.
187
+ HTTP transports use the operating system trust store; set `SSL_CERT_FILE` or
188
+ `SSL_CERT_DIR` for custom certificate authorities.
189
+
190
+ `BRRRAGENT_MCP_URL` configures one default endpoint. `BRRRAGENT_MCP_URLS`
191
+ accepts comma-separated endpoints.
192
+
193
+ Tool filters accept exact names and case-sensitive shell patterns such as
194
+ `*_search`. Exclusions apply during discovery and execution.
195
+
196
+ ## Public API
197
+
198
+ ```python
199
+ from brrragent import (
200
+ AgentUsage,
201
+ KeyPool,
202
+ McpServerConfig,
203
+ McpToolCaller,
204
+ PromptCacheConfig,
205
+ RateLimitExhausted,
206
+ get_default_caller,
207
+ pick_env_key,
208
+ pick_key,
209
+ run_agent,
210
+ )
211
+ ```
212
+
213
+ ## Requirements
214
+
215
+ - Python 3.12 or newer
216
+ - `httpx>=0.27.1,<1`
217
+ - `httpx2>=2.5,<3`
218
+ - `mcp>=2.0.0,<3`
219
+ - `openai>=2.45.0,<3`
220
+ - `google-genai>=1.0.0,<3` for the `gemini` extra
221
+
222
+ ## License
223
+
224
+ MIT
@@ -0,0 +1,206 @@
1
+ # brrragent
2
+
3
+ `brrragent` is a Python package for running a synchronous model/tool loop. It
4
+ provides a common entry point for several model APIs and converts MCP tools into
5
+ the function-calling formats expected by those APIs.
6
+
7
+ ## Overview
8
+
9
+ Main components:
10
+
11
+ - `run_agent`: executes model responses and tool calls until completion.
12
+ - `McpToolCaller`: discovers, filters, routes, and executes tools across one or
13
+ more MCP servers.
14
+ - `McpServerConfig`: configures Streamable HTTP, legacy HTTP+SSE, stdio, or raw
15
+ JSON-RPC compatibility connections.
16
+ - `KeyPool`: selects API keys and removes rate-limited keys from rotation.
17
+
18
+ Model adapters cover OpenAI, OpenAI-compatible APIs, Google Gemini, OpenRouter,
19
+ and the optional Codex OAuth backend. Structured outputs, retry handling, tool
20
+ result truncation, and provider routing are handled by the package.
21
+
22
+ ## Installation
23
+
24
+ Install from PyPI:
25
+
26
+ ```bash
27
+ uv add brrragent
28
+ ```
29
+
30
+ Gemini support uses an optional dependency:
31
+
32
+ ```bash
33
+ uv add "brrragent[gemini]"
34
+ ```
35
+
36
+ Use `uv pip install` instead when installing into an existing environment.
37
+
38
+ ## Basic use
39
+
40
+ ```python
41
+ from brrragent import McpToolCaller, run_agent
42
+
43
+ with McpToolCaller(endpoint="https://mcp.example.com/mcp") as mcp:
44
+ result = run_agent(
45
+ system_prompt="Follow the request and use available tools when needed.",
46
+ user_prompt="Return a short response.",
47
+ model="openai/gpt-5.5",
48
+ mcp=mcp,
49
+ )
50
+
51
+ print(result)
52
+ ```
53
+
54
+ Provider credentials are read from the caller's environment. Common variables
55
+ are `OPENAI_API_KEY`, `OPENROUTER_API_KEY`, and `GEMINI_API_KEY`.
56
+
57
+ ## Codex OAuth
58
+
59
+ Create brrragent-owned Codex credentials with device login:
60
+
61
+ ```bash
62
+ uv run brrragent-auth login
63
+ ```
64
+
65
+ This writes direct OAuth credentials to `~/.cache/brrragent/codex-auth.json`
66
+ and Codex CLI credentials to `~/.cache/brrragent/codex-home/auth.json`, both
67
+ with `0600` permissions. Default routing does not read shared OpenCode auth or
68
+ `~/.codex/auth.json`.
69
+
70
+ Override locations with `BRRRAGENT_AUTH_PATH`, `BRRRAGENT_CODEX_HOME`, or
71
+ command flags. Existing callers can still use `BRRRAGENT_OPENCODE_AUTH_PATH`
72
+ and explicit `CODEX_HOME` values.
73
+
74
+ Use one independently issued profile per application or worker group to avoid
75
+ refresh-token rotation conflicts:
76
+
77
+ ```bash
78
+ uv run brrragent-auth login --profile content-worker
79
+ BRRRAGENT_PROFILE=content-worker uv run your-application
80
+ ```
81
+
82
+ Prefer per-call selection when one process handles multiple use cases:
83
+
84
+ ```python
85
+ from brrragent import CodexAuthConfig, run_agent
86
+
87
+ result = run_agent(
88
+ system_prompt="Return a concise answer.",
89
+ user_prompt="Summarize this input.",
90
+ model="codex/gpt-5.6-luna:medium",
91
+ codex_auth=CodexAuthConfig(profile="content-worker"),
92
+ )
93
+ ```
94
+
95
+ Explicit paths are also supported with
96
+ `CodexAuthConfig(auth_path="...", codex_home="...")`. Per-call selection is
97
+ context-local, so concurrent use cases do not need to mutate process-wide
98
+ environment variables.
99
+
100
+ Named profiles live under `~/.cache/brrragent/profiles/<profile>/`. Run device
101
+ login separately for every profile; copying an auth file also copies its
102
+ rotating refresh token and does not provide isolation. `BRRRAGENT_PROFILE_ROOT`
103
+ can move the profile directory. A named profile also takes precedence over a
104
+ generic `CODEX_HOME` value.
105
+
106
+ ## Prompt caching
107
+
108
+ Keep reusable tools and system instructions before request-specific content.
109
+ Use one versioned key per stable prompt without user IDs, timestamps, URLs, or
110
+ prompt text:
111
+
112
+ ```python
113
+ from brrragent import PromptCacheConfig
114
+
115
+ cache = PromptCacheConfig(key="workflow:v2", ttl="1h")
116
+ ```
117
+
118
+ Pass `prompt_cache=cache` to `run_agent`. brrragent maps it to supported cache
119
+ keys, explicit breakpoints, provider affinity, and Anthropic cache controls.
120
+ Gemini uses its stable system-first prefix for implicit caching. An optional
121
+ `on_usage` callback receives normalized token and cache usage for each request.
122
+
123
+ ## MCP servers
124
+
125
+ One caller can combine multiple endpoints:
126
+
127
+ ```python
128
+ from brrragent import McpToolCaller
129
+
130
+ mcp = McpToolCaller(
131
+ endpoints=[
132
+ "https://mcp.example.com/one",
133
+ "https://mcp.example.com/two",
134
+ ],
135
+ allowed_tools={"*"},
136
+ excluded_tools={"internal_*"},
137
+ )
138
+ ```
139
+
140
+ Use explicit server configuration to mix transports or prefix tool names:
141
+
142
+ ```python
143
+ from brrragent import McpServerConfig, McpToolCaller
144
+
145
+ mcp = McpToolCaller(
146
+ servers=[
147
+ McpServerConfig(
148
+ name="remote",
149
+ url="https://mcp.example.com/mcp",
150
+ tool_prefix="remote_",
151
+ ),
152
+ McpServerConfig(
153
+ name="local",
154
+ transport="stdio",
155
+ command="uvx",
156
+ args=("example-mcp-server",),
157
+ tool_prefix="local_",
158
+ ),
159
+ ]
160
+ )
161
+ ```
162
+
163
+ `transport="auto"` tries Streamable HTTP, legacy HTTP+SSE, then raw JSON-RPC
164
+ POST compatibility. Standard transports use MCP initialization, version
165
+ negotiation, persistent sessions, and clean shutdown. Duplicate visible tool
166
+ names require `tool_prefix` configuration.
167
+
168
+ HTTP auth objects in `McpServerConfig` use `httpx2.Auth`, matching MCP SDK v2.
169
+ HTTP transports use the operating system trust store; set `SSL_CERT_FILE` or
170
+ `SSL_CERT_DIR` for custom certificate authorities.
171
+
172
+ `BRRRAGENT_MCP_URL` configures one default endpoint. `BRRRAGENT_MCP_URLS`
173
+ accepts comma-separated endpoints.
174
+
175
+ Tool filters accept exact names and case-sensitive shell patterns such as
176
+ `*_search`. Exclusions apply during discovery and execution.
177
+
178
+ ## Public API
179
+
180
+ ```python
181
+ from brrragent import (
182
+ AgentUsage,
183
+ KeyPool,
184
+ McpServerConfig,
185
+ McpToolCaller,
186
+ PromptCacheConfig,
187
+ RateLimitExhausted,
188
+ get_default_caller,
189
+ pick_env_key,
190
+ pick_key,
191
+ run_agent,
192
+ )
193
+ ```
194
+
195
+ ## Requirements
196
+
197
+ - Python 3.12 or newer
198
+ - `httpx>=0.27.1,<1`
199
+ - `httpx2>=2.5,<3`
200
+ - `mcp>=2.0.0,<3`
201
+ - `openai>=2.45.0,<3`
202
+ - `google-genai>=1.0.0,<3` for the `gemini` extra
203
+
204
+ ## License
205
+
206
+ MIT
@@ -0,0 +1,13 @@
1
+ # Security Policy
2
+
3
+ ## Supported versions
4
+
5
+ Security fixes target latest published release.
6
+
7
+ ## Reporting a vulnerability
8
+
9
+ Use [GitHub private vulnerability reporting](https://github.com/bvolpato/brrragent/security/advisories/new).
10
+ Do not open public issue for undisclosed vulnerabilities.
11
+
12
+ Include affected version, impact, reproduction steps, and any suggested fix.
13
+ Redact credentials, access tokens, private prompts, and customer data.
@@ -0,0 +1,70 @@
1
+ [project]
2
+ name = "brrragent"
3
+ version = "0.4.2"
4
+ description = "Reusable agent loop with multi-provider routing and MCP tool calling"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ requires-python = ">=3.12"
9
+ dependencies = [
10
+ "httpx>=0.27.1,<1",
11
+ "httpx2>=2.5,<3",
12
+ "mcp>=2.0.0,<3",
13
+ "openai>=2.45.0,<3",
14
+ ]
15
+
16
+ [project.scripts]
17
+ brrragent-auth = "brrragent.auth_cli:main"
18
+
19
+ [project.optional-dependencies]
20
+ gemini = ["google-genai>=1.0.0,<3"]
21
+
22
+ [project.urls]
23
+ Homepage = "https://github.com/bvolpato/brrragent"
24
+ Repository = "https://github.com/bvolpato/brrragent"
25
+ Issues = "https://github.com/bvolpato/brrragent/issues"
26
+
27
+ [dependency-groups]
28
+ dev = [
29
+ "check-wheel-contents>=0.6,<0.7",
30
+ "pytest>=8.0.0",
31
+ "pytest-cov>=7,<8",
32
+ "ruff>=0.16.1,<0.17",
33
+ "twine>=7.0.0,<8",
34
+ ]
35
+
36
+ [build-system]
37
+ requires = ["hatchling"]
38
+ build-backend = "hatchling.build"
39
+
40
+ [tool.hatch.build.targets.wheel]
41
+ packages = ["src/brrragent"]
42
+
43
+ [tool.hatch.build.targets.sdist]
44
+ exclude = ["/.gitignore"]
45
+ include = [
46
+ "/LICENSE",
47
+ "/README.md",
48
+ "/SECURITY.md",
49
+ "/pyproject.toml",
50
+ "/src",
51
+ "/tests",
52
+ ]
53
+
54
+ [tool.pytest.ini_options]
55
+ addopts = ["--strict-config", "--strict-markers"]
56
+
57
+ [tool.ruff]
58
+ target-version = "py312"
59
+
60
+ [tool.ruff.lint]
61
+ select = ["E4", "E7", "E9", "F", "I", "UP", "B"]
62
+
63
+ [tool.coverage.run]
64
+ branch = true
65
+ source = ["brrragent"]
66
+
67
+ [tool.coverage.report]
68
+ fail_under = 75
69
+ show_missing = true
70
+ skip_covered = true
@@ -0,0 +1,40 @@
1
+ """
2
+ brrragent — Reusable agentic react loop with MCP tool calling.
3
+
4
+ Provides:
5
+ - McpToolCaller: Multi-server MCP bridge (OpenAI + Gemini formats)
6
+ - McpServerConfig: Streamable HTTP, SSE, stdio, or legacy JSON configuration
7
+ - run_agent: Smart-routed agentic loop (Gemini direct, OpenAI direct, Codex OAuth, OpenRouter)
8
+ - get_default_caller: Singleton McpToolCaller instance
9
+ - pick_key / pick_env_key: Handle comma-separated API keys
10
+ - KeyPool: Thread-safe key pool with 429 eviction (works for any provider)
11
+ - RateLimitExhausted: Exception when all keys are rate-limited
12
+
13
+ Backends (importable individually):
14
+ - brrragent.openrouter: OpenRouter/OpenAI-compatible backend
15
+ - brrragent.openai_direct: OpenAI native backend
16
+ - brrragent.codex_oauth: ChatGPT/Codex OAuth backend via isolated auth
17
+ - brrragent.gemini: Google Gemini direct backend
18
+ - brrragent.keys: API key utilities
19
+ """
20
+
21
+ from brrragent.agent import run_agent
22
+ from brrragent.codex_oauth import CodexAuthConfig, codex_auth_context
23
+ from brrragent.keys import KeyPool, RateLimitExhausted, pick_env_key, pick_key
24
+ from brrragent.mcp_tools import McpServerConfig, McpToolCaller, get_default_caller
25
+ from brrragent.prompt_cache import AgentUsage, PromptCacheConfig
26
+
27
+ __all__ = [
28
+ "AgentUsage",
29
+ "CodexAuthConfig",
30
+ "KeyPool",
31
+ "McpServerConfig",
32
+ "McpToolCaller",
33
+ "PromptCacheConfig",
34
+ "RateLimitExhausted",
35
+ "get_default_caller",
36
+ "codex_auth_context",
37
+ "pick_env_key",
38
+ "pick_key",
39
+ "run_agent",
40
+ ]
@@ -0,0 +1,7 @@
1
+ def safe_error_summary(error: BaseException) -> str:
2
+ """Describe an error without copying provider-controlled response text."""
3
+ name = type(error).__name__
4
+ status = getattr(error, "status_code", None)
5
+ if isinstance(status, int):
6
+ return f"{name} (HTTP {status})"
7
+ return name