Skip to main content

Overview

Constraint Satisfaction Problem (CSP) solvers form the mathematical foundation of Pricing Intelligence. By modeling pricing configurations as constraint problems, the system can:
  • Validate pricing models for logical consistency
  • Enumerate all valid subscription configurations
  • Optimize plan selection based on objectives (minimize cost, maximize features)
  • Filter configurations by user requirements
This approach provides mathematical rigor that pure LLM reasoning cannot achieve.

What is a CSP?

A Constraint Satisfaction Problem consists of:

Variables

Decision points (e.g., “Which plan?”, “Which add-ons?”)

Domains

Possible values for each variable (e.g., plans = )

Constraints

Rules that valid solutions must satisfy
A solution to a CSP is an assignment of values to variables that satisfies all constraints.

Pricing as a CSP

In Pricing Intelligence, a subscription configuration is modeled as:
Variables:
- plan ∈ {FREE, ESSENTIALS, TEAM, AGENCY}
- addOn_essentialsExtraChannels ∈ {0, 1, 2, 3, ...}  (quantity)
- addOn_teamExtraChannels ∈ {0, 1, 2, 3, ...}

Constraints:
- Exactly one plan must be selected
- addOn_essentialsExtraChannels > 0 → plan = ESSENTIALS
- addOn_teamExtraChannels > 0 → plan = TEAM
- If feature hashtagManager required → plan ∈ {ESSENTIALS, TEAM, AGENCY}
- If socialChannelsLimit >= 10 → ...
- cost = plan.price + Σ(addOn.price * addOn.quantity)
The CSP solver finds all assignments that satisfy these constraints.

Solver Architecture

Pricing Intelligence uses a two-tier solver architecture:
1

Analysis API

Node.js/TypeScript service that translates Pricing2Yaml to solver formats
2

Solver Backends

  • MiniZinc: General-purpose CSP modeling language (default)
  • Choco: Java-based constraint solver library
3

Post-Processing

Analysis API converts solver outputs back to JSON responses

Analysis Operations

The Analysis API exposes four CSP-based operations:

1. Validation

Checks if a pricing model is mathematically consistent. What is validated:

Plan Coherence

Features and limits don’t contradict between plans

Add-On Compatibility

Add-ons reference valid plans and features

Feature Dependencies

Linked features and limits are consistent

Price Consistency

All prices are non-negative and well-defined
curl -X POST "http://localhost:8002/api/v1/pricing/analysis" \
  -F "[email protected]" \
  -F "operation=validate" \
  -F "solver=minizinc"
Configuration Space Size: The number of valid subscription combinations. A pricing model with 4 plans and 3 add-ons might have hundreds or thousands of valid configurations depending on constraints.

2. Subscriptions Enumeration

Enumerates all valid subscription configurations, optionally filtered.
curl -X POST "http://localhost:8002/api/v1/pricing/analysis" \
  -F "[email protected]" \
  -F "operation=subscriptions" \
  -F "solver=minizinc" \
  -F 'filters={"maxPrice": 50, "features": ["hashtagManager"]}'
Use Cases:
  • “Show me all Buffer plans under $50 with hashtag management”
  • “How many configurations include SSO?”
  • “What are my options for 10-50 users?”
Performance Note: Enumerating subscriptions can be computationally expensive for complex pricing models. The solver may take several seconds for models with large configuration spaces. Consider using maxSubscriptionSize filter to reduce complexity.

3. Optimal Configuration

Finds the best configuration according to an objective function. Objectives:
  • minimize: Find the cheapest configuration satisfying filters
  • maximize: Find the most expensive configuration (or maximum revenue potential)
curl -X POST "http://localhost:8002/api/v1/pricing/analysis" \
  -F "[email protected]" \
  -F "operation=optimal" \
  -F "objective=minimize" \
  -F "solver=minizinc" \
  -F 'filters={"usageLimits": {"socialChannelsLimit": 10}}'
Use Cases:
  • “What’s the cheapest way to get 50 users and SSO?”
  • “Find the most expensive GitHub plan” (useful for revenue analysis)
  • “Best value for my requirements”

4. Summary Statistics

Provides high-level structural insights about a pricing model.
curl -X POST "http://localhost:8002/api/v1/pricing/summary" \
  -F "[email protected]"
