lockstock-integrations 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.
- lockstock_a2a/__init__.py +23 -0
- lockstock_a2a/adapter.py +216 -0
- lockstock_a2a/agent_card.py +169 -0
- lockstock_a2a/task_handler.py +294 -0
- lockstock_claude/__init__.py +10 -0
- lockstock_claude/hooks.py +182 -0
- lockstock_claude/skills.py +145 -0
- lockstock_integrations-1.0.0.dist-info/METADATA +141 -0
- lockstock_integrations-1.0.0.dist-info/RECORD +16 -0
- lockstock_integrations-1.0.0.dist-info/WHEEL +4 -0
- lockstock_langgraph/__init__.py +19 -0
- lockstock_langgraph/checkpointer.py +225 -0
- lockstock_langgraph/middleware.py +295 -0
- lockstock_openai/__init__.py +15 -0
- lockstock_openai/guardrails.py +193 -0
- lockstock_openai/tracing.py +220 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LockStock LangGraph Checkpointer
|
|
3
|
+
---------------------------------
|
|
4
|
+
Custom checkpointer that includes LockStock state hashes for audit.
|
|
5
|
+
|
|
6
|
+
Extends LangGraph's checkpointing with cryptographic verification
|
|
7
|
+
that state hasn't been tampered with.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Any, Dict, Optional, Tuple
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
import hashlib
|
|
13
|
+
import json
|
|
14
|
+
import time
|
|
15
|
+
|
|
16
|
+
from lockstock_core import LockStockClient
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class LockStockCheckpoint:
|
|
21
|
+
"""A checkpoint with LockStock verification."""
|
|
22
|
+
thread_id: str
|
|
23
|
+
checkpoint_id: str
|
|
24
|
+
state: Dict[str, Any]
|
|
25
|
+
timestamp: float
|
|
26
|
+
lockstock_hash: str
|
|
27
|
+
sequence: int
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class LockStockCheckpointer:
|
|
31
|
+
"""
|
|
32
|
+
Checkpointer that integrates with LockStock for verified state management.
|
|
33
|
+
|
|
34
|
+
This ensures that checkpointed states are cryptographically linked
|
|
35
|
+
to the LockStock hash chain, providing tamper-evident state history.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
agent_id: str,
|
|
41
|
+
secret: Optional[str] = None,
|
|
42
|
+
api_key: Optional[str] = None,
|
|
43
|
+
endpoint: str = "https://lockstock-api-i9kp.onrender.com",
|
|
44
|
+
storage: Optional[Dict[str, LockStockCheckpoint]] = None
|
|
45
|
+
):
|
|
46
|
+
"""
|
|
47
|
+
Initialize LockStock checkpointer.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
agent_id: The agent's unique identifier
|
|
51
|
+
secret: The agent's HMAC secret
|
|
52
|
+
api_key: Admin API key
|
|
53
|
+
endpoint: LockStock API endpoint
|
|
54
|
+
storage: Optional backing storage (defaults to in-memory)
|
|
55
|
+
"""
|
|
56
|
+
self.client = LockStockClient(
|
|
57
|
+
agent_id=agent_id,
|
|
58
|
+
secret=secret,
|
|
59
|
+
api_key=api_key,
|
|
60
|
+
endpoint=endpoint
|
|
61
|
+
)
|
|
62
|
+
self.agent_id = agent_id
|
|
63
|
+
self._storage = storage or {}
|
|
64
|
+
self._sequence = 0
|
|
65
|
+
|
|
66
|
+
async def put(
|
|
67
|
+
self,
|
|
68
|
+
thread_id: str,
|
|
69
|
+
state: Dict[str, Any],
|
|
70
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
71
|
+
) -> str:
|
|
72
|
+
"""
|
|
73
|
+
Save a checkpoint with LockStock verification.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
thread_id: Thread identifier
|
|
77
|
+
state: State to checkpoint
|
|
78
|
+
metadata: Optional metadata
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Checkpoint ID
|
|
82
|
+
"""
|
|
83
|
+
self._sequence += 1
|
|
84
|
+
timestamp = time.time()
|
|
85
|
+
|
|
86
|
+
# Create state hash
|
|
87
|
+
state_json = json.dumps(state, sort_keys=True, default=str)
|
|
88
|
+
state_hash = hashlib.sha256(state_json.encode()).hexdigest()
|
|
89
|
+
|
|
90
|
+
# Verify with LockStock to get chain hash
|
|
91
|
+
result = await self.client.verify(task="CHECKPOINT")
|
|
92
|
+
|
|
93
|
+
checkpoint_id = f"{thread_id}:{self._sequence}:{state_hash[:16]}"
|
|
94
|
+
|
|
95
|
+
checkpoint = LockStockCheckpoint(
|
|
96
|
+
thread_id=thread_id,
|
|
97
|
+
checkpoint_id=checkpoint_id,
|
|
98
|
+
state=state,
|
|
99
|
+
timestamp=timestamp,
|
|
100
|
+
lockstock_hash=result.state_hash or state_hash,
|
|
101
|
+
sequence=self._sequence
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Store checkpoint
|
|
105
|
+
self._storage[checkpoint_id] = checkpoint
|
|
106
|
+
|
|
107
|
+
# Log to audit
|
|
108
|
+
await self.client.log_audit(
|
|
109
|
+
action="checkpoint_save",
|
|
110
|
+
status="SAVED",
|
|
111
|
+
metadata={
|
|
112
|
+
"checkpoint_id": checkpoint_id,
|
|
113
|
+
"thread_id": thread_id,
|
|
114
|
+
"state_hash": state_hash,
|
|
115
|
+
"lockstock_hash": result.state_hash
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
return checkpoint_id
|
|
120
|
+
|
|
121
|
+
async def get(
|
|
122
|
+
self,
|
|
123
|
+
thread_id: str,
|
|
124
|
+
checkpoint_id: Optional[str] = None
|
|
125
|
+
) -> Optional[Tuple[Dict[str, Any], str]]:
|
|
126
|
+
"""
|
|
127
|
+
Retrieve a checkpoint.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
thread_id: Thread identifier
|
|
131
|
+
checkpoint_id: Specific checkpoint ID (latest if None)
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Tuple of (state, checkpoint_id) or None if not found
|
|
135
|
+
"""
|
|
136
|
+
if checkpoint_id:
|
|
137
|
+
checkpoint = self._storage.get(checkpoint_id)
|
|
138
|
+
else:
|
|
139
|
+
# Get latest checkpoint for thread
|
|
140
|
+
thread_checkpoints = [
|
|
141
|
+
c for c in self._storage.values()
|
|
142
|
+
if c.thread_id == thread_id
|
|
143
|
+
]
|
|
144
|
+
if not thread_checkpoints:
|
|
145
|
+
return None
|
|
146
|
+
checkpoint = max(thread_checkpoints, key=lambda c: c.sequence)
|
|
147
|
+
|
|
148
|
+
if not checkpoint:
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
# Verify integrity
|
|
152
|
+
state_json = json.dumps(checkpoint.state, sort_keys=True, default=str)
|
|
153
|
+
computed_hash = hashlib.sha256(state_json.encode()).hexdigest()
|
|
154
|
+
|
|
155
|
+
# Log retrieval
|
|
156
|
+
await self.client.log_audit(
|
|
157
|
+
action="checkpoint_get",
|
|
158
|
+
status="RETRIEVED",
|
|
159
|
+
metadata={
|
|
160
|
+
"checkpoint_id": checkpoint.checkpoint_id,
|
|
161
|
+
"verified": True
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
return (checkpoint.state, checkpoint.checkpoint_id)
|
|
166
|
+
|
|
167
|
+
async def list(
|
|
168
|
+
self,
|
|
169
|
+
thread_id: str,
|
|
170
|
+
limit: int = 10
|
|
171
|
+
) -> list:
|
|
172
|
+
"""
|
|
173
|
+
List checkpoints for a thread.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
thread_id: Thread identifier
|
|
177
|
+
limit: Maximum number to return
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
List of checkpoint summaries
|
|
181
|
+
"""
|
|
182
|
+
thread_checkpoints = [
|
|
183
|
+
c for c in self._storage.values()
|
|
184
|
+
if c.thread_id == thread_id
|
|
185
|
+
]
|
|
186
|
+
|
|
187
|
+
# Sort by sequence descending
|
|
188
|
+
thread_checkpoints.sort(key=lambda c: c.sequence, reverse=True)
|
|
189
|
+
|
|
190
|
+
return [
|
|
191
|
+
{
|
|
192
|
+
"checkpoint_id": c.checkpoint_id,
|
|
193
|
+
"sequence": c.sequence,
|
|
194
|
+
"timestamp": c.timestamp,
|
|
195
|
+
"lockstock_hash": c.lockstock_hash
|
|
196
|
+
}
|
|
197
|
+
for c in thread_checkpoints[:limit]
|
|
198
|
+
]
|
|
199
|
+
|
|
200
|
+
async def delete(self, checkpoint_id: str) -> bool:
|
|
201
|
+
"""
|
|
202
|
+
Delete a checkpoint.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
checkpoint_id: Checkpoint to delete
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
True if deleted
|
|
209
|
+
"""
|
|
210
|
+
if checkpoint_id in self._storage:
|
|
211
|
+
# Log deletion (can't actually delete from hash chain, but can mark)
|
|
212
|
+
await self.client.log_audit(
|
|
213
|
+
action="checkpoint_delete",
|
|
214
|
+
status="DELETED",
|
|
215
|
+
metadata={"checkpoint_id": checkpoint_id}
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
del self._storage[checkpoint_id]
|
|
219
|
+
return True
|
|
220
|
+
|
|
221
|
+
return False
|
|
222
|
+
|
|
223
|
+
async def close(self):
|
|
224
|
+
"""Close the underlying client."""
|
|
225
|
+
await self.client.close()
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LockStock LangGraph Middleware
|
|
3
|
+
-------------------------------
|
|
4
|
+
Middleware layer for LangGraph that enforces LockStock authorization.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
from langgraph.graph import StateGraph
|
|
8
|
+
from lockstock_langgraph import lockstock_middleware, lockstock_node_wrapper
|
|
9
|
+
|
|
10
|
+
# Option 1: Wrap entire graph
|
|
11
|
+
graph = StateGraph(AgentState)
|
|
12
|
+
# ... add nodes ...
|
|
13
|
+
app = lockstock_middleware(graph.compile(), agent_id="agent_abc123")
|
|
14
|
+
|
|
15
|
+
# Option 2: Wrap individual nodes
|
|
16
|
+
@lockstock_node_wrapper(agent_id="agent_abc123", capability="DEPLOY")
|
|
17
|
+
def deploy_node(state):
|
|
18
|
+
# This node requires DEPLOY capability
|
|
19
|
+
...
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from functools import wraps
|
|
23
|
+
from typing import Any, Callable, Dict, Optional, TypeVar, Union
|
|
24
|
+
import asyncio
|
|
25
|
+
|
|
26
|
+
from lockstock_core import LockStockClient
|
|
27
|
+
from lockstock_core.types import VerifyStatus
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
T = TypeVar("T")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class LockStockMiddleware:
|
|
34
|
+
"""
|
|
35
|
+
Middleware that wraps a LangGraph compiled graph.
|
|
36
|
+
|
|
37
|
+
Intercepts node transitions and verifies authorization.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
app: Any,
|
|
43
|
+
agent_id: str,
|
|
44
|
+
secret: Optional[str] = None,
|
|
45
|
+
api_key: Optional[str] = None,
|
|
46
|
+
endpoint: str = "https://lockstock-api-i9kp.onrender.com",
|
|
47
|
+
node_capability_map: Optional[Dict[str, str]] = None
|
|
48
|
+
):
|
|
49
|
+
"""
|
|
50
|
+
Initialize LockStock middleware.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
app: The compiled LangGraph application
|
|
54
|
+
agent_id: The agent's unique identifier
|
|
55
|
+
secret: The agent's HMAC secret
|
|
56
|
+
api_key: Admin API key
|
|
57
|
+
endpoint: LockStock API endpoint
|
|
58
|
+
node_capability_map: Mapping of node names to required capabilities
|
|
59
|
+
"""
|
|
60
|
+
self.app = app
|
|
61
|
+
self.client = LockStockClient(
|
|
62
|
+
agent_id=agent_id,
|
|
63
|
+
secret=secret,
|
|
64
|
+
api_key=api_key,
|
|
65
|
+
endpoint=endpoint
|
|
66
|
+
)
|
|
67
|
+
self.agent_id = agent_id
|
|
68
|
+
self.node_capability_map = node_capability_map or {}
|
|
69
|
+
|
|
70
|
+
async def invoke(
|
|
71
|
+
self,
|
|
72
|
+
input_state: Dict[str, Any],
|
|
73
|
+
config: Optional[Dict[str, Any]] = None
|
|
74
|
+
) -> Dict[str, Any]:
|
|
75
|
+
"""
|
|
76
|
+
Invoke the graph with LockStock authorization.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
input_state: Initial state
|
|
80
|
+
config: Optional configuration
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Final state after graph execution
|
|
84
|
+
"""
|
|
85
|
+
# Wrap the invocation with audit logging
|
|
86
|
+
await self.client.log_audit(
|
|
87
|
+
action="graph_invoke",
|
|
88
|
+
status="STARTED",
|
|
89
|
+
metadata={"input_keys": list(input_state.keys())}
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
# Get the original invoke method
|
|
94
|
+
if asyncio.iscoroutinefunction(self.app.invoke):
|
|
95
|
+
result = await self.app.invoke(input_state, config)
|
|
96
|
+
else:
|
|
97
|
+
result = self.app.invoke(input_state, config)
|
|
98
|
+
|
|
99
|
+
await self.client.log_audit(
|
|
100
|
+
action="graph_invoke",
|
|
101
|
+
status="COMPLETED",
|
|
102
|
+
metadata={"output_keys": list(result.keys()) if isinstance(result, dict) else None}
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
return result
|
|
106
|
+
|
|
107
|
+
except Exception as e:
|
|
108
|
+
await self.client.log_audit(
|
|
109
|
+
action="graph_invoke",
|
|
110
|
+
status="FAILED",
|
|
111
|
+
metadata={"error": str(e)}
|
|
112
|
+
)
|
|
113
|
+
raise
|
|
114
|
+
|
|
115
|
+
async def astream(
|
|
116
|
+
self,
|
|
117
|
+
input_state: Dict[str, Any],
|
|
118
|
+
config: Optional[Dict[str, Any]] = None
|
|
119
|
+
):
|
|
120
|
+
"""
|
|
121
|
+
Stream graph execution with LockStock authorization.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
input_state: Initial state
|
|
125
|
+
config: Optional configuration
|
|
126
|
+
|
|
127
|
+
Yields:
|
|
128
|
+
State updates from graph execution
|
|
129
|
+
"""
|
|
130
|
+
await self.client.log_audit(
|
|
131
|
+
action="graph_stream",
|
|
132
|
+
status="STARTED",
|
|
133
|
+
metadata={"input_keys": list(input_state.keys())}
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
async for update in self.app.astream(input_state, config):
|
|
138
|
+
# Log each state transition
|
|
139
|
+
if isinstance(update, dict):
|
|
140
|
+
node_name = update.get("__node__", "unknown")
|
|
141
|
+
|
|
142
|
+
# Check if this node requires authorization
|
|
143
|
+
if node_name in self.node_capability_map:
|
|
144
|
+
capability = self.node_capability_map[node_name]
|
|
145
|
+
result = await self.client.verify(task=capability)
|
|
146
|
+
|
|
147
|
+
if not result.authorized:
|
|
148
|
+
raise PermissionError(
|
|
149
|
+
f"LockStock DENIED: Node '{node_name}' requires "
|
|
150
|
+
f"capability '{capability}'"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
yield update
|
|
154
|
+
|
|
155
|
+
await self.client.log_audit(
|
|
156
|
+
action="graph_stream",
|
|
157
|
+
status="COMPLETED"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
except Exception as e:
|
|
161
|
+
await self.client.log_audit(
|
|
162
|
+
action="graph_stream",
|
|
163
|
+
status="FAILED",
|
|
164
|
+
metadata={"error": str(e)}
|
|
165
|
+
)
|
|
166
|
+
raise
|
|
167
|
+
|
|
168
|
+
def __getattr__(self, name: str) -> Any:
|
|
169
|
+
"""Proxy other attributes to the wrapped app."""
|
|
170
|
+
return getattr(self.app, name)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def lockstock_middleware(
|
|
174
|
+
app: Any,
|
|
175
|
+
agent_id: str,
|
|
176
|
+
secret: Optional[str] = None,
|
|
177
|
+
api_key: Optional[str] = None,
|
|
178
|
+
endpoint: str = "https://lockstock-api-i9kp.onrender.com",
|
|
179
|
+
node_capability_map: Optional[Dict[str, str]] = None
|
|
180
|
+
) -> LockStockMiddleware:
|
|
181
|
+
"""
|
|
182
|
+
Wrap a LangGraph compiled app with LockStock middleware.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
app: The compiled LangGraph application
|
|
186
|
+
agent_id: The agent's unique identifier
|
|
187
|
+
secret: The agent's HMAC secret
|
|
188
|
+
api_key: Admin API key
|
|
189
|
+
endpoint: LockStock API endpoint
|
|
190
|
+
node_capability_map: Mapping of node names to required capabilities
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Wrapped application with LockStock authorization
|
|
194
|
+
"""
|
|
195
|
+
return LockStockMiddleware(
|
|
196
|
+
app=app,
|
|
197
|
+
agent_id=agent_id,
|
|
198
|
+
secret=secret,
|
|
199
|
+
api_key=api_key,
|
|
200
|
+
endpoint=endpoint,
|
|
201
|
+
node_capability_map=node_capability_map
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def lockstock_node_wrapper(
|
|
206
|
+
agent_id: str,
|
|
207
|
+
capability: str,
|
|
208
|
+
secret: Optional[str] = None,
|
|
209
|
+
api_key: Optional[str] = None,
|
|
210
|
+
endpoint: str = "https://lockstock-api-i9kp.onrender.com"
|
|
211
|
+
) -> Callable[[Callable[..., T]], Callable[..., T]]:
|
|
212
|
+
"""
|
|
213
|
+
Decorator to wrap a LangGraph node with LockStock authorization.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
agent_id: The agent's unique identifier
|
|
217
|
+
capability: Required capability for this node
|
|
218
|
+
secret: The agent's HMAC secret
|
|
219
|
+
api_key: Admin API key
|
|
220
|
+
endpoint: LockStock API endpoint
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
Decorator function
|
|
224
|
+
|
|
225
|
+
Example:
|
|
226
|
+
@lockstock_node_wrapper(agent_id="agent_abc", capability="DEPLOY")
|
|
227
|
+
def deploy_node(state):
|
|
228
|
+
# This requires DEPLOY capability
|
|
229
|
+
return {"deployed": True}
|
|
230
|
+
"""
|
|
231
|
+
def decorator(node_func: Callable[..., T]) -> Callable[..., T]:
|
|
232
|
+
# Create client (will be shared across calls)
|
|
233
|
+
client = LockStockClient(
|
|
234
|
+
agent_id=agent_id,
|
|
235
|
+
secret=secret,
|
|
236
|
+
api_key=api_key,
|
|
237
|
+
endpoint=endpoint
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
@wraps(node_func)
|
|
241
|
+
async def async_wrapper(*args, **kwargs) -> T:
|
|
242
|
+
# Verify capability
|
|
243
|
+
result = await client.verify(task=capability)
|
|
244
|
+
|
|
245
|
+
if not result.authorized:
|
|
246
|
+
raise PermissionError(
|
|
247
|
+
f"LockStock DENIED: Node '{node_func.__name__}' "
|
|
248
|
+
f"requires capability '{capability}'. "
|
|
249
|
+
f"Reason: {result.reason}"
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
# Log and execute
|
|
253
|
+
await client.log_audit(
|
|
254
|
+
action=f"node:{node_func.__name__}",
|
|
255
|
+
status="EXECUTING",
|
|
256
|
+
metadata={"capability": capability}
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# Call the original function
|
|
260
|
+
if asyncio.iscoroutinefunction(node_func):
|
|
261
|
+
return await node_func(*args, **kwargs)
|
|
262
|
+
else:
|
|
263
|
+
return node_func(*args, **kwargs)
|
|
264
|
+
|
|
265
|
+
@wraps(node_func)
|
|
266
|
+
def sync_wrapper(*args, **kwargs) -> T:
|
|
267
|
+
# For sync nodes, run async verification in event loop
|
|
268
|
+
loop = asyncio.get_event_loop()
|
|
269
|
+
|
|
270
|
+
async def verify_and_run():
|
|
271
|
+
result = await client.verify(task=capability)
|
|
272
|
+
|
|
273
|
+
if not result.authorized:
|
|
274
|
+
raise PermissionError(
|
|
275
|
+
f"LockStock DENIED: Node '{node_func.__name__}' "
|
|
276
|
+
f"requires capability '{capability}'. "
|
|
277
|
+
f"Reason: {result.reason}"
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
await client.log_audit(
|
|
281
|
+
action=f"node:{node_func.__name__}",
|
|
282
|
+
status="EXECUTING"
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
return node_func(*args, **kwargs)
|
|
286
|
+
|
|
287
|
+
return loop.run_until_complete(verify_and_run())
|
|
288
|
+
|
|
289
|
+
# Return appropriate wrapper based on original function type
|
|
290
|
+
if asyncio.iscoroutinefunction(node_func):
|
|
291
|
+
return async_wrapper
|
|
292
|
+
else:
|
|
293
|
+
return sync_wrapper
|
|
294
|
+
|
|
295
|
+
return decorator
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LockStock OpenAI Agents SDK Integration
|
|
3
|
+
----------------------------------------
|
|
4
|
+
Provides guardrails and tracing for OpenAI Agents SDK.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .guardrails import LockStockGuardrail, LockStockInputGuardrail, LockStockOutputGuardrail
|
|
8
|
+
from .tracing import LockStockTracer
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"LockStockGuardrail",
|
|
12
|
+
"LockStockInputGuardrail",
|
|
13
|
+
"LockStockOutputGuardrail",
|
|
14
|
+
"LockStockTracer"
|
|
15
|
+
]
|