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
|
@@ -1,18 +1,27 @@
|
|
|
1
|
+
import inspect
|
|
1
2
|
import time
|
|
2
|
-
from typing import Any, Dict, List,
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
3
4
|
|
|
4
5
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
5
|
-
from pydantic import BaseModel, Field
|
|
6
6
|
|
|
7
7
|
from agno.os.auth import get_authentication_dependency
|
|
8
8
|
from agno.os.schema import (
|
|
9
9
|
BadRequestResponse,
|
|
10
|
+
CallableMetadata,
|
|
11
|
+
DbMetadata,
|
|
12
|
+
FunctionMetadata,
|
|
10
13
|
InternalServerErrorResponse,
|
|
14
|
+
ModelMetadata,
|
|
11
15
|
NotFoundResponse,
|
|
12
16
|
PaginatedResponse,
|
|
13
17
|
PaginationInfo,
|
|
18
|
+
RegistryContentResponse,
|
|
19
|
+
RegistryResourceType,
|
|
20
|
+
SchemaMetadata,
|
|
21
|
+
ToolMetadata,
|
|
14
22
|
UnauthenticatedResponse,
|
|
15
23
|
ValidationErrorResponse,
|
|
24
|
+
VectorDbMetadata,
|
|
16
25
|
)
|
|
17
26
|
from agno.os.settings import AgnoAPISettings
|
|
18
27
|
from agno.registry import Registry
|
|
@@ -20,29 +29,6 @@ from agno.tools.function import Function
|
|
|
20
29
|
from agno.tools.toolkit import Toolkit
|
|
21
30
|
from agno.utils.log import log_error
|
|
22
31
|
|
|
23
|
-
ComponentType = Literal["tool", "toolkit", "model", "db", "vector_db", "schema"]
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
# ============================================
|
|
27
|
-
# Response Schema
|
|
28
|
-
# ============================================
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class ComponentResponse(BaseModel):
|
|
32
|
-
name: str
|
|
33
|
-
component_type: ComponentType
|
|
34
|
-
description: Optional[str] = None
|
|
35
|
-
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
36
|
-
# Tool-specific fields (matching config format)
|
|
37
|
-
parameters: Optional[Dict[str, Any]] = None
|
|
38
|
-
requires_confirmation: Optional[bool] = None
|
|
39
|
-
external_execution: Optional[bool] = None
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# ============================================
|
|
43
|
-
# Router
|
|
44
|
-
# ============================================
|
|
45
|
-
|
|
46
32
|
|
|
47
33
|
def get_registry_router(registry: Registry, settings: AgnoAPISettings = AgnoAPISettings()) -> APIRouter:
|
|
48
34
|
router = APIRouter(
|
|
@@ -78,8 +64,7 @@ def attach_routes(router: APIRouter, registry: Registry) -> APIRouter:
|
|
|
78
64
|
return f"{cls.__module__}.{cls.__name__}"
|
|
79
65
|
|
|
80
66
|
def _maybe_jsonable(value: Any) -> Any:
|
|
81
|
-
#
|
|
82
|
-
# If your Function.parameters is a Pydantic model or custom object, this avoids 500s.
|
|
67
|
+
# Keep only data that is likely JSON serializable
|
|
83
68
|
if value is None:
|
|
84
69
|
return None
|
|
85
70
|
if isinstance(value, (str, int, float, bool)):
|
|
@@ -94,231 +79,394 @@ def attach_routes(router: APIRouter, registry: Registry) -> APIRouter:
|
|
|
94
79
|
# Fallback to string to avoid serialization errors
|
|
95
80
|
return str(value)
|
|
96
81
|
|
|
97
|
-
def
|
|
98
|
-
|
|
82
|
+
def _extract_entrypoint_metadata(
|
|
83
|
+
entrypoint: Any,
|
|
84
|
+
) -> tuple[Optional[str], Optional[str], Optional[str], Optional[str]]:
|
|
85
|
+
"""Extract module, qualname, signature, and return annotation from an entrypoint callable."""
|
|
86
|
+
ep_module: Optional[str] = getattr(entrypoint, "__module__", None)
|
|
87
|
+
ep_qualname: Optional[str] = getattr(entrypoint, "__qualname__", None)
|
|
88
|
+
ep_signature: Optional[str] = None
|
|
89
|
+
ep_return_annotation: Optional[str] = None
|
|
90
|
+
try:
|
|
91
|
+
sig = inspect.signature(entrypoint)
|
|
92
|
+
ep_signature = str(sig)
|
|
93
|
+
if sig.return_annotation is not inspect.Signature.empty:
|
|
94
|
+
ep_return_annotation = str(sig.return_annotation)
|
|
95
|
+
except (ValueError, TypeError):
|
|
96
|
+
pass
|
|
97
|
+
return ep_module, ep_qualname, ep_signature, ep_return_annotation
|
|
98
|
+
|
|
99
|
+
def _get_callable_params(func: Any) -> Dict[str, Any]:
|
|
100
|
+
"""Extract JSON schema-like parameters from a callable using inspect."""
|
|
101
|
+
try:
|
|
102
|
+
sig = inspect.signature(func)
|
|
103
|
+
properties: Dict[str, Any] = {}
|
|
104
|
+
required: List[str] = []
|
|
105
|
+
|
|
106
|
+
for param_name, param in sig.parameters.items():
|
|
107
|
+
if param_name in ("self", "cls"):
|
|
108
|
+
continue
|
|
109
|
+
|
|
110
|
+
prop: Dict[str, Any] = {}
|
|
111
|
+
|
|
112
|
+
# Try to map annotation to JSON schema type
|
|
113
|
+
if param.annotation is not inspect.Parameter.empty:
|
|
114
|
+
ann = param.annotation
|
|
115
|
+
if ann is str or ann == "str":
|
|
116
|
+
prop["type"] = "string"
|
|
117
|
+
elif ann is int or ann == "int":
|
|
118
|
+
prop["type"] = "integer"
|
|
119
|
+
elif ann is float or ann == "float":
|
|
120
|
+
prop["type"] = "number"
|
|
121
|
+
elif ann is bool or ann == "bool":
|
|
122
|
+
prop["type"] = "boolean"
|
|
123
|
+
elif ann is list or ann == "list":
|
|
124
|
+
prop["type"] = "array"
|
|
125
|
+
elif ann is dict or ann == "dict":
|
|
126
|
+
prop["type"] = "object"
|
|
127
|
+
else:
|
|
128
|
+
prop["type"] = "string"
|
|
129
|
+
prop["annotation"] = str(ann)
|
|
130
|
+
else:
|
|
131
|
+
prop["type"] = "string"
|
|
132
|
+
|
|
133
|
+
if param.default is not inspect.Parameter.empty:
|
|
134
|
+
prop["default"] = (
|
|
135
|
+
param.default if _maybe_jsonable(param.default) == param.default else str(param.default)
|
|
136
|
+
)
|
|
137
|
+
else:
|
|
138
|
+
required.append(param_name)
|
|
139
|
+
|
|
140
|
+
properties[param_name] = prop
|
|
141
|
+
|
|
142
|
+
return {"type": "object", "properties": properties, "required": required}
|
|
143
|
+
except (ValueError, TypeError):
|
|
144
|
+
return {"type": "object", "properties": {}, "required": []}
|
|
145
|
+
|
|
146
|
+
def _get_resources(resource_type: Optional[RegistryResourceType] = None) -> List[RegistryContentResponse]:
|
|
147
|
+
resources: List[RegistryContentResponse] = []
|
|
99
148
|
|
|
100
149
|
# Tools
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
150
|
+
if resource_type is None or resource_type == RegistryResourceType.TOOL:
|
|
151
|
+
for tool in getattr(registry, "tools", []) or []:
|
|
152
|
+
if isinstance(tool, Toolkit):
|
|
153
|
+
toolkit_name = _safe_name(tool, fallback=tool.__class__.__name__)
|
|
154
|
+
functions = getattr(tool, "functions", {}) or {}
|
|
155
|
+
|
|
156
|
+
# Build full function details for each function in the toolkit
|
|
157
|
+
function_details: List[CallableMetadata] = []
|
|
158
|
+
for func in functions.values():
|
|
159
|
+
func_name = _safe_name(func, fallback=func.__class__.__name__)
|
|
160
|
+
# Check if function requires confirmation or external execution
|
|
161
|
+
requires_confirmation = getattr(func, "requires_confirmation", None)
|
|
162
|
+
external_execution = getattr(func, "external_execution", None)
|
|
163
|
+
|
|
164
|
+
# If not set on function, check toolkit settings
|
|
165
|
+
if requires_confirmation is None and hasattr(tool, "requires_confirmation_tools"):
|
|
166
|
+
requires_confirmation = func_name in (tool.requires_confirmation_tools or [])
|
|
167
|
+
if external_execution is None and hasattr(tool, "external_execution_required_tools"):
|
|
168
|
+
external_execution = func_name in (tool.external_execution_required_tools or [])
|
|
169
|
+
|
|
170
|
+
# Get parameters - ensure they're processed if needed
|
|
171
|
+
func_params = func.parameters
|
|
172
|
+
default_params = {"type": "object", "properties": {}, "required": []}
|
|
173
|
+
if func_params == default_params and func.entrypoint and not func.skip_entrypoint_processing:
|
|
174
|
+
try:
|
|
175
|
+
func_copy = func.model_copy(deep=False)
|
|
176
|
+
func_copy.process_entrypoint(strict=False)
|
|
177
|
+
func_params = func_copy.parameters
|
|
178
|
+
except Exception:
|
|
179
|
+
pass
|
|
180
|
+
|
|
181
|
+
# Extract callable metadata from entrypoint
|
|
182
|
+
func_module: Optional[str] = None
|
|
183
|
+
func_qualname: Optional[str] = None
|
|
184
|
+
func_signature: Optional[str] = None
|
|
185
|
+
func_return_annotation: Optional[str] = None
|
|
186
|
+
if func.entrypoint:
|
|
187
|
+
func_module, func_qualname, func_signature, func_return_annotation = (
|
|
188
|
+
_extract_entrypoint_metadata(func.entrypoint)
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
func_description = getattr(func, "description", None)
|
|
192
|
+
if func_description is None and func.entrypoint:
|
|
193
|
+
func_description = inspect.getdoc(func.entrypoint)
|
|
194
|
+
|
|
195
|
+
function_details.append(
|
|
196
|
+
CallableMetadata(
|
|
197
|
+
name=func_name,
|
|
198
|
+
description=_safe_str(func_description),
|
|
199
|
+
class_path=_class_path(func),
|
|
200
|
+
module=func_module,
|
|
201
|
+
qualname=func_qualname,
|
|
202
|
+
has_entrypoint=bool(getattr(func, "entrypoint", None)),
|
|
203
|
+
parameters=_maybe_jsonable(func_params),
|
|
204
|
+
requires_confirmation=requires_confirmation,
|
|
205
|
+
external_execution=external_execution,
|
|
206
|
+
signature=func_signature,
|
|
207
|
+
return_annotation=func_return_annotation,
|
|
208
|
+
)
|
|
209
|
+
)
|
|
117
210
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
211
|
+
toolkit_metadata = ToolMetadata(
|
|
212
|
+
class_path=_class_path(tool),
|
|
213
|
+
is_toolkit=True,
|
|
214
|
+
functions=function_details,
|
|
215
|
+
)
|
|
216
|
+
resources.append(
|
|
217
|
+
RegistryContentResponse(
|
|
218
|
+
name=toolkit_name,
|
|
219
|
+
type=RegistryResourceType.TOOL,
|
|
220
|
+
description=_safe_str(getattr(tool, "description", None)),
|
|
221
|
+
metadata=toolkit_metadata.model_dump(exclude_none=True),
|
|
222
|
+
)
|
|
223
|
+
)
|
|
125
224
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
external_execution = func_name in (tool.external_execution_required_tools or [])
|
|
225
|
+
elif isinstance(tool, Function):
|
|
226
|
+
func_name = _safe_name(tool, fallback=tool.__class__.__name__)
|
|
227
|
+
requires_confirmation = getattr(tool, "requires_confirmation", None)
|
|
228
|
+
external_execution = getattr(tool, "external_execution", None)
|
|
131
229
|
|
|
132
230
|
# Get parameters - ensure they're processed if needed
|
|
133
|
-
func_params =
|
|
231
|
+
func_params = tool.parameters
|
|
134
232
|
# If parameters are empty/default and function has entrypoint, try to process it
|
|
135
233
|
default_params = {"type": "object", "properties": {}, "required": []}
|
|
136
|
-
if func_params == default_params and
|
|
234
|
+
if func_params == default_params and tool.entrypoint and not tool.skip_entrypoint_processing:
|
|
137
235
|
try:
|
|
138
236
|
# Create a copy to avoid modifying the original
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
func_params =
|
|
237
|
+
tool_copy = tool.model_copy(deep=False)
|
|
238
|
+
tool_copy.process_entrypoint(strict=False)
|
|
239
|
+
func_params = tool_copy.parameters
|
|
142
240
|
except Exception:
|
|
143
241
|
# If processing fails, use original parameters
|
|
144
242
|
pass
|
|
145
243
|
|
|
146
|
-
|
|
147
|
-
|
|
244
|
+
# Extract callable metadata from entrypoint
|
|
245
|
+
tool_module: Optional[str] = None
|
|
246
|
+
tool_qualname: Optional[str] = None
|
|
247
|
+
tool_signature: Optional[str] = None
|
|
248
|
+
tool_return_annotation: Optional[str] = None
|
|
249
|
+
if tool.entrypoint:
|
|
250
|
+
tool_module, tool_qualname, tool_signature, tool_return_annotation = (
|
|
251
|
+
_extract_entrypoint_metadata(tool.entrypoint)
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
func_tool_metadata = ToolMetadata(
|
|
255
|
+
class_path=_class_path(tool),
|
|
256
|
+
module=tool_module,
|
|
257
|
+
qualname=tool_qualname,
|
|
258
|
+
has_entrypoint=bool(getattr(tool, "entrypoint", None)),
|
|
259
|
+
parameters=_maybe_jsonable(func_params),
|
|
260
|
+
requires_confirmation=requires_confirmation,
|
|
261
|
+
external_execution=external_execution,
|
|
262
|
+
signature=tool_signature,
|
|
263
|
+
return_annotation=tool_return_annotation,
|
|
264
|
+
)
|
|
265
|
+
resources.append(
|
|
266
|
+
RegistryContentResponse(
|
|
148
267
|
name=func_name,
|
|
149
|
-
|
|
150
|
-
description=_safe_str(getattr(
|
|
151
|
-
|
|
152
|
-
requires_confirmation=requires_confirmation,
|
|
153
|
-
external_execution=external_execution,
|
|
154
|
-
metadata={
|
|
155
|
-
"class_path": _class_path(func),
|
|
156
|
-
"toolkit": toolkit_name,
|
|
157
|
-
"has_entrypoint": bool(getattr(func, "entrypoint", None)),
|
|
158
|
-
},
|
|
268
|
+
type=RegistryResourceType.TOOL,
|
|
269
|
+
description=_safe_str(getattr(tool, "description", None)),
|
|
270
|
+
metadata=func_tool_metadata.model_dump(exclude_none=True),
|
|
159
271
|
)
|
|
160
272
|
)
|
|
161
273
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
external_execution = getattr(tool, "external_execution", None)
|
|
274
|
+
elif callable(tool):
|
|
275
|
+
call_name = getattr(tool, "__name__", None) or tool.__class__.__name__
|
|
276
|
+
tool_module = getattr(tool, "__module__", "unknown")
|
|
166
277
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
default_params = {"type": "object", "properties": {}, "required": []}
|
|
171
|
-
if func_params == default_params and tool.entrypoint and not tool.skip_entrypoint_processing:
|
|
278
|
+
# Extract signature
|
|
279
|
+
callable_signature: Optional[str] = None
|
|
280
|
+
callable_return_annotation: Optional[str] = None
|
|
172
281
|
try:
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
except
|
|
178
|
-
# If processing fails, use original parameters
|
|
282
|
+
sig = inspect.signature(tool)
|
|
283
|
+
callable_signature = str(sig)
|
|
284
|
+
if sig.return_annotation is not inspect.Signature.empty:
|
|
285
|
+
callable_return_annotation = str(sig.return_annotation)
|
|
286
|
+
except (ValueError, TypeError):
|
|
179
287
|
pass
|
|
180
288
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
parameters=
|
|
187
|
-
requires_confirmation=
|
|
188
|
-
external_execution=
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
"has_entrypoint": bool(getattr(tool, "entrypoint", None)),
|
|
192
|
-
},
|
|
289
|
+
callable_metadata = ToolMetadata(
|
|
290
|
+
class_path=f"{tool_module}.{call_name}",
|
|
291
|
+
module=tool_module,
|
|
292
|
+
qualname=getattr(tool, "__qualname__", None),
|
|
293
|
+
has_entrypoint=True,
|
|
294
|
+
parameters=_get_callable_params(tool),
|
|
295
|
+
requires_confirmation=None,
|
|
296
|
+
external_execution=None,
|
|
297
|
+
signature=callable_signature,
|
|
298
|
+
return_annotation=callable_return_annotation,
|
|
193
299
|
)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
component_type="tool",
|
|
202
|
-
description=_safe_str(getattr(tool, "__doc__", None)),
|
|
203
|
-
metadata={"class_path": _class_path(tool)},
|
|
300
|
+
resources.append(
|
|
301
|
+
RegistryContentResponse(
|
|
302
|
+
name=str(call_name),
|
|
303
|
+
type=RegistryResourceType.TOOL,
|
|
304
|
+
description=_safe_str(getattr(tool, "__doc__", None)),
|
|
305
|
+
metadata=callable_metadata.model_dump(exclude_none=True),
|
|
306
|
+
)
|
|
204
307
|
)
|
|
205
|
-
)
|
|
206
308
|
|
|
207
309
|
# Models
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
310
|
+
if resource_type is None or resource_type == RegistryResourceType.MODEL:
|
|
311
|
+
for model in getattr(registry, "models", []) or []:
|
|
312
|
+
model_name = (
|
|
313
|
+
_safe_str(getattr(model, "id", None))
|
|
314
|
+
or _safe_str(getattr(model, "name", None))
|
|
315
|
+
or model.__class__.__name__
|
|
316
|
+
)
|
|
317
|
+
model_metadata = ModelMetadata(
|
|
318
|
+
class_path=_class_path(model),
|
|
319
|
+
provider=_safe_str(getattr(model, "provider", None)),
|
|
320
|
+
model_id=_safe_str(getattr(model, "id", None)),
|
|
321
|
+
)
|
|
322
|
+
resources.append(
|
|
323
|
+
RegistryContentResponse(
|
|
324
|
+
name=model_name,
|
|
325
|
+
type=RegistryResourceType.MODEL,
|
|
326
|
+
description=_safe_str(getattr(model, "description", None)),
|
|
327
|
+
metadata=model_metadata.model_dump(exclude_none=True),
|
|
328
|
+
)
|
|
224
329
|
)
|
|
225
|
-
)
|
|
226
330
|
|
|
227
331
|
# Databases
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
332
|
+
if resource_type is None or resource_type == RegistryResourceType.DB:
|
|
333
|
+
for db in getattr(registry, "dbs", []) or []:
|
|
334
|
+
db_name = (
|
|
335
|
+
_safe_str(getattr(db, "name", None)) or _safe_str(getattr(db, "id", None)) or db.__class__.__name__
|
|
336
|
+
)
|
|
337
|
+
db_metadata = DbMetadata(
|
|
338
|
+
class_path=_class_path(db),
|
|
339
|
+
db_id=_safe_str(getattr(db, "id", None)),
|
|
340
|
+
)
|
|
341
|
+
resources.append(
|
|
342
|
+
RegistryContentResponse(
|
|
343
|
+
name=db_name,
|
|
344
|
+
type=RegistryResourceType.DB,
|
|
345
|
+
description=_safe_str(getattr(db, "description", None)),
|
|
346
|
+
metadata=db_metadata.model_dump(exclude_none=True),
|
|
347
|
+
)
|
|
244
348
|
)
|
|
245
|
-
)
|
|
246
349
|
|
|
247
350
|
# Vector databases
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
351
|
+
if resource_type is None or resource_type == RegistryResourceType.VECTOR_DB:
|
|
352
|
+
for vdb in getattr(registry, "vector_dbs", []) or []:
|
|
353
|
+
vdb_name = (
|
|
354
|
+
_safe_str(getattr(vdb, "name", None))
|
|
355
|
+
or _safe_str(getattr(vdb, "id", None))
|
|
356
|
+
or _safe_str(getattr(vdb, "collection", None))
|
|
357
|
+
or _safe_str(getattr(vdb, "table_name", None))
|
|
358
|
+
or vdb.__class__.__name__
|
|
359
|
+
)
|
|
360
|
+
vdb_metadata = VectorDbMetadata(
|
|
361
|
+
class_path=_class_path(vdb),
|
|
362
|
+
vector_db_id=_safe_str(getattr(vdb, "id", None)),
|
|
363
|
+
collection=_safe_str(getattr(vdb, "collection", None)),
|
|
364
|
+
table_name=_safe_str(getattr(vdb, "table_name", None)),
|
|
365
|
+
)
|
|
366
|
+
resources.append(
|
|
367
|
+
RegistryContentResponse(
|
|
368
|
+
name=vdb_name,
|
|
369
|
+
type=RegistryResourceType.VECTOR_DB,
|
|
370
|
+
description=_safe_str(getattr(vdb, "description", None)),
|
|
371
|
+
metadata=vdb_metadata.model_dump(exclude_none=True),
|
|
372
|
+
)
|
|
266
373
|
)
|
|
267
|
-
)
|
|
268
374
|
|
|
269
375
|
# Schemas
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
376
|
+
if resource_type is None or resource_type == RegistryResourceType.SCHEMA:
|
|
377
|
+
for schema in getattr(registry, "schemas", []) or []:
|
|
378
|
+
schema_name = schema.__name__
|
|
379
|
+
schema_json: Optional[Dict[str, Any]] = None
|
|
380
|
+
schema_error: Optional[str] = None
|
|
274
381
|
try:
|
|
275
|
-
|
|
382
|
+
schema_json = schema.model_json_schema() if hasattr(schema, "model_json_schema") else {}
|
|
276
383
|
except Exception as e:
|
|
277
|
-
|
|
384
|
+
schema_error = str(e)
|
|
278
385
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
386
|
+
schema_metadata = SchemaMetadata(
|
|
387
|
+
class_path=_class_path(schema),
|
|
388
|
+
schema=schema_json,
|
|
389
|
+
schema_error=schema_error,
|
|
390
|
+
)
|
|
391
|
+
resources.append(
|
|
392
|
+
RegistryContentResponse(
|
|
393
|
+
name=schema_name,
|
|
394
|
+
type=RegistryResourceType.SCHEMA,
|
|
395
|
+
metadata=schema_metadata.model_dump(exclude_none=True, by_alias=True),
|
|
396
|
+
)
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
# Functions (raw callables used for workflow conditions, selectors, etc.)
|
|
400
|
+
if resource_type is None or resource_type == RegistryResourceType.FUNCTION:
|
|
401
|
+
for func in getattr(registry, "functions", []) or []:
|
|
402
|
+
func_name = getattr(func, "__name__", None) or "anonymous"
|
|
403
|
+
func_module = getattr(func, "__module__", "unknown")
|
|
404
|
+
|
|
405
|
+
# Extract signature
|
|
406
|
+
reg_func_signature: Optional[str] = None
|
|
407
|
+
reg_func_return_annotation: Optional[str] = None
|
|
408
|
+
try:
|
|
409
|
+
sig = inspect.signature(func)
|
|
410
|
+
reg_func_signature = str(sig)
|
|
411
|
+
if sig.return_annotation is not inspect.Signature.empty:
|
|
412
|
+
reg_func_return_annotation = str(sig.return_annotation)
|
|
413
|
+
except (ValueError, TypeError):
|
|
414
|
+
pass
|
|
415
|
+
|
|
416
|
+
func_description = _safe_str(getattr(func, "__doc__", None))
|
|
417
|
+
reg_func_metadata = FunctionMetadata(
|
|
418
|
+
name=func_name,
|
|
419
|
+
description=func_description,
|
|
420
|
+
class_path=f"{func_module}.{func_name}",
|
|
421
|
+
module=func_module,
|
|
422
|
+
qualname=getattr(func, "__qualname__", None),
|
|
423
|
+
has_entrypoint=True,
|
|
424
|
+
parameters=_get_callable_params(func),
|
|
425
|
+
requires_confirmation=None,
|
|
426
|
+
external_execution=None,
|
|
427
|
+
signature=reg_func_signature,
|
|
428
|
+
return_annotation=reg_func_return_annotation,
|
|
429
|
+
)
|
|
430
|
+
resources.append(
|
|
431
|
+
RegistryContentResponse(
|
|
432
|
+
name=func_name,
|
|
433
|
+
type=RegistryResourceType.FUNCTION,
|
|
434
|
+
description=func_description,
|
|
435
|
+
metadata=reg_func_metadata.model_dump(exclude_none=True),
|
|
436
|
+
)
|
|
284
437
|
)
|
|
285
|
-
)
|
|
286
438
|
|
|
287
439
|
# Stable ordering helps pagination
|
|
288
|
-
|
|
289
|
-
return
|
|
440
|
+
resources.sort(key=lambda r: (r.type, r.name))
|
|
441
|
+
return resources
|
|
290
442
|
|
|
291
443
|
@router.get(
|
|
292
444
|
"/registry",
|
|
293
|
-
response_model=PaginatedResponse[
|
|
445
|
+
response_model=PaginatedResponse[RegistryContentResponse],
|
|
294
446
|
response_model_exclude_none=True,
|
|
295
447
|
status_code=200,
|
|
296
448
|
operation_id="list_registry",
|
|
297
449
|
summary="List Registry",
|
|
298
|
-
description="List all
|
|
450
|
+
description="List all resources in the registry with optional filtering.",
|
|
299
451
|
)
|
|
300
452
|
async def list_registry(
|
|
301
|
-
|
|
453
|
+
resource_type: Optional[RegistryResourceType] = Query(None, description="Filter by resource type"),
|
|
302
454
|
name: Optional[str] = Query(None, description="Filter by name (partial match)"),
|
|
303
|
-
include_schema: bool = Query(False, description="Include JSON schema for schema components"),
|
|
304
455
|
page: int = Query(1, ge=1, description="Page number"),
|
|
305
456
|
limit: int = Query(20, ge=1, le=100, description="Items per page"),
|
|
306
|
-
) -> PaginatedResponse[
|
|
457
|
+
) -> PaginatedResponse[RegistryContentResponse]:
|
|
307
458
|
try:
|
|
308
459
|
start_time_ms = time.time() * 1000
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
if component_type:
|
|
312
|
-
components = [c for c in components if c.component_type == component_type]
|
|
460
|
+
resources = _get_resources(resource_type)
|
|
313
461
|
|
|
314
462
|
if name:
|
|
315
463
|
needle = name.lower().strip()
|
|
316
|
-
|
|
464
|
+
resources = [r for r in resources if needle in r.name.lower()]
|
|
317
465
|
|
|
318
|
-
total_count = len(
|
|
466
|
+
total_count = len(resources)
|
|
319
467
|
total_pages = (total_count + limit - 1) // limit if limit > 0 else 0
|
|
320
468
|
start_idx = (page - 1) * limit
|
|
321
|
-
paginated =
|
|
469
|
+
paginated = resources[start_idx : start_idx + limit]
|
|
322
470
|
|
|
323
471
|
return PaginatedResponse(
|
|
324
472
|
data=paginated,
|
|
@@ -331,7 +479,7 @@ def attach_routes(router: APIRouter, registry: Registry) -> APIRouter:
|
|
|
331
479
|
),
|
|
332
480
|
)
|
|
333
481
|
except Exception as e:
|
|
334
|
-
log_error(f"Error listing
|
|
482
|
+
log_error(f"Error listing registry resources: {e}")
|
|
335
483
|
raise HTTPException(status_code=500, detail=str(e))
|
|
336
484
|
|
|
337
485
|
return router
|