LLM Integration#
discopt includes optional LLM-powered features for model explanation, natural language formulation, infeasibility diagnosis, conversational model building, and more. These features use litellm as a universal adapter supporting 100+ LLM providers.
Important
LLM features are purely advisory — they never affect solver correctness. The solver runs identically with or without LLM features enabled. All formulations generated by LLMs pass through model.validate() before use.
Installation#
pip install discopt[llm]
This installs litellm as an optional dependency. All LLM features degrade gracefully when litellm is not installed.
Configuration#
API Key Setup#
Set the API key for your chosen provider via environment variables:
Provider |
Environment Variable |
Model String Example |
|---|---|---|
Anthropic |
|
|
OpenAI |
|
|
|
|
|
Ollama (local) |
(none needed) |
|
AWS Bedrock |
|
|
Azure OpenAI |
|
|
Model Selection#
Models are resolved in this priority order:
Explicit
model=parameter on the function callDISCOPT_LLM_MODELenvironment variableDefault:
anthropic/claude-sonnet-4-20250514
# Set your preferred model globally
export DISCOPT_LLM_MODEL="ollama/llama3"
export ANTHROPIC_API_KEY="sk-ant-..."
Features Overview#
Result Explanation (explain())#
Get a rich, context-aware explanation of any solve result:
result = model.solve()
print(result.explain(llm=True))
The explanation is status-specific:
Optimal: which variables are active, which constraints are binding, solution interpretation
Infeasible: which constraints likely conflict, suggested relaxations
Iteration limit: convergence analysis, parameter tuning suggestions
Time/node limit: B&B progress assessment, relaxation quality analysis
Natural Language Formulation (from_description())#
Create models from natural language descriptions using structured tool calling (not code generation):
import discopt.modeling as dm
model = dm.from_description(
"Minimize total shipping cost from 3 warehouses to 5 customers. "
"Each warehouse has limited supply and each customer has a demand "
"that must be met.",
data={
"supply": [100, 150, 200],
"demand": [80, 60, 70, 40, 50],
"costs": np.random.rand(3, 5) * 100,
},
)
LLM-Enhanced Solving (llm=True)#
Pass llm=True to solve() for pre-solve analysis and automatic result explanation:
result = model.solve(llm=True)
# Pre-solve warnings are logged (advisory only, never blocks solving)
# Result explanation is automatically generated
print(result.explain())
Conversational Model Building (discopt.chat())#
Build models interactively through a conversation:
import discopt
session = discopt.chat()
session.send("I have a factory with 3 machines that can produce 2 products")
session.send("Machine capacities are [100, 150, 200] hours per week")
session.send("Product profits are [50, 80] per unit")
session.send("Solve it")
session.close()
Infeasibility Diagnosis#
When a model is infeasible, get actionable diagnosis:
from discopt.llm.diagnosis import diagnose_infeasibility
result = model.solve()
if result.status == "infeasible":
print(diagnose_infeasibility(model, result))
Solver Strategy Advisor#
Get solver parameter recommendations based on model structure:
from discopt.llm.advisor import suggest_solver_params
params = suggest_solver_params(model)
print(params["reasoning"])
result = model.solve(**{k: v for k, v in params.items() if k != "reasoning"})
Auto-Reformulation Analysis#
Identify reformulation opportunities:
from discopt.llm.reformulation import analyze_reformulations
suggestions = analyze_reformulations(model, llm=True)
for s in suggestions:
print(f"[{s.category}] {s.description}")
print(f" Impact: {s.impact}")
Claude Code Skill Files#
The following Claude Code slash commands are available for discopt users:
Command |
Description |
|---|---|
|
Natural language to discopt model |
|
Solve result diagnostics |
|
Model improvement suggestions |
|
Generate mathematical documentation |
|
Cross-solver translation (Pyomo, GAMS, AMPL, JuMP) |
|
Analyze benchmark JSON results |
Safety Guarantees#
LLM outputs never affect solver math — commentary is decorative; formulations go through
validate()+solve()Structured output over free-form code —
from_description()uses tool calling mapped to Model methods, not arbitrary code generationGraceful degradation — if LLM is unavailable/slow/broken, the solver runs identically without LLM features
Timeouts — 5s for explanation, 30s for formulation. LLM calls never block the B&B loop
No secrets in prompts — model structure sent to LLM is the same info available via
model.summary()