agentropic 0.2.2__tar.gz → 0.3.1__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 (29) hide show
  1. {agentropic-0.2.2 → agentropic-0.3.1}/.gitignore +9 -9
  2. agentropic-0.3.1/LICENSE +21 -0
  3. agentropic-0.3.1/PKG-INFO +147 -0
  4. {agentropic-0.2.2 → agentropic-0.3.1}/README.md +35 -5
  5. {agentropic-0.2.2 → agentropic-0.3.1}/examples/basic_agent.py +10 -2
  6. agentropic-0.3.1/old_022/agentropic-0.2.2-py3-none-any.whl +0 -0
  7. agentropic-0.3.1/old_022/src/agentropic-0.2.2.dist-info/RECORD +12 -0
  8. agentropic-0.3.1/old_022/src/agentropic-0.2.2.dist-info/WHEEL +4 -0
  9. {agentropic-0.2.2 → agentropic-0.3.1}/pyproject.toml +36 -33
  10. agentropic-0.3.1/src/agentropic/__init__.py +30 -0
  11. agentropic-0.3.1/src/agentropic/agent.py +488 -0
  12. agentropic-0.3.1/src/agentropic/llm.py +148 -0
  13. agentropic-0.3.1/src/agentropic/mcp/__init__.py +3 -0
  14. agentropic-0.3.1/src/agentropic/mcp/client.py +169 -0
  15. agentropic-0.3.1/src/agentropic/memory.py +23 -0
  16. agentropic-0.3.1/src/agentropic/tools.py +192 -0
  17. agentropic-0.3.1/src/agentropic/tracing.py +327 -0
  18. {agentropic-0.2.2 → agentropic-0.3.1}/tests/test_agent.py +40 -40
  19. agentropic-0.3.1/tests/test_streaming.py +124 -0
  20. {agentropic-0.2.2 → agentropic-0.3.1/old_022}/src/agentropic/__init__.py +0 -0
  21. {agentropic-0.2.2 → agentropic-0.3.1/old_022}/src/agentropic/agent.py +0 -0
  22. {agentropic-0.2.2 → agentropic-0.3.1/old_022}/src/agentropic/llm.py +0 -0
  23. {agentropic-0.2.2 → agentropic-0.3.1/old_022}/src/agentropic/mcp/__init__.py +0 -0
  24. {agentropic-0.2.2 → agentropic-0.3.1/old_022}/src/agentropic/mcp/client.py +0 -0
  25. {agentropic-0.2.2 → agentropic-0.3.1/old_022}/src/agentropic/memory.py +0 -0
  26. {agentropic-0.2.2 → agentropic-0.3.1/old_022}/src/agentropic/tools.py +0 -0
  27. {agentropic-0.2.2 → agentropic-0.3.1/old_022}/src/agentropic/tracing.py +0 -0
  28. /agentropic-0.2.2/PKG-INFO → /agentropic-0.3.1/old_022/src/agentropic-0.2.2.dist-info/METADATA +0 -0
  29. {agentropic-0.2.2 → agentropic-0.3.1/old_022/src/agentropic-0.2.2.dist-info/licenses}/LICENSE +0 -0
