Luma · Public Developer Documentation
LLM çağrısı bir kütüphane değil,
dil yapısıdır
LLM'ler yazılıma girdiğinde mevcut diller onları karşılamak üzere tasarlanmamıştı. Boşluk yamalarla dolduruldu: prompt için string birleştirme, çıktı için çalışma anında parse, güven için yanına iliştirilen bir validation kütüphanesi, dayanıklılık için her projede yeniden yazılan retry kodu. Luma bu katmanı yama olarak değil dilin kendisi olarak ele alır — ve yalnızca bu katmanı.
Diller yamanıyor
Bir modele soru sorup yapılandırılmış cevap almak bugün her dilde aynı şekilde yapılıyor: metin gönder, metin al, geri kalanına kendin çare bul. Bu çarelerin toplamı artık her LLM projesinde tekrar eden bir yama katmanı oluşturuyor.
Bugünkü yama katmanı
- Tip güvenliği model sınırında biter. Fonksiyonun
Invoicedöndürdüğünü söylersin, derleyici buna inanır. Gerçekte elinde henüz ayrıştırılmamış bir metin vardır. - Doğrulama sözleşmenin parçası değildir. Ayrı bir kütüphane, çağrının yanına elle yerleştirilir. Unutulursa hiçbir şey uyarmaz.
- Retry el yazması glue kodudur. Hatanın modele nasıl anlatılacağı her projede yeniden icat edilir.
- Belirsizlik kaynakta görünmez. Bir fonksiyonun modele ulaşıp ulaşmadığını anlamak için çağrı zincirini takip etmen gerekir.
- Tool çağrısı bir framework soyutlamasıdır. Ne zaman — hatta çalışıp çalışmayacağı — kod okunarak bilinemez.
- Gözlemlenebilirlik sonradan takılır. Decorator'lar, wrapper'lar, elle sayılan token'lar.
Luma'nın karşılığı
- Tip sistemi sınırı geçer.
ask "…" -> Invoicebir tip beyanıdır; sonuç doğrulanmadan o satırdan çıkılmaz. - Doğrulama bir dil kuralıdır. Schema hem isteğin sözleşmesi hem yanıtın denetimidir. Atlanamaz.
- Retry dilin kontrol akışıdır. Doğrulama hatası prompt'a eklenip yeniden denenir; mekanizma dilde tanımlıdır.
- Belirsizlik sözdiziminde işaretlidir.
askbir keyword'dür vepure fniçinde kullanılamaz — parser reddeder. - Tool çağrısı ya deterministiktir ya beyan edilmiştir. Direct çağrı yazdığın yerde çalışır; native çağrı yalnızca izin verdiğin tool'larla mümkündür.
- Telemetry doğuştan vardır. Her model isteği, tool çalıştırması ve her doğrulama kuralı event üretir.
Diller LLM'i sıradan bir I/O çağrısı gibi ele almaya çalışıyor. Oysa LLM bir I/O çağrısı değil: sonucu sözleşmeye uymayabilen bir hesaplama. Bu fark kütüphane seviyesinde değil, dil seviyesinde ele alınmayı hak ediyor.
Pragmatik yaklaşım
Luma yeni bir genel amaçlı dil değildir, olmaya da çalışmaz. Yalnızca yamanın biriktiği katmanı devralır — modelle konuşulan sınırı ve v0.4.0'dan beri onun iki yanındaki sınırları: içeri giren veritabanı satırlarını ve dışarı çıkan sonuçları. Tool'ların ve veritabanı sürücünün gerçek kodu senin JS/TS dosyanda kalır, uygulamanın geri kalanı kendi dilinde kalır. Pragmatik olan kısım budur: her şeyi değiştirmez, tam olarak bozuk olan yeri değiştirir.
Somut olarak bu, bir pipeline'ın tek adımıdır. Yukarıdan bir şey onu zamanlar; Luma tipli satırları çeker, yeniden şekillendirir, zor kısmı sınırlı bir fan-out altında modele gönderir ve sonucu geri yazar. Scheduling, DAG bağımlılıkları ve backfill senin orchestrator'ında kalır — bu çizginin nerede ve neden çekildiği için Veri & Entegrasyon'a bak.
1 · Tek tanım, üç iş
Bir schema yazarsın. Runtime onu birbirinden ayrı üç yerde kullanır:
| Nerede | Ne yapar |
|---|---|
| İstek gönderilirken | Schema, strict response_format.json_schema olarak gateway'e gider; biçim üretim anında zorlanır. |
| Yanıt döndüğünde | Aynı schema ile yerel doğrulama çalışır. Provider'a güvenilmez: tip, aralık, format, where invariant'ı ve bilinmeyen alan kontrolü kendi runtime'ında yapılır. |
| Doğrulama başarısız olursa | Hata metni prompt'a eklenip yeniden denenir. Model neyi yanlış yaptığını okur. |
Bunları elle kurmak üç ayrı kod parçası ve üç ayrı hata kaynağı demektir. Burada aynı tanımın üç görünümüdür; senkronizasyonu kaybolamaz.
Bir kural ya tutuyordu ya da koşuyu durduruyordu. Gerçek extraction'ın ise ikilinin söyleyemediği üçüncü bir hâli var: cevap kullanılabilir ama hakkındaki bir kural tutmadı. @severity("warn"), bir kuralın bunu söyleme biçimi.
| Durum | Ne olur |
|---|---|
| Geçerli | Değer kullanılır. |
| Geçersiz | Koşu eskisi gibi durur. Varsayılan hâlâ bu — @severity geçmeyen bir schema tam olarak eskisi gibi davranır. |
| Geçerli ama şerhli | Değer kullanılır ve kuralın bulduğu şey, programın okuyabileceği bir finding olarak onunla birlikte yol alır. |
Üçüncü duruma yalnızca görünür olduğu için izin var. Şerhli değer de diğerleri gibi retry hakkını alır; denemeler tükendiğinde ölümcül olmak yerine kabul edilir, şerh onu üreten operasyona kaydedilir, ve program hiç okumazsa koşu susmak yerine standart hatada bunu söyler. Ayrıca yol almaz: şerhli bir değer geçmeye çalıştığı ilk tipli sınırda düşer — yani warn, bir üreticinin ne döndürebileceğini genişletir, bir tüketicinin neyi kabul etmek zorunda olduğunu değil. → @severity
2 · Belirsizlik sınırını parser tutar
io fn kaydet(yol: string, icerik: string) -> string {
return io.writeText(yol, icerik)
}
agent Analist {
fn incele(metin: string) -> Analiz {
ask "Analiz et: {metin}" -> Analiz
}
}pure fn toplam(kalemler: Kalem[]) -> float {
s = ask "Topla: {kalemler}" -> Sonuc
return s.deger
}
// 'ask' cannot be used inside a pure fn.
schema Fatura {
toplam: float
where io.exists("@x.json")
}
// where is deterministic and cannot use IO.Bir fonksiyonun modele ulaşıp ulaşmadığı, IO yapıp yapmadığı gövdesine bakınca görünür — ve bu ayrım konvansiyon değil, parse zamanı kuralıdır. Schema doğrulaması bu sayede her zaman deterministiktir: aynı veri her zaman aynı sonucu verir.
3 · Yan etki tipin parçasıdır
tool db_write(request_id: string, value: string) -> DbResult {
side_effects: "write"
scope: "db:write"
credential: "DB_PASSWORD"
idempotent: true
idempotency_key: request_id
}
agent Yazici {
tools: [db_write]
tool_scopes: ["db:write"]
tool_policy: "approval"
}"Model yanlışlıkla veritabanına yazdı" burada code review'un yakalaması gereken bir şey değil, runtime'ın reddettiği bir şeydir. İzin, kimlik bilgisi, yan etki sınıfı ve tekrar güvenliği contract'ın kendisinde durur.
4 · Gözlemlenebilirlik sonradan eklenmez
Tek bir --trace-file bayrağı, hiçbir enstrümantasyon kodu yazmadan şunu verir: her provider isteği (model, süre, token, maliyet, correlation ID'leri), her tool çalıştırması (cache isabeti, timeout, idempotency) ve her doğrulama kuralı — başarılı olanlar dahil.
jq -r '.events[] | select(.type=="validation" and .ok==false)
| "\(.name) [\(.metadata.rule)] \(.error)"' trace.jsonBir çıkarımın neden başarısız olduğunu tahmin etmek yerine okursun. → Telemetry & Trace
5 · Tip sistemi tek bir şekilde durmaz
Bir schema type parameter alabilir ve bir union deneme yanılmayla değil tag ile çözülebilir. İkisi de aynı sebeple var: model sınırında, iki kez yazmak zorunda kaldığınız bir şekil kendisiyle çelişebilen bir şekildir ve tahminle seçilen bir dal, doğru diye raporlanan yanlış bir cevaptır.
schema Invoice {
invoice_number: string
total: float
}
schema Receipt {
reference: string
total: float
}
schema Document = Invoice | Receipt tagged by kind
schema Page<T> {
items: T[]
count: int
}
agent Desk {
fn list() -> Page<Document> {
ask "List the documents in this batch." -> Page<Document>
}
}Page<T> bir kez yazılır ve dosya parse edilirken açılır: Page<Document>, Page_Document adlı sıradan bir schema'ya dönüşür ve provider'a hiçbir genericlik ulaşmaz — istek düz bir object schema'sı taşır. Pair<A, B>, Box<int> ve Page<Envelope<LineItem>> aynı şekilde çalışır; her instantiation, argümanlarından kurulan bir adla bir kez açılır.
Ergonomiyi değil cevabı değiştiren kısım tagged by kind. Yukarıdaki Invoice ile Receipt'in ikisi de total taşıyor, dolayısıyla yalnızca bir toplam içeren bir değer her ikisine de uyar. Tag'siz bir Invoice | Receipt member'ları sırayla dener, ilk uyan kazanır ve koşu başarı raporlar — değer, member'ları hangi sırayla yazdığınız yüzünden fatura oldu. Tag altında kind alanı tam olarak bir member'ı adlandırır ve hiçbir şey denenmez. → Tip sistemi
Gizli bir planlayıcı, otomatik "ReAct döngüsü" veya senin adına prompt zincirleyen bir katman yoktur. Sunduğu şey deterministik bir iskelet: nerede model çağrıldığı, nerede deterministik kodun çalıştığı ve hangi tool'un ne zaman çalışabileceği kaynak koda bakınca görünür. Sürprizler runtime'a değil, parse aşamasına taşınır.
Eksiksiz bir Luma programı
Aşağıdaki dosya çalıştırılabilir bir programın tamamıdır — import edilecek framework, kurulacak class, yazılacak glue kod yoktur.
input metin: string
schema Analiz @desc("Bir metin parçasının yapılandırılmış analizi") {
ozet: string @length(20, 400)
duygu: enum["olumlu", "notr", "olumsuz"]
etiketler: string[] @minItems(1) @maxItems(5) @unique
guven: float [0..1]
}
model Primary {
provider: "openai-compatible"
base_url: "https://gateway.example/v1"
model: "text-model-l"
api_key: getEnv("api_key")
capabilities: ["json_schema"]
}
agent Analist {
model: Primary
role: "Kısa, tarafsız ve yalnızca verilen metne dayalı analiz üret."
temperature: 0
retry: 2
fn incele(metin: string) -> Analiz {
ask "Aşağıdaki metni analiz et:\n\n{metin}" -> Analiz
}
}
analist = Analist()
sonuc = analist.incele(metin){
"ozet": "Kargo teslimatı iki gün gecikmiş, ancak destek ekibi sorunu hızla çözmüş.",
"duygu": "notr",
"etiketler": ["kargo", "gecikme", "destek"],
"guven": 0.82
}Bu tek dosyada çalışan şeyler:
Analizschema'sı JSON Schema'ya çevrilipstrict: trueile gateway'e gönderildi.- Yanıt yerel olarak doğrulandı:
ozetuzunluğu,duyguenum üyeliği,etiketleriçinminItems/maxItems/unique,guveniçin[0..1]aralığı — ve schema'da olmayan bir alan gelseydi o da hata olurdu. - Doğrulama başarısız olsaydı hata metni prompt'a eklenip en fazla 2 kez daha denenecekti (
retry: 2). - API key koda değil environment'a yazıldı;
getEnvdışında hiçbir yerden erişilemez ve trace/prompt/cache içine sızmaz. - Model çağrısı, süresi, token kullanımı ve her doğrulama kuralı telemetry event'i olarak kaydedildi.
Sonuç: sonuc.guven yazdığında bu değerin gerçekten 0 ile 1 arasında bir float olduğunu bilirsin — çünkü öyle olmasaydı program o satıra hiç gelmezdi.
Çalışma modeli
Bir .luma dosyası çalıştırıldığında izlenen yol:
.luma kaynak
│
├─ comment strip → import çözümleme → blok çıkarımı
├─ schema / model / run_policy / tool / agent / pure fn parse
├─ static analysis (ulaşılmaz kod, bilinmeyen schema alanı,
│ dependency cycle, sonsuz loop, kullanılmayan değişken)
↓
program modeli
│
├─ model credential çözümleme (getEnv)
├─ tool registry + tool module yükleme
├─ capability probe (GET /models → json_schema, tool_calling…)
↓
operation yürütme (kaynaktaki sırayla)
│
├── deterministik adımlar assign · map/filter/reduce · if/switch/for/while
├── direct tool tool hesapla({...}) → policy → validate → çalıştır
├── ask schema → JSON Schema → provider adapter
│ └── native tool loop tool_calls → çalıştır → sonucu geri gönder → 2. tur
├── run policy fallback · parallel · race · vote · fusion · combo · escalate
├── parallel blokları fan-out → fan-in (nested/flat) → merge
├── query / command köprü → satır tavanı → satır başına schema doğrulama
├── parallel over eleman başına bir gövde → girdi sıralı sonuç deposu
├── http / state alt süreçte istek · atomik key/value yazma
└── her adımda telemetry event'i
↓
stdout JSON + trace.json / trace.jsonl / otel.jsonYetenek haritası
Tip sistemi
primitive · enum · array · tuple · map<T> · json<T> · union · nullable · nested schema referansı · array uzunluk ve sayısal aralık kısayolları.
Schema & validation
17 field annotation, where invariant'ları, when/else when/else koşullu kuralları, conditional default'lar, schema composition ve deterministik reflection.
Agent & orchestration
Deterministik / LLM / karma method'lar, 30 yapılandırma anahtarı, dependency (uses), 4 memory scope, parallel fan-out/fan-in, token ve cost bütçeleri.
Tool sistemi
Typed tool contract'ları, direct çağrı ve native tool calling, scope/credential/approval politikaları, retry & backoff, idempotency store, cache, circuit breaker, strict sandbox.
◉Model & provider
Model profilleri, OpenAI-compatible / Anthropic / Gemini / Azure adapter'ları, capability probe, response cache, streaming, reasoning, multimodal ve 8 run policy stratejisi.
∑Standard library
math · string · date · collections · regex · numeric · json · schema · agent · io — hepsi deterministik pure fn, where ve @valid içinde çağrılabilir.
IO & multimodal
Proje köküyle sınırlı dosya erişimi, doğal sıralı listFiles, image/PDF marker üretimi ve provider'a özel content mapping.
Telemetry
llm · tool · agent · validation · http · query · command · parallel_over event'leri, correlation ID zinciri, token/cost metrikleri, JSON · JSONL · OpenTelemetry export.
Veri & entegrasyon
SQL injection'ı dilbilgisinde kapatan tipli datasource/query/command contract'ları, std/http, artımlı std/state, parallel over veri fan-out'u ve satır/byte tavanları.
Tasarım ilkeleri
| İlke | Pratikte ne demek |
|---|---|
| Schema tek doğruluk kaynağıdır | Aynı tanım JSON Schema üretimi, yerel doğrulama, retry feedback'i ve TypeScript tool tipleri için kullanılır. İkinci bir yerde tekrar tanımlanmaz. |
| Provider'a güvenilmez | strict: true JSON Schema gönderilse bile dönen yanıt yerel olarak baştan doğrulanır. Bilinmeyen alan da hatadır. |
| Deterministik olan deterministik kalır | pure fn ve where LLM, tool, IO veya dış state kullanamaz — parser bunu parse aşamasında engeller. |
Secret sadece getEnv ile girer | api_key: getEnv("api_key") tek yoldur. Key prompt'a, schema metadata'sına, cache anahtarına ve trace'e yazılmaz; hata mesajlarında redakte edilir. |
| Yan etki bildirilir | Tool'lar side_effects, scope, credential ve needs ile kendini beyan eder; agent tool_policy ile bunları kısıtlar. |
| Her davranışın testi vardır | npm test 550 testi çalıştırır: demo programlar, geçersiz syntax senaryoları, tool/runtime davranışı ve mock gateway üzerinden provider contract'ı. |
Yazım kuralları
schema InvoiceLine {
unit_price: float
quantity: int
}
agent Extractor {
fn extract(md: string) -> InvoiceLine {
ask "..." -> InvoiceLine
}
}Schema invoiceLine { // keyword büyük harf değil
Unit_Price: Float // tip adı lowercase olmalı
}
Agent extractor { // declaration adı büyük harfle başlar
Fn Extract() -> ... {}
}Keyword'ler daima küçük harf (schema, agent, tool, fn, ask); declaration isimleri büyük harfle başlar (Invoice, Analist); yerleşik tipler küçük harftir (string, int, float, bool).
Nereden devam etmeli
Dili öğreneceğim
Statement grameri, expression dili, kontrol akışı, input/context ve import sistemi.
Veri çıkarımı yapacağım
Annotation referansı, where, koşullu kurallar ve doğrulama sırası.
Kendi tool'umu bağlayacağım
Tool contract'ı, registry, module yükleme, güvenlik politikaları ve idempotency.
Belge/görsel işleyeceğim
Çok sayfalı fatura pipeline'ı: PNG → Markdown → typed extraction.
Dayanıklılık istiyorum
Fallback, race, vote, fusion, combo ve maliyet-farkında escalate run policy'leri; capability probe.
Bir hata alıyorum
25 normalize error code, validation mesajları ve teşhis tablosu.
Bir pipeline adımı kuruyorum
Tipli veritabanı contract'ları, HTTP, watermark ile artımlı koşular, parallel over ve hacim tavanları.