Przejdź do treści

Reference węzłów — wszystkie 18 typów

Wszystkie typy węzłów dostępnych w wizualnym edytorze. Każdy węzeł opisany w czterech sekcjach: Co robi, Wejście (pola w inspektorze), Wyjście (pola udostępniane następnym krokom), Najczęstsze błędy.

Jeśli czytasz to po raz pierwszy, zacznij od Builder — tutorial. Pełny wykład techniczny (kontrakty, retry, idempotencja, kompilacja do flow Windmill) — windmill/docs/support/nodes.md w repozytorium.

Krótko

  • 18 typów w czterech kategoriach: trigger (1), logic (3), AI (1), actions (13).
  • Trzy węzły są ukryte z paletySource (wstawiany automatycznie), Append puppy name (część szablonu imion psów), Parse personalization cards (j.w.).
  • Akcje wysyłające (send_reply, send_template, amazon_template_message) nie są ponawiane w razie błędu, żeby nie zdublować wiadomości.
  • Akcje BaseLinker + send_template + create_ticket mają auto-retry: 3 podejścia × 30s.
  • LLM extract ma retry: 2 × 10s.

Spis treści

Kategoria Węzły
Trigger Source
Logic Filter · Delay · Parse personalization cards
AI LLM extract
Actions — wysyłka i odpowiedzi Send reply · Send template · Amazon template message
Actions — zarządzanie zgłoszeniem Update status · Escalate · Mark email read · Create ticket
Actions — BaseLinker BaseLinker field · BaseLinker call · BaseLinker find by tag
Actions — eBay specials eBay dispute action · eBay return decide
Actions — Pupprint Append puppy name

Source

Kategoria: trigger · Ukryty z palety (wstawiany automatycznie jako korzeń) · Bez retry

Co robi: sprawdza, że zgłoszenie naprawdę pochodzi z wybranego źródła i (opcjonalnie) z konkretnego konta — to brama wejściowa scenariusza.

Wejście:

Pole Typ Co wpisujesz
Źródło select Jedno z: email, allegro, amazon, ebay, erli, joom, temu, woocommerce, manual.
Konto account-id Konkretne konto (z listy cs_email_accounts dla email, lub marketplace_accounts dla marketplace) lub puste = dowolne.

Wyjście: brak wartości udostępnianych następnym krokom (kolejne węzły czytają flow_input.issue / flow_input.event bezpośrednio).

Najczęstsze błędy:

  • Event issue_id mismatch — zgłoszenie i zdarzenie nie pasują do siebie. Bug w pobieraczu, zgłoś.
  • Source/account validation failed — zgłoszenie pochodzi z innego źródła niż wybrałeś. Sprawdź pasek triggera.

Filter

Kategoria: logic · Bez retry · Idempotentny

Co robi: porównuje pole z wartością i zwraca continue lub skip — albo cicho kończy scenariusz (raise_on_skip).

Wejście:

Pole Typ Co wpisujesz
Pole text Ścieżka do pola: issue.subject, event.type, text (cały rendering wątku), results.<id_węzła>.<pole>.
Operator select is / is_not / contains / not_contains / in / not_in / gt / gte / lt / lte / exists / not_exists / regex.
Wartość text Pojedyncza wartość do porównania.
Lista wartości string-list Dla in / not_in.
Uwzględniaj wielkość liter boolean Domyślnie false (porównanie case-insensitive).
Zgłoś sentinel przy skip boolean Gdy true, brak dopasowania kończy cały scenariusz cicho (status=filtered_out).
Powód skip text Komentarz wpisany do logu przebiegu.

Wyjście:

  • Dwa uchwyty na karcie: continue (gdy warunek pasuje) i skip (gdy nie pasuje).
  • route (string) — "continue" lub "skip". Można czytać przez {{results.<id>.route}} choć zwykle używa się dwóch krawędzi z karty.

