Skip to content

Commit 734129d

Browse files
committed
Roll forward to launchdarkly-server-sdk-ai 0.20
Apply SDK 0.20 API surface changes (mirrors PR #24): - Import from ldai instead of ldai.client - LDAIAgentConfig -> AIAgentConfigRequest, LDAIAgentDefaults -> AIAgentConfigDefault - ai_client.agent(config, ctx) -> ai_client.agent_config(key, ctx, default=...) - config.tracker (property) -> config.create_tracker() (factory) - tracker.track_latency_ms() -> tracker.track_duration() - Bump launchdarkly-server-sdk-ai to >=0.20.0 Also drop stale ld-aic-cicd entry from uv.lock dev deps (not in pyproject).
1 parent 843eab7 commit 734129d

9 files changed

Lines changed: 58 additions & 718 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",

utils/metrics.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def wrapper(*args, **kwargs):
1313
try:
1414
# Track operation start
1515
config_manager.track_metrics(
16-
supervisor_config.tracker,
16+
supervisor_config.create_tracker(),
1717
lambda: f"{metric_name}_start",
1818
model_name=supervisor_config.model.name if hasattr(supervisor_config, 'model') else None
1919
)
@@ -23,7 +23,7 @@ def wrapper(*args, **kwargs):
2323

2424
# Track successful completion
2525
config_manager.track_metrics(
26-
supervisor_config.tracker,
26+
supervisor_config.create_tracker(),
2727
lambda: f"{metric_name}_success",
2828
model_name=supervisor_config.model.name if hasattr(supervisor_config, 'model') else None
2929
)
@@ -35,7 +35,7 @@ def wrapper(*args, **kwargs):
3535

3636
# Track error with LDAI metrics
3737
config_manager.track_metrics(
38-
supervisor_config.tracker,
38+
supervisor_config.create_tracker(),
3939
lambda: (_ for _ in ()).throw(e), # Trigger error tracking
4040
model_name=supervisor_config.model.name if hasattr(supervisor_config, 'model') else None
4141
)
@@ -47,7 +47,7 @@ def wrapper(*args, **kwargs):
4747
def track_supervisor_decision(config_manager: Any, supervisor_config: Any, next_agent: str):
4848
"""Helper to track supervisor routing decisions"""
4949
config_manager.track_metrics(
50-
supervisor_config.tracker,
50+
supervisor_config.create_tracker(),
5151
lambda: f"supervisor_decision_success_{next_agent}",
5252
model_name=supervisor_config.model.name if hasattr(supervisor_config, 'model') else None
5353
)
@@ -56,7 +56,7 @@ def track_supervisor_decision(config_manager: Any, supervisor_config: Any, next_
5656
def track_workflow_completion(config_manager: Any, supervisor_config: Any, tool_calls: list):
5757
"""Helper to track supervisor workflow completion"""
5858
config_manager.track_metrics(
59-
supervisor_config.tracker,
59+
supervisor_config.create_tracker(),
6060
lambda: f"supervisor_workflow_complete_tools_{len(tool_calls)}",
6161
model_name=supervisor_config.model.name if hasattr(supervisor_config, 'model') else None
6262
)
@@ -66,7 +66,7 @@ def track_agent_orchestration(config_manager: Any, supervisor_config: Any, agent
6666
"""Helper to track agent orchestration start"""
6767
# Track orchestration start
6868
config_manager.track_metrics(
69-
supervisor_config.tracker,
69+
supervisor_config.create_tracker(),
7070
lambda: f"supervisor_orchestrating_{agent_name}_start",
7171
model_name=supervisor_config.model.name if hasattr(supervisor_config, 'model') else None
7272
)
@@ -76,13 +76,13 @@ def track_agent_success(config_manager: Any, supervisor_config: Any, agent_name:
7676
"""Helper to track agent orchestration success"""
7777
if tool_calls is not None:
7878
config_manager.track_metrics(
79-
supervisor_config.tracker,
79+
supervisor_config.create_tracker(),
8080
lambda: f"supervisor_orchestrating_{agent_name}_success_tools_{len(tool_calls)}",
8181
model_name=supervisor_config.model.name if hasattr(supervisor_config, 'model') else None
8282
)
8383
else:
8484
config_manager.track_metrics(
85-
supervisor_config.tracker,
85+
supervisor_config.create_tracker(),
8686
lambda: f"supervisor_orchestrating_{agent_name}_success",
8787
model_name=supervisor_config.model.name if hasattr(supervisor_config, 'model') else None
8888
)

0 commit comments

Comments
 (0)