aiconfig-targeting

Configure AI Config targeting rules to control which variations serve to different users. Enable percentage rollouts, attribute-based rules, segment targeting,…

INSTALLATION
npx skills add https://github.com/launchdarkly/agent-skills --skill aiconfig-targeting
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

AI Config Targeting

Configure targeting rules for AI Configs to control which variations serve to different contexts. Works the same for both completion and agent mode.

Prerequisites

  • LaunchDarkly account with AI Configs enabled
  • API access token with write permissions
  • Project key and environment key
  • Existing AI Config with variations (use aiconfig-create skill)

API Key Detection

  • Check environment variables - LAUNCHDARKLY_API_KEY, LAUNCHDARKLY_API_TOKEN, LD_API_KEY
  • Check MCP config - Claude: ~/.claude/config.json -> mcpServers.launchdarkly.env.LAUNCHDARKLY_API_KEY
  • Prompt user - Only if detection fails

Core Concepts

Evaluation Order

Targeting rules evaluate in this order (same as feature flags):

  • Individual targets - Specific context keys (highest priority)
  • Segment rules - Pre-defined segments
  • Custom rules - Attribute-based conditions (evaluated in order)
  • Default rule - Fallthrough for all others
  • Off variation - When targeting is disabled

Semantic Patch API

AI Config targeting uses semantic patch instructions:

PATCH /api/v2/projects/{projectKey}/ai-configs/{configKey}/targeting

Content-Type: application/json; domain-model=launchdarkly.semanticpatch

Key Concepts

  • variationId: UUIDs, not keys. Always fetch targeting first to get IDs.
  • Weights: Thousandths (50000 = 50%, 100000 = 100%)
  • Clause logic: Multiple clauses = AND, multiple values = OR
  • Null attributes: Rules with null/missing attributes are skipped

Workflow

Step 1: Get Targeting (with Variation IDs)

curl -X GET "https://app.launchdarkly.com/api/v2/projects/{projectKey}/ai-configs/{configKey}/targeting" \

  -H "Authorization: {api_token}" \

  -H "LD-API-Version: beta"

Response includes variations array with _id (UUID) for each variation.

Step 2: Edit the Default Rule

Edit the default rule to serve the variation you created.

Important: The turnTargetingOn instruction does not work for AI Configs. Use updateFallthroughVariationOrRollout instead.

# First, get variation IDs from Step 1 response

# Then set fallthrough to the enabled variation (e.g., "Default" variation)

curl -X PATCH "https://app.launchdarkly.com/api/v2/projects/{projectKey}/ai-configs/{configKey}/targeting" \

  -H "Authorization: {api_token}" \

  -H "Content-Type: application/json; domain-model=launchdarkly.semanticpatch" \

  -H "LD-API-Version: beta" \

  -d '{

    "environmentKey": "production",

    "instructions": [{

      "kind": "updateFallthroughVariationOrRollout",

      "variationId": "your-enabled-variation-uuid"

    }]

  }'

Step 3: Add Targeting Rules

Attribute-based rule:

curl -X PATCH "https://app.launchdarkly.com/api/v2/projects/{projectKey}/ai-configs/{configKey}/targeting" \

  -H "Authorization: {api_token}" \

  -H "Content-Type: application/json; domain-model=launchdarkly.semanticpatch" \

  -H "LD-API-Version: beta" \

  -d '{

    "environmentKey": "production",

    "instructions": [{

      "kind": "addRule",

      "clauses": [{

        "contextKind": "user",

        "attribute": "selectedModel",

        "op": "contains",

        "values": ["sonnet"],

        "negate": false

      }],

      "variation": 0

    }]

  }'

Percentage rollout:

curl -X PATCH "..." \

  -d '{

    "environmentKey": "production",

    "instructions": [{

      "kind": "addRule",

      "clauses": [{

        "contextKind": "user",

        "attribute": "tier",

        "op": "in",

        "values": ["premium"],

        "negate": false

      }],

      "percentageRolloutConfig": {

        "contextKind": "user",

        "bucketBy": "key",

        "variations": [

          {"variation": 0, "weight": 60000},

          {"variation": 1, "weight": 40000}

        ]

      }

    }]

  }'

Set fallthrough (default rule):

curl -X PATCH "..." \

  -d '{

    "environmentKey": "production",

    "instructions": [{

      "kind": "updateFallthroughVariationOrRollout",

      "variationId": "fallback-variation-uuid"

    }]

  }'

Python Implementation

import requests

import os

from typing import Dict, List, Optional

