agno 2.0.0a1__py3-none-any.whl → 2.0.0rc1__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.
Files changed (50) hide show
  1. agno/agent/agent.py +390 -33
  2. agno/api/agent.py +2 -2
  3. agno/api/evals.py +2 -2
  4. agno/api/os.py +1 -1
  5. agno/api/settings.py +2 -2
  6. agno/api/team.py +2 -2
  7. agno/db/dynamo/dynamo.py +0 -6
  8. agno/db/firestore/firestore.py +0 -6
  9. agno/db/in_memory/in_memory_db.py +0 -6
  10. agno/db/json/json_db.py +0 -6
  11. agno/db/mongo/mongo.py +0 -6
  12. agno/db/mysql/utils.py +0 -1
  13. agno/db/postgres/postgres.py +0 -10
  14. agno/db/postgres/utils.py +0 -1
  15. agno/db/redis/redis.py +0 -4
  16. agno/db/singlestore/singlestore.py +0 -10
  17. agno/db/singlestore/utils.py +0 -1
  18. agno/db/sqlite/sqlite.py +0 -4
  19. agno/db/sqlite/utils.py +0 -1
  20. agno/integrations/discord/client.py +5 -1
  21. agno/knowledge/embedder/aws_bedrock.py +2 -2
  22. agno/models/anthropic/claude.py +2 -49
  23. agno/models/message.py +7 -6
  24. agno/os/app.py +158 -62
  25. agno/os/interfaces/agui/agui.py +1 -1
  26. agno/os/interfaces/agui/utils.py +16 -9
  27. agno/os/interfaces/slack/slack.py +2 -3
  28. agno/os/interfaces/whatsapp/whatsapp.py +2 -3
  29. agno/os/mcp.py +255 -0
  30. agno/os/router.py +33 -7
  31. agno/os/routers/evals/evals.py +9 -5
  32. agno/os/routers/knowledge/knowledge.py +30 -7
  33. agno/os/routers/memory/memory.py +17 -8
  34. agno/os/routers/metrics/metrics.py +4 -2
  35. agno/os/routers/session/session.py +8 -3
  36. agno/os/settings.py +0 -1
  37. agno/run/agent.py +87 -2
  38. agno/run/cancel.py +0 -2
  39. agno/team/team.py +2 -2
  40. agno/tools/function.py +65 -7
  41. agno/tools/linear.py +1 -1
  42. agno/utils/gemini.py +31 -1
  43. agno/utils/models/claude.py +49 -0
  44. agno/utils/streamlit.py +454 -0
  45. agno/workflow/workflow.py +8 -1
  46. {agno-2.0.0a1.dist-info → agno-2.0.0rc1.dist-info}/METADATA +1 -1
  47. {agno-2.0.0a1.dist-info → agno-2.0.0rc1.dist-info}/RECORD +50 -48
  48. {agno-2.0.0a1.dist-info → agno-2.0.0rc1.dist-info}/WHEEL +0 -0
  49. {agno-2.0.0a1.dist-info → agno-2.0.0rc1.dist-info}/licenses/LICENSE +0 -0
  50. {agno-2.0.0a1.dist-info → agno-2.0.0rc1.dist-info}/top_level.txt +0 -0
agno/db/dynamo/dynamo.py CHANGED
@@ -506,8 +506,6 @@ class DynamoDb(BaseDb):
506
506
  item = serialize_to_dynamo_item(serialized_session)
507
507
  self.client.put_item(TableName=table_name, Item=item)
508
508
 
509
- log_debug(f"Upserted session with id '{session.session_id}'")
510
-
511
509
  return deserialize_session_result(serialized_session, session, deserialize)
512
510
 
513
511
  except Exception as e:
@@ -852,8 +850,6 @@ class DynamoDb(BaseDb):
852
850
 
853
851
  self.client.put_item(TableName=table_name, Item=item)
854
852
 
855
- log_debug(f"Upserted user memory with id '{memory.memory_id}'")
856
-
857
853
  if not deserialize:
858
854
  return memory_dict
859
855
 
@@ -1530,8 +1526,6 @@ class DynamoDb(BaseDb):
1530
1526
 
1531
1527
  self.client.put_item(TableName=table_name, Item=item)
1532
1528
 
1533
- log_debug(f"Upserted knowledge content with id '{knowledge_row.id}'")
1534
-
1535
1529
  return knowledge_row
1536
1530
 
1537
1531
  except Exception as e:
@@ -532,8 +532,6 @@ class FirestoreDb(BaseDb):
532
532
  return None
533
533
  deserialized_session = deserialize_session_json_fields(result)
534
534
 
535
- log_debug(f"Upserted session with id '{session.session_id}'")
536
-
537
535
  if not deserialize:
538
536
  return deserialized_session
539
537
 
@@ -844,8 +842,6 @@ class FirestoreDb(BaseDb):
844
842
 
845
843
  doc_ref.set(update_doc, merge=True)
846
844
 
847
- log_debug(f"Upserted user memory with id '{memory.memory_id}'")
848
-
849
845
  if not deserialize:
850
846
  return update_doc
851
847
 
@@ -1170,8 +1166,6 @@ class FirestoreDb(BaseDb):
1170
1166
 
1171
1167
  doc_ref.set(update_doc, merge=True)
1172
1168
 
1173
- log_debug(f"Upserted knowledge source with id '{knowledge_row.id}'")
1174
-
1175
1169
  return knowledge_row
1176
1170
 
1177
1171
  except Exception as e:
@@ -280,8 +280,6 @@ class InMemoryDb(BaseDb):
280
280
  session_dict["updated_at"] = session_dict.get("created_at")
281
281
  self._sessions.append(session_dict)
282
282
 
283
- log_debug(f"Upserted session with id '{session.session_id}'")
284
-
285
283
  if not deserialize:
286
284
  return session_dict
287
285
 
@@ -464,8 +462,6 @@ class InMemoryDb(BaseDb):
464
462
  if not memory_updated:
465
463
  self._memories.append(memory_dict)
466
464
 
467
- log_debug(f"Upserted user memory with id '{memory.memory_id}'")
468
-
469
465
  if not deserialize:
470
466
  return memory_dict
471
467
  return UserMemory.from_dict(memory_dict)
@@ -739,8 +735,6 @@ class InMemoryDb(BaseDb):
739
735
  if not item_updated:
740
736
  self._knowledge.append(knowledge_dict)
741
737
 
742
- log_debug(f"Upserted knowledge source with id '{knowledge_row.id}'")
743
-
744
738
  return knowledge_row
745
739
 
746
740
  except Exception as e:
agno/db/json/json_db.py CHANGED
@@ -375,8 +375,6 @@ class JsonDb(BaseDb):
375
375
 
376
376
  self._write_json_file(self.session_table_name, sessions)
377
377
 
378
- log_debug(f"Upserted session with id '{session.session_id}'")
379
-
380
378
  if not deserialize:
381
379
  return session_dict
382
380
 
@@ -580,8 +578,6 @@ class JsonDb(BaseDb):
580
578
 
581
579
  self._write_json_file(self.memory_table_name, memories)
582
580
 
583
- log_debug(f"Upserted user memory with id '{memory.memory_id}'")
584
-
585
581
  if not deserialize:
586
582
  return memory_dict
587
583
  return UserMemory.from_dict(memory_dict)
@@ -874,8 +870,6 @@ class JsonDb(BaseDb):
874
870
 
875
871
  self._write_json_file(self.knowledge_table_name, knowledge_items)
876
872
 
877
- log_debug(f"Upserted knowledge source with id '{knowledge_row.id}'")
878
-
879
873
  return knowledge_row
880
874
 
881
875
  except Exception as e:
agno/db/mongo/mongo.py CHANGED
@@ -504,8 +504,6 @@ class MongoDb(BaseDb):
504
504
 
505
505
  session = deserialize_session_json_fields(result) # type: ignore
506
506
 
507
- log_debug("Upserted session'")
508
-
509
507
  if not deserialize:
510
508
  return session
511
509
 
@@ -537,8 +535,6 @@ class MongoDb(BaseDb):
537
535
 
538
536
  session = deserialize_session_json_fields(result) # type: ignore
539
537
 
540
- log_debug(f"Upserted session with id '{session.session_id}'")
541
-
542
538
  if not deserialize:
543
539
  return session
544
540
 
@@ -570,8 +566,6 @@ class MongoDb(BaseDb):
570
566
 
571
567
  session = deserialize_session_json_fields(result) # type: ignore
572
568
 
573
- log_debug(f"Upserted session with id '{session.session_id}'")
574
-
575
569
  if not deserialize:
576
570
  return session
577
571
 