Najczęstsze błędy:

  • Pole zwraca pustą wartość — operator exists zwróci false, is "cokolwiek" też. Użyj not_exists zamiast is "".
  • Filter nie odpala mimo pasującego tekstu — sprawdź case_sensitive. Domyślnie false, ale jeśli ustawisz true, WIELKIE LITERYwielkie litery.
  • Forward referencefield: results.X.Y gdzie X jest poniżej tego filtra. Convex odrzuci zapis: references unknown step lub not upstream.

Delay

Kategoria: logic · Bez retry

Co robi: czeka N sekund, zanim odpali kolejny węzeł. Maks. 3600s (1h).

Wejście:

Pole Typ Co wpisujesz
Sekundy number (1–3600) Czas oczekiwania.

Wyjście: brak.

Najczęstsze błędy: praktycznie żadne — jeśli wartość będzie zła, zostanie zaklamerowana do [0, 3600].

Typowe użycie: czekanie na propagację efektu zewnętrznego (np. po BaseLinker setOrderFields poczekać 30s, aż macro Baselinkera się wykona).


Parse personalization cards

Kategoria: logic · Ukryty z palety (część szablonu imion psów) · Bez retry · Idempotentny

Co robi: wycina z HTMLa wiadomości email pola <p id="...name"> i <p id="...sku"> (format używany przez maile-zgłoszenia z Allegro i Erli przy zamówieniach z personalizacją), patrzy do katalogu Convex i policzy, ile produktów w zamówieniu jest tagowanych jako personalizowane. To deterministyczny pre-pass, zanim LLM ruszy.

Wejście: brak (czyta issue.messages[*].html).

Wyjście:

Klucz Co znaczy
structured true, jeśli HTML miał rozpoznane <p id> — dane uszczegółowione.
expected_cards liczba kart do wydrukowania (suma per personalizowany SKU); null gdy brak struktury.
candidate_names lista imion-kandydatów wyciągniętych z <p id="...name">.
personalized_skus lista [{sku, count, name}] — produkty katalogowane jako personalizacja.
non_personalized_skus reszta SKU z zamówienia.
unknown_skus SKU, które nie pasują do żadnego produktu w katalogu Convex.

Najczęstsze błędy: węzeł nigdy nie rzuca wyjątku — zwraca structured=False przy braku struktury. Downstream LLM dostaje wtedy puste expected_cards i pracuje w trybie „jedno imię, brak walidacji ilości".


LLM extract

Kategoria: AI · Retry: 2 × 10s · Idempotencja: best-effort (model może zwrócić różne wyniki przy retry)

Co robi: wywołuje lokalny endpoint LLM (Qwen3 na hosth-aio:11434) z promptem i schematem JSON, zwraca pola + confidence (0/1/2).

Wejście:

Pole Typ Co wpisujesz
Prompt template textarea (wymagane) Treść promptu. Wspiera {{text}}, {{subject}}, {{customer_name}}, {{event_type}}, {{issue.subject}}, {{event.type}}, {{results.<id>.<pole>}}.
Pola do wyciągnięcia schema-fields (wymagane) Schemat JSON: lista pól z typami (string / number / boolean).
Jawny tekst wejściowy textarea Opcjonalny — jeśli puste, model dostaje subject + snippet + thread.
Model text Override modelu (puste = qwen3).
API base URL text Override endpointu LLM.
API key override text Klucz API (puste = brak / lokalny endpoint).
Temperature number Domyślnie 0 (deterministyczne).
Timeout (s) number Domyślnie 60.

Wyjście:

Klucz Co znaczy
fields obiekt z polami z extract_schema (np. {color: "czerwony"}).
confidence 0 / 1 / 2 (no_name / vague / certain).
route "no_name" / "vague" / "certain" — 1:1 z confidence, używane do routingu.

Trzy uchwyty wyjścia: certain, vague, no_name. Możesz wykorzystać do routowania (np. certain → wpisz do BaseLinkera, vague → eskalacja, no_name → odpowiedź z prośbą o uzupełnienie).

