tollgate 1.0.3__py3-none-any.whl → 1.0.4__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.
- tollgate/__init__.py +6 -2
- tollgate/grants.py +103 -0
- tollgate/tower.py +58 -37
- tollgate/types.py +25 -0
- {tollgate-1.0.3.dist-info → tollgate-1.0.4.dist-info}/METADATA +25 -1
- {tollgate-1.0.3.dist-info → tollgate-1.0.4.dist-info}/RECORD +8 -7
- {tollgate-1.0.3.dist-info → tollgate-1.0.4.dist-info}/WHEEL +0 -0
- {tollgate-1.0.3.dist-info → tollgate-1.0.4.dist-info}/licenses/LICENSE +0 -0
tollgate/__init__.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from .approvals import (
|
|
2
|
-
ApprovalOutcome,
|
|
3
2
|
ApprovalStore,
|
|
4
3
|
Approver,
|
|
5
4
|
AsyncQueueApprover,
|
|
@@ -15,23 +14,26 @@ from .exceptions import (
|
|
|
15
14
|
TollgateDenied,
|
|
16
15
|
TollgateError,
|
|
17
16
|
)
|
|
17
|
+
from .grants import InMemoryGrantStore
|
|
18
18
|
from .helpers import guard, wrap_tool
|
|
19
19
|
from .policy import PolicyEvaluator, YamlPolicyEvaluator
|
|
20
20
|
from .registry import ToolRegistry
|
|
21
21
|
from .tower import ControlTower
|
|
22
22
|
from .types import (
|
|
23
23
|
AgentContext,
|
|
24
|
+
ApprovalOutcome,
|
|
24
25
|
AuditEvent,
|
|
25
26
|
Decision,
|
|
26
27
|
DecisionType,
|
|
27
28
|
Effect,
|
|
29
|
+
Grant,
|
|
28
30
|
Intent,
|
|
29
31
|
NormalizedToolCall,
|
|
30
32
|
Outcome,
|
|
31
33
|
ToolRequest,
|
|
32
34
|
)
|
|
33
35
|
|
|
34
|
-
__version__ = "1.0.
|
|
36
|
+
__version__ = "1.0.4"
|
|
35
37
|
|
|
36
38
|
__all__ = [
|
|
37
39
|
"ControlTower",
|
|
@@ -42,6 +44,7 @@ __all__ = [
|
|
|
42
44
|
"Decision",
|
|
43
45
|
"DecisionType",
|
|
44
46
|
"Effect",
|
|
47
|
+
"Grant",
|
|
45
48
|
"AuditEvent",
|
|
46
49
|
"Outcome",
|
|
47
50
|
"ApprovalOutcome",
|
|
@@ -57,6 +60,7 @@ __all__ = [
|
|
|
57
60
|
"ToolRegistry",
|
|
58
61
|
"PolicyEvaluator",
|
|
59
62
|
"YamlPolicyEvaluator",
|
|
63
|
+
"InMemoryGrantStore",
|
|
60
64
|
"TollgateError",
|
|
61
65
|
"TollgateDenied",
|
|
62
66
|
"TollgateApprovalDenied",
|
tollgate/grants.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
from .types import AgentContext, Grant, ToolRequest
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class InMemoryGrantStore:
|
|
8
|
+
"""In-memory store for action grants with thread-safe matching logic."""
|
|
9
|
+
|
|
10
|
+
def __init__(self):
|
|
11
|
+
self._grants: dict[str, Grant] = {}
|
|
12
|
+
self._usage_counts: dict[str, int] = {}
|
|
13
|
+
self._lock = asyncio.Lock()
|
|
14
|
+
|
|
15
|
+
async def create_grant(self, grant: Grant) -> str:
|
|
16
|
+
"""Store a new grant."""
|
|
17
|
+
async with self._lock:
|
|
18
|
+
self._grants[grant.id] = grant
|
|
19
|
+
self._usage_counts[grant.id] = 0
|
|
20
|
+
return grant.id
|
|
21
|
+
|
|
22
|
+
async def find_matching_grant(
|
|
23
|
+
self, agent_ctx: AgentContext, tool_request: ToolRequest
|
|
24
|
+
) -> Grant | None:
|
|
25
|
+
"""Find a non-expired grant that matches the request."""
|
|
26
|
+
now = time.time()
|
|
27
|
+
async with self._lock:
|
|
28
|
+
for grant in self._grants.values():
|
|
29
|
+
# 1. Skip expired
|
|
30
|
+
if grant.expires_at <= now:
|
|
31
|
+
continue
|
|
32
|
+
|
|
33
|
+
# 2. Match agent_id
|
|
34
|
+
if grant.agent_id is not None and grant.agent_id != agent_ctx.agent_id:
|
|
35
|
+
continue
|
|
36
|
+
|
|
37
|
+
# 3. Match effect
|
|
38
|
+
if grant.effect is not None and grant.effect != tool_request.effect:
|
|
39
|
+
continue
|
|
40
|
+
|
|
41
|
+
# 4. Match tool (exact or prefix with *)
|
|
42
|
+
if grant.tool is not None:
|
|
43
|
+
if grant.tool.endswith("*"):
|
|
44
|
+
prefix = grant.tool[:-1]
|
|
45
|
+
if not tool_request.tool.startswith(prefix):
|
|
46
|
+
continue
|
|
47
|
+
elif grant.tool != tool_request.tool:
|
|
48
|
+
continue
|
|
49
|
+
|
|
50
|
+
# 5. Match action
|
|
51
|
+
if grant.action is not None and grant.action != tool_request.action:
|
|
52
|
+
continue
|
|
53
|
+
|
|
54
|
+
# 6. Match resource_type
|
|
55
|
+
if (
|
|
56
|
+
grant.resource_type is not None
|
|
57
|
+
and grant.resource_type != tool_request.resource_type
|
|
58
|
+
):
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
# Match found! Increment usage count
|
|
62
|
+
self._usage_counts[grant.id] += 1
|
|
63
|
+
return grant
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
async def get_usage_count(self, grant_id: str) -> int:
|
|
67
|
+
"""Get the number of times a grant has been used."""
|
|
68
|
+
async with self._lock:
|
|
69
|
+
return self._usage_counts.get(grant_id, 0)
|
|
70
|
+
|
|
71
|
+
async def revoke_grant(self, grant_id: str) -> bool:
|
|
72
|
+
"""Remove a grant by ID."""
|
|
73
|
+
async with self._lock:
|
|
74
|
+
if grant_id in self._grants:
|
|
75
|
+
del self._grants[grant_id]
|
|
76
|
+
if grant_id in self._usage_counts:
|
|
77
|
+
del self._usage_counts[grant_id]
|
|
78
|
+
return True
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
async def list_active_grants(self, agent_id: str | None = None) -> list[Grant]:
|
|
82
|
+
"""List all non-expired grants, optionally filtered by agent."""
|
|
83
|
+
now = time.time()
|
|
84
|
+
async with self._lock:
|
|
85
|
+
active = []
|
|
86
|
+
for grant in self._grants.values():
|
|
87
|
+
if grant.expires_at > now:
|
|
88
|
+
if agent_id is None or grant.agent_id == agent_id:
|
|
89
|
+
active.append(grant)
|
|
90
|
+
return active
|
|
91
|
+
|
|
92
|
+
async def cleanup_expired(self) -> int:
|
|
93
|
+
"""Remove all expired grants from the store."""
|
|
94
|
+
now = time.time()
|
|
95
|
+
async with self._lock:
|
|
96
|
+
to_remove = [
|
|
97
|
+
gid for gid, g in self._grants.items() if g.expires_at <= now
|
|
98
|
+
]
|
|
99
|
+
for gid in to_remove:
|
|
100
|
+
del self._grants[gid]
|
|
101
|
+
if gid in self._usage_counts:
|
|
102
|
+
del self._usage_counts[gid]
|
|
103
|
+
return len(to_remove)
|
tollgate/tower.py
CHANGED
|
@@ -32,11 +32,13 @@ class ControlTower:
|
|
|
32
32
|
policy: PolicyEvaluator,
|
|
33
33
|
approver: Approver,
|
|
34
34
|
audit: AuditSink,
|
|
35
|
+
grant_store: Any | None = None,
|
|
35
36
|
redact_fn: Callable[[dict[str, Any]], dict[str, Any]] | None = None,
|
|
36
37
|
):
|
|
37
38
|
self.policy = policy
|
|
38
39
|
self.approver = approver
|
|
39
40
|
self.audit = audit
|
|
41
|
+
self.grant_store = grant_store
|
|
40
42
|
self.redact_fn = redact_fn or self._default_redact
|
|
41
43
|
|
|
42
44
|
@staticmethod
|
|
@@ -86,6 +88,26 @@ class ControlTower:
|
|
|
86
88
|
|
|
87
89
|
# 3. Handle ASK
|
|
88
90
|
if decision.decision == DecisionType.ASK:
|
|
91
|
+
# 3.1 Check Grants
|
|
92
|
+
if self.grant_store:
|
|
93
|
+
matching_grant = await self.grant_store.find_matching_grant(
|
|
94
|
+
agent_ctx, tool_request
|
|
95
|
+
)
|
|
96
|
+
if matching_grant:
|
|
97
|
+
# Grant found! Proceed to execution without asking approver
|
|
98
|
+
result = await self._execute_and_log(
|
|
99
|
+
correlation_id,
|
|
100
|
+
request_hash,
|
|
101
|
+
agent_ctx,
|
|
102
|
+
intent,
|
|
103
|
+
tool_request,
|
|
104
|
+
decision,
|
|
105
|
+
exec_async,
|
|
106
|
+
grant_id=matching_grant.id,
|
|
107
|
+
)
|
|
108
|
+
return result
|
|
109
|
+
|
|
110
|
+
# 3.2 Request Approval if no grant found
|
|
89
111
|
outcome = await self.approver.request_approval_async(
|
|
90
112
|
agent_ctx, intent, tool_request, request_hash, decision.reason
|
|
91
113
|
)
|
|
@@ -120,14 +142,35 @@ class ControlTower:
|
|
|
120
142
|
)
|
|
121
143
|
raise TollgateApprovalDenied(f"Approval failed: {outcome.value}")
|
|
122
144
|
|
|
123
|
-
# 4. Execute tool
|
|
145
|
+
# 4. Execute tool (Policy ALLOW or Approval APPROVED)
|
|
146
|
+
return await self._execute_and_log(
|
|
147
|
+
correlation_id,
|
|
148
|
+
request_hash,
|
|
149
|
+
agent_ctx,
|
|
150
|
+
intent,
|
|
151
|
+
tool_request,
|
|
152
|
+
decision,
|
|
153
|
+
exec_async,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
async def _execute_and_log(
|
|
157
|
+
self,
|
|
158
|
+
correlation_id: str,
|
|
159
|
+
request_hash: str,
|
|
160
|
+
agent_ctx: AgentContext,
|
|
161
|
+
intent: Intent,
|
|
162
|
+
tool_request: ToolRequest,
|
|
163
|
+
decision: Decision,
|
|
164
|
+
exec_async: Callable[[], Awaitable[Any]],
|
|
165
|
+
grant_id: str | None = None,
|
|
166
|
+
) -> Any:
|
|
167
|
+
"""Internal helper to execute tool and log result."""
|
|
124
168
|
result = None
|
|
125
169
|
outcome = Outcome.EXECUTED
|
|
126
170
|
try:
|
|
127
171
|
result = await exec_async()
|
|
128
172
|
except Exception as e:
|
|
129
173
|
outcome = Outcome.FAILED
|
|
130
|
-
# Security: Sanitize exception message to avoid info disclosure
|
|
131
174
|
result_summary = self._sanitize_exception(e)
|
|
132
175
|
self._log(
|
|
133
176
|
correlation_id,
|
|
@@ -137,11 +180,12 @@ class ControlTower:
|
|
|
137
180
|
tool_request,
|
|
138
181
|
decision,
|
|
139
182
|
outcome,
|
|
183
|
+
grant_id=grant_id,
|
|
140
184
|
result_summary=result_summary,
|
|
141
185
|
)
|
|
142
186
|
raise
|
|
143
187
|
|
|
144
|
-
#
|
|
188
|
+
# Final Audit
|
|
145
189
|
result_summary = self._truncate_result(result)
|
|
146
190
|
self._log(
|
|
147
191
|
correlation_id,
|
|
@@ -151,6 +195,7 @@ class ControlTower:
|
|
|
151
195
|
tool_request,
|
|
152
196
|
decision,
|
|
153
197
|
outcome,
|
|
198
|
+
grant_id=grant_id,
|
|
154
199
|
result_summary=result_summary,
|
|
155
200
|
)
|
|
156
201
|
|
|
@@ -177,7 +222,9 @@ class ControlTower:
|
|
|
177
222
|
async def _exec():
|
|
178
223
|
return exec_sync()
|
|
179
224
|
|
|
180
|
-
return asyncio.run(
|
|
225
|
+
return asyncio.run(
|
|
226
|
+
self.execute_async(agent_ctx, intent, tool_request, _exec)
|
|
227
|
+
)
|
|
181
228
|
|
|
182
229
|
def _log(
|
|
183
230
|
self,
|
|
@@ -189,6 +236,7 @@ class ControlTower:
|
|
|
189
236
|
decision: Decision,
|
|
190
237
|
outcome: Outcome,
|
|
191
238
|
approval_id: str | None = None,
|
|
239
|
+
grant_id: str | None = None,
|
|
192
240
|
result_summary: str | None = None,
|
|
193
241
|
):
|
|
194
242
|
# Redact params before logging
|
|
@@ -212,47 +260,20 @@ class ControlTower:
|
|
|
212
260
|
decision=decision,
|
|
213
261
|
outcome=outcome,
|
|
214
262
|
approval_id=approval_id,
|
|
263
|
+
grant_id=grant_id,
|
|
215
264
|
result_summary=result_summary,
|
|
216
265
|
policy_version=decision.policy_version,
|
|
217
266
|
manifest_version=req.manifest_version,
|
|
218
267
|
)
|
|
219
268
|
self.audit.emit(event)
|
|
220
269
|
|
|
270
|
+
def _sanitize_exception(self, e: Exception) -> str:
|
|
271
|
+
"""Sanitize exception message to avoid leaking sensitive data."""
|
|
272
|
+
# Only include the exception type and a generic message for security
|
|
273
|
+
return f"{type(e).__name__}: Execution failed"
|
|
274
|
+
|
|
221
275
|
def _truncate_result(self, result: Any, max_chars: int = 200) -> str | None:
|
|
222
276
|
if result is None:
|
|
223
277
|
return None
|
|
224
278
|
s = str(result)
|
|
225
279
|
return s[:max_chars] + "..." if len(s) > max_chars else s
|
|
226
|
-
|
|
227
|
-
def _sanitize_exception(self, e: Exception, max_chars: int = 100) -> str:
|
|
228
|
-
"""
|
|
229
|
-
Sanitize exception message for audit logging.
|
|
230
|
-
|
|
231
|
-
Removes potentially sensitive information like file paths, stack traces,
|
|
232
|
-
and internal details while preserving the exception type.
|
|
233
|
-
"""
|
|
234
|
-
exc_type = type(e).__name__
|
|
235
|
-
|
|
236
|
-
# For common safe exceptions, include a truncated message
|
|
237
|
-
safe_exceptions = {
|
|
238
|
-
"ValueError",
|
|
239
|
-
"TypeError",
|
|
240
|
-
"KeyError",
|
|
241
|
-
"IndexError",
|
|
242
|
-
"AttributeError",
|
|
243
|
-
"RuntimeError",
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if exc_type in safe_exceptions:
|
|
247
|
-
msg = str(e)
|
|
248
|
-
# Remove file paths (Unix and Windows style)
|
|
249
|
-
import re
|
|
250
|
-
|
|
251
|
-
msg = re.sub(r"['\"]?(/[^\s'\"]+|[A-Za-z]:\\[^\s'\"]+)['\"]?", "[PATH]", msg)
|
|
252
|
-
# Truncate to avoid leaking too much info
|
|
253
|
-
if len(msg) > max_chars:
|
|
254
|
-
msg = msg[:max_chars] + "..."
|
|
255
|
-
return f"{exc_type}: {msg}"
|
|
256
|
-
|
|
257
|
-
# For unknown exceptions, only log the type
|
|
258
|
-
return f"{exc_type}: [details redacted]"
|
tollgate/types.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import uuid
|
|
1
2
|
from collections.abc import Awaitable, Callable
|
|
2
3
|
from dataclasses import asdict, dataclass, field
|
|
3
4
|
from enum import Enum
|
|
@@ -92,6 +93,28 @@ class Decision:
|
|
|
92
93
|
return d
|
|
93
94
|
|
|
94
95
|
|
|
96
|
+
@dataclass(frozen=True)
|
|
97
|
+
class Grant:
|
|
98
|
+
"""A grant that allows bypassing human approval for specific actions."""
|
|
99
|
+
|
|
100
|
+
agent_id: str | None # None = any agent
|
|
101
|
+
effect: Effect | None # None = any effect
|
|
102
|
+
tool: str | None # None = any tool, supports prefix like "mcp:*"
|
|
103
|
+
action: str | None # None = any action
|
|
104
|
+
resource_type: str | None # None = any resource
|
|
105
|
+
expires_at: float # Unix timestamp
|
|
106
|
+
granted_by: str
|
|
107
|
+
created_at: float
|
|
108
|
+
id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
|
109
|
+
reason: str | None = None
|
|
110
|
+
|
|
111
|
+
def to_dict(self) -> dict[str, Any]:
|
|
112
|
+
d = asdict(self)
|
|
113
|
+
if self.effect:
|
|
114
|
+
d["effect"] = self.effect.value
|
|
115
|
+
return d
|
|
116
|
+
|
|
117
|
+
|
|
95
118
|
@dataclass(frozen=True)
|
|
96
119
|
class AuditEvent:
|
|
97
120
|
timestamp: str
|
|
@@ -103,6 +126,7 @@ class AuditEvent:
|
|
|
103
126
|
decision: Decision
|
|
104
127
|
outcome: Outcome
|
|
105
128
|
approval_id: str | None = None
|
|
129
|
+
grant_id: str | None = None
|
|
106
130
|
result_summary: str | None = None
|
|
107
131
|
policy_version: str | None = None
|
|
108
132
|
manifest_version: str | None = None
|
|
@@ -118,6 +142,7 @@ class AuditEvent:
|
|
|
118
142
|
"decision": self.decision.to_dict(),
|
|
119
143
|
"outcome": self.outcome.value,
|
|
120
144
|
"approval_id": self.approval_id,
|
|
145
|
+
"grant_id": self.grant_id,
|
|
121
146
|
"result_summary": self.result_summary,
|
|
122
147
|
"policy_version": self.policy_version,
|
|
123
148
|
"manifest_version": self.manifest_version,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tollgate
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: Runtime enforcement layer for AI agent tool calls using Identity + Intent + Policy
|
|
5
5
|
Author: Tollgate Maintainers
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -77,6 +77,30 @@ Runtime enforcement layer for AI agent tool calls using **Identity + Intent + Po
|
|
|
77
77
|
|
|
78
78
|
## 🚀 v1 Integrations
|
|
79
79
|
|
|
80
|
+
### 🎟️ Session Grants
|
|
81
|
+
Grants allow you to pre-authorize specific actions for an agent session, bypassing human-in-the-loop approvals for repetitive or low-risk tasks.
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from tollgate import Grant, InMemoryGrantStore, Effect
|
|
85
|
+
|
|
86
|
+
# 1. Setup a grant store
|
|
87
|
+
grant_store = InMemoryGrantStore()
|
|
88
|
+
tower = ControlTower(..., grant_store=grant_store)
|
|
89
|
+
|
|
90
|
+
# 2. Issue a grant (e.g., after initial human approval)
|
|
91
|
+
grant = Grant(
|
|
92
|
+
agent_id="my-agent",
|
|
93
|
+
effect=Effect.WRITE,
|
|
94
|
+
tool="mcp:*", # Wildcard prefix: matches any MCP tool (e.g., "mcp:server.write")
|
|
95
|
+
action=None, # Wildcard: matches any action
|
|
96
|
+
resource_type=None,
|
|
97
|
+
expires_at=time.time() + 3600, # Valid for 1 hour
|
|
98
|
+
granted_by="admin-user",
|
|
99
|
+
created_at=time.time()
|
|
100
|
+
)
|
|
101
|
+
await grant_store.create_grant(grant)
|
|
102
|
+
```
|
|
103
|
+
|
|
80
104
|
### MCP (Model Context Protocol)
|
|
81
105
|
Wrap an MCP client to gate all tool calls:
|
|
82
106
|
```python
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
tollgate/__init__.py,sha256=
|
|
1
|
+
tollgate/__init__.py,sha256=6Q6_5rZP0-LOOX-ARz73_wL3EBjxjNnDSIMozJaf2oQ,1411
|
|
2
2
|
tollgate/approvals.py,sha256=lalvun6i5yaZ2EGZ-apQJBrLVmht5OfwgiUFsImPdYE,7814
|
|
3
3
|
tollgate/audit.py,sha256=ugMhuuLoyBNdYD2S_MjGN_ac4nHdtRz15MElqElREIs,1279
|
|
4
4
|
tollgate/exceptions.py,sha256=2yYY3esnHz26dyJlx_Cd_J64ryhexrgc4KliDmiVJSs,882
|
|
5
|
+
tollgate/grants.py,sha256=nPQ0hb2mxFDq6axzFc5mOgSHiT01SiSqhwbQBdFPkaY,3738
|
|
5
6
|
tollgate/helpers.py,sha256=ZFMi19_ogpTlV_svFtgJ9kkmA1sPte1dmDnHYVdWGyo,1657
|
|
6
7
|
tollgate/policy.py,sha256=1_6HcbdpGfZtwmgqapqeRzItc_wbEg_L-fOA-BVIWNg,6110
|
|
7
8
|
tollgate/registry.py,sha256=ENKT-GnwFq0xXZj6qNlKFhAGYsxxBwtZsAdPZp2qlHM,2031
|
|
8
|
-
tollgate/tower.py,sha256=
|
|
9
|
-
tollgate/types.py,sha256=
|
|
9
|
+
tollgate/tower.py,sha256=JI8BGSCC7u0hkxIJmtrH9jflhTKm7py44JUJoz1NJbU,8536
|
|
10
|
+
tollgate/types.py,sha256=Ks3HNawhPzEAggTCab7NLb8zl06777ehEusASJWSj_g,3811
|
|
10
11
|
tollgate/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
12
|
tollgate/integrations/mcp.py,sha256=jBy1d64Yc55fO4JSVfhKqiffVCZdBMmmNLRta9SVfDE,1750
|
|
12
13
|
tollgate/integrations/strands.py,sha256=_AWsIyva_iwLFIQeTMxxbEzLSjneo0YumWfY9oayKeM,3003
|
|
@@ -14,7 +15,7 @@ tollgate/interceptors/__init__.py,sha256=0c3MYyVKGYrBOZ1mMolgrAowrqZrULzAl0vv-s8
|
|
|
14
15
|
tollgate/interceptors/base.py,sha256=uJxHzH0eurcGEVknbk1kQkk_2u2qNtnM--ZRCp52Wyo,1390
|
|
15
16
|
tollgate/interceptors/langchain.py,sha256=_8vXCjWkRKeTlxtXm33a67Gf4po9YiHS7faThMJLohc,2963
|
|
16
17
|
tollgate/interceptors/openai.py,sha256=cHnOQ8keQJRYBm_1RpohKWk3D9Ask22hpuQghm9iahU,3337
|
|
17
|
-
tollgate-1.0.
|
|
18
|
-
tollgate-1.0.
|
|
19
|
-
tollgate-1.0.
|
|
20
|
-
tollgate-1.0.
|
|
18
|
+
tollgate-1.0.4.dist-info/METADATA,sha256=JnYhkyYWulY-WQ0cEuGfvsk6gQsDthGtvrTqCZIlAgk,7748
|
|
19
|
+
tollgate-1.0.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
20
|
+
tollgate-1.0.4.dist-info/licenses/LICENSE,sha256=EZ9SehMCkcatlggcoT7WV0tx-ku4OsAoQf9LmJZvG1g,10806
|
|
21
|
+
tollgate-1.0.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|