agentic-fabriq-sdk 0.1.17__tar.gz → 0.1.19__tar.gz
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.
Potentially problematic release.
This version of agentic-fabriq-sdk might be problematic. Click here for more details.
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/PKG-INFO +3 -3
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/commands/tools.py +36 -54
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/core/client.py +29 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/main.py +0 -2
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/__init__.py +0 -4
- agentic_fabriq_sdk-0.1.19/af_sdk/dx/__init__.py +10 -0
- agentic_fabriq_sdk-0.1.19/af_sdk/dx/runtime.py +64 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/events.py +14 -50
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/fabriq_client.py +66 -14
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/models/__init__.py +0 -8
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/models/types.py +0 -50
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/pyproject.toml +3 -3
- agentic_fabriq_sdk-0.1.17/af_cli/commands/agents.py +0 -238
- agentic_fabriq_sdk-0.1.17/af_sdk/auth/application.py +0 -264
- agentic_fabriq_sdk-0.1.17/af_sdk/dx/__init__.py +0 -12
- agentic_fabriq_sdk-0.1.17/af_sdk/dx/runtime.py +0 -170
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/README.md +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/__init__.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/commands/__init__.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/commands/applications.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/commands/auth.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/commands/config.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/commands/mcp_servers.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/commands/secrets.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/core/__init__.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/core/config.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/core/oauth.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/core/output.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_cli/core/token_storage.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/auth/__init__.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/auth/applications.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/auth/dpop.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/auth/oauth.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/auth/token_cache.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/connectors/__init__.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/connectors/base.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/connectors/registry.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/dx/decorators.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/exceptions.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/models/audit.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/py.typed +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/transport/__init__.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/transport/http.py +0 -0
- {agentic_fabriq_sdk-0.1.17 → agentic_fabriq_sdk-0.1.19}/af_sdk/vault.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentic-fabriq-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.19
|
|
4
4
|
Summary: Agentic Fabriq SDK: high-level client, CLI tool, DX helpers, and auth for AI agents
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Keywords: fabriq,agentic-fabriq,sdk,ai,agents,agentic,fabric,cli
|
|
@@ -17,7 +17,7 @@ Classifier: Typing :: Typed
|
|
|
17
17
|
Requires-Dist: PyJWT (>=2.8.0)
|
|
18
18
|
Requires-Dist: PyYAML (>=6.0.0)
|
|
19
19
|
Requires-Dist: aiohttp (>=3.9.0)
|
|
20
|
-
Requires-Dist: click (>=8.
|
|
20
|
+
Requires-Dist: click (>=8.1.0)
|
|
21
21
|
Requires-Dist: cryptography (>=41.0.0)
|
|
22
22
|
Requires-Dist: httpx (>=0.25.0)
|
|
23
23
|
Requires-Dist: keyring (>=25.0.0)
|
|
@@ -27,7 +27,7 @@ Requires-Dist: opentelemetry-instrumentation-httpx (>=0.41b0)
|
|
|
27
27
|
Requires-Dist: pydantic (>=2.4.0)
|
|
28
28
|
Requires-Dist: rich (>=13.7.0)
|
|
29
29
|
Requires-Dist: stevedore (>=5.1.0)
|
|
30
|
-
Requires-Dist: typer[all] (>=0.
|
|
30
|
+
Requires-Dist: typer[all] (>=0.20.0)
|
|
31
31
|
Requires-Dist: typing-extensions (>=4.0.0)
|
|
32
32
|
Project-URL: Documentation, https://docs.agentic-fabric.org
|
|
33
33
|
Project-URL: Homepage, https://github.com/agentic-fabric/agentic-fabric
|
|
@@ -121,17 +121,20 @@ def get(
|
|
|
121
121
|
|
|
122
122
|
@app.command()
|
|
123
123
|
def invoke(
|
|
124
|
-
|
|
124
|
+
connection_id: str = typer.Argument(..., help="Connection ID (e.g., 'slacker', 'gurt')"),
|
|
125
125
|
method: str = typer.Option(..., "--method", help="Tool method to invoke"),
|
|
126
126
|
params: str = typer.Option(None, "--params", help="JSON string of method parameters (e.g., '{\"channel\": \"test\", \"text\": \"Hello\"}')"),
|
|
127
127
|
format: str = typer.Option("json", "--format", help="Output format (json, table, yaml)"),
|
|
128
128
|
):
|
|
129
|
-
"""Invoke a tool
|
|
129
|
+
"""Invoke a tool using its connection ID.
|
|
130
|
+
|
|
131
|
+
The connection ID identifies which specific tool connection to use.
|
|
132
|
+
Run 'afctl tools list' to see your connection IDs.
|
|
130
133
|
|
|
131
134
|
Examples:
|
|
132
|
-
afctl tools invoke
|
|
133
|
-
afctl tools invoke
|
|
134
|
-
afctl tools invoke
|
|
135
|
+
afctl tools invoke slacker --method get_channels
|
|
136
|
+
afctl tools invoke slacker --method post_message --params '{"channel": "test", "text": "Hello!"}'
|
|
137
|
+
afctl tools invoke gurt --method list_files
|
|
135
138
|
"""
|
|
136
139
|
try:
|
|
137
140
|
# Parse parameters if provided
|
|
@@ -145,59 +148,29 @@ def invoke(
|
|
|
145
148
|
raise typer.Exit(1)
|
|
146
149
|
|
|
147
150
|
with get_client() as client:
|
|
148
|
-
|
|
149
|
-
tool_id = tool_identifier
|
|
150
|
-
try:
|
|
151
|
-
from uuid import UUID
|
|
152
|
-
UUID(tool_identifier)
|
|
153
|
-
# It's already a UUID, use it directly
|
|
154
|
-
except ValueError:
|
|
155
|
-
# Not a UUID, try to look up by name
|
|
156
|
-
info(f"Looking up tool '{tool_identifier}'...")
|
|
157
|
-
try:
|
|
158
|
-
tools_response = client.get("/api/v1/tools")
|
|
159
|
-
|
|
160
|
-
# Handle different response formats
|
|
161
|
-
if isinstance(tools_response, dict) and "tools" in tools_response:
|
|
162
|
-
tools = tools_response["tools"]
|
|
163
|
-
elif isinstance(tools_response, dict) and "items" in tools_response:
|
|
164
|
-
tools = tools_response["items"]
|
|
165
|
-
elif hasattr(tools_response, '__iter__') and not isinstance(tools_response, (str, dict)):
|
|
166
|
-
tools = list(tools_response)
|
|
167
|
-
else:
|
|
168
|
-
tools = []
|
|
169
|
-
|
|
170
|
-
# Find tool by name (case-insensitive)
|
|
171
|
-
matching_tools = [t for t in tools if isinstance(t, dict) and t.get("name", "").lower() == tool_identifier.lower()]
|
|
172
|
-
|
|
173
|
-
if not matching_tools:
|
|
174
|
-
error(f"Tool '{tool_identifier}' not found. Available tools:")
|
|
175
|
-
for t in tools:
|
|
176
|
-
if isinstance(t, dict):
|
|
177
|
-
print(f" - {t.get('name')} (ID: {t.get('id')})")
|
|
178
|
-
raise typer.Exit(1)
|
|
179
|
-
|
|
180
|
-
if len(matching_tools) > 1:
|
|
181
|
-
error(f"Multiple tools found with name '{tool_identifier}':")
|
|
182
|
-
for t in matching_tools:
|
|
183
|
-
print(f" - {t.get('name')} (ID: {t.get('id')})")
|
|
184
|
-
error("Please use the UUID instead.")
|
|
185
|
-
raise typer.Exit(1)
|
|
186
|
-
|
|
187
|
-
tool_id = matching_tools[0].get("id")
|
|
188
|
-
info(f"Resolved '{tool_identifier}' to tool ID: {tool_id}")
|
|
189
|
-
except Exception as lookup_error:
|
|
190
|
-
error(f"Failed to look up tool: {lookup_error}")
|
|
191
|
-
raise typer.Exit(1)
|
|
151
|
+
info(f"Invoking connection '{connection_id}' with method '{method}'...")
|
|
192
152
|
|
|
153
|
+
# Verify connection exists
|
|
154
|
+
connections = client.get("/api/v1/user-connections")
|
|
155
|
+
connection = next((c for c in connections if c.get("connection_id") == connection_id), None)
|
|
156
|
+
|
|
157
|
+
if not connection:
|
|
158
|
+
error(f"Connection '{connection_id}' not found")
|
|
159
|
+
info("Available connections:")
|
|
160
|
+
for conn in connections:
|
|
161
|
+
info(f" - {conn.get('connection_id')} ({conn.get('tool')})")
|
|
162
|
+
raise typer.Exit(1)
|
|
163
|
+
|
|
164
|
+
tool_name = connection.get("tool")
|
|
165
|
+
debug(f"Connection '{connection_id}' uses tool '{tool_name}'")
|
|
166
|
+
|
|
167
|
+
# Use the connection-based invoke endpoint (auto-creates tool if needed)
|
|
193
168
|
data = {
|
|
194
169
|
"method": method,
|
|
195
170
|
"parameters": parameters,
|
|
196
|
-
"context": {},
|
|
197
171
|
}
|
|
198
172
|
|
|
199
|
-
|
|
200
|
-
response = client.post(f"/api/v1/tools/{tool_id}/invoke", data)
|
|
173
|
+
response = client.post(f"/api/v1/tools/connections/{connection_id}/invoke", data)
|
|
201
174
|
|
|
202
175
|
success("Tool invoked successfully")
|
|
203
176
|
|
|
@@ -349,8 +322,11 @@ def add(
|
|
|
349
322
|
if redirect_uri:
|
|
350
323
|
config_payload["redirect_uri"] = redirect_uri
|
|
351
324
|
|
|
325
|
+
# For Google tools, pass tool_type parameter to prevent duplicates
|
|
326
|
+
tool_type_param = f"&tool_type={tool}" if api_tool_name == "google" else ""
|
|
327
|
+
|
|
352
328
|
client.post(
|
|
353
|
-
f"/api/v1/tools/{api_tool_name}/config?connection_id={connection_id}",
|
|
329
|
+
f"/api/v1/tools/{api_tool_name}/config?connection_id={connection_id}{tool_type_param}",
|
|
354
330
|
data=config_payload
|
|
355
331
|
)
|
|
356
332
|
success("✅ OAuth app credentials stored")
|
|
@@ -523,9 +499,15 @@ def disconnect(
|
|
|
523
499
|
info("Cancelled")
|
|
524
500
|
return
|
|
525
501
|
|
|
502
|
+
# Determine the API base tool name (Google tools all use "google")
|
|
503
|
+
api_tool_name = "google" if (tool.startswith("google_") or tool == "gmail") else tool
|
|
504
|
+
|
|
505
|
+
# For Google tools, pass tool_type parameter
|
|
506
|
+
tool_type_param = f"&tool_type={tool}" if api_tool_name == "google" else ""
|
|
507
|
+
|
|
526
508
|
# Delete connection credentials
|
|
527
509
|
client.delete(
|
|
528
|
-
f"/api/v1/tools/{
|
|
510
|
+
f"/api/v1/tools/{api_tool_name}/connection?connection_id={connection_id}{tool_type_param}"
|
|
529
511
|
)
|
|
530
512
|
|
|
531
513
|
success(f"✅ Disconnected: {connection_id}")
|
|
@@ -86,6 +86,35 @@ class AFClient:
|
|
|
86
86
|
|
|
87
87
|
return self._handle_response(response)
|
|
88
88
|
|
|
89
|
+
def try_post(self, path: str, data: Optional[Dict] = None) -> tuple[bool, int, Optional[Dict[str, Any]]]:
|
|
90
|
+
"""Make POST request without exiting on error. Returns (success, status_code, response_data)."""
|
|
91
|
+
url = urljoin(self.config.gateway_url, path)
|
|
92
|
+
debug(f"POST {url}")
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
response = self.client.post(
|
|
96
|
+
path,
|
|
97
|
+
json=data,
|
|
98
|
+
headers=self._get_headers(),
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
debug(f"Response: {response.status_code} {response.url}")
|
|
102
|
+
|
|
103
|
+
if response.status_code >= 400:
|
|
104
|
+
try:
|
|
105
|
+
error_data = response.json()
|
|
106
|
+
return False, response.status_code, error_data
|
|
107
|
+
except:
|
|
108
|
+
return False, response.status_code, {"detail": response.text}
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
return True, response.status_code, response.json()
|
|
112
|
+
except:
|
|
113
|
+
return True, response.status_code, {"message": "Success"}
|
|
114
|
+
|
|
115
|
+
except Exception as e:
|
|
116
|
+
return False, 0, {"detail": str(e)}
|
|
117
|
+
|
|
89
118
|
def put(self, path: str, data: Optional[Dict] = None) -> Dict[str, Any]:
|
|
90
119
|
"""Make PUT request."""
|
|
91
120
|
url = urljoin(self.config.gateway_url, path)
|
|
@@ -9,7 +9,6 @@ import typer
|
|
|
9
9
|
from rich.console import Console
|
|
10
10
|
from rich.table import Table
|
|
11
11
|
|
|
12
|
-
from af_cli.commands.agents import app as agents_app
|
|
13
12
|
from af_cli.commands.applications import app as applications_app
|
|
14
13
|
from af_cli.commands.auth import app as auth_app
|
|
15
14
|
from af_cli.commands.config import app as config_app
|
|
@@ -30,7 +29,6 @@ console = Console()
|
|
|
30
29
|
# Add subcommands
|
|
31
30
|
app.add_typer(auth_app, name="auth", help="Authentication commands")
|
|
32
31
|
app.add_typer(config_app, name="config", help="Configuration commands")
|
|
33
|
-
app.add_typer(agents_app, name="agents", help="Agent management commands")
|
|
34
32
|
app.add_typer(tools_app, name="tools", help="Tool management commands")
|
|
35
33
|
app.add_typer(applications_app, name="applications", help="Application management commands")
|
|
36
34
|
app.add_typer(mcp_servers_app, name="mcp-servers", help="MCP server management commands")
|
|
@@ -15,8 +15,6 @@ from .exceptions import (
|
|
|
15
15
|
ValidationError,
|
|
16
16
|
)
|
|
17
17
|
from .models.types import (
|
|
18
|
-
AgentInvokeRequest,
|
|
19
|
-
AgentInvokeResult,
|
|
20
18
|
ToolInvokeRequest,
|
|
21
19
|
ToolInvokeResult,
|
|
22
20
|
)
|
|
@@ -45,8 +43,6 @@ __all__ = [
|
|
|
45
43
|
"ConnectorError",
|
|
46
44
|
"NotFoundError",
|
|
47
45
|
"ValidationError",
|
|
48
|
-
"AgentInvokeRequest",
|
|
49
|
-
"AgentInvokeResult",
|
|
50
46
|
"ToolInvokeRequest",
|
|
51
47
|
"ToolInvokeResult",
|
|
52
48
|
"HTTPClient",
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
import os
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _base_headers(token: str, tenant_id: Optional[str]) -> Dict[str, str]:
|
|
9
|
+
headers = {"Authorization": f"Bearer {token}"}
|
|
10
|
+
if tenant_id:
|
|
11
|
+
headers["X-Tenant-Id"] = tenant_id
|
|
12
|
+
# Dev helper: allow overriding scopes from env for local testing
|
|
13
|
+
debug_scopes = os.getenv("FABRIQ_DEBUG_SCOPES")
|
|
14
|
+
if debug_scopes is not None:
|
|
15
|
+
headers["X-Debug-Scopes"] = debug_scopes
|
|
16
|
+
return headers
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ToolFabric:
|
|
20
|
+
"""Thin facade over Fabriq provider proxy endpoints (e.g., Slack).
|
|
21
|
+
|
|
22
|
+
This class lets developers think in terms of a "fabric" of tools provided
|
|
23
|
+
by a vendor, while under the hood we call the Gateway proxy endpoints.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, *, provider: str, base_url: str, access_token: str, tenant_id: Optional[str] = None):
|
|
27
|
+
self.provider = provider
|
|
28
|
+
self.base_url = base_url.rstrip("/")
|
|
29
|
+
self.token = access_token
|
|
30
|
+
self.tenant_id = tenant_id
|
|
31
|
+
|
|
32
|
+
def get_tools(self, names: List[str]) -> List[str]:
|
|
33
|
+
# Placeholder: simply returns opaque method identifiers as strings the Agent understands
|
|
34
|
+
return [f"{self.provider}:{name}" for name in names]
|
|
35
|
+
|
|
36
|
+
def invoke(self, action: str, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
37
|
+
url = f"{self.base_url}/api/v1/proxy/{self.provider}/{action}"
|
|
38
|
+
with httpx.Client(timeout=30.0) as c:
|
|
39
|
+
r = c.post(url, json=params, headers=_base_headers(self.token, self.tenant_id))
|
|
40
|
+
r.raise_for_status()
|
|
41
|
+
return r.json()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class MCPServer:
|
|
45
|
+
"""Facade for an MCP server registered in Fabriq (proxy layer)."""
|
|
46
|
+
|
|
47
|
+
def __init__(self, *, server_id: str, base_url: str, access_token: str, tenant_id: Optional[str] = None):
|
|
48
|
+
self.server_id = server_id
|
|
49
|
+
self.base_url = base_url.rstrip("/")
|
|
50
|
+
self.token = access_token
|
|
51
|
+
self.tenant_id = tenant_id
|
|
52
|
+
|
|
53
|
+
def get_tools(self, names: List[str]) -> List[str]:
|
|
54
|
+
return [f"mcp:{self.server_id}:{name}" for name in names]
|
|
55
|
+
|
|
56
|
+
def invoke(self, tool: str, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
57
|
+
url = f"{self.base_url}/api/v1/proxy/mcp/{self.server_id}/invoke"
|
|
58
|
+
payload = {"payload": {"tool": tool, "args": args}}
|
|
59
|
+
with httpx.Client(timeout=60.0) as c:
|
|
60
|
+
r = c.post(url, json=payload, headers=_base_headers(self.token, self.tenant_id))
|
|
61
|
+
r.raise_for_status()
|
|
62
|
+
return r.json()
|
|
63
|
+
|
|
64
|
+
|
|
@@ -35,17 +35,6 @@ class EventMetadata(BaseModel):
|
|
|
35
35
|
trace_id: Optional[str] = None
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
class AgentEvent(BaseModel):
|
|
39
|
-
"""Agent lifecycle event"""
|
|
40
|
-
event_type: str # registered, updated, deleted, invoked
|
|
41
|
-
agent_id: str
|
|
42
|
-
agent_name: str
|
|
43
|
-
agent_type: str
|
|
44
|
-
tenant_id: str
|
|
45
|
-
metadata: EventMetadata
|
|
46
|
-
payload: Dict[str, Any] = Field(default_factory=dict)
|
|
47
|
-
|
|
48
|
-
|
|
49
38
|
class ToolEvent(BaseModel):
|
|
50
39
|
"""Tool lifecycle event"""
|
|
51
40
|
event_type: str # registered, updated, deleted, invoked
|
|
@@ -81,7 +70,7 @@ class SecretEvent(BaseModel):
|
|
|
81
70
|
payload: Dict[str, Any] = Field(default_factory=dict)
|
|
82
71
|
|
|
83
72
|
|
|
84
|
-
Event = Union[
|
|
73
|
+
Event = Union[ToolEvent, InvocationEvent, SecretEvent]
|
|
85
74
|
|
|
86
75
|
|
|
87
76
|
class EventStreamConfig(BaseModel):
|
|
@@ -127,25 +116,10 @@ class EventPublisher:
|
|
|
127
116
|
payload: Optional[Dict] = None,
|
|
128
117
|
correlation_id: Optional[str] = None
|
|
129
118
|
):
|
|
130
|
-
"""Publish agent lifecycle event"""
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
tenant_id=tenant_id
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
event = AgentEvent(
|
|
138
|
-
event_type=event_type,
|
|
139
|
-
agent_id=agent_id,
|
|
140
|
-
agent_name=agent_name,
|
|
141
|
-
agent_type=agent_type,
|
|
142
|
-
tenant_id=tenant_id,
|
|
143
|
-
metadata=metadata,
|
|
144
|
-
payload=payload or {}
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
subject = f"agents.{event_type}.{tenant_id}"
|
|
148
|
-
await self._publish_event(subject, event)
|
|
119
|
+
"""Publish agent lifecycle event - DEPRECATED"""
|
|
120
|
+
# Agent events have been removed from the SDK
|
|
121
|
+
# This method is kept for backward compatibility but does nothing
|
|
122
|
+
pass
|
|
149
123
|
|
|
150
124
|
async def publish_tool_event(
|
|
151
125
|
self,
|
|
@@ -284,19 +258,15 @@ class EventSubscriber:
|
|
|
284
258
|
|
|
285
259
|
async def subscribe_to_agent_events(
|
|
286
260
|
self,
|
|
287
|
-
handler: Callable
|
|
261
|
+
handler: Callable,
|
|
288
262
|
tenant_id: Optional[str] = None,
|
|
289
263
|
event_type: Optional[str] = None,
|
|
290
264
|
consumer_name: str = "agent-events-consumer"
|
|
291
265
|
):
|
|
292
|
-
"""Subscribe to agent events"""
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
if event_type:
|
|
297
|
-
subject = f"agents.{event_type}.*"
|
|
298
|
-
|
|
299
|
-
await self._subscribe(subject, handler, consumer_name, AgentEvent)
|
|
266
|
+
"""Subscribe to agent events - DEPRECATED"""
|
|
267
|
+
# Agent events have been removed from the SDK
|
|
268
|
+
# This method is kept for backward compatibility but does nothing
|
|
269
|
+
pass
|
|
300
270
|
|
|
301
271
|
async def subscribe_to_tool_events(
|
|
302
272
|
self,
|
|
@@ -641,16 +611,10 @@ async def publish_agent_registered(
|
|
|
641
611
|
tenant_id: str,
|
|
642
612
|
payload: Optional[Dict] = None
|
|
643
613
|
):
|
|
644
|
-
"""Convenience function to publish agent registered event"""
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
agent_name=agent_name,
|
|
649
|
-
agent_type=agent_type,
|
|
650
|
-
tenant_id=tenant_id,
|
|
651
|
-
source="af_gateway",
|
|
652
|
-
payload=payload
|
|
653
|
-
)
|
|
614
|
+
"""Convenience function to publish agent registered event - DEPRECATED"""
|
|
615
|
+
# Agent events have been removed from the SDK
|
|
616
|
+
# This function is kept for backward compatibility but does nothing
|
|
617
|
+
pass
|
|
654
618
|
|
|
655
619
|
|
|
656
620
|
async def publish_invocation_started(
|
|
@@ -85,6 +85,7 @@ class FabriqClient:
|
|
|
85
85
|
method: str,
|
|
86
86
|
parameters: Optional[Dict[str, Any]] = None,
|
|
87
87
|
context: Optional[Dict[str, Any]] = None,
|
|
88
|
+
connection_id: Optional[str] = None,
|
|
88
89
|
) -> Dict[str, Any]:
|
|
89
90
|
"""Invoke a tool by name or UUID.
|
|
90
91
|
|
|
@@ -93,6 +94,7 @@ class FabriqClient:
|
|
|
93
94
|
method: Method name to invoke
|
|
94
95
|
parameters: Method parameters
|
|
95
96
|
context: Additional context
|
|
97
|
+
connection_id: Specific connection ID to use (for multi-connection tools)
|
|
96
98
|
|
|
97
99
|
Returns:
|
|
98
100
|
Tool invocation result
|
|
@@ -101,6 +103,8 @@ class FabriqClient:
|
|
|
101
103
|
result = await client.invoke_tool("slack", method="get_channels")
|
|
102
104
|
result = await client.invoke_tool("slack", method="post_message",
|
|
103
105
|
parameters={"channel": "test", "text": "Hello!"})
|
|
106
|
+
result = await client.invoke_tool("slack", method="get_channels",
|
|
107
|
+
connection_id="slacker")
|
|
104
108
|
"""
|
|
105
109
|
# Try to resolve tool name to UUID if not already a UUID
|
|
106
110
|
tool_id = tool_identifier
|
|
@@ -142,10 +146,70 @@ class FabriqClient:
|
|
|
142
146
|
body = {"method": method}
|
|
143
147
|
if parameters is not None:
|
|
144
148
|
body["parameters"] = parameters
|
|
145
|
-
|
|
146
|
-
|
|
149
|
+
|
|
150
|
+
# Add connection_id to context if provided
|
|
151
|
+
if context is not None or connection_id is not None:
|
|
152
|
+
ctx = (context or {}).copy()
|
|
153
|
+
if connection_id is not None:
|
|
154
|
+
ctx["connection_id"] = connection_id
|
|
155
|
+
body["context"] = ctx
|
|
156
|
+
|
|
147
157
|
r = await self._http.post(f"/tools/{tool_id}/invoke", json=body, headers=self._extra_headers)
|
|
148
158
|
return r.json()
|
|
159
|
+
|
|
160
|
+
async def invoke_connection(
|
|
161
|
+
self,
|
|
162
|
+
connection_id: str,
|
|
163
|
+
*,
|
|
164
|
+
method: str,
|
|
165
|
+
parameters: Optional[Dict[str, Any]] = None,
|
|
166
|
+
) -> Dict[str, Any]:
|
|
167
|
+
"""Invoke a tool using its connection ID (preferred method).
|
|
168
|
+
|
|
169
|
+
This uses the connection_id (like 'slacker', 'gurt') to automatically
|
|
170
|
+
find and invoke the correct tool with the right credentials.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
connection_id: Connection identifier (e.g., 'slacker', 'gurt')
|
|
174
|
+
method: Method name to invoke
|
|
175
|
+
parameters: Method parameters
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
Tool invocation result
|
|
179
|
+
|
|
180
|
+
Examples:
|
|
181
|
+
result = await client.invoke_connection("slacker", method="get_channels")
|
|
182
|
+
result = await client.invoke_connection("slacker", method="post_message",
|
|
183
|
+
parameters={"channel": "test", "text": "Hello!"})
|
|
184
|
+
result = await client.invoke_connection("gurt", method="list_files")
|
|
185
|
+
"""
|
|
186
|
+
# This uses the tool invocation with connection_id in context
|
|
187
|
+
# which is supported by current production servers
|
|
188
|
+
body = {
|
|
189
|
+
"method": method,
|
|
190
|
+
"parameters": parameters or {},
|
|
191
|
+
"context": {"connection_id": connection_id}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
# First, get the connection to find the tool name
|
|
195
|
+
connections_resp = await self._http.get("/user-connections", headers=self._extra_headers)
|
|
196
|
+
connections = connections_resp.json()
|
|
197
|
+
|
|
198
|
+
connection = next((c for c in connections if c.get("connection_id") == connection_id), None)
|
|
199
|
+
if not connection:
|
|
200
|
+
raise ValueError(f"Connection '{connection_id}' not found")
|
|
201
|
+
|
|
202
|
+
tool_name = connection.get("tool")
|
|
203
|
+
|
|
204
|
+
# Look up the tool by name to get its UUID
|
|
205
|
+
result = await self.invoke_tool(
|
|
206
|
+
tool_name,
|
|
207
|
+
method=method,
|
|
208
|
+
parameters=parameters,
|
|
209
|
+
connection_id=connection_id
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
return result
|
|
149
213
|
|
|
150
214
|
# -----------------
|
|
151
215
|
# MCP Servers
|
|
@@ -184,18 +248,6 @@ class FabriqClient:
|
|
|
184
248
|
r = await self._http.post(f"/proxy/mcp/{server_id}/invoke", json={"payload": payload, "raw": raw}, headers=self._extra_headers)
|
|
185
249
|
return r.json()
|
|
186
250
|
|
|
187
|
-
# -----------------
|
|
188
|
-
# Agents
|
|
189
|
-
# -----------------
|
|
190
|
-
async def list_agents(self, *, page: int = 1, page_size: int = 20, sort_by: str = "name", sort_order: str = "asc") -> Dict[str, Any]:
|
|
191
|
-
params: Dict[str, Any] = {"page": page, "page_size": page_size, "sort_by": sort_by, "sort_order": sort_order}
|
|
192
|
-
r = await self._http.get("/agents", params=params, headers=self._extra_headers)
|
|
193
|
-
return r.json()
|
|
194
|
-
|
|
195
|
-
async def invoke_agent(self, *, agent_id: str, params: Dict[str, Any], raw: bool = False) -> Dict[str, Any]:
|
|
196
|
-
r = await self._http.post(f"/proxy/agents/{agent_id}/invoke", json={"params": params, "raw": raw}, headers=self._extra_headers)
|
|
197
|
-
return r.json()
|
|
198
|
-
|
|
199
251
|
# -----------------
|
|
200
252
|
# Secrets (Gateway-backed Vault)
|
|
201
253
|
# -----------------
|
|
@@ -3,10 +3,6 @@ Data models for Agentic Fabric SDK.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from .types import (
|
|
6
|
-
Agent,
|
|
7
|
-
AgentCreate,
|
|
8
|
-
AgentInvokeRequest,
|
|
9
|
-
AgentInvokeResult,
|
|
10
6
|
ErrorResponse,
|
|
11
7
|
HealthResponse,
|
|
12
8
|
McpServer,
|
|
@@ -25,10 +21,6 @@ from .types import (
|
|
|
25
21
|
)
|
|
26
22
|
|
|
27
23
|
__all__ = [
|
|
28
|
-
"Agent",
|
|
29
|
-
"AgentCreate",
|
|
30
|
-
"AgentInvokeRequest",
|
|
31
|
-
"AgentInvokeResult",
|
|
32
24
|
"Tool",
|
|
33
25
|
"ToolInvokeRequest",
|
|
34
26
|
"ToolInvokeResult",
|
|
@@ -9,56 +9,6 @@ from uuid import UUID
|
|
|
9
9
|
from pydantic import BaseModel, Field
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class Agent(BaseModel):
|
|
13
|
-
"""Agent model."""
|
|
14
|
-
|
|
15
|
-
id: str
|
|
16
|
-
name: str
|
|
17
|
-
description: Optional[str] = None
|
|
18
|
-
owner: str
|
|
19
|
-
tenant_id: str
|
|
20
|
-
group_path: Optional[str] = None
|
|
21
|
-
endpoint_url: Optional[str] = None
|
|
22
|
-
auth_type: Optional[str] = None
|
|
23
|
-
scopes: List[str] = Field(default_factory=list)
|
|
24
|
-
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
25
|
-
created_at: datetime
|
|
26
|
-
updated_at: datetime
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class AgentCreate(BaseModel):
|
|
30
|
-
"""Request model for creating an agent."""
|
|
31
|
-
|
|
32
|
-
id: str
|
|
33
|
-
name: str
|
|
34
|
-
description: Optional[str] = None
|
|
35
|
-
endpoint_url: Optional[str] = None
|
|
36
|
-
auth_type: Optional[str] = None
|
|
37
|
-
scopes: List[str] = Field(default_factory=list)
|
|
38
|
-
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
39
|
-
group_path: Optional[str] = None
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
class AgentInvokeRequest(BaseModel):
|
|
43
|
-
"""Request model for invoking an agent."""
|
|
44
|
-
|
|
45
|
-
input: str
|
|
46
|
-
params: Dict[str, Any] = Field(default_factory=dict)
|
|
47
|
-
max_tokens: Optional[int] = None
|
|
48
|
-
temperature: Optional[float] = None
|
|
49
|
-
stream: bool = False
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
class AgentInvokeResult(BaseModel):
|
|
53
|
-
"""Result model for agent invocation."""
|
|
54
|
-
|
|
55
|
-
output: str
|
|
56
|
-
logs: List[str] = Field(default_factory=list)
|
|
57
|
-
usage: Optional[Dict[str, Any]] = None
|
|
58
|
-
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
59
|
-
status: str = "success"
|
|
60
|
-
|
|
61
|
-
|
|
62
12
|
class Tool(BaseModel):
|
|
63
13
|
"""Tool model."""
|
|
64
14
|
|
|
@@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
5
5
|
|
|
6
6
|
[tool.poetry]
|
|
7
7
|
name = "agentic-fabriq-sdk"
|
|
8
|
-
version = "0.1.
|
|
8
|
+
version = "0.1.19"
|
|
9
9
|
description = "Agentic Fabriq SDK: high-level client, CLI tool, DX helpers, and auth for AI agents"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
license = "Apache-2.0"
|
|
@@ -43,11 +43,11 @@ aiohttp = ">=3.9.0"
|
|
|
43
43
|
# Plugin/extension system
|
|
44
44
|
stevedore = ">=5.1.0"
|
|
45
45
|
# CLI dependencies
|
|
46
|
-
typer = {extras = ["all"], version = ">=0.
|
|
46
|
+
typer = {extras = ["all"], version = ">=0.20.0"}
|
|
47
47
|
rich = ">=13.7.0"
|
|
48
48
|
keyring = ">=25.0.0"
|
|
49
49
|
cryptography = ">=41.0.0"
|
|
50
|
-
click = ">=8.
|
|
50
|
+
click = ">=8.1.0"
|
|
51
51
|
PyYAML = ">=6.0.0"
|
|
52
52
|
|
|
53
53
|
[tool.poetry.scripts]
|