Skip to content

Commit bc7251b

Browse files
jsonbaileyclaude
andcommitted
fix: Modernize devrel agents tutorial to current Python AI SDK 0.20
- config_manager.py: replace removed LDAIAgentConfig/LDAIAgentDefaults with AIAgentConfigRequest/AIAgentConfigDefault; default_value= -> default= kwarg rename; replace removed .agent() method with agent_config(...) - Hoist tracker = config.create_tracker() (replaces removed config.tracker) across config_manager, supervisor_agent, security_agent, ld_agent_helpers, agent_service, api/main - Replace non-existent track_latency_ms with track_duration in ld_agent_helpers - Canonicalize from ldai.client -> from ldai imports - Bump pyproject.toml pin to launchdarkly-server-sdk-ai>=0.20.0 Refs AIC-2383 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 843eab7 commit bc7251b

7 files changed

Lines changed: 38 additions & 33 deletions

File tree

agents/ld_agent_helpers.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ async def create_agent_with_fresh_config(
275275
prompt=agent_config.instructions
276276
)
277277

278-
return agent, agent_config.tracker, False, effective_recursion_limit
278+
return agent, agent_config.create_tracker(), False, effective_recursion_limit
279279

280280
except Exception as e:
281281
log_student(f"ERROR in create_agent_with_fresh_config: {e}")
@@ -355,8 +355,7 @@ async def ainvoke(self, request_data: dict) -> dict:
355355
# Track success and latency
356356
tracker.track_success()
357357
latency_ms = int((time.time() - start_time) * 1000)
358-
if hasattr(tracker, 'track_latency_ms'):
359-
tracker.track_latency_ms(latency_ms)
358+
tracker.track_duration(latency_ms)
360359

361360
# Extract token usage from new messages
362361
new_messages = response['messages'][prev_message_count:]

agents/security_agent.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ async def call_model(state: AgentState):
112112
"response": "Security check disabled"
113113
}
114114

115+
# Create tracker for token, cost, and success/error metrics
116+
tracker = agent_config.create_tracker()
117+
115118
# Create model with structured output and up-to-date instructions
116119
from agents.ld_agent_helpers import map_provider_to_langchain, create_bedrock_chat_model
117120
from utils.bedrock_helpers import normalize_bedrock_provider
@@ -212,7 +215,7 @@ def _select_provider_and_model(default_provider: str, default_model: str) -> tup
212215
output=usage_data.get("output_tokens", 0),
213216
total=usage_data.get("total_tokens", 0)
214217
)
215-
agent_config.tracker.track_tokens(token_usage)
218+
tracker.track_tokens(token_usage)
216219
log_student(f"SECURITY PII DETECTION TOKENS: {token_usage.total} tokens ({token_usage.input} in, {token_usage.output} out)")
217220

218221
# Track cost metric with AI Config metadata for experiment attribution
@@ -229,7 +232,7 @@ def _select_provider_and_model(default_provider: str, default_model: str) -> tup
229232
log_student(f"COST TRACKING: ${cost:.6f} for {agent_config.model.name}")
230233

231234
# Track success metric
232-
agent_config.tracker.track_success()
235+
tracker.track_success()
233236

234237
# Extract structured results
235238
detected = pii_result.detected
@@ -248,11 +251,11 @@ def _select_provider_and_model(default_provider: str, default_model: str) -> tup
248251
}
249252

250253
except Exception:
251-
254+
252255
# Track error with LDAI metrics
253256
try:
254-
if 'agent_config' in locals() and agent_config and hasattr(agent_config, 'tracker'):
255-
agent_config.tracker.track_error()
257+
if 'agent_config' in locals() and agent_config:
258+
agent_config.create_tracker().track_error()
256259
except Exception:
257260
pass
258261