class AIConfigTargeting:

    """Manager for AI Config targeting rules"""

    def __init__(self, api_token: str, project_key: str):

        self.api_token = api_token

        self.project_key = project_key

        self.base_url = "https://app.launchdarkly.com/api/v2"

    def get_targeting(self, config_key: str) -> Optional[Dict]:

        """Get current targeting with variation IDs."""

        url = f"{self.base_url}/projects/{self.project_key}/ai-configs/{config_key}/targeting"

        response = requests.get(url, headers={

            "Authorization": self.api_token,

            "LD-API-Version": "beta"

        })

        if response.status_code == 200:

            return response.json()

        print(f"[ERROR] {response.status_code}: {response.text}")

        return None

    def get_variation_id(self, config_key: str, variation_key: str) -> Optional[str]:

        """Look up variation UUID from key or name."""

        targeting = self.get_targeting(config_key)

        if targeting:

            for var in targeting.get("variations", []):

                if var.get("key") == variation_key or var.get("name") == variation_key:

                    return var.get("_id")

        return None

    def update_targeting(self, config_key: str, environment: str,

                         instructions: List[Dict], comment: str = "") -> Optional[Dict]:

        """Send semantic patch instructions."""

        url = f"{self.base_url}/projects/{self.project_key}/ai-configs/{config_key}/targeting"

        payload = {"environmentKey": environment, "instructions": instructions}

        if comment:

            payload["comment"] = comment

        response = requests.patch(url, headers={

            "Authorization": self.api_token,

            "Content-Type": "application/json; domain-model=launchdarkly.semanticpatch",

            "LD-API-Version": "beta"

        }, json=payload)

        if response.status_code == 200:

            return response.json()

        print(f"[ERROR] {response.status_code}: {response.text}")

        return None

    def enable_config(self, config_key: str, environment: str,

                      variation_key: str = "default") -> bool:

        """

        Enable an AI Config by setting fallthrough to an enabled variation.

        Note: turnTargetingOn doesn't work for AI Configs. Instead, set the

        fallthrough from the disabled variation (index 0) to an enabled one.

        """

        variation_id = self.get_variation_id(config_key, variation_key)

        if not variation_id:

            print(f"[ERROR] Variation '{variation_key}' not found")

            return False

        return self.set_fallthrough(config_key, environment, variation_id)

    def add_rule(self, config_key: str, environment: str,

                 clauses: List[Dict], variation: int,

                 description: str = "") -> bool:

        """Add targeting rule serving a specific variation index."""

        instruction = {

            "kind": "addRule",

            "clauses": clauses,

            "variation": variation

        }

        if description:

            instruction["description"] = description

        result = self.update_targeting(config_key, environment,

            [instruction], f"Add rule: {description}")

        if result:

            print(f"[OK] Rule added")

            return True

        return False

    def add_rollout_rule(self, config_key: str, environment: str,

                         clauses: List[Dict],

                         weights: List[Dict],

                         bucket_by: str = "key") -> bool:

        """

        Add percentage rollout rule.

        weights: [{"variation": 0, "weight": 50000}, {"variation": 1, "weight": 50000}]

        """

        result = self.update_targeting(config_key, environment, [{

            "kind": "addRule",

            "clauses": clauses,

            "percentageRolloutConfig": {

                "contextKind": "user",

                "bucketBy": bucket_by,

                "variations": weights

            }

        }], "Add percentage rollout")

        if result:

            print(f"[OK] Rollout rule added")

            return True

        return False

    def set_fallthrough(self, config_key: str, environment: str,

                        variation_id: str) -> bool:

        """Set default (fallthrough) variation by UUID."""

        result = self.update_targeting(config_key, environment, [{

            "kind": "updateFallthroughVariationOrRollout",

            "variationId": variation_id

        }], "Set fallthrough")

        if result:

            print(f"[OK] Fallthrough set")

            return True

        return False

    def target_individuals(self, config_key: str, environment: str,

                          context_keys: List[str], variation: int,

                          context_kind: str = "user") -> bool:

        """Target specific context keys."""

        result = self.update_targeting(config_key, environment, [{

            "kind": "addTargets",

            "variation": variation,

            "contextKind": context_kind,

            "values": context_keys

        }], f"Target {len(context_keys)} individuals")

        if result:

            print(f"[OK] Individual targets added")

            return True

        return False

    def target_segment(self, config_key: str, environment: str,

                      segment_keys: List[str], variation: int) -> bool:

        """Target a segment."""

        result = self.update_targeting(config_key, environment, [{

            "kind": "addRule",

            "clauses": [{

                "attribute": "segmentMatch",

                "contextKind": "",  # Leave blank for segments

                "op": "segmentMatch",

                "values": segment_keys,

                "negate": False

            }],

            "variation": variation

        }], f"Target segments: {segment_keys}")

        if result:

            print(f"[OK] Segment targeting added")

            return True

        return False

    def clear_rules(self, config_key: str, environment: str) -> bool:

        """Remove all targeting rules."""

        result = self.update_targeting(config_key, environment,

            [{"kind": "replaceRules", "rules": []}], "Clear all rules")

        if result:

            print(f"[OK] All rules cleared")

            return True

        return False

