agno 2.2.9__py3-none-any.whl → 2.2.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 +27 -5
- agno/db/dynamo/utils.py +1 -1
- agno/db/firestore/utils.py +1 -1
- agno/db/gcs_json/utils.py +1 -1
- agno/db/in_memory/utils.py +1 -1
- agno/db/json/utils.py +1 -1
- agno/db/mongo/utils.py +3 -3
- agno/db/mysql/utils.py +1 -1
- agno/db/postgres/utils.py +1 -1
- agno/db/redis/utils.py +1 -1
- agno/db/singlestore/utils.py +1 -1
- agno/db/sqlite/utils.py +1 -1
- agno/knowledge/chunking/agentic.py +8 -9
- agno/knowledge/chunking/strategy.py +59 -15
- agno/knowledge/embedder/sentence_transformer.py +6 -2
- agno/knowledge/reader/base.py +6 -2
- agno/knowledge/utils.py +20 -0
- agno/models/anthropic/claude.py +45 -9
- agno/models/base.py +4 -0
- agno/os/app.py +35 -19
- agno/os/routers/health.py +5 -3
- agno/os/routers/knowledge/knowledge.py +43 -17
- agno/os/routers/knowledge/schemas.py +4 -3
- agno/run/agent.py +11 -1
- agno/team/team.py +20 -3
- agno/tools/file_generation.py +4 -4
- agno/tools/gmail.py +179 -0
- agno/tools/parallel.py +314 -0
- agno/utils/models/claude.py +2 -1
- agno/workflow/agent.py +2 -2
- agno/workflow/condition.py +26 -4
- agno/workflow/loop.py +9 -0
- agno/workflow/parallel.py +39 -16
- agno/workflow/router.py +25 -4
- agno/workflow/step.py +163 -91
- agno/workflow/steps.py +9 -0
- agno/workflow/types.py +20 -1
- agno/workflow/workflow.py +117 -30
- {agno-2.2.9.dist-info → agno-2.2.11.dist-info}/METADATA +4 -1
- {agno-2.2.9.dist-info → agno-2.2.11.dist-info}/RECORD +43 -42
- {agno-2.2.9.dist-info → agno-2.2.11.dist-info}/WHEEL +0 -0
- {agno-2.2.9.dist-info → agno-2.2.11.dist-info}/licenses/LICENSE +0 -0
- {agno-2.2.9.dist-info → agno-2.2.11.dist-info}/top_level.txt +0 -0
agno/agent/agent.py
CHANGED
|
@@ -1454,6 +1454,7 @@ class Agent:
|
|
|
1454
1454
|
user_id: Optional[str] = None,
|
|
1455
1455
|
session_id: Optional[str] = None,
|
|
1456
1456
|
session_state: Optional[Dict[str, Any]] = None,
|
|
1457
|
+
run_context: Optional[RunContext] = None,
|
|
1457
1458
|
audio: Optional[Sequence[Audio]] = None,
|
|
1458
1459
|
images: Optional[Sequence[Image]] = None,
|
|
1459
1460
|
videos: Optional[Sequence[Video]] = None,
|
|
@@ -1480,6 +1481,7 @@ class Agent:
|
|
|
1480
1481
|
user_id: Optional[str] = None,
|
|
1481
1482
|
session_id: Optional[str] = None,
|
|
1482
1483
|
session_state: Optional[Dict[str, Any]] = None,
|
|
1484
|
+
run_context: Optional[RunContext] = None,
|
|
1483
1485
|
audio: Optional[Sequence[Audio]] = None,
|
|
1484
1486
|
images: Optional[Sequence[Image]] = None,
|
|
1485
1487
|
videos: Optional[Sequence[Video]] = None,
|
|
@@ -1507,6 +1509,7 @@ class Agent:
|
|
|
1507
1509
|
user_id: Optional[str] = None,
|
|
1508
1510
|
session_id: Optional[str] = None,
|
|
1509
1511
|
session_state: Optional[Dict[str, Any]] = None,
|
|
1512
|
+
run_context: Optional[RunContext] = None,
|
|
1510
1513
|
audio: Optional[Sequence[Audio]] = None,
|
|
1511
1514
|
images: Optional[Sequence[Image]] = None,
|
|
1512
1515
|
videos: Optional[Sequence[Video]] = None,
|
|
@@ -1581,7 +1584,7 @@ class Agent:
|
|
|
1581
1584
|
dependencies = dependencies if dependencies is not None else self.dependencies
|
|
1582
1585
|
|
|
1583
1586
|
# Initialize run context
|
|
1584
|
-
run_context = RunContext(
|
|
1587
|
+
run_context = run_context or RunContext(
|
|
1585
1588
|
run_id=run_id,
|
|
1586
1589
|
session_id=session_id,
|
|
1587
1590
|
user_id=user_id,
|
|
@@ -2374,6 +2377,7 @@ class Agent:
|
|
|
2374
2377
|
user_id: Optional[str] = None,
|
|
2375
2378
|
session_id: Optional[str] = None,
|
|
2376
2379
|
session_state: Optional[Dict[str, Any]] = None,
|
|
2380
|
+
run_context: Optional[RunContext] = None,
|
|
2377
2381
|
audio: Optional[Sequence[Audio]] = None,
|
|
2378
2382
|
images: Optional[Sequence[Image]] = None,
|
|
2379
2383
|
videos: Optional[Sequence[Video]] = None,
|
|
@@ -2399,6 +2403,7 @@ class Agent:
|
|
|
2399
2403
|
stream: Literal[True] = True,
|
|
2400
2404
|
user_id: Optional[str] = None,
|
|
2401
2405
|
session_id: Optional[str] = None,
|
|
2406
|
+
run_context: Optional[RunContext] = None,
|
|
2402
2407
|
audio: Optional[Sequence[Audio]] = None,
|
|
2403
2408
|
images: Optional[Sequence[Image]] = None,
|
|
2404
2409
|
videos: Optional[Sequence[Video]] = None,
|
|
@@ -2426,6 +2431,7 @@ class Agent:
|
|
|
2426
2431
|
user_id: Optional[str] = None,
|
|
2427
2432
|
session_id: Optional[str] = None,
|
|
2428
2433
|
session_state: Optional[Dict[str, Any]] = None,
|
|
2434
|
+
run_context: Optional[RunContext] = None,
|
|
2429
2435
|
audio: Optional[Sequence[Audio]] = None,
|
|
2430
2436
|
images: Optional[Sequence[Image]] = None,
|
|
2431
2437
|
videos: Optional[Sequence[Video]] = None,
|
|
@@ -2530,7 +2536,7 @@ class Agent:
|
|
|
2530
2536
|
merge_dictionaries(metadata, self.metadata)
|
|
2531
2537
|
|
|
2532
2538
|
# Initialize run context
|
|
2533
|
-
run_context = RunContext(
|
|
2539
|
+
run_context = run_context or RunContext(
|
|
2534
2540
|
run_id=run_id,
|
|
2535
2541
|
session_id=session_id,
|
|
2536
2542
|
user_id=user_id,
|
|
@@ -2692,6 +2698,7 @@ class Agent:
|
|
|
2692
2698
|
stream_intermediate_steps: Optional[bool] = None,
|
|
2693
2699
|
user_id: Optional[str] = None,
|
|
2694
2700
|
session_id: Optional[str] = None,
|
|
2701
|
+
run_context: Optional[RunContext] = None,
|
|
2695
2702
|
retries: Optional[int] = None,
|
|
2696
2703
|
knowledge_filters: Optional[Dict[str, Any]] = None,
|
|
2697
2704
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
@@ -2709,6 +2716,7 @@ class Agent:
|
|
|
2709
2716
|
stream_events: Whether to stream all events.
|
|
2710
2717
|
user_id: The user id to continue the run for.
|
|
2711
2718
|
session_id: The session id to continue the run for.
|
|
2719
|
+
run_context: The run context to use for the run.
|
|
2712
2720
|
retries: The number of retries to continue the run for.
|
|
2713
2721
|
knowledge_filters: The knowledge filters to use for the run.
|
|
2714
2722
|
dependencies: The dependencies to use for the run.
|
|
@@ -2749,7 +2757,7 @@ class Agent:
|
|
|
2749
2757
|
dependencies = dependencies if dependencies is not None else self.dependencies
|
|
2750
2758
|
|
|
2751
2759
|
# Initialize run context
|
|
2752
|
-
run_context = RunContext(
|
|
2760
|
+
run_context = run_context or RunContext(
|
|
2753
2761
|
run_id=run_id, # type: ignore
|
|
2754
2762
|
session_id=session_id,
|
|
2755
2763
|
user_id=user_id,
|
|
@@ -3251,6 +3259,7 @@ class Agent:
|
|
|
3251
3259
|
stream_intermediate_steps: Optional[bool] = None,
|
|
3252
3260
|
user_id: Optional[str] = None,
|
|
3253
3261
|
session_id: Optional[str] = None,
|
|
3262
|
+
run_context: Optional[RunContext] = None,
|
|
3254
3263
|
retries: Optional[int] = None,
|
|
3255
3264
|
knowledge_filters: Optional[Dict[str, Any]] = None,
|
|
3256
3265
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
@@ -3269,6 +3278,7 @@ class Agent:
|
|
|
3269
3278
|
stream_events: Whether to stream all events.
|
|
3270
3279
|
user_id: The user id to continue the run for.
|
|
3271
3280
|
session_id: The session id to continue the run for.
|
|
3281
|
+
run_context: The run context to use for the run.
|
|
3272
3282
|
retries: The number of retries to continue the run for.
|
|
3273
3283
|
knowledge_filters: The knowledge filters to use for the run.
|
|
3274
3284
|
dependencies: The dependencies to use for continuing the run.
|
|
@@ -3335,7 +3345,7 @@ class Agent:
|
|
|
3335
3345
|
self.model = cast(Model, self.model)
|
|
3336
3346
|
|
|
3337
3347
|
# Initialize run context
|
|
3338
|
-
run_context = RunContext(
|
|
3348
|
+
run_context = run_context or RunContext(
|
|
3339
3349
|
run_id=run_id, # type: ignore
|
|
3340
3350
|
session_id=session_id,
|
|
3341
3351
|
user_id=user_id,
|
|
@@ -5753,6 +5763,9 @@ class Agent:
|
|
|
5753
5763
|
raise ValueError("Db not initialized")
|
|
5754
5764
|
return self.db.get_session(session_id=session_id, session_type=session_type) # type: ignore
|
|
5755
5765
|
except Exception as e:
|
|
5766
|
+
import traceback
|
|
5767
|
+
|
|
5768
|
+
traceback.print_exc(limit=3)
|
|
5756
5769
|
log_warning(f"Error getting session from db: {e}")
|
|
5757
5770
|
return None
|
|
5758
5771
|
|
|
@@ -5763,8 +5776,11 @@ class Agent:
|
|
|
5763
5776
|
try:
|
|
5764
5777
|
if not self.db:
|
|
5765
5778
|
raise ValueError("Db not initialized")
|
|
5766
|
-
return await self.db.get_session(session_id=session_id, session_type=
|
|
5779
|
+
return await self.db.get_session(session_id=session_id, session_type=session_type) # type: ignore
|
|
5767
5780
|
except Exception as e:
|
|
5781
|
+
import traceback
|
|
5782
|
+
|
|
5783
|
+
traceback.print_exc(limit=3)
|
|
5768
5784
|
log_warning(f"Error getting session from db: {e}")
|
|
5769
5785
|
return None
|
|
5770
5786
|
|
|
@@ -5776,6 +5792,9 @@ class Agent:
|
|
|
5776
5792
|
raise ValueError("Db not initialized")
|
|
5777
5793
|
return self.db.upsert_session(session=session) # type: ignore
|
|
5778
5794
|
except Exception as e:
|
|
5795
|
+
import traceback
|
|
5796
|
+
|
|
5797
|
+
traceback.print_exc(limit=3)
|
|
5779
5798
|
log_warning(f"Error upserting session into db: {e}")
|
|
5780
5799
|
return None
|
|
5781
5800
|
|
|
@@ -5786,6 +5805,9 @@ class Agent:
|
|
|
5786
5805
|
raise ValueError("Db not initialized")
|
|
5787
5806
|
return await self.db.upsert_session(session=session) # type: ignore
|
|
5788
5807
|
except Exception as e:
|
|
5808
|
+
import traceback
|
|
5809
|
+
|
|
5810
|
+
traceback.print_exc(limit=3)
|
|
5789
5811
|
log_warning(f"Error upserting session into db: {e}")
|
|
5790
5812
|
return None
|
|
5791
5813
|
|
agno/db/dynamo/utils.py
CHANGED
|
@@ -343,7 +343,7 @@ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
|
343
343
|
]
|
|
344
344
|
all_user_ids = set()
|
|
345
345
|
for session_type, sessions_count_key, runs_count_key in session_types:
|
|
346
|
-
sessions = sessions_data.get(session_type, [])
|
|
346
|
+
sessions = sessions_data.get(session_type, []) or []
|
|
347
347
|
metrics[sessions_count_key] = len(sessions)
|
|
348
348
|
for session in sessions:
|
|
349
349
|
if session.get("user_id"):
|
agno/db/firestore/utils.py
CHANGED
|
@@ -194,7 +194,7 @@ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
|
194
194
|
all_user_ids = set()
|
|
195
195
|
|
|
196
196
|
for session_type, sessions_count_key, runs_count_key in session_types:
|
|
197
|
-
sessions = sessions_data.get(session_type, [])
|
|
197
|
+
sessions = sessions_data.get(session_type, []) or []
|
|
198
198
|
metrics[sessions_count_key] = len(sessions)
|
|
199
199
|
|
|
200
200
|
for session in sessions:
|
agno/db/gcs_json/utils.py
CHANGED
|
@@ -78,7 +78,7 @@ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
|
78
78
|
all_user_ids = set()
|
|
79
79
|
|
|
80
80
|
for session_type, sessions_count_key, runs_count_key in session_types:
|
|
81
|
-
sessions = sessions_data.get(session_type, [])
|
|
81
|
+
sessions = sessions_data.get(session_type, []) or []
|
|
82
82
|
metrics[sessions_count_key] = len(sessions)
|
|
83
83
|
|
|
84
84
|
for session in sessions:
|
agno/db/in_memory/utils.py
CHANGED
|
@@ -78,7 +78,7 @@ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
|
78
78
|
all_user_ids = set()
|
|
79
79
|
|
|
80
80
|
for session_type, sessions_count_key, runs_count_key in session_types:
|
|
81
|
-
sessions = sessions_data.get(session_type, [])
|
|
81
|
+
sessions = sessions_data.get(session_type, []) or []
|
|
82
82
|
metrics[sessions_count_key] = len(sessions)
|
|
83
83
|
|
|
84
84
|
for session in sessions:
|
agno/db/json/utils.py
CHANGED
|
@@ -78,7 +78,7 @@ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
|
78
78
|
all_user_ids = set()
|
|
79
79
|
|
|
80
80
|
for session_type, sessions_count_key, runs_count_key in session_types:
|
|
81
|
-
sessions = sessions_data.get(session_type, [])
|
|
81
|
+
sessions = sessions_data.get(session_type, []) or []
|
|
82
82
|
metrics[sessions_count_key] = len(sessions)
|
|
83
83
|
|
|
84
84
|
for session in sessions:
|
agno/db/mongo/utils.py
CHANGED
|
@@ -89,14 +89,14 @@ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
|
89
89
|
all_user_ids = set()
|
|
90
90
|
|
|
91
91
|
for session_type, sessions_count_key, runs_count_key in session_types:
|
|
92
|
-
sessions = sessions_data.get(session_type, [])
|
|
92
|
+
sessions = sessions_data.get(session_type, []) or []
|
|
93
93
|
metrics[sessions_count_key] = len(sessions)
|
|
94
94
|
|
|
95
95
|
for session in sessions:
|
|
96
96
|
if session.get("user_id"):
|
|
97
97
|
all_user_ids.add(session["user_id"])
|
|
98
|
-
runs = session.get("runs", [])
|
|
99
|
-
metrics[runs_count_key] += len(
|
|
98
|
+
runs = session.get("runs", [])
|
|
99
|
+
metrics[runs_count_key] += len(runs)
|
|
100
100
|
|
|
101
101
|
if runs := session.get("runs", []):
|
|
102
102
|
if isinstance(runs, str):
|
agno/db/mysql/utils.py
CHANGED
|
@@ -205,7 +205,7 @@ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
|
205
205
|
all_user_ids = set()
|
|
206
206
|
|
|
207
207
|
for session_type, sessions_count_key, runs_count_key in session_types:
|
|
208
|
-
sessions = sessions_data.get(session_type, [])
|
|
208
|
+
sessions = sessions_data.get(session_type, []) or []
|
|
209
209
|
metrics[sessions_count_key] = len(sessions)
|
|
210
210
|
|
|
211
211
|
for session in sessions:
|
agno/db/postgres/utils.py
CHANGED
|
@@ -292,7 +292,7 @@ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
|
292
292
|
all_user_ids = set()
|
|
293
293
|
|
|
294
294
|
for session_type, sessions_count_key, runs_count_key in session_types:
|
|
295
|
-
sessions = sessions_data.get(session_type, [])
|
|
295
|
+
sessions = sessions_data.get(session_type, []) or []
|
|
296
296
|
metrics[sessions_count_key] = len(sessions)
|
|
297
297
|
|
|
298
298
|
for session in sessions:
|
agno/db/redis/utils.py
CHANGED
|
@@ -200,7 +200,7 @@ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
|
200
200
|
all_user_ids = set()
|
|
201
201
|
|
|
202
202
|
for session_type, sessions_count_key, runs_count_key in session_types:
|
|
203
|
-
sessions = sessions_data.get(session_type, [])
|
|
203
|
+
sessions = sessions_data.get(session_type, []) or []
|
|
204
204
|
metrics[sessions_count_key] = len(sessions)
|
|
205
205
|
|
|
206
206
|
for session in sessions:
|
agno/db/singlestore/utils.py
CHANGED
|
@@ -234,7 +234,7 @@ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
|
234
234
|
all_user_ids = set()
|
|
235
235
|
|
|
236
236
|
for session_type, sessions_count_key, runs_count_key in session_types:
|
|
237
|
-
sessions = sessions_data.get(session_type, [])
|
|
237
|
+
sessions = sessions_data.get(session_type, []) or []
|
|
238
238
|
metrics[sessions_count_key] = len(sessions)
|
|
239
239
|
|
|
240
240
|
for session in sessions:
|
agno/db/sqlite/utils.py
CHANGED
|
@@ -270,7 +270,7 @@ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
|
270
270
|
all_user_ids = set()
|
|
271
271
|
|
|
272
272
|
for session_type, sessions_count_key, runs_count_key in session_types:
|
|
273
|
-
sessions = sessions_data.get(session_type, [])
|
|
273
|
+
sessions = sessions_data.get(session_type, []) or []
|
|
274
274
|
metrics[sessions_count_key] = len(sessions)
|
|
275
275
|
|
|
276
276
|
for session in sessions:
|
|
@@ -20,13 +20,12 @@ class AgenticChunking(ChunkingStrategy):
|
|
|
20
20
|
except Exception:
|
|
21
21
|
raise ValueError("`openai` isn't installed. Please install it with `pip install openai`")
|
|
22
22
|
model = OpenAIChat(DEFAULT_OPENAI_MODEL_ID)
|
|
23
|
-
|
|
24
|
-
self.max_chunk_size = max_chunk_size
|
|
23
|
+
self.chunk_size = max_chunk_size
|
|
25
24
|
self.model = model
|
|
26
25
|
|
|
27
26
|
def chunk(self, document: Document) -> List[Document]:
|
|
28
27
|
"""Split text into chunks using LLM to determine natural breakpoints based on context"""
|
|
29
|
-
if len(document.content) <= self.
|
|
28
|
+
if len(document.content) <= self.chunk_size:
|
|
30
29
|
return [document]
|
|
31
30
|
|
|
32
31
|
chunks: List[Document] = []
|
|
@@ -35,22 +34,22 @@ class AgenticChunking(ChunkingStrategy):
|
|
|
35
34
|
chunk_number = 1
|
|
36
35
|
|
|
37
36
|
while remaining_text:
|
|
38
|
-
# Ask model to find a good breakpoint within
|
|
39
|
-
prompt = f"""Analyze this text and determine a natural breakpoint within the first {self.
|
|
37
|
+
# Ask model to find a good breakpoint within chunk_size
|
|
38
|
+
prompt = f"""Analyze this text and determine a natural breakpoint within the first {self.chunk_size} characters.
|
|
40
39
|
Consider semantic completeness, paragraph boundaries, and topic transitions.
|
|
41
40
|
Return only the character position number of where to break the text:
|
|
42
41
|
|
|
43
|
-
{remaining_text[: self.
|
|
42
|
+
{remaining_text[: self.chunk_size]}"""
|
|
44
43
|
|
|
45
44
|
try:
|
|
46
45
|
response = self.model.response([Message(role="user", content=prompt)])
|
|
47
46
|
if response and response.content:
|
|
48
|
-
break_point = min(int(response.content.strip()), self.
|
|
47
|
+
break_point = min(int(response.content.strip()), self.chunk_size)
|
|
49
48
|
else:
|
|
50
|
-
break_point = self.
|
|
49
|
+
break_point = self.chunk_size
|
|
51
50
|
except Exception:
|
|
52
51
|
# Fallback to max size if model fails
|
|
53
|
-
break_point = self.
|
|
52
|
+
break_point = self.chunk_size
|
|
54
53
|
|
|
55
54
|
# Extract chunk and update remaining text
|
|
56
55
|
chunk = remaining_text[:break_point].strip()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from enum import Enum
|
|
3
|
-
from typing import List
|
|
3
|
+
from typing import List, Optional
|
|
4
4
|
|
|
5
5
|
from agno.knowledge.document.base import Document
|
|
6
6
|
|
|
@@ -60,7 +60,13 @@ class ChunkingStrategyFactory:
|
|
|
60
60
|
"""Factory for creating chunking strategy instances."""
|
|
61
61
|
|
|
62
62
|
@classmethod
|
|
63
|
-
def create_strategy(
|
|
63
|
+
def create_strategy(
|
|
64
|
+
cls,
|
|
65
|
+
strategy_type: ChunkingStrategyType,
|
|
66
|
+
chunk_size: Optional[int] = None,
|
|
67
|
+
overlap: Optional[int] = None,
|
|
68
|
+
**kwargs,
|
|
69
|
+
) -> ChunkingStrategy:
|
|
64
70
|
"""Create an instance of the chunking strategy with the given parameters."""
|
|
65
71
|
strategy_map = {
|
|
66
72
|
ChunkingStrategyType.AGENTIC_CHUNKER: cls._create_agentic_chunking,
|
|
@@ -71,51 +77,89 @@ class ChunkingStrategyFactory:
|
|
|
71
77
|
ChunkingStrategyType.ROW_CHUNKER: cls._create_row_chunking,
|
|
72
78
|
ChunkingStrategyType.MARKDOWN_CHUNKER: cls._create_markdown_chunking,
|
|
73
79
|
}
|
|
74
|
-
return strategy_map[strategy_type](**kwargs)
|
|
80
|
+
return strategy_map[strategy_type](chunk_size=chunk_size, overlap=overlap, **kwargs)
|
|
75
81
|
|
|
76
82
|
@classmethod
|
|
77
|
-
def _create_agentic_chunking(
|
|
83
|
+
def _create_agentic_chunking(
|
|
84
|
+
cls, chunk_size: Optional[int] = None, overlap: Optional[int] = None, **kwargs
|
|
85
|
+
) -> ChunkingStrategy:
|
|
78
86
|
from agno.knowledge.chunking.agentic import AgenticChunking
|
|
79
87
|
|
|
80
|
-
#
|
|
81
|
-
if
|
|
82
|
-
kwargs["max_chunk_size"] =
|
|
88
|
+
# AgenticChunking accepts max_chunk_size (not chunk_size) and no overlap
|
|
89
|
+
if chunk_size is not None:
|
|
90
|
+
kwargs["max_chunk_size"] = chunk_size
|
|
91
|
+
# Remove overlap since AgenticChunking doesn't support it
|
|
83
92
|
return AgenticChunking(**kwargs)
|
|
84
93
|
|
|
85
94
|
@classmethod
|
|
86
|
-
def _create_document_chunking(
|
|
95
|
+
def _create_document_chunking(
|
|
96
|
+
cls, chunk_size: Optional[int] = None, overlap: Optional[int] = None, **kwargs
|
|
97
|
+
) -> ChunkingStrategy:
|
|
87
98
|
from agno.knowledge.chunking.document import DocumentChunking
|
|
88
99
|
|
|
100
|
+
# DocumentChunking accepts both chunk_size and overlap
|
|
101
|
+
if chunk_size is not None:
|
|
102
|
+
kwargs["chunk_size"] = chunk_size
|
|
103
|
+
if overlap is not None:
|
|
104
|
+
kwargs["overlap"] = overlap
|
|
89
105
|
return DocumentChunking(**kwargs)
|
|
90
106
|
|
|
91
107
|
@classmethod
|
|
92
|
-
def _create_recursive_chunking(
|
|
108
|
+
def _create_recursive_chunking(
|
|
109
|
+
cls, chunk_size: Optional[int] = None, overlap: Optional[int] = None, **kwargs
|
|
110
|
+
) -> ChunkingStrategy:
|
|
93
111
|
from agno.knowledge.chunking.recursive import RecursiveChunking
|
|
94
112
|
|
|
113
|
+
# RecursiveChunking accepts both chunk_size and overlap
|
|
114
|
+
if chunk_size is not None:
|
|
115
|
+
kwargs["chunk_size"] = chunk_size
|
|
116
|
+
if overlap is not None:
|
|
117
|
+
kwargs["overlap"] = overlap
|
|
95
118
|
return RecursiveChunking(**kwargs)
|
|
96
119
|
|
|
97
120
|
@classmethod
|
|
98
|
-
def _create_semantic_chunking(
|
|
121
|
+
def _create_semantic_chunking(
|
|
122
|
+
cls, chunk_size: Optional[int] = None, overlap: Optional[int] = None, **kwargs
|
|
123
|
+
) -> ChunkingStrategy:
|
|
99
124
|
from agno.knowledge.chunking.semantic import SemanticChunking
|
|
100
125
|
|
|
126
|
+
# SemanticChunking accepts chunk_size but not overlap
|
|
127
|
+
if chunk_size is not None:
|
|
128
|
+
kwargs["chunk_size"] = chunk_size
|
|
129
|
+
# Remove overlap since SemanticChunking doesn't support it
|
|
101
130
|
return SemanticChunking(**kwargs)
|
|
102
131
|
|
|
103
132
|
@classmethod
|
|
104
|
-
def _create_fixed_chunking(
|
|
133
|
+
def _create_fixed_chunking(
|
|
134
|
+
cls, chunk_size: Optional[int] = None, overlap: Optional[int] = None, **kwargs
|
|
135
|
+
) -> ChunkingStrategy:
|
|
105
136
|
from agno.knowledge.chunking.fixed import FixedSizeChunking
|
|
106
137
|
|
|
138
|
+
# FixedSizeChunking accepts both chunk_size and overlap
|
|
139
|
+
if chunk_size is not None:
|
|
140
|
+
kwargs["chunk_size"] = chunk_size
|
|
141
|
+
if overlap is not None:
|
|
142
|
+
kwargs["overlap"] = overlap
|
|
107
143
|
return FixedSizeChunking(**kwargs)
|
|
108
144
|
|
|
109
145
|
@classmethod
|
|
110
|
-
def _create_row_chunking(
|
|
146
|
+
def _create_row_chunking(
|
|
147
|
+
cls, chunk_size: Optional[int] = None, overlap: Optional[int] = None, **kwargs
|
|
148
|
+
) -> ChunkingStrategy:
|
|
111
149
|
from agno.knowledge.chunking.row import RowChunking
|
|
112
150
|
|
|
113
|
-
#
|
|
114
|
-
kwargs.pop("chunk_size", None)
|
|
151
|
+
# RowChunking doesn't accept chunk_size or overlap, only skip_header and clean_rows
|
|
115
152
|
return RowChunking(**kwargs)
|
|
116
153
|
|
|
117
154
|
@classmethod
|
|
118
|
-
def _create_markdown_chunking(
|
|
155
|
+
def _create_markdown_chunking(
|
|
156
|
+
cls, chunk_size: Optional[int] = None, overlap: Optional[int] = None, **kwargs
|
|
157
|
+
) -> ChunkingStrategy:
|
|
119
158
|
from agno.knowledge.chunking.markdown import MarkdownChunking
|
|
120
159
|
|
|
160
|
+
# MarkdownChunking accepts both chunk_size and overlap
|
|
161
|
+
if chunk_size is not None:
|
|
162
|
+
kwargs["chunk_size"] = chunk_size
|
|
163
|
+
if overlap is not None:
|
|
164
|
+
kwargs["overlap"] = overlap
|
|
121
165
|
return MarkdownChunking(**kwargs)
|
|
@@ -25,10 +25,14 @@ class SentenceTransformerEmbedder(Embedder):
|
|
|
25
25
|
prompt: Optional[str] = None
|
|
26
26
|
normalize_embeddings: bool = False
|
|
27
27
|
|
|
28
|
-
def
|
|
29
|
-
|
|
28
|
+
def __post_init__(self):
|
|
29
|
+
# Initialize the SentenceTransformer model eagerly to avoid race conditions in async contexts
|
|
30
|
+
if self.sentence_transformer_client is None:
|
|
30
31
|
self.sentence_transformer_client = SentenceTransformer(model_name_or_path=self.id)
|
|
31
32
|
|
|
33
|
+
def get_embedding(self, text: Union[str, List[str]]) -> List[float]:
|
|
34
|
+
if self.sentence_transformer_client is None:
|
|
35
|
+
raise RuntimeError("SentenceTransformer model not initialized")
|
|
32
36
|
model = self.sentence_transformer_client
|
|
33
37
|
embedding = model.encode(text, prompt=self.prompt, normalize_embeddings=self.normalize_embeddings)
|
|
34
38
|
try:
|
agno/knowledge/reader/base.py
CHANGED
|
@@ -44,11 +44,15 @@ class Reader:
|
|
|
44
44
|
self.max_results = max_results
|
|
45
45
|
self.encoding = encoding
|
|
46
46
|
|
|
47
|
-
def set_chunking_strategy_from_string(
|
|
47
|
+
def set_chunking_strategy_from_string(
|
|
48
|
+
self, strategy_name: str, chunk_size: Optional[int] = None, overlap: Optional[int] = None, **kwargs
|
|
49
|
+
) -> None:
|
|
48
50
|
"""Set the chunking strategy from a string name."""
|
|
49
51
|
try:
|
|
50
52
|
strategy_type = ChunkingStrategyType.from_string(strategy_name)
|
|
51
|
-
self.chunking_strategy = ChunkingStrategyFactory.create_strategy(
|
|
53
|
+
self.chunking_strategy = ChunkingStrategyFactory.create_strategy(
|
|
54
|
+
strategy_type, chunk_size=chunk_size, overlap=overlap, **kwargs
|
|
55
|
+
)
|
|
52
56
|
except ValueError as e:
|
|
53
57
|
raise ValueError(f"Failed to set chunking strategy: {e}")
|
|
54
58
|
|
agno/knowledge/utils.py
CHANGED
|
@@ -129,12 +129,32 @@ def get_chunker_info(chunker_key: str) -> Dict:
|
|
|
129
129
|
class_name = chunker_class.__name__
|
|
130
130
|
docstring = chunker_class.__doc__ or f"{class_name} chunking strategy"
|
|
131
131
|
|
|
132
|
+
# Check class __init__ signature for chunk_size and overlap parameters
|
|
133
|
+
metadata = {}
|
|
134
|
+
import inspect
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
sig = inspect.signature(chunker_class.__init__)
|
|
138
|
+
param_names = set(sig.parameters.keys())
|
|
139
|
+
|
|
140
|
+
# If class has chunk_size or max_chunk_size parameter, set default chunk_size
|
|
141
|
+
if "chunk_size" in param_names or "max_chunk_size" in param_names:
|
|
142
|
+
metadata["chunk_size"] = 5000
|
|
143
|
+
|
|
144
|
+
# If class has overlap parameter, set default overlap
|
|
145
|
+
if "overlap" in param_names:
|
|
146
|
+
metadata["chunk_overlap"] = 0
|
|
147
|
+
except Exception:
|
|
148
|
+
# If we can't inspect, skip metadata
|
|
149
|
+
pass
|
|
150
|
+
|
|
132
151
|
return {
|
|
133
152
|
"key": chunker_key,
|
|
134
153
|
"class_name": class_name,
|
|
135
154
|
"name": chunker_key,
|
|
136
155
|
"description": docstring.strip(),
|
|
137
156
|
"strategy_type": strategy_type.value,
|
|
157
|
+
"metadata": metadata,
|
|
138
158
|
}
|
|
139
159
|
except ValueError:
|
|
140
160
|
raise ValueError(f"Unknown chunker key: {chunker_key}")
|
agno/models/anthropic/claude.py
CHANGED
|
@@ -45,6 +45,8 @@ except ImportError as e:
|
|
|
45
45
|
# Import Beta types
|
|
46
46
|
try:
|
|
47
47
|
from anthropic.types.beta import BetaRawContentBlockDeltaEvent, BetaTextDelta
|
|
48
|
+
from anthropic.types.beta.beta_message import BetaMessage
|
|
49
|
+
from anthropic.types.beta.beta_usage import BetaUsage
|
|
48
50
|
except ImportError as e:
|
|
49
51
|
raise ImportError(
|
|
50
52
|
"`anthropic` not installed or missing beta components. Please install with `pip install anthropic`"
|
|
@@ -84,13 +86,14 @@ class Claude(Model):
|
|
|
84
86
|
cache_system_prompt: Optional[bool] = False
|
|
85
87
|
extended_cache_time: Optional[bool] = False
|
|
86
88
|
request_params: Optional[Dict[str, Any]] = None
|
|
87
|
-
mcp_servers: Optional[List[MCPServerConfiguration]] = None
|
|
88
89
|
|
|
89
|
-
#
|
|
90
|
+
# Anthropic beta and experimental features
|
|
91
|
+
betas: Optional[List[str]] = None # Enables specific experimental or newly released features.
|
|
92
|
+
context_management: Optional[Dict[str, Any]] = None
|
|
93
|
+
mcp_servers: Optional[List[MCPServerConfiguration]] = None
|
|
90
94
|
skills: Optional[List[Dict[str, str]]] = (
|
|
91
95
|
None # e.g., [{"type": "anthropic", "skill_id": "pptx", "version": "latest"}]
|
|
92
96
|
)
|
|
93
|
-
betas: Optional[List[str]] = None # Enables specific experimental or newly released features.
|
|
94
97
|
|
|
95
98
|
# Client parameters
|
|
96
99
|
api_key: Optional[str] = None
|
|
@@ -129,6 +132,15 @@ class Claude(Model):
|
|
|
129
132
|
client_params["default_headers"] = self.default_headers
|
|
130
133
|
return client_params
|
|
131
134
|
|
|
135
|
+
def _has_beta_features(self) -> bool:
|
|
136
|
+
"""Check if the model has any Anthropic beta features enabled."""
|
|
137
|
+
return (
|
|
138
|
+
self.mcp_servers is not None
|
|
139
|
+
or self.context_management is not None
|
|
140
|
+
or self.skills is not None
|
|
141
|
+
or self.betas is not None
|
|
142
|
+
)
|
|
143
|
+
|
|
132
144
|
def get_client(self) -> AnthropicClient:
|
|
133
145
|
"""
|
|
134
146
|
Returns an instance of the Anthropic client.
|
|
@@ -208,6 +220,10 @@ class Claude(Model):
|
|
|
208
220
|
_request_params["top_p"] = self.top_p
|
|
209
221
|
if self.top_k:
|
|
210
222
|
_request_params["top_k"] = self.top_k
|
|
223
|
+
if self.betas:
|
|
224
|
+
_request_params["betas"] = self.betas
|
|
225
|
+
if self.context_management:
|
|
226
|
+
_request_params["context_management"] = self.context_management
|
|
211
227
|
if self.mcp_servers:
|
|
212
228
|
_request_params["mcp_servers"] = [
|
|
213
229
|
{k: v for k, v in asdict(server).items() if v is not None} for server in self.mcp_servers
|
|
@@ -279,7 +295,7 @@ class Claude(Model):
|
|
|
279
295
|
chat_messages, system_message = format_messages(messages)
|
|
280
296
|
request_kwargs = self._prepare_request_kwargs(system_message, tools)
|
|
281
297
|
|
|
282
|
-
if self.
|
|
298
|
+
if self._has_beta_features():
|
|
283
299
|
assistant_message.metrics.start_timer()
|
|
284
300
|
provider_response = self.get_client().beta.messages.create(
|
|
285
301
|
model=self.id,
|
|
@@ -346,7 +362,8 @@ class Claude(Model):
|
|
|
346
362
|
if run_response and run_response.metrics:
|
|
347
363
|
run_response.metrics.set_time_to_first_token()
|
|
348
364
|
|
|
349
|
-
|
|
365
|
+
# Beta features
|
|
366
|
+
if self._has_beta_features():
|
|
350
367
|
assistant_message.metrics.start_timer()
|
|
351
368
|
with self.get_client().beta.messages.stream(
|
|
352
369
|
model=self.id,
|
|
@@ -401,7 +418,8 @@ class Claude(Model):
|
|
|
401
418
|
chat_messages, system_message = format_messages(messages)
|
|
402
419
|
request_kwargs = self._prepare_request_kwargs(system_message, tools)
|
|
403
420
|
|
|
404
|
-
|
|
421
|
+
# Beta features
|
|
422
|
+
if self._has_beta_features():
|
|
405
423
|
assistant_message.metrics.start_timer()
|
|
406
424
|
provider_response = await self.get_async_client().beta.messages.create(
|
|
407
425
|
model=self.id,
|
|
@@ -465,7 +483,7 @@ class Claude(Model):
|
|
|
465
483
|
chat_messages, system_message = format_messages(messages)
|
|
466
484
|
request_kwargs = self._prepare_request_kwargs(system_message, tools)
|
|
467
485
|
|
|
468
|
-
if self.
|
|
486
|
+
if self._has_beta_features():
|
|
469
487
|
assistant_message.metrics.start_timer()
|
|
470
488
|
async with self.get_async_client().beta.messages.stream(
|
|
471
489
|
model=self.id,
|
|
@@ -507,7 +525,7 @@ class Claude(Model):
|
|
|
507
525
|
return tool_call_prompt
|
|
508
526
|
return None
|
|
509
527
|
|
|
510
|
-
def _parse_provider_response(self, response: AnthropicMessage, **kwargs) -> ModelResponse:
|
|
528
|
+
def _parse_provider_response(self, response: Union[AnthropicMessage, BetaMessage], **kwargs) -> ModelResponse:
|
|
511
529
|
"""
|
|
512
530
|
Parse the Claude response into a ModelResponse.
|
|
513
531
|
|
|
@@ -582,6 +600,14 @@ class Claude(Model):
|
|
|
582
600
|
if response.usage is not None:
|
|
583
601
|
model_response.response_usage = self._get_metrics(response.usage)
|
|
584
602
|
|
|
603
|
+
# Capture context management information if present
|
|
604
|
+
if self.context_management is not None and hasattr(response, "context_management"):
|
|
605
|
+
if response.context_management is not None: # type: ignore
|
|
606
|
+
model_response.provider_data = model_response.provider_data or {}
|
|
607
|
+
if hasattr(response.context_management, "model_dump"):
|
|
608
|
+
model_response.provider_data["context_management"] = response.context_management.model_dump() # type: ignore
|
|
609
|
+
else:
|
|
610
|
+
model_response.provider_data["context_management"] = response.context_management # type: ignore
|
|
585
611
|
# Extract file IDs if skills are enabled
|
|
586
612
|
if self.skills and response.content:
|
|
587
613
|
file_ids: List[str] = []
|
|
@@ -676,6 +702,16 @@ class Claude(Model):
|
|
|
676
702
|
DocumentCitation(document_title=citation.document_title, cited_text=citation.cited_text)
|
|
677
703
|
)
|
|
678
704
|
|
|
705
|
+
# Capture context management information if present
|
|
706
|
+
if self.context_management is not None and hasattr(response.message, "context_management"): # type: ignore
|
|
707
|
+
context_mgmt = response.message.context_management # type: ignore
|
|
708
|
+
if context_mgmt is not None:
|
|
709
|
+
model_response.provider_data = model_response.provider_data or {}
|
|
710
|
+
if hasattr(context_mgmt, "model_dump"):
|
|
711
|
+
model_response.provider_data["context_management"] = context_mgmt.model_dump()
|
|
712
|
+
else:
|
|
713
|
+
model_response.provider_data["context_management"] = context_mgmt
|
|
714
|
+
|
|
679
715
|
if hasattr(response, "message") and hasattr(response.message, "usage") and response.message.usage is not None: # type: ignore
|
|
680
716
|
model_response.response_usage = self._get_metrics(response.message.usage) # type: ignore
|
|
681
717
|
|
|
@@ -692,7 +728,7 @@ class Claude(Model):
|
|
|
692
728
|
|
|
693
729
|
return model_response
|
|
694
730
|
|
|
695
|
-
def _get_metrics(self, response_usage: Union[Usage, MessageDeltaUsage]) -> Metrics:
|
|
731
|
+
def _get_metrics(self, response_usage: Union[Usage, MessageDeltaUsage, BetaUsage]) -> Metrics:
|
|
696
732
|
"""
|
|
697
733
|
Parse the given Anthropic-specific usage into an Agno Metrics object.
|
|
698
734
|
|