Najczęstsze błędy:

  • No text is available for extraction — pusty text i pusty rendering wątku. Sprawdź, czy wiadomość naprawdę dotarła.
  • LLM response did not contain a JSON object — model zwrócił coś poza JSON-em. Sprawdź prompt — czy jasno mówisz „odpowiedz JSON-em"?
  • LLM response field payload must be an object — model zwrócił fields ale nie jako obiekt. Doprecyzuj prompt.
  • Tymczasowe sieciowe błędy — auto-retry 2 × 10s. Po przekroczeniu — przebieg trafia do DLQ.

Send reply

Kategoria: action · Bez retry (z premedytacją, żeby nie zdublować wiadomości) · NIEidempotentny

Co robi: wysyła literalny tekst przez kanał źródłowy zgłoszenia (Allegro Message Center, eBay CORE messages, Email SMTP, …). Kanał wybierany automatycznie z issue.source.

Wejście:

Pole Typ Co wpisujesz
Treść wiadomości textarea (wymagane) Tekst odpowiedzi. Wspiera {{customer_name}}, {{subject}}, {{order_external_id}}, {{results.<id>.<pole>}}.

Wyjście:

Klucz Co znaczy
sent_at ISO timestamp wysłania.
message_id Identyfikator wiadomości w kanale (np. SMTP Message-ID, Allegro message id).

Najczęstsze błędy:

  • body is empty — pole body puste lub same białe znaki.
  • UnsupportedSourceError — kanał nieobsługiwany (np. manual, lub source nie ma jeszcze wsparcia channel-helpera).
  • SMTP error — konto email odmówiło. Sprawdź hasło aplikacji w Konfiguracji.

Brak retry — nie ponawia w razie błędu

Jeśli send_reply padnie, przebieg trafia do DLQ — operator musi zadecydować, czy ponowić ręcznie. Powód: retry oznaczałby drugą wiadomość do klienta.


Send template

Kategoria: action · Retry: 3 × 30s · NIEidempotentny

Co robi: ładuje szablon wiadomości z Convex (cs_message_templates._id) i wysyła go przez kanał źródłowy. Tak jak send_reply, ale tekst pochodzi z biblioteki szablonów + zmienne.

Wejście:

Pole Typ Co wpisujesz
Template id text (wymagane) Convex id z cs_message_templates.
Zmienne template key-value-list Dodatkowe zmienne do szablonu. Wartości mogą być placeholderami {{results.<id>.<pole>}}.

Wyjście:

Klucz Co znaczy
sent_at, message_id Jak send_reply (nie zadeklarowane formalnie w schemacie).

Najczęstsze błędy:

  • Template variable '{{X}}' is missing — szablon używa {{X}}, ale brak go w kontekście. Dodaj wpis do Zmienne template.
  • Template not found — id nie istnieje w cs_message_templates.

Amazon template message

Kategoria: action · Bez retry · NIEidempotentny · Tylko Amazon

Co robi: wysyła wiadomość do kupującego Amazon przez SP-API Messaging v1. Amazon nie ma free-form messagingu — tylko 9 zamkniętych szablonów (patrz Source setup → Amazon).

Wejście:

Pole Typ Co wpisujesz
Szablon Amazon select (wymagane) Jeden z 9 szablonów. Najbliższy free-form: unexpectedProblem.
Treść wiadomości textarea (wymagane) Tekst — może zawierać placeholdery {{customer_name}}, {{order_external_id}}.
Zmienne szablonu (opcjonalne) key-value-list Dodatkowe zmienne.

Wyjście: order_id (z payloadu Amazon).

Najczęstsze błędy:

  • amazon_template_message is Amazon-onlyissue.source != "amazon". Dodaj filtr źródła wcześniej.
  • Amazon SP-API rejected — np. konto sprzedawcy nie ma pozwolenia na messaging dla danego rynku.

Update status

