@@ -15,6 +15,14 @@ import atexit
1515
1616import posthog
1717from posthog import FeatureFlagEvaluations, FlagValue, Posthog
18+ from posthog.ai.evaluations import (
19+ AsyncOfflineEvaluations,
20+ BooleanScorerConfig,
21+ EvaluationItem,
22+ NumericPassingRule,
23+ NumericScorerConfig,
24+ OfflineEvaluations,
25+ )
1826
1927client = Posthog("phc_test")
2028atexit.register(client.shutdown)
@@ -31,6 +39,40 @@ active: posthog.Span | None = posthog.get_active_span()
3139span.end()
3240
3341_ = (flag_value, all_flags, enabled, payload, evaluations, active)
42+
43+
44+ async def offline_evaluation_types(
45+ sync_client: OfflineEvaluations, async_client: AsyncOfflineEvaluations
46+ ) -> None:
47+ numeric = sync_client.scorers.create(
48+ name="Relevance",
49+ kind="numeric",
50+ config=NumericScorerConfig(
51+ min=0, max=1,
52+ passing_rule=NumericPassingRule(operator="gte", threshold=0.8),
53+ ),
54+ )
55+ config: NumericScorerConfig = numeric.config
56+ experiment = sync_client.create_experiment(name="baseline", run_source="ci")
57+ item = EvaluationItem(input="question", output="answer")
58+ experiment.upload_result(item=item, scorer_version_id=numeric.current_version_id, value=0.9)
59+ boolean = await async_client.scorers.create(
60+ name="Toxicity", kind="boolean", config=BooleanScorerConfig(true_is_failure=True)
61+ )
62+ async_experiment = await async_client.create_experiment(name="async run")
63+ await async_experiment.upload_result(item=item, scorer_version_id=boolean.current_version_id, value=False)
64+ _ = config
65+ PY
66+
67+ cat > " $tmp /invalid_offline_types.py" << 'PY '
68+ # pyright: strict
69+ from posthog.ai.evaluations import BooleanScorerConfig, CategoricalScorerConfig, NumericPassingRule, NumericScorerConfig, OfflineEvaluations
70+
71+ client = OfflineEvaluations(project_id=123, secret_key="phx_example")
72+ NumericPassingRule(operator="gt", threshold=0.8) # expected-type-error
73+ NumericScorerConfig(unknown_setting=True) # expected-type-error
74+ CategoricalScorerConfig(selection_mode="single") # expected-type-error
75+ client.scorers.create(name="Bad config", kind="numeric", config=BooleanScorerConfig(true_is_failure=True)) # expected-type-error
3476PY
3577
3678" $tmp /.venv/bin/python" - << 'PY ' > "$tmp/public_api_access.py"
73115
74116cd " $tmp "
75117" $tmp /.venv/bin/python" -m pyright strict_posthog_types.py public_api_access.py
118+
119+ if " $tmp /.venv/bin/python" -m pyright --outputjson invalid_offline_types.py > invalid_types.json; then
120+ echo " Invalid offline evaluation configurations unexpectedly passed type checking."
121+ exit 1
122+ fi
123+ " $tmp /.venv/bin/python" - << 'PY '
124+ import json
125+ from pathlib import Path
126+
127+ expected = {
128+ index for index, line in enumerate(Path("invalid_offline_types.py").read_text().splitlines())
129+ if "expected-type-error" in line
130+ }
131+ diagnostics = json.loads(Path("invalid_types.json").read_text())["generalDiagnostics"]
132+ actual = {item["range"]["start"]["line"] for item in diagnostics if item["severity"] == "error"}
133+ assert actual == expected, diagnostics
134+ print("Offline evaluation configuration types reject invalid inputs.")
135+ PY
0 commit comments