Repository navigation
feat(sdk): add create_decision_evaluator and DecisionEvaluator (SAO-17973) - #273
Conversation
…-18652) Send X-Galileo-Scorer-Capabilities on /scorers*, */scorer-settings and */metric_settings endpoints so the API includes decision scorers in responses, and add decision to the ScorerTypes and ModelType enums. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…nerated client (SAO-17973) Splice the decision path, CreateDecisionScorerVersionRequest, DecisionLevel, DecisionScorerDB, boolean_probability and decision_scorer into openapi.yaml from the prod client spec (full regeneration yields an unrelated ~45k-line diff) and regenerate. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…7973) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
baz review |
|
|
||
| _logger = logging.getLogger(__name__) | ||
|
|
||
| DEFAULT_DECISION_MODEL_NAME = "jev-1.13.0" |
There was a problem hiding this comment.
should this be defined in code like this?
There was a problem hiding this comment.
Changed in f450fb0 to match how LLM-as-a-judge handles its default model:
DecisionEvaluatornow falls back toConfiguration.default_decision_model(envSPLUNK_AO_DEFAULT_DECISION_MODEL, defaultjev-1.13.0), the same wayLlmEvaluatorusesdefault_scorer_model/SPLUNK_AO_DEFAULT_SCORER_MODEL. Users can move to a new Jev version without waiting for an SDK release.create_decision_evaluatorkeeps a literalmodel_name="jev-1.13.0"default, ascreate_custom_llm_evaluatordoes with"gpt-4.1-mini". The module constant is gone.
Follow-up (not in this PR): Jev names pin a version that TypeSafe will deprecate and retire, so the longer-term fix is for the API to default to the registry's recommended model (recommended_decision_model) when model_name is omitted. Both SDKs could then stop shipping a default.
…creating the scorer (SAO-17973) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…973) DecisionEvaluator falls back to Configuration.default_decision_model (env SPLUNK_AO_DEFAULT_DECISION_MODEL, default jev-1.13.0), the same way LlmEvaluator uses default_scorer_model. create_decision_evaluator keeps a literal default, like create_custom_llm_evaluator. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
etserend
left a comment
There was a problem hiding this comment.
🤖 This review was generated by the Astra agent (claude-opus-5-5). It may contain mistakes.
Verdict: approve — The create, rollback and decode paths are correct and well tested. Only minor issues remain: env-var default parity, status-code classification and a CHANGELOG gap.
Follow-ups
Suggested follow-up work that could be tracked as Jira tickets:
codegen_templates/endpoint_module.py.jinja:17-18: The PR description names a generator bug: the unionto_dictbranches forlist[str] | list[DecisionLevel]are identical.DecisionScorerDB.to_dict(src/splunk_ao/resources/models/decision_scorer_db.py:63-70) has the same bug, so a round-trip of a discrete scorer never serializes levels throughDecisionLevel.to_dict. Fix theunion_propertytemplate and drop theadditional_propertiesworkaround in_build_decision_version_request.
| name: str, | ||
| question: str, | ||
| node_level: StepType = StepType.llm, | ||
| model_name: str = "jev-1.13.0", |
There was a problem hiding this comment.
🟡 minor (design): The thread reply says SPLUNK_AO_DEFAULT_DECISION_MODEL lets users "move to a new Jev version without waiting for an SDK release". That holds only for DecisionEvaluator (src/splunk_ao/evaluator.py:946). Evaluators.create_decision_evaluator and the module function (line 493) hard-code "jev-1.13.0". As a result, a user who sets the env var and calls create_decision_evaluator(...) still gets jev-1.13.0. The ConfigKey description, "Default model for decision evaluators" (src/splunk_ao/configuration.py:113), suggests that both paths use it. The LLM functions copy this gap, but this API is new, so it is cheap to fix now. Suggested fix: default model_name to None in both signatures and resolve it with model_name or Configuration.default_decision_model. The literal then exists in one place only.
| model_name: str = "jev-1.13.0", | |
| model_name: str | None = None, |
🤖 Generated by the Astra agent
| ) | ||
|
|
||
|
|
||
| REJECTION_STATUS_CODES = frozenset({400, 403, 404, 422}) |
There was a problem hiding this comment.
🟡 minor (question): The generated endpoint raises AuthenticationError for 401 and ConflictError for 409 (create_decision_scorer_version_scorers_scorer_id_version_decision_post.py:60-67). Both mean the server refused the request. Neither is in this set, so both fall through to DecisionVersionOutcomeUnknownError. The result is an orphan scorer with no version and an error that says "may have a version", which misleads the user. Please confirm that this choice is intentional. If it is not, add 401 and 409. The delete may also fail on 401, but _delete_scorer_quietly already logs and swallows that failure.
🤖 Generated by the Astra agent
| from `answer_options`. If the version request is clearly rejected (400, 403, 404, 422) the new scorer is | ||
| deleted; on a timeout, network error or 5xx it is kept and the raised error names its id. | ||
| - Scorer requests now send `X-Galileo-Scorer-Capabilities: decision`, and `ScorerTypes` and `ModelType` include | ||
| `decision`, so decision scorers appear in list and get responses and decode. |
There was a problem hiding this comment.
🟡 minor (documentation): This change adds a public configuration key: Configuration.default_decision_model / SPLUNK_AO_DEFAULT_DECISION_MODEL (src/splunk_ao/configuration.py:110-115). The Added section does not mention it. AGENTS.md asks for CHANGELOG updates when public behavior changes. Please add one bullet that names the env var and its jev-1.13.0 default.
🤖 Generated by the Astra agent
Why
Decision-model evaluators (
scorer_type: "decision", for non-LLM judges such as Jev) are live in the API and UI. This adds the Python SDK side: SAO-17973. It also ports SAO-18652 (galileo-python #639), without which the API leaves decision scorers out of list/get responses and the SDK cannot decode them.What
create_decision_evaluator(name, question, node_level, model_name, output_type=None, answer_options, input_type, description, tags, ground_truth)as anEvaluatorsmethod and module function. Inputs are validated up front. Ifoutput_typeis omitted it is inferred fromanswer_options(None to boolean_probability, strings to categorical, levels to discrete); a mismatch raisesValueError.DecisionVersionOutcomeUnknownError(names the scorer id, chains the cause, exported fromsplunk_ao) is raised.DecisionEvaluator(Evaluator):create(), get/list populate question, answer options (including discrete levels the decoder returns as raw dicts), model, node level and output type.X-Galileo-Scorer-Capabilities: decisionheader on/scorers*,*/scorer-settings,*/metric_settings(codegen template andget_scorer_capabilities_header);decisionadded toScorerTypesandModelType.Spec and generator notes
CreateDecisionScorerVersionRequest,DecisionLevel,DecisionScorerDB,OutputTypeEnum += boolean_probabilityanddecision_scoreronBaseScorerVersionResponse/BaseScorerVersionDBwere spliced intoopenapi.yamlfrom the prod client spec, thenscripts/auto-generate-api-client.shwas run.resources/is generated only. (Main's generatedcontrol_applies_to.pyalready drifts from the spec; that regen noise is left out.)CreateDecisionScorerVersionRequest.to_dicthas identicalisinstance(list)branches forlist[str] | list[DecisionLevel], so levels would never serialize. Worked around by sending levels throughadditional_properties(_build_decision_version_request). Fixing theunion_propertytemplate is a follow-up because it touches many generated models.Where to start
src/splunk_ao/evaluators.py(helpers,create_decision_evaluator), thensrc/splunk_ao/evaluator.py(DecisionEvaluator), thentests/test_decision_scorers.py. Commit 1 is the SAO-18652 port, commit 2 the spec splice and regeneration (generated).Tests
poetry run pytest(2386 passed, 4 skipped);poetry run invoke type-checkclean.Related PRs: galileo-python #639 (SAO-18652 original), galileo-js#727 (parity).
🤖 Generated with Claude Code