agentic-blocks 0.1.24__py3-none-any.whl → 0.1.25__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.
agentic_blocks/agent.py CHANGED
@@ -3,19 +3,10 @@ from agentic_blocks.utils.tools_utils import (
3
3
  create_tool_registry,
4
4
  execute_pending_tool_calls,
5
5
  )
6
+ from agentic_blocks.utils.logger import get_logger
6
7
  from agentic_blocks import call_llm, Messages
7
- from rich.panel import Panel
8
- from rich.box import HEAVY
9
- from rich.console import Console
10
- from rich.console import Group
11
-
12
- console = Console(
13
- style="black on bright_white",
14
- force_terminal=True,
15
- width=None,
16
- legacy_windows=False,
17
- color_system="truecolor",
18
- )
8
+
9
+ logger = get_logger(__name__)
19
10
 
20
11
 
21
12
  class Agent:
@@ -23,7 +14,6 @@ class Agent:
23
14
  self.system_prompt = system_prompt
24
15
  self.tools = tools
25
16
  self.tool_registry = create_tool_registry(tools)
26
- self.panels = []
27
17
 
28
18
  # Create nodes
29
19
  self.llm_node = self._create_llm_node()
@@ -49,7 +39,12 @@ class Agent:
49
39
  return messages
50
40
 
51
41
  def exec(self, messages) -> Messages:
42
+ latest_message = messages.get_messages()[-1]
43
+ if latest_message["role"] == "user":
44
+ logger.info(f'message: "{latest_message["content"]}"')
45
+
52
46
  response = call_llm(messages=messages, tools=self.tools)
47
+ # logger.info(f'response: "{response["content"] or "tool_calls"}"')
53
48
  messages.add_response_message(response)
54
49
  return messages
55
50
 
@@ -63,30 +58,19 @@ class Agent:
63
58
 
64
59
  def _create_tool_node(self):
65
60
  class ToolNode(Node):
66
- def __init__(self, tool_registry, agent):
61
+ def __init__(self, tool_registry):
67
62
  super().__init__()
68
63
  self.tool_registry = tool_registry
69
- self.agent = agent
70
64
 
71
65
  def prep(self, shared):
72
66
  return shared["messages"]
73
67
 
