Skip to content

Commit c0e0eac

Browse files
jsonbaileyclaude
andcommitted
chore: update examples for AI SDK ManagedResult and tracker factory APIs
Migrate examples to the AI SDK API surface introduced by the upcoming ldai 0.18 / openai 0.4 / langchain 0.5 release stack: - ManagedModel.invoke() -> .run(); ModelResponse.message.content -> ManagedResult.content - chat_response.evaluations is a single asyncio.Task[List[JudgeResult]]; await it directly instead of asyncio.gather over a list - JudgeResult is a single value (not List); access .sampled / .success / .metric_key / .score / .reasoning / .error_message directly - AIConfig.tracker (field) -> AIConfig.create_tracker() (factory) - track_metrics_of(metrics_extractor, func) parameter order; use track_metrics_of_async for awaited callables (langchain example) - aiclient.create_judge() is sync; drop the unnecessary `await` - direct_judge default key updated to `sample-ai-judge` (configured) - chat_observability now declares launchdarkly-server-sdk-ai-openai as a dependency since it uses aiclient.create_model() which loads providers via the runner factory Bumps published version pins to ^0.18 / ^0.4 / ^0.5 to match the new API. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 916a13e commit c0e0eac

13 files changed

Lines changed: 74 additions & 64 deletions

examples/chat_observability/chat_observability_example.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,22 +88,22 @@ async def async_main():
8888
user_input_1 = "What is feature flagging in 2 sentences?"
8989
print("User Input:", user_input_1)
9090

91-
response_1 = await chat.invoke(user_input_1)
92-
print("Chat Response:", response_1.message.content)
91+
response_1 = await chat.run(user_input_1)
92+
print("Chat Response:", response_1.content)
9393

9494
user_input_2 = "Give me a specific use case example."
9595
print("\nUser Input:", user_input_2)
9696

97-
response_2 = await chat.invoke(user_input_2)
98-
print("Chat Response:", response_2.message.content)
97+
response_2 = await chat.run(user_input_2)
98+
print("Chat Response:", response_2.content)
9999

100-
# Judge evaluations run asynchronously. Await them (e.g. with asyncio.gather) so they
100+
# Judge evaluations run asynchronously. Await them so they
101101
# complete before the process or request ends—even if you don't need to log or use
102102
# the results.
103-
if response_1.evaluations:
104-
await asyncio.gather(*response_1.evaluations)
105-
if response_2.evaluations:
106-
await asyncio.gather(*response_2.evaluations)
103+
if response_1.evaluations is not None:
104+
await response_1.evaluations
105+
if response_2.evaluations is not None:
106+
await response_2.evaluations
107107

108108
print("\nSuccess.")
109109

examples/chat_observability/pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ chat-observability-example = "chat_observability_example:main"
1313
[tool.poetry.dependencies]
1414
python = "^3.10"
1515
python-dotenv = ">=1.0.0"
16-
launchdarkly-server-sdk-ai = "^0.17.0"
16+
launchdarkly-server-sdk-ai = "^0.18.0"
17+
launchdarkly-server-sdk-ai-openai = "^0.4.0"
1718
launchdarkly-observability = ">=0.1.0"
1819
openai = ">=0.2.0"
1920

examples/judge/chat_judge_example.py

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import os
22
from dotenv import load_dotenv
3-
import json
43
import asyncio
54
import ldclient
65
from ldclient import Context
@@ -63,33 +62,38 @@ async def async_main():
6362
user_input = 'How can LaunchDarkly help me?'
6463
print("User Input:", user_input)
6564

66-
# The invoke method will automatically evaluate the chat response with any judges defined in the AI config
67-
chat_response = await chat.invoke(user_input)
68-
print("Chat Response:", chat_response.message.content)
65+
# The run method will automatically evaluate the chat response with any judges defined in the AI config
66+
chat_response = await chat.run(user_input)
67+
print("Chat Response:", chat_response.content)
6968

70-
# Judge evaluations run asynchronously. Await them (e.g. with asyncio.gather) so they
71-
# complete before the process or request ends—even if you don't need to log or use
72-
# the results. Below we await and then log the results for demonstration.
69+
# Judge evaluations run asynchronously. Await them so they complete before the
70+
# process or request ends—even if you don't need to log or use the results.
71+
# Below we await and then log the results for demonstration.
7372

