agentic-blocks 0.1.5__tar.gz → 0.1.7__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.
- {agentic_blocks-0.1.5/src/agentic_blocks.egg-info → agentic_blocks-0.1.7}/PKG-INFO +1 -1
- {agentic_blocks-0.1.5 → agentic_blocks-0.1.7}/pyproject.toml +1 -1
- {agentic_blocks-0.1.5 → agentic_blocks-0.1.7}/src/agentic_blocks/llm.py +13 -3
- {agentic_blocks-0.1.5 → agentic_blocks-0.1.7}/src/agentic_blocks/messages.py +67 -18
- {agentic_blocks-0.1.5 → agentic_blocks-0.1.7/src/agentic_blocks.egg-info}/PKG-INFO +1 -1
- {agentic_blocks-0.1.5 → agentic_blocks-0.1.7}/LICENSE +0 -0
- {agentic_blocks-0.1.5 → agentic_blocks-0.1.7}/README.md +0 -0
- {agentic_blocks-0.1.5 → agentic_blocks-0.1.7}/setup.cfg +0 -0
- {agentic_blocks-0.1.5 → agentic_blocks-0.1.7}/src/agentic_blocks/__init__.py +0 -0
- {agentic_blocks-0.1.5 → agentic_blocks-0.1.7}/src/agentic_blocks/mcp_client.py +0 -0
- {agentic_blocks-0.1.5 → agentic_blocks-0.1.7}/src/agentic_blocks.egg-info/SOURCES.txt +0 -0
- {agentic_blocks-0.1.5 → agentic_blocks-0.1.7}/src/agentic_blocks.egg-info/dependency_links.txt +0 -0
- {agentic_blocks-0.1.5 → agentic_blocks-0.1.7}/src/agentic_blocks.egg-info/requires.txt +0 -0
- {agentic_blocks-0.1.5 → agentic_blocks-0.1.7}/src/agentic_blocks.egg-info/top_level.txt +0 -0
@@ -14,7 +14,7 @@ agentic_blocks = []
|
|
14
14
|
|
15
15
|
[project]
|
16
16
|
name = "agentic-blocks"
|
17
|
-
version = "0.1.
|
17
|
+
version = "0.1.7"
|
18
18
|
description = "Simple building blocks for agentic AI systems with MCP client and conversation management"
|
19
19
|
readme = "README.md"
|
20
20
|
requires-python = ">=3.11"
|
@@ -48,12 +48,20 @@ def call_llm(
|
|
48
48
|
# Get API key
|
49
49
|
if not api_key:
|
50
50
|
api_key = os.getenv("OPENAI_API_KEY")
|
51
|
+
if not api_key:
|
52
|
+
api_key = os.getenv("OPENROUTER_API_KEY")
|
51
53
|
|
52
54
|
if not api_key and not base_url:
|
53
55
|
raise LLMError(
|
54
|
-
"
|
56
|
+
"API key not found. Set OPENROUTER_API_KEY or OPENAI_API_KEY environment variable or pass api_key parameter."
|
55
57
|
)
|
56
58
|
|
59
|
+
if api_key and api_key.startswith("sk-or"):
|
60
|
+
base_url = "https://openrouter.ai/api/v1"
|
61
|
+
|
62
|
+
if base_url and not api_key:
|
63
|
+
api_key = "EMPTY"
|
64
|
+
|
57
65
|
# Initialize OpenAI client
|
58
66
|
client_kwargs = {}
|
59
67
|
if api_key:
|
@@ -137,10 +145,12 @@ def example_usage():
|
|
137
145
|
# Call with raw message list
|
138
146
|
print("\nUsing raw message list:")
|
139
147
|
response2 = call_llm(messages_list, tools=tools, temperature=0.5)
|
140
|
-
if hasattr(response2,
|
148
|
+
if hasattr(response2, "tool_calls") and response2.tool_calls:
|
141
149
|
print(f"Tool calls requested: {len(response2.tool_calls)}")
|
142
150
|
for i, tool_call in enumerate(response2.tool_calls):
|
143
|
-
print(
|
151
|
+
print(
|
152
|
+
f" {i + 1}. {tool_call.function.name}({tool_call.function.arguments})"
|
153
|
+
)
|
144
154
|
else:
|
145
155
|
print(f"Response: {response2.content}")
|
146
156
|
|
@@ -68,9 +68,11 @@ class Messages:
|
|
68
68
|
tool_call: The tool call dictionary with id, type, function, etc.
|
69
69
|
"""
|
70
70
|
# Check if the latest message is an assistant message with tool_calls
|
71
|
-
if (
|
72
|
-
|
73
|
-
and
|
71
|
+
if (
|
72
|
+
self.messages
|
73
|
+
and self.messages[-1].get("role") == "assistant"
|
74
|
+
and "tool_calls" in self.messages[-1]
|
75
|
+
):
|
74
76
|
# Append to existing assistant message
|
75
77
|
self.messages[-1]["tool_calls"].append(tool_call)
|
76
78
|
else:
|
@@ -82,6 +84,33 @@ class Messages:
|
|
82
84
|
}
|
83
85
|
self.messages.append(assistant_message)
|
84
86
|
|
87
|
+
def add_tool_calls(self, tool_calls):
|
88
|
+
"""
|
89
|
+
Add multiple tool calls from ChatCompletionMessageFunctionToolCall objects.
|
90
|
+
|
91
|
+
Args:
|
92
|
+
tool_calls: A list of ChatCompletionMessageFunctionToolCall objects or a single object
|
93
|
+
"""
|
94
|
+
# Handle single tool call or list of tool calls
|
95
|
+
if not isinstance(tool_calls, list):
|
96
|
+
tool_calls = [tool_calls]
|
97
|
+
|
98
|
+
# Create assistant message with empty content and tool calls
|
99
|
+
assistant_message = {"role": "assistant", "content": "", "tool_calls": []}
|
100
|
+
|
101
|
+
for tool_call in tool_calls:
|
102
|
+
tool_call_dict = {
|
103
|
+
"id": tool_call.id,
|
104
|
+
"type": tool_call.type,
|
105
|
+
"function": {
|
106
|
+
"name": tool_call.function.name,
|
107
|
+
"arguments": tool_call.function.arguments,
|
108
|
+
},
|
109
|
+
}
|
110
|
+
assistant_message["tool_calls"].append(tool_call_dict)
|
111
|
+
|
112
|
+
self.messages.append(assistant_message)
|
113
|
+
|
85
114
|
def add_tool_response(self, tool_call_id: str, content: str):
|
86
115
|
"""
|
87
116
|
Add a tool response message.
|
@@ -108,7 +137,7 @@ class Messages:
|
|
108
137
|
for response in tool_responses:
|
109
138
|
tool_call_id = response.get("tool_call_id", "unknown")
|
110
139
|
is_error = response.get("is_error", False)
|
111
|
-
|
140
|
+
|
112
141
|
if is_error:
|
113
142
|
content = f"Error: {response.get('error', 'Unknown error')}"
|
114
143
|
else:
|
@@ -125,6 +154,23 @@ class Messages:
|
|
125
154
|
|
126
155
|
self.add_tool_response(tool_call_id, content)
|
127
156
|
|
157
|
+
def add_response_message(self, model_response):
|
158
|
+
"""
|
159
|
+
Add a response message (ChatCompletionMessage) to the conversation.
|
160
|
+
|
161
|
+
Args:
|
162
|
+
model_response: A ChatCompletionMessage object with role, content, and potentially tool_calls
|
163
|
+
"""
|
164
|
+
# If there are tool calls, use add_tool_calls
|
165
|
+
if model_response.tool_calls:
|
166
|
+
self.add_tool_calls(model_response.tool_calls)
|
167
|
+
# If there's also content, update the message content
|
168
|
+
if model_response.content:
|
169
|
+
self.messages[-1]["content"] = model_response.content
|
170
|
+
else:
|
171
|
+
# No tool calls, just add content as assistant message
|
172
|
+
self.add_assistant_message(model_response.content or "")
|
173
|
+
|
128
174
|
def get_messages(self) -> List[Dict[str, Any]]:
|
129
175
|
"""Get the current messages list."""
|
130
176
|
return self.messages
|
@@ -140,17 +186,20 @@ class Messages:
|
|
140
186
|
return False
|
141
187
|
|
142
188
|
last_message = self.messages[-1]
|
143
|
-
|
189
|
+
|
144
190
|
# Check if the last message is an assistant message with tool calls
|
145
191
|
if last_message.get("role") == "assistant" and "tool_calls" in last_message:
|
146
192
|
# Check if there are subsequent tool responses
|
147
193
|
tool_call_ids = {tc.get("id") for tc in last_message["tool_calls"]}
|
148
|
-
|
194
|
+
|
149
195
|
# Look for tool responses after this message
|
150
196
|
for msg in reversed(self.messages):
|
151
|
-
if
|
197
|
+
if (
|
198
|
+
msg.get("role") == "tool"
|
199
|
+
and msg.get("tool_call_id") in tool_call_ids
|
200
|
+
):
|
152
201
|
tool_call_ids.remove(msg.get("tool_call_id"))
|
153
|
-
|
202
|
+
|
154
203
|
# If there are still unresponded tool call IDs, we have pending calls
|
155
204
|
return len(tool_call_ids) > 0
|
156
205
|
|
@@ -172,7 +221,7 @@ class Messages:
|
|
172
221
|
for j, tool_call in enumerate(message["tool_calls"], 1):
|
173
222
|
function_name = tool_call.get("function", {}).get("name", "unknown")
|
174
223
|
lines.append(f" └─ Tool Call {j}: {function_name}")
|
175
|
-
|
224
|
+
|
176
225
|
# Handle tool messages
|
177
226
|
elif role == "tool":
|
178
227
|
tool_call_id = message.get("tool_call_id", "unknown")
|
@@ -180,7 +229,7 @@ class Messages:
|
|
180
229
|
if len(content) > 200:
|
181
230
|
content = content[:197] + "..."
|
182
231
|
lines.append(f"{i}. {role} [{tool_call_id[:8]}...]: {content}")
|
183
|
-
|
232
|
+
|
184
233
|
# Handle other message types
|
185
234
|
else:
|
186
235
|
# Truncate long content for readability
|
@@ -198,28 +247,28 @@ def example_usage():
|
|
198
247
|
messages = Messages(
|
199
248
|
system_prompt="You are a helpful assistant.",
|
200
249
|
user_prompt="Hello, how are you?",
|
201
|
-
add_date_and_time=True
|
250
|
+
add_date_and_time=True,
|
202
251
|
)
|
203
|
-
|
252
|
+
|
204
253
|
# Add assistant response
|
205
254
|
messages.add_assistant_message("I'm doing well, thank you!")
|
206
|
-
|
255
|
+
|
207
256
|
# Add a tool call
|
208
257
|
tool_call = {
|
209
258
|
"id": "call_123",
|
210
259
|
"type": "function",
|
211
|
-
"function": {"name": "get_weather", "arguments": '{"location": "Paris"}'}
|
260
|
+
"function": {"name": "get_weather", "arguments": '{"location": "Paris"}'},
|
212
261
|
}
|
213
262
|
messages.add_tool_call(tool_call)
|
214
|
-
|
263
|
+
|
215
264
|
# Add tool response
|
216
265
|
messages.add_tool_response("call_123", "The weather in Paris is sunny, 22°C")
|
217
|
-
|
266
|
+
|
218
267
|
print("Conversation:")
|
219
268
|
print(messages)
|
220
|
-
|
269
|
+
|
221
270
|
print(f"\nHas pending tool calls: {messages.has_pending_tool_calls()}")
|
222
271
|
|
223
272
|
|
224
273
|
if __name__ == "__main__":
|
225
|
-
example_usage()
|
274
|
+
example_usage()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{agentic_blocks-0.1.5 → agentic_blocks-0.1.7}/src/agentic_blocks.egg-info/dependency_links.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|