Kategoria: action · Bez retry · Idempotentny

Co robi: zmienia cs_issues.status zgłoszenia.

Wejście:

Pole Typ Co wpisujesz
Nowy status select (wymagane) new, classified, action_suggested, in_progress, resolved, escalated.

Wyjście: previous + current (oba statusy).

Najczęstsze błędy: praktycznie żadne — Convex zaakceptuje każdy z 6 statusów.


Escalate

Kategoria: action · Bez retry · NIEidempotentny (każde wywołanie tworzy nową notatkę)

Co robi: dodaje notatkę eskalacji do zgłoszenia + zmienia status na escalated. Notatka jest widoczna w wątku CS jako wpis systemowy.

Wejście:

Pole Typ Co wpisujesz
Powód eskalacji textarea (wymagane) Tekst widoczny operatorowi.
Assignee text Opcjonalny — kto ma się zająć.

Wyjście: note_id.

Najczęstsze błędy:

  • reason is required — pole reason puste.

Eskalacja jako default w gałęziach

Eskalacja to często „bezpieczny default" — gdy LLM ma niskie confidence, gdy filter zwraca skip, gdy capability eBay nie pozwala działać automatycznie. Operator widzi notatkę w wątku i wie, że scenariusz świadomie odpuścił.


Mark email read

Kategoria: action · Bez retry · Idempotentny

Co robi: flaguje wiadomość Gmail jako przeczytaną (\Seen przez IMAP). Tylko dla zgłoszeń z source = email — dla innych źródeł cicho pomija.

Wejście: brak.

Wyjście: marked (liczba flagowanych UID), host, opcjonalnie skipped: true, reason.

Najczęstsze błędy: węzeł połyka błędy IMAP jako skipped zamiast je rzucać — przebieg się nie zatrzyma. Jeśli widzisz w logu imap error: …, sprawdź, czy hasło aplikacji nie wygasło.