agents/supervisor_agent.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,14 @@ def create_supervisor_agent(supervisor_config, support_config, security_config,
119119

120120
# Import needed modules for model creation
121121
from langchain.chat_models import init_chat_model
122-
122+
123123
# Create child agents with config manager
124124
support_agent = create_support_agent(support_config, config_manager)
125125
security_agent = create_security_agent(security_config, config_manager)
126126

127+
# Create tracker for the supervisor's own LLM calls and orchestration metrics
128+
supervisor_tracker = supervisor_config.create_tracker()
129+
127130
log_debug(f"SUPERVISOR INSTRUCTIONS: {supervisor_config.instructions}")
128131

129132
def _select_provider_and_model(default_provider: str, default_model: str) -> tuple[str, str]:
@@ -217,7 +220,7 @@ def pii_prescreen_node(state: SupervisorState):
217220
output=usage_data.get("output_tokens", 0),
218221
total=usage_data.get("total_tokens", 0)
219222
)
220-
supervisor_config.tracker.track_tokens(token_usage)
223+
supervisor_tracker.track_tokens(token_usage)
221224
log_student(f"PII PRESCREEN TOKENS: {token_usage.total} tokens ({token_usage.input} in, {token_usage.output} out)")
222225

223226
# Track cost metric with AI Config metadata for experiment attribution
@@ -234,7 +237,7 @@ def pii_prescreen_node(state: SupervisorState):
234237
log_student(f"COST TRACKING: ${cost:.6f} for {supervisor_config.model.name}")
235238

236239
# Track success metric
237-
supervisor_config.tracker.track_success()
240+
supervisor_tracker.track_success()
238241

239242
# Log the intelligent decision
240243
log_student(f"ROUTING: {screening_result.recommended_route} ({screening_result.confidence:.1f}) - {screening_result.reasoning}")
@@ -390,8 +393,7 @@ async def security_node(state: SupervisorState):
390393

391394
except Exception as e:
392395
# Track error with LDAI metrics
393-
if 'supervisor_config' in locals() and supervisor_config and hasattr(supervisor_config, 'tracker'):
394-
supervisor_config.tracker.track_error()
396+
supervisor_tracker.track_error()
395397

