agentropic 0.2.1__tar.gz → 0.2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentropic
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: A minimal, hackable agentic framework: LLM calls via litellm, @tool-decorated functions, and MCP server support.
5
5
  Author-email: Vishal Tiwari <vishaltiwari.up2019@gmail.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agentropic"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
  description = "A minimal, hackable agentic framework: LLM calls via litellm, @tool-decorated functions, and MCP server support."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -3,6 +3,7 @@ from .tools import tool, Tool, ToolRegistry
3
3
  from .llm import LLMClient, LLMResponse
4
4
  from .memory import Memory
5
5
  from .mcp.client import MCPConnection, StdioServer, SSEServer, MCPServerConfig
6
+ from .tracing import AgentTracer, NullTracer
6
7
 
7
8
  __version__ = "0.2.0"
8
9
 
@@ -21,4 +22,6 @@ __all__ = [
21
22
  "StdioServer",
22
23
  "SSEServer",
23
24
  "MCPServerConfig",
25
+ "AgentTracer",
26
+ "NullTracer",
24
27
  ]
@@ -90,6 +90,11 @@ def _display_width(text: str) -> int:
90
90
  # Tracer
91
91
  # --------------------------------------------------------------------------
92
92
 
93
+ # Module-level so the welcome banner shows exactly once per process,
94
+ # no matter how many Agent/AgentTracer instances get created.
95
+ _WELCOME_SHOWN = False
96
+
97
+
93
98
  class AgentTracer:
94
99
  """Renders one Agent run as a live, colorized trace in the terminal.
95
100
 
@@ -119,31 +124,71 @@ class AgentTracer:
119
124
  width = _term_width()
120
125
  return f"{self._c(color)}{char * width}{self._c(C.RESET)}"
121
126
 
122
- def _box(self, title: str, lines: list[str], color: str, title_color: str = "") -> None:
127
+ def _box(
128
+ self,
129
+ title: str,
130
+ lines: list[str],
131
+ color: str,
132
+ title_color: str = "",
133
+ center_title: bool = False,
134
+ center_lines: bool = False,
135
+ ) -> None:
123
136
  width = _term_width()
124
137
  inner = width - 4
125
138
  title_color = title_color or color
126
139
  top = f"{self._c(color)}╭{'─' * (width - 2)}╮{self._c(C.RESET)}"
127
140
  bot = f"{self._c(color)}╰{'─' * (width - 2)}╯{self._c(C.RESET)}"
128
- head = f"{self._c(color)}│ {self._c(C.RESET)}{self._c(C.BOLD)}{self._c(title_color)}{title}{self._c(C.RESET)}"
129
- head_pad = width - 4 - _display_width(title)
130
- head += " " * max(head_pad, 0) + f"{self._c(color)} │{self._c(C.RESET)}"
141
+
142
+ if center_title:
143
+ left_pad = max((inner - _display_width(title)) // 2, 0)
144
+ right_pad = max(inner - _display_width(title) - left_pad, 0)
145
+ else:
146
+ left_pad, right_pad = 0, max(inner - _display_width(title), 0)
147
+
148
+ head = (
149
+ f"{self._c(color)}│ {self._c(C.RESET)}{' ' * left_pad}"
150
+ f"{self._c(C.BOLD)}{self._c(title_color)}{title}{self._c(C.RESET)}"
151
+ f"{' ' * right_pad}{self._c(color)} │{self._c(C.RESET)}"
152
+ )
131
153
  self._print(top)
132
154
  self._print(head)
133
155
  if lines:
134
156
  self._print(f"{self._c(color)}├{'─' * (width - 2)}┤{self._c(C.RESET)}")
135
157
  for raw in lines:
136
158
  for wrapped in _wrap(raw, inner) or [""]:
137
- pad = inner - _display_width(wrapped)
159
+ w = _display_width(wrapped)
160
+ if center_lines:
161
+ lp = max((inner - w) // 2, 0)
162
+ rp = max(inner - w - lp, 0)
163
+ else:
164
+ lp, rp = 0, max(inner - w, 0)
138
165
  self._print(
139
- f"{self._c(color)}│ {self._c(C.RESET)}{wrapped}"
140
- f"{' ' * max(pad, 0)}{self._c(color)} │{self._c(C.RESET)}"
166
+ f"{self._c(color)}│ {self._c(C.RESET)}{' ' * lp}{wrapped}"
167
+ f"{' ' * rp}{self._c(color)} │{self._c(C.RESET)}"
141
168
  )
142
169
  self._print(bot)
143
170
 
144
171
  # -- lifecycle events ---------------------------------------------------
145
172
 
173
+ def welcome_banner(self, force: bool = False) -> None:
174
+ """Prints a one-time 'Welcome to Agentropic' banner. Shown
175
+ automatically before the first run in a process; call with
176
+ force=True to show it again (e.g. at the very start of a script)."""
177
+ global _WELCOME_SHOWN
178
+ if _WELCOME_SHOWN and not force:
179
+ return
180
+ _WELCOME_SHOWN = True
181
+ self._print()
182
+ self._box(
183
+ "🚀 Welcome to Agentropic — Agentic AI System",
184
+ [f"{self._c(C.DIM)}Powered by your agents, tools, and memory.{self._c(C.RESET)}"],
185
+ color=C.MAGENTA,
186
+ center_title=True,
187
+ center_lines=True,
188
+ )
189
+
146
190
  def run_start(self, user_input: str) -> None:
191
+ self.welcome_banner()
147
192
  self._run_started = time.monotonic()
148
193
  self._print()
149
194
  self._box(
File without changes
File without changes
File without changes