GNL
Dokümanlar menüsü
Core · Ücretsiz@gnldev/durable

Çalıştırma-başına limitler & maliyet tavanı

RunLimits ile çalıştırma-başına token/maliyet/araç-çağrısı tavanında kaçak bir çalıştırmayı durdurun.

Ne işe yarar / ne zaman kullanılır#

Bir çalıştırma kontrolden çıkabilir — pahalı bir model döngüsü, kaçak bir araç-çağrısı zinciri, bütçelemediğiniz bir maliyet. RunLimits, birikimli token'lara, birikimli USD maliyetine ve başarılı araç çağrısı sayısına çalıştırma-başına bir tavan koyar; ayrıca aynı çağrıyı tekrarlayıp duran bir aracı da tespit edebilir. Tüm alanlar opsiyoneldir: bir alanı ayarlamazsanız o koruma çalıştırma için KAPALIdır.

RunLimits alanları#

Her alan opsiyoneldir ve ayarlanmazsa bağımsız olarak kapalıdır:

typeRunLimits.maxTokens

Birikimli toplam token (girdi + çıktı + cache'lenmiş) bu değeri AŞARSA çalıştırmayı durdurur.

typeRunLimits.maxCostUsd

Birikimli USD maliyeti bu değeri AŞARSA çalıştırmayı durdurur.

typeRunLimits.maxToolCalls

Başarılı araç çağrısı sayısı bu değere ULAŞTIĞINDA (>=) BİR SONRAKİ araç çağrısını bloklar.

typeRunLimits.loopDetection

Opsiyonel alt-yapılandırma: { maxRepeats?, onRepeat? }. Aynı argsHash ile art arda çağrılan aynı aracı tespit eder.

typeloopDetection.maxRepeats

Kaç ardışık özdeş (araç + argsHash) çağrının cevabı tetikleyeceği.

typeloopDetection.onRepeat

'block' anında ToolLoopDetectedError fırlatır; 'reflect' önce modele bir nudge iletir.

Kurulum / import#

RunLimits @gnldev/durable içinde yaşar ve çalıştırma başına ayarlanır:

import { createGnl, RunLimitExceededError, type RunLimits } from '@gnldev/durable';

Adım adım kullanım#

gnl.run'a limits geçin. Bir tavan aşıldığında RunLimitExceededError fırlatılır ve çalıştırma bir sonraki adıma geçmez:

// Checked AFTER each model step is journaled a single step can OVERSHOOT the ceiling,
// but the run does not proceed to the NEXT step once it is breached.
const limits: RunLimits = { maxTokens: 50_000, maxCostUsd: 0.5, maxToolCalls: 20 };

try {
  await gnl.run('asst', { runId: 'r5', prompt: 'do the task', limits });
} catch (e) {
  if (e instanceof RunLimitExceededError) {
    // e.detail: { kind, value, limit }
    console.log('limit exceeded:', e.detail.kind, e.detail.value, e.detail.limit);
  }
}

Aynı araç çağrısını tekrarlayıp duran bir modeli yakalamak için loopDetection ayarlayın. reflect, hiçbir şey bloklanmadan önce modele toparlanma şansı verir:

// Same tool + identical argsHash repeated 3 times IN A ROW 'reflect' delivers a nudge
// instead of an immediate block ('block' throws ToolLoopDetectedError right away).
const limits: RunLimits = { loopDetection: { maxRepeats: 3, onRepeat: 'reflect' } };

Hata fırlatıldığında journal tutarlı kalır — limiti yükseltin ve çalıştırmanın bıraktığı yerden devam etmek için AYNI runId ile resume edin:

// The journal stays consistent when RunLimitExceededError is thrown.
// Raise the ceiling and resume the SAME runId to continue past the cut-off point:
await gnl.run('asst', { runId: 'r5', prompt: 'continue', limits: { maxTokens: 200_000 } });

Streaming: bloklanan/limit ihlallerini yakalamak#

streamDurable / gnl.stream(name, opts), bir loop, maxToolCalls, duplicate ya da tainted BLOCK ateşlendiğinde çağrılan bir onBlocked callback'i kabul eder. Guard'ın ürettiği HAM yapılandırılmış ihlali alır — fırlatılan bir hatanın taşıyacağı bilgiyle aynısını. result.text artık bir block'ta da REDDEDİLİR; bu, gnl.run'dan fırlatılan RunLimitExceededError'ı yansıtır, böylece stream'in doğrudan bir tüketicisi bunu sessizce kaçıramaz.

// onBlocked fires for a loop / maxToolCalls / duplicate / tainted BLOCK you get the
// RAW breach and decide what (if anything) the user sees. GNL does not invent a
// user-facing message.
const result = await gnl.stream('asst', {
  runId: 'r6',
  prompt: 'do the task',
  limits,
  onBlocked: (breach) => {
    // breach.kind identifies which guard fired; breach.message is a raw diagnostic
    // string; breach.detail carries that guard's own structured payload.
    console.log('blocked:', breach.kind, breach.message);
  },
});

try {
  const text = await result.text;
} catch (e) {
  // result.text REJECTS on a block — mirrors runDurable throwing, so a direct
  // consumer of the stream cannot silently miss the breach.
}

API referansı#

classRunLimitExceededError

Bir tavan aşıldığında enforceStepLimits tarafından fırlatılır. detail: { kind, value, limit }; kind bir RunLimitKind'dır.

typeRunLimitKind

'maxCostUsd' | 'maxTokens' | 'maxToolCalls' — RunLimitExceededError.detail içinde hangi tavanın aşıldığını belirtir.

fnenforceStepLimits

maxTokens/maxCostUsd'yi her model adımı journal'landıktan SONRA kontrol eder; maxCostUsd ve maxTokens'ın ikisi de null ise erken döner.

typeRunOptions.limits

RunOptions.limits ile ayarlanır, runDurable/streamDurable'a iletilir ve alt-ajanlar tarafından olduğu gibi devralınır.

typeonBlocked

streamDurable / gnl.stream(name, opts) üzerinde callback seçeneği: (breach) => void. Bir loop, maxToolCalls, duplicate ya da tainted guard BLOCK olduğunda çağrılır. breach: { kind, message, detail } — kullanıcıya gösterilecek bir metin değil, ham yapılandırılmış payload. Bir block ateşlendiğinde result.text de reddedilir.

Organizasyon-seviyesi bütçeyle aynı değil
RunLimits / RunLimitExceededError tamamen çalıştırma-başınadır@gnldev/server'daki organizasyon-seviyesi bütçe sisteminden (BudgetExceededError → HTTP 402, budget-quota sayfasında belgelenir) FARKLIDIR; o sistem harcamayı çalıştırmalar boyunca organizasyon başına izler. İkisini birbirine karıştırmayın: bir çalıştırma RunLimits sınırları içinde kalıp yine de organizasyon bütçesi tarafından reddedilebilir, ya da tersi olabilir.
Not
Tavan konusunda dürüst olalım: enforceStepLimits, maxTokens/maxCostUsd'yi her adım journal'landıktan SONRA kontrol eder; bu yüzden kesme noktası tavanı AŞAN İLK adıma denk gelir — tek bir adım tavanı AŞABİLİR. Garanti 'bir sonraki adıma geçmez'dir, '<= tam olarak limit' değildir. Kaçağı durdurur; bir adımı yarıda kesmez.
GNL kullanıcıya gösterilecek mesajınızı yazmaz
onBlocked size framework'ün yapılandırılmış ihlalini verir — kind, message, detail — bir son kullanıcı için düşünülmüş bir cümle değil. Kullanıcının ne göreceğine (görecekse) karar vermek uygulamanızın işidir: GNL kullanıcıya gösterilecek bir mesaj icat etmez.
İlgili
Sıkça birlikte kullanılan organizasyon-seviyesi karşılığı için budget-quota'ya, RunLimits'in karşılaştırdığı maliyet rakamının nasıl hesaplandığı için cost-observability'ye ve tekrarlanan yan-etkili bir çağrıya karşı kardeş çalıştırma-başına koruma için duplicate-guard'a bakın.