Data mapping

Every supplier adds another mapping to write and maintain.

Each supplier or integration names the same fields its own way, so each one gets its own mapping class, and each mapping breaks when the supplier renames a column. With IntentSharp one call asks the model to read each supplier's payload into your own model, and checks each answer against your type and your validation rules.

What it costs today

Supplier A sends item_no and a price as text, supplier B sends SKU and a cost in cents, supplier C sends one line of values separated by semicolons. Each needs a mapping: field names, unit conversions, lookups and the tests around them, written once per supplier and fixed every time a supplier renames a field or changes a unit.

The mappings multiply with every integration, and what each source means ends up spread across classes nobody wants to touch.

How IntentSharp handles it

Three suppliers send the same drive belt in three shapes. One AIConvertAsync<CatalogItem> call per payload returns your own CatalogItem, checked against four rules in code. Supplier B's first answer keeps its lower-case SKU, which the SKU rule refuses, and the second attempt passes.

Program.cs
// The same product from three suppliers, each in its own shape.(string Supplier, object Payload)[] feeds =[    ("Supplier A", JsonNode.Parse("""{"item_no": "A-1192", "desc": "Drive belt, 40 mm", "price": "31.00", "ccy": "EUR", "pack": "box of 10"}""")!),    ("Supplier B", JsonNode.Parse("""{"SKU": "a1192", "Description": "DRIVE BELT 40MM", "UnitCost": {"amount": 3100, "unit": "cents"}, "Currency": "eur", "Qty": 10}""")!),    ("Supplier C", "A-1192;Drive belt 40 mm;31,00;EUR;10 per pack"),]; // Your one internal model's rules, in code, checked on every// answer before your code sees it.var rules = new AIRequestOptions()    .Require<CatalogItem>(i => Regex.IsMatch(i.Sku, @"^[A-Z]-\d{4}$"),        "Sku must look like A-1192.")    .Require<CatalogItem>(i => i.UnitPrice > 0,        "UnitPrice must be greater than zero.")    .Require<CatalogItem>(i => i.Currency is "EUR" or "GBP" or "USD",        "Currency must be EUR, GBP or USD.")    .Require<CatalogItem>(i => i.PackSize >= 1,        "PackSize must be at least 1."); // One conversion for every supplier's shape, and no mapping class// per supplier.var items = new List<AIResult<CatalogItem>>();foreach (var (_, payload) in feeds)    items.Add(await payload.AIConvertAsync<CatalogItem>(client, rules));
Output: three suppliers, one model
Supplier A:  Success: true, after 1 attempt  Value: CatalogItem { Sku = A-1192, Name = Drive belt, 40 mm, UnitPrice = 31.00, Currency = EUR, PackSize = 10 }Supplier B:  [Validator] attempt 1: Sku must look like A-1192.  Success: true, after 2 attempts  Value: CatalogItem { Sku = A-1192, Name = Drive belt, 40 mm, UnitPrice = 31.00, Currency = EUR, PackSize = 10 }Supplier C:  Success: true, after 1 attempt  Value: CatalogItem { Sku = A-1192, Name = Drive belt, 40 mm, UnitPrice = 31.00, Currency = EUR, PackSize = 10 }Model calls: 4, one per payload and one for the retry

Each payload is converted on its own call: the model reads every payload, and the output counts the calls. Asking the model once per shape and then mapping in your process, with no model call, is on the roadmap as Next; it is not in 1.0.0-rc.3. 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 model and its rules are code you own. Only the reading of each supplier's shape moves to the call.

  • Your model and its rules. CatalogItem is your own record, and the Require<T> rules run in your process on every answer. An answer that breaks one goes back to the model with the finding, within the attempts you allow.
  • A model call per payload. Each conversion is a model call, plus one per retry, and the limits on attempts, model calls and time apply to each call. For a repeated identical request, the request cache (WithCache) answers with no call for the answer, still checked against the current rules.
  • What a failure looks like. Success is false and Issues lists each finding, with its attempt. Value is then the type's default, so read Success first.
  • What the type does not say. Your type sets the shape: required members, nulls and enum names. Ranges, patterns and allowed values belong in your rules, as the SKU and currency rules here.
  • What is sent. Each payload goes to the provider you choose, with the findings on a retry, so keep secrets and personal data out of rule messages. Nothing goes to C2Consultants.
  • A pass is not a proof. A pass means the answer met your type and your rules, not that it is right. Supplier B sends its cost in cents: a price read as 3100 in place of 31.00 passes these four rules. Add a range rule, or a Validate<T> check against your own price list.

Convert one supplier's feed into your own model.

dotnet add package IntentSharp --prerelease