control-zero 0.2.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.
- control_zero/__init__.py +31 -0
- control_zero/client.py +584 -0
- control_zero/integrations/crewai/__init__.py +53 -0
- control_zero/integrations/crewai/agent.py +267 -0
- control_zero/integrations/crewai/crew.py +381 -0
- control_zero/integrations/crewai/task.py +291 -0
- control_zero/integrations/crewai/tool.py +299 -0
- control_zero/integrations/langchain/__init__.py +58 -0
- control_zero/integrations/langchain/agent.py +311 -0
- control_zero/integrations/langchain/callbacks.py +441 -0
- control_zero/integrations/langchain/chain.py +319 -0
- control_zero/integrations/langchain/graph.py +441 -0
- control_zero/integrations/langchain/tool.py +271 -0
- control_zero/llm/__init__.py +77 -0
- control_zero/llm/anthropic/__init__.py +35 -0
- control_zero/llm/anthropic/client.py +136 -0
- control_zero/llm/anthropic/messages.py +375 -0
- control_zero/llm/base.py +551 -0
- control_zero/llm/cohere/__init__.py +32 -0
- control_zero/llm/cohere/client.py +402 -0
- control_zero/llm/gemini/__init__.py +34 -0
- control_zero/llm/gemini/client.py +486 -0
- control_zero/llm/groq/__init__.py +32 -0
- control_zero/llm/groq/client.py +330 -0
- control_zero/llm/mistral/__init__.py +32 -0
- control_zero/llm/mistral/client.py +319 -0
- control_zero/llm/ollama/__init__.py +31 -0
- control_zero/llm/ollama/client.py +439 -0
- control_zero/llm/openai/__init__.py +34 -0
- control_zero/llm/openai/chat.py +331 -0
- control_zero/llm/openai/client.py +182 -0
- control_zero/logging/__init__.py +5 -0
- control_zero/logging/async_logger.py +65 -0
- control_zero/mcp/__init__.py +5 -0
- control_zero/mcp/middleware.py +148 -0
- control_zero/policy/__init__.py +5 -0
- control_zero/policy/enforcer.py +99 -0
- control_zero/secrets/__init__.py +5 -0
- control_zero/secrets/manager.py +77 -0
- control_zero/types.py +51 -0
- control_zero-0.2.0.dist-info/METADATA +216 -0
- control_zero-0.2.0.dist-info/RECORD +44 -0
- control_zero-0.2.0.dist-info/WHEEL +4 -0
- control_zero-0.2.0.dist-info/licenses/LICENSE +17 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Control Zero Ollama Governance Wrapper.
|
|
3
|
+
|
|
4
|
+
This module provides governance wrappers for Ollama local LLMs,
|
|
5
|
+
enabling policy enforcement, usage tracking, and audit logging for
|
|
6
|
+
locally-hosted open-source models.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
from control_zero import ControlZeroClient
|
|
10
|
+
from control_zero.llm.ollama import GovernedOllama
|
|
11
|
+
import ollama
|
|
12
|
+
|
|
13
|
+
# Initialize Control Zero
|
|
14
|
+
cz_client = ControlZeroClient(api_key="cz_live_xxx")
|
|
15
|
+
cz_client.initialize()
|
|
16
|
+
|
|
17
|
+
# Create governed Ollama client
|
|
18
|
+
governed = GovernedOllama(control_zero=cz_client)
|
|
19
|
+
|
|
20
|
+
# All calls are now governed
|
|
21
|
+
response = governed.chat(
|
|
22
|
+
model="llama3.2",
|
|
23
|
+
messages=[{"role": "user", "content": "Hello"}]
|
|
24
|
+
)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from control_zero.llm.ollama.client import GovernedOllama
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"GovernedOllama",
|
|
31
|
+
]
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Governed Ollama client wrapper.
|
|
3
|
+
|
|
4
|
+
Provides governance features for Ollama local LLMs including:
|
|
5
|
+
- Model access control
|
|
6
|
+
- Usage tracking
|
|
7
|
+
- PII detection and masking
|
|
8
|
+
- Audit logging
|
|
9
|
+
|
|
10
|
+
Ollama runs models locally, so there's no cost tracking,
|
|
11
|
+
but governance policies still apply for:
|
|
12
|
+
- Model allowlisting
|
|
13
|
+
- Request limits
|
|
14
|
+
- PII detection
|
|
15
|
+
- Audit logging
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import time
|
|
19
|
+
from typing import Any, Dict, Iterator, List, Optional
|
|
20
|
+
|
|
21
|
+
from control_zero.llm.base import (
|
|
22
|
+
GovernanceAction,
|
|
23
|
+
GovernedLLM,
|
|
24
|
+
GovernedChatMixin,
|
|
25
|
+
LLMGovernanceConfig,
|
|
26
|
+
LLMUsageMetrics,
|
|
27
|
+
)
|
|
28
|
+
from control_zero.policy import PolicyDeniedError
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class GovernedOllama(GovernedLLM, GovernedChatMixin):
|
|
32
|
+
"""
|
|
33
|
+
Governed wrapper for Ollama local LLMs.
|
|
34
|
+
|
|
35
|
+
Ollama runs models locally, providing privacy and cost benefits.
|
|
36
|
+
This wrapper adds governance for:
|
|
37
|
+
- Model access control (which local models can be used)
|
|
38
|
+
- Request rate limiting
|
|
39
|
+
- PII detection and masking
|
|
40
|
+
- Audit logging
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
from control_zero import ControlZeroClient
|
|
44
|
+
from control_zero.llm.ollama import GovernedOllama
|
|
45
|
+
|
|
46
|
+
cz = ControlZeroClient(api_key="...")
|
|
47
|
+
cz.initialize()
|
|
48
|
+
|
|
49
|
+
governed = GovernedOllama(
|
|
50
|
+
control_zero=cz,
|
|
51
|
+
host="http://localhost:11434",
|
|
52
|
+
config=LLMGovernanceConfig(
|
|
53
|
+
model_policy=ModelPolicy(
|
|
54
|
+
allowed_models=["llama3.2", "mistral"]
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
response = governed.chat(
|
|
60
|
+
model="llama3.2",
|
|
61
|
+
messages=[{"role": "user", "content": "Hello!"}]
|
|
62
|
+
)
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def __init__(
|
|
66
|
+
self,
|
|
67
|
+
control_zero: Any,
|
|
68
|
+
config: Optional[LLMGovernanceConfig] = None,
|
|
69
|
+
user_context: Optional[Dict[str, Any]] = None,
|
|
70
|
+
host: Optional[str] = None,
|
|
71
|
+
timeout: Optional[float] = None,
|
|
72
|
+
):
|
|
73
|
+
"""
|
|
74
|
+
Initialize a governed Ollama client.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
control_zero: Control Zero client for policy and logging
|
|
78
|
+
config: Governance configuration
|
|
79
|
+
user_context: Context about the current user
|
|
80
|
+
host: Ollama server host (default: http://localhost:11434)
|
|
81
|
+
timeout: Request timeout in seconds
|
|
82
|
+
"""
|
|
83
|
+
try:
|
|
84
|
+
import ollama
|
|
85
|
+
except ImportError:
|
|
86
|
+
raise ImportError(
|
|
87
|
+
"ollama is required for Ollama support. "
|
|
88
|
+
"Install it with: pip install ollama"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Create client with optional parameters
|
|
92
|
+
client_kwargs = {}
|
|
93
|
+
if host:
|
|
94
|
+
client_kwargs["host"] = host
|
|
95
|
+
if timeout:
|
|
96
|
+
client_kwargs["timeout"] = timeout
|
|
97
|
+
|
|
98
|
+
client = ollama.Client(**client_kwargs) if client_kwargs else ollama
|
|
99
|
+
|
|
100
|
+
super().__init__(client, control_zero, config, user_context)
|
|
101
|
+
|
|
102
|
+
self._host = host or "http://localhost:11434"
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def provider_name(self) -> str:
|
|
106
|
+
return "ollama"
|
|
107
|
+
|
|
108
|
+
def chat(
|
|
109
|
+
self,
|
|
110
|
+
*,
|
|
111
|
+
model: str,
|
|
112
|
+
messages: List[Dict[str, Any]],
|
|
113
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
114
|
+
stream: bool = False,
|
|
115
|
+
format: Optional[str] = None,
|
|
116
|
+
options: Optional[Dict[str, Any]] = None,
|
|
117
|
+
keep_alive: Optional[str] = None,
|
|
118
|
+
**kwargs,
|
|
119
|
+
) -> Any:
|
|
120
|
+
"""
|
|
121
|
+
Create a governed chat completion.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
model: Model to use (e.g., "llama3.2", "mistral")
|
|
125
|
+
messages: List of message dicts
|
|
126
|
+
tools: Tool definitions (if model supports it)
|
|
127
|
+
stream: Whether to stream
|
|
128
|
+
format: Response format (e.g., "json")
|
|
129
|
+
options: Model options (temperature, top_p, etc.)
|
|
130
|
+
keep_alive: How long to keep model loaded
|
|
131
|
+
**kwargs: Additional parameters
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Chat response or stream iterator
|
|
135
|
+
|
|
136
|
+
Raises:
|
|
137
|
+
PolicyDeniedError: If request violates policy
|
|
138
|
+
"""
|
|
139
|
+
start_time = time.time()
|
|
140
|
+
|
|
141
|
+
# Estimate tokens
|
|
142
|
+
estimated_tokens = self._estimate_message_tokens(messages)
|
|
143
|
+
|
|
144
|
+
# Prepare tools for policy check
|
|
145
|
+
tools_to_check = []
|
|
146
|
+
if tools:
|
|
147
|
+
tools_to_check = [{"name": t.get("function", {}).get("name", ""), "type": "function"} for t in tools]
|
|
148
|
+
|
|
149
|
+
# Run governance checks
|
|
150
|
+
self._pre_request_checks(
|
|
151
|
+
model=model,
|
|
152
|
+
action=GovernanceAction.CHAT_COMPLETION,
|
|
153
|
+
messages=messages,
|
|
154
|
+
functions=tools_to_check,
|
|
155
|
+
estimated_tokens=estimated_tokens,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Process messages for governance
|
|
159
|
+
processed_messages = self._process_messages_for_governance(messages)
|
|
160
|
+
|
|
161
|
+
# Filter tools
|
|
162
|
+
filtered_tools = self._filter_tools_for_governance(tools)
|
|
163
|
+
|
|
164
|
+
# Build request
|
|
165
|
+
request_kwargs = {
|
|
166
|
+
"model": model,
|
|
167
|
+
"messages": processed_messages,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if filtered_tools:
|
|
171
|
+
request_kwargs["tools"] = filtered_tools
|
|
172
|
+
if format:
|
|
173
|
+
request_kwargs["format"] = format
|
|
174
|
+
if options:
|
|
175
|
+
request_kwargs["options"] = options
|
|
176
|
+
if keep_alive:
|
|
177
|
+
request_kwargs["keep_alive"] = keep_alive
|
|
178
|
+
|
|
179
|
+
request_kwargs.update(kwargs)
|
|
180
|
+
|
|
181
|
+
# Handle streaming
|
|
182
|
+
if stream:
|
|
183
|
+
return self._create_stream(request_kwargs, start_time, model)
|
|
184
|
+
|
|
185
|
+
# Make API call
|
|
186
|
+
try:
|
|
187
|
+
response = self._client.chat(**request_kwargs)
|
|
188
|
+
latency_ms = int((time.time() - start_time) * 1000)
|
|
189
|
+
|
|
190
|
+
# Extract metrics (Ollama provides some usage info)
|
|
191
|
+
prompt_tokens = response.get("prompt_eval_count", estimated_tokens)
|
|
192
|
+
output_tokens = response.get("eval_count", 0)
|
|
193
|
+
total_tokens = prompt_tokens + output_tokens
|
|
194
|
+
|
|
195
|
+
# Count tool calls
|
|
196
|
+
tool_call_count = 0
|
|
197
|
+
message = response.get("message", {})
|
|
198
|
+
if message.get("tool_calls"):
|
|
199
|
+
tool_call_count = len(message["tool_calls"])
|
|
200
|
+
|
|
201
|
+
metrics = LLMUsageMetrics(
|
|
202
|
+
provider="ollama",
|
|
203
|
+
model=model,
|
|
204
|
+
action=GovernanceAction.CHAT_COMPLETION,
|
|
205
|
+
input_tokens=prompt_tokens,
|
|
206
|
+
output_tokens=output_tokens,
|
|
207
|
+
total_tokens=total_tokens,
|
|
208
|
+
latency_ms=latency_ms,
|
|
209
|
+
estimated_cost=0.0, # Local models have no API cost
|
|
210
|
+
function_calls=tool_call_count,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
self._post_request_update(metrics)
|
|
214
|
+
self._log_request(model, GovernanceAction.CHAT_COMPLETION, metrics)
|
|
215
|
+
|
|
216
|
+
return response
|
|
217
|
+
|
|
218
|
+
except PolicyDeniedError:
|
|
219
|
+
raise
|
|
220
|
+
except Exception as e:
|
|
221
|
+
latency_ms = int((time.time() - start_time) * 1000)
|
|
222
|
+
metrics = LLMUsageMetrics(
|
|
223
|
+
provider="ollama",
|
|
224
|
+
model=model,
|
|
225
|
+
action=GovernanceAction.CHAT_COMPLETION,
|
|
226
|
+
latency_ms=latency_ms,
|
|
227
|
+
)
|
|
228
|
+
self._log_request(
|
|
229
|
+
model, GovernanceAction.CHAT_COMPLETION, metrics,
|
|
230
|
+
status="error", error=str(e)
|
|
231
|
+
)
|
|
232
|
+
raise
|
|
233
|
+
|
|
234
|
+
def _create_stream(
|
|
235
|
+
self,
|
|
236
|
+
request_kwargs: Dict[str, Any],
|
|
237
|
+
start_time: float,
|
|
238
|
+
model: str,
|
|
239
|
+
) -> Iterator[Any]:
|
|
240
|
+
"""Create a governed streaming response."""
|
|
241
|
+
request_kwargs["stream"] = True
|
|
242
|
+
total_tokens = 0
|
|
243
|
+
|
|
244
|
+
try:
|
|
245
|
+
stream = self._client.chat(**request_kwargs)
|
|
246
|
+
|
|
247
|
+
for chunk in stream:
|
|
248
|
+
total_tokens += 1
|
|
249
|
+
yield chunk
|
|
250
|
+
|
|
251
|
+
latency_ms = int((time.time() - start_time) * 1000)
|
|
252
|
+
|
|
253
|
+
metrics = LLMUsageMetrics(
|
|
254
|
+
provider="ollama",
|
|
255
|
+
model=model,
|
|
256
|
+
action=GovernanceAction.CHAT_COMPLETION,
|
|
257
|
+
total_tokens=total_tokens,
|
|
258
|
+
latency_ms=latency_ms,
|
|
259
|
+
estimated_cost=0.0,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
self._post_request_update(metrics)
|
|
263
|
+
self._log_request(model, GovernanceAction.CHAT_COMPLETION, metrics)
|
|
264
|
+
|
|
265
|
+
except Exception as e:
|
|
266
|
+
latency_ms = int((time.time() - start_time) * 1000)
|
|
267
|
+
metrics = LLMUsageMetrics(
|
|
268
|
+
provider="ollama",
|
|
269
|
+
model=model,
|
|
270
|
+
action=GovernanceAction.CHAT_COMPLETION,
|
|
271
|
+
latency_ms=latency_ms,
|
|
272
|
+
)
|
|
273
|
+
self._log_request(
|
|
274
|
+
model, GovernanceAction.CHAT_COMPLETION, metrics,
|
|
275
|
+
status="error", error=str(e)
|
|
276
|
+
)
|
|
277
|
+
raise
|
|
278
|
+
|
|
279
|
+
def generate(
|
|
280
|
+
self,
|
|
281
|
+
*,
|
|
282
|
+
model: str,
|
|
283
|
+
prompt: str,
|
|
284
|
+
stream: bool = False,
|
|
285
|
+
format: Optional[str] = None,
|
|
286
|
+
options: Optional[Dict[str, Any]] = None,
|
|
287
|
+
system: Optional[str] = None,
|
|
288
|
+
**kwargs,
|
|
289
|
+
) -> Any:
|
|
290
|
+
"""
|
|
291
|
+
Generate text with governance (legacy API).
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
model: Model to use
|
|
295
|
+
prompt: Input prompt
|
|
296
|
+
stream: Whether to stream
|
|
297
|
+
format: Response format
|
|
298
|
+
options: Model options
|
|
299
|
+
system: System prompt
|
|
300
|
+
**kwargs: Additional parameters
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
Generate response or stream iterator
|
|
304
|
+
"""
|
|
305
|
+
start_time = time.time()
|
|
306
|
+
|
|
307
|
+
# Governance checks
|
|
308
|
+
messages = [{"role": "user", "content": prompt}]
|
|
309
|
+
if system:
|
|
310
|
+
messages.insert(0, {"role": "system", "content": system})
|
|
311
|
+
|
|
312
|
+
estimated_tokens = len(prompt) // 4
|
|
313
|
+
|
|
314
|
+
self._pre_request_checks(
|
|
315
|
+
model=model,
|
|
316
|
+
action=GovernanceAction.TEXT_COMPLETION,
|
|
317
|
+
messages=messages,
|
|
318
|
+
estimated_tokens=estimated_tokens,
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
# Process prompt for PII
|
|
322
|
+
processed_prompt = prompt
|
|
323
|
+
if self._config.content_policy.enable_pii_detection:
|
|
324
|
+
if self._config.content_policy.pii_action == "mask":
|
|
325
|
+
processed_prompt = self._mask_pii(prompt)
|
|
326
|
+
|
|
327
|
+
request_kwargs = {
|
|
328
|
+
"model": model,
|
|
329
|
+
"prompt": processed_prompt,
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if format:
|
|
333
|
+
request_kwargs["format"] = format
|
|
334
|
+
if options:
|
|
335
|
+
request_kwargs["options"] = options
|
|
336
|
+
if system:
|
|
337
|
+
request_kwargs["system"] = system
|
|
338
|
+
if stream:
|
|
339
|
+
request_kwargs["stream"] = True
|
|
340
|
+
|
|
341
|
+
request_kwargs.update(kwargs)
|
|
342
|
+
|
|
343
|
+
try:
|
|
344
|
+
response = self._client.generate(**request_kwargs)
|
|
345
|
+
latency_ms = int((time.time() - start_time) * 1000)
|
|
346
|
+
|
|
347
|
+
metrics = LLMUsageMetrics(
|
|
348
|
+
provider="ollama",
|
|
349
|
+
model=model,
|
|
350
|
+
action=GovernanceAction.TEXT_COMPLETION,
|
|
351
|
+
input_tokens=response.get("prompt_eval_count", estimated_tokens),
|
|
352
|
+
output_tokens=response.get("eval_count", 0),
|
|
353
|
+
latency_ms=latency_ms,
|
|
354
|
+
estimated_cost=0.0,
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
self._post_request_update(metrics)
|
|
358
|
+
self._log_request(model, GovernanceAction.TEXT_COMPLETION, metrics)
|
|
359
|
+
|
|
360
|
+
return response
|
|
361
|
+
|
|
362
|
+
except Exception as e:
|
|
363
|
+
latency_ms = int((time.time() - start_time) * 1000)
|
|
364
|
+
metrics = LLMUsageMetrics(
|
|
365
|
+
provider="ollama",
|
|
366
|
+
model=model,
|
|
367
|
+
action=GovernanceAction.TEXT_COMPLETION,
|
|
368
|
+
latency_ms=latency_ms,
|
|
369
|
+
)
|
|
370
|
+
self._log_request(
|
|
371
|
+
model, GovernanceAction.TEXT_COMPLETION, metrics,
|
|
372
|
+
status="error", error=str(e)
|
|
373
|
+
)
|
|
374
|
+
raise
|
|
375
|
+
|
|
376
|
+
def list(self) -> Any:
|
|
377
|
+
"""List available models (pass-through)."""
|
|
378
|
+
return self._client.list()
|
|
379
|
+
|
|
380
|
+
def show(self, model: str) -> Any:
|
|
381
|
+
"""Show model info (pass-through)."""
|
|
382
|
+
return self._client.show(model)
|
|
383
|
+
|
|
384
|
+
def pull(self, model: str, **kwargs) -> Any:
|
|
385
|
+
"""Pull a model (pass-through)."""
|
|
386
|
+
return self._client.pull(model, **kwargs)
|
|
387
|
+
|
|
388
|
+
def _filter_tools_for_governance(
|
|
389
|
+
self,
|
|
390
|
+
tools: Optional[List[Dict[str, Any]]],
|
|
391
|
+
) -> Optional[List[Dict[str, Any]]]:
|
|
392
|
+
"""Filter tools according to governance policies."""
|
|
393
|
+
if not tools:
|
|
394
|
+
return tools
|
|
395
|
+
|
|
396
|
+
policy = self._config.function_policy
|
|
397
|
+
|
|
398
|
+
if not policy.allowed_functions and not policy.denied_functions:
|
|
399
|
+
return tools
|
|
400
|
+
|
|
401
|
+
filtered = []
|
|
402
|
+
for tool in tools:
|
|
403
|
+
tool_name = tool.get("function", {}).get("name", "")
|
|
404
|
+
|
|
405
|
+
if policy.denied_functions:
|
|
406
|
+
denied = any(d.lower() in tool_name.lower() for d in policy.denied_functions)
|
|
407
|
+
if denied:
|
|
408
|
+
continue
|
|
409
|
+
|
|
410
|
+
if policy.allowed_functions:
|
|
411
|
+
allowed = any(a.lower() in tool_name.lower() for a in policy.allowed_functions)
|
|
412
|
+
if not allowed:
|
|
413
|
+
continue
|
|
414
|
+
|
|
415
|
+
filtered.append(tool)
|
|
416
|
+
|
|
417
|
+
return filtered if filtered else None
|
|
418
|
+
|
|
419
|
+
def _estimate_message_tokens(self, messages: List[Dict[str, Any]]) -> int:
|
|
420
|
+
"""Estimate token count for messages."""
|
|
421
|
+
total_chars = sum(len(str(m.get("content", ""))) for m in messages)
|
|
422
|
+
return max(1, total_chars // 4)
|
|
423
|
+
|
|
424
|
+
def with_user_context(self, user_context: Dict[str, Any]) -> "GovernedOllama":
|
|
425
|
+
merged_context = {**self._user_context, **user_context}
|
|
426
|
+
return GovernedOllama(
|
|
427
|
+
control_zero=self._cz,
|
|
428
|
+
config=self._config,
|
|
429
|
+
user_context=merged_context,
|
|
430
|
+
host=self._host,
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
def with_config(self, config: LLMGovernanceConfig) -> "GovernedOllama":
|
|
434
|
+
return GovernedOllama(
|
|
435
|
+
control_zero=self._cz,
|
|
436
|
+
config=config,
|
|
437
|
+
user_context=self._user_context,
|
|
438
|
+
host=self._host,
|
|
439
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Control Zero OpenAI Governance Wrapper.
|
|
3
|
+
|
|
4
|
+
This module provides governance wrappers for the OpenAI Python SDK,
|
|
5
|
+
enabling policy enforcement, cost tracking, and audit logging for
|
|
6
|
+
all OpenAI API calls.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
from control_zero import ControlZeroClient
|
|
10
|
+
from control_zero.llm.openai import GovernedOpenAI
|
|
11
|
+
from openai import OpenAI
|
|
12
|
+
|
|
13
|
+
# Initialize Control Zero
|
|
14
|
+
cz_client = ControlZeroClient(api_key="cz_live_xxx")
|
|
15
|
+
cz_client.initialize()
|
|
16
|
+
|
|
17
|
+
# Wrap OpenAI client with governance
|
|
18
|
+
openai_client = OpenAI()
|
|
19
|
+
governed = GovernedOpenAI(client=openai_client, control_zero=cz_client)
|
|
20
|
+
|
|
21
|
+
# All calls are now governed
|
|
22
|
+
response = governed.chat.completions.create(
|
|
23
|
+
model="gpt-4",
|
|
24
|
+
messages=[{"role": "user", "content": "Hello"}]
|
|
25
|
+
)
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from control_zero.llm.openai.client import GovernedOpenAI
|
|
29
|
+
from control_zero.llm.openai.chat import GovernedChatCompletions
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"GovernedOpenAI",
|
|
33
|
+
"GovernedChatCompletions",
|
|
34
|
+
]
|