-
Notifications
You must be signed in to change notification settings - Fork 0
feat: record judge scores as gen_ai.evaluation.result #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ccschmitz-launchdarkly
wants to merge
7
commits into
main
Choose a base branch
from
O11Y-1888-judge-evals
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
2604816
feat: record judge scores as gen_ai.evaluation.result
71b0334
Merge branch 'O11Y-1888-emit-conversation-id-and-evaluation' into O11…
8fa4f6f
fix: stop exporting judge reasoning, validate the score, freeze the e…
74b24d9
Merge branch 'O11Y-1888-emit-conversation-id-and-evaluation' into O11…
ccschmitz-launchdarkly e600df9
docs: add a multi-turn conversation + judge example
ccschmitz-launchdarkly 005ce8e
Merge remote-tracking branch 'origin/main' into O11Y-1888-judge-evals
ccschmitz-launchdarkly 8a66365
feat: gate the judge explanation on capture_content
ccschmitz-launchdarkly File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| """ | ||
| Example: a multi-turn conversation grouped under one ``gen_ai.conversation.id``, with inline | ||
| judge evaluation on every turn. | ||
|
|
||
| This is the end-to-end check for O11Y-1888. Run it, then open the printed conversation id in | ||
| LaunchDarkly's Conversations view and confirm: | ||
|
|
||
| 1. One conversation, three turns — not three conversations. Every span of every turn carries | ||
| the same id: root, ``chat``, ``execute_tool``, and the judge's own ``invoke_agent``. | ||
| 2. Each turn shows a score badge, sourced from the ``gen_ai.evaluation.result`` span event on | ||
| the judge span. | ||
| 3. No judge reasoning anywhere in the telemetry. The score and the judge's config key are | ||
| exported; the explanation is not, because it is model prose about the user's conversation | ||
| and content attributes require ``capture_content``. The reasoning IS printed below, straight | ||
| from ``judge_results`` — that is the caller's copy, and it is unaffected. | ||
|
|
||
| The flag key must point at an AI Config with a ``judge_configuration``, otherwise there are no | ||
| judge turns to look at. | ||
|
|
||
| Usage (via main.py): | ||
| python main.py conversation <flag-key> "<opening message>" | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import sys | ||
| from typing import Any | ||
|
|
||
| import examples.register # noqa: F401 – side-effect: populate global_registry | ||
| from examples.utils import new_context, new_conversation_id | ||
| from launchdarkly_ai_server import config, conversation_id, global_registry | ||
|
|
||
| FOLLOW_UPS = [ | ||
| "Can you give me a concrete example of that?", | ||
| "What is the most common mistake teams make with it?", | ||
| ] | ||
|
|
||
|
|
||
| async def run(key: str, user_input: str) -> None: | ||
| conversation = new_conversation_id("conversation-example") | ||
| ctx = new_context() | ||
| history: list[dict[str, Any]] = [] | ||
|
|
||
| print(f"[conversation] {conversation}", file=sys.stderr) | ||
|
|
||
| turns = [user_input or "What is a feature flag?", *FOLLOW_UPS] | ||
|
|
||
| for index, prompt in enumerate(turns, start=1): | ||
| # One binding per turn, same id every time — that is what makes them one conversation | ||
| # rather than three. Re-binding per turn is the realistic shape: each turn is usually a | ||
| # separate inbound request that looks the id up from its own thread/session. | ||
| with conversation_id(conversation): | ||
| response = await config( | ||
| key=key, | ||
| registry=global_registry, | ||
| ).invoke(prompt, ctx, None, history) | ||
|
|
||
| text = ( | ||
| response.response | ||
| if isinstance(response.response, str) | ||
| else str(response.response) | ||
| ) | ||
| print(f"\n─── turn {index} ───\n> {prompt}\n{text}") | ||
|
|
||
| judge_results = response.judge_results or {} | ||
| for judge_key, result in judge_results.items(): | ||
| # `response` here is the judge's reasoning. It reaches the caller and is deliberately | ||
| # absent from the span — see the module docstring. | ||
| score = getattr(result, "score", None) | ||
| reasoning = getattr(result, "response", None) | ||
| print( | ||
| f"[judge] {judge_key} score={score} reasoning={reasoning}", | ||
| file=sys.stderr, | ||
| ) | ||
| if not judge_results: | ||
| print( | ||
| "[judge] no judges ran — does this AI Config have a judge_configuration?", | ||
| file=sys.stderr, | ||
| ) | ||
|
|
||
| history.append({"role": "user", "content": prompt}) | ||
| history.append({"role": "assistant", "content": text}) | ||
|
|
||
| print( | ||
| f"\n[conversation] done — open {conversation} in the Conversations view", | ||
| file=sys.stderr, | ||
| ) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Judge example reads dicts as objects
Medium Severity
The new conversation example prints judge
scoreandreasoningviagetattron eachjudge_resultsvalue. Inline judges still store plain dicts (score/responsekeys), so those attributes are missing and the example always printsNoneeven when a judge ran. That makes the O11Y-1888 end-to-end check look like it had no scores.Reviewed by Cursor Bugbot for commit 005ce8e. Configure here.