erdo 0.1.31__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.
- erdo/__init__.py +35 -0
- erdo/_generated/__init__.py +18 -0
- erdo/_generated/actions/__init__.py +34 -0
- erdo/_generated/actions/analysis.py +179 -0
- erdo/_generated/actions/bot.py +186 -0
- erdo/_generated/actions/codeexec.py +199 -0
- erdo/_generated/actions/llm.py +148 -0
- erdo/_generated/actions/memory.py +463 -0
- erdo/_generated/actions/pdfextractor.py +97 -0
- erdo/_generated/actions/resource_definitions.py +296 -0
- erdo/_generated/actions/sqlexec.py +90 -0
- erdo/_generated/actions/utils.py +475 -0
- erdo/_generated/actions/webparser.py +119 -0
- erdo/_generated/actions/websearch.py +85 -0
- erdo/_generated/condition/__init__.py +556 -0
- erdo/_generated/internal.py +51 -0
- erdo/_generated/internal_actions.py +91 -0
- erdo/_generated/parameters.py +17 -0
- erdo/_generated/secrets.py +17 -0
- erdo/_generated/template_functions.py +55 -0
- erdo/_generated/types.py +3907 -0
- erdo/actions/__init__.py +40 -0
- erdo/bot_permissions.py +266 -0
- erdo/cli_entry.py +73 -0
- erdo/conditions/__init__.py +11 -0
- erdo/config/__init__.py +5 -0
- erdo/config/config.py +140 -0
- erdo/formatting.py +279 -0
- erdo/install_cli.py +140 -0
- erdo/integrations.py +131 -0
- erdo/invoke/__init__.py +11 -0
- erdo/invoke/client.py +234 -0
- erdo/invoke/invoke.py +555 -0
- erdo/state.py +376 -0
- erdo/sync/__init__.py +17 -0
- erdo/sync/client.py +95 -0
- erdo/sync/extractor.py +492 -0
- erdo/sync/sync.py +327 -0
- erdo/template.py +136 -0
- erdo/test/__init__.py +41 -0
- erdo/test/evaluate.py +272 -0
- erdo/test/runner.py +263 -0
- erdo/types.py +1431 -0
- erdo-0.1.31.dist-info/METADATA +471 -0
- erdo-0.1.31.dist-info/RECORD +48 -0
- erdo-0.1.31.dist-info/WHEEL +4 -0
- erdo-0.1.31.dist-info/entry_points.txt +2 -0
- erdo-0.1.31.dist-info/licenses/LICENSE +22 -0
erdo/formatting.py
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"""Output formatting helpers for bot invocations.
|
|
2
|
+
|
|
3
|
+
This module provides utilities to parse and format bot invocation events
|
|
4
|
+
into human-readable output. Use these for displaying invocation results
|
|
5
|
+
in terminals, scripts, or other user-facing contexts.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> from erdo import invoke
|
|
9
|
+
>>> from erdo.formatting import format_invocation
|
|
10
|
+
>>>
|
|
11
|
+
>>> response = invoke("my_agent", messages=[...])
|
|
12
|
+
>>> print(format_invocation(response))
|
|
13
|
+
Bot: my agent
|
|
14
|
+
Invocation ID: abc-123
|
|
15
|
+
|
|
16
|
+
Result:
|
|
17
|
+
The answer is 4
|
|
18
|
+
|
|
19
|
+
>>> # Verbose mode shows steps
|
|
20
|
+
>>> print(format_invocation(response, verbose=True))
|
|
21
|
+
Bot: my agent
|
|
22
|
+
Invocation ID: abc-123
|
|
23
|
+
|
|
24
|
+
Steps:
|
|
25
|
+
✓ step1 (utils.echo)
|
|
26
|
+
✓ step2 (llm.message)
|
|
27
|
+
|
|
28
|
+
Result:
|
|
29
|
+
The answer is 4
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
import json
|
|
33
|
+
from dataclasses import dataclass, field
|
|
34
|
+
from typing import Any, Dict, List, Optional
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class StepInfo:
|
|
39
|
+
"""Information about a single step execution."""
|
|
40
|
+
|
|
41
|
+
key: str
|
|
42
|
+
action: str
|
|
43
|
+
status: str = "completed"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class InvocationSummary:
|
|
48
|
+
"""Structured summary of a bot invocation.
|
|
49
|
+
|
|
50
|
+
This provides a clean, parsed view of the invocation events
|
|
51
|
+
with key information extracted and organized.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
bot_name: Optional[str] = None
|
|
55
|
+
bot_key: Optional[str] = None
|
|
56
|
+
invocation_id: Optional[str] = None
|
|
57
|
+
steps: List[StepInfo] = field(default_factory=list)
|
|
58
|
+
result: Optional[Any] = None
|
|
59
|
+
error: Optional[str] = None
|
|
60
|
+
success: bool = True
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def parse_invocation_events(
|
|
64
|
+
events: List[Dict[str, Any]],
|
|
65
|
+
bot_key: Optional[str] = None,
|
|
66
|
+
invocation_id: Optional[str] = None,
|
|
67
|
+
) -> InvocationSummary:
|
|
68
|
+
"""Parse raw invocation events into a structured summary.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
events: List of event dictionaries from the backend
|
|
72
|
+
bot_key: Bot key to use as fallback if not in events
|
|
73
|
+
invocation_id: Invocation ID to use as fallback if not in events
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
InvocationSummary with parsed information
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
>>> summary = parse_invocation_events(response.events, bot_key="my_agent")
|
|
80
|
+
>>> print(summary.bot_name)
|
|
81
|
+
'my agent'
|
|
82
|
+
>>> print(summary.steps)
|
|
83
|
+
[StepInfo(key='step1', action='utils.echo', status='completed')]
|
|
84
|
+
"""
|
|
85
|
+
summary = InvocationSummary(
|
|
86
|
+
bot_key=bot_key,
|
|
87
|
+
invocation_id=invocation_id,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
steps_seen = set()
|
|
91
|
+
final_messages = []
|
|
92
|
+
|
|
93
|
+
for event in events:
|
|
94
|
+
# Events are dicts with 'payload' and 'metadata'
|
|
95
|
+
payload = event.get("payload", {})
|
|
96
|
+
metadata = event.get("metadata", {})
|
|
97
|
+
|
|
98
|
+
# Extract invocation ID if not set
|
|
99
|
+
if summary.invocation_id is None:
|
|
100
|
+
if "invocation_id" in payload:
|
|
101
|
+
summary.invocation_id = payload["invocation_id"]
|
|
102
|
+
elif "invocation_id" in metadata:
|
|
103
|
+
summary.invocation_id = metadata["invocation_id"]
|
|
104
|
+
|
|
105
|
+
# Extract bot name
|
|
106
|
+
if summary.bot_name is None and "bot_name" in payload:
|
|
107
|
+
summary.bot_name = payload["bot_name"]
|
|
108
|
+
|
|
109
|
+
# Track step execution
|
|
110
|
+
if "action_type" in payload and "key" in payload:
|
|
111
|
+
step_key = payload["key"]
|
|
112
|
+
# Only add if not already present (avoid duplicates)
|
|
113
|
+
if step_key not in steps_seen:
|
|
114
|
+
steps_seen.add(step_key)
|
|
115
|
+
summary.steps.append(
|
|
116
|
+
StepInfo(
|
|
117
|
+
key=step_key,
|
|
118
|
+
action=payload["action_type"],
|
|
119
|
+
status="completed",
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Collect user-visible text messages (final output)
|
|
124
|
+
if isinstance(payload, str) and metadata.get("user_visibility") == "visible":
|
|
125
|
+
if metadata.get("message_content_id"): # It's part of a message
|
|
126
|
+
final_messages.append(payload)
|
|
127
|
+
|
|
128
|
+
# Combine final messages into result
|
|
129
|
+
if final_messages:
|
|
130
|
+
summary.result = "".join(final_messages)
|
|
131
|
+
|
|
132
|
+
return summary
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def format_invocation(
|
|
136
|
+
response: Any,
|
|
137
|
+
mode: str = "text",
|
|
138
|
+
verbose: bool = False,
|
|
139
|
+
) -> str:
|
|
140
|
+
"""Format a bot invocation response for display.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
response: InvokeResult from invoke()
|
|
144
|
+
mode: Output mode - 'text' or 'json'
|
|
145
|
+
verbose: Show detailed step execution (only applies to text mode)
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Formatted string ready to print
|
|
149
|
+
|
|
150
|
+
Example:
|
|
151
|
+
>>> from erdo import invoke
|
|
152
|
+
>>> from erdo.formatting import format_invocation
|
|
153
|
+
>>>
|
|
154
|
+
>>> response = invoke("my_agent", messages=[...])
|
|
155
|
+
>>>
|
|
156
|
+
>>> # Simple text output
|
|
157
|
+
>>> print(format_invocation(response))
|
|
158
|
+
>>>
|
|
159
|
+
>>> # Verbose text output (shows steps)
|
|
160
|
+
>>> print(format_invocation(response, verbose=True))
|
|
161
|
+
>>>
|
|
162
|
+
>>> # JSON output
|
|
163
|
+
>>> print(format_invocation(response, mode='json'))
|
|
164
|
+
"""
|
|
165
|
+
if mode == "json":
|
|
166
|
+
return _format_as_json(response)
|
|
167
|
+
else:
|
|
168
|
+
return _format_as_text(response, verbose)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _format_as_json(response: Any) -> str:
|
|
172
|
+
"""Format response as JSON."""
|
|
173
|
+
output = {
|
|
174
|
+
"success": response.success,
|
|
175
|
+
"invocation_id": response.invocation_id,
|
|
176
|
+
"result": response.result,
|
|
177
|
+
"error": response.error,
|
|
178
|
+
"events": response.events,
|
|
179
|
+
}
|
|
180
|
+
return json.dumps(output, indent=2)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _format_as_text(response: Any, verbose: bool = False) -> str:
|
|
184
|
+
"""Format response as human-readable text."""
|
|
185
|
+
# Get bot key from response if available
|
|
186
|
+
bot_key = getattr(response, "bot_id", None)
|
|
187
|
+
invocation_id = response.invocation_id
|
|
188
|
+
|
|
189
|
+
# Parse events to extract information
|
|
190
|
+
# Handle SDK response structure: response.result may contain {'events': [...]}
|
|
191
|
+
events_to_process = response.events
|
|
192
|
+
if isinstance(response.result, dict) and "events" in response.result:
|
|
193
|
+
events_to_process = response.result["events"]
|
|
194
|
+
|
|
195
|
+
summary = parse_invocation_events(
|
|
196
|
+
events_to_process,
|
|
197
|
+
bot_key=bot_key,
|
|
198
|
+
invocation_id=invocation_id,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# Build output
|
|
202
|
+
lines = []
|
|
203
|
+
|
|
204
|
+
if not response.success:
|
|
205
|
+
lines.append(f"❌ Invocation failed: {response.error}")
|
|
206
|
+
return "\n".join(lines)
|
|
207
|
+
|
|
208
|
+
# Header
|
|
209
|
+
lines.append(f"Bot: {summary.bot_name or summary.bot_key or 'unknown'}")
|
|
210
|
+
lines.append(f"Invocation ID: {summary.invocation_id or 'N/A'}")
|
|
211
|
+
|
|
212
|
+
# Steps (verbose mode)
|
|
213
|
+
if verbose and summary.steps:
|
|
214
|
+
lines.append("")
|
|
215
|
+
lines.append("Steps:")
|
|
216
|
+
for step in summary.steps:
|
|
217
|
+
status_icon = "✓" if step.status == "completed" else "•"
|
|
218
|
+
lines.append(f" {status_icon} {step.key} ({step.action})")
|
|
219
|
+
|
|
220
|
+
# Result
|
|
221
|
+
if summary.result:
|
|
222
|
+
lines.append("")
|
|
223
|
+
lines.append("Result:")
|
|
224
|
+
|
|
225
|
+
# Try to parse as JSON for pretty printing
|
|
226
|
+
try:
|
|
227
|
+
parsed = json.loads(summary.result)
|
|
228
|
+
lines.append(json.dumps(parsed, indent=2))
|
|
229
|
+
except (json.JSONDecodeError, TypeError):
|
|
230
|
+
# If not JSON, print as is
|
|
231
|
+
lines.append(summary.result)
|
|
232
|
+
elif response.result is not None:
|
|
233
|
+
lines.append("")
|
|
234
|
+
lines.append("Result:")
|
|
235
|
+
if isinstance(response.result, (dict, list)):
|
|
236
|
+
lines.append(json.dumps(response.result, indent=2))
|
|
237
|
+
else:
|
|
238
|
+
lines.append(str(response.result))
|
|
239
|
+
|
|
240
|
+
return "\n".join(lines)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# Convenience method to add to InvokeResult
|
|
244
|
+
def add_format_method():
|
|
245
|
+
"""Add format() method to InvokeResult class.
|
|
246
|
+
|
|
247
|
+
This is called during SDK initialization to add the format()
|
|
248
|
+
method to InvokeResult instances.
|
|
249
|
+
"""
|
|
250
|
+
try:
|
|
251
|
+
from .invoke.invoke import InvokeResult
|
|
252
|
+
|
|
253
|
+
def format_method(self, mode="text", verbose=False):
|
|
254
|
+
"""Format this invocation result for display.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
mode: 'text' or 'json'
|
|
258
|
+
verbose: Show step details (text mode only)
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
Formatted string
|
|
262
|
+
|
|
263
|
+
Example:
|
|
264
|
+
>>> response = invoke("my_agent", messages=[...])
|
|
265
|
+
>>> print(response.format())
|
|
266
|
+
>>> print(response.format(verbose=True))
|
|
267
|
+
"""
|
|
268
|
+
return format_invocation(self, mode=mode, verbose=verbose)
|
|
269
|
+
|
|
270
|
+
# Add method to class
|
|
271
|
+
InvokeResult.format = format_method
|
|
272
|
+
|
|
273
|
+
except ImportError:
|
|
274
|
+
# InvokeResult not available, skip
|
|
275
|
+
pass
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
# Auto-add format method on import
|
|
279
|
+
add_format_method()
|
erdo/install_cli.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Post-install script to download Erdo CLI binary from GitHub releases."""
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import platform
|
|
6
|
+
import shutil
|
|
7
|
+
import urllib.request
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_platform_info():
|
|
12
|
+
"""Get platform and architecture information."""
|
|
13
|
+
system = platform.system().lower()
|
|
14
|
+
machine = platform.machine().lower()
|
|
15
|
+
|
|
16
|
+
# Normalize architecture names
|
|
17
|
+
if machine in ["x86_64", "amd64"]:
|
|
18
|
+
arch = "amd64"
|
|
19
|
+
elif machine in ["aarch64", "arm64"]:
|
|
20
|
+
arch = "arm64"
|
|
21
|
+
else:
|
|
22
|
+
raise RuntimeError(f"Unsupported architecture: {machine}")
|
|
23
|
+
|
|
24
|
+
# Map platform names
|
|
25
|
+
if system == "darwin":
|
|
26
|
+
platform_name = "darwin"
|
|
27
|
+
elif system == "linux":
|
|
28
|
+
platform_name = "linux"
|
|
29
|
+
elif system == "windows":
|
|
30
|
+
platform_name = "windows"
|
|
31
|
+
else:
|
|
32
|
+
raise RuntimeError(f"Unsupported platform: {system}")
|
|
33
|
+
|
|
34
|
+
return platform_name, arch
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_download_url(version="latest"):
|
|
38
|
+
"""Get the download URL for the CLI binary."""
|
|
39
|
+
platform_name, arch = get_platform_info()
|
|
40
|
+
|
|
41
|
+
# Construct the binary name based on GoReleaser naming convention
|
|
42
|
+
if platform_name == "windows":
|
|
43
|
+
binary_name = f"erdo-cli_Windows_{arch}.zip"
|
|
44
|
+
else:
|
|
45
|
+
# For Unix-like systems, use tar.gz
|
|
46
|
+
if arch == "amd64":
|
|
47
|
+
arch_name = "x86_64"
|
|
48
|
+
else:
|
|
49
|
+
arch_name = arch
|
|
50
|
+
|
|
51
|
+
platform_title = platform_name.title()
|
|
52
|
+
binary_name = f"erdo-cli_{platform_title}_{arch_name}.tar.gz"
|
|
53
|
+
|
|
54
|
+
base_url = "https://github.com/erdoai/homebrew-tap/releases"
|
|
55
|
+
if version == "latest":
|
|
56
|
+
return f"{base_url}/latest/download/{binary_name}"
|
|
57
|
+
else:
|
|
58
|
+
return f"{base_url}/download/{version}/{binary_name}"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def download_and_install_cli():
|
|
62
|
+
"""Download and install the CLI binary."""
|
|
63
|
+
try:
|
|
64
|
+
# Get the installation directory
|
|
65
|
+
package_dir = Path(__file__).parent
|
|
66
|
+
bin_dir = package_dir / "bin"
|
|
67
|
+
bin_dir.mkdir(exist_ok=True)
|
|
68
|
+
|
|
69
|
+
platform_name, arch = get_platform_info()
|
|
70
|
+
|
|
71
|
+
# Determine the final binary name
|
|
72
|
+
if platform_name == "windows":
|
|
73
|
+
binary_name = "erdo.exe"
|
|
74
|
+
else:
|
|
75
|
+
binary_name = "erdo"
|
|
76
|
+
|
|
77
|
+
binary_path = bin_dir / binary_name
|
|
78
|
+
|
|
79
|
+
# Skip if binary already exists
|
|
80
|
+
if binary_path.exists():
|
|
81
|
+
print(f"Erdo CLI already installed at {binary_path}")
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
print("Downloading Erdo CLI...")
|
|
85
|
+
download_url = get_download_url()
|
|
86
|
+
|
|
87
|
+
# Download the archive
|
|
88
|
+
archive_path = bin_dir / "erdo-cli-archive"
|
|
89
|
+
urllib.request.urlretrieve(download_url, archive_path)
|
|
90
|
+
|
|
91
|
+
# Extract the binary
|
|
92
|
+
if platform_name == "windows":
|
|
93
|
+
import zipfile
|
|
94
|
+
|
|
95
|
+
with zipfile.ZipFile(archive_path, "r") as zip_ref:
|
|
96
|
+
# Extract erdo.exe from the zip
|
|
97
|
+
for file_info in zip_ref.filelist:
|
|
98
|
+
if file_info.filename.endswith("erdo.exe"):
|
|
99
|
+
with (
|
|
100
|
+
zip_ref.open(file_info) as source,
|
|
101
|
+
open(binary_path, "wb") as target,
|
|
102
|
+
):
|
|
103
|
+
shutil.copyfileobj(source, target)
|
|
104
|
+
break
|
|
105
|
+
else:
|
|
106
|
+
import tarfile
|
|
107
|
+
|
|
108
|
+
with tarfile.open(archive_path, "r:gz") as tar_ref:
|
|
109
|
+
# Extract erdo binary from the tar.gz
|
|
110
|
+
for member in tar_ref.getmembers():
|
|
111
|
+
if member.name.endswith("/erdo") or member.name == "erdo":
|
|
112
|
+
with (
|
|
113
|
+
tar_ref.extractfile(member) as source,
|
|
114
|
+
open(binary_path, "wb") as target,
|
|
115
|
+
):
|
|
116
|
+
shutil.copyfileobj(source, target)
|
|
117
|
+
break
|
|
118
|
+
|
|
119
|
+
# Make executable on Unix-like systems
|
|
120
|
+
if platform_name != "windows":
|
|
121
|
+
os.chmod(binary_path, 0o755)
|
|
122
|
+
|
|
123
|
+
# Clean up the archive
|
|
124
|
+
archive_path.unlink()
|
|
125
|
+
|
|
126
|
+
print(f"✓ Erdo CLI installed to {binary_path}")
|
|
127
|
+
|
|
128
|
+
# Add to PATH instructions
|
|
129
|
+
print("\nTo use the CLI globally, add the following to your PATH:")
|
|
130
|
+
print(f'export PATH="{bin_dir}:$PATH"')
|
|
131
|
+
|
|
132
|
+
except Exception as e:
|
|
133
|
+
print(f"Warning: Failed to download Erdo CLI: {e}")
|
|
134
|
+
print(
|
|
135
|
+
"You can manually download it from: https://github.com/erdoai/homebrew-tap/releases"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
if __name__ == "__main__":
|
|
140
|
+
download_and_install_cli()
|
erdo/integrations.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Integration base classes for defining integration configurations in Python
|
|
2
|
+
"""
|
|
3
|
+
Integration configuration framework for defining integration configs alongside agents.
|
|
4
|
+
|
|
5
|
+
This module provides base classes for defining integration configurations in Python,
|
|
6
|
+
which can then be synced to the backend alongside agents.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Any, Dict, Optional
|
|
10
|
+
|
|
11
|
+
from ._generated.types import IntegrationConfig, IntegrationDefinition
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Helper function to create integration configurations
|
|
15
|
+
def create_integration_config(**kwargs) -> IntegrationConfig:
|
|
16
|
+
"""Create an IntegrationConfig with the provided parameters."""
|
|
17
|
+
# Extract metadata
|
|
18
|
+
source = kwargs.pop("source", "python")
|
|
19
|
+
file_path = kwargs.pop("file_path", None)
|
|
20
|
+
|
|
21
|
+
# Create IntegrationDefinition with remaining parameters
|
|
22
|
+
definition = IntegrationDefinition(**kwargs)
|
|
23
|
+
|
|
24
|
+
return IntegrationConfig(definition=definition, source=source, file_path=file_path)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Base class that acts like IntegrationConfig but handles initialization better
|
|
28
|
+
class IntegrationConfigClass:
|
|
29
|
+
"""Base class for integration configurations using auto-generated types."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, **kwargs):
|
|
32
|
+
"""Initialize with integration definition parameters."""
|
|
33
|
+
# Extract metadata
|
|
34
|
+
self._source = kwargs.pop("source", "python")
|
|
35
|
+
self._file_path = kwargs.pop("file_path", None)
|
|
36
|
+
|
|
37
|
+
# Create the definition with remaining kwargs
|
|
38
|
+
self._definition = IntegrationDefinition(**kwargs)
|
|
39
|
+
|
|
40
|
+
# Create the actual IntegrationConfig instance
|
|
41
|
+
self._config = IntegrationConfig(
|
|
42
|
+
definition=self._definition, source=self._source, file_path=self._file_path
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
46
|
+
"""Convert to dictionary for syncing to backend."""
|
|
47
|
+
return self._config.to_dict()
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def definition(self) -> IntegrationDefinition:
|
|
51
|
+
"""Access the underlying definition."""
|
|
52
|
+
return self._definition
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def config(self) -> IntegrationConfig:
|
|
56
|
+
"""Access the underlying IntegrationConfig."""
|
|
57
|
+
return self._config
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# Helper functions for common credential schemas
|
|
61
|
+
def oauth_credential_schema() -> Dict[str, Any]:
|
|
62
|
+
"""Helper to create standard OAuth credential schema"""
|
|
63
|
+
return {
|
|
64
|
+
"access_token": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"description": "OAuth access token",
|
|
67
|
+
"required": True,
|
|
68
|
+
"source": "integration_credentials",
|
|
69
|
+
},
|
|
70
|
+
"refresh_token": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"description": "OAuth refresh token",
|
|
73
|
+
"required": False,
|
|
74
|
+
"source": "integration_credentials",
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def database_credential_schema() -> Dict[str, Any]:
|
|
80
|
+
"""Helper to create standard database credential schema"""
|
|
81
|
+
return {
|
|
82
|
+
"host": {
|
|
83
|
+
"type": "string",
|
|
84
|
+
"description": "Database host",
|
|
85
|
+
"required": True,
|
|
86
|
+
"source": "integration_credentials",
|
|
87
|
+
},
|
|
88
|
+
"port": {
|
|
89
|
+
"type": "string",
|
|
90
|
+
"description": "Database port",
|
|
91
|
+
"required": True,
|
|
92
|
+
"source": "integration_credentials",
|
|
93
|
+
},
|
|
94
|
+
"database": {
|
|
95
|
+
"type": "string",
|
|
96
|
+
"description": "Database name",
|
|
97
|
+
"required": True,
|
|
98
|
+
"source": "integration_credentials",
|
|
99
|
+
},
|
|
100
|
+
"username": {
|
|
101
|
+
"type": "string",
|
|
102
|
+
"description": "Database username",
|
|
103
|
+
"required": True,
|
|
104
|
+
"source": "integration_credentials",
|
|
105
|
+
},
|
|
106
|
+
"password": {
|
|
107
|
+
"type": "string",
|
|
108
|
+
"description": "Database password",
|
|
109
|
+
"required": True,
|
|
110
|
+
"source": "integration_credentials",
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def api_key_credential_schema(
|
|
116
|
+
key_name: str = "api_key", header_name: Optional[str] = None
|
|
117
|
+
) -> Dict[str, Any]:
|
|
118
|
+
"""Helper to create standard API key credential schema"""
|
|
119
|
+
schema = {
|
|
120
|
+
key_name: {
|
|
121
|
+
"type": "string",
|
|
122
|
+
"description": "API Key for authentication",
|
|
123
|
+
"required": True,
|
|
124
|
+
"source": "integration_credentials",
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if header_name:
|
|
129
|
+
schema[key_name]["header"] = header_name
|
|
130
|
+
|
|
131
|
+
return schema
|
erdo/invoke/__init__.py
ADDED