Messy input

Messy input takes a custom method for every format.

Emails, free-text forms and partner payloads carry the same order, and each one usually gets its own parsing method, with its own regular expressions and edge cases. With IntentSharp one call asks the model to read each of them into your own C# type, and checks each answer against the type and your rules before your code sees it.

What it costs today

An order arrives as an email, as the text of a web form, and as a partner's JSON with its own field names and its own way of writing a date. Each format gets a method: string splitting for the form, regular expressions for the email, a mapping class for the JSON, and a growing list of special cases for "forty" instead of 40 or a date written month first.

Every new sender or phrasing is another case to find, usually after a request has failed or, worse, been read wrongly and passed on with a default value nobody noticed.

How IntentSharp handles it

Four inputs go through the same call: an email, a form's free text, a partner's JSON payload and an email that gives no quantity. The first three come back as the same OrderRequest. Its Quantity is an int?, so the fourth answer can leave it null instead of inventing a number, and your rule refuses the null on both attempts: the fourth input comes back as a failed result, not as an order with a made-up quantity.

Program.cs
var email = "Hi, this is Dana from Northwind. We'd like 40 units of SKU A-1192 delivered by 2026-11-15.";var formText = "Name: Dana (Northwind)\nItem: A-1192 belts\nHow many: forty\nNeeded by: 15 November 2026";var partnerJson = JsonNode.Parse("""{"buyer": {"name": "Dana", "company": "Northwind"}, "lines": [{"part": "a1192", "qty": "40"}], "due": "11/15/2026"}""");var vagueEmail = "Can you send some A-1192 belts by mid November? We will confirm how many next week."; // One call and one result type for every kind of input, with your// own rule on the result. OrderRequest.Quantity is an int?, so an// answer can leave it null when the input gives none.var options = new AIRequestOptions().Require<OrderRequest>(    o => o.Quantity is > 0, "Quantity must be a positive number.");var orders = new List<AIResult<OrderRequest>>();object?[] inputs = [email, formText, partnerJson, vagueEmail];foreach (var input in inputs)    orders.Add(await input.AIAsync<OrderRequest>(        client, "Extract the purchase request.", options));
Output: four inputs, one type
email: Success: true, after 1 attempt  OrderRequest { Contact = Dana (Northwind), Sku = A-1192, Quantity = 40, DeliverBy = 2026-11-15 }form text: Success: true, after 1 attempt  OrderRequest { Contact = Dana (Northwind), Sku = A-1192, Quantity = 40, DeliverBy = 2026-11-15 }partner JSON: Success: true, after 1 attempt  OrderRequest { Contact = Dana (Northwind), Sku = A-1192, Quantity = 40, DeliverBy = 2026-11-15 }email without a quantity: Success: false, after 2 attempts  [Validator] attempt 1: Quantity must be a positive number.  [Validator] attempt 2: Quantity must be a positive number.Model calls: 5

OrderRequest is Contact, Sku, Quantity as an int?, and DeliverBy. Compiled and run with every build of this site; the model's answers are scripted, and a pass means the answer met the checks, not that it is right.

What stays in your control

Your type says what an order is, and your rules say what a good one looks like.

  • Your type sets the shape. A required member missing from the answer is reported, not filled with a default. A null passes only where your type allows one, and enums are read by name.
  • Your rules set the limits. Ranges, lengths and patterns are not part of a type: Require<T> and Validate<T> run in your process on every answer, and a failed rule goes back to the model with the finding.
  • What a failure looks like. Success is false, Value is the type's default and Issues lists each finding with its attempt, as the fourth input shows. Read Success before Value.
  • Members the input may not give. The model must fill every member, so give one the input may leave out a nullable type, as Quantity is here, or DateOnly? for a date, or the model invents it. An invented value passes any rule it happens to meet.
  • What is sent. The input goes in full, never cut, to the provider you choose, and input over MaxInputChars (200,000 characters by default) is refused before any model call. Text only: no images or files.
  • A pass is not a proof. A pass means the answer met your type and your rules, not that it is right: a quantity or a date read wrongly still passes when it meets them.

Read your next input into your own type.

dotnet add package IntentSharp --prerelease