Typowe użycie: ostatni krok scenariusza email — operator może traktować skrzynkę Gmail jako kolejkę („wszystko nieprzeczytane = jeszcze nie obsłużone scenariuszem").


Create ticket

Kategoria: action · Retry: 3 × 30s · NIEidempotentny (każde wywołanie tworzy nowy issue Forgejo)

Co robi: otwiera issue w Forgejo (git.aiofactory.pl) z tytułem, treścią i listą labelek. Używane, gdy zgłoszenie wymaga interwencji deweloperskiej.

Wejście:

Pole Typ Co wpisujesz
Tytuł text (wymagane) Wspiera placeholdery {{subject}}, {{customer_name}}, {{issue_id}}.
Opis textarea (wymagane) Pełna treść issue.
Labelki string-list Lista nazw labelek Forgejo (np. ["bug", "needs-triage"]).

Wyjście: ticket_url, ticket_id.

Najczęstsze błędy:

  • Template variable missing — placeholder {{X}} w tytule/opisie nie ma wartości.
  • Forgejo HTTP 401 — token Forgejo wygasł / brak uprawnień.

BaseLinker field

Kategoria: action · Retry: 3 × 30s · Idempotentny (overwrite)

Co robi: zapisuje wartość do dodatkowego pola zamówienia BaseLinker (extra_field_1 / extra_field_2 lub custom field id).

Wejście:

Pole Typ Co wpisujesz
Order external id text Puste = issue.order_external_id (z fallbackiem).
Field id text (wymagane) extra_field_1, extra_field_2, lub custom additional-field id z BaseLinkera.
Wartość text (wymagane) Co wpisać. Wspiera pełen kontekst placeholderów.

Wyjście: brak udostępnianych formalnie. Skrypt zwraca bl_response (raw odpowiedź BL).

Najczęstsze błędy:

  • No order_external_id could be resolved — zgłoszenie nie ma order_external_id ani podanego override'u.
  • MissingCredentialError (BaseLinker token) — brak aktywnego konta BaseLinker.

Typowe użycie: zapis koloru / imienia psa do dedykowanego pola BL — patrz szablon kolorów.


BaseLinker call

Kategoria: action · Retry: 3 × 30s · Idempotencja: zależy od metody

Co robi: generyczny węzeł — wywołaj dowolną metodę API BaseLinker z parametrami. Używane, gdy specjalizowane węzły (baselinker_field, baselinker_find_order_product_by_tag) nie wystarczają.

Wejście:

Pole Typ Co wpisujesz
Metoda BL select (wymagane) getOrders, setOrderStatus, setOrderFields, runOrderMacroTrigger, deleteOrderProduct, addOrderProduct, addInvoice, getJournalList.
Parametry key-value-list Klucz → wartość. Wartości mogą być {{results.<id>.<pole>}}.

Wyjście: result — surowa odpowiedź BaseLinkera. Czytasz przez {{results.<id>.result.<pole>}}.

Najczęstsze błędy:

  • BaseLinker method is required — pole method puste.
  • BaseLinker error — np. setOrderStatus z nieistniejącym status_id.

Idempotencja zależy od metody

getOrders / getJournalList to read-only — bezpieczne. setOrderStatus / setOrderFields to overwrite — ponowienie nadpisze tym samym, OK. addOrderProduct / addInvoice to insert — retry tworzy duplikaty! Dla tych metod ustaw Bez retry w paśmie tytułu (lub po prostu unikaj retry).


BaseLinker find by tag

Kategoria: action · Retry: 3 × 30s · Idempotentny (read-only)

Co robi: pobiera zamówienie BaseLinker po external_order_id i znajduje pierwszą linię, której produkt w katalogu Convex ma podany tag (np. ZZ - AI - PodajKolor). Używane przed deleteOrderProduct lub setOrderProductFields.

Wejście:

Pole Typ Co wpisujesz
Tag produktu text (wymagane) Dokładny match (case-sensitive) — np. ZZ - AI - PodajKolor.
Order external id text Puste = issue.order_external_id.
Pomiń scenariusz gdy brak tagu boolean Gdy true, brak linii z tagiem kończy scenariusz cicho (status=filtered_out).

Wyjście:

Klucz Co znaczy
found bool — czy znaleziono linię.
order_product_id id linii zamówienia (puste przy found=false).
bl_order_id wewnętrzne BL order_id.
product_id BL catalog product_id.
sku, name SKU i nazwa linii.

Najczęstsze błędy:

  • tag is required — pole tag puste.
  • no_order_external_id — zgłoszenie nie ma order_external_id ani override'u.

eBay dispute action

Kategoria: action · Bez retry · NIEidempotentny (eBay odrzuci drugą próbę) · Tylko eBay payment_dispute

Co robi: wykonuje akcję na payment dispute eBay (Sell Payment Dispute API). Trzy akcje: accept (akceptujesz spór, zwracasz pieniądze), contest (kwestionujesz spór), add_evidence (dodajesz dowód do trwającego sporu).

Wejście:

Pole Typ Co wpisujesz
Akcja select (wymagane) accept / contest / add_evidence.
Contest reason select (Wymagane dla contest) Powód: SELLER_PROVIDES_PROOF_OF_DELIVERY / …
Evidence / opis textarea Tekst dowodu / opisu.
Evidence file URL text URL pliku do załączenia (opcjonalny, dla add_evidence).
Evidence type select Typ dowodu: PROOF_OF_DELIVERY / PROOF_OF_IDENTITY / SIGNATURE_CONFIRMATION / TRACKING / OTHER.

Wyjście: dispute_id, revision (z odpowiedzi eBay).

Najczęstsze błędy:

  • amazon_dispute_action is eBay-only — zgłoszenie nie z eBay.
  • capability gate failed — pobieracz nie ustawił capabilities.can_X na true. Sprawdź czy zgłoszenie ma flagę.
  • Already accepted/contested — drugie wywołanie tej samej akcji.

eBay return decide

Kategoria: action · Bez retry · NIEidempotentny · Tylko eBay return

Co robi: decyduje o zwrocie eBay (Post-Order v2 decide). Cztery decyzje: ACCEPT_RETURN, DECLINE_RETURN, OFFER_PARTIAL_REFUND, PROVIDE_RMA.

Wejście:

Pole Typ Co wpisujesz
Decyzja select (wymagane) Jedna z 4 wymienionych.
Wiadomość (opcjonalna) textarea Komentarz dla kupującego.
Kwota partial refund number Wymagana dla OFFER_PARTIAL_REFUND.
Waluta partial refund select EUR / USD / GBP / PLN.

Wyjście: return_id + raw response.

Najczęstsze błędy:

  • capability gate failedcapabilities.can_decide = false.
  • eBay rejected decision — np. zwrot już zamknięty.

Append puppy name

Kategoria: action · Ukryty z palety (część szablonu imion psów) · Bez retry · NIEidempotentny

Co robi: dla zamówienia z personalizowanymi naklejkami: rekoncyliuje tablicę imion (z LLM) z liczbą oczekiwanych kart (z parse_personalization_cards) i zapisuje jeden wpis puppy_names per kartę. Cycle-padding gdy klient podał mniej imion niż produktów.

Wejście:

Pole Typ Co wpisujesz
Tablica imion text (wymagane) Zwykle {{results.llm_dog_name.fields.names}}.
Oczekiwana liczba kart text Zwykle {{results.parse_cards.expected_cards}} (puste = tryb unstructured, jedna karta na imię).
Rozmiar karty text (wymagane) big lub small. Może być {{results.llm_dog_name.fields.size}}.

Wyjście: count (ile kart utworzono), created (lista id), expected_cards, reconciled_names.

Najczęstsze błędy:

  • zero names — LLM nie wyciągnął żadnego imienia.
  • more names than cards — operator widzi błąd; albo prompt LLM zwraca za dużo, albo expected_cards jest źle policzony.
  • invalid sizesize to nie big ani small.

Najczęstsze błędy walidacji przy zapisie scenariusza

Niezależnie od węzła, te błędy widzisz przy Zapisz:

Komunikat Przyczyna Rozwiązanie
must have exactly one root node Graf ma >1 węzeł bez wejścia, albo żaden Wstaw / usuń, aby był dokładnie jeden korzeń (Source).
contains a cycle at node '<id>' Krawędź wraca do węzła wyżej Usuń krawędź zwrotną.
merges are not supported (node '<id>' has multiple inputs) Dwie krawędzie zbiegają się w jeden węzeł Skopiuj końcowy węzeł — jeden per gałąź.
contains disconnected nodes: <id> Węzeł nie jest osiągalny z korzenia Połącz go krawędzią lub usuń.
references unknown step '<id>' Placeholder odwołuje się do węzła, którego już nie ma Popraw placeholder — wybierz z panelu Upstream outputs.
references '<id>' which is not upstream Placeholder wskazuje węzeł poniżej tego Reorganizuj graf — referencje tylko w górę.
exposes no data output '<key>' Klucz nie jest w dataOutputs upstream node-a Sprawdź dostępne klucze w panelu Upstream outputs.
needs labeled outputs to preserve branch routing Filter / LLM extract z >1 wyjściem nie ma labelek na krawędziach Przeciągnij krawędzie z konkretnych uchwytów (np. continue / skip).

Cross-linki


Dane techniczne

  • Katalog typów (źródło prawdy): convex/convex/support/nodeCatalog.ts (pola, etykiety, dataOutputs).
  • Implementacje runtime: windmill/f/support/nodes/<typ>.py — jeden plik per typ.
  • Kompilator graf → flow Windmill: convex/convex/support/flow_sync.ts.
  • Pełna referencja techniczna (z retry-policies, idempotencją, error modes): windmill/docs/support/nodes.md.