stirrup 0.1.0__py3-none-any.whl

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.
stirrup/utils/text.py ADDED
@@ -0,0 +1,11 @@
1
+ def truncate_msg(msg: str, max_length: int) -> str:
2
+ """Truncate long messages by removing middle portion, keeping start and end with ellipsis indicator."""
3
+ msg_len = len(msg)
4
+ if msg_len <= max_length:
5
+ return msg
6
+ else:
7
+ return (
8
+ msg[: max_length // 2]
9
+ + f"\n... This content has been truncated from an original {msg_len} characters to stay below {max_length} characters ...\n"
10
+ + msg[-max_length // 2 :]
11
+ )
@@ -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,32 @@
1
+ stirrup/__init__.py,sha256=Dol0Uui-gBslpplfEUrgxkqqJWr1ucZJej5finNvCeI,1869
2
+ stirrup/clients/__init__.py,sha256=oO8VMHmbUhoxFyC0JLQs_kUFNSRlvTj5xz0FgzBb98E,405
3
+ stirrup/clients/chat_completions_client.py,sha256=p_EeXqRuo6mWnzgMhy22SWdHE6_OXIRyABvCKgHdnu4,7586
4
+ stirrup/clients/litellm_client.py,sha256=J-HDv7ZZTkNYC-aeSNyd7xTDd_5r8DEeXOPz9eQMC7A,4985
5
+ stirrup/clients/utils.py,sha256=Yyeh6unQSvqgDTDhjpD5DoRu_wP_nWfsNv9DGXQwgo8,5452
6
+ stirrup/constants.py,sha256=h3NzsePJ4FKpImTpV5xtFeJarKb67jR_6n89tNOkQYs,523
7
+ stirrup/core/__init__.py,sha256=ReBVl7B9h_FNkZ77vCx2xlfuK1JuQ0yTSXrEgc4tONU,39
8
+ stirrup/core/agent.py,sha256=8nHlcyh5Ji3_vXSH6dPbKvFCV7Q7cmnJcSaj0Sy-zto,49158
9
+ stirrup/core/exceptions.py,sha256=CzLVAi7Ns-t9BWSkqQUCB7ypVHAesV2s4a09-i0NXyQ,213
10
+ stirrup/core/models.py,sha256=ONiCFC9NcoeArX4629n9s5FNLi02ynqZRiCxJbzhr1Q,21061
11
+ stirrup/prompts/__init__.py,sha256=e4bpTktBaFPuO_bIW5DelGNWtT6_NIUqnD2lRv8n89I,796
12
+ stirrup/prompts/base_system_prompt.txt,sha256=KZ2_JhJ91u4oMqRZvhuAp99nb6ZkXkJdVbIRN6drVME,348
13
+ stirrup/prompts/message_summarizer.txt,sha256=uQoTxreMuC42rTGSZmoH1Dnj06WrEQb0gLkDvVMhosQ,1173
14
+ stirrup/prompts/message_summarizer_bridge.txt,sha256=sWbfnHtI6RWemBIyQsnqHMGpnU-E6FTbfUC6rvkEHLY,372
15
+ stirrup/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ stirrup/tools/__init__.py,sha256=ohyeMvXb6oURiAyoHi0VmC9ksZSRyGleT341VNzHCy4,2714
17
+ stirrup/tools/calculator.py,sha256=JkuGmGZJtaKbC4vHVrIph4aTjlGcFMhhv5MB1ntqgv4,1278
18
+ stirrup/tools/code_backends/__init__.py,sha256=O3Rs76r0YcQ27voTrx_zuhIEFawK3b1TQdKi70MORG8,987
19
+ stirrup/tools/code_backends/base.py,sha256=Nx0tTDX4GKoBWQK2F953vSsFgWCcOd_1WNtYCA4FG4o,17021
20
+ stirrup/tools/code_backends/docker.py,sha256=xtQmAwGkXc9jKlrN2ydYn66HpQ8cQW1b-4bTmN87S4U,29616
21
+ stirrup/tools/code_backends/e2b.py,sha256=1V52m1pQ4uBjGRL9iBeJik2beWURLupODCFtAtng9sE,13696
22
+ stirrup/tools/code_backends/local.py,sha256=6KOn-4aoySHVElitqWX5yN_WDrpJkQlsgSif2q0hYi8,18903
23
+ stirrup/tools/finish.py,sha256=K_NxwOwdvncT2QTua2A_8lZ9MwK4WQQ5FL2gdUrE29c,936
24
+ stirrup/tools/mcp.py,sha256=4wWYae95y8Bs7e36hHwnxRfVVj0PABrsRStw492lLaw,18749
25
+ stirrup/tools/view_image.py,sha256=zazCpZMtLOD6lplLPYGNQ8JeYfc0oUDJoUUyVAp3AMU,3126
26
+ stirrup/tools/web.py,sha256=2yfBJsu8GgFI7Oh1dFlXwNaXth6WQfGpbJFU-rV-yuI,12261
27
+ stirrup/utils/__init__.py,sha256=4kcuExrphSXqgxRgu1q8_Z6Rrb9aAZpIo4Xq4S9Twuk,230
28
+ stirrup/utils/logging.py,sha256=3Li6MhjLJWwaXHDZ06EjgW42XDL6V3ZDt9rccV_ZYZ4,34292
29
+ stirrup/utils/text.py,sha256=3lGlcXFzQ-Mclsbu7wJciG3CcHvQ_Sk98tqOZxYLlGw,479
30
+ stirrup-0.1.0.dist-info/WHEEL,sha256=xDCZ-UyfvkGuEHPeI7BcJzYKIZzdqN8A8o1M5Om8IyA,79
31
+ stirrup-0.1.0.dist-info/METADATA,sha256=dUTUx3W620GhVbZBqXuaI6nwR-4Q6vLmfiE_4BE1S6A,12003
32
+ stirrup-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.9.17
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any