agno/db/mysql/utils.py CHANGED
@@ -111,7 +111,6 @@ def is_valid_table(db_engine: Engine, table_name: str, table_type: str, db_schem
111
111
  log_warning(f"Missing columns {missing_columns} in table {db_schema}.{table_name}")
112
112
  return False
113
113
 
114
- log_debug(f"Table {db_schema}.{table_name} has all expected columns")
115
114
  return True
116
115
  except Exception as e:
117
116
  log_error(f"Error validating table schema for {db_schema}.{table_name}: {e}")
@@ -610,8 +610,6 @@ class PostgresDb(BaseDb):
610
610
  row = result.fetchone()
611
611
  session_dict = dict(row._mapping)
612
612
 
613
- log_debug(f"Upserted agent session with id '{session_dict.get('session_id')}'")
614
-
615
613
  if session_dict is None or not deserialize:
616
614
  return session_dict
617
615
  return AgentSession.from_dict(session_dict)
@@ -648,8 +646,6 @@ class PostgresDb(BaseDb):
648
646
  row = result.fetchone()
649
647
  session_dict = dict(row._mapping)
650
648
 
651
- log_debug(f"Upserted team session with id '{session_dict.get('session_id')}'")
652
-
653
649
  if session_dict is None or not deserialize:
654
650
  return session_dict
655
651
  return TeamSession.from_dict(session_dict)
@@ -686,8 +682,6 @@ class PostgresDb(BaseDb):
686
682
  row = result.fetchone()
687
683
  session_dict = dict(row._mapping)
688
684
 
689
- log_debug(f"Upserted workflow session with id '{session_dict.get('session_id')}'")
690
-
691
685
  if session_dict is None or not deserialize:
692
686
  return session_dict
693
687
  return WorkflowSession.from_dict(session_dict)
@@ -1033,8 +1027,6 @@ class PostgresDb(BaseDb):
1033
1027
 
1034
1028
  memory_raw = dict(row._mapping)
1035
1029
 
1036
- log_debug(f"Upserted user memory with id '{memory.memory_id}'")
1037
-
1038
1030
  if not memory_raw or not deserialize:
1039
1031
  return memory_raw
1040
1032
 
@@ -1405,8 +1397,6 @@ class PostgresDb(BaseDb):
1405
1397
  )
1406
1398
  sess.execute(stmt)
1407
1399
 
1408
- log_debug(f"Upserted knowledge row with id '{knowledge_row.id}'")
1409
-
1410
1400
  return knowledge_row
1411
1401
 
1412
1402
  except Exception as e:
agno/db/postgres/utils.py CHANGED
@@ -107,7 +107,6 @@ def is_valid_table(db_engine: Engine, table_name: str, table_type: str, db_schem
107
107
  log_warning(f"Missing columns {missing_columns} in table {db_schema}.{table_name}")
108
108
  return False
109
109
 
110
- log_debug(f"Table {db_schema}.{table_name} has all expected columns")
111
110
  return True
112
111
  except Exception as e:
113
112
  log_error(f"Error validating table schema for {db_schema}.{table_name}: {e}")
agno/db/redis/redis.py CHANGED
@@ -572,8 +572,6 @@ class RedisDb(BaseDb):
572
572
  if not success:
573
573
  return None
574
574
 
575
- log_debug(f"Upserted session with id '{session.session_id}'")
576
-
577
575
  if not deserialize:
578
576
  return data
579
577
 
@@ -1140,8 +1138,6 @@ class RedisDb(BaseDb):
1140
1138
  data = knowledge_row.model_dump()
1141
1139
  success = self._store_record("knowledge", knowledge_row.id, data) # type: ignore
1142
1140
 
1143
- log_debug(f"Upserted knowledge content with id '{knowledge_row.id}'")
1144
-
1145
1141
  return knowledge_row if success else None
1146
1142
 
1147
1143
  except Exception as e:
@@ -697,8 +697,6 @@ class SingleStoreDb(BaseDb):
697
697
  if row is None:
698
698
  return None
699
699
 
700
- log_debug(f"Upserted session with id '{session_dict.get('session_id')}'")
701
-
702
700
  if not deserialize:
703
701
  return row._mapping
704
702
 
@@ -740,8 +738,6 @@ class SingleStoreDb(BaseDb):
740
738
  if row is None:
741
739
  return None
742
740
 
743
- log_debug(f"Upserted session with id '{session_dict.get('session_id')}'")
744
-
745
741
  if not deserialize:
746
742
  return row._mapping
747
743
 
@@ -783,8 +779,6 @@ class SingleStoreDb(BaseDb):
783
779
  if row is None:
784
780
  return None
785
781
 
786
- log_debug(f"Upserted session with id '{session_dict.get('session_id')}'")
787
-
788
782
  if not deserialize:
789
783
  return row._mapping
790
784
 
@@ -1117,8 +1111,6 @@ class SingleStoreDb(BaseDb):
1117
1111
  if row is None:
1118
1112
  return None
1119
1113
 
1120
- log_debug(f"Upserted user memory with id '{memory.memory_id}'")
1121
-
1122
1114
  memory_raw = row._mapping
1123
1115
  if not memory_raw or not deserialize:
1124
1116
  return memory_raw
@@ -1461,8 +1453,6 @@ class SingleStoreDb(BaseDb):
1461
1453
  stmt = stmt.on_duplicate_key_update(**update_fields)
1462
1454
  sess.execute(stmt)
1463
1455
 
1464
- log_debug(f"Upserted knowledge source with id '{knowledge_row.id}'")
1465
-
1466
1456
  return knowledge_row
1467
1457
 
1468
1458
  except Exception as e:
@@ -133,7 +133,6 @@ def is_valid_table(db_engine: Engine, table_name: str, table_type: str, db_schem
133
133
  log_warning(f"Missing columns {missing_columns} in table {table_ref}")
134
134
  return False
135
135
 
136
- log_debug(f"Table {table_ref} has all expected columns")
137
136
  return True
138
137
 
139
138
  except Exception as e:
agno/db/sqlite/sqlite.py CHANGED
@@ -995,8 +995,6 @@ class SqliteDb(BaseDb):
995
995
  if row is None:
996
996
  return None
997
997
 
998
- log_debug(f"Upserted user memory with id '{memory.memory_id}'")
999
-
1000
998
  memory_raw = row._mapping
1001
999
  if not memory_raw or not deserialize:
1002
1000
  return memory_raw
@@ -1358,8 +1356,6 @@ class SqliteDb(BaseDb):
1358
1356
  )
1359
1357
  sess.execute(stmt)
1360
1358
 
1361
- log_debug(f"Upserted knowledge content with id '{knowledge_row.id}'")
1362
-
1363
1359
  return knowledge_row
1364
1360
 
1365
1361
  except Exception as e:
agno/db/sqlite/utils.py CHANGED
@@ -91,7 +91,6 @@ def is_valid_table(db_engine: Engine, table_name: str, table_type: str, db_schem
91
91
  log_warning(f"Missing columns {missing_columns} in table {table_name}")
92
92
  return False
93
93
 
94
- log_debug(f"Table {table_name} has all expected columns")
95
94
  return True
96
95
  except Exception as e:
97
96
  log_error(f"Error validating table schema for {table_name}: {e}")
@@ -8,6 +8,7 @@ from agno.agent.agent import Agent, RunOutput
8
8
  from agno.media import Audio, File, Image, Video
9
9
  from agno.team.team import Team, TeamRunOutput
10
10
  from agno.utils.log import log_info, log_warning
11
+ from agno.utils.message import get_text_from_message
11
12
 
12
13
  try:
13
14
  import discord
@@ -167,7 +168,10 @@ class DiscordClient:
167
168
  thread=thread, message=f"Reasoning: \n{response.reasoning_content}", italics=True
168
169
  )
169
170
 
170
- await self._send_discord_messages(thread=thread, message=str(response.content))
171
+ # Handle structured outputs properly
172
+ content_message = get_text_from_message(response.content) if response.content is not None else ""
173
+
174
+ await self._send_discord_messages(thread=thread, message=content_message)
171
175
 
172
176
  async def _send_discord_messages(self, thread: discord.channel, message: str, italics: bool = False): # type: ignore
173
177
  if len(message) < 1500:
@@ -272,7 +272,7 @@ class AwsBedrockEmbedder(Embedder):
272
272
  contentType="application/json",
273
273
  accept="application/json",
274
274
  )
275
- response_body = json.loads(response["body"].read().decode("utf-8"))
275
+ response_body = json.loads((await response["body"].read()).decode("utf-8"))
276
276
 
277
277
  # Extract embeddings using the same logic as get_embedding
278
278
  if "embeddings" in response_body:
@@ -308,7 +308,7 @@ class AwsBedrockEmbedder(Embedder):
308
308
  contentType="application/json",
309
309
  accept="application/json",
310
310
  )