@@ -1,9 +1,9 @@
1
- __pycache__/
2
- *.pyc
3
- .venv/
4
- venv/
5
- dist/
6
- build/
7
- *.egg-info/
8
- .pytest_cache/
9
- .env
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ venv/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .pytest_cache/
9
+ .env
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Your Name
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,147 @@
1
+ Metadata-Version: 2.5
2
+ Name: agentropic
3
+ Version: 0.3.1
4
+ Summary: A minimal, hackable agentic framework: LLM calls via litellm, @tool-decorated functions, and MCP server support.
5
+ Author-email: Vishal Tiwari <vishaltiwari.up2019@gmail.com>
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Keywords: agent,agents,ai,litellm,llm,mcp,tools
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
+ Requires-Python: >=3.9
16
+ Requires-Dist: litellm>=1.40.0
17
+ Requires-Dist: mcp>=1.0.0; python_version >= '3.10'
18
+ Requires-Dist: pydantic>=2.0.0
19
+ Provides-Extra: dev
20
+ Requires-Dist: build; extra == 'dev'
21
+ Requires-Dist: pytest; extra == 'dev'
22
+ Requires-Dist: pytest-asyncio; extra == 'dev'
23
+ Requires-Dist: twine; extra == 'dev'
24
+ Provides-Extra: mcp
25
+ Requires-Dist: mcp>=1.0.0; extra == 'mcp'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # agentropic
29
+
30
+ A small, hackable agentic framework in ~500 lines you fully own and can read
31
+ top to bottom in an afternoon:
32
+
33
+ - **LLM calls** go through [litellm](https://github.com/BerriAI/litellm), so you get
34
+ every provider (OpenAI, Anthropic, Gemini, Ollama, Bedrock, 100+ more) for free.
35
+ - **`@tool` decorator** turns any python function into an LLM-callable tool with
36
+ an auto-generated JSON schema (from type hints + docstring).
37
+ - **MCP support** — connect to any Model Context Protocol server (stdio or SSE)
38
+ and its tools show up in your agent automatically, indistinguishable from
39
+ local tools.
40
+ - **Agent loop** handles the call → tool-call → tool-result → call cycle for you,
41
+ with memory, streaming, and a max-iteration safety valve.
42
+
43
+ ## Install (once published)
44
+
45
+ ```bash
46
+ pip install agentropic
47
+ ```
48
+
49
+ ## Quick start
50
+
51
+ ```python
52
+ import asyncio
53
+ from agentropic import Agent, tool
54
+
55
+ @tool()
56
+ def get_weather(city: str) -> str:
57
+ """Get the current weather for a city."""
58
+ return f"It's sunny in {city}."
59
+
60
+ async def main():
61
+ agent = Agent(
62
+ name="assistant",
63
+ model="gpt-4o-mini", # any litellm model string
64
+ instructions="You are helpful and concise.",
65
+ tools=[get_weather],
66
+ )
67
+ print(await agent.arun("What's the weather in Paris?"))
68
+
69
+ asyncio.run(main())
70
+ ```
71
+
72
+ Sync usage: `agent.run("...")` (wraps `arun` in `asyncio.run` for you).
73
+
74
+ ## Streaming (`stream=True`)
75
+
76
+ Same shape as the OpenAI / Anthropic SDKs. Tool calls work while streaming.
77
+
78
+ ```python
79
+ async for event in await agent.arun("Weather in Tokyo?", stream=True):
80
+ if event.type == "text":
81
+ print(event.text, end="", flush=True)
82
+ elif event.type == "tool_call":
83
+ print(f"\n[calling {event.tool_call.name}({event.tool_call.arguments})]")
84
+ elif event.type == "tool_result":
85
+ print(f"[-> {event.tool_call.result}]")
86
+ elif event.type == "done":
87
+ result = event.result # full AgentRunResult (usage, tool_calls, ...)
88
+ ```
89
+
90
+ With `verbose=True` (the default) the colourful terminal tracer already prints
91
+ tokens live, so only print `event.text` yourself when using `verbose=False`
92
+ (e.g. rendering into your own UI).
93
+
94
+ Sync: `for event in agent.run("...", stream=True): ...`
95
+ Text only: `async for token in agent.astream("..."): ...`
96
+
97
+ ## Using MCP servers
98
+
99
+ ```python
100
+ from agentropic import Agent
101
+ from agentropic.mcp import StdioServer, HTTPServer
102
+
103
+ agent = Agent(
104
+ name="fs-agent",
105
+ model="gpt-4o-mini",
106
+ mcp_servers=[
107
+ StdioServer(command="npx", args=["-y", "@modelcontextprotocol/server-filesystem", "."]),
108
+ # HTTPServer(url="https://example.com/mcp", headers={"Authorization": "Bearer ..."}),
109
+ ],
110
+ )
111
+
112
+ # Option A: connect once, keep servers alive across turns (recommended)
113
+ async with agent:
114
+ print(await agent.arun("List the files in the current directory."))
115
+ print(await agent.arun("Now read README.md"))
116
+
117
+ # Option B: no context manager — servers connect/disconnect around each run
118
+ print(await agent.arun("List the files in the current directory."))
119
+ ```
120
+
121
+ Requires the optional MCP dependency: `pip install "agentropic[mcp]"`, plus whatever
122
+ the server itself needs (e.g. Node.js for `npx` servers). Transports: `StdioServer`,
123
+ `HTTPServer` (streamable HTTP), `SSEServer` (legacy SSE).
124
+
125
+ ## Architecture
126
+
127
+ ```
128
+ src/agentropic/
129
+ ├── __init__.py # public API surface
130
+ ├── agent.py # the run loop: LLM <-> tools <-> memory
131
+ ├── llm.py # thin litellm wrapper (swap providers here)
132
+ ├── tools.py # @tool decorator, Tool, ToolRegistry, schema generation
133
+ ├── memory.py # in-memory conversation history (swap for your own store)
134
+ └── mcp/
135
+ └── client.py # MCP stdio/SSE client -> Tool adapter
136
+ ```
137
+
138
+ Everything is a plain, editable python file — no hidden magic, no plugin
139
+ registry you can't inspect. Read `agent.py` first; that's the whole loop.
140
+
141
+ ## Extending it
142
+
143
+ - **Custom memory / persistence**: implement `.add()`, `.get()`, `.clear()`
144
+ matching `Memory` and pass `memory=YourMemory()` to `Agent`.
145
+ - **Multi-agent handoff**: compose multiple `Agent` instances and route
146
+ between them yourself, or wrap one agent's `arun` as a tool for another.
147
+
@@ -44,23 +44,56 @@ asyncio.run(main())
44
44
 
45
45
  Sync usage: `agent.run("...")` (wraps `arun` in `asyncio.run` for you).
46
46
 
47
+ ## Streaming (`stream=True`)
48
+
49
+ Same shape as the OpenAI / Anthropic SDKs. Tool calls work while streaming.
50
+
51
+ ```python
52
+ async for event in await agent.arun("Weather in Tokyo?", stream=True):
53
+ if event.type == "text":
54
+ print(event.text, end="", flush=True)
55
+ elif event.type == "tool_call":
56
+ print(f"\n[calling {event.tool_call.name}({event.tool_call.arguments})]")
57
+ elif event.type == "tool_result":
58
+ print(f"[-> {event.tool_call.result}]")
59
+ elif event.type == "done":
60
+ result = event.result # full AgentRunResult (usage, tool_calls, ...)
61
+ ```
62
+
63
+ With `verbose=True` (the default) the colourful terminal tracer already prints
64
+ tokens live, so only print `event.text` yourself when using `verbose=False`
65
+ (e.g. rendering into your own UI).
66
+
67
+ Sync: `for event in agent.run("...", stream=True): ...`
68
+ Text only: `async for token in agent.astream("..."): ...`
69
+
47
70
  ## Using MCP servers
48
71
 
49
72
  ```python
50
73
  from agentropic import Agent
51
- from agentropic.mcp import StdioServer
74
+ from agentropic.mcp import StdioServer, HTTPServer
52
75
 
53
76
  agent = Agent(
54
77
  name="fs-agent",
55
78
  model="gpt-4o-mini",
56
79
  mcp_servers=[
57
80
  StdioServer(command="npx", args=["-y", "@modelcontextprotocol/server-filesystem", "."]),
81
+ # HTTPServer(url="https://example.com/mcp", headers={"Authorization": "Bearer ..."}),
58
82
  ],
59
83
  )
84
+
85
+ # Option A: connect once, keep servers alive across turns (recommended)
86
+ async with agent:
87
+ print(await agent.arun("List the files in the current directory."))
88
+ print(await agent.arun("Now read README.md"))
89
+
90
+ # Option B: no context manager — servers connect/disconnect around each run
60
91
  print(await agent.arun("List the files in the current directory."))
61
92
  ```
62
93
 
63
- Requires the optional MCP dependency: `pip install "agentropic[mcp]"`.
94
+ Requires the optional MCP dependency: `pip install "agentropic[mcp]"`, plus whatever
95
+ the server itself needs (e.g. Node.js for `npx` servers). Transports: `StdioServer`,
96
+ `HTTPServer` (streamable HTTP), `SSEServer` (legacy SSE).
64
97
 
65
98
  ## Architecture
66
99
 
@@ -82,9 +115,6 @@ registry you can't inspect. Read `agent.py` first; that's the whole loop.
82
115
 
83
116
  - **Custom memory / persistence**: implement `.add()`, `.get()`, `.clear()`
84
117
  matching `Memory` and pass `memory=YourMemory()` to `Agent`.
85
- - **Streaming with tool calls**: `astream()` currently falls back to a
86
- non-streamed `arun()` when tools are registered — extend it if you need
87
- token-level streaming mid-tool-loop.
88
118
  - **Multi-agent handoff**: compose multiple `Agent` instances and route
89
119
  between them yourself, or wrap one agent's `arun` as a tool for another.
90
120
 
@@ -25,13 +25,13 @@ def add(a: int, b: int) -> int:
25
25
  async def main():
26
26
  agent = Agent(
27
27
  name="assistant",
28
- model="gemini/gemini-2.5-flash-lite",
28
+ model="gemini/gemini-3.1-flash-lite",
29
29
  api_key=os.getenv("api_key"),
30
30
  instructions="You are a concise, helpful assistant. Use tools when needed.",
31
31
  tools=[get_weather, add],
32
32
  # Optional: pull in tools from a live MCP server too.
33
33
  # mcp_servers=[StdioServer(command="npx", args=["-y", "@modelcontextprotocol/server-filesystem", "."])],
34
- max_iterations=2,
34
+ max_iterations=5,
35
35
  )
36
36
 
37
37
  result = await agent.arun("What's the weather in Tokyo, and what's 12 + 30?")
@@ -55,6 +55,14 @@ async def main():
55
55
  result2 = await agent.arun("Say that last sum again, but doubled.")
56
56
  print(result2.output)
57
57
 
58
+ # Streaming (stream=True): with verbose=True (default) the colourful
59
+ # tracer prints tokens live for you, so just consume the events.
60
+ # If you render tokens yourself (e.g. a web UI), use verbose=False and
61
+ # handle event.type == "text" / "tool_call" / "tool_result" / "done".
62
+ async for event in await agent.arun("Weather in Delhi? Answer in one sentence.", stream=True):
63
+ if event.type == "done":
64
+ final = event.result
65
+
58
66
 
59
67
  if __name__ == "__main__":
60
68
  asyncio.run(main())
@@ -0,0 +1,12 @@
1
+ agentropic/__init__.py,sha256=6QOkNZLR9WtTDRc1hqIxPaMWrpLPEeqjVSTkYmr0fmM,627
2
+ agentropic/agent.py,sha256=D6xNLWYt36FGAl2WO7urbb3GTXoUVnp1RIkSfMPeUhQ,12683
3
+ agentropic/llm.py,sha256=Zqq4bcbL-O5GcZzE2v583h4VNSB-3uSTP4ACzZBjCNw,2797
4
+ agentropic/memory.py,sha256=Vw7oCN36PQtckpoo5ImgNO9LHr085yOKYFXYyMH7rF0,782
5
+ agentropic/tools.py,sha256=rQB18qjapoASE72vSk8kBLgBMgWiicBVAbNwcpEY_2A,5901
6
+ agentropic/tracing.py,sha256=2ASQaSHuob_M_2knZ8CxvmYDedX1D9xh22rxmQutovk,10061
7
+ agentropic/mcp/__init__.py,sha256=6tDmJRwKWkNuSZLz1BUR57xXe_EoILDUf9TKdd0984g,151
8
+ agentropic/mcp/client.py,sha256=d0UMmHVRGnGSKrVCmdIPjfp660d_DpmKQ4j2eix_Kw0,3453
9
+ agentropic-0.2.2.dist-info/METADATA,sha256=vsZGDz-DIYeMjPGz2NxU242b9dE43jh1Hy0-OGBM3jA,3952
10
+ agentropic-0.2.2.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
11
+ agentropic-0.2.2.dist-info/licenses/LICENSE,sha256=v2spsd7N1pKFFh2G8wGP_45iwe5S0DYiJzG4im8Rupc,1066
12
+ agentropic-0.2.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.31.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -1,34 +1,37 @@
1
- [build-system]
2
- requires = ["hatchling"]
3
- build-backend = "hatchling.build"
4
-
5
- [project]
6
- name = "agentropic"
7
- version = "0.2.2"
8
- description = "A minimal, hackable agentic framework: LLM calls via litellm, @tool-decorated functions, and MCP server support."
9
- readme = "README.md"
10
- requires-python = ">=3.9"
11
- license = { text = "MIT" }
12
- authors = [
13
- { name = "Vishal Tiwari", email = "vishaltiwari.up2019@gmail.com" }
14
- ]
15
- keywords = ["llm", "agent", "agents", "litellm", "mcp", "tools", "ai"]
16
- classifiers = [
17
- "Programming Language :: Python :: 3",
18
- "License :: OSI Approved :: MIT License",
19
- "Operating System :: OS Independent",
20
- "Development Status :: 3 - Alpha",
21
- "Intended Audience :: Developers",
22
- "Topic :: Software Development :: Libraries :: Python Modules",
23
- ]
24
- dependencies = [
25
- "litellm>=1.40.0",
26
- "pydantic>=2.0.0",
27
- ]
28
-
29
- [project.optional-dependencies]
30
- mcp = ["mcp>=1.0.0"]
31
- dev = ["pytest", "pytest-asyncio", "build", "twine"]
32
-
33
- [tool.hatch.build.targets.wheel]
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "agentropic"
7
+ version = "0.3.1"
8
+ description = "A minimal, hackable agentic framework: LLM calls via litellm, @tool-decorated functions, and MCP server support."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "Vishal Tiwari", email = "vishaltiwari.up2019@gmail.com" }
14
+ ]
15
+ keywords = ["llm", "agent", "agents", "litellm", "mcp", "tools", "ai"]
16
+ classifiers = [
17
+ "Programming Language :: Python :: 3",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ "Development Status :: 3 - Alpha",
21
+ "Intended Audience :: Developers",
22
+ "Topic :: Software Development :: Libraries :: Python Modules",
23
+ ]
24
+ dependencies = [
25
+ "litellm>=1.40.0",
26
+ "pydantic>=2.0.0",
27
+ # litellm imports `mcp` internally whenever tools are passed (seen in
28
+ # litellm 1.106.x), so it is effectively required for tool calling.
29
+ "mcp>=1.0.0; python_version>='3.10'",
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ mcp = ["mcp>=1.0.0"] # kept for backward compat: pip install "agentropic[mcp]"
34
+ dev = ["pytest", "pytest-asyncio", "build", "twine"]
35
+
36
+ [tool.hatch.build.targets.wheel]
34
37
  packages = ["src/agentropic"]
@@ -0,0 +1,30 @@
1
+ from .agent import Agent, AgentError, AgentRunResult, StreamEvent, ToolCallRecord
2
+ from .tools import tool, Tool, ToolRegistry
3
+ from .llm import LLMClient, LLMResponse
4
+ from .memory import Memory
5
+ from .mcp.client import MCPConnection, MCPError, StdioServer, SSEServer, HTTPServer, MCPServerConfig
6
+ from .tracing import AgentTracer, NullTracer
7
+
8
+ __version__ = "0.3.1"
9
+
10
+ __all__ = [
11
+ "Agent",
12
+ "AgentError",
13
+ "AgentRunResult",
14
+ "StreamEvent",
15
+ "ToolCallRecord",
16
+ "MCPError",
17
+ "HTTPServer",
18
+ "tool",
19
+ "Tool",
20
+ "ToolRegistry",
21
+ "LLMClient",
22
+ "LLMResponse",
23
+ "Memory",
24
+ "MCPConnection",
25
+ "StdioServer",
26
+ "SSEServer",
27
+ "MCPServerConfig",
28
+ "AgentTracer",
29
+ "NullTracer",
30
+ ]