Use Cases:
  • “How many features does Buffer offer?”
  • “What’s the price range for Salesforce plans?”
  • “Does this pricing have annual payment options?”
The summary operation does NOT use CSP solvers — it’s a simple structural analysis of the YAML. This makes it very fast but limited to catalog-level insights.

Filter Semantics

Filters define constraints that solutions must satisfy:
interface FilterCriteria {
  minPrice?: number;               // Lower bound on total cost
  maxPrice?: number;               // Upper bound on total cost
  maxSubscriptionSize?: number;    // Max items (1 plan + N add-ons)
  features?: string[];             // Required features (must be true)
  usageLimits?: Record<string, number>;  // Min threshold values
}

How Filters Become Constraints

1

Price Constraints

minPrice and maxPrice are translated to:
cost >= minPrice ∧ cost <= maxPrice
2

Feature Constraints

Each feature in features array becomes:
effective_features[featureName] = true
Where effective_features is computed from plan defaults + plan overrides + add-on additions.
3

Usage Limit Constraints

Each entry in usageLimits becomes:
effective_limits[limitName] >= thresholdValue
Where effective_limits = plan limits + add-on extensions.
4

Size Constraint

maxSubscriptionSize becomes:
1 + Σ(addOn.quantity > 0) <= maxSubscriptionSize

Example: Filter Translation

{
  "maxPrice": 50,
  "features": ["hashtagManager", "videoScheduling"],
  "usageLimits": {
    "socialChannelsLimit": 5
  },
  "maxSubscriptionSize": 3
}

Solver Comparison

Pricing Intelligence supports two solver backends:

MiniZinc (Default)

MiniZinc

Language: High-level constraint modelingPros:
  • Expressive modeling language
  • Multiple backend solvers (Gecode, Chuffed, etc.)
  • Widely used in research and industry
  • Better optimization performance
Cons:
  • Requires external binary installation
  • Slightly slower for simple enumeration
Best For: Optimization problems, complex constraints

Choco

Choco

Language: Java libraryPros:
  • Native Java integration
  • Fast for enumeration
  • No external dependencies
  • Good for large configuration spaces
Cons:
  • Less expressive than MiniZinc
  • Requires Java runtime
  • Optimization can be slower
Best For: Subscription enumeration, validation
Recommendation: Use MiniZinc (default) for most operations. Consider Choco only for enumeration-heavy workloads or Java-based deployments.

Implementation Details

YAML to Solver Translation

The Analysis API converts Pricing2Yaml to solver-specific formats:
1

Parse YAML

Load and validate Pricing2Yaml structure
2

Extract Schema

Identify all features, limits, plans, and add-ons
3

Generate Solver Model

For MiniZinc: Generate .mzn model file and .dzn data fileFor Choco: Build Java constraint model programmatically
4

Add User Constraints

Append filter constraints to the model
5

Invoke Solver

Execute solver with timeout limits
6

Parse Results

Convert solver output back to JSON

Example: MiniZinc Model Excerpt

% Decision variables
var PlanType: selectedPlan;
array[AddOnType] of var int: addOnQuantities;

% Cost calculation
var float: totalCost = planPrice[selectedPlan] + 
  sum(a in AddOnType)(addOnPrice[a] * addOnQuantities[a]);

% Feature computation (from plan and add-ons)
array[FeatureType] of var bool: effectiveFeatures;
constraint forall(f in FeatureType)(
  effectiveFeatures[f] = (
    planFeatures[selectedPlan, f] \/ 
    exists(a in AddOnType where addOnQuantities[a] > 0)(
      addOnFeatures[a, f]
    )
  )
);

% User filter: maxPrice constraint
constraint totalCost <= 50.0;

% User filter: required features
constraint effectiveFeatures[HashtagManager] = true;

% Optimization objective
solve minimize totalCost;

Asynchronous Job Processing

CSP solving can be time-consuming, so the Analysis API uses background jobs:
1

Submit Job

POST to /api/v1/pricing/analysis returns {"jobId": "...", "status": "PENDING"}
2

Poll Status

GET /api/v1/pricing/analysis/{jobId} returns current status
3

Retrieve Results

