snowglobe 0.4.8__py3-none-any.whl → 0.4.9__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.
- snowglobe/client/src/app.py +107 -22
- snowglobe/client/src/cli.py +29 -11
- snowglobe/client/src/telemetry.py +146 -0
- {snowglobe-0.4.8.dist-info → snowglobe-0.4.9.dist-info}/METADATA +15 -1
- {snowglobe-0.4.8.dist-info → snowglobe-0.4.9.dist-info}/RECORD +9 -8
- {snowglobe-0.4.8.dist-info → snowglobe-0.4.9.dist-info}/WHEEL +0 -0
- {snowglobe-0.4.8.dist-info → snowglobe-0.4.9.dist-info}/entry_points.txt +0 -0
- {snowglobe-0.4.8.dist-info → snowglobe-0.4.9.dist-info}/licenses/LICENSE +0 -0
- {snowglobe-0.4.8.dist-info → snowglobe-0.4.9.dist-info}/top_level.txt +0 -0
snowglobe/client/src/app.py
CHANGED
@@ -14,6 +14,7 @@ from functools import wraps
|
|
14
14
|
from logging import getLogger
|
15
15
|
from typing import Dict
|
16
16
|
from urllib.parse import quote_plus
|
17
|
+
import uuid
|
17
18
|
|
18
19
|
import httpx
|
19
20
|
import uvicorn
|
@@ -21,6 +22,8 @@ from apscheduler import AsyncScheduler
|
|
21
22
|
from apscheduler.triggers.interval import IntervalTrigger
|
22
23
|
from fastapi import FastAPI, HTTPException, Request
|
23
24
|
|
25
|
+
from snowglobe.client.src.telemetry import trace_completion_fn, trace_risk_evaluation_fn
|
26
|
+
|
24
27
|
from .cli_utils import info, shutdown_manager
|
25
28
|
from .config import config, get_api_key_or_raise
|
26
29
|
from .models import CompletionFunctionOutputs, CompletionRequest, RiskEvaluationRequest
|
@@ -127,16 +130,32 @@ async def process_application_heartbeat(app_id):
|
|
127
130
|
try:
|
128
131
|
prompt = "Hello from Snowglobe!"
|
129
132
|
test_request = CompletionRequest(messages=[{"role": "user", "content": prompt}])
|
130
|
-
|
133
|
+
heartbeat_id = uuid.uuid4().hex
|
134
|
+
agent = apps.get(app_id, {})
|
135
|
+
agent_name = agent.get("name", "")
|
136
|
+
completion_fn = agent.get("completion_fn")
|
131
137
|
if not completion_fn:
|
132
138
|
LOGGER.warning(
|
133
139
|
f"No completion function found for application {app_id}. Skipping heartbeat."
|
134
140
|
)
|
135
141
|
return
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
142
|
+
|
143
|
+
@trace_completion_fn(
|
144
|
+
agent_name=agent_name,
|
145
|
+
conversation_id=heartbeat_id,
|
146
|
+
message_id=heartbeat_id,
|
147
|
+
session_id=heartbeat_id,
|
148
|
+
simulation_name=f"{agent_name} Heartbeat",
|
149
|
+
span_type="snowglobe/heartbeat",
|
150
|
+
)
|
151
|
+
async def run_completion_fn(completion_request: CompletionRequest):
|
152
|
+
if asyncio.iscoroutinefunction(completion_fn):
|
153
|
+
response = await completion_fn(completion_request)
|
154
|
+
else:
|
155
|
+
response = completion_fn(completion_request)
|
156
|
+
return response
|
157
|
+
|
158
|
+
response = await run_completion_fn(test_request)
|
140
159
|
if not isinstance(response, CompletionFunctionOutputs):
|
141
160
|
LOGGER.error(
|
142
161
|
f"Completion function for application {app_id} did not return a valid response. Expected CompletionFunctionOutputs, got {type(response)}"
|
@@ -201,19 +220,31 @@ async def process_application_heartbeat(app_id):
|
|
201
220
|
return connection_test_response.json()
|
202
221
|
|
203
222
|
|
204
|
-
async def process_risk_evaluation(test, risk_name):
|
223
|
+
async def process_risk_evaluation(test, risk_name, simulation_name, agent_name):
|
205
224
|
"""finds correct risk and calls the risk evaluation function and creates a risk evaluation for the test"""
|
206
225
|
start = time.time()
|
207
226
|
|
208
227
|
messages = await fetch_messages(test=test)
|
209
228
|
|
210
|
-
|
211
|
-
risk_evaluation = await risks[risk_name](
|
212
|
-
RiskEvaluationRequest(messages=messages)
|
213
|
-
)
|
214
|
-
else:
|
215
|
-
risk_evaluation = risks[risk_name](RiskEvaluationRequest(messages=messages))
|
229
|
+
risk_eval_req = RiskEvaluationRequest(messages=messages)
|
216
230
|
|
231
|
+
@trace_risk_evaluation_fn(
|
232
|
+
agent_name=agent_name,
|
233
|
+
conversation_id=test["conversation_id"],
|
234
|
+
message_id=test["id"],
|
235
|
+
session_id=test["conversation_id"],
|
236
|
+
simulation_name=simulation_name,
|
237
|
+
span_type=f"snowglobe/risk-evaluation/{risk_name}",
|
238
|
+
risk_name=risk_name,
|
239
|
+
)
|
240
|
+
async def run_risk_evaluation_fn(risk_evaluation_request: RiskEvaluationRequest):
|
241
|
+
if asyncio.iscoroutinefunction(risks[risk_name]):
|
242
|
+
risk_evaluation = await risks[risk_name](risk_evaluation_request)
|
243
|
+
else:
|
244
|
+
risk_evaluation = risks[risk_name](risk_evaluation_request)
|
245
|
+
return risk_evaluation
|
246
|
+
|
247
|
+
risk_evaluation = await run_risk_evaluation_fn(risk_eval_req)
|
217
248
|
LOGGER.debug(f"Risk evaluation output: {risk_evaluation}")
|
218
249
|
|
219
250
|
# Extract fields from risk_evaluation object
|
@@ -249,16 +280,33 @@ async def process_risk_evaluation(test, risk_name):
|
|
249
280
|
raise Exception("Error posting risk evaluation, task is not healthy")
|
250
281
|
|
251
282
|
|
252
|
-
async def process_test(test, completion_fn, app_id):
|
283
|
+
async def process_test(test, completion_fn, app_id, simulation_name):
|
253
284
|
"""Processes a test by converting it to OpenAI style messages and calling the completion function"""
|
254
285
|
start = time.time()
|
255
286
|
# convert test to openai style messages
|
256
287
|
messages = await fetch_messages(test=test)
|
257
288
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
289
|
+
agent = apps.get(app_id, {})
|
290
|
+
agent_name = agent.get("name", "")
|
291
|
+
|
292
|
+
completion_req = CompletionRequest(messages=messages)
|
293
|
+
|
294
|
+
@trace_completion_fn(
|
295
|
+
agent_name=agent_name,
|
296
|
+
conversation_id=test["conversation_id"],
|
297
|
+
message_id=test["id"],
|
298
|
+
session_id=test["conversation_id"],
|
299
|
+
simulation_name=simulation_name,
|
300
|
+
span_type="snowglobe/completion",
|
301
|
+
)
|
302
|
+
async def run_completion_fn(completion_request: CompletionRequest):
|
303
|
+
if asyncio.iscoroutinefunction(completion_fn):
|
304
|
+
completionOutput = await completion_fn(completion_request)
|
305
|
+
else:
|
306
|
+
completionOutput = completion_fn(completion_request)
|
307
|
+
return completionOutput
|
308
|
+
|
309
|
+
completionOutput = await run_completion_fn(completion_req)
|
262
310
|
|
263
311
|
LOGGER.debug(f"Completion output: {completionOutput}")
|
264
312
|
|
@@ -388,7 +436,11 @@ async def poll_for_completions():
|
|
388
436
|
try:
|
389
437
|
completion_request = await httpx.AsyncClient().post(
|
390
438
|
f"{config.SNOWGLOBE_CLIENT_URL}/completion",
|
391
|
-
json={
|
439
|
+
json={
|
440
|
+
"test": test,
|
441
|
+
"app_id": app_id,
|
442
|
+
"simulation_name": experiment["name"],
|
443
|
+
},
|
392
444
|
timeout=30,
|
393
445
|
)
|
394
446
|
except (httpx.ConnectError, httpx.TimeoutException) as e:
|
@@ -543,6 +595,31 @@ async def poll_for_risk_evaluations():
|
|
543
595
|
)
|
544
596
|
continue
|
545
597
|
experiment = experiment_request.json()
|
598
|
+
|
599
|
+
try:
|
600
|
+
app_request = await client.get(
|
601
|
+
f"{config.CONTROL_PLANE_URL}/api/applications/{experiment['app_id']}",
|
602
|
+
headers={"x-api-key": get_api_key_or_raise()},
|
603
|
+
)
|
604
|
+
except (httpx.ConnectError, httpx.TimeoutException) as e:
|
605
|
+
if shutdown_manager.is_shutdown_requested():
|
606
|
+
LOGGER.debug(f"HTTP error during shutdown (expected): {e}")
|
607
|
+
return
|
608
|
+
else:
|
609
|
+
LOGGER.error(
|
610
|
+
f"Connection error fetching application {experiment['app_id']}: {e}"
|
611
|
+
)
|
612
|
+
continue
|
613
|
+
|
614
|
+
app_name = experiment["app_id"]
|
615
|
+
if not app_request.is_success:
|
616
|
+
LOGGER.error(
|
617
|
+
f"Error fetching application {experiment['app_id']}: {app_request.text}"
|
618
|
+
)
|
619
|
+
else:
|
620
|
+
application = app_request.json()
|
621
|
+
app_name = application["name"]
|
622
|
+
|
546
623
|
risk_eval_count = 0
|
547
624
|
|
548
625
|
for risk_name in risks.keys():
|
@@ -608,7 +685,12 @@ async def poll_for_risk_evaluations():
|
|
608
685
|
try:
|
609
686
|
risk_eval_response = await httpx.AsyncClient().post(
|
610
687
|
f"{config.SNOWGLOBE_CLIENT_URL}/risk-evaluation",
|
611
|
-
json={
|
688
|
+
json={
|
689
|
+
"test": test,
|
690
|
+
"risk_name": risk_name,
|
691
|
+
"simulation_name": experiment["name"],
|
692
|
+
"agent_name": app_name,
|
693
|
+
},
|
612
694
|
timeout=30,
|
613
695
|
)
|
614
696
|
except (
|
@@ -686,7 +768,7 @@ async def lifespan(app: FastAPI):
|
|
686
768
|
"agent_wrapper", agent_file_path
|
687
769
|
)
|
688
770
|
agent_module = importlib.util.module_from_spec(spec)
|
689
|
-
|
771
|
+
|
690
772
|
# Add current directory to path
|
691
773
|
sys_path_backup = sys.path.copy()
|
692
774
|
current_dir = os.getcwd()
|
@@ -848,6 +930,7 @@ def create_client():
|
|
848
930
|
completion_body = await request.json()
|
849
931
|
test = completion_body.get("test")
|
850
932
|
app_id = completion_body.get("app_id")
|
933
|
+
simulation_name = completion_body.get("simulation_name")
|
851
934
|
# both are required non empty strings
|
852
935
|
if not test or not app_id:
|
853
936
|
raise HTTPException(
|
@@ -862,7 +945,7 @@ def create_client():
|
|
862
945
|
completion_fn = apps.get(app_id, {}).get("completion_fn")
|
863
946
|
LOGGER.debug(f"Received test: {test['id']}")
|
864
947
|
|
865
|
-
await process_test(test, completion_fn, app_id)
|
948
|
+
await process_test(test, completion_fn, app_id, simulation_name)
|
866
949
|
return {"status": "processed"}
|
867
950
|
|
868
951
|
@app.post("/heartbeat")
|
@@ -902,10 +985,12 @@ def create_client():
|
|
902
985
|
body = await request.json()
|
903
986
|
test = body.get("test")
|
904
987
|
risk_name = body.get("risk_name")
|
988
|
+
simulation_name = body.get("simulation_name")
|
989
|
+
agent_name = body.get("agent_name")
|
905
990
|
LOGGER.debug(f"Received risk evaluation for test: {test['id']}")
|
906
991
|
|
907
992
|
# For now, just simulate processing
|
908
|
-
await process_risk_evaluation(test, risk_name)
|
993
|
+
await process_risk_evaluation(test, risk_name, simulation_name, agent_name)
|
909
994
|
return {"status": "risk evaluation processed"}
|
910
995
|
|
911
996
|
return app
|
snowglobe/client/src/cli.py
CHANGED
@@ -6,6 +6,7 @@ import signal
|
|
6
6
|
import sys
|
7
7
|
import threading
|
8
8
|
import time
|
9
|
+
import uuid
|
9
10
|
import webbrowser
|
10
11
|
from importlib.metadata import version
|
11
12
|
from typing import Optional, Tuple
|
@@ -15,6 +16,8 @@ import uvicorn
|
|
15
16
|
from fastapi import FastAPI, Request
|
16
17
|
from fastapi.middleware.cors import CORSMiddleware
|
17
18
|
|
19
|
+
from snowglobe.client.src.telemetry import trace_completion_fn
|
20
|
+
|
18
21
|
# Import start_client lazily inside the start command to avoid config initialization
|
19
22
|
from .cli_utils import (
|
20
23
|
check_auth_status,
|
@@ -198,7 +201,7 @@ def test(
|
|
198
201
|
else:
|
199
202
|
info("Check your implementation and try again.")
|
200
203
|
docs_link(
|
201
|
-
"Troubleshooting guide", "https://
|
204
|
+
"Troubleshooting guide", "https://snowglobe.so/docs/troubleshooting"
|
202
205
|
)
|
203
206
|
raise typer.Exit(1)
|
204
207
|
|
@@ -231,7 +234,7 @@ def init(
|
|
231
234
|
if not is_auth:
|
232
235
|
error("Authentication required to initialize agents")
|
233
236
|
info("Please run 'snowglobe-connect auth' first to set up authentication")
|
234
|
-
docs_link("Setup guide", "https://
|
237
|
+
docs_link("Setup guide", "https://snowglobe.so/docs/setup")
|
235
238
|
raise typer.Exit(1)
|
236
239
|
|
237
240
|
success("Authenticated successfully")
|
@@ -256,9 +259,7 @@ def init(
|
|
256
259
|
raise typer.Exit(0)
|
257
260
|
elif selected == "new":
|
258
261
|
info("Creating new application not yet implemented in init command")
|
259
|
-
info(
|
260
|
-
"Please visit https://snowglobe.guardrails-ai.com/applications/create to create a new app"
|
261
|
-
)
|
262
|
+
info("Please visit https://snowglobe.guardrailsai.com/app to create a new app")
|
262
263
|
info("Then run this command again to select it")
|
263
264
|
raise typer.Exit(0)
|
264
265
|
|
@@ -388,10 +389,24 @@ def test_agent_wrapper(filename: str, app_id: str, app_name: str) -> Tuple[bool,
|
|
388
389
|
]
|
389
390
|
)
|
390
391
|
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
392
|
+
test_id = uuid.uuid4()
|
393
|
+
|
394
|
+
@trace_completion_fn(
|
395
|
+
agent_name=app_name,
|
396
|
+
conversation_id=test_id,
|
397
|
+
message_id=test_id,
|
398
|
+
session_id=test_id,
|
399
|
+
simulation_name=f"{app_name} CLI Test",
|
400
|
+
span_type="snowglobe/cli-test",
|
401
|
+
)
|
402
|
+
async def run_process_scenario(completion_request: CompletionRequest):
|
403
|
+
if asyncio.iscoroutinefunction(process_scenario):
|
404
|
+
response = asyncio.run(process_scenario(completion_request))
|
405
|
+
else:
|
406
|
+
response = process_scenario(completion_request)
|
407
|
+
return response
|
408
|
+
|
409
|
+
response = asyncio.run(run_process_scenario(test_request))
|
395
410
|
|
396
411
|
if hasattr(response, "response") and isinstance(response.response, str):
|
397
412
|
if response.response == "Your response here":
|
@@ -402,6 +417,7 @@ def test_agent_wrapper(filename: str, app_id: str, app_name: str) -> Tuple[bool,
|
|
402
417
|
|
403
418
|
except Exception as e:
|
404
419
|
import traceback
|
420
|
+
|
405
421
|
traceback.print_exc()
|
406
422
|
return False, f"Error: {str(e)}"
|
407
423
|
|
@@ -434,7 +450,7 @@ def enhanced_error_handler(status_code: int, operation: str = "operation") -> No
|
|
434
450
|
error("Authentication failed")
|
435
451
|
info("Your API key may be invalid or expired")
|
436
452
|
info("Run 'snowglobe-connect auth' to set up authentication")
|
437
|
-
docs_link("Authentication help", "https://
|
453
|
+
docs_link("Authentication help", "https://snowglobe.so/docs/auth")
|
438
454
|
elif status_code == 403:
|
439
455
|
error("Access forbidden")
|
440
456
|
info("You don't have permission for this operation")
|
@@ -606,6 +622,7 @@ def _create_auth_server(config_key: str, rc_path: str) -> FastAPI:
|
|
606
622
|
return {"written": True}
|
607
623
|
except Exception as e:
|
608
624
|
import traceback
|
625
|
+
|
609
626
|
traceback.print_exc()
|
610
627
|
error(f"Failed to process key configuration: {e}")
|
611
628
|
return {"error": "Failed to process key configuration request"}
|
@@ -624,7 +641,7 @@ def _show_auth_success_next_steps() -> None:
|
|
624
641
|
console.print("3. Start the client:")
|
625
642
|
console.print(" [bold green]snowglobe-connect start[/bold green]")
|
626
643
|
console.print()
|
627
|
-
docs_link("Getting started guide", "https://
|
644
|
+
docs_link("Getting started guide", "https://snowglobe.so/docs/getting-started")
|
628
645
|
|
629
646
|
|
630
647
|
def _poll_for_api_key(rc_path: str, timeout: int = 300) -> bool:
|
@@ -792,6 +809,7 @@ def start(
|
|
792
809
|
console.print()
|
793
810
|
except Exception:
|
794
811
|
import traceback
|
812
|
+
|
795
813
|
traceback.print_exc()
|
796
814
|
# Do not block startup if we cannot load agents mapping
|
797
815
|
pass
|
@@ -0,0 +1,146 @@
|
|
1
|
+
import os
|
2
|
+
from importlib.metadata import version as importlib_version
|
3
|
+
from typing import Callable
|
4
|
+
from functools import wraps
|
5
|
+
|
6
|
+
from snowglobe.client.src.models import CompletionRequest, RiskEvaluationRequest
|
7
|
+
|
8
|
+
try:
|
9
|
+
import mlflow
|
10
|
+
import mlflow.tracing
|
11
|
+
|
12
|
+
mlflow.tracing.enable()
|
13
|
+
except ImportError:
|
14
|
+
mlflow = None
|
15
|
+
|
16
|
+
SNOWGLOBE_VERSION = importlib_version("snowglobe")
|
17
|
+
|
18
|
+
|
19
|
+
def trace_completion_fn(
|
20
|
+
*,
|
21
|
+
session_id: str,
|
22
|
+
conversation_id: str,
|
23
|
+
message_id: str,
|
24
|
+
simulation_name: str,
|
25
|
+
agent_name: str,
|
26
|
+
span_type: str,
|
27
|
+
):
|
28
|
+
def trace_decorator(completion_fn: Callable):
|
29
|
+
disable_mlflow = os.getenv("SNOWGLOBE_DISABLE_MLFLOW_TRACING") or ""
|
30
|
+
if mlflow and disable_mlflow.lower() != "true":
|
31
|
+
mlflow_experiment_name = (
|
32
|
+
os.getenv("MLFLOW_EXPERIMENT_NAME") or simulation_name
|
33
|
+
)
|
34
|
+
mlflow.set_experiment(mlflow_experiment_name)
|
35
|
+
|
36
|
+
mlflow_active_model_id = os.getenv("MLFLOW_ACTIVE_MODEL_ID")
|
37
|
+
if mlflow_active_model_id:
|
38
|
+
mlflow.set_active_model(model_id=mlflow_active_model_id)
|
39
|
+
else:
|
40
|
+
mlflow.set_active_model(name=agent_name)
|
41
|
+
|
42
|
+
span_attributes = {
|
43
|
+
"snowglobe.version": SNOWGLOBE_VERSION,
|
44
|
+
"type": span_type,
|
45
|
+
"session_id": session_id,
|
46
|
+
"conversation_id": conversation_id,
|
47
|
+
"message_id": message_id,
|
48
|
+
"simulation_name": simulation_name,
|
49
|
+
"agent_name": agent_name,
|
50
|
+
}
|
51
|
+
|
52
|
+
@mlflow.trace(
|
53
|
+
name=span_type,
|
54
|
+
span_type=span_type,
|
55
|
+
attributes=span_attributes,
|
56
|
+
)
|
57
|
+
@wraps(completion_fn)
|
58
|
+
async def completion_fn_wrapper(test_request: CompletionRequest):
|
59
|
+
try:
|
60
|
+
mlflow.update_current_trace(
|
61
|
+
metadata={"mlflow.trace.session": session_id},
|
62
|
+
tags={
|
63
|
+
"session_id": session_id,
|
64
|
+
"conversation_id": conversation_id,
|
65
|
+
"message_id": message_id,
|
66
|
+
"simulation_name": simulation_name,
|
67
|
+
"agent_name": agent_name,
|
68
|
+
},
|
69
|
+
)
|
70
|
+
response = await completion_fn(test_request)
|
71
|
+
return response
|
72
|
+
except Exception as e:
|
73
|
+
raise e
|
74
|
+
|
75
|
+
return completion_fn_wrapper
|
76
|
+
else:
|
77
|
+
return completion_fn
|
78
|
+
|
79
|
+
return trace_decorator
|
80
|
+
|
81
|
+
|
82
|
+
def trace_risk_evaluation_fn(
|
83
|
+
*,
|
84
|
+
session_id: str,
|
85
|
+
conversation_id: str,
|
86
|
+
message_id: str,
|
87
|
+
simulation_name: str,
|
88
|
+
agent_name: str,
|
89
|
+
span_type: str,
|
90
|
+
risk_name,
|
91
|
+
):
|
92
|
+
def trace_decorator(risk_evaluation_fn: Callable):
|
93
|
+
disable_mlflow = os.getenv("SNOWGLOBE_DISABLE_MLFLOW_TRACING") or ""
|
94
|
+
if mlflow and disable_mlflow.lower() != "true":
|
95
|
+
mlflow_experiment_name = (
|
96
|
+
os.getenv("MLFLOW_EXPERIMENT_NAME") or simulation_name
|
97
|
+
)
|
98
|
+
mlflow.set_experiment(mlflow_experiment_name)
|
99
|
+
|
100
|
+
mlflow_active_model_id = os.getenv("MLFLOW_ACTIVE_MODEL_ID")
|
101
|
+
if mlflow_active_model_id:
|
102
|
+
mlflow.set_active_model(model_id=mlflow_active_model_id)
|
103
|
+
else:
|
104
|
+
mlflow.set_active_model(name=agent_name)
|
105
|
+
span_attributes = {
|
106
|
+
"snowglobe.version": SNOWGLOBE_VERSION,
|
107
|
+
"type": span_type,
|
108
|
+
"session_id": session_id,
|
109
|
+
"conversation_id": conversation_id,
|
110
|
+
"message_id": message_id,
|
111
|
+
"simulation_name": simulation_name,
|
112
|
+
"agent_name": agent_name,
|
113
|
+
"risk_name": risk_name,
|
114
|
+
}
|
115
|
+
|
116
|
+
@mlflow.trace(
|
117
|
+
name=span_type,
|
118
|
+
span_type=span_type,
|
119
|
+
attributes=span_attributes,
|
120
|
+
)
|
121
|
+
@wraps(risk_evaluation_fn)
|
122
|
+
async def risk_evaluation_fn_wrapper(
|
123
|
+
risk_evaluation_request: RiskEvaluationRequest,
|
124
|
+
):
|
125
|
+
try:
|
126
|
+
mlflow.update_current_trace(
|
127
|
+
metadata={"mlflow.trace.session": session_id},
|
128
|
+
tags={
|
129
|
+
"session_id": session_id,
|
130
|
+
"conversation_id": conversation_id,
|
131
|
+
"message_id": message_id,
|
132
|
+
"simulation_name": simulation_name,
|
133
|
+
"agent_name": agent_name,
|
134
|
+
"risk_name": risk_name,
|
135
|
+
},
|
136
|
+
)
|
137
|
+
response = await risk_evaluation_fn(risk_evaluation_request)
|
138
|
+
return response
|
139
|
+
except Exception as e:
|
140
|
+
raise e
|
141
|
+
|
142
|
+
return risk_evaluation_fn_wrapper
|
143
|
+
else:
|
144
|
+
return risk_evaluation_fn
|
145
|
+
|
146
|
+
return trace_decorator
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: snowglobe
|
3
|
-
Version: 0.4.
|
3
|
+
Version: 0.4.9
|
4
4
|
Summary: client server for usage with snowglobe experiments
|
5
5
|
Author-email: Guardrails AI <contact@guardrailsai.com>
|
6
6
|
License: MIT License
|
@@ -119,3 +119,17 @@ def process_scenario(request: CompletionRequest) -> CompletionFunctionOutputs:
|
|
119
119
|
)
|
120
120
|
return CompletionFunctionOutputs(response=response.choices[0].message.content)
|
121
121
|
```
|
122
|
+
|
123
|
+
## Tracing with MLflow
|
124
|
+
The Snowglobe Connect SDK has MLflow tracing built in! Simply `pip install mlflow` and the sdk will take care of the rest. Read more about MLflow's tracing capability for GenAI Apps [here](https://mlflow.org/docs/latest/genai/tracing/app-instrumentation/).
|
125
|
+
|
126
|
+
### Enhancing Snowglobe Connect SDK's Traces with Autologging
|
127
|
+
You can turn on mlflow autologging in your app to add additional context to the traces the Snowglobe Connect SDK captures. In you app's entry point simply call the appropriate autolog method for the LLM provider you're using. The below example shows how to enable this for LiteLLM:
|
128
|
+
```py
|
129
|
+
import mlflow
|
130
|
+
|
131
|
+
mlflow.litellm.autolog()
|
132
|
+
```
|
133
|
+
|
134
|
+
### Disable Snowglobe Connect SDK's MLflow Tracing
|
135
|
+
If you already use MLflow and don't want the Snowglobe Connect SDK to capture additional traces, you can disable this feature by setting the `SNOWGLOBE_DISABLE_MLFLOW_TRACING` environment variable to `true`.
|
@@ -1,15 +1,16 @@
|
|
1
1
|
snowglobe/client/__init__.py,sha256=kzp9wPUUYBXqDSKZbfmD4vrAQvrWSW5HOvtpFlEJWfs,353
|
2
|
-
snowglobe/client/src/app.py,sha256=
|
3
|
-
snowglobe/client/src/cli.py,sha256=
|
2
|
+
snowglobe/client/src/app.py,sha256=CaDtbMn6ZXooQJuaPAHOXs2r9FkFqDxTrgqoW3Rl_2I,42686
|
3
|
+
snowglobe/client/src/cli.py,sha256=I3LVWJmyvUzOQlV0gv_AeMuEazfy8GsYdH6svo7cZOU,28544
|
4
4
|
snowglobe/client/src/cli_utils.py,sha256=6C7J5gow8xveQYF4w6ewtQJKI7VvlLTx7FS_7gl7RwI,17227
|
5
5
|
snowglobe/client/src/config.py,sha256=YRx_AQEZoHaAqk6guTxynIEGV_iJ3wNNGtMmaKsYMbc,10488
|
6
6
|
snowglobe/client/src/models.py,sha256=BX310WrDN9Fd8v68me3XGL_ic1ulvjCrZyIT2ND1eUo,866
|
7
7
|
snowglobe/client/src/project_manager.py,sha256=Ze-qs4dQI2kIV-PmtWZ1b67hMUfsnsMHus90aT8HOow,9970
|
8
8
|
snowglobe/client/src/stats.py,sha256=IdaXroOZBmvLVa_p9pDE6hsxsc7-fBEDnLf8O6Ch0GA,1596
|
9
|
+
snowglobe/client/src/telemetry.py,sha256=N91Q37YfJaUYPa7BUAs_3x4LxjculwlETIKC5k1dbig,5045
|
9
10
|
snowglobe/client/src/utils.py,sha256=hHOht0hc8fv3OuPTz2Tqs639CzSAF34JTZs5ifKV6YI,3708
|
10
|
-
snowglobe-0.4.
|
11
|
-
snowglobe-0.4.
|
12
|
-
snowglobe-0.4.
|
13
|
-
snowglobe-0.4.
|
14
|
-
snowglobe-0.4.
|
15
|
-
snowglobe-0.4.
|
11
|
+
snowglobe-0.4.9.dist-info/licenses/LICENSE,sha256=S90V6iFU5ZeSg44JQYS1To3pa7ZEobrHc_t483qSKSI,1070
|
12
|
+
snowglobe-0.4.9.dist-info/METADATA,sha256=NckGusSPGxCyboKGK79F-PiNaXstK6FOxmM0p0lOB5g,5406
|
13
|
+
snowglobe-0.4.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
14
|
+
snowglobe-0.4.9.dist-info/entry_points.txt,sha256=mqx4mTwFPHttjctE2ceYTYWCCIG30Ji2C89aaCYgHcM,71
|
15
|
+
snowglobe-0.4.9.dist-info/top_level.txt,sha256=PoyYihnCBjRyjeIT19yBcE47JTe7i1OwRXvJ4d5EohM,10
|
16
|
+
snowglobe-0.4.9.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|