variably-sdk 2.1.0__tar.gz → 2.3.0__tar.gz
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.
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/PKG-INFO +114 -1
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/README.md +113 -0
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/pyproject.toml +1 -1
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/src/variably/client.py +11 -4
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/src/variably/http_client.py +7 -5
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/src/variably/version.py +1 -1
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/src/variably_sdk.egg-info/PKG-INFO +114 -1
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/setup.cfg +0 -0
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/setup.py +0 -0
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/src/variably/__init__.py +0 -0
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/src/variably/cache.py +0 -0
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/src/variably/config.py +0 -0
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/src/variably/errors.py +0 -0
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/src/variably/logger.py +0 -0
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/src/variably/types.py +0 -0
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/src/variably_sdk.egg-info/SOURCES.txt +0 -0
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/src/variably_sdk.egg-info/dependency_links.txt +0 -0
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/src/variably_sdk.egg-info/requires.txt +0 -0
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/src/variably_sdk.egg-info/top_level.txt +0 -0
- {variably_sdk-2.1.0 → variably_sdk-2.3.0}/tests/test_streaming.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: variably-sdk
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Official Python SDK for Variably feature flags, LLM experimentation, and prompt optimization platform
|
|
5
5
|
Author: Variably
|
|
6
6
|
Author-email: Variably <support@variably.com>
|
|
@@ -187,6 +187,114 @@ if meta:
|
|
|
187
187
|
print(f"Tokens: {meta.token_usage}")
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
+
### Context-Aware Evaluation (Better RAG Quality) — v2.2.0+
|
|
191
|
+
|
|
192
|
+
For RAG chatbots, passing conversation history and retrieved chunks enables **groundedness scoring, hallucination detection, and conversational coherence** — dimensions that are impossible to evaluate in isolation.
|
|
193
|
+
|
|
194
|
+
The `evaluation_context` parameter is **not sent to the LLM** — it's only used by Variably's evaluator for richer scoring.
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
# Step 1: Collect conversation history from your session
|
|
198
|
+
workflow_history = [
|
|
199
|
+
{"role": "user", "content": "What causes diabetes?"},
|
|
200
|
+
{"role": "assistant", "content": "Key factors include genetics, diet..."},
|
|
201
|
+
{"role": "user", "content": "What about potatoes?"},
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
# Step 2: Collect retrieved RAG chunks (after your retrieval step)
|
|
205
|
+
reference_materials = [
|
|
206
|
+
{
|
|
207
|
+
"id": "chunk-001",
|
|
208
|
+
"content": "Unhealthy diets high in refined sugars, fats...",
|
|
209
|
+
"source": "Kenya National Clinical Guidelines",
|
|
210
|
+
"type": "chunk",
|
|
211
|
+
"relevance_score": 0.89,
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"id": "chunk-002",
|
|
215
|
+
"content": "Modifiable risk factors include obesity...",
|
|
216
|
+
"source": "Kenya National Clinical Guidelines",
|
|
217
|
+
"type": "chunk",
|
|
218
|
+
"relevance_score": 0.82,
|
|
219
|
+
},
|
|
220
|
+
]
|
|
221
|
+
|
|
222
|
+
# Step 3: Pass evaluation_context in your evaluate call
|
|
223
|
+
response = client.evaluate_prompt(
|
|
224
|
+
experiment_key="rag-prompt-experiment",
|
|
225
|
+
user_context={"user_id": "user-123"},
|
|
226
|
+
input_variables={"query": "What about potatoes?", "context": context_text},
|
|
227
|
+
evaluation_mode="full",
|
|
228
|
+
evaluation_context={
|
|
229
|
+
"reference_materials": reference_materials,
|
|
230
|
+
"workflow_history": workflow_history,
|
|
231
|
+
"retrieval_query": "potato consumption glycemic index diabetes risk",
|
|
232
|
+
},
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Same works with streaming
|
|
236
|
+
stream = client.evaluate_prompt_stream(
|
|
237
|
+
experiment_key="rag-prompt-experiment",
|
|
238
|
+
user_context={"user_id": "user-123"},
|
|
239
|
+
input_variables={"query": "What about potatoes?", "context": context_text},
|
|
240
|
+
evaluation_context={
|
|
241
|
+
"reference_materials": reference_materials,
|
|
242
|
+
"workflow_history": workflow_history,
|
|
243
|
+
},
|
|
244
|
+
)
|
|
245
|
+
for token in stream:
|
|
246
|
+
print(token, end="", flush=True)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
**What this enables:**
|
|
250
|
+
|
|
251
|
+
| Dimension | Description | Requires |
|
|
252
|
+
|-----------|-------------|----------|
|
|
253
|
+
| `faithfulness` | % of claims grounded in retrieved chunks | `reference_materials` |
|
|
254
|
+
| `hallucination_rate` | % of claims with no source in context | `reference_materials` |
|
|
255
|
+
| `context_utilization` | % of relevant chunks actually used | `reference_materials` |
|
|
256
|
+
| `attribution_accuracy` | Do citations map to correct chunks? | `reference_materials` |
|
|
257
|
+
| `conversation_consistency` | No contradictions with prior turns | `workflow_history` |
|
|
258
|
+
| `context_retention` | Maintains topic awareness across turns | `workflow_history` |
|
|
259
|
+
| `transparency` | Discloses when going beyond source material | `reference_materials` |
|
|
260
|
+
|
|
261
|
+
**BYOR mode** also supports `evaluation_context` — pass it in `submit_response()`:
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
result = client.submit_response(
|
|
265
|
+
experiment_key="my-experiment",
|
|
266
|
+
variant_key=variant.variant_key,
|
|
267
|
+
executed_prompt=prompt,
|
|
268
|
+
response=llm_response,
|
|
269
|
+
user_context=user_context,
|
|
270
|
+
input_variables=input_variables,
|
|
271
|
+
provider=variant.provider,
|
|
272
|
+
model=variant.model,
|
|
273
|
+
latency_ms=latency,
|
|
274
|
+
evaluation_context={
|
|
275
|
+
"reference_materials": reference_materials,
|
|
276
|
+
"workflow_history": workflow_history,
|
|
277
|
+
},
|
|
278
|
+
)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
#### evaluation_context Schema
|
|
282
|
+
|
|
283
|
+
| Field | Type | Description |
|
|
284
|
+
|-------|------|-------------|
|
|
285
|
+
| `reference_materials` | `list[dict]` | RAG chunks / source documents for groundedness scoring |
|
|
286
|
+
| `reference_materials[].id` | `str` | Unique chunk identifier |
|
|
287
|
+
| `reference_materials[].content` | `str` | Chunk text content |
|
|
288
|
+
| `reference_materials[].source` | `str` (optional) | Source document URL or name |
|
|
289
|
+
| `reference_materials[].type` | `str` (optional) | e.g. `"chunk"`, `"document"` |
|
|
290
|
+
| `reference_materials[].relevance_score` | `float` (optional) | Retriever similarity score |
|
|
291
|
+
| `workflow_history` | `list[dict]` | Conversation turns for coherence scoring |
|
|
292
|
+
| `workflow_history[].role` | `str` | `"user"` or `"assistant"` |
|
|
293
|
+
| `workflow_history[].content` | `str` | Message content |
|
|
294
|
+
| `retrieval_query` | `str` (optional) | The rewritten query sent to the retriever |
|
|
295
|
+
|
|
296
|
+
See [Context-Aware RAG Evaluation](../../docs/concepts/context-aware-rag-evaluation.md) for the full concept doc with architecture diagrams and integration examples.
|
|
297
|
+
|
|
190
298
|
#### Integration with LangGraph / FastAPI streaming
|
|
191
299
|
|
|
192
300
|
```python
|
|
@@ -240,6 +348,11 @@ Content-Type: application/json
|
|
|
240
348
|
},
|
|
241
349
|
"input_variables": {
|
|
242
350
|
"query": "What are the symptoms of Type 2 diabetes?"
|
|
351
|
+
},
|
|
352
|
+
"evaluation_context": {
|
|
353
|
+
"reference_materials": [{"id": "chunk-1", "content": "...", "source": "...", "type": "chunk"}],
|
|
354
|
+
"workflow_history": [{"role": "user", "content": "..."}],
|
|
355
|
+
"retrieval_query": "diabetes symptoms type 2"
|
|
243
356
|
}
|
|
244
357
|
}
|
|
245
358
|
```
|
|
@@ -142,6 +142,114 @@ if meta:
|
|
|
142
142
|
print(f"Tokens: {meta.token_usage}")
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
+
### Context-Aware Evaluation (Better RAG Quality) — v2.2.0+
|
|
146
|
+
|
|
147
|
+
For RAG chatbots, passing conversation history and retrieved chunks enables **groundedness scoring, hallucination detection, and conversational coherence** — dimensions that are impossible to evaluate in isolation.
|
|
148
|
+
|
|
149
|
+
The `evaluation_context` parameter is **not sent to the LLM** — it's only used by Variably's evaluator for richer scoring.
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
# Step 1: Collect conversation history from your session
|
|
153
|
+
workflow_history = [
|
|
154
|
+
{"role": "user", "content": "What causes diabetes?"},
|
|
155
|
+
{"role": "assistant", "content": "Key factors include genetics, diet..."},
|
|
156
|
+
{"role": "user", "content": "What about potatoes?"},
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
# Step 2: Collect retrieved RAG chunks (after your retrieval step)
|
|
160
|
+
reference_materials = [
|
|
161
|
+
{
|
|
162
|
+
"id": "chunk-001",
|
|
163
|
+
"content": "Unhealthy diets high in refined sugars, fats...",
|
|
164
|
+
"source": "Kenya National Clinical Guidelines",
|
|
165
|
+
"type": "chunk",
|
|
166
|
+
"relevance_score": 0.89,
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"id": "chunk-002",
|
|
170
|
+
"content": "Modifiable risk factors include obesity...",
|
|
171
|
+
"source": "Kenya National Clinical Guidelines",
|
|
172
|
+
"type": "chunk",
|
|
173
|
+
"relevance_score": 0.82,
|
|
174
|
+
},
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
# Step 3: Pass evaluation_context in your evaluate call
|
|
178
|
+
response = client.evaluate_prompt(
|
|
179
|
+
experiment_key="rag-prompt-experiment",
|
|
180
|
+
user_context={"user_id": "user-123"},
|
|
181
|
+
input_variables={"query": "What about potatoes?", "context": context_text},
|
|
182
|
+
evaluation_mode="full",
|
|
183
|
+
evaluation_context={
|
|
184
|
+
"reference_materials": reference_materials,
|
|
185
|
+
"workflow_history": workflow_history,
|
|
186
|
+
"retrieval_query": "potato consumption glycemic index diabetes risk",
|
|
187
|
+
},
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
# Same works with streaming
|
|
191
|
+
stream = client.evaluate_prompt_stream(
|
|
192
|
+
experiment_key="rag-prompt-experiment",
|
|
193
|
+
user_context={"user_id": "user-123"},
|
|
194
|
+
input_variables={"query": "What about potatoes?", "context": context_text},
|
|
195
|
+
evaluation_context={
|
|
196
|
+
"reference_materials": reference_materials,
|
|
197
|
+
"workflow_history": workflow_history,
|
|
198
|
+
},
|
|
199
|
+
)
|
|
200
|
+
for token in stream:
|
|
201
|
+
print(token, end="", flush=True)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**What this enables:**
|
|
205
|
+
|
|
206
|
+
| Dimension | Description | Requires |
|
|
207
|
+
|-----------|-------------|----------|
|
|
208
|
+
| `faithfulness` | % of claims grounded in retrieved chunks | `reference_materials` |
|
|
209
|
+
| `hallucination_rate` | % of claims with no source in context | `reference_materials` |
|
|
210
|
+
| `context_utilization` | % of relevant chunks actually used | `reference_materials` |
|
|
211
|
+
| `attribution_accuracy` | Do citations map to correct chunks? | `reference_materials` |
|
|
212
|
+
| `conversation_consistency` | No contradictions with prior turns | `workflow_history` |
|
|
213
|
+
| `context_retention` | Maintains topic awareness across turns | `workflow_history` |
|
|
214
|
+
| `transparency` | Discloses when going beyond source material | `reference_materials` |
|
|
215
|
+
|
|
216
|
+
**BYOR mode** also supports `evaluation_context` — pass it in `submit_response()`:
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
result = client.submit_response(
|
|
220
|
+
experiment_key="my-experiment",
|
|
221
|
+
variant_key=variant.variant_key,
|
|
222
|
+
executed_prompt=prompt,
|
|
223
|
+
response=llm_response,
|
|
224
|
+
user_context=user_context,
|
|
225
|
+
input_variables=input_variables,
|
|
226
|
+
provider=variant.provider,
|
|
227
|
+
model=variant.model,
|
|
228
|
+
latency_ms=latency,
|
|
229
|
+
evaluation_context={
|
|
230
|
+
"reference_materials": reference_materials,
|
|
231
|
+
"workflow_history": workflow_history,
|
|
232
|
+
},
|
|
233
|
+
)
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
#### evaluation_context Schema
|
|
237
|
+
|
|
238
|
+
| Field | Type | Description |
|
|
239
|
+
|-------|------|-------------|
|
|
240
|
+
| `reference_materials` | `list[dict]` | RAG chunks / source documents for groundedness scoring |
|
|
241
|
+
| `reference_materials[].id` | `str` | Unique chunk identifier |
|
|
242
|
+
| `reference_materials[].content` | `str` | Chunk text content |
|
|
243
|
+
| `reference_materials[].source` | `str` (optional) | Source document URL or name |
|
|
244
|
+
| `reference_materials[].type` | `str` (optional) | e.g. `"chunk"`, `"document"` |
|
|
245
|
+
| `reference_materials[].relevance_score` | `float` (optional) | Retriever similarity score |
|
|
246
|
+
| `workflow_history` | `list[dict]` | Conversation turns for coherence scoring |
|
|
247
|
+
| `workflow_history[].role` | `str` | `"user"` or `"assistant"` |
|
|
248
|
+
| `workflow_history[].content` | `str` | Message content |
|
|
249
|
+
| `retrieval_query` | `str` (optional) | The rewritten query sent to the retriever |
|
|
250
|
+
|
|
251
|
+
See [Context-Aware RAG Evaluation](../../docs/concepts/context-aware-rag-evaluation.md) for the full concept doc with architecture diagrams and integration examples.
|
|
252
|
+
|
|
145
253
|
#### Integration with LangGraph / FastAPI streaming
|
|
146
254
|
|
|
147
255
|
```python
|
|
@@ -195,6 +303,11 @@ Content-Type: application/json
|
|
|
195
303
|
},
|
|
196
304
|
"input_variables": {
|
|
197
305
|
"query": "What are the symptoms of Type 2 diabetes?"
|
|
306
|
+
},
|
|
307
|
+
"evaluation_context": {
|
|
308
|
+
"reference_materials": [{"id": "chunk-1", "content": "...", "source": "...", "type": "chunk"}],
|
|
309
|
+
"workflow_history": [{"role": "user", "content": "..."}],
|
|
310
|
+
"retrieval_query": "diabetes symptoms type 2"
|
|
198
311
|
}
|
|
199
312
|
}
|
|
200
313
|
```
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "variably-sdk"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.3.0"
|
|
8
8
|
description = "Official Python SDK for Variably feature flags, LLM experimentation, and prompt optimization platform"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.7"
|
|
@@ -480,7 +480,8 @@ class VariablyClient:
|
|
|
480
480
|
experiment_key: str,
|
|
481
481
|
user_context: UserContext,
|
|
482
482
|
input_variables: Dict[str, Any],
|
|
483
|
-
|
|
483
|
+
evaluation_context: Optional[Dict[str, Any]] = None,
|
|
484
|
+
**kwargs,
|
|
484
485
|
) -> EvaluateResponse:
|
|
485
486
|
"""Managed execution: Variably selects variant, calls LLM, evaluates."""
|
|
486
487
|
if not experiment_key or not isinstance(experiment_key, str):
|
|
@@ -493,7 +494,8 @@ class VariablyClient:
|
|
|
493
494
|
)
|
|
494
495
|
|
|
495
496
|
response = self.http_client.evaluate_prompt(
|
|
496
|
-
experiment_key, dict(user_context), input_variables,
|
|
497
|
+
experiment_key, dict(user_context), input_variables,
|
|
498
|
+
evaluation_context=evaluation_context,
|
|
497
499
|
)
|
|
498
500
|
|
|
499
501
|
return EvaluateResponse(
|
|
@@ -513,7 +515,8 @@ class VariablyClient:
|
|
|
513
515
|
experiment_key: str,
|
|
514
516
|
user_context: UserContext,
|
|
515
517
|
input_variables: Dict[str, Any],
|
|
516
|
-
|
|
518
|
+
evaluation_context: Optional[Dict[str, Any]] = None,
|
|
519
|
+
**kwargs,
|
|
517
520
|
) -> StreamingEvaluateResponse:
|
|
518
521
|
"""Managed execution with streaming. Yields token strings.
|
|
519
522
|
|
|
@@ -534,7 +537,8 @@ class VariablyClient:
|
|
|
534
537
|
)
|
|
535
538
|
|
|
536
539
|
raw_response = self.http_client.evaluate_prompt_stream(
|
|
537
|
-
experiment_key, dict(user_context), input_variables,
|
|
540
|
+
experiment_key, dict(user_context), input_variables,
|
|
541
|
+
evaluation_context=evaluation_context,
|
|
538
542
|
)
|
|
539
543
|
|
|
540
544
|
return StreamingEvaluateResponse(raw_response)
|
|
@@ -552,6 +556,7 @@ class VariablyClient:
|
|
|
552
556
|
latency_ms: Optional[int] = None,
|
|
553
557
|
prompt_tokens: Optional[int] = None,
|
|
554
558
|
completion_tokens: Optional[int] = None,
|
|
559
|
+
evaluation_context: Optional[Dict[str, Any]] = None,
|
|
555
560
|
) -> SubmitResponseResult:
|
|
556
561
|
"""Submit your own LLM response for 41-dimensional evaluation (BYOR step 3)."""
|
|
557
562
|
if not experiment_key or not isinstance(experiment_key, str):
|
|
@@ -583,6 +588,8 @@ class VariablyClient:
|
|
|
583
588
|
data["prompt_tokens"] = prompt_tokens
|
|
584
589
|
if completion_tokens is not None:
|
|
585
590
|
data["completion_tokens"] = completion_tokens
|
|
591
|
+
if evaluation_context:
|
|
592
|
+
data["evaluation_context"] = evaluation_context
|
|
586
593
|
|
|
587
594
|
result = self.http_client.submit_response(data)
|
|
588
595
|
|
|
@@ -60,7 +60,7 @@ class HTTPClient:
|
|
|
60
60
|
session.headers.update({
|
|
61
61
|
"Content-Type": "application/json",
|
|
62
62
|
"X-API-Key": self.config.api_key,
|
|
63
|
-
"User-Agent": "Variably-Python-SDK/2.
|
|
63
|
+
"User-Agent": "Variably-Python-SDK/2.3.0",
|
|
64
64
|
})
|
|
65
65
|
|
|
66
66
|
return session
|
|
@@ -185,15 +185,16 @@ class HTTPClient:
|
|
|
185
185
|
def evaluate_prompt(
|
|
186
186
|
self, experiment_key: str, user_context: Dict[str, Any],
|
|
187
187
|
input_variables: Dict[str, Any],
|
|
188
|
-
|
|
188
|
+
evaluation_context: Optional[Dict[str, Any]] = None,
|
|
189
189
|
) -> Dict[str, Any]:
|
|
190
190
|
"""Managed execution: select variant, call LLM, evaluate."""
|
|
191
191
|
request_data = {
|
|
192
192
|
"experiment_key": experiment_key,
|
|
193
193
|
"user_context": self._convert_user_context(user_context),
|
|
194
194
|
"input_variables": input_variables,
|
|
195
|
-
"evaluation_mode": evaluation_mode,
|
|
196
195
|
}
|
|
196
|
+
if evaluation_context:
|
|
197
|
+
request_data["evaluation_context"] = evaluation_context
|
|
197
198
|
|
|
198
199
|
return self._make_request(
|
|
199
200
|
"POST",
|
|
@@ -205,15 +206,16 @@ class HTTPClient:
|
|
|
205
206
|
def evaluate_prompt_stream(
|
|
206
207
|
self, experiment_key: str, user_context: Dict[str, Any],
|
|
207
208
|
input_variables: Dict[str, Any],
|
|
208
|
-
|
|
209
|
+
evaluation_context: Optional[Dict[str, Any]] = None,
|
|
209
210
|
) -> requests.Response:
|
|
210
211
|
"""Managed execution with streaming: returns raw SSE response for token parsing."""
|
|
211
212
|
request_data = {
|
|
212
213
|
"experiment_key": experiment_key,
|
|
213
214
|
"user_context": self._convert_user_context(user_context),
|
|
214
215
|
"input_variables": input_variables,
|
|
215
|
-
"evaluation_mode": evaluation_mode,
|
|
216
216
|
}
|
|
217
|
+
if evaluation_context:
|
|
218
|
+
request_data["evaluation_context"] = evaluation_context
|
|
217
219
|
|
|
218
220
|
url = f"{self.config.base_url}/api/v1/internal/sdk/prompt-experiments/evaluate-stream"
|
|
219
221
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: variably-sdk
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Official Python SDK for Variably feature flags, LLM experimentation, and prompt optimization platform
|
|
5
5
|
Author: Variably
|
|
6
6
|
Author-email: Variably <support@variably.com>
|
|
@@ -187,6 +187,114 @@ if meta:
|
|
|
187
187
|
print(f"Tokens: {meta.token_usage}")
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
+
### Context-Aware Evaluation (Better RAG Quality) — v2.2.0+
|
|
191
|
+
|
|
192
|
+
For RAG chatbots, passing conversation history and retrieved chunks enables **groundedness scoring, hallucination detection, and conversational coherence** — dimensions that are impossible to evaluate in isolation.
|
|
193
|
+
|
|
194
|
+
The `evaluation_context` parameter is **not sent to the LLM** — it's only used by Variably's evaluator for richer scoring.
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
# Step 1: Collect conversation history from your session
|
|
198
|
+
workflow_history = [
|
|
199
|
+
{"role": "user", "content": "What causes diabetes?"},
|
|
200
|
+
{"role": "assistant", "content": "Key factors include genetics, diet..."},
|
|
201
|
+
{"role": "user", "content": "What about potatoes?"},
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
# Step 2: Collect retrieved RAG chunks (after your retrieval step)
|
|
205
|
+
reference_materials = [
|
|
206
|
+
{
|
|
207
|
+
"id": "chunk-001",
|
|
208
|
+
"content": "Unhealthy diets high in refined sugars, fats...",
|
|
209
|
+
"source": "Kenya National Clinical Guidelines",
|
|
210
|
+
"type": "chunk",
|
|
211
|
+
"relevance_score": 0.89,
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"id": "chunk-002",
|
|
215
|
+
"content": "Modifiable risk factors include obesity...",
|
|
216
|
+
"source": "Kenya National Clinical Guidelines",
|
|
217
|
+
"type": "chunk",
|
|
218
|
+
"relevance_score": 0.82,
|
|
219
|
+
},
|
|
220
|
+
]
|
|
221
|
+
|
|
222
|
+
# Step 3: Pass evaluation_context in your evaluate call
|
|
223
|
+
response = client.evaluate_prompt(
|
|
224
|
+
experiment_key="rag-prompt-experiment",
|
|
225
|
+
user_context={"user_id": "user-123"},
|
|
226
|
+
input_variables={"query": "What about potatoes?", "context": context_text},
|
|
227
|
+
evaluation_mode="full",
|
|
228
|
+
evaluation_context={
|
|
229
|
+
"reference_materials": reference_materials,
|
|
230
|
+
"workflow_history": workflow_history,
|
|
231
|
+
"retrieval_query": "potato consumption glycemic index diabetes risk",
|
|
232
|
+
},
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Same works with streaming
|
|
236
|
+
stream = client.evaluate_prompt_stream(
|
|
237
|
+
experiment_key="rag-prompt-experiment",
|
|
238
|
+
user_context={"user_id": "user-123"},
|
|
239
|
+
input_variables={"query": "What about potatoes?", "context": context_text},
|
|
240
|
+
evaluation_context={
|
|
241
|
+
"reference_materials": reference_materials,
|
|
242
|
+
"workflow_history": workflow_history,
|
|
243
|
+
},
|
|
244
|
+
)
|
|
245
|
+
for token in stream:
|
|
246
|
+
print(token, end="", flush=True)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
**What this enables:**
|
|
250
|
+
|
|
251
|
+
| Dimension | Description | Requires |
|
|
252
|
+
|-----------|-------------|----------|
|
|
253
|
+
| `faithfulness` | % of claims grounded in retrieved chunks | `reference_materials` |
|
|
254
|
+
| `hallucination_rate` | % of claims with no source in context | `reference_materials` |
|
|
255
|
+
| `context_utilization` | % of relevant chunks actually used | `reference_materials` |
|
|
256
|
+
| `attribution_accuracy` | Do citations map to correct chunks? | `reference_materials` |
|
|
257
|
+
| `conversation_consistency` | No contradictions with prior turns | `workflow_history` |
|
|
258
|
+
| `context_retention` | Maintains topic awareness across turns | `workflow_history` |
|
|
259
|
+
| `transparency` | Discloses when going beyond source material | `reference_materials` |
|
|
260
|
+
|
|
261
|
+
**BYOR mode** also supports `evaluation_context` — pass it in `submit_response()`:
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
result = client.submit_response(
|
|
265
|
+
experiment_key="my-experiment",
|
|
266
|
+
variant_key=variant.variant_key,
|
|
267
|
+
executed_prompt=prompt,
|
|
268
|
+
response=llm_response,
|
|
269
|
+
user_context=user_context,
|
|
270
|
+
input_variables=input_variables,
|
|
271
|
+
provider=variant.provider,
|
|
272
|
+
model=variant.model,
|
|
273
|
+
latency_ms=latency,
|
|
274
|
+
evaluation_context={
|
|
275
|
+
"reference_materials": reference_materials,
|
|
276
|
+
"workflow_history": workflow_history,
|
|
277
|
+
},
|
|
278
|
+
)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
#### evaluation_context Schema
|
|
282
|
+
|
|
283
|
+
| Field | Type | Description |
|
|
284
|
+
|-------|------|-------------|
|
|
285
|
+
| `reference_materials` | `list[dict]` | RAG chunks / source documents for groundedness scoring |
|
|
286
|
+
| `reference_materials[].id` | `str` | Unique chunk identifier |
|
|
287
|
+
| `reference_materials[].content` | `str` | Chunk text content |
|
|
288
|
+
| `reference_materials[].source` | `str` (optional) | Source document URL or name |
|
|
289
|
+
| `reference_materials[].type` | `str` (optional) | e.g. `"chunk"`, `"document"` |
|
|
290
|
+
| `reference_materials[].relevance_score` | `float` (optional) | Retriever similarity score |
|
|
291
|
+
| `workflow_history` | `list[dict]` | Conversation turns for coherence scoring |
|
|
292
|
+
| `workflow_history[].role` | `str` | `"user"` or `"assistant"` |
|
|
293
|
+
| `workflow_history[].content` | `str` | Message content |
|
|
294
|
+
| `retrieval_query` | `str` (optional) | The rewritten query sent to the retriever |
|
|
295
|
+
|
|
296
|
+
See [Context-Aware RAG Evaluation](../../docs/concepts/context-aware-rag-evaluation.md) for the full concept doc with architecture diagrams and integration examples.
|
|
297
|
+
|
|
190
298
|
#### Integration with LangGraph / FastAPI streaming
|
|
191
299
|
|
|
192
300
|
```python
|
|
@@ -240,6 +348,11 @@ Content-Type: application/json
|
|
|
240
348
|
},
|
|
241
349
|
"input_variables": {
|
|
242
350
|
"query": "What are the symptoms of Type 2 diabetes?"
|
|
351
|
+
},
|
|
352
|
+
"evaluation_context": {
|
|
353
|
+
"reference_materials": [{"id": "chunk-1", "content": "...", "source": "...", "type": "chunk"}],
|
|
354
|
+
"workflow_history": [{"role": "user", "content": "..."}],
|
|
355
|
+
"retrieval_query": "diabetes symptoms type 2"
|
|
243
356
|
}
|
|
244
357
|
}
|
|
245
358
|
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|