396398
return {
397399
"messages": [AIMessage(content=f"Security agent error: {e}")],
@@ -501,8 +503,7 @@ async def support_node(state: SupervisorState):
501503

502504
except Exception as e:
503505
# Track error with LDAI metrics
504-
if 'supervisor_config' in locals() and supervisor_config and hasattr(supervisor_config, 'tracker'):
505-
supervisor_config.tracker.track_error()
506+
supervisor_tracker.track_error()
506507

507508
return {
508509
"messages": [AIMessage(content=f"Support agent error: {e}")],

api/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ async def submit_feedback(feedback: FeedbackRequest):
110110

111111
# Track feedback using config_manager
112112
success = agent_service.config_manager.track_feedback(
113-
support_config.tracker,
113+
support_config.create_tracker(),
114114
thumbs_up=thumbs_up
115115
)
116116

api/services/agent_service.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,9 @@ async def process_message(self, user_id: str, message: str, user_context: dict =
236236
def get_variation_key(ai_config, agent_name):
237237
try:
238238
# The variation key is stored in the tracker object
239-
if hasattr(ai_config, 'tracker') and hasattr(ai_config.tracker, '_variation_key'):
240-
variation_key = ai_config.tracker._variation_key
239+
tracker = ai_config.create_tracker()
240+
if hasattr(tracker, '_variation_key'):
241+
variation_key = tracker._variation_key
241242
log_debug(f"VARIATION EXTRACTED for {agent_name}: {variation_key}")
242243
return variation_key
243244
else:

config_manager.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from pathlib import Path
99
import ldclient
1010
from ldclient import Context
11-
from ldai.client import LDAIClient, LDAIAgentConfig, LDAIAgentDefaults, ModelConfig, ProviderConfig
11+
from ldai import LDAIClient, AIAgentConfigRequest, AIAgentConfigDefault, ModelConfig, ProviderConfig
1212
from ldai.tracker import FeedbackKind
1313
from dotenv import load_dotenv
1414
from utils.logger import log_student, log_debug
@@ -68,14 +68,14 @@ def _load_config_defaults(self):
6868
" ld-aic validate --config-keys 'supervisor-agent,support-agent,security-agent'"
6969
)
7070

71-
def _get_default_config(self, config_key: str) -> LDAIAgentDefaults:
71+
def _get_default_config(self, config_key: str) -> AIAgentConfigDefault:
7272
"""Get fallback config from .ai_config_defaults.json
7373
7474
Args:
7575
config_key: The AI config key (e.g., 'support-agent')
7676
7777
Returns:
78-
LDAIAgentDefaults object with config from the defaults file
78+
AIAgentConfigDefault object with config from the defaults file
7979
8080
Raises:
8181
ValueError: If config key not found in defaults
@@ -93,9 +93,9 @@ def _get_default_config(self, config_key: str) -> LDAIAgentDefaults:
9393

9494
config_data = self.config_defaults[config_key]
9595

96-
# Convert JSON config to LDAIAgentDefaults
96+
# Convert JSON config to AIAgentConfigDefault
9797
# Note: Tools are managed by LaunchDarkly and not part of defaults
98-
return LDAIAgentDefaults(
98+
return AIAgentConfigDefault(
9999
enabled=config_data.get("enabled", True),
100100
model=ModelConfig(
101101
name=config_data["model"]["name"],
@@ -207,21 +207,21 @@ async def get_config(self, user_id: str, config_key: str = None, user_context: d
207207
default_config = self._get_default_config(ai_config_key)
208208
log_debug(f"CONFIG MANAGER: Loaded fallback default - model: {default_config.model.name}")
209209

210-
agent_config = LDAIAgentConfig(
211-
key=ai_config_key,
212-
default_value=default_config # Use validated production defaults from file
210+
# Call LaunchDarkly - SDK automatically falls back to default if LD unavailable
211+
result = self.ai_client.agent_config(
212+
ai_config_key,
213+
ld_user_context,
214+
default=default_config, # Use validated production defaults from file
213215
)
214-
215-
# Call LaunchDarkly - SDK automatically falls back to default_value if LD unavailable
216-
result = self.ai_client.agent(agent_config, ld_user_context)
217216
log_debug("CONFIG MANAGER: ✅ Got config (from LaunchDarkly or fallback)")
218217

219218
# Debug the actual configuration received (basic info only)
220219
try:
221220
config_dict = result.to_dict()
222221
log_debug(f"CONFIG MANAGER: Model: {config_dict.get('model', {}).get('name', 'unknown')}")
223-
if hasattr(result, 'tracker') and hasattr(result.tracker, '_variation_key'):
224-
log_debug(f"CONFIG MANAGER: Variation: {result.tracker._variation_key}")
222+
tracker = result.create_tracker()
223+
if hasattr(tracker, '_variation_key'):
224+
log_debug(f"CONFIG MANAGER: Variation: {tracker._variation_key}")
225225
except Exception as debug_e:
226226
log_debug(f"CONFIG MANAGER: Could not debug result: {debug_e}")
227227

@@ -251,10 +251,11 @@ def track_cost_metric(self, agent_config, context, cost, config_key):
251251
"""
252252
try:
253253
# Extract metadata from agent_config for experiment attribution
254+
tracker = agent_config.create_tracker()
254255
metadata = {
255256
"version": 1,
256257
"configKey": config_key,
257-
"variationKey": agent_config.tracker._variation_key if hasattr(agent_config.tracker, '_variation_key') else 'unknown',
258+
"variationKey": tracker._variation_key if hasattr(tracker, '_variation_key') else 'unknown',
258259
"modelName": agent_config.model.name if hasattr(agent_config, 'model') else 'unknown',
259260
"providerName": agent_config.provider.name if hasattr(agent_config, 'provider') else 'unknown'
260261
}

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ dependencies = [
1414
"pydantic>=2.0.0",
1515
# LaunchDarkly
1616
"launchdarkly-server-sdk>=9.0.0",
17-
"launchdarkly-server-sdk-ai>=0.1.0",
17+
"launchdarkly-server-sdk-ai>=0.20.0",
1818
# AWS Bedrock Integration
1919
"langchain-aws>=0.2.0",
2020
"boto3>=1.35.0",

0 commit comments

Comments
 (0)