agno 2.4.6__py3-none-any.whl → 2.4.8__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 +5 -1
- agno/db/base.py +2 -0
- agno/db/postgres/postgres.py +5 -5
- agno/db/singlestore/singlestore.py +4 -5
- agno/db/sqlite/sqlite.py +4 -4
- agno/knowledge/embedder/aws_bedrock.py +325 -106
- agno/knowledge/knowledge.py +83 -1853
- agno/knowledge/loaders/__init__.py +29 -0
- agno/knowledge/loaders/azure_blob.py +423 -0
- agno/knowledge/loaders/base.py +187 -0
- agno/knowledge/loaders/gcs.py +267 -0
- agno/knowledge/loaders/github.py +415 -0
- agno/knowledge/loaders/s3.py +281 -0
- agno/knowledge/loaders/sharepoint.py +439 -0
- agno/knowledge/reader/website_reader.py +2 -2
- agno/knowledge/remote_knowledge.py +151 -0
- agno/knowledge/reranker/aws_bedrock.py +299 -0
- agno/learn/machine.py +5 -6
- agno/learn/stores/session_context.py +10 -2
- agno/models/azure/openai_chat.py +6 -11
- agno/models/neosantara/__init__.py +5 -0
- agno/models/neosantara/neosantara.py +42 -0
- agno/models/utils.py +5 -0
- agno/os/app.py +4 -1
- agno/os/interfaces/agui/router.py +1 -1
- agno/os/routers/components/components.py +2 -0
- agno/os/routers/knowledge/knowledge.py +0 -1
- agno/os/routers/registry/registry.py +340 -192
- agno/os/routers/workflows/router.py +7 -1
- agno/os/schema.py +104 -0
- agno/registry/registry.py +4 -0
- agno/run/workflow.py +3 -0
- agno/session/workflow.py +1 -1
- agno/skills/utils.py +100 -2
- agno/team/team.py +6 -3
- agno/tools/mcp/mcp.py +26 -1
- agno/vectordb/lancedb/lance_db.py +22 -7
- agno/workflow/__init__.py +4 -0
- agno/workflow/cel.py +299 -0
- agno/workflow/condition.py +280 -58
- agno/workflow/loop.py +177 -46
- agno/workflow/parallel.py +75 -4
- agno/workflow/router.py +260 -44
- agno/workflow/step.py +14 -7
- agno/workflow/steps.py +43 -0
- agno/workflow/workflow.py +104 -46
- {agno-2.4.6.dist-info → agno-2.4.8.dist-info}/METADATA +25 -37
- {agno-2.4.6.dist-info → agno-2.4.8.dist-info}/RECORD +51 -39
- {agno-2.4.6.dist-info → agno-2.4.8.dist-info}/WHEEL +0 -0
- {agno-2.4.6.dist-info → agno-2.4.8.dist-info}/licenses/LICENSE +0 -0
- {agno-2.4.6.dist-info → agno-2.4.8.dist-info}/top_level.txt +0 -0
|
@@ -555,7 +555,13 @@ def get_workflow_router(
|
|
|
555
555
|
db_workflows = get_workflows(db=os.db, registry=os.registry)
|
|
556
556
|
if db_workflows:
|
|
557
557
|
for db_workflow in db_workflows:
|
|
558
|
-
|
|
558
|
+
try:
|
|
559
|
+
workflows.append(WorkflowSummaryResponse.from_workflow(workflow=db_workflow))
|
|
560
|
+
except Exception as e:
|
|
561
|
+
workflow_id = getattr(db_workflow, "id", "unknown")
|
|
562
|
+
logger.error(f"Error converting workflow {workflow_id} to response: {e}")
|
|
563
|
+
# Continue processing other workflows even if this one fails
|
|
564
|
+
continue
|
|
559
565
|
|
|
560
566
|
return workflows
|
|
561
567
|
|
agno/os/schema.py
CHANGED
|
@@ -618,6 +618,7 @@ class ComponentUpdate(BaseModel):
|
|
|
618
618
|
description: Optional[str] = None
|
|
619
619
|
component_type: Optional[str] = None
|
|
620
620
|
metadata: Optional[Dict[str, Any]] = None
|
|
621
|
+
current_version: Optional[int] = None
|
|
621
622
|
|
|
622
623
|
|
|
623
624
|
class ConfigUpdate(BaseModel):
|
|
@@ -626,3 +627,106 @@ class ConfigUpdate(BaseModel):
|
|
|
626
627
|
stage: Optional[str] = None
|
|
627
628
|
notes: Optional[str] = None
|
|
628
629
|
links: Optional[List[Dict[str, Any]]] = None
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
class RegistryResourceType(str, Enum):
|
|
633
|
+
"""Types of resources that can be stored in a registry."""
|
|
634
|
+
|
|
635
|
+
TOOL = "tool"
|
|
636
|
+
MODEL = "model"
|
|
637
|
+
DB = "db"
|
|
638
|
+
VECTOR_DB = "vector_db"
|
|
639
|
+
SCHEMA = "schema"
|
|
640
|
+
FUNCTION = "function"
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
class CallableMetadata(BaseModel):
|
|
644
|
+
"""Common metadata for callable components (tools, functions)."""
|
|
645
|
+
|
|
646
|
+
name: str = Field(..., description="Callable name")
|
|
647
|
+
description: Optional[str] = Field(None, description="Callable description")
|
|
648
|
+
class_path: str = Field(..., description="Full module path to the class/function")
|
|
649
|
+
module: Optional[str] = Field(None, description="Module where the callable is defined")
|
|
650
|
+
qualname: Optional[str] = Field(None, description="Qualified name of the callable")
|
|
651
|
+
has_entrypoint: bool = Field(..., description="Whether the callable has an executable entrypoint")
|
|
652
|
+
parameters: Dict[str, Any] = Field(default_factory=dict, description="JSON schema of parameters")
|
|
653
|
+
requires_confirmation: Optional[bool] = Field(None, description="Whether execution requires user confirmation")
|
|
654
|
+
external_execution: Optional[bool] = Field(None, description="Whether execution happens externally")
|
|
655
|
+
signature: Optional[str] = Field(None, description="Function signature string")
|
|
656
|
+
return_annotation: Optional[str] = Field(None, description="Return type annotation")
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
class ToolMetadata(BaseModel):
|
|
660
|
+
"""Metadata for tool registry components."""
|
|
661
|
+
|
|
662
|
+
class_path: str = Field(..., description="Full module path to the tool class")
|
|
663
|
+
is_toolkit: bool = Field(False, description="Whether this is a toolkit containing multiple functions")
|
|
664
|
+
functions: Optional[List[CallableMetadata]] = Field(
|
|
665
|
+
None, description="Functions in the toolkit (if is_toolkit=True)"
|
|
666
|
+
)
|
|
667
|
+
|
|
668
|
+
# Fields for non-toolkit tools (Function or raw callable)
|
|
669
|
+
module: Optional[str] = Field(None, description="Module where the callable is defined")
|
|
670
|
+
qualname: Optional[str] = Field(None, description="Qualified name of the callable")
|
|
671
|
+
has_entrypoint: Optional[bool] = Field(None, description="Whether the tool has an executable entrypoint")
|
|
672
|
+
parameters: Optional[Dict[str, Any]] = Field(None, description="JSON schema of parameters")
|
|
673
|
+
requires_confirmation: Optional[bool] = Field(None, description="Whether execution requires user confirmation")
|
|
674
|
+
external_execution: Optional[bool] = Field(None, description="Whether execution happens externally")
|
|
675
|
+
signature: Optional[str] = Field(None, description="Function signature string")
|
|
676
|
+
return_annotation: Optional[str] = Field(None, description="Return type annotation")
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
class ModelMetadata(BaseModel):
|
|
680
|
+
"""Metadata for model registry components."""
|
|
681
|
+
|
|
682
|
+
class_path: str = Field(..., description="Full module path to the model class")
|
|
683
|
+
provider: Optional[str] = Field(None, description="Model provider (e.g., openai, anthropic)")
|
|
684
|
+
model_id: Optional[str] = Field(None, description="Model identifier")
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
class DbMetadata(BaseModel):
|
|
688
|
+
"""Metadata for database registry components."""
|
|
689
|
+
|
|
690
|
+
class_path: str = Field(..., description="Full module path to the database class")
|
|
691
|
+
db_id: Optional[str] = Field(None, description="Database identifier")
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
class VectorDbMetadata(BaseModel):
|
|
695
|
+
"""Metadata for vector database registry components."""
|
|
696
|
+
|
|
697
|
+
class_path: str = Field(..., description="Full module path to the vector database class")
|
|
698
|
+
vector_db_id: Optional[str] = Field(None, description="Vector database identifier")
|
|
699
|
+
collection: Optional[str] = Field(None, description="Collection name")
|
|
700
|
+
table_name: Optional[str] = Field(None, description="Table name (for SQL-based vector stores)")
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
class SchemaMetadata(BaseModel):
|
|
704
|
+
"""Metadata for schema registry components."""
|
|
705
|
+
|
|
706
|
+
class_path: str = Field(..., description="Full module path to the schema class")
|
|
707
|
+
schema_: Optional[Dict[str, Any]] = Field(None, alias="schema", description="JSON schema definition")
|
|
708
|
+
schema_error: Optional[str] = Field(None, description="Error message if schema generation failed")
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
class FunctionMetadata(CallableMetadata):
|
|
712
|
+
"""Metadata for function registry components (workflow conditions, selectors, etc.)."""
|
|
713
|
+
|
|
714
|
+
pass
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
# Union of all metadata types for type hints
|
|
718
|
+
RegistryMetadata = Union[
|
|
719
|
+
ToolMetadata,
|
|
720
|
+
ModelMetadata,
|
|
721
|
+
DbMetadata,
|
|
722
|
+
VectorDbMetadata,
|
|
723
|
+
SchemaMetadata,
|
|
724
|
+
FunctionMetadata,
|
|
725
|
+
]
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
class RegistryContentResponse(BaseModel):
|
|
729
|
+
name: str
|
|
730
|
+
type: RegistryResourceType
|
|
731
|
+
description: Optional[str] = None
|
|
732
|
+
metadata: Optional[Dict[str, Any]] = None
|
agno/registry/registry.py
CHANGED
|
@@ -26,6 +26,7 @@ class Registry:
|
|
|
26
26
|
dbs: List[BaseDb] = field(default_factory=list)
|
|
27
27
|
vector_dbs: List[VectorDb] = field(default_factory=list)
|
|
28
28
|
schemas: List[Type[BaseModel]] = field(default_factory=list)
|
|
29
|
+
functions: List[Callable] = field(default_factory=list)
|
|
29
30
|
|
|
30
31
|
@cached_property
|
|
31
32
|
def _entrypoint_lookup(self) -> Dict[str, Callable]:
|
|
@@ -66,3 +67,6 @@ class Registry:
|
|
|
66
67
|
if self.dbs:
|
|
67
68
|
return next((db for db in self.dbs if db.id == db_id), None)
|
|
68
69
|
return None
|
|
70
|
+
|
|
71
|
+
def get_function(self, name: str) -> Optional[Callable]:
|
|
72
|
+
return next((f for f in self.functions if f.__name__ == name), None)
|
agno/run/workflow.py
CHANGED
|
@@ -311,6 +311,9 @@ class ConditionExecutionCompletedEvent(BaseWorkflowRunOutputEvent):
|
|
|
311
311
|
condition_result: Optional[bool] = None
|
|
312
312
|
executed_steps: Optional[int] = None
|
|
313
313
|
|
|
314
|
+
# Which branch was executed: "if", "else", or None (condition false with no else_steps)
|
|
315
|
+
branch: Optional[str] = None
|
|
316
|
+
|
|
314
317
|
# Results from executed steps
|
|
315
318
|
step_results: List[StepOutput] = field(default_factory=list)
|
|
316
319
|
|
agno/session/workflow.py
CHANGED
|
@@ -16,7 +16,7 @@ from agno.utils.log import log_debug, logger
|
|
|
16
16
|
|
|
17
17
|
@dataclass
|
|
18
18
|
class WorkflowSession:
|
|
19
|
-
"""Workflow Session
|
|
19
|
+
"""Workflow Session for pipeline-based workflows"""
|
|
20
20
|
|
|
21
21
|
# Session UUID - this is the workflow_session_id that gets set on agents/teams
|
|
22
22
|
session_id: str
|
agno/skills/utils.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"""Utility functions for the skills module."""
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
import platform
|
|
4
5
|
import stat
|
|
5
6
|
import subprocess
|
|
7
|
+
import sys
|
|
6
8
|
from dataclasses import dataclass
|
|
7
9
|
from pathlib import Path
|
|
8
10
|
from typing import List, Optional
|
|
@@ -41,6 +43,95 @@ def ensure_executable(file_path: Path) -> None:
|
|
|
41
43
|
os.chmod(file_path, current_mode | stat.S_IXUSR)
|
|
42
44
|
|
|
43
45
|
|
|
46
|
+
def parse_shebang(script_path: Path) -> Optional[str]:
|
|
47
|
+
"""Parse the shebang line from a script file to determine the interpreter.
|
|
48
|
+
|
|
49
|
+
Handles various shebang formats:
|
|
50
|
+
- #!/usr/bin/env python3 -> "python3"
|
|
51
|
+
- #!/usr/bin/python3 -> "python3"
|
|
52
|
+
- #!/bin/bash -> "bash"
|
|
53
|
+
- #!/usr/bin/env -S node -> "node"
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
script_path: Path to the script file.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
The interpreter name (e.g., "python3", "bash") or None if no valid shebang.
|
|
60
|
+
"""
|
|
61
|
+
try:
|
|
62
|
+
with open(script_path, "r", encoding="utf-8") as f:
|
|
63
|
+
first_line = f.readline().strip()
|
|
64
|
+
except (OSError, UnicodeDecodeError):
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
if not first_line.startswith("#!"):
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
shebang = first_line[2:].strip()
|
|
71
|
+
if not shebang:
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
parts = shebang.split()
|
|
75
|
+
|
|
76
|
+
# Handle /usr/bin/env style shebangs
|
|
77
|
+
if Path(parts[0]).name == "env":
|
|
78
|
+
# Skip any flags (like -S) and get the interpreter
|
|
79
|
+
for part in parts[1:]:
|
|
80
|
+
if not part.startswith("-"):
|
|
81
|
+
return part
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
# Handle direct path shebangs like #!/bin/bash or #!/usr/bin/python3
|
|
85
|
+
# Extract the basename of the path
|
|
86
|
+
interpreter_path = parts[0]
|
|
87
|
+
return Path(interpreter_path).name
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_interpreter_command(interpreter: str) -> List[str]:
|
|
91
|
+
"""Map an interpreter name to a Windows-compatible command.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
interpreter: The interpreter name from shebang (e.g., "python3", "bash").
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
A list representing the command to invoke the interpreter.
|
|
98
|
+
"""
|
|
99
|
+
# Normalize interpreter name
|
|
100
|
+
interpreter_lower = interpreter.lower()
|
|
101
|
+
|
|
102
|
+
# Python interpreters - use current Python executable
|
|
103
|
+
if interpreter_lower in ("python", "python3", "python2"):
|
|
104
|
+
return [sys.executable]
|
|
105
|
+
|
|
106
|
+
# Other interpreters - pass through as-is
|
|
107
|
+
# This includes: bash, sh, node, ruby, perl, etc.
|
|
108
|
+
# These need to be available in PATH on Windows
|
|
109
|
+
return [interpreter]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _build_windows_command(script_path: Path, args: List[str]) -> List[str]:
|
|
113
|
+
"""Build the command list for executing a script on Windows.
|
|
114
|
+
|
|
115
|
+
On Windows, shebang lines are not processed by the OS, so we need to
|
|
116
|
+
parse the shebang and explicitly invoke the interpreter.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
script_path: Path to the script file.
|
|
120
|
+
args: Arguments to pass to the script.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
A list representing the full command to execute.
|
|
124
|
+
"""
|
|
125
|
+
interpreter = parse_shebang(script_path)
|
|
126
|
+
|
|
127
|
+
if interpreter:
|
|
128
|
+
cmd_prefix = get_interpreter_command(interpreter)
|
|
129
|
+
return [*cmd_prefix, str(script_path), *args]
|
|
130
|
+
|
|
131
|
+
# Fallback: try direct execution (may fail, but provides clear error)
|
|
132
|
+
return [str(script_path), *args]
|
|
133
|
+
|
|
134
|
+
|
|
44
135
|
@dataclass
|
|
45
136
|
class ScriptResult:
|
|
46
137
|
"""Result of a script execution."""
|
|
@@ -58,6 +149,10 @@ def run_script(
|
|
|
58
149
|
) -> ScriptResult:
|
|
59
150
|
"""Execute a script and return the result.
|
|
60
151
|
|
|
152
|
+
On Unix-like systems, scripts are executed directly using their shebang.
|
|
153
|
+
On Windows, the shebang is parsed to determine the interpreter since
|
|
154
|
+
Windows does not natively support shebang lines.
|
|
155
|
+
|
|
61
156
|
Args:
|
|
62
157
|
script_path: Path to the script to execute.
|
|
63
158
|
args: Optional list of arguments to pass to the script.
|
|
@@ -71,8 +166,11 @@ def run_script(
|
|
|
71
166
|
subprocess.TimeoutExpired: If script exceeds timeout.
|
|
72
167
|
FileNotFoundError: If script or interpreter not found.
|
|
73
168
|
"""
|
|
74
|
-
|
|
75
|
-
|
|
169
|
+
if platform.system() == "Windows":
|
|
170
|
+
cmd = _build_windows_command(script_path, args or [])
|
|
171
|
+
else:
|
|
172
|
+
ensure_executable(script_path)
|
|
173
|
+
cmd = [str(script_path), *(args or [])]
|
|
76
174
|
|
|
77
175
|
result = subprocess.run(
|
|
78
176
|
cmd,
|
agno/team/team.py
CHANGED
|
@@ -7094,7 +7094,7 @@ class Team:
|
|
|
7094
7094
|
get_chat_history_func = get_chat_history # type: ignore
|
|
7095
7095
|
return Function.from_callable(get_chat_history_func, name="get_chat_history")
|
|
7096
7096
|
|
|
7097
|
-
def _update_session_state_tool(self,
|
|
7097
|
+
def _update_session_state_tool(self, run_context: RunContext, session_state_updates: dict) -> str:
|
|
7098
7098
|
"""
|
|
7099
7099
|
Update the shared session state. Provide any updates as a dictionary of key-value pairs.
|
|
7100
7100
|
Example:
|
|
@@ -7103,6 +7103,9 @@ class Team:
|
|
|
7103
7103
|
Args:
|
|
7104
7104
|
session_state_updates (dict): The updates to apply to the shared session state. Should be a dictionary of key-value pairs.
|
|
7105
7105
|
"""
|
|
7106
|
+
if run_context.session_state is None:
|
|
7107
|
+
run_context.session_state = {}
|
|
7108
|
+
session_state = run_context.session_state
|
|
7106
7109
|
for key, value in session_state_updates.items():
|
|
7107
7110
|
session_state[key] = value
|
|
7108
7111
|
|
|
@@ -8731,8 +8734,8 @@ class Team:
|
|
|
8731
8734
|
tool_choice=config.get("tool_choice"),
|
|
8732
8735
|
get_member_information_tool=config.get("get_member_information_tool", False),
|
|
8733
8736
|
# --- Schema settings ---
|
|
8734
|
-
|
|
8735
|
-
|
|
8737
|
+
input_schema=config.get("input_schema"),
|
|
8738
|
+
output_schema=config.get("output_schema"),
|
|
8736
8739
|
# --- Parser and output settings ---
|
|
8737
8740
|
# parser_model=config.get("parser_model"), # TODO
|
|
8738
8741
|
parser_model_prompt=config.get("parser_model_prompt"),
|
agno/tools/mcp/mcp.py
CHANGED
|
@@ -74,6 +74,13 @@ class MCPTools(Toolkit):
|
|
|
74
74
|
Only relevant with HTTP transports (Streamable HTTP or SSE).
|
|
75
75
|
Creates a new session per agent run with dynamic headers merged into connection config.
|
|
76
76
|
"""
|
|
77
|
+
# Extract these before super().__init__() to bypass early validation
|
|
78
|
+
# (tools aren't available until build_tools() is called)
|
|
79
|
+
requires_confirmation_tools = kwargs.pop("requires_confirmation_tools", None)
|
|
80
|
+
external_execution_required_tools = kwargs.pop("external_execution_required_tools", None)
|
|
81
|
+
stop_after_tool_call_tools = kwargs.pop("stop_after_tool_call_tools", None)
|
|
82
|
+
show_result_tools = kwargs.pop("show_result_tools", None)
|
|
83
|
+
|
|
77
84
|
super().__init__(name="MCPTools", **kwargs)
|
|
78
85
|
|
|
79
86
|
if url is not None:
|
|
@@ -92,6 +99,10 @@ class MCPTools(Toolkit):
|
|
|
92
99
|
# because tools are not available until `initialize()` is called.
|
|
93
100
|
self.include_tools = include_tools
|
|
94
101
|
self.exclude_tools = exclude_tools
|
|
102
|
+
self.requires_confirmation_tools = requires_confirmation_tools or []
|
|
103
|
+
self.external_execution_required_tools = external_execution_required_tools or []
|
|
104
|
+
self.stop_after_tool_call_tools = stop_after_tool_call_tools or []
|
|
105
|
+
self.show_result_tools = show_result_tools or []
|
|
95
106
|
self.refresh_connection = refresh_connection
|
|
96
107
|
self.tool_name_prefix = tool_name_prefix
|
|
97
108
|
|
|
@@ -575,13 +586,27 @@ class MCPTools(Toolkit):
|
|
|
575
586
|
mcp_tools_instance=self,
|
|
576
587
|
)
|
|
577
588
|
# Create a Function for the tool
|
|
589
|
+
# Apply toolkit-level settings
|
|
590
|
+
tool_name = tool.name
|
|
591
|
+
stop_after = tool_name in self.stop_after_tool_call_tools
|
|
592
|
+
show_result = tool_name in self.show_result_tools or stop_after
|
|
593
|
+
|
|
578
594
|
f = Function(
|
|
579
|
-
name=tool_name_prefix +
|
|
595
|
+
name=tool_name_prefix + tool_name,
|
|
580
596
|
description=tool.description,
|
|
581
597
|
parameters=tool.inputSchema,
|
|
582
598
|
entrypoint=entrypoint,
|
|
583
599
|
# Set skip_entrypoint_processing to True to avoid processing the entrypoint
|
|
584
600
|
skip_entrypoint_processing=True,
|
|
601
|
+
# Apply toolkit-level settings for HITL and control flow
|
|
602
|
+
requires_confirmation=tool_name in self.requires_confirmation_tools,
|
|
603
|
+
external_execution=tool_name in self.external_execution_required_tools,
|
|
604
|
+
stop_after_tool_call=stop_after,
|
|
605
|
+
show_result=show_result,
|
|
606
|
+
# Apply toolkit-level cache settings
|
|
607
|
+
cache_results=self.cache_results,
|
|
608
|
+
cache_dir=self.cache_dir,
|
|
609
|
+
cache_ttl=self.cache_ttl,
|
|
585
610
|
)
|
|
586
611
|
|
|
587
612
|
# Register the Function with the toolkit
|
|
@@ -104,7 +104,7 @@ class LanceDb(VectorDb):
|
|
|
104
104
|
self.async_connection: Optional[lancedb.AsyncConnection] = async_connection
|
|
105
105
|
self.async_table: Optional[lancedb.db.AsyncTable] = async_table
|
|
106
106
|
|
|
107
|
-
if table_name and table_name in self.connection
|
|
107
|
+
if table_name and table_name in self._get_table_names(self.connection):
|
|
108
108
|
# Open the table if it exists
|
|
109
109
|
try:
|
|
110
110
|
self.table = self.connection.open_table(name=table_name)
|
|
@@ -157,6 +157,21 @@ class LanceDb(VectorDb):
|
|
|
157
157
|
|
|
158
158
|
log_debug(f"Initialized LanceDb with table: '{self.table_name}'")
|
|
159
159
|
|
|
160
|
+
def _get_table_names(self, conn: lancedb.DBConnection) -> List[str]:
|
|
161
|
+
"""Get table names with backward compatibility for older LanceDB versions."""
|
|
162
|
+
# Prefer list_tables over table_names (deprecated in new versions)
|
|
163
|
+
if hasattr(conn, "list_tables"):
|
|
164
|
+
return conn.list_tables().tables
|
|
165
|
+
return conn.table_names()
|
|
166
|
+
|
|
167
|
+
async def _get_async_table_names(self, conn: lancedb.AsyncConnection) -> List[str]:
|
|
168
|
+
"""Get table names asynchronously with backward compatibility for older LanceDB versions."""
|
|
169
|
+
# Prefer list_tables over table_names (deprecated in new versions)
|
|
170
|
+
if hasattr(conn, "list_tables"):
|
|
171
|
+
table_list = await conn.list_tables()
|
|
172
|
+
return table_list.tables
|
|
173
|
+
return await conn.table_names()
|
|
174
|
+
|
|
160
175
|
def _prepare_vector(self, embedding) -> List[float]:
|
|
161
176
|
"""Prepare vector embedding for insertion, ensuring correct dimensions and type."""
|
|
162
177
|
if embedding is not None and len(embedding) > 0:
|
|
@@ -186,7 +201,7 @@ class LanceDb(VectorDb):
|
|
|
186
201
|
self.async_connection = await lancedb.connect_async(self.uri)
|
|
187
202
|
# Only try to open table if it exists and we don't have it already
|
|
188
203
|
if self.async_table is None:
|
|
189
|
-
table_names = await self.async_connection
|
|
204
|
+
table_names = await self._get_async_table_names(self.async_connection)
|
|
190
205
|
if self.table_name in table_names:
|
|
191
206
|
try:
|
|
192
207
|
self.async_table = await self.async_connection.open_table(self.table_name)
|
|
@@ -199,7 +214,7 @@ class LanceDb(VectorDb):
|
|
|
199
214
|
"""Refresh the sync connection to see changes made by async operations."""
|
|
200
215
|
try:
|
|
201
216
|
# Re-establish sync connection to see async changes
|
|
202
|
-
if self.connection and self.table_name in self.connection
|
|
217
|
+
if self.connection is not None and self.table_name in self._get_table_names(self.connection):
|
|
203
218
|
self.table = self.connection.open_table(self.table_name)
|
|
204
219
|
except Exception as e:
|
|
205
220
|
log_debug(f"Could not refresh sync connection: {e}")
|
|
@@ -459,7 +474,7 @@ class LanceDb(VectorDb):
|
|
|
459
474
|
Returns:
|
|
460
475
|
List[Document]: List of matching documents
|
|
461
476
|
"""
|
|
462
|
-
if self.connection:
|
|
477
|
+
if self.connection is not None:
|
|
463
478
|
self.table = self.connection.open_table(name=self.table_name)
|
|
464
479
|
|
|
465
480
|
results = None
|
|
@@ -641,8 +656,8 @@ class LanceDb(VectorDb):
|
|
|
641
656
|
# If we have an async table that was created, the table exists
|
|
642
657
|
if self.async_table is not None:
|
|
643
658
|
return True
|
|
644
|
-
if self.connection:
|
|
645
|
-
return self.table_name in self.connection
|
|
659
|
+
if self.connection is not None:
|
|
660
|
+
return self.table_name in self._get_table_names(self.connection)
|
|
646
661
|
return False
|
|
647
662
|
|
|
648
663
|
async def async_exists(self) -> bool:
|
|
@@ -653,7 +668,7 @@ class LanceDb(VectorDb):
|
|
|
653
668
|
# Check if table exists in database without trying to open it
|
|
654
669
|
if self.async_connection is None:
|
|
655
670
|
self.async_connection = await lancedb.connect_async(self.uri)
|
|
656
|
-
table_names = await self.async_connection
|
|
671
|
+
table_names = await self._get_async_table_names(self.async_connection)
|
|
657
672
|
return self.table_name in table_names
|
|
658
673
|
|
|
659
674
|
async def async_get_count(self) -> int:
|
agno/workflow/__init__.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from agno.workflow.agent import WorkflowAgent
|
|
2
|
+
from agno.workflow.cel import CEL_AVAILABLE, validate_cel_expression
|
|
2
3
|
from agno.workflow.condition import Condition
|
|
3
4
|
from agno.workflow.loop import Loop
|
|
4
5
|
from agno.workflow.parallel import Parallel
|
|
@@ -24,4 +25,7 @@ __all__ = [
|
|
|
24
25
|
"StepOutput",
|
|
25
26
|
"get_workflow_by_id",
|
|
26
27
|
"get_workflows",
|
|
28
|
+
# CEL utilities
|
|
29
|
+
"CEL_AVAILABLE",
|
|
30
|
+
"validate_cel_expression",
|
|
27
31
|
]
|