execlave-sdk 1.0.0__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.
- execlave/__init__.py +40 -0
- execlave/agent.py +204 -0
- execlave/client.py +1217 -0
- execlave/connectors.py +89 -0
- execlave/errors.py +110 -0
- execlave/otel.py +149 -0
- execlave/trace.py +150 -0
- execlave_sdk-1.0.0.dist-info/METADATA +309 -0
- execlave_sdk-1.0.0.dist-info/RECORD +10 -0
- execlave_sdk-1.0.0.dist-info/WHEEL +4 -0
execlave/__init__.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Execlave Python SDK
|
|
3
|
+
|
|
4
|
+
Official SDK for integrating AI agents with the Execlave governance platform.
|
|
5
|
+
Provides tracing, prompt management, and governance capabilities.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .client import Execlave, ExeclaveClient
|
|
9
|
+
from .agent import Agent
|
|
10
|
+
from .trace import Trace
|
|
11
|
+
from .errors import (
|
|
12
|
+
ExeclaveError,
|
|
13
|
+
ExeclaveAuthError,
|
|
14
|
+
AgentPausedError,
|
|
15
|
+
PolicyBlockedError,
|
|
16
|
+
PolicyDeniedError,
|
|
17
|
+
ApprovalTimeoutError,
|
|
18
|
+
EnforcementUnavailableError,
|
|
19
|
+
QuotaExceededError,
|
|
20
|
+
PlanLimitExceededError,
|
|
21
|
+
)
|
|
22
|
+
from .connectors import run_openai_chat, run_langchain
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"Execlave",
|
|
26
|
+
"ExeclaveClient", # backward compat alias
|
|
27
|
+
"Agent",
|
|
28
|
+
"Trace",
|
|
29
|
+
"ExeclaveError",
|
|
30
|
+
"ExeclaveAuthError",
|
|
31
|
+
"AgentPausedError",
|
|
32
|
+
"PolicyBlockedError",
|
|
33
|
+
"PolicyDeniedError",
|
|
34
|
+
"ApprovalTimeoutError",
|
|
35
|
+
"EnforcementUnavailableError",
|
|
36
|
+
"QuotaExceededError",
|
|
37
|
+
"PlanLimitExceededError",
|
|
38
|
+
"run_openai_chat",
|
|
39
|
+
"run_langchain",
|
|
40
|
+
]
|
execlave/agent.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""Agent object returned from register_agent()."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Agent:
|
|
7
|
+
"""
|
|
8
|
+
Represents a registered agent. Provides prompt management methods.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, exe: Any, data: dict):
|
|
12
|
+
self._exe = exe
|
|
13
|
+
self.id: str = data.get("id", "")
|
|
14
|
+
self.agent_id: str = data.get("agentId", "")
|
|
15
|
+
self.name: str = data.get("name", "")
|
|
16
|
+
self.environment: str = data.get("environment", "")
|
|
17
|
+
self.status: str = data.get("status", "active")
|
|
18
|
+
self._data = data
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def is_paused(self) -> bool:
|
|
22
|
+
return self.status == "paused"
|
|
23
|
+
|
|
24
|
+
def deploy_prompt(
|
|
25
|
+
self,
|
|
26
|
+
prompt_template: str,
|
|
27
|
+
system_message: str | None = None,
|
|
28
|
+
model_name: str | None = None,
|
|
29
|
+
model_parameters: dict | None = None,
|
|
30
|
+
change_type: str = "minor",
|
|
31
|
+
change_description: str | None = None,
|
|
32
|
+
description: str | None = None,
|
|
33
|
+
version_tag: str | None = None,
|
|
34
|
+
environment: str | None = None,
|
|
35
|
+
require_approval: bool = True,
|
|
36
|
+
) -> "PromptVersion":
|
|
37
|
+
"""
|
|
38
|
+
Deploy a new prompt version for this agent.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
prompt_template: The prompt template string with {placeholders}
|
|
42
|
+
system_message: Optional system message
|
|
43
|
+
model_name: Model to use (e.g. 'gpt-4-turbo')
|
|
44
|
+
model_parameters: Dict with temperature, max_tokens, etc.
|
|
45
|
+
change_type: 'major' | 'minor' | 'patch'
|
|
46
|
+
change_description: Description of what changed
|
|
47
|
+
description: Alias for change_description (for JS SDK parity)
|
|
48
|
+
version_tag: Semantic version tag (e.g. 'v1.6.0')
|
|
49
|
+
environment: Target environment
|
|
50
|
+
require_approval: Whether to require approval before deployment
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
PromptVersion object
|
|
54
|
+
"""
|
|
55
|
+
params = model_parameters or {}
|
|
56
|
+
resolved_description = change_description or description
|
|
57
|
+
payload = {
|
|
58
|
+
"agentId": self.id,
|
|
59
|
+
"promptTemplate": prompt_template,
|
|
60
|
+
"systemMessage": system_message,
|
|
61
|
+
"modelName": model_name,
|
|
62
|
+
"temperature": params.get("temperature"),
|
|
63
|
+
"maxTokens": params.get("max_tokens"),
|
|
64
|
+
"otherParams": {k: v for k, v in params.items() if k not in ("temperature", "max_tokens")},
|
|
65
|
+
"changeSummary": resolved_description or change_type,
|
|
66
|
+
"versionTag": version_tag,
|
|
67
|
+
}
|
|
68
|
+
# Remove None values
|
|
69
|
+
payload = {k: v for k, v in payload.items() if v is not None}
|
|
70
|
+
|
|
71
|
+
resp = self._exe._request("POST", self._exe._api_path("/prompt-versions"), json=payload)
|
|
72
|
+
version_data = resp.get("data", {})
|
|
73
|
+
return PromptVersion(self._exe, version_data)
|
|
74
|
+
|
|
75
|
+
def get_current_prompt(self) -> Optional["PromptVersion"]:
|
|
76
|
+
"""
|
|
77
|
+
Fetch the currently deployed prompt version for this agent.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
PromptVersion or None if no version is deployed.
|
|
81
|
+
"""
|
|
82
|
+
resp = self._exe._request(
|
|
83
|
+
"GET",
|
|
84
|
+
self._exe._api_path(f"/prompt-versions?agentId={self.id}&deployed=true"),
|
|
85
|
+
)
|
|
86
|
+
versions = resp.get("data", [])
|
|
87
|
+
for v in versions:
|
|
88
|
+
if v.get("isDeployed"):
|
|
89
|
+
return PromptVersion(self._exe, v)
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
def list_prompt_versions(self) -> list["PromptVersion"]:
|
|
93
|
+
"""
|
|
94
|
+
List all prompt versions for this agent.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
List of PromptVersion objects.
|
|
98
|
+
"""
|
|
99
|
+
resp = self._exe._request(
|
|
100
|
+
"GET",
|
|
101
|
+
self._exe._api_path(f"/prompt-versions?agentId={self.id}"),
|
|
102
|
+
)
|
|
103
|
+
return [PromptVersion(self._exe, v) for v in resp.get("data", [])]
|
|
104
|
+
|
|
105
|
+
def refresh_status(self) -> str:
|
|
106
|
+
"""
|
|
107
|
+
Poll the current agent status from the API.
|
|
108
|
+
|
|
109
|
+
Updates self.status in-place and returns it.
|
|
110
|
+
"""
|
|
111
|
+
resp = self._exe._request(
|
|
112
|
+
"GET",
|
|
113
|
+
self._exe._api_path(f"/agents/{self.id}/status-poll"),
|
|
114
|
+
)
|
|
115
|
+
data = resp.get("data", {})
|
|
116
|
+
self.status = data.get("status", self.status)
|
|
117
|
+
return self.status
|
|
118
|
+
|
|
119
|
+
def promote_to_production(
|
|
120
|
+
self,
|
|
121
|
+
version_id: str,
|
|
122
|
+
require_approval: bool = False,
|
|
123
|
+
deployment_notes: str | None = None,
|
|
124
|
+
) -> "PromptVersion":
|
|
125
|
+
"""
|
|
126
|
+
Promote a specific prompt version to production.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
version_id: The ID of the version to promote.
|
|
130
|
+
require_approval: Whether to require admin approval.
|
|
131
|
+
deployment_notes: Notes for the deployment.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
PromptVersion object for the promoted version.
|
|
135
|
+
"""
|
|
136
|
+
payload: dict = {
|
|
137
|
+
"environment": "production",
|
|
138
|
+
"requireApproval": require_approval,
|
|
139
|
+
}
|
|
140
|
+
if deployment_notes is not None:
|
|
141
|
+
payload["deploymentNotes"] = deployment_notes
|
|
142
|
+
|
|
143
|
+
resp = self._exe._request(
|
|
144
|
+
"POST",
|
|
145
|
+
self._exe._api_path(f"/prompt-versions/{version_id}/deploy"),
|
|
146
|
+
json=payload,
|
|
147
|
+
)
|
|
148
|
+
return PromptVersion(self._exe, resp.get("data", {}))
|
|
149
|
+
|
|
150
|
+
def rollback_to_version(self, version_number: int, reason: str = "Rollback requested") -> "PromptVersion":
|
|
151
|
+
"""
|
|
152
|
+
Rollback to a specific prompt version by version number.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
version_number: The version number to rollback to
|
|
156
|
+
reason: Reason for the rollback
|
|
157
|
+
"""
|
|
158
|
+
# Find the version by number
|
|
159
|
+
versions = self._exe._request("GET", self._exe._api_path(f"/prompt-versions?agentId={self.id}"))
|
|
160
|
+
target = None
|
|
161
|
+
for v in versions.get("data", []):
|
|
162
|
+
if v.get("versionNumber") == version_number:
|
|
163
|
+
target = v
|
|
164
|
+
break
|
|
165
|
+
|
|
166
|
+
if not target:
|
|
167
|
+
from .errors import ExeclaveError
|
|
168
|
+
raise ExeclaveError(f"Version {version_number} not found for agent {self.agent_id}")
|
|
169
|
+
|
|
170
|
+
resp = self._exe._request("POST", self._exe._api_path(f"/prompt-versions/{target['id']}/rollback"), json={"reason": reason})
|
|
171
|
+
return PromptVersion(self._exe, resp.get("data", {}))
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class PromptVersion:
|
|
175
|
+
"""Represents a prompt version."""
|
|
176
|
+
|
|
177
|
+
def __init__(self, exe: Any, data: dict):
|
|
178
|
+
self._exe = exe
|
|
179
|
+
self.id: str = data.get("id", "")
|
|
180
|
+
self.version_number: int = data.get("versionNumber", 0)
|
|
181
|
+
self.version_tag: str = data.get("versionTag", "")
|
|
182
|
+
self.approval_status: str = data.get("approvalStatus", "pending")
|
|
183
|
+
self.is_active: bool = data.get("isActive", False)
|
|
184
|
+
self._data = data
|
|
185
|
+
|
|
186
|
+
def promote_to_production(
|
|
187
|
+
self,
|
|
188
|
+
require_approval: bool = True,
|
|
189
|
+
deployment_notes: str | None = None,
|
|
190
|
+
) -> "PromptVersion":
|
|
191
|
+
"""
|
|
192
|
+
Deploy this version to production.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
require_approval: Whether approval is required first
|
|
196
|
+
deployment_notes: Notes about this deployment
|
|
197
|
+
"""
|
|
198
|
+
resp = self._exe._request("POST", self._exe._api_path(f"/prompt-versions/{self.id}/deploy"), json={
|
|
199
|
+
"environment": "production"
|
|
200
|
+
})
|
|
201
|
+
data = resp.get("data", {})
|
|
202
|
+
self.approval_status = data.get("approvalStatus", self.approval_status)
|
|
203
|
+
self.is_active = data.get("isActive", self.is_active)
|
|
204
|
+
return self
|