When status = "COMPLETED", the response includes full results
type JobStatus = 
  | "PENDING"    // Queued, not started
  | "RUNNING"    // Solver executing
  | "COMPLETED"  // Success, results available
  | "FAILED"     // Error occurred
  | "TIMEOUT";   // Solver exceeded time limit

Performance Considerations

Computational Complexity

CSP solving is NP-complete in the general case. Performance depends on:

Configuration Space

More plans × more add-ons = exponentially more combinations

Constraint Complexity

Interdependent features/limits increase solving time

Filter Selectivity

Tighter constraints = smaller search space = faster solving

Solver Heuristics

MiniZinc and Choco use different search strategies

Optimization Tips

1

Use maxSubscriptionSize

Limit the number of add-ons to reduce the search space:
{"maxSubscriptionSize": 5}  // Max 1 plan + 4 add-ons
2

Prefer optimal over subscriptions

Finding one optimal solution is faster than enumerating all solutions
3

Add price bounds

Even loose bounds help:
{"minPrice": 0, "maxPrice": 1000}
4

Use validation first

If a pricing model is invalid, optimization will fail. Validate first.

Timeout Configuration

The Analysis API enforces timeouts:
// From analysis_api configuration
const SOLVER_TIMEOUT_MS = 120000;  // 2 minutes
const JOB_TIMEOUT_MS = 300000;     // 5 minutes
If a job exceeds these limits, it’s marked as TIMEOUT status.

Real-World Example

Let’s trace how a question becomes a CSP:
1

User Question

“What’s the cheapest way to get 10 channels and video scheduling in Buffer?”
2

Harvey's Plan

{
  "name": "optimal",
  "objective": "minimize",
  "filters": {
    "features": ["videoScheduling"],
    "usageLimits": {"socialChannelsLimit": 10}
  }
}
3

Analysis API Translation

Converts to MiniZinc:
var {FREE, ESSENTIALS, TEAM, AGENCY}: plan;
var 0..20: essentialsExtra;
var 0..20: teamExtra;
var 0..20: agencyExtra;

var float: cost = plan_price[plan] + 
  essentialsExtra * 6 + teamExtra * 12 + agencyExtra * 6;

% videoScheduling requires ESSENTIALS, TEAM, or AGENCY
constraint plan != FREE;

% Compute effective socialChannelsLimit
var int: channels = 
  if plan == FREE then 3
  elsif plan == ESSENTIALS then 1 + essentialsExtra
  elsif plan == TEAM then 1 + teamExtra
  else 10 + agencyExtra endif;

% User requires >= 10 channels
constraint channels >= 10;

solve minimize cost;
4

Solver Execution

MiniZinc finds:
  • AGENCY plan (10 channels built-in) = $120
  • ESSENTIALS + 9 add-ons (1 + 9 = 10 channels) = 6 + 9*6 = $60
Optimal: ESSENTIALS + 9 extra channels at $60/month
5

Result

{
  "optimal": {
    "subscription": {
      "plan": "ESSENTIALS",
      "addOns": [
        "essentialsExtraChannels",
        "essentialsExtraChannels",
        "essentialsExtraChannels",
        "essentialsExtraChannels",
        "essentialsExtraChannels",
        "essentialsExtraChannels",
        "essentialsExtraChannels",
        "essentialsExtraChannels",
        "essentialsExtraChannels"
      ]
    },
    "cost": 60.0
  }
}
6

Harvey's Answer

“The cheapest way to get 10 channels with video scheduling is the ESSENTIALS plan (6/month)plus9additionalchanneladdons(6/month) plus **9 additional channel add-ons** (6 each), for a total of **60/month.WhiletheAGENCYplanincludes10channelsbydefault,itcosts60/month**. While the AGENCY plan includes 10 channels by default, it costs 120/month, making the ESSENTIALS approach more economical.”

Next: MCP Protocol

Learn how Harvey communicates with the MCP server to invoke these CSP operations

Further Reading

Analysis API Reference

Full API documentation

MiniZinc Docs

MiniZinc constraint modeling language

Choco Solver

Choco constraint solver documentation

CSP Theory

Wikipedia: Constraint satisfaction problems

Build docs developers (and LLMs) love