Luma public docs
English

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ı.

550
geçen test
13
std modül
30
agent ayarı
8
run policy
18
field annotation
25
normalize error code

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 Invoice dö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 "…" -> Invoice bir 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. ask bir keyword'dür ve pure fn iç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:

NeredeNe yapar
İstek gönderilirkenSchema, strict response_format.json_schema olarak gateway'e gider; biçim üretim anında zorlanır.
Yanıt döndüğündeAynı 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 olursaHata 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.

v0.8.0'dan beri doğrulanmış değerin iki değil üç durumu var

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.

DurumNe olur
GeçerliDeğer kullanılır.
GeçersizKoşu eskisi gibi durur. Varsayılan hâlâ bu — @severity geçmeyen bir schema tam olarak eskisi gibi davranır.
Geçerli ama şerhliDeğ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

✓ Parser kabul eder
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
  }
}
✗ Parser reddeder
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

luma
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.json

Bir çı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.

luma
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

Luma bir agent framework'ü değil

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.

lumaanaliz.luma
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)
jsonçıktı
{
  "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:

  • Analiz schema'sı JSON Schema'ya çevrilip strict: true ile gateway'e gönderildi.
  • Yanıt yerel olarak doğrulandı: ozet uzunluğu, duygu enum üyeliği, etiketler için minItems/maxItems/unique, guven iç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ı; getEnv dışı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.json

Yetenek 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

İlkePratikte ne demek
Schema tek doğruluk kaynağıdırAynı 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üvenilmezstrict: 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ırpure fn ve where LLM, tool, IO veya dış state kullanamaz — parser bunu parse aşamasında engeller.
Secret sadece getEnv ile girerapi_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 bildirilirTool'lar side_effects, scope, credential ve needs ile kendini beyan eder; agent tool_policy ile bunları kısıtlar.
Her davranışın testi vardırnpm 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ı

✓ Doğru
schema InvoiceLine {
  unit_price: float
  quantity: int
}

agent Extractor {
  fn extract(md: string) -> InvoiceLine {
    ask "..." -> InvoiceLine
  }
}
✗ Yanlış
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