74
- def exec(self, messages) -> Messages:
75
- tool_calls = messages.get_pending_tool_calls()[0]
76
- tool_name = tool_calls["tool_name"]
77
- tool_arguments = tool_calls["arguments"]
78
-
79
- # Format arguments nicely
80
- if isinstance(tool_arguments, dict):
81
- args_str = ", ".join(
82
- [f"{k}={v}" for k, v in tool_arguments.items()]
68
+ def exec(self, messages: Messages) -> Messages:
69
+ tool_calls = messages.get_pending_tool_calls()
70
+ for tool_call in tool_calls:
71
+ logger.info(
72
+ f'tool_call: "{tool_call["tool_name"]}({tool_call["arguments"]})"'
83
73
  )
84
- formatted_call = f"{tool_name}({args_str})"
85
- else:
86
- formatted_call = f"{tool_name}({tool_arguments})"
87
-
88
- tool_panel = self.agent.create_panel(formatted_call, "Tool Calls")
89
- self.agent.panels.append(tool_panel)
90
74
 
91
75
  tool_responses = execute_pending_tool_calls(
92
76
  messages, self.tool_registry
@@ -97,7 +81,7 @@ class Agent:
97
81
  def post(self, shared, prep_res, messages):
98
82
  return "llm_node"
99
83
 
100
- return ToolNode(self.tool_registry, self)
84
+ return ToolNode(self.tool_registry)
101
85
 
102
86
  def _create_answer_node(self):
103
87
  class AnswerNode(Node):
@@ -110,6 +94,7 @@ class Agent:
110
94
 
111
95
  def invoke(self, user_prompt: str) -> str:
112
96
  messages = Messages(user_prompt=user_prompt)
97
+ logger.info(f'message: "{user_prompt}"')
113
98
  if self.system_prompt:
114
99
  messages.add_system_message(self.system_prompt)
115
100
 
@@ -117,29 +102,3 @@ class Agent:
117
102
  self.flow.run(shared)
118
103
 
119
104
  return shared["answer"]
120
-
121
- def print_response(self, user_prompt: str, stream: bool = False):
122
- # Reset panels and start with message
123
- self.panels = []
124
- message_panel = self.create_panel(user_prompt, "Message")
125
- self.panels.append(message_panel)
126
-
127
- # Always collect all panels first
128
- response = self.invoke(user_prompt)
129
- response_panel = self.create_panel(response, "Response")
130
- self.panels.append(response_panel)
131
-
132
- # Print all panels as a group (no gaps)
133
- panel_group = Group(*self.panel)
134
- console.print(panel_group)
135
-
136
- def create_panel(self, content, title, border_style="blue"):
137
- return Panel(
138
- content,
139
- title=title,
140
- title_align="left",
141
- border_style=border_style,
142
- box=HEAVY,
143
- expand=True, # Full terminal width
144
- padding=(1, 1), # Internal padding
145
- )
@@ -0,0 +1,169 @@
1
+ from pocketflow import Node, Flow
2
+ from agentic_blocks.utils.tools_utils import (
3
+ create_tool_registry,
4
+ execute_pending_tool_calls,
5
+ )
6
+ from agentic_blocks import call_llm, Messages
7
+
8
+
9
+ from rich.console import Group, Console
10
+ from rich.json import JSON
11
+ from rich.live import Live
12
+ from rich.markdown import Markdown
13
+ from rich.status import Status
14
+ from rich.text import Text
15
+ from rich.box import HEAVY
16
+ from rich.panel import Panel
17
+ from agentic_blocks.utils.rich_logger import print_response
18
+
19
+
20
+ class Agent:
21
+ def __init__(self, system_prompt: str, tools: list):
22
+ self.system_prompt = system_prompt
23
+ self.tools = tools
24
+ self.tool_registry = create_tool_registry(tools)
25
+ self.panels = []
26
+
27
+ # Create nodes
28
+ self.llm_node = self._create_llm_node()
29
+ self.tool_node = self._create_tool_node()
30
+ self.answer_node = self._create_answer_node()
31
+
32
+ # Set up flow
33
+ self.llm_node - "tool_node" >> self.tool_node
34
+ self.tool_node - "llm_node" >> self.llm_node
35
+ self.llm_node - "answer_node" >> self.answer_node
36
+
37
+ self.flow = Flow(self.llm_node)
38
+
39
+ def _create_llm_node(self):
40
+ class LLMNode(Node):
41
+ def __init__(self, system_prompt, tools):
42
+ super().__init__()
43
+ self.system_prompt = system_prompt
44
+ self.tools = tools
45
+
46
+ def prep(self, shared):
47
+ messages = shared["messages"]
48
+ return messages
49
+
50
+ def exec(self, messages) -> Messages:
51
+ response = call_llm(messages=messages, tools=self.tools)
52
+ messages.add_response_message(response)
53
+ return messages
54
+
55
+ def post(self, shared, prep_res, messages):
56
+ if messages.has_pending_tool_calls():
57
+ return "tool_node"
58
+ else:
59
+ return "answer_node"
60
+
61
+ return LLMNode(self.system_prompt, self.tools)
62
+
63
+ def _create_tool_node(self):
64
+ class ToolNode(Node):
65
+ def __init__(self, tool_registry, agent):
66
+ super().__init__()
67
+ self.tool_registry = tool_registry
68
+ self.agent = agent
69
+
70
+ def prep(self, shared):
71
+ return shared["messages"]
72
+
73
+ def exec(self, messages) -> Messages:
74
+ tool_calls = messages.get_pending_tool_calls()[0]
75
+ tool_name = tool_calls["tool_name"]
76
+ tool_arguments = tool_calls["arguments"]
77
+
78
+ # Format arguments nicely
79
+ if isinstance(tool_arguments, dict):
80
+ args_str = ", ".join(
81
+ [f"{k}={v}" for k, v in tool_arguments.items()]
82
+ )
83
+ formatted_call = f"{tool_name}({args_str})"
84
+ else:
85
+ formatted_call = f"{tool_name}({tool_arguments})"
86
+
87
+ tool_panel = self.agent.create_panel(formatted_call, "Tool Calls")
88
+ self.agent.panels.append(tool_panel)
89
+
90
+ self.agent.live_log.update(Group(*self.agent.panels))
91
+ self.agent.live_log.refresh()
92
+
93
+ tool_responses = execute_pending_tool_calls(
94
+ messages, self.tool_registry
95
+ )
96
+ messages.add_tool_responses(tool_responses)
97
+ return messages
98
+
99
+ def post(self, shared, prep_res, messages):
100
+ return "llm_node"
101
+
102
+ return ToolNode(self.tool_registry, self)
103
+
104
+ def _create_answer_node(self):
105
+ class AnswerNode(Node):
106
+ def prep(self, shared):
107
+ messages = shared["messages"]
108
+ shared["answer"] = messages.get_messages()[-1]["content"]
109
+ return messages
110
+
111
+ return AnswerNode()
112
+
113
+ def invoke(self, user_prompt: str) -> str:
114
+ messages = Messages(user_prompt=user_prompt)
115
+ if self.system_prompt:
116
+ messages.add_system_message(self.system_prompt)
117
+
118
+ shared = {"messages": messages}
119
+ self.flow.run(shared)
120
+
121
+ return shared["answer"]
122
+
123
+ def create_panel(self, content, title, border_style="blue"):
124
+ return Panel(
125
+ content,
126
+ title=title,
127
+ title_align="left",
128
+ border_style=border_style,
129
+ box=HEAVY,
130
+ expand=True,
131
+ padding=(1, 1),
132
+ )
133
+
134
+ def print_response(self, user_prompt: str):
135
+ messages = Messages(user_prompt=user_prompt)
136
+ if self.system_prompt:
137
+ messages.add_system_message(self.system_prompt)
138
+
139
+ shared = {"messages": messages}
140
+
141
+ with Live(console=Console(), auto_refresh=False) as self.live_log:
142
+ status = Status("Thinking...", spinner="aesthetic", speed=0.4)
143
+ self.live_log.update(status)
144
+ self.live_log.refresh() # Explicit refresh for Jupyter
145
+
146
+ self.panels = [status]
147
+
148
+ message_panel = self.create_panel(
149
+ content=Text(user_prompt, style="green"),
150
+ title="Message",
151
+ border_style="cyan",
152
+ )
153
+
154
+ self.panels.append(message_panel)
155
+ self.live_log.update(Group(*self.panels))
156
+ self.live_log.refresh()
157
+
158
+ self.flow.run(shared)
159
+ response = shared["answer"]
160
+
161
+ response_panel = self.create_panel(
162
+ content=Text(response, style="bold blue"),
163
+ title="Response",
164
+ border_style="green",
165
+ )
166
+
167
+ self.panels.append(response_panel)
168
+ self.live_log.update(Group(*self.panels))
169
+ self.live_log.refresh()
@@ -0,0 +1,14 @@
1
+ import logging
2
+
3
+ # Auto-configure logging when module is imported
4
+ logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
5
+
6
+ # Suppress HTTP request logs
7
+ logging.getLogger("openai").setLevel(logging.WARNING)
8
+ logging.getLogger("httpx").setLevel(logging.WARNING)
9
+ logging.getLogger("urllib3").setLevel(logging.WARNING)
10
+
11
+
12
+ def get_logger(name):
13
+ """Get a logger with the given name."""
14
+ return logging.getLogger(name)
@@ -0,0 +1,59 @@
1
+ import time
2
+ from rich.console import Group, Console
3
+ from rich.json import JSON
4
+ from rich.live import Live
5
+ from rich.markdown import Markdown
6
+ from rich.status import Status
7
+ from rich.text import Text
8
+ from rich.box import HEAVY
9
+ from rich.panel import Panel
10
+
11
+ # Create a console instance for Jupyter
12
+
13
+
14
+ def create_panel(content, title, border_style="blue"):
15
+ return Panel(
16
+ content,
17
+ title=title,
18
+ title_align="left",
19
+ border_style=border_style,
20
+ box=HEAVY,
21
+ expand=True,
22
+ padding=(1, 1),
23
+ )
24
+
25
+
26
+ def print_response(user_prompt):
27
+ with Live(console=Console(), auto_refresh=False) as live_log:
28
+ status = Status("Thinking...", spinner="aesthetic", speed=0.4)
29
+ live_log.update(status)
30
+ live_log.refresh() # Explicit refresh for Jupyter
31
+
32
+ # Create panels
33
+ panels = [status]
34
+
35
+ message_panel = create_panel(
36
+ content=Text(user_prompt, style="green"),
37
+ title="Message",
38
+ border_style="cyan",
39
+ )
40
+
41
+ panels.append(message_panel)
42
+ live_log.update(Group(*panels))
43
+ live_log.refresh() # Explicit refresh for Jupyter
44
+
45
+ # Add some delay to see the display
46
+
47
+ for i in range(3):
48
+ time.sleep(2)
49
+
50
+ # Add a response panel
51
+ response_panel = create_panel(
52
+ content=Text("Response: " + str(i), style="bold blue"),
53
+ title="Response",
54
+ border_style="green",
55
+ )
56
+
57
+ panels.append(response_panel)
58
+ live_log.update(Group(*panels))
59
+ live_log.refresh()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-blocks
3
- Version: 0.1.24
3
+ Version: 0.1.25
4
4
  Summary: Simple building blocks for agentic AI systems with MCP client and conversation management
5
5
  Author-email: Magnus Bjelkenhed <bjelkenhed@gmail.com>
6
6
  License: MIT
@@ -0,0 +1,15 @@
1
+ agentic_blocks/__init__.py,sha256=LJy2tzTwX9ZjPw8dqkXOWiude7ZDDIaBIvaLC8U4d_Y,435
2
+ agentic_blocks/agent.py,sha256=gGSHHmPl3p_LguKKxUNhbCZXJV_V6MccmtfoEAC5fO4,3558
3
+ agentic_blocks/agent_rich.py,sha256=MTMFJ1gEMd3mfj65YxSwh50yp19KZKrP40cRiJfDMdU,5639
4
+ agentic_blocks/llm.py,sha256=9OnpOFqZ7CoOoy8DECW7gxSGtpw-76hX-NvV003TKBU,5330
5
+ agentic_blocks/mcp_client.py,sha256=15mIN_Qw0OVNJAvfgO3jVZS4-AU4TtvEQSFDlL9ruqA,9773
6
+ agentic_blocks/messages.py,sha256=iJEULGGFGHVp8ViE2oKykU9JVfvGKoySCDhxlHm29Lk,12014
7
+ agentic_blocks/utils/logger.py,sha256=BblwT4Oj6YdHEYV4KtIzpKrcCiRvp_VVC1quk6GN3wo,436
8
+ agentic_blocks/utils/rich_logger.py,sha256=TT2D9v6bLYGPMiwUvb3mMcGHeCmlvD89dSwCxwDGfMA,1612
9
+ agentic_blocks/utils/tools_utils.py,sha256=ZX6cqvtaV_evwMUnR_uukdvEtQah6xkDMBy7jwR7tJg,7145
10
+ agentic_blocks/visualization/visualize.py,sha256=x59lFDKXqaFlereCEhN7GWDimiNRNcb9USeRPv8YY5I,39647
11
+ agentic_blocks-0.1.25.dist-info/licenses/LICENSE,sha256=r4IcBaAjTv3-yfjXgDPuRD953Qci0Y0nQn5JfHwLyBY,1073
12
+ agentic_blocks-0.1.25.dist-info/METADATA,sha256=KQes4V0dczfXrwupfrpMd7ywqN3_XEn_GlcJt_hifco,12126
13
+ agentic_blocks-0.1.25.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
+ agentic_blocks-0.1.25.dist-info/top_level.txt,sha256=-1a4RAemqicXLU1rRzw4QHV3KlNeQDNxVs3m2gAT238,15
15
+ agentic_blocks-0.1.25.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- agentic_blocks/__init__.py,sha256=LJy2tzTwX9ZjPw8dqkXOWiude7ZDDIaBIvaLC8U4d_Y,435
2
- agentic_blocks/agent.py,sha256=KZ2dQnb4IxMK9xOMXFYIBIPofVtLmpISftn-YbSul14,4840
3
- agentic_blocks/llm.py,sha256=9OnpOFqZ7CoOoy8DECW7gxSGtpw-76hX-NvV003TKBU,5330
4
- agentic_blocks/mcp_client.py,sha256=15mIN_Qw0OVNJAvfgO3jVZS4-AU4TtvEQSFDlL9ruqA,9773
5
- agentic_blocks/messages.py,sha256=iJEULGGFGHVp8ViE2oKykU9JVfvGKoySCDhxlHm29Lk,12014
6
- agentic_blocks/utils/tools_utils.py,sha256=ZX6cqvtaV_evwMUnR_uukdvEtQah6xkDMBy7jwR7tJg,7145
7
- agentic_blocks/visualization/visualize.py,sha256=x59lFDKXqaFlereCEhN7GWDimiNRNcb9USeRPv8YY5I,39647
8
- agentic_blocks-0.1.24.dist-info/licenses/LICENSE,sha256=r4IcBaAjTv3-yfjXgDPuRD953Qci0Y0nQn5JfHwLyBY,1073
9
- agentic_blocks-0.1.24.dist-info/METADATA,sha256=lNOys5YTca4ZPZmVQ2SBLYKvpBpsfSgKE3y1_5XjjG8,12126
10
- agentic_blocks-0.1.24.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
- agentic_blocks-0.1.24.dist-info/top_level.txt,sha256=-1a4RAemqicXLU1rRzw4QHV3KlNeQDNxVs3m2gAT238,15
12
- agentic_blocks-0.1.24.dist-info/RECORD,,