agno 2.3.25__py3-none-any.whl → 2.4.0__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/__init__.py +4 -0
- agno/agent/agent.py +1428 -558
- agno/agent/remote.py +13 -0
- agno/db/base.py +339 -0
- agno/db/postgres/async_postgres.py +116 -12
- agno/db/postgres/postgres.py +1229 -25
- agno/db/postgres/schemas.py +48 -1
- agno/db/sqlite/async_sqlite.py +119 -4
- agno/db/sqlite/schemas.py +51 -0
- agno/db/sqlite/sqlite.py +1173 -13
- agno/db/utils.py +37 -1
- agno/knowledge/__init__.py +4 -0
- agno/knowledge/chunking/code.py +1 -1
- agno/knowledge/chunking/semantic.py +1 -1
- agno/knowledge/chunking/strategy.py +4 -0
- agno/knowledge/filesystem.py +412 -0
- agno/knowledge/knowledge.py +2767 -2254
- agno/knowledge/protocol.py +134 -0
- agno/knowledge/reader/arxiv_reader.py +2 -2
- agno/knowledge/reader/base.py +9 -7
- agno/knowledge/reader/csv_reader.py +5 -5
- agno/knowledge/reader/docx_reader.py +2 -2
- agno/knowledge/reader/field_labeled_csv_reader.py +2 -2
- agno/knowledge/reader/firecrawl_reader.py +2 -2
- agno/knowledge/reader/json_reader.py +2 -2
- agno/knowledge/reader/markdown_reader.py +2 -2
- agno/knowledge/reader/pdf_reader.py +5 -4
- agno/knowledge/reader/pptx_reader.py +2 -2
- agno/knowledge/reader/reader_factory.py +110 -0
- agno/knowledge/reader/s3_reader.py +2 -2
- agno/knowledge/reader/tavily_reader.py +2 -2
- agno/knowledge/reader/text_reader.py +2 -2
- agno/knowledge/reader/web_search_reader.py +2 -2
- agno/knowledge/reader/website_reader.py +5 -3
- agno/knowledge/reader/wikipedia_reader.py +2 -2
- agno/knowledge/reader/youtube_reader.py +2 -2
- agno/knowledge/utils.py +37 -29
- agno/learn/__init__.py +6 -0
- agno/learn/machine.py +35 -0
- agno/learn/schemas.py +82 -11
- agno/learn/stores/__init__.py +3 -0
- agno/learn/stores/decision_log.py +1156 -0
- agno/learn/stores/learned_knowledge.py +6 -6
- agno/models/anthropic/claude.py +24 -0
- agno/models/aws/bedrock.py +20 -0
- agno/models/base.py +48 -4
- agno/models/cohere/chat.py +25 -0
- agno/models/google/gemini.py +50 -5
- agno/models/litellm/chat.py +38 -0
- agno/models/openai/chat.py +7 -0
- agno/models/openrouter/openrouter.py +46 -0
- agno/models/response.py +16 -0
- agno/os/app.py +83 -44
- agno/os/middleware/__init__.py +2 -0
- agno/os/middleware/trailing_slash.py +27 -0
- agno/os/router.py +1 -0
- agno/os/routers/agents/router.py +29 -16
- agno/os/routers/agents/schema.py +6 -4
- agno/os/routers/components/__init__.py +3 -0
- agno/os/routers/components/components.py +466 -0
- agno/os/routers/evals/schemas.py +4 -3
- agno/os/routers/health.py +3 -3
- agno/os/routers/knowledge/knowledge.py +3 -3
- agno/os/routers/memory/schemas.py +4 -2
- agno/os/routers/metrics/metrics.py +9 -11
- agno/os/routers/metrics/schemas.py +10 -6
- agno/os/routers/registry/__init__.py +3 -0
- agno/os/routers/registry/registry.py +337 -0
- agno/os/routers/teams/router.py +20 -8
- agno/os/routers/teams/schema.py +6 -4
- agno/os/routers/traces/traces.py +5 -5
- agno/os/routers/workflows/router.py +38 -11
- agno/os/routers/workflows/schema.py +1 -1
- agno/os/schema.py +92 -26
- agno/os/utils.py +133 -16
- agno/reasoning/anthropic.py +2 -2
- agno/reasoning/azure_ai_foundry.py +2 -2
- agno/reasoning/deepseek.py +2 -2
- agno/reasoning/default.py +6 -7
- agno/reasoning/gemini.py +2 -2
- agno/reasoning/helpers.py +6 -7
- agno/reasoning/manager.py +4 -10
- agno/reasoning/ollama.py +2 -2
- agno/reasoning/openai.py +2 -2
- agno/reasoning/vertexai.py +2 -2
- agno/registry/__init__.py +3 -0
- agno/registry/registry.py +68 -0
- agno/run/agent.py +57 -0
- agno/run/base.py +7 -0
- agno/run/team.py +57 -0
- agno/skills/agent_skills.py +10 -3
- agno/team/__init__.py +3 -1
- agno/team/team.py +1276 -326
- agno/tools/duckduckgo.py +25 -71
- agno/tools/exa.py +0 -21
- agno/tools/function.py +35 -83
- agno/tools/knowledge.py +9 -4
- agno/tools/mem0.py +11 -10
- agno/tools/memory.py +47 -46
- agno/tools/parallel.py +0 -7
- agno/tools/reasoning.py +30 -23
- agno/tools/tavily.py +4 -1
- agno/tools/websearch.py +93 -0
- agno/tools/website.py +1 -1
- agno/tools/wikipedia.py +1 -1
- agno/tools/workflow.py +48 -47
- agno/utils/agent.py +42 -5
- agno/utils/events.py +160 -2
- agno/utils/print_response/agent.py +0 -31
- agno/utils/print_response/team.py +0 -2
- agno/utils/print_response/workflow.py +0 -2
- agno/utils/team.py +61 -11
- agno/vectordb/lancedb/lance_db.py +4 -1
- agno/vectordb/mongodb/mongodb.py +1 -1
- agno/vectordb/qdrant/qdrant.py +4 -4
- agno/workflow/__init__.py +3 -1
- agno/workflow/condition.py +0 -21
- agno/workflow/loop.py +0 -21
- agno/workflow/parallel.py +0 -21
- agno/workflow/router.py +0 -21
- agno/workflow/step.py +117 -24
- agno/workflow/steps.py +0 -21
- agno/workflow/workflow.py +625 -63
- {agno-2.3.25.dist-info → agno-2.4.0.dist-info}/METADATA +46 -76
- {agno-2.3.25.dist-info → agno-2.4.0.dist-info}/RECORD +128 -117
- {agno-2.3.25.dist-info → agno-2.4.0.dist-info}/WHEEL +0 -0
- {agno-2.3.25.dist-info → agno-2.4.0.dist-info}/licenses/LICENSE +0 -0
- {agno-2.3.25.dist-info → agno-2.4.0.dist-info}/top_level.txt +0 -0
agno/os/schema.py
CHANGED
|
@@ -16,11 +16,7 @@ from agno.os.config import (
|
|
|
16
16
|
SessionConfig,
|
|
17
17
|
TracesConfig,
|
|
18
18
|
)
|
|
19
|
-
from agno.os.utils import
|
|
20
|
-
extract_input_media,
|
|
21
|
-
get_run_input,
|
|
22
|
-
get_session_name,
|
|
23
|
-
)
|
|
19
|
+
from agno.os.utils import extract_input_media, get_run_input, get_session_name, to_utc_datetime
|
|
24
20
|
from agno.session import AgentSession, TeamSession, WorkflowSession
|
|
25
21
|
from agno.team.remote import RemoteTeam
|
|
26
22
|
from agno.team.team import Team
|
|
@@ -79,10 +75,12 @@ class InternalServerErrorResponse(BaseModel):
|
|
|
79
75
|
|
|
80
76
|
|
|
81
77
|
class HealthResponse(BaseModel):
|
|
82
|
-
model_config = ConfigDict(
|
|
78
|
+
model_config = ConfigDict(
|
|
79
|
+
json_schema_extra={"example": {"status": "ok", "instantiated_at": "2025-06-10T12:00:00Z"}}
|
|
80
|
+
)
|
|
83
81
|
|
|
84
82
|
status: str = Field(..., description="Health status of the service")
|
|
85
|
-
instantiated_at:
|
|
83
|
+
instantiated_at: datetime = Field(..., description="Timestamp when service was instantiated")
|
|
86
84
|
|
|
87
85
|
|
|
88
86
|
class InterfaceResponse(BaseModel):
|
|
@@ -145,7 +143,8 @@ class ConfigResponse(BaseModel):
|
|
|
145
143
|
name: Optional[str] = Field(None, description="Name of the OS instance")
|
|
146
144
|
description: Optional[str] = Field(None, description="Description of the OS instance")
|
|
147
145
|
available_models: Optional[List[str]] = Field(None, description="List of available models")
|
|
148
|
-
|
|
146
|
+
os_database: Optional[str] = Field(None, description="ID of the database used for the OS instance")
|
|
147
|
+
databases: List[str] = Field(..., description="List of database IDs used by the components of the OS instance")
|
|
149
148
|
chat: Optional[ChatConfig] = Field(None, description="Chat configuration")
|
|
150
149
|
|
|
151
150
|
session: Optional[SessionConfig] = Field(None, description="Session configuration")
|
|
@@ -212,8 +211,8 @@ class SessionSchema(BaseModel):
|
|
|
212
211
|
return None
|
|
213
212
|
return None
|
|
214
213
|
|
|
215
|
-
created_at =
|
|
216
|
-
updated_at =
|
|
214
|
+
created_at = to_utc_datetime(session.get("created_at", 0))
|
|
215
|
+
updated_at = to_utc_datetime(session.get("updated_at", created_at))
|
|
217
216
|
return cls(
|
|
218
217
|
session_id=session.get("session_id", ""),
|
|
219
218
|
session_name=session_name,
|
|
@@ -282,8 +281,8 @@ class AgentSessionDetailSchema(BaseModel):
|
|
|
282
281
|
metrics=session.session_data.get("session_metrics", {}) if session.session_data else None, # type: ignore
|
|
283
282
|
metadata=session.metadata,
|
|
284
283
|
chat_history=[message.to_dict() for message in session.get_chat_history()],
|
|
285
|
-
created_at=created_at,
|
|
286
|
-
updated_at=updated_at,
|
|
284
|
+
created_at=to_utc_datetime(created_at),
|
|
285
|
+
updated_at=to_utc_datetime(updated_at),
|
|
287
286
|
)
|
|
288
287
|
|
|
289
288
|
|
|
@@ -322,8 +321,8 @@ class TeamSessionDetailSchema(BaseModel):
|
|
|
322
321
|
metrics=session.session_data.get("session_metrics", {}) if session.session_data else None,
|
|
323
322
|
metadata=session.metadata,
|
|
324
323
|
chat_history=[message.to_dict() for message in session.get_chat_history()],
|
|
325
|
-
created_at=created_at,
|
|
326
|
-
updated_at=updated_at,
|
|
324
|
+
created_at=to_utc_datetime(created_at),
|
|
325
|
+
updated_at=to_utc_datetime(updated_at),
|
|
327
326
|
)
|
|
328
327
|
|
|
329
328
|
|
|
@@ -339,13 +338,15 @@ class WorkflowSessionDetailSchema(BaseModel):
|
|
|
339
338
|
workflow_data: Optional[dict] = Field(None, description="Workflow-specific data")
|
|
340
339
|
metadata: Optional[dict] = Field(None, description="Additional metadata")
|
|
341
340
|
|
|
342
|
-
created_at: Optional[
|
|
343
|
-
updated_at: Optional[
|
|
341
|
+
created_at: Optional[datetime] = Field(None, description="Session creation timestamp")
|
|
342
|
+
updated_at: Optional[datetime] = Field(None, description="Last update timestamp")
|
|
344
343
|
|
|
345
344
|
@classmethod
|
|
346
345
|
def from_session(cls, session: WorkflowSession) -> "WorkflowSessionDetailSchema":
|
|
347
346
|
session_dict = session.to_dict()
|
|
348
347
|
session_name = get_session_name({**session_dict, "session_type": "workflow"})
|
|
348
|
+
created_at = datetime.fromtimestamp(session.created_at, tz=timezone.utc) if session.created_at else None
|
|
349
|
+
updated_at = datetime.fromtimestamp(session.updated_at, tz=timezone.utc) if session.updated_at else created_at
|
|
349
350
|
return cls(
|
|
350
351
|
session_id=session.session_id,
|
|
351
352
|
user_id=session.user_id,
|
|
@@ -356,8 +357,8 @@ class WorkflowSessionDetailSchema(BaseModel):
|
|
|
356
357
|
session_state=session.session_data.get("session_state", None) if session.session_data else None,
|
|
357
358
|
workflow_data=session.workflow_data,
|
|
358
359
|
metadata=session.metadata,
|
|
359
|
-
created_at=
|
|
360
|
-
updated_at=
|
|
360
|
+
created_at=to_utc_datetime(created_at),
|
|
361
|
+
updated_at=to_utc_datetime(updated_at),
|
|
361
362
|
)
|
|
362
363
|
|
|
363
364
|
|
|
@@ -418,9 +419,7 @@ class RunSchema(BaseModel):
|
|
|
418
419
|
files=run_dict.get("files", []),
|
|
419
420
|
response_audio=run_dict.get("response_audio", None),
|
|
420
421
|
input_media=extract_input_media(run_dict),
|
|
421
|
-
created_at=
|
|
422
|
-
if run_dict.get("created_at") is not None
|
|
423
|
-
else None,
|
|
422
|
+
created_at=to_utc_datetime(run_dict.get("created_at")),
|
|
424
423
|
)
|
|
425
424
|
|
|
426
425
|
|
|
@@ -468,9 +467,7 @@ class TeamRunSchema(BaseModel):
|
|
|
468
467
|
messages=[message for message in run_dict.get("messages", [])] if run_dict.get("messages") else None,
|
|
469
468
|
tools=[tool for tool in run_dict.get("tools", [])] if run_dict.get("tools") else None,
|
|
470
469
|
events=[event for event in run_dict["events"]] if run_dict.get("events") else None,
|
|
471
|
-
created_at=
|
|
472
|
-
if run_dict.get("created_at") is not None
|
|
473
|
-
else None,
|
|
470
|
+
created_at=to_utc_datetime(run_dict.get("created_at")),
|
|
474
471
|
references=run_dict.get("references", []),
|
|
475
472
|
citations=run_dict.get("citations", None),
|
|
476
473
|
reasoning_messages=run_dict.get("reasoning_messages", []),
|
|
@@ -496,7 +493,7 @@ class WorkflowRunSchema(BaseModel):
|
|
|
496
493
|
step_results: Optional[list[dict]] = Field(None, description="Results from each workflow step")
|
|
497
494
|
step_executor_runs: Optional[list[dict]] = Field(None, description="Executor runs for each step")
|
|
498
495
|
metrics: Optional[dict] = Field(None, description="Performance and usage metrics")
|
|
499
|
-
created_at: Optional[
|
|
496
|
+
created_at: Optional[datetime] = Field(None, description="Run creation timestamp")
|
|
500
497
|
reasoning_content: Optional[str] = Field(None, description="Reasoning content if reasoning was enabled")
|
|
501
498
|
reasoning_steps: Optional[List[dict]] = Field(None, description="List of reasoning steps")
|
|
502
499
|
references: Optional[List[dict]] = Field(None, description="References cited in the workflow")
|
|
@@ -525,7 +522,7 @@ class WorkflowRunSchema(BaseModel):
|
|
|
525
522
|
metrics=run_response.get("metrics", {}),
|
|
526
523
|
step_results=run_response.get("step_results", []),
|
|
527
524
|
step_executor_runs=run_response.get("step_executor_runs", []),
|
|
528
|
-
created_at=run_response
|
|
525
|
+
created_at=to_utc_datetime(run_response.get("created_at")),
|
|
529
526
|
reasoning_content=run_response.get("reasoning_content", ""),
|
|
530
527
|
reasoning_steps=run_response.get("reasoning_steps", []),
|
|
531
528
|
references=run_response.get("references", []),
|
|
@@ -560,3 +557,72 @@ class PaginatedResponse(BaseModel, Generic[T]):
|
|
|
560
557
|
|
|
561
558
|
data: List[T] = Field(..., description="List of items for the current page")
|
|
562
559
|
meta: PaginationInfo = Field(..., description="Pagination metadata")
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
class ComponentType(str, Enum):
|
|
563
|
+
AGENT = "agent"
|
|
564
|
+
TEAM = "team"
|
|
565
|
+
WORKFLOW = "workflow"
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
class ComponentCreate(BaseModel):
|
|
569
|
+
name: str = Field(..., description="Display name")
|
|
570
|
+
component_id: Optional[str] = Field(
|
|
571
|
+
None, description="Unique identifier for the entity. Auto-generated from name if not provided."
|
|
572
|
+
)
|
|
573
|
+
component_type: ComponentType = Field(..., description="Type of entity: agent, team, or workflow")
|
|
574
|
+
description: Optional[str] = Field(None, description="Optional description")
|
|
575
|
+
metadata: Optional[Dict[str, Any]] = Field(None, description="Optional metadata")
|
|
576
|
+
# Config parameters are optional, but if provided, they will be used to create the initial config
|
|
577
|
+
config: Optional[Dict[str, Any]] = Field(None, description="Optional configuration")
|
|
578
|
+
label: Optional[str] = Field(None, description="Optional label (e.g., 'stable')")
|
|
579
|
+
stage: str = Field("draft", description="Stage: 'draft' or 'published'")
|
|
580
|
+
notes: Optional[str] = Field(None, description="Optional notes")
|
|
581
|
+
set_current: bool = Field(True, description="Set as current version")
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
class ComponentResponse(BaseModel):
|
|
585
|
+
component_id: str
|
|
586
|
+
component_type: ComponentType
|
|
587
|
+
name: Optional[str] = None
|
|
588
|
+
description: Optional[str] = None
|
|
589
|
+
current_version: Optional[int] = None
|
|
590
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
591
|
+
created_at: int
|
|
592
|
+
updated_at: Optional[int] = None
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
class ConfigCreate(BaseModel):
|
|
596
|
+
config: Dict[str, Any] = Field(..., description="The configuration data")
|
|
597
|
+
version: Optional[int] = Field(None, description="Optional version number")
|
|
598
|
+
label: Optional[str] = Field(None, description="Optional label (e.g., 'stable')")
|
|
599
|
+
stage: str = Field("draft", description="Stage: 'draft' or 'published'")
|
|
600
|
+
notes: Optional[str] = Field(None, description="Optional notes")
|
|
601
|
+
links: Optional[List[Dict[str, Any]]] = Field(None, description="Optional links to child components")
|
|
602
|
+
set_current: bool = Field(True, description="Set as current version")
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
class ComponentConfigResponse(BaseModel):
|
|
606
|
+
component_id: str
|
|
607
|
+
version: int
|
|
608
|
+
label: Optional[str] = None
|
|
609
|
+
stage: str
|
|
610
|
+
config: Dict[str, Any]
|
|
611
|
+
notes: Optional[str] = None
|
|
612
|
+
created_at: int
|
|
613
|
+
updated_at: Optional[int] = None
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
class ComponentUpdate(BaseModel):
|
|
617
|
+
name: Optional[str] = None
|
|
618
|
+
description: Optional[str] = None
|
|
619
|
+
component_type: Optional[str] = None
|
|
620
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
class ConfigUpdate(BaseModel):
|
|
624
|
+
config: Optional[Dict[str, Any]] = None
|
|
625
|
+
label: Optional[str] = None
|
|
626
|
+
stage: Optional[str] = None
|
|
627
|
+
notes: Optional[str] = None
|
|
628
|
+
links: Optional[List[Dict[str, Any]]] = None
|
agno/os/utils.py
CHANGED
|
@@ -14,6 +14,7 @@ from agno.media import Audio, Image, Video
|
|
|
14
14
|
from agno.media import File as FileMedia
|
|
15
15
|
from agno.models.message import Message
|
|
16
16
|
from agno.os.config import AgentOSConfig
|
|
17
|
+
from agno.registry import Registry
|
|
17
18
|
from agno.remote.base import RemoteDb, RemoteKnowledge
|
|
18
19
|
from agno.run.agent import RunOutputEvent
|
|
19
20
|
from agno.run.team import TeamRunOutputEvent
|
|
@@ -24,6 +25,20 @@ from agno.utils.log import log_warning, logger
|
|
|
24
25
|
from agno.workflow import RemoteWorkflow, Workflow
|
|
25
26
|
|
|
26
27
|
|
|
28
|
+
def to_utc_datetime(value: Optional[Union[int, float, datetime]]) -> Optional[datetime]:
|
|
29
|
+
"""Convert a timestamp to a UTC datetime."""
|
|
30
|
+
if value is None:
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
if isinstance(value, datetime):
|
|
34
|
+
# If already a datetime, make sure the timezone is UTC
|
|
35
|
+
if value.tzinfo is None:
|
|
36
|
+
return value.replace(tzinfo=timezone.utc)
|
|
37
|
+
return value
|
|
38
|
+
|
|
39
|
+
return datetime.fromtimestamp(value, tz=timezone.utc)
|
|
40
|
+
|
|
41
|
+
|
|
27
42
|
async def get_request_kwargs(request: Request, endpoint_func: Callable) -> Dict[str, Any]:
|
|
28
43
|
"""Given a Request and an endpoint function, return a dictionary with all extra form data fields.
|
|
29
44
|
|
|
@@ -414,38 +429,140 @@ def extract_format(file: UploadFile) -> Optional[str]:
|
|
|
414
429
|
|
|
415
430
|
|
|
416
431
|
def get_agent_by_id(
|
|
417
|
-
agent_id: str,
|
|
432
|
+
agent_id: str,
|
|
433
|
+
agents: Optional[List[Union[Agent, RemoteAgent]]] = None,
|
|
434
|
+
db: Optional[Union[BaseDb, AsyncBaseDb]] = None,
|
|
435
|
+
registry: Optional[Registry] = None,
|
|
436
|
+
version: Optional[int] = None,
|
|
437
|
+
create_fresh: bool = False,
|
|
418
438
|
) -> Optional[Union[Agent, RemoteAgent]]:
|
|
419
|
-
|
|
439
|
+
"""Get an agent by ID, optionally creating a fresh instance for request isolation.
|
|
440
|
+
|
|
441
|
+
When create_fresh=True, creates a new agent instance using deep_copy() to prevent
|
|
442
|
+
state contamination between concurrent requests. The new instance shares heavy
|
|
443
|
+
resources (db, model, MCP tools) but has isolated mutable state.
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
agent_id: The agent ID to look up
|
|
447
|
+
agents: List of agents to search
|
|
448
|
+
create_fresh: If True, creates a new instance using deep_copy()
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
The agent instance (shared or fresh copy based on create_fresh)
|
|
452
|
+
"""
|
|
453
|
+
if agent_id is None:
|
|
420
454
|
return None
|
|
421
455
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
456
|
+
# Try to get the agent from the list of agents
|
|
457
|
+
if agents:
|
|
458
|
+
for agent in agents:
|
|
459
|
+
if agent.id == agent_id:
|
|
460
|
+
if create_fresh and isinstance(agent, Agent):
|
|
461
|
+
return agent.deep_copy()
|
|
462
|
+
return agent
|
|
463
|
+
|
|
464
|
+
# Try to get the agent from the database
|
|
465
|
+
if db and isinstance(db, BaseDb):
|
|
466
|
+
from agno.agent.agent import get_agent_by_id as get_agent_by_id_db
|
|
467
|
+
|
|
468
|
+
try:
|
|
469
|
+
db_agent = get_agent_by_id_db(db=db, id=agent_id, version=version, registry=registry)
|
|
470
|
+
return db_agent
|
|
471
|
+
except Exception as e:
|
|
472
|
+
logger.error(f"Error getting agent {agent_id} from database: {e}")
|
|
473
|
+
return None
|
|
474
|
+
|
|
425
475
|
return None
|
|
426
476
|
|
|
427
477
|
|
|
428
478
|
def get_team_by_id(
|
|
429
|
-
team_id: str,
|
|
479
|
+
team_id: str,
|
|
480
|
+
teams: Optional[List[Union[Team, RemoteTeam]]] = None,
|
|
481
|
+
create_fresh: bool = False,
|
|
482
|
+
db: Optional[Union[BaseDb, AsyncBaseDb]] = None,
|
|
483
|
+
version: Optional[int] = None,
|
|
484
|
+
registry: Optional[Registry] = None,
|
|
430
485
|
) -> Optional[Union[Team, RemoteTeam]]:
|
|
431
|
-
|
|
486
|
+
"""Get a team by ID, optionally creating a fresh instance for request isolation.
|
|
487
|
+
|
|
488
|
+
When create_fresh=True, creates a new team instance using deep_copy() to prevent
|
|
489
|
+
state contamination between concurrent requests. Member agents are also deep copied.
|
|
490
|
+
|
|
491
|
+
Args:
|
|
492
|
+
team_id: The team ID to look up
|
|
493
|
+
teams: List of teams to search
|
|
494
|
+
create_fresh: If True, creates a new instance using deep_copy()
|
|
495
|
+
|
|
496
|
+
Returns:
|
|
497
|
+
The team instance (shared or fresh copy based on create_fresh)
|
|
498
|
+
"""
|
|
499
|
+
if team_id is None:
|
|
432
500
|
return None
|
|
433
501
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
502
|
+
if teams:
|
|
503
|
+
for team in teams:
|
|
504
|
+
if team.id == team_id:
|
|
505
|
+
if create_fresh and isinstance(team, Team):
|
|
506
|
+
return team.deep_copy()
|
|
507
|
+
return team
|
|
508
|
+
|
|
509
|
+
if db and isinstance(db, BaseDb):
|
|
510
|
+
from agno.team.team import get_team_by_id as get_team_by_id_db
|
|
511
|
+
|
|
512
|
+
try:
|
|
513
|
+
db_team = get_team_by_id_db(db=db, id=team_id, version=version, registry=registry)
|
|
514
|
+
return db_team
|
|
515
|
+
except Exception as e:
|
|
516
|
+
logger.error(f"Error getting team {team_id} from database: {e}")
|
|
517
|
+
return None
|
|
518
|
+
|
|
437
519
|
return None
|
|
438
520
|
|
|
439
521
|
|
|
440
522
|
def get_workflow_by_id(
|
|
441
|
-
workflow_id: str,
|
|
523
|
+
workflow_id: str,
|
|
524
|
+
workflows: Optional[List[Union[Workflow, RemoteWorkflow]]] = None,
|
|
525
|
+
create_fresh: bool = False,
|
|
526
|
+
db: Optional[Union[BaseDb, AsyncBaseDb]] = None,
|
|
527
|
+
version: Optional[int] = None,
|
|
528
|
+
registry: Optional[Registry] = None,
|
|
442
529
|
) -> Optional[Union[Workflow, RemoteWorkflow]]:
|
|
443
|
-
|
|
530
|
+
"""Get a workflow by ID, optionally creating a fresh instance for request isolation.
|
|
531
|
+
|
|
532
|
+
When create_fresh=True, creates a new workflow instance using deep_copy() to prevent
|
|
533
|
+
state contamination between concurrent requests. Steps containing agents/teams are also deep copied.
|
|
534
|
+
|
|
535
|
+
Args:
|
|
536
|
+
workflow_id: The workflow ID to look up
|
|
537
|
+
workflows: List of workflows to search
|
|
538
|
+
create_fresh: If True, creates a new instance using deep_copy()
|
|
539
|
+
db: Optional database interface
|
|
540
|
+
version: Workflow version, if needed
|
|
541
|
+
registry: Optional Registry instance
|
|
542
|
+
|
|
543
|
+
Returns:
|
|
544
|
+
The workflow instance (shared or fresh copy based on create_fresh)
|
|
545
|
+
"""
|
|
546
|
+
if workflow_id is None:
|
|
444
547
|
return None
|
|
445
548
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
549
|
+
if workflows:
|
|
550
|
+
for workflow in workflows:
|
|
551
|
+
if workflow.id == workflow_id:
|
|
552
|
+
if create_fresh and isinstance(workflow, Workflow):
|
|
553
|
+
return workflow.deep_copy()
|
|
554
|
+
return workflow
|
|
555
|
+
|
|
556
|
+
if db and isinstance(db, BaseDb):
|
|
557
|
+
from agno.workflow.workflow import get_workflow_by_id as get_workflow_by_id_db
|
|
558
|
+
|
|
559
|
+
try:
|
|
560
|
+
db_workflow = get_workflow_by_id_db(db=db, id=workflow_id, version=version, registry=registry)
|
|
561
|
+
return db_workflow
|
|
562
|
+
except Exception as e:
|
|
563
|
+
logger.error(f"Error getting workflow {workflow_id} from database: {e}")
|
|
564
|
+
return None
|
|
565
|
+
|
|
449
566
|
return None
|
|
450
567
|
|
|
451
568
|
|
|
@@ -819,7 +936,7 @@ def format_duration_ms(duration_ms: Optional[int]) -> str:
|
|
|
819
936
|
return f"{duration_ms / 1000:.2f}s"
|
|
820
937
|
|
|
821
938
|
|
|
822
|
-
def
|
|
939
|
+
def timestamp_to_datetime(datetime_str: str, param_name: str = "datetime") -> "datetime":
|
|
823
940
|
"""Parse an ISO 8601 datetime string and convert to UTC.
|
|
824
941
|
|
|
825
942
|
Args:
|
agno/reasoning/anthropic.py
CHANGED
|
@@ -69,7 +69,7 @@ def get_anthropic_reasoning_stream(
|
|
|
69
69
|
redacted_reasoning_content: Optional[str] = None
|
|
70
70
|
|
|
71
71
|
try:
|
|
72
|
-
for event in reasoning_agent.run(input=messages, stream=True,
|
|
72
|
+
for event in reasoning_agent.run(input=messages, stream=True, stream_events=True):
|
|
73
73
|
if hasattr(event, "event"):
|
|
74
74
|
if event.event == RunEvent.run_content:
|
|
75
75
|
# Stream reasoning content as it arrives
|
|
@@ -140,7 +140,7 @@ async def aget_anthropic_reasoning_stream(
|
|
|
140
140
|
redacted_reasoning_content: Optional[str] = None
|
|
141
141
|
|
|
142
142
|
try:
|
|
143
|
-
async for event in reasoning_agent.arun(input=messages, stream=True,
|
|
143
|
+
async for event in reasoning_agent.arun(input=messages, stream=True, stream_events=True):
|
|
144
144
|
if hasattr(event, "event"):
|
|
145
145
|
if event.event == RunEvent.run_content:
|
|
146
146
|
# Stream reasoning content as it arrives
|
|
@@ -86,7 +86,7 @@ def get_ai_foundry_reasoning_stream(
|
|
|
86
86
|
reasoning_content: str = ""
|
|
87
87
|
|
|
88
88
|
try:
|
|
89
|
-
for event in reasoning_agent.run(input=messages, stream=True,
|
|
89
|
+
for event in reasoning_agent.run(input=messages, stream=True, stream_events=True):
|
|
90
90
|
if hasattr(event, "event"):
|
|
91
91
|
if event.event == RunEvent.run_content:
|
|
92
92
|
# Check for reasoning_content attribute first (native reasoning)
|
|
@@ -132,7 +132,7 @@ async def aget_ai_foundry_reasoning_stream(
|
|
|
132
132
|
reasoning_content: str = ""
|
|
133
133
|
|
|
134
134
|
try:
|
|
135
|
-
async for event in reasoning_agent.arun(input=messages, stream=True,
|
|
135
|
+
async for event in reasoning_agent.arun(input=messages, stream=True, stream_events=True):
|
|
136
136
|
if hasattr(event, "event"):
|
|
137
137
|
if event.event == RunEvent.run_content:
|
|
138
138
|
# Check for reasoning_content attribute first (native reasoning)
|
agno/reasoning/deepseek.py
CHANGED
|
@@ -69,7 +69,7 @@ def get_deepseek_reasoning_stream(
|
|
|
69
69
|
reasoning_content: str = ""
|
|
70
70
|
|
|
71
71
|
try:
|
|
72
|
-
for event in reasoning_agent.run(input=messages, stream=True,
|
|
72
|
+
for event in reasoning_agent.run(input=messages, stream=True, stream_events=True):
|
|
73
73
|
if hasattr(event, "event"):
|
|
74
74
|
if event.event == RunEvent.run_content:
|
|
75
75
|
# Stream reasoning content as it arrives
|
|
@@ -140,7 +140,7 @@ async def aget_deepseek_reasoning_stream(
|
|
|
140
140
|
reasoning_content: str = ""
|
|
141
141
|
|
|
142
142
|
try:
|
|
143
|
-
async for event in reasoning_agent.arun(input=messages, stream=True,
|
|
143
|
+
async for event in reasoning_agent.arun(input=messages, stream=True, stream_events=True):
|
|
144
144
|
if hasattr(event, "event"):
|
|
145
145
|
if event.event == RunEvent.run_content:
|
|
146
146
|
# Stream reasoning content as it arrives
|
agno/reasoning/default.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from textwrap import dedent
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Callable, Dict, List, Literal, Optional, Union
|
|
5
5
|
|
|
6
6
|
from agno.models.base import Model
|
|
7
7
|
from agno.reasoning.step import ReasoningSteps
|
|
8
|
+
from agno.run.base import RunContext
|
|
8
9
|
from agno.tools import Toolkit
|
|
9
10
|
from agno.tools.function import Function
|
|
10
11
|
|
|
@@ -19,9 +20,7 @@ def get_default_reasoning_agent(
|
|
|
19
20
|
telemetry: bool = True,
|
|
20
21
|
debug_mode: bool = False,
|
|
21
22
|
debug_level: Literal[1, 2] = 1,
|
|
22
|
-
|
|
23
|
-
dependencies: Optional[Dict[str, Any]] = None,
|
|
24
|
-
metadata: Optional[Dict[str, Any]] = None,
|
|
23
|
+
run_context: Optional[RunContext] = None,
|
|
25
24
|
) -> Optional["Agent"]: # type: ignore # noqa: F821
|
|
26
25
|
from agno.agent import Agent
|
|
27
26
|
|
|
@@ -89,9 +88,9 @@ def get_default_reasoning_agent(
|
|
|
89
88
|
telemetry=telemetry,
|
|
90
89
|
debug_mode=debug_mode,
|
|
91
90
|
debug_level=debug_level,
|
|
92
|
-
session_state=session_state,
|
|
93
|
-
dependencies=dependencies,
|
|
94
|
-
metadata=metadata,
|
|
91
|
+
session_state=run_context.session_state if run_context else None,
|
|
92
|
+
dependencies=run_context.dependencies if run_context else None,
|
|
93
|
+
metadata=run_context.metadata if run_context else None,
|
|
95
94
|
)
|
|
96
95
|
|
|
97
96
|
return agent
|
agno/reasoning/gemini.py
CHANGED
|
@@ -94,7 +94,7 @@ def get_gemini_reasoning_stream(
|
|
|
94
94
|
reasoning_content: str = ""
|
|
95
95
|
|
|
96
96
|
try:
|
|
97
|
-
for event in reasoning_agent.run(input=messages, stream=True,
|
|
97
|
+
for event in reasoning_agent.run(input=messages, stream=True, stream_events=True):
|
|
98
98
|
if hasattr(event, "event"):
|
|
99
99
|
if event.event == RunEvent.run_content:
|
|
100
100
|
# Stream reasoning content as it arrives
|
|
@@ -134,7 +134,7 @@ async def aget_gemini_reasoning_stream(
|
|
|
134
134
|
reasoning_content: str = ""
|
|
135
135
|
|
|
136
136
|
try:
|
|
137
|
-
async for event in reasoning_agent.arun(input=messages, stream=True,
|
|
137
|
+
async for event in reasoning_agent.arun(input=messages, stream=True, stream_events=True):
|
|
138
138
|
if hasattr(event, "event"):
|
|
139
139
|
if event.event == RunEvent.run_content:
|
|
140
140
|
# Stream reasoning content as it arrives
|
agno/reasoning/helpers.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import List, Literal, Optional
|
|
2
2
|
|
|
3
3
|
from agno.models.base import Model
|
|
4
4
|
from agno.models.message import Message
|
|
5
5
|
from agno.reasoning.step import NextAction, ReasoningStep
|
|
6
|
+
from agno.run.base import RunContext
|
|
6
7
|
from agno.run.messages import RunMessages
|
|
7
8
|
from agno.utils.log import logger
|
|
8
9
|
|
|
@@ -12,9 +13,7 @@ def get_reasoning_agent(
|
|
|
12
13
|
telemetry: bool = False,
|
|
13
14
|
debug_mode: bool = False,
|
|
14
15
|
debug_level: Literal[1, 2] = 1,
|
|
15
|
-
|
|
16
|
-
dependencies: Optional[Dict[str, Any]] = None,
|
|
17
|
-
metadata: Optional[Dict[str, Any]] = None,
|
|
16
|
+
run_context: Optional[RunContext] = None,
|
|
18
17
|
) -> "Agent": # type: ignore # noqa: F821
|
|
19
18
|
from agno.agent import Agent
|
|
20
19
|
|
|
@@ -23,9 +22,9 @@ def get_reasoning_agent(
|
|
|
23
22
|
telemetry=telemetry,
|
|
24
23
|
debug_mode=debug_mode,
|
|
25
24
|
debug_level=debug_level,
|
|
26
|
-
session_state=session_state,
|
|
27
|
-
dependencies=dependencies,
|
|
28
|
-
metadata=metadata,
|
|
25
|
+
session_state=run_context.session_state if run_context else None,
|
|
26
|
+
dependencies=run_context.dependencies if run_context else None,
|
|
27
|
+
metadata=run_context.metadata if run_context else None,
|
|
29
28
|
)
|
|
30
29
|
|
|
31
30
|
|
agno/reasoning/manager.py
CHANGED
|
@@ -14,7 +14,6 @@ from dataclasses import dataclass, field
|
|
|
14
14
|
from enum import Enum
|
|
15
15
|
from typing import (
|
|
16
16
|
TYPE_CHECKING,
|
|
17
|
-
Any,
|
|
18
17
|
AsyncIterator,
|
|
19
18
|
Callable,
|
|
20
19
|
Dict,
|
|
@@ -29,6 +28,7 @@ from typing import (
|
|
|
29
28
|
from agno.models.base import Model
|
|
30
29
|
from agno.models.message import Message
|
|
31
30
|
from agno.reasoning.step import NextAction, ReasoningStep, ReasoningSteps
|
|
31
|
+
from agno.run.base import RunContext
|
|
32
32
|
from agno.run.messages import RunMessages
|
|
33
33
|
from agno.tools import Toolkit
|
|
34
34
|
from agno.tools.function import Function
|
|
@@ -87,9 +87,7 @@ class ReasoningConfig:
|
|
|
87
87
|
telemetry: bool = True
|
|
88
88
|
debug_mode: bool = False
|
|
89
89
|
debug_level: Literal[1, 2] = 1
|
|
90
|
-
|
|
91
|
-
dependencies: Optional[Dict[str, Any]] = None
|
|
92
|
-
metadata: Optional[Dict[str, Any]] = None
|
|
90
|
+
run_context: Optional[RunContext] = None
|
|
93
91
|
|
|
94
92
|
|
|
95
93
|
@dataclass
|
|
@@ -161,9 +159,7 @@ class ReasoningManager:
|
|
|
161
159
|
telemetry=self.config.telemetry,
|
|
162
160
|
debug_mode=self.config.debug_mode,
|
|
163
161
|
debug_level=self.config.debug_level,
|
|
164
|
-
|
|
165
|
-
dependencies=self.config.dependencies,
|
|
166
|
-
metadata=self.config.metadata,
|
|
162
|
+
run_context=self.config.run_context,
|
|
167
163
|
)
|
|
168
164
|
|
|
169
165
|
def _get_default_reasoning_agent(self, model: Model) -> Optional["Agent"]:
|
|
@@ -183,9 +179,7 @@ class ReasoningManager:
|
|
|
183
179
|
telemetry=self.config.telemetry,
|
|
184
180
|
debug_mode=self.config.debug_mode,
|
|
185
181
|
debug_level=self.config.debug_level,
|
|
186
|
-
|
|
187
|
-
dependencies=self.config.dependencies,
|
|
188
|
-
metadata=self.config.metadata,
|
|
182
|
+
run_context=self.config.run_context,
|
|
189
183
|
)
|
|
190
184
|
|
|
191
185
|
def is_native_reasoning_model(self, model: Optional[Model] = None) -> bool:
|
agno/reasoning/ollama.py
CHANGED
|
@@ -86,7 +86,7 @@ def get_ollama_reasoning_stream(
|
|
|
86
86
|
reasoning_content: str = ""
|
|
87
87
|
|
|
88
88
|
try:
|
|
89
|
-
for event in reasoning_agent.run(input=messages, stream=True,
|
|
89
|
+
for event in reasoning_agent.run(input=messages, stream=True, stream_events=True):
|
|
90
90
|
if hasattr(event, "event"):
|
|
91
91
|
if event.event == RunEvent.run_content:
|
|
92
92
|
# Check for reasoning_content attribute first (native reasoning)
|
|
@@ -132,7 +132,7 @@ async def aget_ollama_reasoning_stream(
|
|
|
132
132
|
reasoning_content: str = ""
|
|
133
133
|
|
|
134
134
|
try:
|
|
135
|
-
async for event in reasoning_agent.arun(input=messages, stream=True,
|
|
135
|
+
async for event in reasoning_agent.arun(input=messages, stream=True, stream_events=True):
|
|
136
136
|
if hasattr(event, "event"):
|
|
137
137
|
if event.event == RunEvent.run_content:
|
|
138
138
|
# Check for reasoning_content attribute first (native reasoning)
|
agno/reasoning/openai.py
CHANGED
|
@@ -112,7 +112,7 @@ def get_openai_reasoning_stream(
|
|
|
112
112
|
reasoning_content: str = ""
|
|
113
113
|
|
|
114
114
|
try:
|
|
115
|
-
for event in reasoning_agent.run(input=messages, stream=True,
|
|
115
|
+
for event in reasoning_agent.run(input=messages, stream=True, stream_events=True):
|
|
116
116
|
if hasattr(event, "event"):
|
|
117
117
|
if event.event == RunEvent.run_content:
|
|
118
118
|
# Check for reasoning_content attribute first (native reasoning)
|
|
@@ -168,7 +168,7 @@ async def aget_openai_reasoning_stream(
|
|
|
168
168
|
reasoning_content: str = ""
|
|
169
169
|
|
|
170
170
|
try:
|
|
171
|
-
async for event in reasoning_agent.arun(input=messages, stream=True,
|
|
171
|
+
async for event in reasoning_agent.arun(input=messages, stream=True, stream_events=True):
|
|
172
172
|
if hasattr(event, "event"):
|
|
173
173
|
if event.event == RunEvent.run_content:
|
|
174
174
|
# Check for reasoning_content attribute first (native reasoning)
|
agno/reasoning/vertexai.py
CHANGED
|
@@ -94,7 +94,7 @@ def get_vertexai_reasoning_stream(
|
|
|
94
94
|
redacted_reasoning_content: Optional[str] = None
|
|
95
95
|
|
|
96
96
|
try:
|
|
97
|
-
for event in reasoning_agent.run(input=messages, stream=True,
|
|
97
|
+
for event in reasoning_agent.run(input=messages, stream=True, stream_events=True):
|
|
98
98
|
if hasattr(event, "event"):
|
|
99
99
|
if event.event == RunEvent.run_content:
|
|
100
100
|
# Stream reasoning content as it arrives
|
|
@@ -136,7 +136,7 @@ async def aget_vertexai_reasoning_stream(
|
|
|
136
136
|
redacted_reasoning_content: Optional[str] = None
|
|
137
137
|
|
|
138
138
|
try:
|
|
139
|
-
async for event in reasoning_agent.arun(input=messages, stream=True,
|
|
139
|
+
async for event in reasoning_agent.arun(input=messages, stream=True, stream_events=True):
|
|
140
140
|
if hasattr(event, "event"):
|
|
141
141
|
if event.event == RunEvent.run_content:
|
|
142
142
|
# Stream reasoning content as it arrives
|