stirrup 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. stirrup-0.1.0/LICENSE +21 -0
  2. stirrup-0.1.0/PKG-INFO +318 -0
  3. stirrup-0.1.0/README.md +253 -0
  4. stirrup-0.1.0/pyproject.toml +100 -0
  5. stirrup-0.1.0/src/stirrup/__init__.py +76 -0
  6. stirrup-0.1.0/src/stirrup/clients/__init__.py +14 -0
  7. stirrup-0.1.0/src/stirrup/clients/chat_completions_client.py +219 -0
  8. stirrup-0.1.0/src/stirrup/clients/litellm_client.py +141 -0
  9. stirrup-0.1.0/src/stirrup/clients/utils.py +161 -0
  10. stirrup-0.1.0/src/stirrup/constants.py +14 -0
  11. stirrup-0.1.0/src/stirrup/core/__init__.py +1 -0
  12. stirrup-0.1.0/src/stirrup/core/agent.py +1097 -0
  13. stirrup-0.1.0/src/stirrup/core/exceptions.py +7 -0
  14. stirrup-0.1.0/src/stirrup/core/models.py +599 -0
  15. stirrup-0.1.0/src/stirrup/prompts/__init__.py +22 -0
  16. stirrup-0.1.0/src/stirrup/prompts/base_system_prompt.txt +1 -0
  17. stirrup-0.1.0/src/stirrup/prompts/message_summarizer.txt +27 -0
  18. stirrup-0.1.0/src/stirrup/prompts/message_summarizer_bridge.txt +11 -0
  19. stirrup-0.1.0/src/stirrup/py.typed +0 -0
  20. stirrup-0.1.0/src/stirrup/tools/__init__.py +77 -0
  21. stirrup-0.1.0/src/stirrup/tools/calculator.py +32 -0
  22. stirrup-0.1.0/src/stirrup/tools/code_backends/__init__.py +38 -0
  23. stirrup-0.1.0/src/stirrup/tools/code_backends/base.py +454 -0
  24. stirrup-0.1.0/src/stirrup/tools/code_backends/docker.py +752 -0
  25. stirrup-0.1.0/src/stirrup/tools/code_backends/e2b.py +359 -0
  26. stirrup-0.1.0/src/stirrup/tools/code_backends/local.py +481 -0
  27. stirrup-0.1.0/src/stirrup/tools/finish.py +23 -0
  28. stirrup-0.1.0/src/stirrup/tools/mcp.py +500 -0
  29. stirrup-0.1.0/src/stirrup/tools/view_image.py +83 -0
  30. stirrup-0.1.0/src/stirrup/tools/web.py +336 -0
  31. stirrup-0.1.0/src/stirrup/utils/__init__.py +10 -0
  32. stirrup-0.1.0/src/stirrup/utils/logging.py +944 -0
  33. stirrup-0.1.0/src/stirrup/utils/text.py +11 -0
stirrup-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Artificial Analysis
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.
stirrup-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,318 @@
1
+ Metadata-Version: 2.3
2
+ Name: stirrup
3
+ Version: 0.1.0
4
+ Summary: The lightweight foundation for building agents
5
+ Keywords: ai,agent,llm,openai,anthropic,tools,framework
6
+ Author: Artificial Analysis, Inc.
7
+ Author-email: Artificial Analysis, Inc. <info@artificialanalysis.ai>
8
+ License: MIT License
9
+
10
+ Copyright (c) 2025 Artificial Analysis
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
29
+ Classifier: Development Status :: 4 - Beta
30
+ Classifier: Intended Audience :: Developers
31
+ Classifier: License :: OSI Approved :: MIT License
32
+ Classifier: Operating System :: OS Independent
33
+ Classifier: Programming Language :: Python :: 3
34
+ Classifier: Programming Language :: Python :: 3.12
35
+ Classifier: Programming Language :: Python :: 3.13
36
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
37
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
38
+ Classifier: Typing :: Typed
39
+ Requires-Dist: anyio>=4.0.0
40
+ Requires-Dist: filetype>=1.0.0
41
+ Requires-Dist: httpx>=0.20.0
42
+ Requires-Dist: json-schema-to-pydantic>=0.2.0
43
+ Requires-Dist: moviepy>=2.0.0
44
+ Requires-Dist: openai>=1.0.0
45
+ Requires-Dist: pillow>=10.4.0
46
+ Requires-Dist: pydantic>=2.0.0
47
+ Requires-Dist: tenacity>=5.0.0
48
+ Requires-Dist: trafilatura>=1.9.0
49
+ Requires-Dist: stirrup[litellm,e2b,docker,mcp] ; extra == 'all'
50
+ Requires-Dist: docker>=7.0.0 ; extra == 'docker'
51
+ Requires-Dist: python-dotenv>=1.0.0 ; extra == 'docker'
52
+ Requires-Dist: e2b-code-interpreter>=2.3.0 ; extra == 'e2b'
53
+ Requires-Dist: litellm>=1.79.3 ; extra == 'litellm'
54
+ Requires-Dist: mcp>=1.9.0 ; extra == 'mcp'
55
+ Requires-Python: >=3.12
56
+ Project-URL: Documentation, https://artificialanalysis.github.io/Stirrup
57
+ Project-URL: Homepage, https://github.com/ArtificialAnalysis/Stirrup
58
+ Project-URL: Repository, https://github.com/ArtificialAnalysis/Stirrup
59
+ Provides-Extra: all
60
+ Provides-Extra: docker
61
+ Provides-Extra: e2b
62
+ Provides-Extra: litellm
63
+ Provides-Extra: mcp
64
+ Description-Content-Type: text/markdown
65
+
66
+ <div align="center">
67
+ <a href="">
68
+ <picture>
69
+ <img alt="Stirrup" src="assets/stirrup-banner.png" width="700">
70
+ </picture>
71
+ </a>
72
+ <br></br>
73
+ <h1>The lightweight foundation for building agents</h1>
74
+ <br>
75
+ </div>
76
+
77
+
78
+ <p align="center">
79
+ <a href="https://pypi.python.org/pypi/stirrup"><img src="https://img.shields.io/pypi/v/stirrup" alt="PyPI version" /></a>&nbsp;<!--
80
+ --><a href="https://github.com/ArtificialAnalysis/Stirrup/blob/main/LICENSE"><img src="https://img.shields.io/github/license/ArtificialAnalysis/Stirrup" alt="License" /></a>&nbsp;<!--
81
+ --><a href="https://artificialanalysis.github.io/Stirrup"><img src="https://img.shields.io/badge/MkDocs-4F46E5?logo=materialformkdocs&logoColor=fff" alt="MkDocs" /></a>
82
+ </p>
83
+
84
+
85
+ Stirrup is a lightweight framework, or starting point template, for building agents. It differs from other agent frameworks by:
86
+
87
+ - **Working with the model, not against it:** Stirrup gets out of the way and lets the model choose its own approach to completing tasks (similar to Claude Code). Many frameworks impose rigid workflows that can degrade results.
88
+ - **Best practices and tools built-in:** We analyzed the leading agents (Claude Code, Codex, and others) to understand and incorporate best practices relating to topics like context management and foundational tools (e.g., code execution).
89
+ - **Fully customizable:** Use Stirrup as a package or as a starting template to build your own fully customized agents.
90
+
91
+ ## Features
92
+
93
+ - **Essential tools built-in:**
94
+ - Online search / web browsing
95
+ - Code execution (local, Docker container, E2B sandbox)
96
+ - MCP client
97
+ - Document input and output
98
+ - **Flexible tool execution:** A generic `Tool` class allows easy tool definition and extension
99
+ - **Context management:** Automatically summarizes conversation history when approaching context limits
100
+ - **Flexible provider support:** Pre-built support for OpenAI-compatible APIs and LiteLLM, or bring your own client
101
+ - **Multimodal support:** Process images, video, and audio with automatic format conversion
102
+
103
+ ## Installation
104
+
105
+ ```bash
106
+ # Core framework
107
+ uv pip install stirrup # or: uv add stirrup
108
+
109
+ # With all optional components
110
+ uv pip install stirrup[all] # or: uv add stirrup[all]
111
+
112
+ # Individual extras
113
+ uv pip install stirrup[litellm] # or: uv add stirrup[litellm]
114
+ uv pip install stirrup[docker] # or: uv add stirrup[docker]
115
+ uv pip install stirrup[e2b] # or: uv add stirrup[e2b]
116
+ uv pip install stirrup[mcp] # or: uv add stirrup[mcp]
117
+ ```
118
+
119
+ ## Quick Start
120
+
121
+ ```python
122
+ import asyncio
123
+
124
+ from stirrup import Agent
125
+ from stirrup.clients.chat_completions_client import ChatCompletionsClient
126
+
127
+
128
+ async def main() -> None:
129
+ """Run an agent that searches the web and creates a chart."""
130
+
131
+ # Create client using ChatCompletionsClient
132
+ # Automatically uses OPENROUTER_API_KEY environment variable
133
+ client = ChatCompletionsClient(
134
+ base_url="https://openrouter.ai/api/v1",
135
+ model="anthropic/claude-sonnet-4.5",
136
+ )
137
+
138
+ # As no tools are provided, the agent will use the default tools, which consist of:
139
+ # - Web tools (web search and web fetching, note web search requires BRAVE_API_KEY)
140
+ # - Local code execution tool (to execute shell commands)
141
+ agent = Agent(client=client, name="agent", max_turns=15)
142
+
143
+ # Run with session context - handles tool lifecycle, logging and file outputs
144
+ async with agent.session(output_dir="./output/getting_started_example") as session:
145
+ finish_params, history, metadata = await session.run(
146
+ """
147
+ What is the population of Australia over the last 3 years? Search the web to find out and create a
148
+ simple chart using matplotlib showing the current population per year."""
149
+ )
150
+
151
+ print("Finish params: ", finish_params)
152
+ print("History: ", history)
153
+ print("Metadata: ", metadata)
154
+
155
+
156
+ if __name__ == "__main__":
157
+ asyncio.run(main())
158
+ ```
159
+
160
+ > **Note:** This example uses OpenRouter. Set `OPENROUTER_API_KEY` in your environment before running. Web search requires a `BRAVE_API_KEY`. The agent will still work without it, but web search will be unavailable.
161
+
162
+ ## How It Works
163
+
164
+ - **`Agent`** - Configures and runs the agent loop until a finish tool is called or max turns reached
165
+ - **`session()`** - Context manager that sets up tools, manages files, and handles cleanup
166
+ - **`Tool`** - Define tools with Pydantic parameters
167
+ - **`ToolProvider`** - Manage tools that require lifecycle (connections, temp directories, etc.)
168
+ - **`DEFAULT_TOOLS`** - Standard tools included by default: code execution and web tools
169
+
170
+ ## Using Other LLM Providers
171
+
172
+ For non-OpenAI providers, change the base URL of the `ChatCompletionsClient`, use the `LiteLLMClient` (requires installation of optional `stirrup[litellm]` dependencies), or create your own client.
173
+
174
+ ### OpenAI-Compatible APIs
175
+
176
+ ```python
177
+ # Create client using Deepseek's OpenAI-compatible endpoint
178
+ client = ChatCompletionsClient(
179
+ base_url="https://api.deepseek.com",
180
+ model="deepseek-chat", # or "deepseek-reasoner" for R1
181
+ api_key=os.environ["DEEPSEEK_API_KEY"],
182
+ )
183
+
184
+ agent = Agent(client=client, name="deepseek_agent")
185
+ ```
186
+
187
+ ### LiteLLM (Anthropic, Google, etc.)
188
+
189
+ ```python
190
+ # Create LiteLLM client for Anthropic Claude
191
+ # See https://docs.litellm.ai/docs/providers for all supported providers
192
+ client = LiteLLMClient(
193
+ model_slug="anthropic/claude-sonnet-4-5",
194
+ max_tokens=200_000,
195
+ )
196
+
197
+ # Pass client to Agent - model info comes from client.model_slug
198
+ agent = Agent(
199
+ client=client,
200
+ name="claude_agent",
201
+ )
202
+ ```
203
+
204
+ See [LiteLLM Example](https://artificialanalysis.github.io/Stirrup/examples/#litellm-multi-provider-support) or [Deepseek Example](https://artificialanalysis.github.io/Stirrup/examples/#openai-compatible-apis-deepseek-vllm-ollama) for complete examples.
205
+
206
+ ## Default Tools
207
+
208
+ When you create an `Agent` without specifying tools, it uses `DEFAULT_TOOLS`:
209
+
210
+ | Tool Provider | Tools Provided | Description |
211
+ | --------------------------- | ------------------------- | ------------------------------------------------------------ |
212
+ | `LocalCodeExecToolProvider` | `code_exec` | Execute shell commands in an isolated temp directory |
213
+ | `WebToolProvider` | `web_fetch`, `web_search` | Fetch web pages and search (search requires `BRAVE_API_KEY`) |
214
+
215
+ ## Extending with Pre-Built Tools
216
+
217
+ ```python
218
+ import asyncio
219
+
220
+ from stirrup import Agent
221
+ from stirrup.clients.chat_completions_client import ChatCompletionsClient
222
+ from stirrup.tools import CALCULATOR_TOOL, DEFAULT_TOOLS
223
+
224
+ # Create client for OpenRouter
225
+ client = ChatCompletionsClient(
226
+ base_url="https://openrouter.ai/api/v1",
227
+ model="anthropic/claude-sonnet-4.5",
228
+ )
229
+
230
+ # Create agent with default tools + calculator tool
231
+ agent = Agent(
232
+ client=client,
233
+ name="web_calculator_agent",
234
+ tools=[*DEFAULT_TOOLS, CALCULATOR_TOOL],
235
+ )
236
+ ```
237
+
238
+ ## Defining Custom Tools
239
+
240
+ ```python
241
+ from pydantic import BaseModel, Field
242
+
243
+ from stirrup import Agent, Tool, ToolResult, ToolUseCountMetadata
244
+ from stirrup.clients.chat_completions_client import ChatCompletionsClient
245
+ from stirrup.tools import DEFAULT_TOOLS
246
+
247
+
248
+ class GreetParams(BaseModel):
249
+ """Parameters for the greet tool."""
250
+
251
+ name: str = Field(description="Name of the person to greet")
252
+ formal: bool = Field(default=False, description="Use formal greeting")
253
+
254
+
255
+ def greet(params: GreetParams) -> ToolResult[ToolUseCountMetadata]:
256
+ greeting = f"Good day, {params.name}." if params.formal else f"Hey {params.name}!"
257
+
258
+ return ToolResult(
259
+ content=greeting,
260
+ metadata=ToolUseCountMetadata(),
261
+ )
262
+
263
+
264
+ GREET_TOOL = Tool(
265
+ name="greet",
266
+ description="Greet someone by name",
267
+ parameters=GreetParams,
268
+ executor=greet,
269
+ )
270
+
271
+ # Create client for OpenRouter
272
+ client = ChatCompletionsClient(
273
+ base_url="https://openrouter.ai/api/v1",
274
+ model="anthropic/claude-sonnet-4.5",
275
+ )
276
+
277
+ # Add custom tool to default tools
278
+ agent = Agent(
279
+ client=client,
280
+ name="greeting_agent",
281
+ tools=[*DEFAULT_TOOLS, GREET_TOOL],
282
+ )
283
+ ```
284
+
285
+ ## Next Steps
286
+
287
+ - [Getting Started](https://artificialanalysis.github.io/Stirrup/getting-started/) - Installation and first agent tutorial
288
+ - [Core Concepts](https://artificialanalysis.github.io/Stirrup/concepts/) - Understand Agent, Tools, and Sessions
289
+ - [Examples](https://artificialanalysis.github.io/Stirrup/examples/) - Working examples for common patterns
290
+ - [Creating Tools](https://artificialanalysis.github.io/Stirrup/guides/tools/) - Build your own tools
291
+
292
+ ## Documentation
293
+
294
+ Full documentation: [artificialanalysis.github.io/Stirrup](https://artificialanalysis.github.io/Stirrup)
295
+
296
+ Build and serve locally:
297
+
298
+ ```bash
299
+ uv run mkdocs serve
300
+ ```
301
+
302
+ ## Development
303
+
304
+ ```bash
305
+ # Format and lint code
306
+ uv run ruff format
307
+ uv run ruff check
308
+
309
+ # Type check
310
+ uv run ty check
311
+
312
+ # Run tests
313
+ uv run pytest tests
314
+ ```
315
+
316
+ ## License
317
+
318
+ Licensed under the [MIT LICENSE](LICENSE).
@@ -0,0 +1,253 @@
1
+ <div align="center">
2
+ <a href="">
3
+ <picture>
4
+ <img alt="Stirrup" src="assets/stirrup-banner.png" width="700">
5
+ </picture>
6
+ </a>
7
+ <br></br>
8
+ <h1>The lightweight foundation for building agents</h1>
9
+ <br>
10
+ </div>
11
+
12
+
13
+ <p align="center">
14
+ <a href="https://pypi.python.org/pypi/stirrup"><img src="https://img.shields.io/pypi/v/stirrup" alt="PyPI version" /></a>&nbsp;<!--
15
+ --><a href="https://github.com/ArtificialAnalysis/Stirrup/blob/main/LICENSE"><img src="https://img.shields.io/github/license/ArtificialAnalysis/Stirrup" alt="License" /></a>&nbsp;<!--
16
+ --><a href="https://artificialanalysis.github.io/Stirrup"><img src="https://img.shields.io/badge/MkDocs-4F46E5?logo=materialformkdocs&logoColor=fff" alt="MkDocs" /></a>
17
+ </p>
18
+
19
+
20
+ Stirrup is a lightweight framework, or starting point template, for building agents. It differs from other agent frameworks by:
21
+
22
+ - **Working with the model, not against it:** Stirrup gets out of the way and lets the model choose its own approach to completing tasks (similar to Claude Code). Many frameworks impose rigid workflows that can degrade results.
23
+ - **Best practices and tools built-in:** We analyzed the leading agents (Claude Code, Codex, and others) to understand and incorporate best practices relating to topics like context management and foundational tools (e.g., code execution).
24
+ - **Fully customizable:** Use Stirrup as a package or as a starting template to build your own fully customized agents.
25
+
26
+ ## Features
27
+
28
+ - **Essential tools built-in:**
29
+ - Online search / web browsing
30
+ - Code execution (local, Docker container, E2B sandbox)
31
+ - MCP client
32
+ - Document input and output
33
+ - **Flexible tool execution:** A generic `Tool` class allows easy tool definition and extension
34
+ - **Context management:** Automatically summarizes conversation history when approaching context limits
35
+ - **Flexible provider support:** Pre-built support for OpenAI-compatible APIs and LiteLLM, or bring your own client
36
+ - **Multimodal support:** Process images, video, and audio with automatic format conversion
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ # Core framework
42
+ uv pip install stirrup # or: uv add stirrup
43
+
44
+ # With all optional components
45
+ uv pip install stirrup[all] # or: uv add stirrup[all]
46
+
47
+ # Individual extras
48
+ uv pip install stirrup[litellm] # or: uv add stirrup[litellm]
49
+ uv pip install stirrup[docker] # or: uv add stirrup[docker]
50
+ uv pip install stirrup[e2b] # or: uv add stirrup[e2b]
51
+ uv pip install stirrup[mcp] # or: uv add stirrup[mcp]
52
+ ```
53
+
54
+ ## Quick Start
55
+
56
+ ```python
57
+ import asyncio
58
+
59
+ from stirrup import Agent
60
+ from stirrup.clients.chat_completions_client import ChatCompletionsClient
61
+
62
+
63
+ async def main() -> None:
64
+ """Run an agent that searches the web and creates a chart."""
65
+
66
+ # Create client using ChatCompletionsClient
67
+ # Automatically uses OPENROUTER_API_KEY environment variable
68
+ client = ChatCompletionsClient(
69
+ base_url="https://openrouter.ai/api/v1",
70
+ model="anthropic/claude-sonnet-4.5",
71
+ )
72
+
73
+ # As no tools are provided, the agent will use the default tools, which consist of:
74
+ # - Web tools (web search and web fetching, note web search requires BRAVE_API_KEY)
75
+ # - Local code execution tool (to execute shell commands)
76
+ agent = Agent(client=client, name="agent", max_turns=15)
77
+
78
+ # Run with session context - handles tool lifecycle, logging and file outputs
79
+ async with agent.session(output_dir="./output/getting_started_example") as session:
80
+ finish_params, history, metadata = await session.run(
81
+ """
82
+ What is the population of Australia over the last 3 years? Search the web to find out and create a
83
+ simple chart using matplotlib showing the current population per year."""
84
+ )
85
+
86
+ print("Finish params: ", finish_params)
87
+ print("History: ", history)
88
+ print("Metadata: ", metadata)
89
+
90
+
91
+ if __name__ == "__main__":
92
+ asyncio.run(main())
93
+ ```
94
+
95
+ > **Note:** This example uses OpenRouter. Set `OPENROUTER_API_KEY` in your environment before running. Web search requires a `BRAVE_API_KEY`. The agent will still work without it, but web search will be unavailable.
96
+
97
+ ## How It Works
98
+
99
+ - **`Agent`** - Configures and runs the agent loop until a finish tool is called or max turns reached
100
+ - **`session()`** - Context manager that sets up tools, manages files, and handles cleanup
101
+ - **`Tool`** - Define tools with Pydantic parameters
102
+ - **`ToolProvider`** - Manage tools that require lifecycle (connections, temp directories, etc.)
103
+ - **`DEFAULT_TOOLS`** - Standard tools included by default: code execution and web tools
104
+
105
+ ## Using Other LLM Providers
106
+
107
+ For non-OpenAI providers, change the base URL of the `ChatCompletionsClient`, use the `LiteLLMClient` (requires installation of optional `stirrup[litellm]` dependencies), or create your own client.
108
+
109
+ ### OpenAI-Compatible APIs
110
+
111
+ ```python
112
+ # Create client using Deepseek's OpenAI-compatible endpoint
113
+ client = ChatCompletionsClient(
114
+ base_url="https://api.deepseek.com",
115
+ model="deepseek-chat", # or "deepseek-reasoner" for R1
116
+ api_key=os.environ["DEEPSEEK_API_KEY"],
117
+ )
118
+
119
+ agent = Agent(client=client, name="deepseek_agent")
120
+ ```
121
+
122
+ ### LiteLLM (Anthropic, Google, etc.)
123
+
124
+ ```python
125
+ # Create LiteLLM client for Anthropic Claude
126
+ # See https://docs.litellm.ai/docs/providers for all supported providers
127
+ client = LiteLLMClient(
128
+ model_slug="anthropic/claude-sonnet-4-5",
129
+ max_tokens=200_000,
130
+ )
131
+
132
+ # Pass client to Agent - model info comes from client.model_slug
133
+ agent = Agent(
134
+ client=client,
135
+ name="claude_agent",
136
+ )
137
+ ```
138
+
139
+ See [LiteLLM Example](https://artificialanalysis.github.io/Stirrup/examples/#litellm-multi-provider-support) or [Deepseek Example](https://artificialanalysis.github.io/Stirrup/examples/#openai-compatible-apis-deepseek-vllm-ollama) for complete examples.
140
+
141
+ ## Default Tools
142
+
143
+ When you create an `Agent` without specifying tools, it uses `DEFAULT_TOOLS`:
144
+
145
+ | Tool Provider | Tools Provided | Description |
146
+ | --------------------------- | ------------------------- | ------------------------------------------------------------ |
147
+ | `LocalCodeExecToolProvider` | `code_exec` | Execute shell commands in an isolated temp directory |
148
+ | `WebToolProvider` | `web_fetch`, `web_search` | Fetch web pages and search (search requires `BRAVE_API_KEY`) |
149
+
150
+ ## Extending with Pre-Built Tools
151
+
152
+ ```python
153
+ import asyncio
154
+
155
+ from stirrup import Agent
156
+ from stirrup.clients.chat_completions_client import ChatCompletionsClient
157
+ from stirrup.tools import CALCULATOR_TOOL, DEFAULT_TOOLS
158
+
159
+ # Create client for OpenRouter
160
+ client = ChatCompletionsClient(
161
+ base_url="https://openrouter.ai/api/v1",
162
+ model="anthropic/claude-sonnet-4.5",
163
+ )
164
+
165
+ # Create agent with default tools + calculator tool
166
+ agent = Agent(
167
+ client=client,
168
+ name="web_calculator_agent",
169
+ tools=[*DEFAULT_TOOLS, CALCULATOR_TOOL],
170
+ )
171
+ ```
172
+
173
+ ## Defining Custom Tools
174
+
175
+ ```python
176
+ from pydantic import BaseModel, Field
177
+
178
+ from stirrup import Agent, Tool, ToolResult, ToolUseCountMetadata
179
+ from stirrup.clients.chat_completions_client import ChatCompletionsClient
180
+ from stirrup.tools import DEFAULT_TOOLS
181
+
182
+
183
+ class GreetParams(BaseModel):
184
+ """Parameters for the greet tool."""
185
+
186
+ name: str = Field(description="Name of the person to greet")
187
+ formal: bool = Field(default=False, description="Use formal greeting")
188
+
189
+
190
+ def greet(params: GreetParams) -> ToolResult[ToolUseCountMetadata]:
191
+ greeting = f"Good day, {params.name}." if params.formal else f"Hey {params.name}!"
192
+
193
+ return ToolResult(
194
+ content=greeting,
195
+ metadata=ToolUseCountMetadata(),
196
+ )
197
+
198
+
199
+ GREET_TOOL = Tool(
200
+ name="greet",
201
+ description="Greet someone by name",
202
+ parameters=GreetParams,
203
+ executor=greet,
204
+ )
205
+
206
+ # Create client for OpenRouter
207
+ client = ChatCompletionsClient(
208
+ base_url="https://openrouter.ai/api/v1",
209
+ model="anthropic/claude-sonnet-4.5",
210
+ )
211
+
212
+ # Add custom tool to default tools
213
+ agent = Agent(
214
+ client=client,
215
+ name="greeting_agent",
216
+ tools=[*DEFAULT_TOOLS, GREET_TOOL],
217
+ )
218
+ ```
219
+
220
+ ## Next Steps
221
+
222
+ - [Getting Started](https://artificialanalysis.github.io/Stirrup/getting-started/) - Installation and first agent tutorial
223
+ - [Core Concepts](https://artificialanalysis.github.io/Stirrup/concepts/) - Understand Agent, Tools, and Sessions
224
+ - [Examples](https://artificialanalysis.github.io/Stirrup/examples/) - Working examples for common patterns
225
+ - [Creating Tools](https://artificialanalysis.github.io/Stirrup/guides/tools/) - Build your own tools
226
+
227
+ ## Documentation
228
+
229
+ Full documentation: [artificialanalysis.github.io/Stirrup](https://artificialanalysis.github.io/Stirrup)
230
+
231
+ Build and serve locally:
232
+
233
+ ```bash
234
+ uv run mkdocs serve
235
+ ```
236
+
237
+ ## Development
238
+
239
+ ```bash
240
+ # Format and lint code
241
+ uv run ruff format
242
+ uv run ruff check
243
+
244
+ # Type check
245
+ uv run ty check
246
+
247
+ # Run tests
248
+ uv run pytest tests
249
+ ```
250
+
251
+ ## License
252
+
253
+ Licensed under the [MIT LICENSE](LICENSE).
@@ -0,0 +1,100 @@
1
+ [project]
2
+ name = "stirrup"
3
+ version = "0.1.0"
4
+ description = "The lightweight foundation for building agents"
5
+ readme = "README.md"
6
+ license = { file = "LICENSE" }
7
+ authors = [
8
+ { name = "Artificial Analysis, Inc.", email = "info@artificialanalysis.ai" },
9
+
10
+ ]
11
+ requires-python = ">=3.12"
12
+ keywords = ["ai", "agent", "llm", "openai", "anthropic", "tools", "framework"]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
22
+ "Topic :: Software Development :: Libraries :: Python Modules",
23
+ "Typing :: Typed",
24
+ ]
25
+ dependencies = [
26
+ "anyio>=4.0.0",
27
+ "filetype>=1.0.0",
28
+ "httpx>=0.20.0",
29
+ "json-schema-to-pydantic>=0.2.0",
30
+ "moviepy>=2.0.0",
31
+ "openai>=1.0.0",
32
+ "pillow>=10.4.0",
33
+ "pydantic>=2.0.0",
34
+ "tenacity>=5.0.0",
35
+ "trafilatura>=1.9.0",
36
+ ]
37
+
38
+ [project.optional-dependencies]
39
+ litellm = ["litellm>=1.79.3"]
40
+ e2b = ["e2b-code-interpreter>=2.3.0"]
41
+ docker = ["docker>=7.0.0", "python-dotenv>=1.0.0"]
42
+ mcp = ["mcp>=1.9.0"]
43
+ all = ["stirrup[litellm,e2b,docker,mcp]"]
44
+
45
+ [project.urls]
46
+ Homepage = "https://github.com/ArtificialAnalysis/Stirrup"
47
+ Documentation = "https://artificialanalysis.github.io/Stirrup"
48
+ Repository = "https://github.com/ArtificialAnalysis/Stirrup"
49
+
50
+ [build-system]
51
+ requires = ["uv_build>=0.9.3,<0.10.0"]
52
+ build-backend = "uv_build"
53
+
54
+ [dependency-groups]
55
+ dev = [
56
+ "griffe-pydantic>=1.1.8",
57
+ "mkdocs-material>=9.7.0",
58
+ "mkdocstrings>=0.30.1",
59
+ "mkdocstrings-python>=1.19.0",
60
+ "pytest>=9.0.0",
61
+ "rich>=14.2.0",
62
+ "ruff>=0.14.4",
63
+ "ty>=0.0.1a32",
64
+ ]
65
+
66
+ [tool.pytest.ini_options]
67
+ addopts = ["--strict-markers"]
68
+ anyio_mode = "auto"
69
+ markers = [
70
+ "docker: marks tests as requiring the docker package (deselect with '-m \"not docker\"')",
71
+ "e2b: marks tests as requiring the e2b package (deselect with '-m \"not e2b\"')",
72
+ ]
73
+
74
+ [tool.ruff]
75
+ line-length = 120
76
+ fix = true
77
+ show-fixes = true
78
+
79
+ [tool.ruff.lint]
80
+ select = [
81
+ "F",
82
+ "E",
83
+ "W",
84
+ "ANN",
85
+ "N",
86
+ "B",
87
+ "I",
88
+ "ARG",
89
+ "SLF",
90
+ "UP",
91
+ "PERF",
92
+ "RUF",
93
+ "SIM",
94
+ "S113",
95
+ "ASYNC",
96
+ ]
97
+ ignore = [
98
+ "E501", # line too long
99
+ "ASYNC109", # timeout param in async fn - false positive when passing to external APIs
100
+ ]