agno 2.1.10__py3-none-any.whl → 2.2.1__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.
- agno/agent/agent.py +1594 -1248
- agno/knowledge/knowledge.py +11 -0
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +14 -0
- agno/knowledge/types.py +1 -0
- agno/models/anthropic/claude.py +2 -2
- agno/models/base.py +4 -4
- agno/models/ollama/chat.py +7 -2
- agno/os/app.py +1 -1
- agno/os/interfaces/a2a/router.py +2 -2
- agno/os/interfaces/agui/router.py +2 -2
- agno/os/router.py +7 -7
- agno/os/routers/evals/schemas.py +31 -31
- agno/os/routers/health.py +6 -2
- agno/os/routers/knowledge/schemas.py +49 -47
- agno/os/routers/memory/schemas.py +16 -16
- agno/os/routers/metrics/schemas.py +16 -16
- agno/os/routers/session/session.py +382 -7
- agno/os/schema.py +254 -231
- agno/os/utils.py +1 -1
- agno/run/agent.py +54 -1
- agno/run/team.py +48 -0
- agno/run/workflow.py +15 -5
- agno/session/summary.py +45 -13
- agno/session/team.py +90 -5
- agno/team/team.py +1130 -849
- agno/utils/agent.py +372 -0
- agno/utils/events.py +144 -2
- agno/utils/message.py +60 -0
- agno/utils/print_response/agent.py +10 -6
- agno/utils/print_response/team.py +6 -4
- agno/utils/print_response/workflow.py +7 -5
- agno/utils/team.py +9 -8
- agno/workflow/condition.py +17 -9
- agno/workflow/loop.py +18 -10
- agno/workflow/parallel.py +14 -6
- agno/workflow/router.py +16 -8
- agno/workflow/step.py +14 -6
- agno/workflow/steps.py +14 -6
- agno/workflow/workflow.py +331 -123
- {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/METADATA +63 -23
- {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/RECORD +45 -43
- {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/WHEEL +0 -0
- {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/licenses/LICENSE +0 -0
- {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/top_level.txt +0 -0
agno/os/utils.py
CHANGED
|
@@ -158,7 +158,7 @@ def extract_input_media(run_dict: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
158
158
|
"files": [],
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
input = run_dict.get("input",
|
|
161
|
+
input = run_dict.get("input", {})
|
|
162
162
|
input_media["images"].extend(input.get("images", []))
|
|
163
163
|
input_media["videos"].extend(input.get("videos", []))
|
|
164
164
|
input_media["audios"].extend(input.get("audios", []))
|
agno/run/agent.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from dataclasses import asdict, dataclass, field
|
|
2
2
|
from enum import Enum
|
|
3
3
|
from time import time
|
|
4
|
-
from typing import Any, Dict, List, Optional, Sequence, Union
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence, Union
|
|
5
5
|
|
|
6
6
|
from pydantic import BaseModel
|
|
7
7
|
|
|
@@ -13,6 +13,9 @@ from agno.reasoning.step import ReasoningStep
|
|
|
13
13
|
from agno.run.base import BaseRunOutputEvent, MessageReferences, RunStatus
|
|
14
14
|
from agno.utils.log import logger
|
|
15
15
|
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from agno.session.summary import SessionSummary
|
|
18
|
+
|
|
16
19
|
|
|
17
20
|
@dataclass
|
|
18
21
|
class RunInput:
|
|
@@ -109,6 +112,7 @@ class RunEvent(str, Enum):
|
|
|
109
112
|
|
|
110
113
|
run_started = "RunStarted"
|
|
111
114
|
run_content = "RunContent"
|
|
115
|
+
run_content_completed = "RunContentCompleted"
|
|
112
116
|
run_intermediate_content = "RunIntermediateContent"
|
|
113
117
|
run_completed = "RunCompleted"
|
|
114
118
|
run_error = "RunError"
|
|
@@ -120,6 +124,9 @@ class RunEvent(str, Enum):
|
|
|
120
124
|
pre_hook_started = "PreHookStarted"
|
|
121
125
|
pre_hook_completed = "PreHookCompleted"
|
|
122
126
|
|
|
127
|
+
post_hook_started = "PostHookStarted"
|
|
128
|
+
post_hook_completed = "PostHookCompleted"
|
|
129
|
+
|
|
123
130
|
tool_call_started = "ToolCallStarted"
|
|
124
131
|
tool_call_completed = "ToolCallCompleted"
|
|
125
132
|
|
|
@@ -130,6 +137,9 @@ class RunEvent(str, Enum):
|
|
|
130
137
|
memory_update_started = "MemoryUpdateStarted"
|
|
131
138
|
memory_update_completed = "MemoryUpdateCompleted"
|
|
132
139
|
|
|
140
|
+
session_summary_started = "SessionSummaryStarted"
|
|
141
|
+
session_summary_completed = "SessionSummaryCompleted"
|
|
142
|
+
|
|
133
143
|
parser_model_response_started = "ParserModelResponseStarted"
|
|
134
144
|
parser_model_response_completed = "ParserModelResponseCompleted"
|
|
135
145
|
|
|
@@ -200,6 +210,11 @@ class RunContentEvent(BaseAgentRunEvent):
|
|
|
200
210
|
reasoning_messages: Optional[List[Message]] = None
|
|
201
211
|
|
|
202
212
|
|
|
213
|
+
@dataclass
|
|
214
|
+
class RunContentCompletedEvent(BaseAgentRunEvent):
|
|
215
|
+
event: str = RunEvent.run_content_completed.value
|
|
216
|
+
|
|
217
|
+
|
|
203
218
|
@dataclass
|
|
204
219
|
class IntermediateRunContentEvent(BaseAgentRunEvent):
|
|
205
220
|
event: str = RunEvent.run_intermediate_content.value
|
|
@@ -277,6 +292,18 @@ class PreHookCompletedEvent(BaseAgentRunEvent):
|
|
|
277
292
|
run_input: Optional[RunInput] = None
|
|
278
293
|
|
|
279
294
|
|
|
295
|
+
@dataclass
|
|
296
|
+
class PostHookStartedEvent(BaseAgentRunEvent):
|
|
297
|
+
event: str = RunEvent.post_hook_started.value
|
|
298
|
+
post_hook_name: Optional[str] = None
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
@dataclass
|
|
302
|
+
class PostHookCompletedEvent(BaseAgentRunEvent):
|
|
303
|
+
event: str = RunEvent.post_hook_completed.value
|
|
304
|
+
post_hook_name: Optional[str] = None
|
|
305
|
+
|
|
306
|
+
|
|
280
307
|
@dataclass
|
|
281
308
|
class MemoryUpdateStartedEvent(BaseAgentRunEvent):
|
|
282
309
|
event: str = RunEvent.memory_update_started.value
|
|
@@ -287,6 +314,17 @@ class MemoryUpdateCompletedEvent(BaseAgentRunEvent):
|
|
|
287
314
|
event: str = RunEvent.memory_update_completed.value
|
|
288
315
|
|
|
289
316
|
|
|
317
|
+
@dataclass
|
|
318
|
+
class SessionSummaryStartedEvent(BaseAgentRunEvent):
|
|
319
|
+
event: str = RunEvent.session_summary_started.value
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
@dataclass
|
|
323
|
+
class SessionSummaryCompletedEvent(BaseAgentRunEvent):
|
|
324
|
+
event: str = RunEvent.session_summary_completed.value
|
|
325
|
+
session_summary: Optional["SessionSummary"] = None
|
|
326
|
+
|
|
327
|
+
|
|
290
328
|
@dataclass
|
|
291
329
|
class ReasoningStartedEvent(BaseAgentRunEvent):
|
|
292
330
|
event: str = RunEvent.reasoning_started.value
|
|
@@ -347,11 +385,17 @@ class OutputModelResponseCompletedEvent(BaseAgentRunEvent):
|
|
|
347
385
|
class CustomEvent(BaseAgentRunEvent):
|
|
348
386
|
event: str = RunEvent.custom_event.value
|
|
349
387
|
|
|
388
|
+
def __init__(self, **kwargs):
|
|
389
|
+
# Store arbitrary attributes directly on the instance
|
|
390
|
+
for key, value in kwargs.items():
|
|
391
|
+
setattr(self, key, value)
|
|
392
|
+
|
|
350
393
|
|
|
351
394
|
RunOutputEvent = Union[
|
|
352
395
|
RunStartedEvent,
|
|
353
396
|
RunContentEvent,
|
|
354
397
|
IntermediateRunContentEvent,
|
|
398
|
+
RunContentCompletedEvent,
|
|
355
399
|
RunCompletedEvent,
|
|
356
400
|
RunErrorEvent,
|
|
357
401
|
RunCancelledEvent,
|
|
@@ -359,11 +403,15 @@ RunOutputEvent = Union[
|
|
|
359
403
|
RunContinuedEvent,
|
|
360
404
|
PreHookStartedEvent,
|
|
361
405
|
PreHookCompletedEvent,
|
|
406
|
+
PostHookStartedEvent,
|
|
407
|
+
PostHookCompletedEvent,
|
|
362
408
|
ReasoningStartedEvent,
|
|
363
409
|
ReasoningStepEvent,
|
|
364
410
|
ReasoningCompletedEvent,
|
|
365
411
|
MemoryUpdateStartedEvent,
|
|
366
412
|
MemoryUpdateCompletedEvent,
|
|
413
|
+
SessionSummaryStartedEvent,
|
|
414
|
+
SessionSummaryCompletedEvent,
|
|
367
415
|
ToolCallStartedEvent,
|
|
368
416
|
ToolCallCompletedEvent,
|
|
369
417
|
ParserModelResponseStartedEvent,
|
|
@@ -378,6 +426,7 @@ RunOutputEvent = Union[
|
|
|
378
426
|
RUN_EVENT_TYPE_REGISTRY = {
|
|
379
427
|
RunEvent.run_started.value: RunStartedEvent,
|
|
380
428
|
RunEvent.run_content.value: RunContentEvent,
|
|
429
|
+
RunEvent.run_content_completed.value: RunContentCompletedEvent,
|
|
381
430
|
RunEvent.run_intermediate_content.value: IntermediateRunContentEvent,
|
|
382
431
|
RunEvent.run_completed.value: RunCompletedEvent,
|
|
383
432
|
RunEvent.run_error.value: RunErrorEvent,
|
|
@@ -386,11 +435,15 @@ RUN_EVENT_TYPE_REGISTRY = {
|
|
|
386
435
|
RunEvent.run_continued.value: RunContinuedEvent,
|
|
387
436
|
RunEvent.pre_hook_started.value: PreHookStartedEvent,
|
|
388
437
|
RunEvent.pre_hook_completed.value: PreHookCompletedEvent,
|
|
438
|
+
RunEvent.post_hook_started.value: PostHookStartedEvent,
|
|
439
|
+
RunEvent.post_hook_completed.value: PostHookCompletedEvent,
|
|
389
440
|
RunEvent.reasoning_started.value: ReasoningStartedEvent,
|
|
390
441
|
RunEvent.reasoning_step.value: ReasoningStepEvent,
|
|
391
442
|
RunEvent.reasoning_completed.value: ReasoningCompletedEvent,
|
|
392
443
|
RunEvent.memory_update_started.value: MemoryUpdateStartedEvent,
|
|
393
444
|
RunEvent.memory_update_completed.value: MemoryUpdateCompletedEvent,
|
|
445
|
+
RunEvent.session_summary_started.value: SessionSummaryStartedEvent,
|
|
446
|
+
RunEvent.session_summary_completed.value: SessionSummaryCompletedEvent,
|
|
394
447
|
RunEvent.tool_call_started.value: ToolCallStartedEvent,
|
|
395
448
|
RunEvent.tool_call_completed.value: ToolCallCompletedEvent,
|
|
396
449
|
RunEvent.parser_model_response_started.value: ParserModelResponseStartedEvent,
|
agno/run/team.py
CHANGED
|
@@ -109,6 +109,7 @@ class TeamRunEvent(str, Enum):
|
|
|
109
109
|
run_started = "TeamRunStarted"
|
|
110
110
|
run_content = "TeamRunContent"
|
|
111
111
|
run_intermediate_content = "TeamRunIntermediateContent"
|
|
112
|
+
run_content_completed = "TeamRunContentCompleted"
|
|
112
113
|
run_completed = "TeamRunCompleted"
|
|
113
114
|
run_error = "TeamRunError"
|
|
114
115
|
run_cancelled = "TeamRunCancelled"
|
|
@@ -116,6 +117,9 @@ class TeamRunEvent(str, Enum):
|
|
|
116
117
|
pre_hook_started = "TeamPreHookStarted"
|
|
117
118
|
pre_hook_completed = "TeamPreHookCompleted"
|
|
118
119
|
|
|
120
|
+
post_hook_started = "TeamPostHookStarted"
|
|
121
|
+
post_hook_completed = "TeamPostHookCompleted"
|
|
122
|
+
|
|
119
123
|
tool_call_started = "TeamToolCallStarted"
|
|
120
124
|
tool_call_completed = "TeamToolCallCompleted"
|
|
121
125
|
|
|
@@ -126,6 +130,9 @@ class TeamRunEvent(str, Enum):
|
|
|
126
130
|
memory_update_started = "TeamMemoryUpdateStarted"
|
|
127
131
|
memory_update_completed = "TeamMemoryUpdateCompleted"
|
|
128
132
|
|
|
133
|
+
session_summary_started = "TeamSessionSummaryStarted"
|
|
134
|
+
session_summary_completed = "TeamSessionSummaryCompleted"
|
|
135
|
+
|
|
129
136
|
parser_model_response_started = "TeamParserModelResponseStarted"
|
|
130
137
|
parser_model_response_completed = "TeamParserModelResponseCompleted"
|
|
131
138
|
|
|
@@ -207,6 +214,11 @@ class IntermediateRunContentEvent(BaseTeamRunEvent):
|
|
|
207
214
|
content_type: str = "str"
|
|
208
215
|
|
|
209
216
|
|
|
217
|
+
@dataclass
|
|
218
|
+
class RunContentCompletedEvent(BaseTeamRunEvent):
|
|
219
|
+
event: str = TeamRunEvent.run_content_completed.value
|
|
220
|
+
|
|
221
|
+
|
|
210
222
|
@dataclass
|
|
211
223
|
class RunCompletedEvent(BaseTeamRunEvent):
|
|
212
224
|
event: str = TeamRunEvent.run_completed.value
|
|
@@ -263,6 +275,18 @@ class PreHookCompletedEvent(BaseTeamRunEvent):
|
|
|
263
275
|
run_input: Optional[TeamRunInput] = None
|
|
264
276
|
|
|
265
277
|
|
|
278
|
+
@dataclass
|
|
279
|
+
class PostHookStartedEvent(BaseTeamRunEvent):
|
|
280
|
+
event: str = TeamRunEvent.post_hook_started.value
|
|
281
|
+
post_hook_name: Optional[str] = None
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
@dataclass
|
|
285
|
+
class PostHookCompletedEvent(BaseTeamRunEvent):
|
|
286
|
+
event: str = TeamRunEvent.post_hook_completed.value
|
|
287
|
+
post_hook_name: Optional[str] = None
|
|
288
|
+
|
|
289
|
+
|
|
266
290
|
@dataclass
|
|
267
291
|
class MemoryUpdateStartedEvent(BaseTeamRunEvent):
|
|
268
292
|
event: str = TeamRunEvent.memory_update_started.value
|
|
@@ -273,6 +297,17 @@ class MemoryUpdateCompletedEvent(BaseTeamRunEvent):
|
|
|
273
297
|
event: str = TeamRunEvent.memory_update_completed.value
|
|
274
298
|
|
|
275
299
|
|
|
300
|
+
@dataclass
|
|
301
|
+
class SessionSummaryStartedEvent(BaseTeamRunEvent):
|
|
302
|
+
event: str = TeamRunEvent.session_summary_started.value
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
@dataclass
|
|
306
|
+
class SessionSummaryCompletedEvent(BaseTeamRunEvent):
|
|
307
|
+
event: str = TeamRunEvent.session_summary_completed.value
|
|
308
|
+
session_summary: Optional[Any] = None
|
|
309
|
+
|
|
310
|
+
|
|
276
311
|
@dataclass
|
|
277
312
|
class ReasoningStartedEvent(BaseTeamRunEvent):
|
|
278
313
|
event: str = TeamRunEvent.reasoning_started.value
|
|
@@ -333,11 +368,17 @@ class OutputModelResponseCompletedEvent(BaseTeamRunEvent):
|
|
|
333
368
|
class CustomEvent(BaseTeamRunEvent):
|
|
334
369
|
event: str = TeamRunEvent.custom_event.value
|
|
335
370
|
|
|
371
|
+
def __init__(self, **kwargs):
|
|
372
|
+
# Store arbitrary attributes directly on the instance
|
|
373
|
+
for key, value in kwargs.items():
|
|
374
|
+
setattr(self, key, value)
|
|
375
|
+
|
|
336
376
|
|
|
337
377
|
TeamRunOutputEvent = Union[
|
|
338
378
|
RunStartedEvent,
|
|
339
379
|
RunContentEvent,
|
|
340
380
|
IntermediateRunContentEvent,
|
|
381
|
+
RunContentCompletedEvent,
|
|
341
382
|
RunCompletedEvent,
|
|
342
383
|
RunErrorEvent,
|
|
343
384
|
RunCancelledEvent,
|
|
@@ -348,6 +389,8 @@ TeamRunOutputEvent = Union[
|
|
|
348
389
|
ReasoningCompletedEvent,
|
|
349
390
|
MemoryUpdateStartedEvent,
|
|
350
391
|
MemoryUpdateCompletedEvent,
|
|
392
|
+
SessionSummaryStartedEvent,
|
|
393
|
+
SessionSummaryCompletedEvent,
|
|
351
394
|
ToolCallStartedEvent,
|
|
352
395
|
ToolCallCompletedEvent,
|
|
353
396
|
ParserModelResponseStartedEvent,
|
|
@@ -362,16 +405,21 @@ TEAM_RUN_EVENT_TYPE_REGISTRY = {
|
|
|
362
405
|
TeamRunEvent.run_started.value: RunStartedEvent,
|
|
363
406
|
TeamRunEvent.run_content.value: RunContentEvent,
|
|
364
407
|
TeamRunEvent.run_intermediate_content.value: IntermediateRunContentEvent,
|
|
408
|
+
TeamRunEvent.run_content_completed.value: RunContentCompletedEvent,
|
|
365
409
|
TeamRunEvent.run_completed.value: RunCompletedEvent,
|
|
366
410
|
TeamRunEvent.run_error.value: RunErrorEvent,
|
|
367
411
|
TeamRunEvent.run_cancelled.value: RunCancelledEvent,
|
|
368
412
|
TeamRunEvent.pre_hook_started.value: PreHookStartedEvent,
|
|
369
413
|
TeamRunEvent.pre_hook_completed.value: PreHookCompletedEvent,
|
|
414
|
+
TeamRunEvent.post_hook_started.value: PostHookStartedEvent,
|
|
415
|
+
TeamRunEvent.post_hook_completed.value: PostHookCompletedEvent,
|
|
370
416
|
TeamRunEvent.reasoning_started.value: ReasoningStartedEvent,
|
|
371
417
|
TeamRunEvent.reasoning_step.value: ReasoningStepEvent,
|
|
372
418
|
TeamRunEvent.reasoning_completed.value: ReasoningCompletedEvent,
|
|
373
419
|
TeamRunEvent.memory_update_started.value: MemoryUpdateStartedEvent,
|
|
374
420
|
TeamRunEvent.memory_update_completed.value: MemoryUpdateCompletedEvent,
|
|
421
|
+
TeamRunEvent.session_summary_started.value: SessionSummaryStartedEvent,
|
|
422
|
+
TeamRunEvent.session_summary_completed.value: SessionSummaryCompletedEvent,
|
|
375
423
|
TeamRunEvent.tool_call_started.value: ToolCallStartedEvent,
|
|
376
424
|
TeamRunEvent.tool_call_completed.value: ToolCallCompletedEvent,
|
|
377
425
|
TeamRunEvent.parser_model_response_started.value: ParserModelResponseStartedEvent,
|
agno/run/workflow.py
CHANGED
|
@@ -6,9 +6,9 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
|
|
6
6
|
from pydantic import BaseModel
|
|
7
7
|
|
|
8
8
|
from agno.media import Audio, Image, Video
|
|
9
|
-
from agno.run.agent import RunOutput
|
|
9
|
+
from agno.run.agent import RunEvent, RunOutput, run_output_event_from_dict
|
|
10
10
|
from agno.run.base import BaseRunOutputEvent, RunStatus
|
|
11
|
-
from agno.run.team import TeamRunOutput
|
|
11
|
+
from agno.run.team import TeamRunEvent, TeamRunOutput, team_run_output_event_from_dict
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
from agno.workflow.types import StepOutput, WorkflowMetrics
|
|
@@ -388,6 +388,11 @@ class CustomEvent(BaseWorkflowRunOutputEvent):
|
|
|
388
388
|
|
|
389
389
|
event: str = WorkflowRunEvent.custom_event.value
|
|
390
390
|
|
|
391
|
+
def __init__(self, **kwargs):
|
|
392
|
+
# Store arbitrary attributes directly on the instance
|
|
393
|
+
for key, value in kwargs.items():
|
|
394
|
+
setattr(self, key, value)
|
|
395
|
+
|
|
391
396
|
|
|
392
397
|
# Union type for all workflow run response events
|
|
393
398
|
WorkflowRunOutputEvent = Union[
|
|
@@ -442,10 +447,15 @@ WORKFLOW_RUN_EVENT_TYPE_REGISTRY = {
|
|
|
442
447
|
|
|
443
448
|
def workflow_run_output_event_from_dict(data: dict) -> BaseWorkflowRunOutputEvent:
|
|
444
449
|
event_type = data.get("event", "")
|
|
445
|
-
|
|
446
|
-
|
|
450
|
+
if event_type in {e.value for e in RunEvent}:
|
|
451
|
+
return run_output_event_from_dict(data) # type: ignore
|
|
452
|
+
elif event_type in {e.value for e in TeamRunEvent}:
|
|
453
|
+
return team_run_output_event_from_dict(data) # type: ignore
|
|
454
|
+
else:
|
|
455
|
+
event_class = WORKFLOW_RUN_EVENT_TYPE_REGISTRY.get(event_type)
|
|
456
|
+
if not event_class:
|
|
447
457
|
raise ValueError(f"Unknown workflow event type: {event_type}")
|
|
448
|
-
return
|
|
458
|
+
return event_class.from_dict(data) # type: ignore
|
|
449
459
|
|
|
450
460
|
|
|
451
461
|
@dataclass
|
agno/session/summary.py
CHANGED
|
@@ -102,7 +102,23 @@ class SessionSummaryManager:
|
|
|
102
102
|
system_prompt += "<conversation>"
|
|
103
103
|
for message in conversation:
|
|
104
104
|
if message.role == "user":
|
|
105
|
-
|
|
105
|
+
# Handle empty user messages with media - note what media was provided
|
|
106
|
+
if not message.content or (isinstance(message.content, str) and message.content.strip() == ""):
|
|
107
|
+
media_types = []
|
|
108
|
+
if hasattr(message, "images") and message.images:
|
|
109
|
+
media_types.append(f"{len(message.images)} image(s)")
|
|
110
|
+
if hasattr(message, "videos") and message.videos:
|
|
111
|
+
media_types.append(f"{len(message.videos)} video(s)")
|
|
112
|
+
if hasattr(message, "audio") and message.audio:
|
|
113
|
+
media_types.append(f"{len(message.audio)} audio file(s)")
|
|
114
|
+
if hasattr(message, "files") and message.files:
|
|
115
|
+
media_types.append(f"{len(message.files)} file(s)")
|
|
116
|
+
|
|
117
|
+
if media_types:
|
|
118
|
+
conversation_messages.append(f"User: [Provided {', '.join(media_types)}]")
|
|
119
|
+
# Skip empty messages with no media
|
|
120
|
+
else:
|
|
121
|
+
conversation_messages.append(f"User: {message.content}")
|
|
106
122
|
elif message.role in ["assistant", "model"]:
|
|
107
123
|
conversation_messages.append(f"Assistant: {message.content}\n")
|
|
108
124
|
system_prompt += "\n".join(conversation_messages)
|
|
@@ -118,23 +134,27 @@ class SessionSummaryManager:
|
|
|
118
134
|
def _prepare_summary_messages(
|
|
119
135
|
self,
|
|
120
136
|
session: Optional["Session"] = None,
|
|
121
|
-
) -> List[Message]:
|
|
122
|
-
"""Prepare messages for session summary generation"""
|
|
137
|
+
) -> Optional[List[Message]]:
|
|
138
|
+
"""Prepare messages for session summary generation. Returns None if no meaningful messages to summarize."""
|
|
139
|
+
if not session:
|
|
140
|
+
return None
|
|
141
|
+
|
|
123
142
|
self.model = cast(Model, self.model)
|
|
124
143
|
response_format = self.get_response_format(self.model)
|
|
125
144
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
conversation=session.get_messages_for_session(), # type: ignore
|
|
130
|
-
response_format=response_format,
|
|
131
|
-
),
|
|
132
|
-
Message(role="user", content="Provide the summary of the conversation."),
|
|
133
|
-
]
|
|
134
|
-
if session
|
|
135
|
-
else []
|
|
145
|
+
system_message = self.get_system_message(
|
|
146
|
+
conversation=session.get_messages_for_session(), # type: ignore
|
|
147
|
+
response_format=response_format,
|
|
136
148
|
)
|
|
137
149
|
|
|
150
|
+
if system_message is None:
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
return [
|
|
154
|
+
system_message,
|
|
155
|
+
Message(role="user", content="Provide the summary of the conversation."),
|
|
156
|
+
]
|
|
157
|
+
|
|
138
158
|
def _process_summary_response(self, summary_response, session_summary_model: "Model") -> Optional[SessionSummary]: # type: ignore
|
|
139
159
|
"""Process the model response into a SessionSummary"""
|
|
140
160
|
from datetime import datetime
|
|
@@ -191,6 +211,12 @@ class SessionSummaryManager:
|
|
|
191
211
|
return None
|
|
192
212
|
|
|
193
213
|
messages = self._prepare_summary_messages(session)
|
|
214
|
+
|
|
215
|
+
# Skip summary generation if there are no meaningful messages
|
|
216
|
+
if messages is None:
|
|
217
|
+
log_debug("No meaningful messages to summarize, skipping session summary")
|
|
218
|
+
return None
|
|
219
|
+
|
|
194
220
|
response_format = self.get_response_format(self.model)
|
|
195
221
|
|
|
196
222
|
summary_response = self.model.response(messages=messages, response_format=response_format)
|
|
@@ -212,6 +238,12 @@ class SessionSummaryManager:
|
|
|
212
238
|
return None
|
|
213
239
|
|
|
214
240
|
messages = self._prepare_summary_messages(session)
|
|
241
|
+
|
|
242
|
+
# Skip summary generation if there are no meaningful messages
|
|
243
|
+
if messages is None:
|
|
244
|
+
log_debug("No meaningful messages to summarize, skipping session summary")
|
|
245
|
+
return None
|
|
246
|
+
|
|
215
247
|
response_format = self.get_response_format(self.model)
|
|
216
248
|
|
|
217
249
|
summary_response = await self.model.aresponse(messages=messages, response_format=response_format)
|
agno/session/team.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import asdict, dataclass
|
|
4
|
-
from typing import Any, Dict, List, Mapping, Optional, Union
|
|
4
|
+
from typing import Any, Dict, List, Mapping, Optional, Tuple, Union
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel
|
|
5
7
|
|
|
6
8
|
from agno.models.message import Message
|
|
7
9
|
from agno.run.agent import RunOutput, RunStatus
|
|
@@ -243,6 +245,78 @@ class TeamSession:
|
|
|
243
245
|
final_messages.append(assistant_message_from_run)
|
|
244
246
|
return final_messages
|
|
245
247
|
|
|
248
|
+
def get_team_history(self, num_runs: Optional[int] = None) -> List[Tuple[str, str]]:
|
|
249
|
+
"""Get team history as structured data (input, response pairs) -> This is the history of the team leader, not the members.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
num_runs: Number of recent runs to include. If None, returns all available history.
|
|
253
|
+
"""
|
|
254
|
+
if not self.runs:
|
|
255
|
+
return []
|
|
256
|
+
|
|
257
|
+
from agno.run.base import RunStatus
|
|
258
|
+
|
|
259
|
+
# Get completed runs only (exclude current/pending run)
|
|
260
|
+
completed_runs = [run for run in self.runs if run.status == RunStatus.completed and run.parent_run_id is None]
|
|
261
|
+
|
|
262
|
+
if num_runs is not None and len(completed_runs) > num_runs:
|
|
263
|
+
recent_runs = completed_runs[-num_runs:]
|
|
264
|
+
else:
|
|
265
|
+
recent_runs = completed_runs
|
|
266
|
+
|
|
267
|
+
if not recent_runs:
|
|
268
|
+
return []
|
|
269
|
+
|
|
270
|
+
# Return structured data as list of (input, response) tuples
|
|
271
|
+
history_data = []
|
|
272
|
+
for run in recent_runs:
|
|
273
|
+
# Get input
|
|
274
|
+
input_str = ""
|
|
275
|
+
if run.input:
|
|
276
|
+
input_str = run.input.input_content_string()
|
|
277
|
+
|
|
278
|
+
# Get response
|
|
279
|
+
response_str = ""
|
|
280
|
+
if run.content:
|
|
281
|
+
response_str = (
|
|
282
|
+
run.content.model_dump_json(indent=2, exclude_none=True)
|
|
283
|
+
if isinstance(run.content, BaseModel)
|
|
284
|
+
else str(run.content)
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
history_data.append((input_str, response_str))
|
|
288
|
+
|
|
289
|
+
return history_data
|
|
290
|
+
|
|
291
|
+
def get_team_history_context(self, num_runs: Optional[int] = None) -> Optional[str]:
|
|
292
|
+
"""Get formatted team history context for steps
|
|
293
|
+
|
|
294
|
+
Args:
|
|
295
|
+
num_runs: Number of recent runs to include. If None, returns all available history.
|
|
296
|
+
"""
|
|
297
|
+
history_data = self.get_team_history(num_runs)
|
|
298
|
+
|
|
299
|
+
if not history_data:
|
|
300
|
+
return None
|
|
301
|
+
|
|
302
|
+
# Format as team history context using the structured data
|
|
303
|
+
context_parts = ["<team_history_context>"]
|
|
304
|
+
|
|
305
|
+
for i, (input_str, response_str) in enumerate(history_data, 1):
|
|
306
|
+
context_parts.append(f"[run-{i}]")
|
|
307
|
+
|
|
308
|
+
if input_str:
|
|
309
|
+
context_parts.append(f"input: {input_str}")
|
|
310
|
+
if response_str:
|
|
311
|
+
context_parts.append(f"response: {response_str}")
|
|
312
|
+
|
|
313
|
+
context_parts.append("") # Empty line between runs
|
|
314
|
+
|
|
315
|
+
context_parts.append("</team_history_context>")
|
|
316
|
+
context_parts.append("") # Empty line before current input
|
|
317
|
+
|
|
318
|
+
return "\n".join(context_parts)
|
|
319
|
+
|
|
246
320
|
def get_session_summary(self) -> Optional[SessionSummary]:
|
|
247
321
|
"""Get the session summary for the session"""
|
|
248
322
|
|
|
@@ -252,17 +326,28 @@ class TeamSession:
|
|
|
252
326
|
return self.summary # type: ignore
|
|
253
327
|
|
|
254
328
|
# Chat History functions
|
|
255
|
-
def get_chat_history(
|
|
256
|
-
|
|
329
|
+
def get_chat_history(
|
|
330
|
+
self, skip_history_messages: bool = True, skip_roles: Optional[List[str]] = None
|
|
331
|
+
) -> List[Message]:
|
|
332
|
+
"""
|
|
333
|
+
Get the chat history for the session.
|
|
334
|
+
This is all messages across all runs for the team leader.
|
|
335
|
+
"""
|
|
257
336
|
|
|
258
337
|
messages = []
|
|
259
338
|
if self.runs is None:
|
|
260
339
|
return []
|
|
261
340
|
|
|
262
341
|
for run in self.runs or []:
|
|
263
|
-
if run.
|
|
342
|
+
if run.parent_run_id is not None:
|
|
264
343
|
continue
|
|
265
344
|
|
|
266
|
-
|
|
345
|
+
if run.messages is not None:
|
|
346
|
+
for msg in run.messages or []:
|
|
347
|
+
if skip_history_messages and msg.from_history:
|
|
348
|
+
continue
|
|
349
|
+
if skip_roles and msg.role in skip_roles:
|
|
350
|
+
continue
|
|
351
|
+
messages.append(msg)
|
|
267
352
|
|
|
268
353
|
return messages
|