variably-sdk 2.8.0__tar.gz → 2.9.1__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.8.0 → variably_sdk-2.9.1}/PKG-INFO +1 -1
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/pyproject.toml +1 -1
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/client.py +70 -0
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/observe.py +10 -0
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/version.py +1 -1
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably_sdk.egg-info/PKG-INFO +1 -1
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/README.md +0 -0
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/setup.cfg +0 -0
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/setup.py +0 -0
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/__init__.py +0 -0
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/cache.py +0 -0
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/config.py +0 -0
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/errors.py +0 -0
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/http_client.py +0 -0
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/logger.py +0 -0
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/types.py +0 -0
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably_sdk.egg-info/SOURCES.txt +0 -0
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably_sdk.egg-info/dependency_links.txt +0 -0
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably_sdk.egg-info/requires.txt +0 -0
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably_sdk.egg-info/top_level.txt +0 -0
- {variably_sdk-2.8.0 → variably_sdk-2.9.1}/tests/test_streaming.py +0 -0
|
@@ -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.9.1"
|
|
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"
|
|
@@ -554,6 +554,20 @@ class VariablyClient:
|
|
|
554
554
|
|
|
555
555
|
return StreamingEvaluateResponse(raw_response)
|
|
556
556
|
|
|
557
|
+
# Allowed values for the `kind` parameter on submit_response. Marks
|
|
558
|
+
# what type of LLM turn this is so the dashboard can suppress
|
|
559
|
+
# grounding/hallucination/attribution scoring on turns where those
|
|
560
|
+
# dimensions don't apply (router, classifier, tool-use). Default
|
|
561
|
+
# "generative" preserves existing behavior for callers not yet on
|
|
562
|
+
# this version.
|
|
563
|
+
_SUBMIT_RESPONSE_KINDS = (
|
|
564
|
+
"generative", # default — narrative answer; full 41D scoring
|
|
565
|
+
"classifier", # intent / safety / routing decision; no claims
|
|
566
|
+
"router", # workflow control turn; no claims
|
|
567
|
+
"tool_use", # function/tool call; no narrative response
|
|
568
|
+
"transformer", # query rewrite / expansion / summarization step
|
|
569
|
+
)
|
|
570
|
+
|
|
557
571
|
def submit_response(
|
|
558
572
|
self,
|
|
559
573
|
experiment_key: str,
|
|
@@ -570,6 +584,7 @@ class VariablyClient:
|
|
|
570
584
|
evaluation_context: Optional[Dict[str, Any]] = None,
|
|
571
585
|
tags: Optional[List[str]] = None,
|
|
572
586
|
metadata: Optional[Dict[str, Any]] = None,
|
|
587
|
+
kind: Optional[str] = None,
|
|
573
588
|
) -> SubmitResponseResult:
|
|
574
589
|
"""Submit your own LLM response for 41-dimensional evaluation (BYOR step 3).
|
|
575
590
|
|
|
@@ -577,6 +592,22 @@ class VariablyClient:
|
|
|
577
592
|
tags: Grouping labels for filtering (e.g. ["production", "rag-v2"]).
|
|
578
593
|
metadata: Arbitrary key-value data attached to this evaluation
|
|
579
594
|
(e.g. {"customer_id": "...", "release": "..."}).
|
|
595
|
+
kind: What type of LLM turn this is. One of:
|
|
596
|
+
- "generative" (default) — a narrative answer with claims
|
|
597
|
+
to ground. Full 41-dimensional scoring applies.
|
|
598
|
+
- "classifier" — an intent / safety / routing decision
|
|
599
|
+
(e.g. {"is_relevant": true, "should_generate": true}).
|
|
600
|
+
No factual claims, so grounding/hallucination/attribution
|
|
601
|
+
scores will be suppressed in dashboards.
|
|
602
|
+
- "router" — a workflow control turn (chooses next node).
|
|
603
|
+
- "tool_use" — a function/tool call with no narrative.
|
|
604
|
+
- "transformer" — a query rewrite / expansion / summary
|
|
605
|
+
step that's part of the pipeline, not the answer.
|
|
606
|
+
|
|
607
|
+
When non-generative kinds are passed, the SDK injects the
|
|
608
|
+
kind into tags (as `kind:<value>`) and metadata so the
|
|
609
|
+
dashboard can filter and skip grounding scores. Latency,
|
|
610
|
+
token, and variant-level analytics still work.
|
|
580
611
|
"""
|
|
581
612
|
if not experiment_key or not isinstance(experiment_key, str):
|
|
582
613
|
raise ConfigurationError(
|
|
@@ -586,6 +617,22 @@ class VariablyClient:
|
|
|
586
617
|
raise ConfigurationError(
|
|
587
618
|
"Variant key must be a non-empty string", "variant_key"
|
|
588
619
|
)
|
|
620
|
+
if kind is not None and kind not in self._SUBMIT_RESPONSE_KINDS:
|
|
621
|
+
raise ConfigurationError(
|
|
622
|
+
f"kind must be one of {self._SUBMIT_RESPONSE_KINDS}, got {kind!r}",
|
|
623
|
+
"kind",
|
|
624
|
+
)
|
|
625
|
+
|
|
626
|
+
# Inject kind into tags + metadata so the dashboard can filter
|
|
627
|
+
# without needing a backend deploy. Preserves any caller-supplied
|
|
628
|
+
# values; we only add, never overwrite.
|
|
629
|
+
if kind and kind != "generative":
|
|
630
|
+
kind_tag = f"kind:{kind}"
|
|
631
|
+
tags = list(tags) if tags else []
|
|
632
|
+
if kind_tag not in tags:
|
|
633
|
+
tags.append(kind_tag)
|
|
634
|
+
metadata = dict(metadata) if metadata else {}
|
|
635
|
+
metadata.setdefault("kind", kind)
|
|
589
636
|
|
|
590
637
|
data: Dict[str, Any] = {
|
|
591
638
|
"experiment_key": experiment_key,
|
|
@@ -613,6 +660,8 @@ class VariablyClient:
|
|
|
613
660
|
data["tags"] = tags
|
|
614
661
|
if metadata:
|
|
615
662
|
data["metadata"] = metadata
|
|
663
|
+
if kind:
|
|
664
|
+
data["kind"] = kind
|
|
616
665
|
|
|
617
666
|
result = self.http_client.submit_response(data)
|
|
618
667
|
|
|
@@ -640,6 +689,8 @@ class VariablyClient:
|
|
|
640
689
|
prompt_tokens: Optional[int] = None, # Input token count
|
|
641
690
|
completion_tokens: Optional[int] = None, # Output token count
|
|
642
691
|
cost: Optional[float] = None, # Cost in USD
|
|
692
|
+
# Turn-type marker — see _SUBMIT_RESPONSE_KINDS for allowed values.
|
|
693
|
+
kind: Optional[str] = None,
|
|
643
694
|
) -> LogResult:
|
|
644
695
|
"""Log a prompt/response pair for multi-dimensional evaluation (Observe Mode).
|
|
645
696
|
|
|
@@ -673,6 +724,23 @@ class VariablyClient:
|
|
|
673
724
|
# Normalize prompt to string
|
|
674
725
|
prompt_str = prompt if isinstance(prompt, str) else str(prompt)
|
|
675
726
|
|
|
727
|
+
# Validate kind and inject the marker into tags + metadata so the
|
|
728
|
+
# dashboard can suppress grounding/hallucination/attribution
|
|
729
|
+
# scores on non-generative turns. See submit_response for the
|
|
730
|
+
# full rationale; the same logic applies in Observe Mode.
|
|
731
|
+
if kind is not None and kind not in self._SUBMIT_RESPONSE_KINDS:
|
|
732
|
+
raise ConfigurationError(
|
|
733
|
+
f"kind must be one of {self._SUBMIT_RESPONSE_KINDS}, got {kind!r}",
|
|
734
|
+
"kind",
|
|
735
|
+
)
|
|
736
|
+
if kind and kind != "generative":
|
|
737
|
+
kind_tag = f"kind:{kind}"
|
|
738
|
+
tags = list(tags) if tags else []
|
|
739
|
+
if kind_tag not in tags:
|
|
740
|
+
tags.append(kind_tag)
|
|
741
|
+
metadata = dict(metadata) if metadata else {}
|
|
742
|
+
metadata.setdefault("kind", kind)
|
|
743
|
+
|
|
676
744
|
data: Dict[str, Any] = {
|
|
677
745
|
"prompt": prompt_str,
|
|
678
746
|
"response": response,
|
|
@@ -709,6 +777,8 @@ class VariablyClient:
|
|
|
709
777
|
data["cost"] = cost
|
|
710
778
|
if metadata:
|
|
711
779
|
data["metadata"] = metadata
|
|
780
|
+
if kind:
|
|
781
|
+
data["kind"] = kind
|
|
712
782
|
|
|
713
783
|
try:
|
|
714
784
|
result = self.http_client.log_observation(data)
|
|
@@ -177,6 +177,15 @@ def observe(
|
|
|
177
177
|
tags: Optional[List[str]] = None, # Grouping labels, e.g. ["production", "rag", "v2"]
|
|
178
178
|
metadata: Optional[Dict[str, Any]] = None, # Any arbitrary key-value data for filtering
|
|
179
179
|
|
|
180
|
+
# --- Turn type ---
|
|
181
|
+
# Marks what kind of LLM turn this is. Use "classifier" / "router" /
|
|
182
|
+
# "tool_use" / "transformer" for non-generative turns so the dashboard
|
|
183
|
+
# suppresses grounding/hallucination/attribution scores (which don't
|
|
184
|
+
# apply to JSON control structures or function calls). Default
|
|
185
|
+
# "generative" preserves existing behavior.
|
|
186
|
+
# Allowed: "generative" (default), "classifier", "router", "tool_use", "transformer".
|
|
187
|
+
kind: Optional[str] = None,
|
|
188
|
+
|
|
180
189
|
# --- Backward compatibility ---
|
|
181
190
|
conversation_history: Optional[List[Dict[str, str]]] = None, # Deprecated: use prior_turns instead
|
|
182
191
|
) -> Optional[Dict[str, Any]]:
|
|
@@ -263,6 +272,7 @@ def observe(
|
|
|
263
272
|
user_id=user_id,
|
|
264
273
|
session_id=session_id,
|
|
265
274
|
metadata=metadata,
|
|
275
|
+
kind=kind,
|
|
266
276
|
)
|
|
267
277
|
|
|
268
278
|
return {
|
|
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
|
|
File without changes
|
|
File without changes
|