agno 2.3.12__py3-none-any.whl → 2.3.14__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 (55) hide show
  1. agno/agent/agent.py +1125 -1401
  2. agno/eval/__init__.py +21 -8
  3. agno/knowledge/embedder/azure_openai.py +0 -1
  4. agno/knowledge/embedder/google.py +1 -1
  5. agno/models/anthropic/claude.py +4 -1
  6. agno/models/azure/openai_chat.py +11 -5
  7. agno/models/base.py +8 -4
  8. agno/models/openai/chat.py +0 -2
  9. agno/models/openai/responses.py +2 -2
  10. agno/os/app.py +112 -5
  11. agno/os/auth.py +190 -3
  12. agno/os/config.py +9 -0
  13. agno/os/interfaces/a2a/router.py +619 -9
  14. agno/os/interfaces/a2a/utils.py +31 -32
  15. agno/os/middleware/__init__.py +2 -0
  16. agno/os/middleware/jwt.py +670 -108
  17. agno/os/router.py +0 -1
  18. agno/os/routers/agents/router.py +22 -4
  19. agno/os/routers/agents/schema.py +14 -1
  20. agno/os/routers/teams/router.py +20 -4
  21. agno/os/routers/teams/schema.py +14 -1
  22. agno/os/routers/workflows/router.py +88 -9
  23. agno/os/scopes.py +469 -0
  24. agno/os/utils.py +86 -53
  25. agno/reasoning/anthropic.py +85 -1
  26. agno/reasoning/azure_ai_foundry.py +93 -1
  27. agno/reasoning/deepseek.py +91 -1
  28. agno/reasoning/gemini.py +81 -1
  29. agno/reasoning/groq.py +103 -1
  30. agno/reasoning/manager.py +1244 -0
  31. agno/reasoning/ollama.py +93 -1
  32. agno/reasoning/openai.py +113 -1
  33. agno/reasoning/vertexai.py +85 -1
  34. agno/run/agent.py +11 -0
  35. agno/run/base.py +1 -1
  36. agno/run/team.py +11 -0
  37. agno/session/team.py +0 -3
  38. agno/team/team.py +1204 -1452
  39. agno/tools/postgres.py +1 -1
  40. agno/utils/cryptography.py +22 -0
  41. agno/utils/events.py +69 -2
  42. agno/utils/hooks.py +4 -10
  43. agno/utils/print_response/agent.py +52 -2
  44. agno/utils/print_response/team.py +141 -10
  45. agno/utils/prompts.py +8 -6
  46. agno/utils/string.py +46 -0
  47. agno/utils/team.py +1 -1
  48. agno/vectordb/chroma/chromadb.py +1 -0
  49. agno/vectordb/milvus/milvus.py +32 -3
  50. agno/vectordb/redis/redisdb.py +16 -2
  51. {agno-2.3.12.dist-info → agno-2.3.14.dist-info}/METADATA +3 -2
  52. {agno-2.3.12.dist-info → agno-2.3.14.dist-info}/RECORD +55 -52
  53. {agno-2.3.12.dist-info → agno-2.3.14.dist-info}/WHEEL +0 -0
  54. {agno-2.3.12.dist-info → agno-2.3.14.dist-info}/licenses/LICENSE +0 -0
  55. {agno-2.3.12.dist-info → agno-2.3.14.dist-info}/top_level.txt +0 -0
agno/reasoning/ollama.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import List, Optional
3
+ from typing import AsyncIterator, Iterator, List, Optional, Tuple
4
4
 
5
5
  from agno.models.base import Model
6
6
  from agno.models.message import Message
