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

Durable workflow'lar

Çok-adımlı workflow'ları aynı runId ile durable çalıştırır; suspend/resume güvenlidir, retry() ile bildirimsel yeniden deneme (journal'lı sayaç) sunar ve /workflows/:name/run ucundan REST olarak sunulur.

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

Tek bir agent çağrısının ötesine geçen, birden çok adımdan oluşan iş akışlarını (ör. önce bir kayıt oluştur, sonra ödeme kontrolü yap, ardından onay bekleyip bildirim gönder) modellemek için kullanılır. Her adım journal'a ayrı ayrı kaydedilir; süreç ortasında proses çökerse veya bir adım (sleep/waitFor) askıya alınırsa, aynı runId ile tekrar çağrıldığında tamamlanmış adımlar yeniden ÇALIŞMAZ (replay), yalnızca kaldığı yerden devam eder.

Somut senaryo: bir sipariş akışında "ödeme onayı bekle" adımı saatler sürebilir. Süreç bu sırada kapansa bile, aynı runId ile tekrar çalıştırıldığında ödeme adımı hâlâ askıdaysa suspended döner; koşul sağlandığında adım gerçekten ilerler ve sonraki adımlara geçilir.

Kurulum / import#

Workflow'u tanımlamak için @gnldev/workflow paketindeki workflow() builder'ı kullanılır; çalıştırmak/kaydetmek için ise @gnldev/durable'ın createGnl registry'si gerekir (Workflow sınıfı, createGnl'ın beklediği WorkflowLike yapısal arayüzünü zaten karşılar — ayrı bir uyarlama gerekmez).

import
import { workflow, step, retry } from '@gnldev/workflow';
import { createGnl } from '@gnldev/durable';

Adım adım kullanım#

1) Workflow'u workflow() ve step(id, run) ile sıralı adımlar halinde tanımlayın:

workflow tanımı
const myWorkflow = workflow<{ email: string }>()
  .then(step('onboard:create-account', async (input) => {
    // ... create the account
    return { ...input, accountId: 'acc_1' };
  }))
  .then(step('onboard:send-welcome', async (input) => {
    // ... send the welcome email
    return { ...input, welcomed: true };
  }));

2) Workflow'u createGnl'ın workflows alanına isimle kaydedin:

kayıt
const config: CreateGnlConfig = {
  storage,
  agents: { /* ... */ },
  workflows: { onboard: myWorkflow },
};
const gnl = createGnl(config);

3) runWorkflow ile aynı runId'yi vererek çalıştırın — süreç kesilse bile aynı runId ile tekrar çağrı, tamamlanmış adımları atlayıp kaldığı yerden devam eder:

çalıştırma
const res = await gnl.runWorkflow('onboard', input, { runId: 'wf-1' });
// POST /workflows/onboard/run with the same runId -> resumes where it stopped

REST tarafında bu, @gnldev/server'ın otomatik ürettiği POST /workflows/:name/run ucu üzerinden aynı sözleşmeyle sunulur — gövdede { runId?, input } beklenir; verilen runId journal'da zaten bir iz taşıyorsa istek otomatik olarak bir resume kabul edilir (bütçe kapısı bu durumda atlanır, yalnız gerçekten yeni iş zorlanır). Kayıtlı workflow'ların adım listesi GET /workflows ile introspect edilebilir.

4) Bir adımı bildirimsel olarak retry'lamak için retry(step, policy) kullanın — attempts (ilk çalıştırma dahil toplam deneme), opsiyonel backoffMs (sabit ms ya da deneme indeksinden hesaplayan fonksiyon) ve tüm denemeler tükenince koşacak opsiyonel fallback adımı alır. Sarılmış adım drop-in'dir — aynı id'yi taşır, then/branch/parallel içinde sarılmamış bir adım gibi kullanılır:

retry ile bildirimsel yeniden deneme
import { retry, step } from '@gnldev/workflow';

const chargeStep = retry(
  step('order:charge', async (input) => chargeCard(input)),
  {
    attempts: 3,
    backoffMs: (attempt) => attempt * 500, // 500ms, 1000ms, ...
    fallback: step('order:charge-fallback', async (input) => ({ ...input, charged: false })),
  },
);

const orderWorkflow = workflow<{ orderId: string }>().then(chargeStep);

Deneme sayacı `${runId}:wf:${id}:attempts` anahtarıyla journal'a yazılır — crash-resume'da sayaç sıfırdan BAŞLAMAZ, "toplam N deneme" sözü process ölümlerinden bağımsız kalır. Önceki bir koşuda tükenmiş bir adım, resume'da doğrudan fallback'e (varsa) gider — yeniden N deneme yapılmaz. Askı (WorkflowSuspended, ör. sleep/waitFor) hata sayılmaz ve deneme tüketmeden aynen dışarı yayılır. fallback, adımın kendi anahtarında ayrıca journal'lanır — fallback koşup crash olursa resume'da yeniden çalışmaz.

API referansı#

fnworkflow

workflow<Input>() — bir workflow tanımını başlatır; .then(step) sıralı adımları zincirler.

fnstep

step(id, run) — journal'lanan tek bir adım. id, bir resume'un eşleştiği şeydir; yani bir etiket değil, sözleşmenin parçasıdır.

fnwaitForResume

Tipli insan-döngüde askı: workflow durur ve operatörün verdiği değerle runResumable({ resume }) üzerinden devam eder.

fncancelWorkflowRun

Dayanıklı iptal — karar journal'lanır, yani sonradan dönen bir worker da onu görür.

fnforkWorkflowRun

Bir workflow çalıştırmasını belirli bir adıma kadar yeni bir runId'ye kopyalar; forkRun'ın workflow düzeyindeki karşılığı.

fncreateGnl

Agent + workflow registry'sini kurar; dönen nesnenin runWorkflow/listWorkflows metodları workflow'ları çalıştırır/introspect eder.

typeWorkflowLike

createGnl'ın workflows alanına verilebilecek yapısal arayüz — build()/run()/opsiyonel runResumable(). @gnldev/workflow'un Workflow sınıfı bunu zaten karşılar.

typeWorkflowMeta

listWorkflows()'un döndürdüğü introspeksiyon şekli: { name, steps: { id, kind }[] }.

typeWorkflowRunResult

runWorkflow() sonucu: runId, output, suspended/paused bayrakları, stepId/reason ve adım başına çıktılar.

typeRunOptions

gnl.run()/stream() için çağrı seçenekleri (runId, prompt/messages, context, vb.) — runWorkflow ayrıca kendi { runId?, maxSteps? } seçeneğini kullanır.

fnretry

(step, policy: RetryPolicy) → Step. Bir adımı bildirimsel retry-policy ile sarar — drop-in (aynı id); deneme sayacı journal'da (crash-resume'da tükenmez), askı deneme tüketmez, fallback ayrı journaled.

typeRetryPolicy

{ attempts, backoffMs?: number | ((attempt) => number), fallback?: Step } — attempts ilk çalıştırma dahil toplam deneme sayısı.

classRetryExhaustedError

Tüm denemeler tükendi ve fallback verilmedi — { stepId, attempts, cause } taşır.

retry + crash-resume
Deneme sayacının journal'da olması, uzun-süren bir workflow'un ortasında proses çökse bile "toplam N deneme" garantisinin korunmasını sağlar: resume, kaldığı deneme sayısından devam eder, baştan saymaz. Retry edilen adımın kendisi yan-etkiliyse (ör. dış API çağrısı) idempotent olmalı ya da içeride bir durable tool kullanmalıdır — denemelerin İÇİ journal'lanmaz, yalnızca sayaç ve final çıktı journal'lanır.
İpucu
Adım fonksiyonlarında yalnızca input ve ctx parametrelerine güvenin; dış (closure) durum yerine journal'lanan çıktıya dayanın — aksi halde resume sırasında replay edilen adımlarla gerçek zamanlı yeniden çalışan adımlar arasında tutarsızlık oluşabilir.
Suspend adımları
sleep/waitFor içeren workflow'lar için run() yerine runResumable() gerekir — düz run() askıya alma sinyalini (WorkflowSuspended) fırlatıp hata olarak yükseltir. createGnl.runWorkflow, workflow'da runResumable varsa onu otomatik tercih eder.

İlgili sayfalar#