7473
# Log judge evaluation results with full detail
75-
if chat_response.evaluations is not None and len(chat_response.evaluations) > 0:
74+
if chat_response.evaluations is not None:
7675
# Note: Judge evaluations run asynchronously and do not block your application.
7776
# Results are automatically sent to LaunchDarkly for AI config metrics.
7877
# You only need to await if you want to access the evaluation results in your code.
7978
print("\nNote: Awaiting judge results (optional - done here for demonstration only).")
80-
eval_results = await asyncio.gather(*chat_response.evaluations)
81-
82-
# Convert results, replacing None with a message
83-
results_to_display = [
84-
result.to_dict() if result is not None else "not evaluated"
85-
for result in eval_results
86-
]
87-
79+
eval_results = await chat_response.evaluations
80+
8881
print("Judge results:")
89-
print(json.dumps(results_to_display, indent=2, default=str))
90-
91-
if None in eval_results:
92-
print("\nNote: Some judge evaluations were skipped.")
82+
for result in eval_results:
83+
print(f" - sampled: {result.sampled}")
84+
print(f" success: {result.success}")
85+
if result.error_message is not None:
86+
print(f" error_message: {result.error_message}")
87+
if result.metric_key is not None:
88+
print(f" metric_key: {result.metric_key}")
89+
if result.score is not None:
90+
print(f" score: {result.score}")
91+
if result.reasoning is not None:
92+
print(f" reasoning: {result.reasoning}")
93+
94+
skipped = [r for r in eval_results if not r.sampled]
95+
if skipped:
96+
print("\nNote: Some judge evaluations were skipped (not sampled).")
9397
print("This typically happens when the sample rate doesn't require this evaluation, or due to a configuration issue.")
9498
print("Check application logs for more details.")
9599
else:

examples/judge/direct_judge_example.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import os
22
from dotenv import load_dotenv
3-
import json
43
import asyncio
54
import ldclient
65
from ldclient import Context
@@ -13,7 +12,7 @@
1312
sdk_key = os.getenv('LAUNCHDARKLY_SDK_KEY')
1413

1514
# Set judge_key to the Judge key you want to use.
16-
judge_key = os.getenv('LAUNCHDARKLY_AI_JUDGE_KEY', 'sample-ai-judge-accuracy')
15+
judge_key = os.getenv('LAUNCHDARKLY_AI_JUDGE_KEY', 'sample-ai-judge')
1716

1817

1918
async def async_main():
@@ -54,8 +53,8 @@ async def async_main():
5453
# {'role': 'user', 'content': 'RESPONSE TO EVALUATE: {{response_to_evaluate}}'},
5554
# ],
5655
# )
57-
# judge = await aiclient.create_judge(judge_key, context, default)
58-
judge = await aiclient.create_judge(judge_key, context)
56+
# judge = aiclient.create_judge(judge_key, context, default)
57+
judge = aiclient.create_judge(judge_key, context)
5958

6059
if not judge:
6160
print(f"*** AI judge configuration is not enabled for key: {judge_key}")
@@ -70,20 +69,26 @@ async def async_main():
7069

7170
judge_response = await judge.evaluate(input_text, output_text)
7271

73-
if judge_response is None:
74-
print("\nJudge evaluation was skipped.")
75-
print("This typically happens when the sample rate doesn't require this evaluation, or due to a configuration issue.")
76-
print("Check application logs for more details.")
77-
return
78-
7972
# Track the judge evaluation scores on the tracker for the aiConfig you are evaluating
8073
# Example:
8174
# aiConfig.tracker.track_eval_scores(judge_response.evals)
8275

83-
# Convert JudgeResponse to dict for display using to_dict()
84-
judge_response_dict = judge_response.to_dict()
8576
print("Judge Response:")
86-
print(json.dumps(judge_response_dict, indent=2, default=str))
77+
print(f" sampled: {judge_response.sampled}")
78+
print(f" success: {judge_response.success}")
79+
if judge_response.error_message is not None:
80+
print(f" error_message: {judge_response.error_message}")
81+
if judge_response.metric_key is not None:
82+
print(f" metric_key: {judge_response.metric_key}")
83+
if judge_response.score is not None:
84+
print(f" score: {judge_response.score}")
85+
if judge_response.reasoning is not None:
86+
print(f" reasoning: {judge_response.reasoning}")
87+
88+
if not judge_response.sampled:
89+
print("\nNote: Judge evaluation was not sampled.")
90+
print("This typically happens when the sample rate doesn't require this evaluation, or due to a configuration issue.")
91+
print("Check application logs for more details.")
8792

