Luma public docs
English

Dil

Tip Sistemi

Bir schema alanının tipi üç işi birden yapar: gateway'e gönderilen JSON Schema'yı üretir, dönen yanıtı yerel olarak doğrular ve --generate-tool-types ile TypeScript declaration'ına çevrilir. Bu sayfa tiplerin tamamını ve her birinin JSON Schema karşılığını listeler.

Temel tipler

TipEş anlamlıKabul edilen değerJSON Schema
string—JS string{ "type": "string" }
intintegerNumber.isInteger geçen değer{ "type": "integer" }
floatnumberSonlu sayı — NaN ve Infinity reddedilir{ "type": "number" }
boolbooleantrue / false{ "type": "boolean" }
null—Yalnızca null{ "type": "null" }
any—Her şey — doğrulama yapılmaz{}
int ile float ayrımı gerçektir

int alanına 3.5 gelirse X.y must be an integer. hatası alırsın; 3.0 ise Number.isInteger açısından geçerlidir. float alanına NaN veya Infinity gelirse X.y must be a number. hatası alırsın — bu, bozuk model çıktısını erken yakalar.

Bileşik tipler

enum

luma
status: enum["pending", "approved", "rejected"]

Yalnızca string değerler alır ve boş olamaz. JSON Schema karşılığı { "type": "string", "enum": [...] }. Listede olmayan bir değer X.y must be one of these values: … hatası verir.

Array

luma
tags: string[]        // unbounded list of strings
items: LineItem[]      // list of nested schemas
grid: int[][]         // nested array

JSON Schema: { "type": "array", "items": { … } }. Her eleman ayrı ayrı doğrulanır ve hata mesajında indeks görünür: Invoice.items[2].amount missing.

Elemana [] ile erişilir — rows[0], rows[i].customer.name — ve aynı biçim bir ='in solunda yazma yapar. Sonun ötesini okumak adlandırılmış hatadır, sessiz bir null değil: xs[5] is out of bounds (length 2). Sonun ötesine yazmak da reddedilir; liste indekslenerek büyümez. Sınır durumlarının tam tablosu dil sayfasındadır.

map<T> / record<T>

luma
metrics: map<float>       // { "score": 0.95, "confidence": 0.8 }
labels: record<string>    // record is an alias for map

Dinamik anahtarlı nesne. JSON Schema: { "type": "object", "additionalProperties": { … } }. Anahtarlar serbesttir, değerlerin tipi doğrulanır.

Değere m["key"] ile erişilir; olmayan bir anahtara yazmak onu oluşturur. Olmayan bir anahtarı okumak ise null döndürmez — eksik anahtar adlandırılmış bir hatadır: m["missing"] is not present. Bu yalnızca runtime hakkında değil, tip hakkında bir ifadedir: map<T> T tutar, T | null değil; yani lookup, programa null'ın girebileceği bir yer değildir. Emin olmadığın bir anahtar okunmadan önce kontrol edilmelidir:

import col from "std/collections"

agent Reader {
  fn read(labels: map<string>, key: string) -> Out {
    if contains(col.keys(labels), key) {
      return { value: labels[key] }
    }
    return { value: "unknown" }
  }
}

İndekslemenin sınır durumlarının tam tablosu — listeler ve map'ler, okuma ve yazma — dil sayfasındadır.

map<T> strict gateway'lerde risklidir

Strict structured-output modu (strict: true) çoğu gateway'de her object'in tüm anahtarlarını properties içinde listelemesini ve additionalProperties: false olmasını bekler. Dinamik anahtarlı bir map<T> bu koşulu sağlayamaz ve provider isteği reddedebilir.

LLM çıktısı olarak dinamik metadata taşıman gerekiyorsa anahtar-değer çifti listesi kullan — bu desen strict modda güvenlidir:

schema MetaEntry {
  key: string
  value: string
}

// inside InvoiceExtract:
meta: MetaEntry[] @desc("Schema alanlarına girmeyen tüm bilgiler")

map<T>'yi deterministik kod içinde (tool çıktısı, return ile üretilen değer) rahatça kullanabilirsin; kısıt yalnızca LLM'e gönderilen schema'lar içindir.

json<T>

luma
import json from "std/json"