Instruction Reference

Note: turnTargetingOn and turnTargetingOff do not work for AI Configs. AI Configs have targeting enabled by default. To "enable" a config, set the fallthrough to an enabled variation using updateFallthroughVariationOrRollout.

Rules

Kind

Description

addRule

Add rule with clauses and variation/rollout

removeRule

Remove by ruleId

replaceRules

Replace all rules

reorderRules

Change evaluation order

updateRuleVariationOrRollout

Update what a rule serves

Fallthrough

Kind

Description

updateFallthroughVariationOrRollout

Set default variation or rollout

Individual Targets

Kind

Description

addTargets

Target specific context keys

removeTargets

Remove specific targets

replaceTargets

Replace all targets

Operators Reference

Operator

Description

Example

in

Value in list

["premium", "enterprise"]

contains

String contains

["sonnet"]

startsWith

String prefix

["user-"]

endsWith

String suffix

[".edu"]

matches

Regex match

["^user-\\d+$"]

greaterThan / lessThan

Numeric comparison

[100]

before / after

Date comparison

["2024-12-31T00:00:00Z"]

semVerEqual / semVerGreaterThan

Version comparison

["2.0.0"]

segmentMatch

Segment membership

["beta-testers"]

Clause Structure

{

  "contextKind": "user",

  "attribute": "email",

  "op": "endsWith",

  "values": [".edu"],

  "negate": false

}
  • Multiple clauses = AND (all must match)
  • Multiple values = OR (any can match)
  • negate: true inverts the operator

Rollout Types

Manual Percentage Rollout

{

  "percentageRolloutConfig": {

    "contextKind": "user",

    "bucketBy": "key",

    "variations": [

      {"variation": 0, "weight": 50000},

      {"variation": 1, "weight": 50000}

    ]

  }

}

Progressive Rollout

{

  "progressiveRolloutConfig": {

    "contextKind": "user",

    "controlVariation": 1,

    "endVariation": 0,

    "steps": [

      {"rolloutWeight": 1000, "duration": {"quantity": 4, "unit": "hour"}},

      {"rolloutWeight": 5000, "duration": {"quantity": 4, "unit": "hour"}},

      {"rolloutWeight": 10000, "duration": {"quantity": 4, "unit": "hour"}}

    ]

  }

}

Guarded Rollout

{

  "guardedRolloutConfig": {

    "randomizationUnit": "user",

    "stages": [

      {"rolloutWeight": 1000, "monitoringWindowMilliseconds": 17280000},

      {"rolloutWeight": 5000, "monitoringWindowMilliseconds": 17280000}

    ],

    "metrics": [{

      "metricKey": "error-rate",

      "onRegression": {"rollback": true},

      "regressionThreshold": 0.01

    }]

  }

}

Common Patterns

Model Routing by Attribute

# Route based on selectedModel context attribute

targeting.add_rule(

    config_key="model-selector",

    environment="production",

    clauses=[{

        "contextKind": "user",

        "attribute": "selectedModel",

        "op": "contains",

        "values": ["sonnet"],

        "negate": False

    }],

    variation=0,  # Sonnet variation index

    description="Route sonnet requests"

)

Tier-Based Variation

targeting.add_rule(

    config_key="chat-assistant",

    environment="production",

    clauses=[{

        "contextKind": "user",

        "attribute": "tier",

        "op": "in",

        "values": ["premium", "enterprise"],

        "negate": False

    }],

    variation=0  # Premium model variation

)

Segment Targeting

targeting.target_segment(

    config_key="chat-assistant",

    environment="production",

    segment_keys=["beta-testers"],

    variation=1  # Experimental variation

)

Error Handling

Status

Cause

Solution

400

Invalid semantic patch

Check instruction format, ops must be lowercase

403

Insufficient permissions

Check API token

404

Config not found

Verify projectKey and configKey

422

Invalid variation

Use index (0, 1, 2...) or UUID from targeting response

Next Steps

After configuring targeting:

  • Provide config URL:
https://app.launchdarkly.com/projects/{projectKey}/ai-configs/{configKey}
  • Monitor performance with aiconfig-ai-metrics
  • Attach judges with aiconfig-online-evals
  • Set up guarded rollouts for automatic regression detection

Related Skills

  • aiconfig-create - Create AI Configs with variations
  • aiconfig-variations - Manage variations
  • aiconfig-online-evals - Attach judges
  • aiconfig-segments - Create segments for targeting

References

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card