Skip to content

[FSSDK-11587] Implement CMAB config #439

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
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5187d1d
add cmab config
Mat001 Jun 20, 2025
c256379
remove enabled config, remove 3 ENV vars for cmab, only use a single one
Mat001 Jun 20, 2025
f3430cf
cleanup readme
Mat001 Jun 20, 2025
67382b0
add cmab logic to agent
Mat001 Jun 25, 2025
f20c537
add cmab config
Mat001 Jun 20, 2025
04815d9
remove enabled config, remove 3 ENV vars for cmab, only use a single one
Mat001 Jun 20, 2025
a892d8a
cleanup readme
Mat001 Jun 20, 2025
f3f334d
add cmab logic to agent
Mat001 Jun 25, 2025
f760d41
Merge branch 'master' into mpirnovar-cmab-config-fssdk-11587
Mat001 Jul 17, 2025
7adae03
Use go-sdk branch mpirnovar-cmab-gosdk-agent-fssdk-11589 for CMAB sup…
Mat001 Jul 25, 2025
b0d5626
removed comment
Mat001 Jul 25, 2025
789914e
fix formatting
Mat001 Jul 25, 2025
fbea582
surface cmabUUID in Decide API response
Mat001 Jul 25, 2025
f2be578
Add support for returning the cmabUUID field in Decide API responses …
Mat001 Jul 26, 2025
95249f0
remove duplicate CmabUUID
Mat001 Jul 26, 2025
70f98d8
Add configurable CMAB prediction endpoint for FSC testing
Mat001 Jul 26, 2025
9988eee
Force GitHub refresh
Mat001 Jul 27, 2025
79b5da0
add prediction endpoint handling to main.go
Mat001 Jul 27, 2025
7151c5b
Update agent to use CMAB configuration approach
Mat001 Jul 28, 2025
bd5d113
fix tests
Mat001 Jul 28, 2025
7f34019
Force GitHub refresh
Mat001 Jul 28, 2025
f508b73
configure ENV var OPTIMIZELY_CMAB_PREDICTIONENDPOINT to allow fsc tes…
Mat001 Jul 29, 2025
a6db9b5
remove %s, already in the endpoint in fsc
Mat001 Jul 29, 2025
9cc1e9d
Add client reset functionality for FSC CMAB test isolation
Mat001 Jul 29, 2025
ec06047
Trigger PR check
Mat001 Jul 30, 2025
8a3e27b
fix formatting issues
Mat001 Jul 30, 2025
7efc05e
Refactored CMAB configuration from unstructured map[string]interface{…
Mat001 Jul 31, 2025
725834d
restore retry config
Mat001 Aug 11, 2025
8e212ab
fix Prisma crypto dependency alert
Mat001 Aug 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 5 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,23 +145,16 @@ Below is a comprehensive list of available configuration properties.
| webhook.projects.<_projectId_>.secret | N/A | Webhook secret used to validate webhook requests originating from the respective projectId |
| webhook.projects.<_projectId_>.skipSignatureCheck | N/A | Boolean to indicate whether the signature should be validated. TODO remove in favor of empty secret. |

### CMAB Configuration Examples
### CMAB Configuration Example

**Complete CMAB Configuration (OPTIMIZELY_CMAB)**:
```json
{
"enabled": true,
"predictionEndpoint": "https://custom-endpoint.com",
"predictionEndpoint": "https://prediction.cmab.optimizely.com/predict/%s",
"requestTimeout": "5s",
"cache": {
"type": "redis",
"type": "memory",
"size": 2000,
"ttl": "45m",
"redis": {
"host": "redis:6379",
"password": "",
"database": 0
}
"ttl": "45m"
},
"retryConfig": {
"maxRetries": 3,
Expand All @@ -170,28 +163,7 @@ Below is a comprehensive list of available configuration properties.
"backoffMultiplier": 2.0
}
}

CMAB Cache Configuration (OPTIMIZELY_CMAB_CACHE):

{
"type": "redis",
"size": 2000,
"ttl": "45m",
"redis": {
"host": "redis:6379",
"password": "",
"database": 0
}
}

CMAB Retry Configuration (OPTIMIZELY_CMAB_RETRYCONFIG):

{
"maxRetries": 3,
"initialBackoff": "100ms",
"maxBackoff": "10s",
"backoffMultiplier": 2.0
}
```

More information about configuring Agent can be found in the [Advanced Configuration Notes](https://docs.developers.optimizely.com/experimentation/v4.0.0-full-stack/docs/advanced-configuration).

Expand Down
3 changes: 0 additions & 3 deletions cmd/optimizely/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,6 @@ func loadConfig(v *viper.Viper) *config.AgentConfig {
// Handle CMAB configuration using the same approach as UserProfileService
// Check for complete CMAB configuration first
if cmab := v.GetStringMap("cmab"); len(cmab) > 0 {
if enabled, ok := cmab["enabled"].(bool); ok {
conf.CMAB.Enabled = enabled
}
if endpoint, ok := cmab["predictionEndpoint"].(string); ok {
conf.CMAB.PredictionEndpoint = endpoint
}
Expand Down
4 changes: 0 additions & 4 deletions cmd/optimizely/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,12 @@ func assertWebhook(t *testing.T, actual config.WebhookConfig) {

func assertCMAB(t *testing.T, cmab config.CMABConfig) {
fmt.Println("In assertCMAB, received CMAB config:")
fmt.Printf(" Enabled: %v\n", cmab.Enabled)
fmt.Printf(" PredictionEndpoint: %s\n", cmab.PredictionEndpoint)
fmt.Printf(" RequestTimeout: %v\n", cmab.RequestTimeout)
fmt.Printf(" Cache: %#v\n", cmab.Cache)
fmt.Printf(" RetryConfig: %#v\n", cmab.RetryConfig)

// Base assertions
assert.True(t, cmab.Enabled)
assert.Equal(t, "https://custom-cmab-endpoint.example.com", cmab.PredictionEndpoint)
assert.Equal(t, 15*time.Second, cmab.RequestTimeout)

Expand Down Expand Up @@ -299,7 +297,6 @@ func TestCMABEnvDebug(t *testing.T) {

// Debug: Print the parsed config
fmt.Println("Parsed CMAB config from JSON env var:")
fmt.Printf(" Enabled: %v\n", conf.CMAB.Enabled)
fmt.Printf(" PredictionEndpoint: %s\n", conf.CMAB.PredictionEndpoint)
fmt.Printf(" RequestTimeout: %v\n", conf.CMAB.RequestTimeout)
fmt.Printf(" Cache: %+v\n", conf.CMAB.Cache)
Expand All @@ -326,7 +323,6 @@ func TestCMABPartialConfig(t *testing.T) {
conf := loadConfig(v)

// Base assertions
assert.True(t, conf.CMAB.Enabled)
assert.Equal(t, "https://base-endpoint.example.com", conf.CMAB.PredictionEndpoint)

// Cache assertions
Expand Down
9 changes: 1 addition & 8 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -267,10 +267,8 @@ synchronization:
## cmab: Contextual Multi-Armed Bandit configuration
##
cmab:
## enable or disable CMAB functionality
enabled: false
## URL for CMAB prediction service
predictionEndpoint: "https://prediction.cmab.optimizely.com"
predictionEndpoint: "https://prediction.cmab.optimizely.com/predict/%s"
## timeout for CMAB API requests
requestTimeout: 10s
## CMAB cache configuration
Expand All @@ -281,11 +279,6 @@ cmab:
size: 1000
## time-to-live for cached decisions
ttl: 30m
## Redis configuration (if using redis cache)
redis:
host: "localhost:6379"
password: ""
database: 0
## retry configuration for CMAB API requests
retryConfig:
## maximum number of retry attempts
Expand Down
11 changes: 1 addition & 10 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,18 +141,12 @@ func NewDefaultConfig() *AgentConfig {
},
},
CMAB: CMABConfig{
Enabled: false,
PredictionEndpoint: "https://prediction.cmab.optimizely.com",
PredictionEndpoint: "https://prediction.cmab.optimizely.com/predict/%s",
RequestTimeout: 10 * time.Second,
Cache: map[string]interface{}{
"type": "memory",
"size": 1000,
"ttl": "30m",
"redis": map[string]interface{}{
"host": "localhost:6379",
"password": "",
"database": 0,
},
},
RetryConfig: map[string]interface{}{
"maxRetries": 3,
Expand Down Expand Up @@ -411,9 +405,6 @@ type RuntimeConfig struct {

// CMABConfig holds configuration for the Contextual Multi-Armed Bandit functionality
type CMABConfig struct {
// Enabled indicates whether the CMAB functionality is enabled
Enabled bool `json:"enabled"`

// PredictionEndpoint is the URL for CMAB predictions
PredictionEndpoint string `json:"predictionEndpoint"`

Expand Down
16 changes: 2 additions & 14 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,7 @@ func TestDefaultConfig(t *testing.T) {
assert.Equal(t, 0, conf.Runtime.MutexProfileFraction)

// CMAB configuration
assert.False(t, conf.CMAB.Enabled)
assert.Equal(t, "https://prediction.cmab.optimizely.com", conf.CMAB.PredictionEndpoint)
assert.Equal(t, "https://prediction.cmab.optimizely.com/predict/%s", conf.CMAB.PredictionEndpoint)
assert.Equal(t, 10*time.Second, conf.CMAB.RequestTimeout)

// Test cache settings as maps
Expand All @@ -111,11 +110,6 @@ func TestDefaultConfig(t *testing.T) {
assert.Equal(t, 1000, cacheMap["size"])
assert.Equal(t, "30m", cacheMap["ttl"])

redisMap := cacheMap["redis"].(map[string]interface{})
assert.Equal(t, "localhost:6379", redisMap["host"])
assert.Equal(t, "", redisMap["password"])
assert.Equal(t, 0, redisMap["database"])

// Test retry settings as maps
retryMap := conf.CMAB.RetryConfig
assert.Equal(t, 3, retryMap["maxRetries"])
Expand Down Expand Up @@ -261,8 +255,7 @@ func TestDefaultCMABConfig(t *testing.T) {
conf := NewDefaultConfig()

// Test default values
assert.False(t, conf.CMAB.Enabled)
assert.Equal(t, "https://prediction.cmab.optimizely.com", conf.CMAB.PredictionEndpoint)
assert.Equal(t, "https://prediction.cmab.optimizely.com/predict/%s", conf.CMAB.PredictionEndpoint)
assert.Equal(t, 10*time.Second, conf.CMAB.RequestTimeout)

// Test default cache settings as maps
Expand All @@ -271,11 +264,6 @@ func TestDefaultCMABConfig(t *testing.T) {
assert.Equal(t, 1000, cacheMap["size"])
assert.Equal(t, "30m", cacheMap["ttl"])

redisMap := cacheMap["redis"].(map[string]interface{})
assert.Equal(t, "localhost:6379", redisMap["host"])
assert.Equal(t, "", redisMap["password"])
assert.Equal(t, 0, redisMap["database"])

// Test default retry settings as maps
retryMap := conf.CMAB.RetryConfig
assert.Equal(t, 3, retryMap["maxRetries"])
Expand Down