schema Envelope {
  raw: string           // JSON text
  data: any              // parsed, unvalidated value
  payload: json<Payload>    // parsed AND validated against Payload
}
json<T> bir string değildir

json<T> tipi, değeri T'ye göre doğrular. JSON metni tutmak istiyorsan tip string olmalıdır. JSON Schema üretiminde json<T> doğrudan T'ye açılır, yani gateway açısından T ile birebir aynıdır.

Tuple

luma
coordinate: [float, float]      // exactly two elements, positional

Sabit uzunluklu, konum-tipli array. Doğrulamada eleman sayısı ve her konumun tipi kontrol edilir. En az bir virgül içermelidir ([float] tuple değildir).

Tuple'a eleman annotation'ı uygulanmaz

Tuple, JSON Schema'da prefixItems olarak yayılır ve ask çıktısı olarak sorunsuz kullanılabilir. Ancak sayısal ve string annotation'lar tuple'ın konumlarına uygulanamaz; parser bunları reddeder:

Error: @range can only be used on numeric fields or numeric arrays.

Konum başına kısıt gerekiyorsa adlandırılmış alanlar hem daha okunaklı hem daha ifade gücü yüksektir:

schema Point {
  lat: float [-90..90]
  lon: float [-180..180]
}

Schema referansı

luma
schema Address {
  city: string
}

schema Customer {
  address: Address      // nested object
  history: Address[]    // list of nested objects
}

JSON Schema'da { "$ref": "#/$defs/Address" } olarak üretilir; tanım $defs altında bir kez yer alır.

$ref alanlarına description yazılmaz

Bir alanın tipi doğrudan bir schema referansıysa, o alandaki @desc JSON Schema'ya aktarılmaz. Bunun sebebi bazı gateway'lerin $ref ile aynı seviyede description gördüğünde isteği reddetmesidir. Açıklamayı hedef schema'nın kendisine (schema Address @desc("…")) veya alanın içindeki alanlara yaz.

Parametrik schema — Name<T>

luma
schema LineItem {
  sku: string
  amount: float
}

schema Page<T> {
  items: T[]
  total: int
}

schema Report {
  page: Page<LineItem>
}

Bir schema tip parametresi alabilir. Gövdenin içinde parametre başka herhangi bir tip adı gibi okunur; her kullanım yeri de somut bir argüman verir: Page<LineItem>, Page<Customer>.

Page<LineItem> dosya parse edilirken açılır. Parser Page'in gövdesini kopyalar, T'nin durduğu yere LineItem'ı koyar ve sonucu Page_LineItem adıyla sıradan bir schema olarak kaydeder. O noktadan sonra hiçbir şey ortada bir parametre olduğunu bilmez — JSON Schema üretimi, doğrulama, $defs, where, when, @desc ve std/schema reflection'ı, elle yazılmış bir schema'da nasıl işliyorsa açılmış schema üzerinde de aynen öyle işler; sağlayıcıya hiçbir genericlik ulaşmaz.

Gördüğün ad, açılmış addır. Hem $defs anahtarı odur, hem schema.name(…) onu döndürür, hem de o schema'ya karşı üretilen doğrulama hatasının öneki odur:

Page_LineItem.items[0].amount missing.

Schema adının kabul edildiği her pozisyon

PozisyonYazılışı
Method dönüş tipifn list() -> Page<LineItem>
Schema alanıpage: Page<LineItem>
Agent method parametresifn take(page: Page<LineItem>) -> Report
pure fn / io fn parametresipure fn count(page: Page<LineItem>) -> int
Tool parametresitool inspect(page: Page<LineItem>) -> Box<int>
Tipli yerel değişkenlocal: Page<LineItem> = page

Asıl önemli olan taşıma durumu: üreten method Page<LineItem> döndürür, tüketen method aynı şeyi parametre olarak alır; böylece sarmalayıcı ikisinin arasındaki sınırda elle açılmak zorunda kalmaz.

Birden fazla parametre, iç içe kullanım, primitive'ler

luma
schema LineItem {
  sku: string
}

schema Customer {
  name: string
}

schema Page<T> {
  items: T[]
}

schema Pair<A, B> {
  left: A
  right: B
}

schema Box<T> {
  value: T
}