@@ -65,3 +65,95 @@ async def aget_ollama_reasoning(reasoning_agent: "Agent", messages: List[Message
65
65
  return Message(
66
66
  role="assistant", content=f"<thinking>\n{reasoning_content}\n</thinking>", reasoning_content=reasoning_content
67
67
  )
68
+
69
+
70
+ def get_ollama_reasoning_stream(
71
+ reasoning_agent: "Agent", # type: ignore # noqa: F821
72
+ messages: List[Message],
73
+ ) -> Iterator[Tuple[Optional[str], Optional[Message]]]:
74
+ """
75
+ Stream reasoning content from Ollama model.
76
+
77
+ For reasoning models on Ollama (qwq, deepseek-r1, etc.), we use the main content output as reasoning content.
78
+
79
+ Yields:
80
+ Tuple of (reasoning_content_delta, final_message)
81
+ - During streaming: (reasoning_content_delta, None)
82
+ - At the end: (None, final_message)
83
+ """
84
+ from agno.run.agent import RunEvent
85
+
86
+ reasoning_content: str = ""
87
+
88
+ try:
89
+ for event in reasoning_agent.run(input=messages, stream=True, stream_intermediate_steps=True):
90
+ if hasattr(event, "event"):
91
+ if event.event == RunEvent.run_content:
92
+ # Check for reasoning_content attribute first (native reasoning)
93
+ if hasattr(event, "reasoning_content") and event.reasoning_content:
94
+ reasoning_content += event.reasoning_content
95
+ yield (event.reasoning_content, None)
96
+ # Use the main content as reasoning content
97
+ elif hasattr(event, "content") and event.content:
98
+ reasoning_content += event.content
99
+ yield (event.content, None)
100
+ elif event.event == RunEvent.run_completed:
101
+ pass
102
+ except Exception as e:
103
+ logger.warning(f"Reasoning error: {e}")
104
+ return
105
+
106
+ # Yield final message
107
+ if reasoning_content:
108
+ final_message = Message(
109
+ role="assistant",
110
+ content=f"<thinking>\n{reasoning_content}\n</thinking>",
111
+ reasoning_content=reasoning_content,
112
+ )
113
+ yield (None, final_message)
114
+
115
+
116
+ async def aget_ollama_reasoning_stream(
117
+ reasoning_agent: "Agent", # type: ignore # noqa: F821
118
+ messages: List[Message],
119
+ ) -> AsyncIterator[Tuple[Optional[str], Optional[Message]]]:
120
+ """
121
+ Stream reasoning content from Ollama model asynchronously.
122
+
123
+ For reasoning models on Ollama (qwq, deepseek-r1, etc.), we use the main content output as reasoning content.
124
+
125
+ Yields:
126
+ Tuple of (reasoning_content_delta, final_message)
127
+ - During streaming: (reasoning_content_delta, None)
128
+ - At the end: (None, final_message)
129
+ """
130
+ from agno.run.agent import RunEvent
131
+
132
+ reasoning_content: str = ""
133
+
134
+ try:
135
+ async for event in reasoning_agent.arun(input=messages, stream=True, stream_intermediate_steps=True):
136
+ if hasattr(event, "event"):
137
+ if event.event == RunEvent.run_content:
138
+ # Check for reasoning_content attribute first (native reasoning)
139
+ if hasattr(event, "reasoning_content") and event.reasoning_content:
140
+ reasoning_content += event.reasoning_content
141
+ yield (event.reasoning_content, None)
142
+ # Use the main content as reasoning content
143
+ elif hasattr(event, "content") and event.content:
144
+ reasoning_content += event.content
145
+ yield (event.content, None)
146
+ elif event.event == RunEvent.run_completed:
147
+ pass
148
+ except Exception as e:
149
+ logger.warning(f"Reasoning error: {e}")
150
+ return
151
+
152
+ # Yield final message
153
+ if reasoning_content:
154
+ final_message = Message(
155
+ role="assistant",
156
+ content=f"<thinking>\n{reasoning_content}\n</thinking>",
157
+ reasoning_content=reasoning_content,
158
+ )
159
+ yield (None, final_message)
agno/reasoning/openai.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import List, Optional
3
+ from typing import AsyncIterator, Iterator, List, Optional, Tuple
4
4
 
5
5
  from agno.models.base import Model
6
6
  from agno.models.message import Message
@@ -84,3 +84,115 @@ async def aget_openai_reasoning(reasoning_agent: "Agent", messages: List[Message
84
84
  return Message(
85
85
  role="assistant", content=f"<thinking>\n{reasoning_content}\n</thinking>", reasoning_content=reasoning_content
86
86
  )
87
+
88
+
89
+ def get_openai_reasoning_stream(
90
+ reasoning_agent: "Agent", # type: ignore # noqa: F821
91
+ messages: List[Message],
92
+ ) -> Iterator[Tuple[Optional[str], Optional[Message]]]:
93
+ """
94
+ Stream reasoning content from OpenAI model.
95
+
96
+ For OpenAI reasoning models, we use the main content output as reasoning content.
97
+
98
+ Yields:
99
+ Tuple of (reasoning_content_delta, final_message)
100
+ - During streaming: (reasoning_content_delta, None)
101
+ - At the end: (None, final_message)
102
+ """
103
+ from agno.run.agent import RunEvent
104
+
105
+ # Update system message role to "system"
106
+ for message in messages:
107
+ if message.role == "developer":
108
+ message.role = "system"
109
+
110
+ reasoning_content: str = ""
111
+
112
+ try:
113
+ for event in reasoning_agent.run(input=messages, stream=True, stream_intermediate_steps=True):
114
+ if hasattr(event, "event"):
115
+ if event.event == RunEvent.run_content:
116
+ # Check for reasoning_content attribute first (native reasoning)
117
+ if hasattr(event, "reasoning_content") and event.reasoning_content:
118
+ reasoning_content += event.reasoning_content
119
+ yield (event.reasoning_content, None)
120
+ # Use the main content as reasoning content
121
+ elif hasattr(event, "content") and event.content:
122
+ reasoning_content += event.content
123
+ yield (event.content, None)
124
+ elif event.event == RunEvent.run_completed:
125
+ # Check for reasoning_content at completion (OpenAIResponses with reasoning_summary)
126
+ if hasattr(event, "reasoning_content") and event.reasoning_content:
127
+ # If we haven't accumulated any reasoning content yet, use this
128
+ if not reasoning_content:
129
+ reasoning_content = event.reasoning_content
130
+ yield (event.reasoning_content, None)
131
+ except Exception as e:
132
+ logger.warning(f"Reasoning error: {e}")
133
+ return
134
+
135
+ # Yield final message
136
+ if reasoning_content:
137
+ final_message = Message(
138
+ role="assistant",
139
+ content=f"<thinking>\n{reasoning_content}\n</thinking>",
140
+ reasoning_content=reasoning_content,
141
+ )
142
+ yield (None, final_message)
143
+
144
+
145
+ async def aget_openai_reasoning_stream(
146
+ reasoning_agent: "Agent", # type: ignore # noqa: F821
147
+ messages: List[Message],
148
+ ) -> AsyncIterator[Tuple[Optional[str], Optional[Message]]]:
149
+ """
150
+ Stream reasoning content from OpenAI model asynchronously.
151
+
152
+ For OpenAI reasoning models, we use the main content output as reasoning content.
153
+
154
+ Yields:
155
+ Tuple of (reasoning_content_delta, final_message)
156
+ - During streaming: (reasoning_content_delta, None)
157
+ - At the end: (None, final_message)
158
+ """
159
+ from agno.run.agent import RunEvent
160
+
161
+ # Update system message role to "system"
162
+ for message in messages:
163
+ if message.role == "developer":
164
+ message.role = "system"
165
+
166
+ reasoning_content: str = ""
167
+
168
+ try:
169
+ async for event in reasoning_agent.arun(input=messages, stream=True, stream_intermediate_steps=True):
170
+ if hasattr(event, "event"):
171
+ if event.event == RunEvent.run_content:
172
+ # Check for reasoning_content attribute first (native reasoning)
173
+ if hasattr(event, "reasoning_content") and event.reasoning_content:
174
+ reasoning_content += event.reasoning_content
175
+ yield (event.reasoning_content, None)
176
+ # Use the main content as reasoning content
177
+ elif hasattr(event, "content") and event.content:
178
+ reasoning_content += event.content
179
+ yield (event.content, None)
180
+ elif event.event == RunEvent.run_completed:
181
+ # Check for reasoning_content at completion (OpenAIResponses with reasoning_summary)
182
+ if hasattr(event, "reasoning_content") and event.reasoning_content:
183
+ # If we haven't accumulated any reasoning content yet, use this
184
+ if not reasoning_content:
185
+ reasoning_content = event.reasoning_content
186
+ yield (event.reasoning_content, None)
187
+ except Exception as e:
188
+ logger.warning(f"Reasoning error: {e}")
189
+ return
190
+
191
+ # Yield final message
192
+ if reasoning_content:
193
+ final_message = Message(
194
+ role="assistant",
195
+ content=f"<thinking>\n{reasoning_content}\n</thinking>",
196
+ reasoning_content=reasoning_content,
197
+ )
198
+ yield (None, final_message)
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import List, Optional
3
+ from typing import AsyncIterator, Iterator, List, Optional, Tuple
4
4
 
5
5
  from agno.models.base import Model
6
6
  from agno.models.message import Message
@@ -74,3 +74,87 @@ async def aget_vertexai_reasoning(reasoning_agent: "Agent", messages: List[Messa
74
74
  reasoning_content=reasoning_content,
75
75
  redacted_reasoning_content=redacted_reasoning_content,
76
76
  )
77
+
78
+
79
+ def get_vertexai_reasoning_stream(
80
+ reasoning_agent: "Agent", # type: ignore # noqa: F821
81
+ messages: List[Message],
82
+ ) -> Iterator[Tuple[Optional[str], Optional[Message]]]:
83
+ """
84
+ Stream reasoning content from VertexAI Claude model.
85
+
86
+ Yields:
87
+ Tuple of (reasoning_content_delta, final_message)
88
+ - During streaming: (reasoning_content_delta, None)
89
+ - At the end: (None, final_message)
90
+ """
91
+ from agno.run.agent import RunEvent
92
+
93
+ reasoning_content: str = ""
94
+ redacted_reasoning_content: Optional[str] = None
95
+
96
+ try:
97
+ for event in reasoning_agent.run(input=messages, stream=True, stream_intermediate_steps=True):
98
+ if hasattr(event, "event"):
99
+ if event.event == RunEvent.run_content:
100
+ # Stream reasoning content as it arrives
101
+ if hasattr(event, "reasoning_content") and event.reasoning_content:
102
+ reasoning_content += event.reasoning_content
103
+ yield (event.reasoning_content, None)
104
+ elif event.event == RunEvent.run_completed:
105
+ pass
106
+ except Exception as e:
107
+ logger.warning(f"Reasoning error: {e}")
108
+ return
109
+
110
+ # Yield final message
111
+ if reasoning_content:
112
+ final_message = Message(
113
+ role="assistant",
114
+ content=f"<thinking>\n{reasoning_content}\n</thinking>",
115
+ reasoning_content=reasoning_content,
116
+ redacted_reasoning_content=redacted_reasoning_content,
117
+ )
118
+ yield (None, final_message)
119
+
120
+
121
+ async def aget_vertexai_reasoning_stream(
122
+ reasoning_agent: "Agent", # type: ignore # noqa: F821
123
+ messages: List[Message],
124
+ ) -> AsyncIterator[Tuple[Optional[str], Optional[Message]]]:
125
+ """
126
+ Stream reasoning content from VertexAI Claude model asynchronously.
127
+
128
+ Yields:
129
+ Tuple of (reasoning_content_delta, final_message)
130
+ - During streaming: (reasoning_content_delta, None)
131
+ - At the end: (None, final_message)
132
+ """
133
+ from agno.run.agent import RunEvent
134
+
135
+ reasoning_content: str = ""
136
+ redacted_reasoning_content: Optional[str] = None
137
+
138
+ try:
139
+ async for event in reasoning_agent.arun(input=messages, stream=True, stream_intermediate_steps=True):
140
+ if hasattr(event, "event"):
141
+ if event.event == RunEvent.run_content:
142
+ # Stream reasoning content as it arrives
143
+ if hasattr(event, "reasoning_content") and event.reasoning_content:
144
+ reasoning_content += event.reasoning_content
145
+ yield (event.reasoning_content, None)
146
+ elif event.event == RunEvent.run_completed:
147
+ pass
148
+ except Exception as e:
149
+ logger.warning(f"Reasoning error: {e}")
150
+ return
151
+
152
+ # Yield final message
153
+ if reasoning_content:
154
+ final_message = Message(
155
+ role="assistant",
156
+ content=f"<thinking>\n{reasoning_content}\n</thinking>",
157
+ reasoning_content=reasoning_content,
158
+ redacted_reasoning_content=redacted_reasoning_content,
159
+ )
160
+ yield (None, final_message)
agno/run/agent.py CHANGED
@@ -156,6 +156,7 @@ class RunEvent(str, Enum):
156
156
 
157
157
  reasoning_started = "ReasoningStarted"
158
158
  reasoning_step = "ReasoningStep"
159
+ reasoning_content_delta = "ReasoningContentDelta"
159
160
  reasoning_completed = "ReasoningCompleted"
160
161
 
161
162
  memory_update_started = "MemoryUpdateStarted"
@@ -373,6 +374,14 @@ class ReasoningStepEvent(BaseAgentRunEvent):
373
374
  reasoning_content: str = ""
374
375
 
375
376
 
377
+ @dataclass
378
+ class ReasoningContentDeltaEvent(BaseAgentRunEvent):
379
+ """Event for streaming reasoning content chunks as they arrive."""
380
+
381
+ event: str = RunEvent.reasoning_content_delta.value
382
+ reasoning_content: str = "" # The delta/chunk of reasoning content
383
+
384
+
376
385
  @dataclass
377
386
  class ReasoningCompletedEvent(BaseAgentRunEvent):
378
387
  event: str = RunEvent.reasoning_completed.value
@@ -442,6 +451,7 @@ RunOutputEvent = Union[
442
451
  PostHookCompletedEvent,
443
452
  ReasoningStartedEvent,
444
453
  ReasoningStepEvent,
454
+ ReasoningContentDeltaEvent,
445
455
  ReasoningCompletedEvent,
446
456
  MemoryUpdateStartedEvent,
447
457
  MemoryUpdateCompletedEvent,
@@ -474,6 +484,7 @@ RUN_EVENT_TYPE_REGISTRY = {
474
484
  RunEvent.post_hook_completed.value: PostHookCompletedEvent,
475
485
  RunEvent.reasoning_started.value: ReasoningStartedEvent,
476
486
  RunEvent.reasoning_step.value: ReasoningStepEvent,
487
+ RunEvent.reasoning_content_delta.value: ReasoningContentDeltaEvent,
477
488
  RunEvent.reasoning_completed.value: ReasoningCompletedEvent,
478
489
  RunEvent.memory_update_started.value: MemoryUpdateStartedEvent,
479
490
  RunEvent.memory_update_completed.value: MemoryUpdateCompletedEvent,
agno/run/base.py CHANGED
@@ -22,7 +22,7 @@ class RunContext:
22
22
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
23
23
  metadata: Optional[Dict[str, Any]] = None
24
24
  session_state: Optional[Dict[str, Any]] = None
25
- output_schema: Optional[Type[BaseModel]] = None
25
+ output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None
26
26
 
27
27
 
28
28
  @dataclass
agno/run/team.py CHANGED
@@ -149,6 +149,7 @@ class TeamRunEvent(str, Enum):
149
149
 
150
150
  reasoning_started = "TeamReasoningStarted"
151
151
  reasoning_step = "TeamReasoningStep"
152
+ reasoning_content_delta = "TeamReasoningContentDelta"
152
153
  reasoning_completed = "TeamReasoningCompleted"
153
154
 
154
155
  memory_update_started = "TeamMemoryUpdateStarted"
@@ -346,6 +347,14 @@ class ReasoningStepEvent(BaseTeamRunEvent):
346
347
  reasoning_content: str = ""
347
348
 
348
349
 
350
+ @dataclass
351
+ class ReasoningContentDeltaEvent(BaseTeamRunEvent):
352
+ """Event for streaming reasoning content chunks as they arrive."""
353
+
354
+ event: str = TeamRunEvent.reasoning_content_delta.value
355
+ reasoning_content: str = "" # The delta/chunk of reasoning content
356
+
357
+
349
358
  @dataclass
350
359
  class ReasoningCompletedEvent(BaseTeamRunEvent):
351
360
  event: str = TeamRunEvent.reasoning_completed.value
@@ -411,6 +420,7 @@ TeamRunOutputEvent = Union[
411
420
  PreHookCompletedEvent,
412
421
  ReasoningStartedEvent,
413
422
  ReasoningStepEvent,
423
+ ReasoningContentDeltaEvent,
414
424
  ReasoningCompletedEvent,
415
425
  MemoryUpdateStartedEvent,
416
426
  MemoryUpdateCompletedEvent,
@@ -440,6 +450,7 @@ TEAM_RUN_EVENT_TYPE_REGISTRY = {
440
450
  TeamRunEvent.post_hook_completed.value: PostHookCompletedEvent,
441
451
  TeamRunEvent.reasoning_started.value: ReasoningStartedEvent,
442
452
  TeamRunEvent.reasoning_step.value: ReasoningStepEvent,
453
+ TeamRunEvent.reasoning_content_delta.value: ReasoningContentDeltaEvent,
443
454
  TeamRunEvent.reasoning_completed.value: ReasoningCompletedEvent,
444
455
  TeamRunEvent.memory_update_started.value: MemoryUpdateStartedEvent,
445
456
  TeamRunEvent.memory_update_completed.value: MemoryUpdateCompletedEvent,
agno/session/team.py CHANGED
@@ -91,10 +91,7 @@ class TeamSession:
91
91
 
92
92
  def upsert_run(self, run_response: Union[TeamRunOutput, RunOutput]):
93
93
  """Adds a RunOutput, together with some calculated data, to the runs list."""
94
-
95
94
  messages = run_response.messages
96
- if messages is None:
97
- return
98
95
 
99
96
  # Make message duration None
100
97
  for m in messages or []: