Files
wehub-resource-sync 5a558eb09e
Backend Tests / discover-tests (push) Waiting to run
Backend Tests / ${{ matrix.name }} (push) Blocked by required conditions
Build and Publish SDK / build-and-publish (push) Waiting to run
Build Opik Docker Images / set-version (push) Waiting to run
Build Opik Docker Images / build-backend (push) Blocked by required conditions
Build Opik Docker Images / build-sandbox-executor-python (push) Blocked by required conditions
Build Opik Docker Images / build-python-backend (push) Blocked by required conditions
Build Opik Docker Images / build-frontend (push) Blocked by required conditions
Build Opik Docker Images / create-git-tag (push) Blocked by required conditions
ClickHouse Migration Cluster Check / validate-clickhouse-migrations (push) Waiting to run
Docs - Publish / run (push) Waiting to run
E2E Tests - Post Merge (v2) / 🧪 E2E v2 Tests (${{ github.event.inputs.tier || 't1' }}) (push) Waiting to run
E2E Tests - Post Merge (v2) / 📢 Slack Notification (push) Blocked by required conditions
Frontend Unit Tests / Test on Node 20 (push) Waiting to run
Guardrails E2E Tests / Select Python version matrix (push) Waiting to run
Guardrails E2E Tests / Guardrails E2E Tests ${{matrix.python_version}} (push) Blocked by required conditions
Guardrails E2E Tests / 📢 Slack Notification (push) Blocked by required conditions
Guardrails Backend Unit Tests / Guardrails Backend Unit Tests (push) Waiting to run
Guardrails Backend Unit Tests / 📢 Slack Notification (push) Blocked by required conditions
Lint Opik Helm Chart / lint-helm-chart (Helm v3.21.0) (push) Waiting to run
Lint Opik Helm Chart / lint-helm-chart (Helm v4.2.0) (push) Waiting to run
Lint Opik Helm Chart / unittest-helm-chart (push) Waiting to run
Lint Opik Helm Chart / render-equality (push) Waiting to run
Opik Optimizer - Unit Tests / Opik Optimizer Unit Tests Python ${{matrix.python_version}} (push) Waiting to run
Python BE E2E Tests / Python BE E2E (push) Waiting to run
Python Backend Tests / run-python-backend-tests (push) Waiting to run
Python SDK Unit Tests / Python SDK Unit Tests ${{matrix.python_version}} (push) Waiting to run
Release Drafter / update_release_draft (push) Waiting to run
SDK E2E Libraries Integration Tests / Check Secrets (push) Waiting to run
SDK E2E Libraries Integration Tests / Missed OpenAI API Key Warning (push) Blocked by required conditions
SDK E2E Libraries Integration Tests / build-opik (push) Blocked by required conditions
SDK E2E Libraries Integration Tests / E2E Lib Integration Python ${{matrix.python_version}} (push) Blocked by required conditions
TypeScript SDK Integration Build & Publish / build-and-publish (opik-gemini) (push) Waiting to run
TypeScript SDK Integration Build & Publish / build-and-publish (opik-langchain) (push) Waiting to run
TypeScript SDK Integration Build & Publish / build-and-publish (opik-openai) (push) Waiting to run
TypeScript SDK Integration Build & Publish / build-and-publish (opik-otel) (push) Waiting to run
TypeScript SDK Integration Build & Publish / build-and-publish (opik-vercel) (push) Waiting to run
TypeScript SDK Build & Publish / build-and-publish (push) Waiting to run
TypeScript SDK Unit Tests / Test on Node ${{ matrix.node-version }} (push) Waiting to run
TypeScript SDK Compatibility V1.x E2E Tests / Select Node version matrix (push) Has been cancelled
TypeScript SDK Compatibility V1.x E2E Tests / TypeScript SDK Compatibility V1.x E2E Tests Node ${{matrix.node_version}} (push) Has been cancelled
TypeScript SDK E2E Tests / TypeScript SDK E2E Tests Node ${{matrix.node_version}} (push) Has been cancelled
Opik Optimizer - E2E Tests / build-opik (push) Has been cancelled
TypeScript SDK Compatibility V1.x E2E Tests / build-opik (push) Has been cancelled
Python SDK E2E Tests / Select Python version matrix (push) Has been cancelled
Python SDK E2E Tests / Python SDK E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Python SDK E2E Tests / build-opik (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / Select Python version matrix (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / Python SDK Compatibility V1.x E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / build-opik (push) Has been cancelled
TypeScript SDK E2E Tests / Select Node version matrix (push) Has been cancelled
TypeScript SDK E2E Tests / build-opik (push) Has been cancelled
Opik Optimizer - E2E Tests / Opik Optimizer E2E Tests Python ${{matrix.python_version}} (push) Has been cancelled
Opik Optimizer - E2E Tests / Opik Optimizer Integration Smoke Tests (push) Has been cancelled
🐙 Code Quality / detect (push) Has been cancelled
🐙 Code Quality / lint (${{ matrix.leg.name }}) (push) Has been cancelled
🐙 Code Quality / summary (push) Has been cancelled
TypeScript SDK Library Integration Tests / Check Secrets (push) Has been cancelled
TypeScript SDK Library Integration Tests / opik-vercel (Vercel AI SDK / eve) (push) Has been cancelled
SDK Library Integration Tests Runner / Check Secrets (push) Has been cancelled
SDK Library Integration Tests Runner / Missed OpenAI API Key Warning (push) Has been cancelled
SDK Library Integration Tests Runner / Build (push) Has been cancelled
SDK Library Integration Tests Runner / openai_tests (push) Has been cancelled
SDK Library Integration Tests Runner / langchain_tests (push) Has been cancelled
SDK Library Integration Tests Runner / langchain_legacy_tests (push) Has been cancelled
SDK Library Integration Tests Runner / llama_index_tests (push) Has been cancelled
SDK Library Integration Tests Runner / anthropic_tests (push) Has been cancelled
SDK Library Integration Tests Runner / mistral_tests (push) Has been cancelled
SDK Library Integration Tests Runner / groq_tests (push) Has been cancelled
SDK Library Integration Tests Runner / aisuite_tests (push) Has been cancelled
SDK Library Integration Tests Runner / haystack_tests (push) Has been cancelled
SDK Library Integration Tests Runner / dspy_tests (push) Has been cancelled
SDK Library Integration Tests Runner / crewai_v0_tests (push) Has been cancelled
SDK Library Integration Tests Runner / crewai_v1_tests (push) Has been cancelled
SDK Library Integration Tests Runner / genai_tests (push) Has been cancelled
SDK Library Integration Tests Runner / adk_tests (push) Has been cancelled
SDK Library Integration Tests Runner / adk_legacy_1_3_0_tests (push) Has been cancelled
SDK Library Integration Tests Runner / evaluation_metrics_tests (push) Has been cancelled
SDK Library Integration Tests Runner / bedrock_tests (push) Has been cancelled
SDK Library Integration Tests Runner / litellm_tests (push) Has been cancelled
SDK Library Integration Tests Runner / harbor_tests (push) Has been cancelled
SDK Library Integration Tests Runner / Slack Notification (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:25:44 +08:00

218 lines
7.9 KiB
Python

"""
Local demo for ``opik.evaluate_resume``.
Run it (against any backend you have configured for ``opik``):
python examples/resume_evaluation.py
What it does, top to bottom:
1. Creates a small sentiment-classification dataset.
2. Runs ``opik.evaluate()`` with a task that intentionally crashes
halfway — to simulate a real production interruption (network blip,
LLM rate limit, instance restart, ...).
3. Catches the crash and reports what got done.
4. Calls ``opik.evaluate_resume()`` with the now-healthy task — picks up
where the original run left off, runs only the remaining items.
5. Prints the final converged state.
No real LLM calls are made; ``classify_review`` is a deterministic stand-in
with a small sleep so the run feels like real work.
"""
import time
import uuid
from typing import Dict
import opik
from opik.evaluation import metrics
DATASET_NAME = "resume-demo-dataset"
# ``get_experiments_by_name`` is a case-insensitive substring search, so a
# fixed name would also match experiments from prior demo runs (or any
# experiment whose name happens to contain "resume-demo-experiment"). Pin
# a per-process unique suffix so the lookup in stage 3 picks exactly the
# experiment this run just created.
EXPERIMENT_NAME = f"resume-demo-experiment-{uuid.uuid4().hex[:8]}"
# (review text, expected sentiment) pairs — drive both the dataset and the
# fake classifier. Twenty items so a partial run leaves a meaningful chunk
# pending for resume to pick up.
REVIEWS = [
("I love this product!", "positive"),
("Worst experience ever.", "negative"),
("It was okay, nothing special.", "neutral"),
("Absolutely fantastic, highly recommend!", "positive"),
("Total waste of money.", "negative"),
("Mediocre at best.", "neutral"),
("Amazing quality and great service!", "positive"),
("I want a refund.", "negative"),
("Pretty good but room for improvement.", "neutral"),
("Five stars, no complaints.", "positive"),
("Returned it within a week.", "negative"),
("Does what it says on the tin.", "neutral"),
("Best purchase I've made all year!", "positive"),
("Stopped working after two days.", "negative"),
("Average product, average price.", "neutral"),
("Highly impressed by the build quality.", "positive"),
("Customer support was unhelpful.", "negative"),
("Acceptable for the price point.", "neutral"),
("Genuinely delighted with this.", "positive"),
("Misleading description, do not buy.", "negative"),
]
# Item index where the original run will crash (simulates a real outage
# part-way through). With 20 items, 12 leaves 8 pending for resume to do.
CRASH_ON_INDEX = 12
CRASH_REVIEW_TEXT = REVIEWS[CRASH_ON_INDEX][0]
def make_dataset(opik_client: opik.Opik) -> opik.Dataset:
"""Recreate the demo dataset from scratch so the script is idempotent."""
try:
opik_client.delete_dataset(DATASET_NAME)
except Exception:
pass
dataset = opik_client.create_dataset(DATASET_NAME)
dataset.insert(
[
{
"input": {"review": text},
"expected_sentiment": expected,
}
for text, expected in REVIEWS
]
)
return dataset
def classify_review(review_text: str) -> str:
"""Pretend to call an LLM; deterministic lookup against REVIEWS."""
time.sleep(0.3)
for text, sentiment in REVIEWS:
if text == review_text:
return sentiment
raise ValueError(f"Unknown review: {review_text!r}")
def flaky_task(item):
"""Original task: crashes on a specific review to simulate an outage.
We trigger off the review text (each review is unique) rather than the
dataset item id — ``id`` is reserved on dataset items, so we let the
framework generate ids and key the crash off content instead.
"""
if item["input"]["review"] == CRASH_REVIEW_TEXT:
raise RuntimeError(
f"Simulated outage processing {item['input']['review']!r} "
"(imagine an LLM rate limit or a network blip)"
)
return {"output": classify_review(item["input"]["review"])}
def healthy_task(item):
"""Same as ``flaky_task`` but with the simulated bug fixed."""
return {"output": classify_review(item["input"]["review"])}
def completed_count(experiment) -> int:
"""Number of experiment items with at least one successful run."""
return sum(
1 for item in experiment.get_items() if item.evaluation_task_output is not None
)
def main() -> None:
opik_client = opik.Opik()
# ----- 1. Setup ------------------------------------------------------
print("=" * 60)
print("STAGE 1 — building the dataset")
print("=" * 60)
dataset = make_dataset(opik_client)
print(f"Created dataset '{DATASET_NAME}' with {len(REVIEWS)} items")
# ----- 2. Initial evaluation (crashes mid-way) -----------------------
print()
print("=" * 60)
print("STAGE 2 — running evaluate() with a flaky task")
print("=" * 60)
print(f"Task will crash on review #{CRASH_ON_INDEX}: {CRASH_REVIEW_TEXT!r} ...")
try:
opik.evaluate(
dataset=dataset,
task=flaky_task,
scoring_metrics=[metrics.Equals()],
scoring_key_mapping={"reference": "expected_sentiment"},
experiment_name=EXPERIMENT_NAME,
task_threads=1,
verbose=0,
)
except RuntimeError as exc:
print(f"Evaluation interrupted (as expected): {exc}")
# ----- 3. Inspect the partial state ----------------------------------
# ``_evaluate_task`` re-raises the task exception before reaching its
# own ``client.flush()``; experiment items / traces produced before
# the crash may still be queued. Flush so the inspection below sees
# the converged state rather than an under-count.
opik_client.flush()
experiments = opik_client.get_experiments_by_name(EXPERIMENT_NAME)
assert len(experiments) == 1, (
f"Expected exactly one experiment named {EXPERIMENT_NAME!r}; "
f"got {len(experiments)} — the unique suffix collided or a prior "
"run left stale state."
)
experiment_id = experiments[0].id
experiment = opik_client.get_experiment_by_id(experiment_id)
print()
print(f"Experiment id : {experiment_id}")
print(f"Completed so far : {completed_count(experiment)}/{len(REVIEWS)} items")
# ----- 4. Resume -----------------------------------------------------
print()
print("=" * 60)
print("STAGE 3 — calling evaluate_resume() with the healthy task")
print("=" * 60)
resume_result = opik.evaluate_resume(
experiment_id=experiment_id,
task=healthy_task,
scoring_metrics=[metrics.Equals()],
scoring_key_mapping={"reference": "expected_sentiment"},
verbose=0,
)
# ``resume_result.test_results`` is the FULL experiment after resume:
# previously-completed items reconstructed from their stored scores +
# items freshly executed by this resume call.
print(
f"Resume returned {len(resume_result.test_results)} test results "
f"(reconstructed previous + freshly executed)."
)
score_counts: Dict[str, int] = {}
for test_result in resume_result.test_results:
score_value = test_result.score_results[0].value
bucket = "1.0" if score_value == 1.0 else f"{score_value}"
score_counts[bucket] = score_counts.get(bucket, 0) + 1
for bucket, count in sorted(score_counts.items()):
print(f" equals_metric={bucket}: {count} items")
# ----- 5. Verify convergence -----------------------------------------
print()
print("=" * 60)
print("STAGE 4 — final state")
print("=" * 60)
experiment = opik_client.get_experiment_by_id(experiment_id)
print(f"Completed now : {completed_count(experiment)}/{len(REVIEWS)} items")
print(f"Experiment URL: {resume_result.experiment_url}")
if __name__ == "__main__":
main()