agno 2.0.9__py3-none-any.whl → 2.0.11__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 +13 -6
- agno/models/aws/claude.py +1 -1
- agno/models/litellm/chat.py +17 -0
- agno/os/app.py +69 -22
- agno/os/utils.py +98 -0
- agno/team/team.py +10 -5
- agno/utils/models/claude.py +2 -2
- agno/vectordb/llamaindex/__init__.py +3 -0
- agno/workflow/workflow.py +11 -6
- {agno-2.0.9.dist-info → agno-2.0.11.dist-info}/METADATA +1 -1
- {agno-2.0.9.dist-info → agno-2.0.11.dist-info}/RECORD +14 -15
- agno/utils/models/aws_claude.py +0 -170
- {agno-2.0.9.dist-info → agno-2.0.11.dist-info}/WHEEL +0 -0
- {agno-2.0.9.dist-info → agno-2.0.11.dist-info}/licenses/LICENSE +0 -0
- {agno-2.0.9.dist-info → agno-2.0.11.dist-info}/top_level.txt +0 -0
agno/agent/agent.py
CHANGED
|
@@ -135,6 +135,8 @@ class Agent:
|
|
|
135
135
|
add_session_state_to_context: bool = False
|
|
136
136
|
# Set to True to give the agent tools to update the session_state dynamically
|
|
137
137
|
enable_agentic_state: bool = False
|
|
138
|
+
# Set to True to overwrite the stored session_state with the session_state provided in the run. Default behaviour merges the current session state with the session state in the db
|
|
139
|
+
overwrite_db_session_state: bool = False
|
|
138
140
|
# If True, cache the current Agent session in memory for faster access
|
|
139
141
|
cache_session: bool = False
|
|
140
142
|
|
|
@@ -348,6 +350,7 @@ class Agent:
|
|
|
348
350
|
session_id: Optional[str] = None,
|
|
349
351
|
session_state: Optional[Dict[str, Any]] = None,
|
|
350
352
|
add_session_state_to_context: bool = False,
|
|
353
|
+
overwrite_db_session_state: bool = False,
|
|
351
354
|
enable_agentic_state: bool = False,
|
|
352
355
|
cache_session: bool = False,
|
|
353
356
|
search_session_history: Optional[bool] = False,
|
|
@@ -432,6 +435,7 @@ class Agent:
|
|
|
432
435
|
|
|
433
436
|
self.session_id = session_id
|
|
434
437
|
self.session_state = session_state
|
|
438
|
+
self.overwrite_db_session_state = overwrite_db_session_state
|
|
435
439
|
self.enable_agentic_state = enable_agentic_state
|
|
436
440
|
self.cache_session = cache_session
|
|
437
441
|
|
|
@@ -1113,7 +1117,7 @@ class Agent:
|
|
|
1113
1117
|
self._update_metadata(session=agent_session)
|
|
1114
1118
|
|
|
1115
1119
|
# Update session state from DB
|
|
1116
|
-
session_state = self.
|
|
1120
|
+
session_state = self._load_session_state(session=agent_session, session_state=session_state)
|
|
1117
1121
|
|
|
1118
1122
|
# Determine runtime dependencies
|
|
1119
1123
|
run_dependencies = dependencies if dependencies is not None else self.dependencies
|
|
@@ -1174,6 +1178,7 @@ class Agent:
|
|
|
1174
1178
|
run_id=run_id,
|
|
1175
1179
|
session_id=session_id,
|
|
1176
1180
|
agent_id=self.id,
|
|
1181
|
+
user_id=user_id,
|
|
1177
1182
|
agent_name=self.name,
|
|
1178
1183
|
metadata=metadata,
|
|
1179
1184
|
input=run_input,
|
|
@@ -1752,7 +1757,7 @@ class Agent:
|
|
|
1752
1757
|
self._update_metadata(session=agent_session)
|
|
1753
1758
|
|
|
1754
1759
|
# Update session state from DB
|
|
1755
|
-
session_state = self.
|
|
1760
|
+
session_state = self._load_session_state(session=agent_session, session_state=session_state)
|
|
1756
1761
|
|
|
1757
1762
|
# Determine run dependencies
|
|
1758
1763
|
run_dependencies = dependencies if dependencies is not None else self.dependencies
|
|
@@ -1807,6 +1812,7 @@ class Agent:
|
|
|
1807
1812
|
run_id=run_id,
|
|
1808
1813
|
session_id=session_id,
|
|
1809
1814
|
agent_id=self.id,
|
|
1815
|
+
user_id=user_id,
|
|
1810
1816
|
agent_name=self.name,
|
|
1811
1817
|
metadata=metadata,
|
|
1812
1818
|
input=run_input,
|
|
@@ -2019,7 +2025,7 @@ class Agent:
|
|
|
2019
2025
|
self._update_metadata(session=agent_session)
|
|
2020
2026
|
|
|
2021
2027
|
# Update session state from DB
|
|
2022
|
-
session_state = self.
|
|
2028
|
+
session_state = self._load_session_state(session=agent_session, session_state=session_state)
|
|
2023
2029
|
|
|
2024
2030
|
run_dependencies = dependencies if dependencies is not None else self.dependencies
|
|
2025
2031
|
|
|
@@ -2403,7 +2409,7 @@ class Agent:
|
|
|
2403
2409
|
self._update_metadata(session=agent_session)
|
|
2404
2410
|
|
|
2405
2411
|
# Update session state from DB
|
|
2406
|
-
session_state = self.
|
|
2412
|
+
session_state = self._load_session_state(session=agent_session, session_state=session_state)
|
|
2407
2413
|
|
|
2408
2414
|
run_dependencies = dependencies if dependencies is not None else self.dependencies
|
|
2409
2415
|
|
|
@@ -4252,8 +4258,8 @@ class Agent:
|
|
|
4252
4258
|
log_warning(f"Error upserting session into db: {e}")
|
|
4253
4259
|
return None
|
|
4254
4260
|
|
|
4255
|
-
def
|
|
4256
|
-
"""Load the
|
|
4261
|
+
def _load_session_state(self, session: AgentSession, session_state: Dict[str, Any]):
|
|
4262
|
+
"""Load and return the stored session_state from the database, optionally merging it with the given one"""
|
|
4257
4263
|
|
|
4258
4264
|
# Get the session_state from the database and merge with proper precedence
|
|
4259
4265
|
# At this point session_state contains: agent_defaults + run_params
|
|
@@ -4264,6 +4270,7 @@ class Agent:
|
|
|
4264
4270
|
session_state_from_db is not None
|
|
4265
4271
|
and isinstance(session_state_from_db, dict)
|
|
4266
4272
|
and len(session_state_from_db) > 0
|
|
4273
|
+
and not self.overwrite_db_session_state
|
|
4267
4274
|
):
|
|
4268
4275
|
# This preserves precedence: run_params > db_state > agent_defaults
|
|
4269
4276
|
merged_state = session_state_from_db.copy()
|
agno/models/aws/claude.py
CHANGED
|
@@ -10,7 +10,7 @@ from agno.models.message import Message
|
|
|
10
10
|
from agno.models.response import ModelResponse
|
|
11
11
|
from agno.run.agent import RunOutput
|
|
12
12
|
from agno.utils.log import log_debug, log_error, log_warning
|
|
13
|
-
from agno.utils.models.
|
|
13
|
+
from agno.utils.models.claude import format_messages
|
|
14
14
|
|
|
15
15
|
try:
|
|
16
16
|
from anthropic import AnthropicBedrock, APIConnectionError, APIStatusError, AsyncAnthropicBedrock, RateLimitError
|
agno/models/litellm/chat.py
CHANGED
|
@@ -38,6 +38,10 @@ class LiteLLM(Model):
|
|
|
38
38
|
max_tokens: Optional[int] = None
|
|
39
39
|
temperature: float = 0.7
|
|
40
40
|
top_p: float = 1.0
|
|
41
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
42
|
+
extra_headers: Optional[Dict[str, Any]] = None
|
|
43
|
+
extra_query: Optional[Dict[str, Any]] = None
|
|
44
|
+
extra_body: Optional[Dict[str, Any]] = None
|
|
41
45
|
request_params: Optional[Dict[str, Any]] = None
|
|
42
46
|
|
|
43
47
|
client: Optional[Any] = None
|
|
@@ -148,10 +152,23 @@ class LiteLLM(Model):
|
|
|
148
152
|
base_params["api_key"] = self.api_key
|
|
149
153
|
if self.api_base:
|
|
150
154
|
base_params["api_base"] = self.api_base
|
|
155
|
+
if self.extra_headers:
|
|
156
|
+
base_params["extra_headers"] = self.extra_headers
|
|
157
|
+
if self.extra_query:
|
|
158
|
+
base_params["extra_query"] = self.extra_query
|
|
151
159
|
if tools:
|
|
152
160
|
base_params["tools"] = tools
|
|
153
161
|
base_params["tool_choice"] = "auto"
|
|
154
162
|
|
|
163
|
+
# Handle metadata via extra_body as per LiteLLM docs
|
|
164
|
+
if self.metadata:
|
|
165
|
+
if self.extra_body:
|
|
166
|
+
base_params["extra_body"] = {**self.extra_body, "metadata": self.metadata}
|
|
167
|
+
else:
|
|
168
|
+
base_params["extra_body"] = {"metadata": self.metadata}
|
|
169
|
+
elif self.extra_body:
|
|
170
|
+
base_params["extra_body"] = self.extra_body
|
|
171
|
+
|
|
155
172
|
# Add additional request params if provided
|
|
156
173
|
request_params: Dict[str, Any] = {k: v for k, v in base_params.items() if v is not None}
|
|
157
174
|
if self.request_params:
|
agno/os/app.py
CHANGED
|
@@ -12,6 +12,7 @@ from rich.panel import Panel
|
|
|
12
12
|
from starlette.requests import Request
|
|
13
13
|
|
|
14
14
|
from agno.agent.agent import Agent
|
|
15
|
+
from agno.db.base import BaseDb
|
|
15
16
|
from agno.os.config import (
|
|
16
17
|
AgentOSConfig,
|
|
17
18
|
DatabaseConfig,
|
|
@@ -36,7 +37,11 @@ from agno.os.routers.memory import get_memory_router
|
|
|
36
37
|
from agno.os.routers.metrics import get_metrics_router
|
|
37
38
|
from agno.os.routers.session import get_session_router
|
|
38
39
|
from agno.os.settings import AgnoAPISettings
|
|
39
|
-
from agno.os.utils import
|
|
40
|
+
from agno.os.utils import (
|
|
41
|
+
collect_mcp_tools_from_team,
|
|
42
|
+
collect_mcp_tools_from_workflow,
|
|
43
|
+
update_cors_middleware,
|
|
44
|
+
)
|
|
40
45
|
from agno.team.team import Team
|
|
41
46
|
from agno.utils.log import logger
|
|
42
47
|
from agno.utils.string import generate_id, generate_id_from_name
|
|
@@ -130,7 +135,7 @@ class AgentOS:
|
|
|
130
135
|
self.lifespan = lifespan
|
|
131
136
|
|
|
132
137
|
# List of all MCP tools used inside the AgentOS
|
|
133
|
-
self.mcp_tools = []
|
|
138
|
+
self.mcp_tools: List[Any] = []
|
|
134
139
|
|
|
135
140
|
if self.agents:
|
|
136
141
|
for agent in self.agents:
|
|
@@ -140,7 +145,8 @@ class AgentOS:
|
|
|
140
145
|
# Checking if the tool is a MCPTools or MultiMCPTools instance
|
|
141
146
|
type_name = type(tool).__name__
|
|
142
147
|
if type_name in ("MCPTools", "MultiMCPTools"):
|
|
143
|
-
self.mcp_tools
|
|
148
|
+
if tool not in self.mcp_tools:
|
|
149
|
+
self.mcp_tools.append(tool)
|
|
144
150
|
|
|
145
151
|
agent.initialize_agent()
|
|
146
152
|
|
|
@@ -149,13 +155,8 @@ class AgentOS:
|
|
|
149
155
|
|
|
150
156
|
if self.teams:
|
|
151
157
|
for team in self.teams:
|
|
152
|
-
# Track all MCP tools
|
|
153
|
-
|
|
154
|
-
for tool in team.tools:
|
|
155
|
-
# Checking if the tool is a MCPTools or MultiMCPTools instance
|
|
156
|
-
type_name = type(tool).__name__
|
|
157
|
-
if type_name in ("MCPTools", "MultiMCPTools"):
|
|
158
|
-
self.mcp_tools.append(tool)
|
|
158
|
+
# Track all MCP tools recursively
|
|
159
|
+
collect_mcp_tools_from_team(team, self.mcp_tools)
|
|
159
160
|
|
|
160
161
|
team.initialize_team()
|
|
161
162
|
|
|
@@ -171,7 +172,8 @@ class AgentOS:
|
|
|
171
172
|
|
|
172
173
|
if self.workflows:
|
|
173
174
|
for workflow in self.workflows:
|
|
174
|
-
#
|
|
175
|
+
# Track MCP tools recursively in workflow members
|
|
176
|
+
collect_mcp_tools_from_workflow(workflow, self.mcp_tools)
|
|
175
177
|
if not workflow.id:
|
|
176
178
|
workflow.id = generate_id_from_name(workflow.name)
|
|
177
179
|
|
|
@@ -415,40 +417,85 @@ class AgentOS:
|
|
|
415
417
|
|
|
416
418
|
def _auto_discover_databases(self) -> None:
|
|
417
419
|
"""Auto-discover the databases used by all contextual agents, teams and workflows."""
|
|
418
|
-
|
|
419
|
-
|
|
420
|
+
from agno.db.base import BaseDb
|
|
421
|
+
|
|
422
|
+
dbs: Dict[str, BaseDb] = {}
|
|
423
|
+
knowledge_dbs: Dict[str, BaseDb] = {} # Track databases specifically used for knowledge
|
|
420
424
|
|
|
421
425
|
for agent in self.agents or []:
|
|
422
426
|
if agent.db:
|
|
423
|
-
dbs
|
|
427
|
+
self._register_db_with_validation(dbs, agent.db)
|
|
424
428
|
if agent.knowledge and agent.knowledge.contents_db:
|
|
425
|
-
knowledge_dbs
|
|
429
|
+
self._register_db_with_validation(knowledge_dbs, agent.knowledge.contents_db)
|
|
426
430
|
# Also add to general dbs if it's used for both purposes
|
|
427
431
|
if agent.knowledge.contents_db.id not in dbs:
|
|
428
|
-
dbs
|
|
432
|
+
self._register_db_with_validation(dbs, agent.knowledge.contents_db)
|
|
429
433
|
|
|
430
434
|
for team in self.teams or []:
|
|
431
435
|
if team.db:
|
|
432
|
-
dbs
|
|
436
|
+
self._register_db_with_validation(dbs, team.db)
|
|
433
437
|
if team.knowledge and team.knowledge.contents_db:
|
|
434
|
-
knowledge_dbs
|
|
438
|
+
self._register_db_with_validation(knowledge_dbs, team.knowledge.contents_db)
|
|
435
439
|
# Also add to general dbs if it's used for both purposes
|
|
436
440
|
if team.knowledge.contents_db.id not in dbs:
|
|
437
|
-
dbs
|
|
441
|
+
self._register_db_with_validation(dbs, team.knowledge.contents_db)
|
|
438
442
|
|
|
439
443
|
for workflow in self.workflows or []:
|
|
440
444
|
if workflow.db:
|
|
441
|
-
dbs
|
|
445
|
+
self._register_db_with_validation(dbs, workflow.db)
|
|
442
446
|
|
|
443
447
|
for interface in self.interfaces or []:
|
|
444
448
|
if interface.agent and interface.agent.db:
|
|
445
|
-
dbs
|
|
449
|
+
self._register_db_with_validation(dbs, interface.agent.db)
|
|
446
450
|
elif interface.team and interface.team.db:
|
|
447
|
-
dbs
|
|
451
|
+
self._register_db_with_validation(dbs, interface.team.db)
|
|
448
452
|
|
|
449
453
|
self.dbs = dbs
|
|
450
454
|
self.knowledge_dbs = knowledge_dbs
|
|
451
455
|
|
|
456
|
+
def _register_db_with_validation(self, registered_dbs: Dict[str, Any], db: BaseDb) -> None:
|
|
457
|
+
"""Register a database in the contextual OS after validating it is not conflicting with registered databases"""
|
|
458
|
+
if db.id in registered_dbs:
|
|
459
|
+
existing_db = registered_dbs[db.id]
|
|
460
|
+
if not self._are_db_instances_compatible(existing_db, db):
|
|
461
|
+
raise ValueError(
|
|
462
|
+
f"Database ID conflict detected: Two different database instances have the same ID '{db.id}'. "
|
|
463
|
+
f"Database instances with the same ID must point to the same database with identical configuration."
|
|
464
|
+
)
|
|
465
|
+
registered_dbs[db.id] = db
|
|
466
|
+
|
|
467
|
+
def _are_db_instances_compatible(self, db1: BaseDb, db2: BaseDb) -> bool:
|
|
468
|
+
"""
|
|
469
|
+
Return True if the two given database objects are compatible
|
|
470
|
+
Two database objects are compatible if they point to the same database with identical configuration.
|
|
471
|
+
"""
|
|
472
|
+
# If they're the same object reference, they're compatible
|
|
473
|
+
if db1 is db2:
|
|
474
|
+
return True
|
|
475
|
+
|
|
476
|
+
if type(db1) is not type(db2):
|
|
477
|
+
return False
|
|
478
|
+
|
|
479
|
+
if hasattr(db1, "db_url") and hasattr(db2, "db_url"):
|
|
480
|
+
if db1.db_url != db2.db_url: # type: ignore
|
|
481
|
+
return False
|
|
482
|
+
|
|
483
|
+
if hasattr(db1, "db_file") and hasattr(db2, "db_file"):
|
|
484
|
+
if db1.db_file != db2.db_file: # type: ignore
|
|
485
|
+
return False
|
|
486
|
+
|
|
487
|
+
# If table names are different, they're not compatible
|
|
488
|
+
if (
|
|
489
|
+
db1.session_table_name != db2.session_table_name
|
|
490
|
+
or db1.memory_table_name != db2.memory_table_name
|
|
491
|
+
or db1.metrics_table_name != db2.metrics_table_name
|
|
492
|
+
or db1.eval_table_name != db2.eval_table_name
|
|
493
|
+
or db1.knowledge_table_name != db2.knowledge_table_name
|
|
494
|
+
):
|
|
495
|
+
return False
|
|
496
|
+
|
|
497
|
+
return True
|
|
498
|
+
|
|
452
499
|
def _auto_discover_knowledge_instances(self) -> None:
|
|
453
500
|
"""Auto-discover the knowledge instances used by all contextual agents, teams and workflows."""
|
|
454
501
|
knowledge_instances = []
|
agno/os/utils.py
CHANGED
|
@@ -303,3 +303,101 @@ def update_cors_middleware(app: FastAPI, new_origins: list):
|
|
|
303
303
|
allow_headers=["*"],
|
|
304
304
|
expose_headers=["*"],
|
|
305
305
|
)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def collect_mcp_tools_from_team(team: Team, mcp_tools: List[Any]) -> None:
|
|
309
|
+
"""Recursively collect MCP tools from a team and its members."""
|
|
310
|
+
# Check the team tools
|
|
311
|
+
if team.tools:
|
|
312
|
+
for tool in team.tools:
|
|
313
|
+
type_name = type(tool).__name__
|
|
314
|
+
if type_name in ("MCPTools", "MultiMCPTools"):
|
|
315
|
+
if tool not in mcp_tools:
|
|
316
|
+
mcp_tools.append(tool)
|
|
317
|
+
|
|
318
|
+
# Recursively check team members
|
|
319
|
+
if team.members:
|
|
320
|
+
for member in team.members:
|
|
321
|
+
if isinstance(member, Agent):
|
|
322
|
+
if member.tools:
|
|
323
|
+
for tool in member.tools:
|
|
324
|
+
type_name = type(tool).__name__
|
|
325
|
+
if type_name in ("MCPTools", "MultiMCPTools"):
|
|
326
|
+
if tool not in mcp_tools:
|
|
327
|
+
mcp_tools.append(tool)
|
|
328
|
+
|
|
329
|
+
elif isinstance(member, Team):
|
|
330
|
+
# Recursively check nested team
|
|
331
|
+
collect_mcp_tools_from_team(member, mcp_tools)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def collect_mcp_tools_from_workflow(workflow: Workflow, mcp_tools: List[Any]) -> None:
|
|
335
|
+
"""Recursively collect MCP tools from a workflow and its steps."""
|
|
336
|
+
from agno.workflow.steps import Steps
|
|
337
|
+
|
|
338
|
+
# Recursively check workflow steps
|
|
339
|
+
if workflow.steps:
|
|
340
|
+
if isinstance(workflow.steps, list):
|
|
341
|
+
# Handle list of steps
|
|
342
|
+
for step in workflow.steps:
|
|
343
|
+
collect_mcp_tools_from_workflow_step(step, mcp_tools)
|
|
344
|
+
|
|
345
|
+
elif isinstance(workflow.steps, Steps):
|
|
346
|
+
# Handle Steps container
|
|
347
|
+
if steps := workflow.steps.steps:
|
|
348
|
+
for step in steps:
|
|
349
|
+
collect_mcp_tools_from_workflow_step(step, mcp_tools)
|
|
350
|
+
|
|
351
|
+
elif callable(workflow.steps):
|
|
352
|
+
pass
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def collect_mcp_tools_from_workflow_step(step: Any, mcp_tools: List[Any]) -> None:
|
|
356
|
+
"""Collect MCP tools from a single workflow step."""
|
|
357
|
+
from agno.workflow.condition import Condition
|
|
358
|
+
from agno.workflow.loop import Loop
|
|
359
|
+
from agno.workflow.parallel import Parallel
|
|
360
|
+
from agno.workflow.router import Router
|
|
361
|
+
from agno.workflow.step import Step
|
|
362
|
+
from agno.workflow.steps import Steps
|
|
363
|
+
|
|
364
|
+
if isinstance(step, Step):
|
|
365
|
+
# Check step's agent
|
|
366
|
+
if step.agent:
|
|
367
|
+
if step.agent.tools:
|
|
368
|
+
for tool in step.agent.tools:
|
|
369
|
+
type_name = type(tool).__name__
|
|
370
|
+
if type_name in ("MCPTools", "MultiMCPTools"):
|
|
371
|
+
if tool not in mcp_tools:
|
|
372
|
+
mcp_tools.append(tool)
|
|
373
|
+
# Check step's team
|
|
374
|
+
if step.team:
|
|
375
|
+
collect_mcp_tools_from_team(step.team, mcp_tools)
|
|
376
|
+
|
|
377
|
+
elif isinstance(step, Steps):
|
|
378
|
+
if steps := step.steps:
|
|
379
|
+
for step in steps:
|
|
380
|
+
collect_mcp_tools_from_workflow_step(step, mcp_tools)
|
|
381
|
+
|
|
382
|
+
elif isinstance(step, (Parallel, Loop, Condition, Router)):
|
|
383
|
+
# These contain other steps - recursively check them
|
|
384
|
+
if hasattr(step, "steps") and step.steps:
|
|
385
|
+
for sub_step in step.steps:
|
|
386
|
+
collect_mcp_tools_from_workflow_step(sub_step, mcp_tools)
|
|
387
|
+
|
|
388
|
+
elif isinstance(step, Agent):
|
|
389
|
+
# Direct agent in workflow steps
|
|
390
|
+
if step.tools:
|
|
391
|
+
for tool in step.tools:
|
|
392
|
+
type_name = type(tool).__name__
|
|
393
|
+
if type_name in ("MCPTools", "MultiMCPTools"):
|
|
394
|
+
if tool not in mcp_tools:
|
|
395
|
+
mcp_tools.append(tool)
|
|
396
|
+
|
|
397
|
+
elif isinstance(step, Team):
|
|
398
|
+
# Direct team in workflow steps
|
|
399
|
+
collect_mcp_tools_from_team(step, mcp_tools)
|
|
400
|
+
|
|
401
|
+
elif isinstance(step, Workflow):
|
|
402
|
+
# Nested workflow
|
|
403
|
+
collect_mcp_tools_from_workflow(step, mcp_tools)
|
agno/team/team.py
CHANGED
|
@@ -156,6 +156,8 @@ class Team:
|
|
|
156
156
|
add_session_state_to_context: bool = False
|
|
157
157
|
# Set to True to give the team tools to update the session_state dynamically
|
|
158
158
|
enable_agentic_state: bool = False
|
|
159
|
+
# Set to True to overwrite the stored session_state with the session_state provided in the run
|
|
160
|
+
overwrite_db_session_state: bool = False
|
|
159
161
|
# If True, cache the current Team session in memory for faster access
|
|
160
162
|
cache_session: bool = False
|
|
161
163
|
|
|
@@ -364,6 +366,7 @@ class Team:
|
|
|
364
366
|
session_state: Optional[Dict[str, Any]] = None,
|
|
365
367
|
add_session_state_to_context: bool = False,
|
|
366
368
|
enable_agentic_state: bool = False,
|
|
369
|
+
overwrite_db_session_state: bool = False,
|
|
367
370
|
resolve_in_context: bool = True,
|
|
368
371
|
cache_session: bool = False,
|
|
369
372
|
description: Optional[str] = None,
|
|
@@ -453,6 +456,7 @@ class Team:
|
|
|
453
456
|
self.session_state = session_state
|
|
454
457
|
self.add_session_state_to_context = add_session_state_to_context
|
|
455
458
|
self.enable_agentic_state = enable_agentic_state
|
|
459
|
+
self.overwrite_db_session_state = overwrite_db_session_state
|
|
456
460
|
self.resolve_in_context = resolve_in_context
|
|
457
461
|
self.cache_session = cache_session
|
|
458
462
|
|
|
@@ -1156,7 +1160,7 @@ class Team:
|
|
|
1156
1160
|
self._update_metadata(session=team_session)
|
|
1157
1161
|
|
|
1158
1162
|
# Update session state from DB
|
|
1159
|
-
session_state = self.
|
|
1163
|
+
session_state = self._load_session_state(session=team_session, session_state=session_state)
|
|
1160
1164
|
|
|
1161
1165
|
# Determine runtime dependencies
|
|
1162
1166
|
run_dependencies = dependencies if dependencies is not None else self.dependencies
|
|
@@ -1780,7 +1784,7 @@ class Team:
|
|
|
1780
1784
|
self._update_metadata(session=team_session)
|
|
1781
1785
|
|
|
1782
1786
|
# Update session state from DB
|
|
1783
|
-
session_state = self.
|
|
1787
|
+
session_state = self._load_session_state(session=team_session, session_state=session_state)
|
|
1784
1788
|
|
|
1785
1789
|
# Determine run dependencies (runtime override takes priority)
|
|
1786
1790
|
run_dependencies = dependencies if dependencies is not None else self.dependencies
|
|
@@ -4270,7 +4274,7 @@ class Team:
|
|
|
4270
4274
|
system_message_content += f"{indent * ' '} - Name: {member.name}\n"
|
|
4271
4275
|
if member.role is not None:
|
|
4272
4276
|
system_message_content += f"{indent * ' '} - Role: {member.role}\n"
|
|
4273
|
-
if member.tools
|
|
4277
|
+
if member.tools and self.add_member_tools_to_context:
|
|
4274
4278
|
system_message_content += f"{indent * ' '} - Member tools:\n"
|
|
4275
4279
|
for _tool in member.tools:
|
|
4276
4280
|
if isinstance(_tool, Toolkit):
|
|
@@ -6104,8 +6108,8 @@ class Team:
|
|
|
6104
6108
|
self._upsert_session(session=session)
|
|
6105
6109
|
log_debug(f"Created or updated TeamSession record: {session.session_id}")
|
|
6106
6110
|
|
|
6107
|
-
def
|
|
6108
|
-
"""Load the
|
|
6111
|
+
def _load_session_state(self, session: TeamSession, session_state: Dict[str, Any]) -> Dict[str, Any]:
|
|
6112
|
+
"""Load and return the stored session_state from the database, optionally merging it with the given one"""
|
|
6109
6113
|
|
|
6110
6114
|
from agno.utils.merge_dict import merge_dictionaries
|
|
6111
6115
|
|
|
@@ -6118,6 +6122,7 @@ class Team:
|
|
|
6118
6122
|
session_state_from_db is not None
|
|
6119
6123
|
and isinstance(session_state_from_db, dict)
|
|
6120
6124
|
and len(session_state_from_db) > 0
|
|
6125
|
+
and not self.overwrite_db_session_state
|
|
6121
6126
|
):
|
|
6122
6127
|
# This preserves precedence: run_params > db_state > agent_defaults
|
|
6123
6128
|
merged_state = session_state_from_db.copy()
|
agno/utils/models/claude.py
CHANGED
|
@@ -69,11 +69,11 @@ def _format_image_for_message(image: Image) -> Optional[Dict[str, Any]]:
|
|
|
69
69
|
try:
|
|
70
70
|
# Case 0: Image is an Anthropic uploaded file
|
|
71
71
|
if image.content is not None and hasattr(image.content, "id"):
|
|
72
|
-
|
|
72
|
+
content_bytes = image.content
|
|
73
73
|
|
|
74
74
|
# Case 1: Image is a URL
|
|
75
75
|
if image.url is not None:
|
|
76
|
-
|
|
76
|
+
content_bytes = image.get_content_bytes() # type: ignore
|
|
77
77
|
|
|
78
78
|
# Case 2: Image is a local file path
|
|
79
79
|
elif image.filepath is not None:
|
agno/workflow/workflow.py
CHANGED
|
@@ -136,6 +136,8 @@ class Workflow:
|
|
|
136
136
|
user_id: Optional[str] = None
|
|
137
137
|
# Default session state (stored in the database to persist across runs)
|
|
138
138
|
session_state: Optional[Dict[str, Any]] = None
|
|
139
|
+
# Set to True to overwrite the stored session_state with the session_state provided in the run
|
|
140
|
+
overwrite_db_session_state: bool = False
|
|
139
141
|
|
|
140
142
|
# If True, the workflow runs in debug mode
|
|
141
143
|
debug_mode: Optional[bool] = False
|
|
@@ -176,6 +178,7 @@ class Workflow:
|
|
|
176
178
|
steps: Optional[WorkflowSteps] = None,
|
|
177
179
|
session_id: Optional[str] = None,
|
|
178
180
|
session_state: Optional[Dict[str, Any]] = None,
|
|
181
|
+
overwrite_db_session_state: bool = False,
|
|
179
182
|
user_id: Optional[str] = None,
|
|
180
183
|
debug_mode: Optional[bool] = False,
|
|
181
184
|
stream: Optional[bool] = None,
|
|
@@ -194,6 +197,7 @@ class Workflow:
|
|
|
194
197
|
self.steps = steps
|
|
195
198
|
self.session_id = session_id
|
|
196
199
|
self.session_state = session_state
|
|
200
|
+
self.overwrite_db_session_state = overwrite_db_session_state
|
|
197
201
|
self.user_id = user_id
|
|
198
202
|
self.debug_mode = debug_mode
|
|
199
203
|
self.store_events = store_events
|
|
@@ -599,8 +603,8 @@ class Workflow:
|
|
|
599
603
|
# Update the current metadata with the metadata from the database which is updated in place
|
|
600
604
|
self.metadata = session.metadata
|
|
601
605
|
|
|
602
|
-
def
|
|
603
|
-
"""Load the
|
|
606
|
+
def _load_session_state(self, session: WorkflowSession, session_state: Dict[str, Any]):
|
|
607
|
+
"""Load and return the stored session_state from the database, optionally merging it with the given one"""
|
|
604
608
|
|
|
605
609
|
from agno.utils.merge_dict import merge_dictionaries
|
|
606
610
|
|
|
@@ -613,6 +617,7 @@ class Workflow:
|
|
|
613
617
|
session_state_from_db is not None
|
|
614
618
|
and isinstance(session_state_from_db, dict)
|
|
615
619
|
and len(session_state_from_db) > 0
|
|
620
|
+
and not self.overwrite_db_session_state
|
|
616
621
|
):
|
|
617
622
|
# This preserves precedence: run_params > db_state > agent_defaults
|
|
618
623
|
merged_state = session_state_from_db.copy()
|
|
@@ -1716,7 +1721,7 @@ class Workflow:
|
|
|
1716
1721
|
self._update_metadata(session=workflow_session)
|
|
1717
1722
|
|
|
1718
1723
|
# Update session state from DB
|
|
1719
|
-
session_state = self.
|
|
1724
|
+
session_state = self._load_session_state(session=workflow_session, session_state=session_state)
|
|
1720
1725
|
|
|
1721
1726
|
self._prepare_steps()
|
|
1722
1727
|
|
|
@@ -1807,7 +1812,7 @@ class Workflow:
|
|
|
1807
1812
|
self._update_metadata(session=workflow_session)
|
|
1808
1813
|
|
|
1809
1814
|
# Update session state from DB
|
|
1810
|
-
session_state = self.
|
|
1815
|
+
session_state = self._load_session_state(session=workflow_session, session_state=session_state)
|
|
1811
1816
|
|
|
1812
1817
|
self._prepare_steps()
|
|
1813
1818
|
|
|
@@ -1967,7 +1972,7 @@ class Workflow:
|
|
|
1967
1972
|
self._update_metadata(session=workflow_session)
|
|
1968
1973
|
|
|
1969
1974
|
# Update session state from DB
|
|
1970
|
-
session_state = self.
|
|
1975
|
+
session_state = self._load_session_state(session=workflow_session, session_state=session_state)
|
|
1971
1976
|
|
|
1972
1977
|
log_debug(f"Workflow Run Start: {self.name}", center=True)
|
|
1973
1978
|
|
|
@@ -2137,7 +2142,7 @@ class Workflow:
|
|
|
2137
2142
|
self._update_metadata(session=workflow_session)
|
|
2138
2143
|
|
|
2139
2144
|
# Update session state from DB
|
|
2140
|
-
session_state = self.
|
|
2145
|
+
session_state = self._load_session_state(session=workflow_session, session_state=session_state)
|
|
2141
2146
|
|
|
2142
2147
|
log_debug(f"Async Workflow Run Start: {self.name}", center=True)
|
|
2143
2148
|
|
|
@@ -4,7 +4,7 @@ agno/exceptions.py,sha256=HWuuNFS5J0l1RYJsdUrSx51M22aFEoh9ltoeonXBoBw,2891
|
|
|
4
4
|
agno/media.py,sha256=eTfYb_pwhX_PCIVPSrW4VYRqmoxKABEF1aZClrVvQ30,16500
|
|
5
5
|
agno/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
agno/agent/__init__.py,sha256=s7S3FgsjZxuaabzi8L5n4aSH8IZAiZ7XaNNcySGR-EQ,1051
|
|
7
|
-
agno/agent/agent.py,sha256=
|
|
7
|
+
agno/agent/agent.py,sha256=hkbbQr90Puq2Hj2RTkeuSd3iYggpHfOpFpA0SEpTM2s,330453
|
|
8
8
|
agno/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
agno/api/agent.py,sha256=fKlQ62E_C9Rjd7Zus3Gs3R1RG-IhzFV-ICpkb6SLqYc,932
|
|
10
10
|
agno/api/api.py,sha256=Z7iWbrjheJcGLeeDYrtTCWiKTVqjH0uJI35UNWOtAXw,973
|
|
@@ -158,7 +158,7 @@ agno/models/anthropic/__init__.py,sha256=nbReX3p17JCwfrMDR9hR7-OaEFZm80I7dng93dl
|
|
|
158
158
|
agno/models/anthropic/claude.py,sha256=8v9AbtYJmhXF-e6X5yRcRki3xE7GOowLbQh6jeGiei0,26363
|
|
159
159
|
agno/models/aws/__init__.py,sha256=TbcwQwv9A7KjqBM5RQBR8x46GvyyCxbBCjwkpjfVGKE,352
|
|
160
160
|
agno/models/aws/bedrock.py,sha256=4H4Y-ZC8nz1Dcag1JxT3orpvzSlHPaNhTy9NXaqCSY8,30919
|
|
161
|
-
agno/models/aws/claude.py,sha256=
|
|
161
|
+
agno/models/aws/claude.py,sha256=sL47z9fM3jxGbARkr0mlAVYEKKX854J3u-Qeb5gIADo,14746
|
|
162
162
|
agno/models/azure/__init__.py,sha256=EoFdJHjayvmv_VOmaW9cJguwA1K5OFS_nFeazyn0B2w,605
|
|
163
163
|
agno/models/azure/ai_foundry.py,sha256=V75lk4pH_RVFzVhWYz9-ZSA3dcaQe2SpRF1lrAm_agw,18673
|
|
164
164
|
agno/models/azure/openai_chat.py,sha256=RYHmcHA7cn79_U6GTjNP5vYiYET6IfPgUp6XZ2WEiyQ,4423
|
|
@@ -190,7 +190,7 @@ agno/models/internlm/internlm.py,sha256=9oX7gbvVS-2GJhGvfug7sVFWMLCg-m1icQyUeFZk
|
|
|
190
190
|
agno/models/langdb/__init__.py,sha256=ubh5nDcxyH33_ONwsmY4tWQz5esRwRjHBe68u9hdAIM,45
|
|
191
191
|
agno/models/langdb/langdb.py,sha256=tTorf22oSTA632LzFor6UtJLqAOP6gsEKtNimNeehZI,1631
|
|
192
192
|
agno/models/litellm/__init__.py,sha256=5e4yHqepF9-fOE0DMDIKnH6psFV1OcRgfAD5BaoVRgI,353
|
|
193
|
-
agno/models/litellm/chat.py,sha256=
|
|
193
|
+
agno/models/litellm/chat.py,sha256=4gaK3YhFeeVQl9-mzicFLFdxWwQ3FM7krZP_AmIPuIg,19209
|
|
194
194
|
agno/models/litellm/litellm_openai.py,sha256=6TNf6nRbCibPWEyubDmSLnoKpAKcM31afiklAYbujyY,759
|
|
195
195
|
agno/models/llama_cpp/__init__.py,sha256=oxOZfqEcOdcB74VLwOTO6bPmXHHA88uaeJO-IkXgr8A,84
|
|
196
196
|
agno/models/llama_cpp/llama_cpp.py,sha256=tWIF0TQH-sK29xCrTe7oy0SjSC-FlOmwmPL1d5IVMPM,673
|
|
@@ -232,14 +232,14 @@ agno/models/vllm/vllm.py,sha256=anBt3gshctbmda-OZJ51tOgbHHemsrSbQLMDqB39HIA,2707
|
|
|
232
232
|
agno/models/xai/__init__.py,sha256=ukcCxnCHxTtkJNA2bAMTX4MhCv1wJcbiq8ZIfYczIxs,55
|
|
233
233
|
agno/models/xai/xai.py,sha256=OqqhPySt64hQ4MM_qYhdMPPjoLRNhExyC-01sN86hJw,4158
|
|
234
234
|
agno/os/__init__.py,sha256=h8oQu7vhD5RZf09jkyM_Kt1Kdq_d5kFB9gJju8QPwcY,55
|
|
235
|
-
agno/os/app.py,sha256=
|
|
235
|
+
agno/os/app.py,sha256=FWHnivzKhJH1FayoobkQIrfZceQXU51DK8FUWQQz29A,26500
|
|
236
236
|
agno/os/auth.py,sha256=FyBtAKWtg-qSunCas5m5pK1dVEmikOSZvcCp5r25tTA,1844
|
|
237
237
|
agno/os/config.py,sha256=u4R9yazQXIcKjR3QzEIZw_XAe_OHp3xn0ff7SVkj2jA,2893
|
|
238
238
|
agno/os/mcp.py,sha256=5L0TZwInuCWM7TQ7MI_DjMWfVIIB0jxVCqaVEdR62UU,8114
|
|
239
239
|
agno/os/router.py,sha256=IFgQCyx16MHHA2UAqr1T8I2_dKJ_QqmOOYdnGjWABHE,62517
|
|
240
240
|
agno/os/schema.py,sha256=bdmGLQoBJyetiAZeajJG_gEKLaQSSEwiDwKy0q4Pa-M,38278
|
|
241
241
|
agno/os/settings.py,sha256=Cn5_8lZI8Vx1UaUYqs9h6Qp4IMDFn4f3c35uppiaMy4,1343
|
|
242
|
-
agno/os/utils.py,sha256=
|
|
242
|
+
agno/os/utils.py,sha256=VRxUQCBhjL62Qk2-KE-fl--RZEh1FDJ2Z0gjYpeIK3M,14528
|
|
243
243
|
agno/os/interfaces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
244
244
|
agno/os/interfaces/base.py,sha256=giU0y-egtXLqokN1HQyQIWl4NBawTk_ua7ytKGFmZRc,527
|
|
245
245
|
agno/os/interfaces/agui/__init__.py,sha256=1zrGICk4roXUINwSFZfqH6sBsbHmD5KjGYVJMGg4fKQ,66
|
|
@@ -294,7 +294,7 @@ agno/session/summary.py,sha256=2a74rDzrOGkWjrFkHQ6gf1dsZBU0_G1lgDE7b3fPaQw,8405
|
|
|
294
294
|
agno/session/team.py,sha256=ckz-h_sYoFEbqLmNYglS0mamI9QzLfh-2ixsrHW6lU0,10383
|
|
295
295
|
agno/session/workflow.py,sha256=8dWTona5jen1iPYwjcvxq1XG5EQDFnd28BEjcbqzl4s,5004
|
|
296
296
|
agno/team/__init__.py,sha256=toHidBOo5M3n_TIVtIKHgcDbLL9HR-_U-YQYuIt_XtE,847
|
|
297
|
-
agno/team/team.py,sha256=
|
|
297
|
+
agno/team/team.py,sha256=UmVMAPLHqwoW2x2BR4KrjHsJ2emd9dQ989TVNE3-WMw,315032
|
|
298
298
|
agno/tools/__init__.py,sha256=jNll2sELhPPbqm5nPeT4_uyzRO2_KRTW-8Or60kioS0,210
|
|
299
299
|
agno/tools/agentql.py,sha256=S82Z9aTNr-E5wnA4fbFs76COljJtiQIjf2grjz3CkHU,4104
|
|
300
300
|
agno/tools/airflow.py,sha256=uf2rOzZpSU64l_qRJ5Raku-R3Gky-uewmYkh6W0-oxg,2610
|
|
@@ -456,8 +456,7 @@ agno/utils/whatsapp.py,sha256=242VwGOdbgkxVeIj4D899mpT3GnG_IpcaKnd5qebhTA,9936
|
|
|
456
456
|
agno/utils/yaml_io.py,sha256=cwTqCE-eBGoi87KLDcwB6iyWe0NcvEmadQjWL1cQ7jE,860
|
|
457
457
|
agno/utils/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
458
458
|
agno/utils/models/ai_foundry.py,sha256=PmhETWhdqZCq8NbDe-MdZVuRXx6DbVOePCyPFiPLceo,1511
|
|
459
|
-
agno/utils/models/
|
|
460
|
-
agno/utils/models/claude.py,sha256=QYnARhGrLSvP2kfFCa7t-hnaZdU9Nby-B-21ocT9_rA,11293
|
|
459
|
+
agno/utils/models/claude.py,sha256=XkRS4XVIDJt440SlzQFemrIDpIsOs39AvK1fjyMuruU,11229
|
|
461
460
|
agno/utils/models/cohere.py,sha256=wir2K9u4RmOwq7T7n_2UPZFHfwmnrt_u91Psd_DFqdE,3266
|
|
462
461
|
agno/utils/models/llama.py,sha256=Z5fdOFUFnov1JgUDcP6ICK3M7o64UB1fkcwAs2XaZkM,2515
|
|
463
462
|
agno/utils/models/mistral.py,sha256=SVcJ8Q8SFeadNwCr8BARbET0gvGiylmaDKzcSJ9kWq0,4189
|
|
@@ -489,7 +488,7 @@ agno/vectordb/langchaindb/__init__.py,sha256=BxGs6tcEKTiydbVJL3P5djlnafS5Bbgql3u
|
|
|
489
488
|
agno/vectordb/langchaindb/langchaindb.py,sha256=VieHU4W-lRmYN0bLo2vmb7rGkmlNkPtjXILD4_F8818,5505
|
|
490
489
|
agno/vectordb/lightrag/__init__.py,sha256=fgQpA8pZW-jEHI91SZ_xgmROmv14oKdwCQZ8LpyipaE,84
|
|
491
490
|
agno/vectordb/lightrag/lightrag.py,sha256=GBReSzGBxpv0u0WTTZYTezZWpJQNj1zwL70ori3_cD4,14390
|
|
492
|
-
agno/vectordb/llamaindex/__init__.py,sha256=
|
|
491
|
+
agno/vectordb/llamaindex/__init__.py,sha256=fgdWIntUOX3n5YEPcPfr-yPb2kvmSy5ngQ5_UqAIrgM,103
|
|
493
492
|
agno/vectordb/llamaindex/llamaindexdb.py,sha256=co_GtDJuorGG0cN-taWiuNUXGwNlgE044PQjTT7lfcU,5432
|
|
494
493
|
agno/vectordb/milvus/__init__.py,sha256=I9V-Rm-rIYxWdRVIs6bKI-6JSJsyOd1-vvasvVpYHuE,127
|
|
495
494
|
agno/vectordb/milvus/milvus.py,sha256=fbpXXOV_ahxP5kX0KoPZ4_4M3kWC4gjGX0GEtuIVD8o,42394
|
|
@@ -520,9 +519,9 @@ agno/workflow/router.py,sha256=ZAiVsh2F_9ssKj0_RzHWzfgimaZ5hfb3Ni1Xx_SkVR0,26625
|
|
|
520
519
|
agno/workflow/step.py,sha256=15vZFN5HlO3IkHqsxkM-2InUzCWC0Ee8BtoJ-OAhS5w,52485
|
|
521
520
|
agno/workflow/steps.py,sha256=uRE4oGWs2cA-TrX881AEa69zu6rheXH81mNOZiRrNvg,23719
|
|
522
521
|
agno/workflow/types.py,sha256=ZEFyaKH42261_3yx810ABfi70RUfjPGSwiIep7n53yg,17485
|
|
523
|
-
agno/workflow/workflow.py,sha256=
|
|
524
|
-
agno-2.0.
|
|
525
|
-
agno-2.0.
|
|
526
|
-
agno-2.0.
|
|
527
|
-
agno-2.0.
|
|
528
|
-
agno-2.0.
|
|
522
|
+
agno/workflow/workflow.py,sha256=yjjVZ0MvkLjGgVaVt8jFWlm2urx7zZdAuJ0We9KRu2U,112738
|
|
523
|
+
agno-2.0.11.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
|
|
524
|
+
agno-2.0.11.dist-info/METADATA,sha256=rLaQ6VJDCNK1fLPp3sy3-TGYfF68OXGElkkmms5Qvho,22018
|
|
525
|
+
agno-2.0.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
526
|
+
agno-2.0.11.dist-info/top_level.txt,sha256=MKyeuVesTyOKIXUhc-d_tPa2Hrh0oTA4LM0izowpx70,5
|
|
527
|
+
agno-2.0.11.dist-info/RECORD,,
|
agno/utils/models/aws_claude.py
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from typing import Any, Dict, List, Optional, Tuple
|
|
3
|
-
|
|
4
|
-
from agno.media import Image
|
|
5
|
-
from agno.models.message import Message
|
|
6
|
-
from agno.utils.log import log_error, log_warning
|
|
7
|
-
|
|
8
|
-
try:
|
|
9
|
-
from anthropic.types import (
|
|
10
|
-
TextBlock,
|
|
11
|
-
ToolUseBlock,
|
|
12
|
-
)
|
|
13
|
-
except ImportError:
|
|
14
|
-
log_error("`anthropic[bedrock]` not installed. Please install it via `pip install anthropic[bedrock]`.")
|
|
15
|
-
raise
|
|
16
|
-
|
|
17
|
-
ROLE_MAP = {
|
|
18
|
-
"system": "system",
|
|
19
|
-
"user": "user",
|
|
20
|
-
"assistant": "assistant",
|
|
21
|
-
"tool": "user",
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def _format_image_for_message(image: Image) -> Optional[Dict[str, Any]]:
|
|
26
|
-
"""
|
|
27
|
-
Add an image to a message by converting it to base64 encoded format.
|
|
28
|
-
"""
|
|
29
|
-
using_filetype = False
|
|
30
|
-
|
|
31
|
-
import base64
|
|
32
|
-
|
|
33
|
-
# 'imghdr' was deprecated in Python 3.11: https://docs.python.org/3/library/imghdr.html
|
|
34
|
-
# 'filetype' used as a fallback
|
|
35
|
-
try:
|
|
36
|
-
import imghdr
|
|
37
|
-
except (ModuleNotFoundError, ImportError):
|
|
38
|
-
try:
|
|
39
|
-
import filetype
|
|
40
|
-
|
|
41
|
-
using_filetype = True
|
|
42
|
-
except (ModuleNotFoundError, ImportError):
|
|
43
|
-
raise ImportError("`filetype` not installed. Please install using `pip install filetype`")
|
|
44
|
-
|
|
45
|
-
type_mapping = {
|
|
46
|
-
"jpeg": "image/jpeg",
|
|
47
|
-
"jpg": "image/jpeg",
|
|
48
|
-
"png": "image/png",
|
|
49
|
-
"gif": "image/gif",
|
|
50
|
-
"webp": "image/webp",
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
try:
|
|
54
|
-
# Case 1: Image is a URL
|
|
55
|
-
if image.url is not None:
|
|
56
|
-
content_bytes = image.get_content_bytes() # type: ignore
|
|
57
|
-
|
|
58
|
-
# Case 2: Image is a local file path
|
|
59
|
-
elif image.filepath is not None:
|
|
60
|
-
from pathlib import Path
|
|
61
|
-
|
|
62
|
-
path = Path(image.filepath) if isinstance(image.filepath, str) else image.filepath
|
|
63
|
-
if path.exists() and path.is_file():
|
|
64
|
-
with open(image.filepath, "rb") as f:
|
|
65
|
-
content_bytes = f.read()
|
|
66
|
-
else:
|
|
67
|
-
log_error(f"Image file not found: {image}")
|
|
68
|
-
return None
|
|
69
|
-
|
|
70
|
-
# Case 3: Image is a bytes object
|
|
71
|
-
elif image.content is not None:
|
|
72
|
-
content_bytes = image.content
|
|
73
|
-
|
|
74
|
-
else:
|
|
75
|
-
log_error(f"Unsupported image type: {type(image)}")
|
|
76
|
-
return None
|
|
77
|
-
|
|
78
|
-
if using_filetype:
|
|
79
|
-
kind = filetype.guess(content_bytes)
|
|
80
|
-
if not kind:
|
|
81
|
-
log_error("Unable to determine image type")
|
|
82
|
-
return None
|
|
83
|
-
|
|
84
|
-
img_type = kind.extension
|
|
85
|
-
else:
|
|
86
|
-
img_type = imghdr.what(None, h=content_bytes) # type: ignore
|
|
87
|
-
|
|
88
|
-
if not img_type:
|
|
89
|
-
log_error("Unable to determine image type")
|
|
90
|
-
return None
|
|
91
|
-
|
|
92
|
-
media_type = type_mapping.get(img_type)
|
|
93
|
-
if not media_type:
|
|
94
|
-
log_error(f"Unsupported image type: {img_type}")
|
|
95
|
-
return None
|
|
96
|
-
|
|
97
|
-
return {
|
|
98
|
-
"type": "image",
|
|
99
|
-
"source": {
|
|
100
|
-
"type": "base64",
|
|
101
|
-
"media_type": media_type,
|
|
102
|
-
"data": base64.b64encode(content_bytes).decode("utf-8"), # type: ignore
|
|
103
|
-
},
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
except Exception as e:
|
|
107
|
-
log_error(f"Error processing image: {e}")
|
|
108
|
-
return None
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def format_messages(messages: List[Message]) -> Tuple[List[Dict[str, str]], str]:
|
|
112
|
-
"""
|
|
113
|
-
Process the list of messages and separate them into API messages and system messages.
|
|
114
|
-
|
|
115
|
-
Args:
|
|
116
|
-
messages (List[Message]): The list of messages to process.
|
|
117
|
-
|
|
118
|
-
Returns:
|
|
119
|
-
Tuple[List[Dict[str, str]], str]: A tuple containing the list of API messages and the concatenated system messages.
|
|
120
|
-
"""
|
|
121
|
-
|
|
122
|
-
chat_messages: List[Dict[str, str]] = []
|
|
123
|
-
system_messages: List[str] = []
|
|
124
|
-
|
|
125
|
-
for message in messages:
|
|
126
|
-
content = message.content or ""
|
|
127
|
-
if message.role == "system":
|
|
128
|
-
if content is not None:
|
|
129
|
-
system_messages.append(content) # type: ignore
|
|
130
|
-
continue
|
|
131
|
-
elif message.role == "user":
|
|
132
|
-
if isinstance(content, str):
|
|
133
|
-
content = [{"type": "text", "text": content}]
|
|
134
|
-
|
|
135
|
-
if message.images is not None:
|
|
136
|
-
for image in message.images:
|
|
137
|
-
image_content = _format_image_for_message(image)
|
|
138
|
-
if image_content:
|
|
139
|
-
content.append(image_content)
|
|
140
|
-
|
|
141
|
-
if message.files is not None and len(message.files) > 0:
|
|
142
|
-
log_warning("Files are not supported for AWS Bedrock Claude")
|
|
143
|
-
|
|
144
|
-
if message.audio is not None and len(message.audio) > 0:
|
|
145
|
-
log_warning("Audio is not supported for AWS Bedrock Claude")
|
|
146
|
-
|
|
147
|
-
if message.videos is not None and len(message.videos) > 0:
|
|
148
|
-
log_warning("Video is not supported for AWS Bedrock Claude")
|
|
149
|
-
|
|
150
|
-
# Handle tool calls from history
|
|
151
|
-
elif message.role == "assistant":
|
|
152
|
-
content = []
|
|
153
|
-
|
|
154
|
-
if isinstance(message.content, str) and message.content and len(message.content.strip()) > 0:
|
|
155
|
-
content.append(TextBlock(text=message.content, type="text"))
|
|
156
|
-
|
|
157
|
-
if message.tool_calls:
|
|
158
|
-
for tool_call in message.tool_calls:
|
|
159
|
-
content.append(
|
|
160
|
-
ToolUseBlock(
|
|
161
|
-
id=tool_call["id"],
|
|
162
|
-
input=json.loads(tool_call["function"]["arguments"])
|
|
163
|
-
if "arguments" in tool_call["function"]
|
|
164
|
-
else {},
|
|
165
|
-
name=tool_call["function"]["name"],
|
|
166
|
-
type="tool_use",
|
|
167
|
-
)
|
|
168
|
-
)
|
|
169
|
-
chat_messages.append({"role": ROLE_MAP[message.role], "content": content}) # type: ignore
|
|
170
|
-
return chat_messages, " ".join(system_messages)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|