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.
Files changed (21) hide show
  1. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/PKG-INFO +1 -1
  2. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/pyproject.toml +1 -1
  3. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/client.py +70 -0
  4. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/observe.py +10 -0
  5. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/version.py +1 -1
  6. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably_sdk.egg-info/PKG-INFO +1 -1
  7. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/README.md +0 -0
  8. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/setup.cfg +0 -0
  9. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/setup.py +0 -0
  10. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/__init__.py +0 -0
  11. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/cache.py +0 -0
  12. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/config.py +0 -0
  13. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/errors.py +0 -0
  14. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/http_client.py +0 -0
  15. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/logger.py +0 -0
  16. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably/types.py +0 -0
  17. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably_sdk.egg-info/SOURCES.txt +0 -0
  18. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably_sdk.egg-info/dependency_links.txt +0 -0
  19. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably_sdk.egg-info/requires.txt +0 -0
  20. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/src/variably_sdk.egg-info/top_level.txt +0 -0
  21. {variably_sdk-2.8.0 → variably_sdk-2.9.1}/tests/test_streaming.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: variably-sdk
3
- Version: 2.8.0
3
+ Version: 2.9.1
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>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "variably-sdk"
7
- version = "2.8.0"
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 {
@@ -1,3 +1,3 @@
1
1
  """Version information for Variably SDK."""
2
2
 
3
- __version__ = "2.8.0"
3
+ __version__ = "2.9.1"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: variably-sdk
3
- Version: 2.8.0
3
+ Version: 2.9.1
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>
File without changes
File without changes
File without changes