variably-sdk 2.3.0__tar.gz → 2.5.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.3.0 → variably_sdk-2.5.0}/PKG-INFO +1 -1
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/pyproject.toml +1 -1
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/setup.py +1 -1
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/src/variably/client.py +99 -0
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/src/variably/http_client.py +9 -0
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/src/variably/types.py +16 -1
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/src/variably_sdk.egg-info/PKG-INFO +1 -1
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/README.md +0 -0
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/setup.cfg +0 -0
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/src/variably/__init__.py +0 -0
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/src/variably/cache.py +0 -0
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/src/variably/config.py +0 -0
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/src/variably/errors.py +0 -0
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/src/variably/logger.py +0 -0
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/src/variably/version.py +0 -0
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/src/variably_sdk.egg-info/SOURCES.txt +0 -0
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/src/variably_sdk.egg-info/dependency_links.txt +0 -0
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/src/variably_sdk.egg-info/requires.txt +0 -0
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/src/variably_sdk.egg-info/top_level.txt +0 -0
- {variably_sdk-2.3.0 → variably_sdk-2.5.0}/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.5.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"
|
|
@@ -14,6 +14,7 @@ from .types import (
|
|
|
14
14
|
EvaluateResponse,
|
|
15
15
|
Event,
|
|
16
16
|
FlagResult,
|
|
17
|
+
LogResult,
|
|
17
18
|
PromptVariant,
|
|
18
19
|
SDKMetrics,
|
|
19
20
|
StreamingEvaluateResponse,
|
|
@@ -595,6 +596,104 @@ class VariablyClient:
|
|
|
595
596
|
|
|
596
597
|
return SubmitResponseResult(status=result.get("status", "ok"))
|
|
597
598
|
|
|
599
|
+
def log(
|
|
600
|
+
self,
|
|
601
|
+
prompt: Union[str, List[Dict[str, Any]]],
|
|
602
|
+
response: str,
|
|
603
|
+
*,
|
|
604
|
+
conversation_history: Optional[List[Dict[str, Any]]] = None,
|
|
605
|
+
reference_materials: Optional[List[Dict[str, Any]]] = None,
|
|
606
|
+
retrieval_query: Optional[str] = None,
|
|
607
|
+
tags: Optional[List[str]] = None,
|
|
608
|
+
user_id: Optional[str] = None,
|
|
609
|
+
session_id: Optional[str] = None,
|
|
610
|
+
provider: Optional[str] = None,
|
|
611
|
+
model: Optional[str] = None,
|
|
612
|
+
latency_ms: Optional[int] = None,
|
|
613
|
+
prompt_tokens: Optional[int] = None,
|
|
614
|
+
completion_tokens: Optional[int] = None,
|
|
615
|
+
cost: Optional[float] = None,
|
|
616
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
617
|
+
) -> LogResult:
|
|
618
|
+
"""Log a prompt/response pair for 43-dimension evaluation (Observe Mode).
|
|
619
|
+
|
|
620
|
+
Zero-friction entry point — no experiment creation needed. Just log your
|
|
621
|
+
prompts and responses and see quality scores in the dashboard.
|
|
622
|
+
|
|
623
|
+
Args:
|
|
624
|
+
prompt: The prompt sent to the LLM (str or messages list).
|
|
625
|
+
response: The LLM's response text.
|
|
626
|
+
conversation_history: Prior conversation turns for cross-turn coherence scoring.
|
|
627
|
+
reference_materials: Retrieved RAG chunks for grounding/hallucination scoring.
|
|
628
|
+
retrieval_query: The query sent to the retriever (for retrieval relevance scoring).
|
|
629
|
+
tags: Optional tags for grouping observations (e.g. ["production", "chatbot"]).
|
|
630
|
+
user_id: End-user ID for per-user analytics.
|
|
631
|
+
session_id: Session ID for multi-turn tracking.
|
|
632
|
+
provider: LLM provider name (e.g. "openai", "anthropic").
|
|
633
|
+
model: Model name (e.g. "gpt-4", "claude-3-opus").
|
|
634
|
+
latency_ms: LLM call latency in milliseconds.
|
|
635
|
+
prompt_tokens: Number of input tokens.
|
|
636
|
+
completion_tokens: Number of output tokens.
|
|
637
|
+
cost: Cost in USD for this LLM call.
|
|
638
|
+
metadata: Arbitrary key-value metadata.
|
|
639
|
+
|
|
640
|
+
Returns:
|
|
641
|
+
LogResult with observation_id and status.
|
|
642
|
+
"""
|
|
643
|
+
# Normalize prompt to string
|
|
644
|
+
prompt_str = prompt if isinstance(prompt, str) else str(prompt)
|
|
645
|
+
|
|
646
|
+
data: Dict[str, Any] = {
|
|
647
|
+
"prompt": prompt_str,
|
|
648
|
+
"response": response,
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
# Build evaluation_context from multi-turn/RAG params
|
|
652
|
+
evaluation_context: Dict[str, Any] = {}
|
|
653
|
+
if conversation_history:
|
|
654
|
+
evaluation_context["conversation_history"] = conversation_history
|
|
655
|
+
if reference_materials:
|
|
656
|
+
evaluation_context["reference_materials"] = reference_materials
|
|
657
|
+
if retrieval_query:
|
|
658
|
+
evaluation_context["retrieval_query"] = retrieval_query
|
|
659
|
+
if evaluation_context:
|
|
660
|
+
data["evaluation_context"] = evaluation_context
|
|
661
|
+
|
|
662
|
+
if tags:
|
|
663
|
+
data["tags"] = tags
|
|
664
|
+
if user_id:
|
|
665
|
+
data["user_id"] = user_id
|
|
666
|
+
if session_id:
|
|
667
|
+
data["session_id"] = session_id
|
|
668
|
+
if provider:
|
|
669
|
+
data["provider"] = provider
|
|
670
|
+
if model:
|
|
671
|
+
data["model"] = model
|
|
672
|
+
if latency_ms is not None:
|
|
673
|
+
data["latency_ms"] = latency_ms
|
|
674
|
+
if prompt_tokens is not None:
|
|
675
|
+
data["prompt_tokens"] = prompt_tokens
|
|
676
|
+
if completion_tokens is not None:
|
|
677
|
+
data["completion_tokens"] = completion_tokens
|
|
678
|
+
if cost is not None:
|
|
679
|
+
data["cost"] = cost
|
|
680
|
+
if metadata:
|
|
681
|
+
data["metadata"] = metadata
|
|
682
|
+
|
|
683
|
+
try:
|
|
684
|
+
result = self.http_client.log_observation(data)
|
|
685
|
+
return LogResult(
|
|
686
|
+
observation_id=result.get("observation_id", ""),
|
|
687
|
+
experiment_id=result.get("experiment_id", ""),
|
|
688
|
+
status=result.get("status", "queued"),
|
|
689
|
+
)
|
|
690
|
+
except Exception as error:
|
|
691
|
+
self.logger.error(
|
|
692
|
+
"Observation logging failed",
|
|
693
|
+
{"error": str(error)},
|
|
694
|
+
)
|
|
695
|
+
raise
|
|
696
|
+
|
|
598
697
|
def clear_cache(self) -> None:
|
|
599
698
|
"""Clear the cache."""
|
|
600
699
|
self.cache.clear()
|
|
@@ -243,6 +243,15 @@ class HTTPClient:
|
|
|
243
243
|
retryable=False,
|
|
244
244
|
)
|
|
245
245
|
|
|
246
|
+
def log_observation(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
247
|
+
"""Log a prompt/response pair for observe-mode evaluation."""
|
|
248
|
+
return self._make_request(
|
|
249
|
+
"POST",
|
|
250
|
+
"/api/v1/internal/sdk/observe/log",
|
|
251
|
+
json=data,
|
|
252
|
+
retryable=False,
|
|
253
|
+
)
|
|
254
|
+
|
|
246
255
|
def _make_request(
|
|
247
256
|
self,
|
|
248
257
|
method: str,
|
|
@@ -346,4 +346,19 @@ class SubmitResponseResult:
|
|
|
346
346
|
self.status = status
|
|
347
347
|
|
|
348
348
|
def __repr__(self) -> str:
|
|
349
|
-
return f"SubmitResponseResult(status='{self.status}')"
|
|
349
|
+
return f"SubmitResponseResult(status='{self.status}')"
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
class LogResult:
|
|
353
|
+
"""Result from observe/log endpoint."""
|
|
354
|
+
|
|
355
|
+
def __init__(self, observation_id: str, experiment_id: str, status: str):
|
|
356
|
+
self.observation_id = observation_id
|
|
357
|
+
self.experiment_id = experiment_id
|
|
358
|
+
self.status = status
|
|
359
|
+
|
|
360
|
+
def __repr__(self) -> str:
|
|
361
|
+
return (
|
|
362
|
+
f"LogResult(observation_id='{self.observation_id}', "
|
|
363
|
+
f"experiment_id='{self.experiment_id}', status='{self.status}')"
|
|
364
|
+
)
|
|
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
|