Fix NCalc case sensitivity and add formula validation
- Normalize IF/Abs/Pow/etc. to lowercase before evaluation so AI-generated or manually typed uppercase function names no longer cause "Function not found" errors - Add NormalizeAndValidate() which normalizes then does a parse-only check on save — invalid formulas are rejected with a clear error before storing - Update AI system prompt to list all functions in lowercase and explicitly call out case-sensitivity; add if() to the supported function list - Add collapsible NCalc quick-reference panel in the formula editor showing all operators, functions (lowercase), built-in variables, and an example Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using NCalc2;
|
||||
using Anthropic.SDK;
|
||||
using Anthropic.SDK.Messaging;
|
||||
@@ -24,8 +25,22 @@ public class CustomFormulaAiService : ICustomFormulaAiService
|
||||
|
||||
private const string SystemPrompt = @"You are an expert pricing formula engineer for a powder coating business.
|
||||
Your job is to generate NCalc expressions that calculate either a fixed price or a surface area
|
||||
from user-supplied field values. NCalc supports standard math operators (+, -, *, /, %, Pow()),
|
||||
comparison operators, and the Abs(), Round(), Max(), Min() built-in functions.
|
||||
from user-supplied field values.
|
||||
|
||||
CRITICAL: NCalc function names are CASE-SENSITIVE and must be ALL LOWERCASE.
|
||||
Supported built-in functions (always write these exactly as shown):
|
||||
if(condition, trueValue, falseValue) — conditional expression
|
||||
abs(x) — absolute value
|
||||
round(x, digits) — round to N decimal places
|
||||
max(a, b) — larger of two values
|
||||
min(a, b) — smaller of two values
|
||||
pow(base, exponent) — exponentiation
|
||||
sqrt(x) — square root
|
||||
Standard operators: + - * / %
|
||||
Comparison operators: < > <= >= == !=
|
||||
Boolean operators: && || !
|
||||
|
||||
Do NOT use: IF, Abs, Round, Max, Min, Pow, Sqrt (uppercase versions) — NCalc will reject them.
|
||||
|
||||
The user will describe a custom fabricated item (e.g., 'Roof curb', 'Electrical enclosure',
|
||||
'Tubular frame') and you must produce a pricing formula template.
|
||||
@@ -172,6 +187,35 @@ Rules:
|
||||
}
|
||||
}
|
||||
|
||||
// Lowercase NCalc built-in names before evaluation so that user-typed or AI-generated
|
||||
// uppercase variants (IF, Abs, POW, etc.) don't produce "Function not found" errors.
|
||||
private static readonly Regex _ncalcFuncRegex = new(
|
||||
@"\b(if|abs|round|max|min|pow|sqrt|ceiling|floor|truncate|sign|log|exp)\b(?=\s*\()",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static string NormalizeFormula(string formula) =>
|
||||
_ncalcFuncRegex.Replace(formula, m => m.Value.ToLowerInvariant());
|
||||
|
||||
/// <inheritdoc />
|
||||
public (string NormalizedFormula, string? Error) NormalizeAndValidate(string formula)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(formula))
|
||||
return (formula, "Formula cannot be empty.");
|
||||
|
||||
var normalized = NormalizeFormula(formula);
|
||||
try
|
||||
{
|
||||
var expr = new Expression(normalized);
|
||||
if (expr.HasErrors())
|
||||
return (formula, expr.Error);
|
||||
return (normalized, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (formula, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EvaluateFormulaResponse EvaluateFormula(EvaluateFormulaRequest request)
|
||||
{
|
||||
@@ -183,7 +227,7 @@ Rules:
|
||||
var variables = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
|
||||
request.VariablesJson ?? "{}") ?? new();
|
||||
|
||||
var expr = new Expression(request.Formula);
|
||||
var expr = new Expression(NormalizeFormula(request.Formula));
|
||||
foreach (var kv in variables)
|
||||
{
|
||||
expr.Parameters[kv.Key] = kv.Value.ValueKind == JsonValueKind.Number
|
||||
|
||||
Reference in New Issue
Block a user