schema Envelope<T> {
  payload: T
  trace_id: string
}

schema Deep<T> {
  inner: Box<T>
}

schema Catalogue {
  pair: Pair<LineItem, Customer>
  pages: Page<Envelope<LineItem>>
  count: Box<int>
  deep: Deep<LineItem>
}

Tip argümanı bir primitive (Box<int>), bir schema veya başka bir açılım (Page<Envelope<LineItem>>) olabilir; generic bir gövde de ikinci bir generic'e başvurabilir — yukarıdaki Deep<T> içinde bir Box<T> taşıyor. Birbirinden farklı her açılım bir kez üretilir ve adı argümanlarından kurulur: Pair_LineItem_Customer, Page_Envelope_LineItem, Box_int, Deep_LineItem. Argüman sayısı tutmazsa sayı adıyla söylenir: Generic schema 'Pair' expects 2 type argument(s), got 1.

Parametre neyin yerine geçer, ona ne verilebilir

Yer değiştirmenin iki tarafı da yukarıdaki örneklerin düşündürdüğünden geniştir. Parametre çıplak bir alanla sınırlı değildir: her bileşik pozisyona yerleşir, yani tek bir bildirim aynı tipin array, map, nullable, tuple ve ham JSON biçimlerinin hepsini kapsar.

luma
schema LineItem {
  sku: string
}

schema Multi<T> {
  one: T
  many: T[]
  lookup: map<T>
  maybe: T | null
  pair: [T, string]
  raw: json<T>
}

schema Catalogue {
  rows: Multi<LineItem>
}

Argümanın da bir schema adı olması gerekmez. Tip gramerinin yazabildiği her şey verilebilir; her biri argümanın kendi yazılı biçiminden kurulan bir adla açılır:

YazılışıAçılım
Box<string[]>Box_string_array
Box<map<int>>Box_map_int
Box<string | null>Box_union_string_or_null
Box<enum["red", "green"]>Box_enum_red_green
Box<json<any>>Box_json_any
Box<Box<Box<LineItem>>>Box_Box_Box_LineItem

Type parameter yalnızca schema'ya ait bir yapıdır. Başka bir blok üzerine yazmak generic bir agent ya da generic bir fonksiyon vermez.

v0.9.0'dan beri modül sınırını geçiyor

Composition ve generic'ler eskiden import'lar birleştirilmeden önce çözülüyordu; bu yüzden ikisi de yazıldıkları dosyada kalıyordu. Çok modüllü bir paket ortak bir taban şemayı paylaşamıyordu — User başka bir dosyadan geldiğinde schema Admin = User + Audit Unknown schema composition part: User ile düşüyordu ve import edilmiş bir Page<T> alan tipinde instantiate edilemiyordu. Çözüm artık birleştirme sonrasına ertelendi; üç biçim de sınırı geçiyor:

import "./base.luma"      // Taban, Ek, Zarf<T>

schema Birlesik = Taban + Ek        // composition
schema Kullanim = Zarf<Ic>          // generic alias
schema Sonuc { kutu: Zarf<Ic> }     // generic alan tipi

Alias biçimi yalnızca yeni-modüllerarası değil — dilde hiç yoktu. schema K = Zarf<Ic> sessizce yutuluyordu: satır hiçbir şey olarak ayrıştırılıyor, K hiç kaydedilmiyor ve hata ancak sonradan No schema named 'K' was found olarak yüzeye çıkıyordu. Bilinen hiçbir biçime uymayan schema X = … satırı artık adıyla reddediliyor:

Error: Schema definition 'schema K = …' matches no known form: use 'A + B' composition, 'A | B tagged by field' union, or a 'Template<Args>' generic alias.
Ertelemenin ölçülmüş dört sınırı

Erteleme alan tiplerini ve alias'ları kapsıyor. Her şeyi kapsamıyor ve sınırlar varsayılmadı, ölçüldü:

  • Parametre pozisyonundaki bir generic — agent method'u ya da pure fn parametresi — hâlâ eager çözülür; import edilmiş bir şablon orada kullanılamaz.
  • Bekleyen bir generic alias composition parçası olamaz (Unknown schema composition part). O zincir gerekiyorsa alias'ı kendi modülünde somutlaştır.
  • Namespace'li import üzerinden erişilen şablon (mod.Zarf<Ic>) kapsam dışıdır — namespace'li şema adlandırması ayrı bir yüzey ve ölçülmedi.
  • Aynı ada sahip şablonu ihraç eden iki modül Import generic schema conflict ile reddedilir. Elmas — aynı modüle iki kez ulaşmak — şemalarda olduğu gibi no-op'tur.
Açılım başına tek kopya

Açılımlar kullanım yerine göre değil, açılımın kendisine göre önbelleğe alınır. Aynı method'un hem parametresinde hem dönüş tipinde Page<LineItem> yazmak yine tek bir Page_LineItem üretir; ikinci kullanım, birincinin kaydettiği schema'ya çözülür. $defs'in her sarmalayıcı anımıyla büyümemesini sağlayan şey budur.

Parse zamanında reddedilenler

Bunların her biri programı çalışmadan durdurur:

Error: Unknown type argument schema 'Missing' for generic 'Page'.
Error: Generic schema cycle found: Tree<int> -> Tree<int>.
Error: Tagged unions cannot use generic schema members: Document.
Error: Tagged union 'Document' must contain at least two members.
  • Hiçbir şeye karşılık gelmeyen bir tip argümanı — Page<Missing>. Argümanlar çözülür, öylece kabul edilmez.
  • Kendini açan bir generic — içinde children: Tree<T>[] taşıyan bir Tree<T>. Açılımın duracağı bir nokta yok; bu yüzden döngü sonsuza kadar işletilmek yerine onu üreten zincirle birlikte bildirilir.
  • Etiketli union üyesi olarak kullanılan bir generic — schema Document = Page<Invoice> | Page<Receipt> tagged by kind. Etiket üyenin schema adını taşır; bir açılımın oraya hangi adı vereceği tahmin edilmek yerine karara bağlanmadan bırakıldı. Ters yön serbest: etiketli union gayet iyi bir tip argümanıdır ve Envelope<Document> her açılım gibi açılır.
  • Bir alttaki kural da öyle: etiketli union en az iki üye ister, dolayısıyla schema Document = Invoice tagged by kind de reddedilir. Tek dallı bir toplam tip yalnızca yeniden adlandırmadır.
Generic ad bir tiptir, değer değil

Reflection bir değer alır, Page tek başına değer değildir; bu yüzden schema.toJsonSchema(Page<LineItem>) hiç çalışmaz:

Error: 'Page' variable is not defined yet.

Açılıma başka yoldan uzan: açılımı bir alanında tutan schema üzerinden reflection yap ya da açılmış adı doğrudan yaz — schema.toJsonSchema(Page_LineItem) — programdaki herhangi bir pozisyon onu açtığı anda o ad vardır. Hiçbir yerde açılım yoksa aynı mesaj bu kez açılmış adla geri gelir.

Union ve nullable

luma
note: string | null
value: int | float | null
flexible: string | int

Union, | ile yazılır ve JSON Schema'da anyOf üretir. Doğrulama sırasında üyeler sırayla denenir; hiçbiri tutmazsa tüm alt hatalar birleştirilerek raporlanır:

Customer.tax_id does not match any union type: Customer.tax_id must be a string. | Customer.tax_id must be null.

Etiketli union — tagged by

Bir alan tipinin içine yazılan |, etiketsiz bir union'dır. = ve tagged by ile tanımlanan schema ise başka bir şeydir: bir sum type. Değer üyelerden biridir ya da diğeridir; hangisi olduğunu bir etiket alanı söyler.

luma
schema Invoice {
  date: string
  total: float
  invoice_number?: string
}

schema Receipt {
  date: string
  total: float
  reference?: string
}

schema Document = Invoice | Receipt tagged by kind

Tanım tek satırdır, gövdesi yoktur ve en az iki üye ister; üyelerin hepsi önceden tanımlı schema olmalıdır — tanınmayan bir üyede parser Tagged union 'Document' member schema 'Voucher' was not found. diyerek durur. Bu noktadan sonra iki yazım tamamen farklı kurallarla çözümlenir:

YazımDeğer nasıl eşlenir
A | B — alan tipi içindeÜyeler sırayla denenir; doğrulamayı ilk geçen kazanır.
schema D = A | B tagged by kYalnızca k etiketi okunur ve tam olarak bir üyeyi adlandırır. Deneme yapılmaz.
Üyeler örtüştüğünde sırayla deneme, yanlış cevabı doğru diye raporlar

Gerçek schema'lar örtüşür. Yukarıdaki Invoice ile Receipt hem date hem total taşır ve onları ayıran alanlar opsiyoneldir — dolayısıyla yalnızca tarih ve toplam içeren bir değer ikisini de sorunsuz doğrular. Etiketsiz bir doc: Invoice | Receipt alanında ilk üye kazanır, doğrulama orada biter ve çalıştırma başarı raporlar. Değer, sırf üyeler o sırayla yazıldığı için fatura oldu.

Aynı alanı doc: Receipt | Invoice diye yazmak tıpatıp aynı değeri makbuza çevirir. Programda başka hiçbir şey değişmez ve hiçbir uyarı çıkmaz. Etiket bu tahmini ortadan kaldırır: tagged by kind altında "kind": "Receipt" taşıyan bir değer, Invoice ne kadar iyi oturuyor olursa olsun bir Receipt'tir.

Etiket alanı enjekte edilir, yazılmaz

tagged by kind, her üyeye kind alanını ekler ve bu alanın değeri o üyenin schema adıdır: "Invoice", "Receipt". Alanı sen hiç yazmazsın ve üyeler değişmez — Invoice tek başına, alan tipi, parametre ya da ask çıktısı olarak kullanıldığında yine tam olarak kendi tanımladığı alanlara sahiptir, aralarında kind yoktur.

Etiket enjekte edildiği için, onu bir de üyenin tanımlaması çakışmadır; parser tüm programı reddeder:

Error: Tagged union 'Document' discriminator 'kind' conflicts with member schema 'Invoice'.

Etiketin okunması

Etiket her şeyden önce okunur. Tek bir üyeyi seçer ve değerin kalanı yalnızca o üyeye göre doğrulanır; böylece bütün üyeler otururken bile seçilen dal deterministiktir:

luma
agent Router {
  fn route(document: Document) -> Result {
    match document {
      case Invoice invoice { return { branch: "Invoice" } }
      case Receipt receipt { return { branch: "Receipt" } }
    }
  }
}

router = Router()
out = router.route({ kind: "Receipt", date: "2026-02-01", total: 42.0 })

Etiketi olmayan — ya da string olmayan — bir değer hiçbir üyeye ulaşamaz; etiketi union dışındaki bir schema'yı adlandıran değer de öyle:

Router.route.document.kind must identify a tagged union member.
Router.route.document.kind has unknown tagged union member 'Voucher'.

Baştaki ön ek, doğrulanan değerin yoludur; burada Router.route metodunun document parametresi.

Opsiyonel / nullable matrisi

Bu tablo Luma'da en sık karıştırılan ayrımdır:

YazımAlan eksik olabilir?Değer null olabilir?JSON Schema required
x: string✗ hayır✗ hayır✓ içinde
x?: string✓ evet✗ hayır✗ değil
x: string | null✗ hayır✓ evet✓ içinde
x?: string | null✓ evet✓ evet✗ değil
x: string @default("a")✓ (eksikse doldurulur)✗ hayır✗ değil
LLM çıktısı için hangisi?

Modelden veri çıkarımı yaparken T | null genellikle ?'dan iyidir: alan her zaman mevcut olur, modelin "bulamadım" demesi için açık bir yol kalır ve strict schema modunda required listesinde kaldığı için model alanı atlayamaz. ? ise modelin alanı sessizce yutmasına izin verir.

// The preferred pattern in invoice extraction:
invoice_number: string | null @desc("Fatura numarası")
due_date: string | null @desc("Son ödeme tarihi")
items: InvoiceItem[] @desc("Tüm kalemler; yoksa boş liste")

Array ve null

YazımAnlamı
items: Item[]Zorunlu array. Boş liste [] geçerlidir.
items: Item[] @minItems(1)En az bir eleman zorunlu.
items: Item[] | nullnull ya da array; array boş olabilir.
items?: Item[]Alan hiç bulunmayabilir.