311
- response_body = json.loads(response["body"].read().decode("utf-8"))
311
+ response_body = json.loads((await response["body"].read()).decode("utf-8"))
312
312
 
313
313
  embedding: List[float] = []
314
314
  # Extract embeddings using the same logic as get_embedding_and_usage
@@ -13,7 +13,7 @@ from agno.models.metrics import Metrics
13
13
  from agno.models.response import ModelResponse
14
14
  from agno.run.agent import RunOutput
15
15
  from agno.utils.log import log_debug, log_error, log_warning
16
- from agno.utils.models.claude import MCPServerConfiguration, format_messages
16
+ from agno.utils.models.claude import MCPServerConfiguration, format_messages, format_tools_for_model
17
17
 
18
18
  try:
19
19
  from anthropic import Anthropic as AnthropicClient
@@ -174,59 +174,12 @@ class Claude(Model):
174
174
  request_kwargs["system"] = [{"text": system_message, "type": "text"}]
175
175
 
176
176
  if tools:
177
- request_kwargs["tools"] = self._format_tools_for_model(tools)
177
+ request_kwargs["tools"] = format_tools_for_model(tools)
178
178
 
179
179
  if request_kwargs:
180
180
  log_debug(f"Calling {self.provider} with request parameters: {request_kwargs}", log_level=2)
181
181
  return request_kwargs
182
182
 
183
- def _format_tools_for_model(self, tools: Optional[List[Dict[str, Any]]] = None) -> Optional[List[Dict[str, Any]]]:
184
- """
185
- Transforms function definitions into a format accepted by the Anthropic API.
186
- """
187
- if not tools:
188
- return None
189
-
190
- parsed_tools: List[Dict[str, Any]] = []
191
- for tool_def in tools:
192
- if tool_def.get("type", "") != "function":
193
- parsed_tools.append(tool_def)
194
- continue
195
-
196
- func_def = tool_def.get("function", {})
197
- parameters: Dict[str, Any] = func_def.get("parameters", {})
198
- properties: Dict[str, Any] = parameters.get("properties", {})
199
- required_params: List[str] = []
200
-
201
- for param_name, param_info in properties.items():
202
- param_type = param_info.get("type", "")
203
- param_type_list: List[str] = [param_type] if isinstance(param_type, str) else param_type or []
204
-
205
- if "null" not in param_type_list:
206
- required_params.append(param_name)
207
-
208
- input_properties: Dict[str, Dict[str, Union[str, List[str]]]] = {}
209
- for param_name, param_info in properties.items():
210
- input_properties[param_name] = {
211
- "description": param_info.get("description", ""),
212
- }
213
- if "type" not in param_info and "anyOf" in param_info:
214
- input_properties[param_name]["anyOf"] = param_info["anyOf"]
215
- else:
216
- input_properties[param_name]["type"] = param_info.get("type", "")
217
-
218
- tool = {
219
- "name": func_def.get("name") or "",
220
- "description": func_def.get("description") or "",
221
- "input_schema": {
222
- "type": parameters.get("type", "object"),
223
- "properties": input_properties,
224
- "required": required_params,
225
- },
226
- }
227
- parsed_tools.append(tool)
228
- return parsed_tools
229
-
230
183
  def invoke(
231
184
  self,
232
185
  messages: List[Message],
agno/models/message.py CHANGED
@@ -228,12 +228,13 @@ class Message(BaseModel):
228
228
  if isinstance(tool_call_arguments, dict)
229
229
  else json.loads(tool_call_arguments)
230
230
  )
231
- # Ensure tool_call_args is a dictionary before calling .items()
232
- if isinstance(tool_call_args, dict):
233
- arguments = ", ".join(f"{k}: {v}" for k, v in tool_call_args.items())
234
- tool_calls_list.append(f" Arguments: '{arguments}'")
235
- else:
236
- tool_calls_list.append(f" Arguments: '{tool_call_args}'")
231
+ if tool_call_args:
232
+ # Ensure tool_call_args is a dictionary before calling .items()
233
+ if isinstance(tool_call_args, dict):
234
+ arguments = ", ".join(f"{k}: {v}" for k, v in tool_call_args.items())
235
+ tool_calls_list.append(f" Arguments: '{arguments}'")
236
+ else:
237
+ tool_calls_list.append(f" Arguments: '{tool_call_args}'")
237
238
  except json.JSONDecodeError:
238
239
  tool_calls_list.append(" Arguments: 'Invalid JSON format'")
239
240
  tool_calls_str = "\n".join(tool_calls_list)