8893
print("Success.")
8994
except Exception as err:

examples/judge/pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ direct-judge-example = "direct_judge_example:main"
1717
[tool.poetry.dependencies]
1818
python = "^3.10"
1919
python-dotenv = ">=1.0.0"
20-
launchdarkly-server-sdk-ai = "^0.17.0"
21-
launchdarkly-server-sdk-ai-openai = "^0.3.0"
22-
launchdarkly-server-sdk-ai-langchain = "^0.4.0"
20+
launchdarkly-server-sdk-ai = "^0.18.0"
21+
launchdarkly-server-sdk-ai-openai = "^0.4.0"
22+
launchdarkly-server-sdk-ai-langchain = "^0.5.0"
2323
openai = ">=1.0.0"
2424

2525
[build-system]

examples/langchain/langchain_example.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,12 @@ async def async_main():
6666
context,
6767
variables={'myUserVariable': "Testing Variable"}
6868
)
69-
tracker = config_value.tracker
70-
7169
if not config_value.enabled:
7270
print("AI Config is disabled")
7371
return
7472

73+
tracker = config_value.create_tracker()
74+
7575
try:
7676
# Create LangChain model instance using init_chat_model
7777
# Map the provider from config_value to LangChain format
@@ -90,9 +90,9 @@ async def async_main():
9090
messages.append({'role': 'user', 'content': USER_INPUT})
9191

9292
# Track the LangChain completion with LaunchDarkly metrics using the LD LangChain provider's extractor
93-
completion = await tracker.track_metrics_of(
94-
lambda: llm.ainvoke(messages),
93+
completion = await tracker.track_metrics_of_async(
9594
get_ai_metrics_from_response,
95+
lambda: llm.ainvoke(messages),
9696
)
9797
ai_response = completion.content
9898

examples/langchain/pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ langchain-example = "langchain_example:main"
1313
[tool.poetry.dependencies]
1414
python = "^3.10"
1515
python-dotenv = ">=1.0.0"
16-
launchdarkly-server-sdk-ai = "^0.17.0"
17-
launchdarkly-server-sdk-ai-langchain = "^0.4.0"
16+
launchdarkly-server-sdk-ai = "^0.18.0"
17+
launchdarkly-server-sdk-ai-langchain = "^0.5.0"
1818
langchain = "^1.0.0"
1919
langchain-core = "^1.0.0"
2020
langchain-openai = "^1.0.0"

examples/langgraph_agent/langgraph_agent_example.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def main():
115115

116116
try:
117117
# Track and execute the agent
118-
response = track_langgraph_metrics(agent_config.tracker, lambda: agent.invoke({
118+
response = track_langgraph_metrics(agent_config.create_tracker(), lambda: agent.invoke({
119119
"messages": [{"role": "user", "content": "What is the weather in Tokyo?"}]
120120
}))
121121

examples/langgraph_agent/pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ langgraph-agent-example = "langgraph_agent_example:main"
1313
[tool.poetry.dependencies]
1414
python = "^3.10"
1515
python-dotenv = ">=1.0.0"
16-
launchdarkly-server-sdk-ai = "^0.17.0"
17-
launchdarkly-server-sdk-ai-langchain = "^0.4.0"
16+
launchdarkly-server-sdk-ai = "^0.18.0"
17+
launchdarkly-server-sdk-ai-langchain = "^0.5.0"
1818
langchain = "^1.0.0"
1919
langchain-core = "^1.0.0"
2020
langchain-openai = "^1.0.0"

examples/langgraph_multi_agent/langgraph_multi_agent_example.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def create_agent_with_config(aiclient, config_key, context):
9595
# Create a React agent with the LLM
9696
agent = create_react_agent(llm, [], prompt=agent_config.instructions)
9797

98-
return agent, agent_config.tracker, False
98+
return agent, agent_config.create_tracker(), False
9999

100100
def ai_node(
101101
state: CodeReviewState,

0 commit comments

Comments
 (0)