Nullable bir array'e uygulanan @minItems, @unique gibi koleksiyon annotation'ları yalnızca değer gerçekten array olduğunda çalışır; değer null ise atlanır.

Boyut ve aralık kısayolları

Tek boşluğun anlamı değiştirdiği yer
YazımAnlamı
float[0..1]0–1 elemanlı float array'i (köşeli parantez tipe yapışık)
float [0..1]0 ile 1 arasında tek bir float (tip ile parantez arasında boşluk)

Bu ikisi sessizce farklı davranır ve tipik olarak confidence: float[0..1] yazan biri, modelin 0.82 yerine [0.82] döndürmesini bekleyen bir schema üretmiş olur.

KısayolAnlamıEşdeğeri
string[3]Tam olarak 3 elemanlı string array'istring[] @minItems(3) @maxItems(3)
LineItem[1..100]1 ile 100 arasında elemanLineItem[] @minItems(1) @maxItems(100)
float [0..1]0 ≤ değer ≤ 1float @range(0, 1)
int [1..99]1 ≤ değer ≤ 99int @range(1, 99)
float[1..3] [0..100]1–3 elemanlı array; her eleman 0–100 arası—
luma
schema Answer {
  title: string
  paragraphs: string[2]           // exactly two paragraphs
  people: Person[1..3]        // 1-3 people
  scores: float[1..3] [0..100]
  confidence: float [0..1]        // a single number between 0 and 1
}
Aralık kısayolu ile @range birlikte kullanılamaz

score: float [0..100] @range(0, 50) yazarsan parser Field range cannot be specified with both legacy syntax and @range. hatası verir. Birini seç.

JSON Schema eşlemesi

Aşağıdaki schema ve onun ürettiği gerçek JSON Schema çıktısı, schema.toJsonSchema(...) ile alınmıştır:

luma
schema Address {
  city: string
}

schema Customer @desc("Fatura müşterisi") {
  name: string @length(2, 40) @title("Ad Soyad") @example("Ayşe Yılmaz")
  tax_id: string | null @length(10) @desc("Vergi kimlik numarası")
  email?: string @fmt("email") @alias("e_mail")
  score: float [0..100]
  tags: string[] @minItems(1) @unique @contains("vip")
  kind: enum["individual", "company"]
  metrics: map<float>
  address: Address
  active: bool @default(true)

  where score >= 0
  when kind == "company" require [tax_id]
}
jsonschema.toJsonSchema(Customer)
{
  "type": "object",
  "description": "Fatura müşterisi",
  "properties": {
    "name": { "type": "string", "minLength": 2, "maxLength": 40,
              "examples": ["Ayşe Yılmaz"], "title": "Ad Soyad" },
    "tax_id": { "anyOf": [ { "type": "string", "minLength": 10, "maxLength": 10 },
                           { "type": "null" } ],
                "description": "Vergi kimlik numarası" },
    "email": { "type": "string", "format": "email",
               "x-luma-aliases": ["e_mail"] },
    "score": { "type": "number", "minimum": 0, "maximum": 100 },
    "tags": { "type": "array", "items": { "type": "string" },
              "minItems": 1, "uniqueItems": true,
              "contains": { "const": "vip" } },
    "kind": { "type": "string", "enum": ["individual", "company"] },
    "metrics": { "type": "object", "additionalProperties": { "type": "number" } },
    "address": { "$ref": "#/$defs/Address" },
    "active": { "type": "boolean", "default": true }
  },
  "required": ["name", "tax_id", "score", "tags", "kind", "metrics", "address"],
  "additionalProperties": false,
  "allOf": [
    { "if":   { "properties": { "kind": { "const": "company" } } },
      "then": { "allOf": [ { "required": ["tax_id"] } ] } }
  ],
  "x-luma-where": [ { "expression": "score >= 0" } ],
  "x-luma-conditionals": [ … ]
}

Dikkat edilecek noktalar

  • @length(10) tek argümanla verildiğinde hem minLength hem maxLength 10 olur — yani tam olarak 10 karakter.
  • Union alanında annotation'lar ilgili branch'e uygulanır: uzunluk kısıtı string branch'ine gider, null branch'i dokunulmadan kalır.
  • @default ve ? alanı required dışında bırakır.
  • Kök nesne her zaman additionalProperties: false içerir — model schema dışı alan üretemez.
  • where ve koşullu kurallar x-luma-* uzantılarıyla taşınır; bunlar gateway tarafından yok sayılır, doğrulamayı Luma yerel olarak yapar.

JSON Schema'da etiketli union

Her üye anyOf içinde eksiksiz yayılır, etiket her birinde bir const'a sabitlenir ve o üyenin required listesine katılır. Ayırt etme işini yapan da o const'tur: bir belge Receipt'tir çünkü orada kind yalnızca "Receipt" olabilir. Aşağıdaki, yukarıdaki Document union'ının gerçek çıktısıdır:

jsonschema.toJsonSchema(Document)
{
  "anyOf": [
    { "type": "object",
      "properties": {
        "date": { "type": "string" },
        "total": { "type": "number" },
        "invoice_number": { "type": "string" },
        "kind": { "type": "string", "const": "Invoice" }
      },
      "required": ["date", "total", "kind"],
      "additionalProperties": false },
    { "type": "object",
      "properties": {
        "date": { "type": "string" },
        "total": { "type": "number" },
        "reference": { "type": "string" },
        "kind": { "type": "string", "const": "Receipt" }
      },
      "required": ["date", "total", "kind"],
      "additionalProperties": false }
  ]
}
oneOf yok, discriminator yok — bu bilinçli

v0.5.0'a kadar bu yüzey oneOf ve yanında bir discriminator yayıyordu. Katı json_schema modu ikisini de reddediyor, yani etiketli union — v0.2.0'ın manşet özelliği — katı bir provider'a hiç ulaşamıyordu. Wire biçimi, etiketi const olarak taşıyan anyOf'a taşındı; aynı bilgiyi her provider'ın kabul ettiği bir şekilde taşıyor ve schema.toJsonSchema tam olarak wire'a gideni bildiriyor. → strict mod

Etiketsiz yazımda bunların hiçbiri yoktur. doc: Invoice | Receipt alanı düz bir referans listesine dönüşür ve schema'da hangi üyenin kastedildiğini gateway'e — ya da bir doğrulayıcıya — söyleyen hiçbir şey kalmaz:

"doc": { "anyOf": [ { "$ref": "#/$defs/Invoice" }, { "$ref": "#/$defs/Receipt" } ] }

JSON Schema'da parametrik schema

Görülecek özel bir şey yok; parse zamanında açmanın bütün amacı da bu. Aşağıdaki, çıktı tipi Page<LineItem> olan bir ask için giden gerçek istek gövdesi; modelin eline dar bir object schema geçiyor ve ortada bir parametre yazıldığını hiç öğrenmiyor:

jsonresponse_format.json_schema.schema
{
  "type": "object",
  "properties": {
    "items": { "type": "array", "items": { "$ref": "#/$defs/LineItem" } },
    "total": { "type": "integer" }
  },
  "required": ["items", "total"],
  "additionalProperties": false,
  "$defs": {
    "Page_LineItem": {
      "type": "object",
      "properties": {
        "items": { "type": "array", "items": { "$ref": "#/$defs/LineItem" } },
        "total": { "type": "integer" }
      },
      "required": ["items", "total"],
      "additionalProperties": false
    },
    "LineItem": {
      "type": "object",
      "properties": {
        "sku": { "type": "string" },
        "amount": { "type": "number" }
      },
      "required": ["sku", "amount"],
      "additionalProperties": false
    }
  }
}

$defs yalnızca bu kök schema'nın eriştiklerini taşır: açılımın kendisi ve onun başvurduğu LineItem. Aynı programda başka bir yerde, hiçbir isteğin uğramadığı bir yolda duran Page<Customer> gövdede yoktur — bir açılım, erişilebilir bir şey ona işaret etmedikçe istek maliyeti değildir. Parametrik schema'ların $defs budaması inene kadar bekletilmesinin sebebi buydu: budama olmadan tek bir sarmalayıcı, ödeme yapılan her çağrıda payload tipi sayısı kadar çoğalırdı.

Tam eşleme tablosu

Luma tipiJSON Schema
string{"type":"string"}
int{"type":"integer"}
float{"type":"number"}
bool{"type":"boolean"}
null{"type":"null"}
any{} (kısıt yok)
enum[…]{"type":"string","enum":[…]}
T[]{"type":"array","items":T}
map<T>{"type":"object","additionalProperties":T}
json<T>T ile aynı
A | B{"anyOf":[A, B]}
schema D = A | B tagged by k{"anyOf":[A, B]} — her üye ayrıca k'yı const olarak kazanır; ayırt etmeyi o yapar
[A, B] (tuple){"type":"array","prefixItems":[A,B],"minItems":2,"maxItems":2}
SchemaName{"$ref":"#/$defs/SchemaName"}
schema P<T> {…}, P<X> olarak{"$ref":"#/$defs/P_X"} — açılım $defs altında sıradan bir object schema olarak durur

Hangi tipi ne zaman?

✓ Model çıktısı için sağlam
schema Record {
  id: string
  amount: float | null
  status: enum["open", "closed"]
  items: LineItem[]
  meta: MetaEntry[]
}
✗ Model çıktısı için kırılgan
schema Record {
  id: any             // no validation
  amount: float           // no "not found" path
  status: string          // free text
  items?: LineItem[]      // the model may skip it
  meta: map<any>        // may be rejected in strict mode
}
İhtiyaçTercihNeden
Sabit değerler kümesienum[…]Model serbest metin üretemez; enum ihlali yerel olarak yakalanır.
Bulunmayabilecek değerT | nullAlan zorunlu kalır, modele açık bir 'yok' yolu verir.
Şemaya sığmayan ek bilgiEntry[] (key/value)Strict gateway'lerle uyumlu; map<T> değil.
Serbest JSON taşımakany veya json<T>any doğrulamaz; json<T> doğrular.
Sabit uzunluklu sayı çiftifloat[2] veya iki alanlı schemaTuple konum başına kısıt taşıyamaz.
Para/ölçü değerifloat + whereTutarlılık invariant'ını where ile bağla.
Kimlik/kodstring + @match / @fmtBiçimi schema seviyesinde zorla.

Query satırı için tip seçmek

v0.4.0'dan beri aynı tip sözlüğü, hiçbir modelin üretmediği veriyi de tipler. Bir query Schema[] döndürür ve onu satır satır doğrular; bir parallel over her eleman sonucunu bildirilen eleman schema'sına göre doğrular. İkisi de düşen pozisyonu adlandırır — Orders.row[0].amount must be a number., classified[2].band must be one of these values: … — yani yukarıdaki tablo hâlâ geçerlidir, ama arkasındaki muhakeme değişir.

Model çıktısı tipi bir pazarlıktır: modelin ne söyleyebileceğini şekillendirirsin ve her tercih, katılığı modelin uyum sağlayabilmesiyle takas eder. Satır tipi ise bir tariftir: şekil zaten tablo tarafından sabitlenmiştir, seninle pazarlık eden kimse yoktur ve tek soru bildirimin köprünün geri verdiğiyle örtüşüp örtüşmediğidir. Girişte hiçbir şey dönüştürülmez — bildirilen tip, sürücünün ürettiği JS değerine karşı kontrol edilir — dolayısıyla Date olarak gelen bir kolon string'i karşılamaz ve çözüm daha gevşek bir tip değil, SQL ya da köprüdür.

İhtiyaçTercihNeden
NOT NULL kolonTTablo bunu zaten garanti eder. Bunu ihlal eden satır schema kaymasıdır; yutulmasını değil, bildirilmesini istersin.
Nullable kolonT | nullSQL NULL değeri null olarak gelir ve çıplak T satırı reddeder. x?: T burada işe yaramaz — o, eksik anahtara izin verir; select'ten çıkan satır ise kolonu anahtar olarak taşır.
Aggregate (count, sum)int / float, çıplak Schema dönüşüyle[] içermeyen dönüş tipi tam olarak bir satır olduğunu iddia eder: query 'Totals' expected exactly one row, got 3.
Tipini henüz netleştirmediğin kolonany, o da geçici olarakany hiçbir şey doğrulamaz; sınır sorunu adlandırmayı bırakır ve uyumsuzluk sonradan, satır indeksi olmadan geri döner. Keşif sırasında uygun, zamanlanmış bir işte risktir.