feat(flows): frontend for the dedup node — palette, key config, i18n ×14 (#5264)

This commit is contained in:
Cyrus Gray
2026-07-29 21:52:29 +05:30
committed by GitHub
parent d125b857ff
commit 713751fdcd
21 changed files with 175 additions and 8 deletions
@@ -0,0 +1,37 @@
/**
* Behavior tests for the `dedup` node config form (issue #5263). Covers the
* single `key` field: it renders and emits a `config.key` patch as the
* `=`-bindable expression is typed. `useT()` falls back to the bundled
* English map with no provider mounted (same convention as the sibling
* `memoryFields.test.tsx`).
*/
import { fireEvent, render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import { DedupForm } from '../dedupFields';
function renderDedupForm(config: Record<string, unknown> = {}) {
const onChange = vi.fn();
render(<DedupForm config={config} onChange={onChange} />);
return { onChange };
}
describe('DedupForm', () => {
it('renders the key field', () => {
renderDedupForm();
expect(screen.getByTestId('node-config-dedup-key')).toBeInTheDocument();
});
it('seeds the key field from the existing config', () => {
renderDedupForm({ key: '=item.id' });
expect(screen.getByTestId('node-config-dedup-key')).toHaveValue('=item.id');
});
it('emits a key patch as the expression is typed', () => {
const { onChange } = renderDedupForm();
fireEvent.change(screen.getByTestId('node-config-dedup-key'), {
target: { value: '=item.id' },
});
expect(onChange).toHaveBeenLastCalledWith({ key: '=item.id' });
});
});
@@ -0,0 +1,44 @@
/**
* `dedup` node config form (issue #5263 — 14th tinyflows `NodeKind`). Skips
* items already seen, keyed by a stable per-item `=`-expression evaluated
* against each item (e.g. `=item.id`) — the engine tracks which keys have
* already passed through this node and drops repeats.
*
* Field keys mirror what the engine actually reads at runtime:
* - `key` — `=`-bindable, required. The only config field this node has.
*/
import { useT } from '../../../../lib/i18n/I18nContext';
import { configString, ExpressionField } from './nodeConfigFields';
import type { UpstreamExpressionOption } from './upstreamOptions';
/**
* Deliberately narrower than `NodeConfigFormProps` (`nodeConfigForms.tsx`) —
* this form needs no `connections` (dedup nodes don't use the credential
* picker), so it isn't imported here just to keep the shape identical. A
* component typed against this subset is still assignable into
* `NODE_CONFIG_FORMS`'s `NodeConfigForm` slot (the fuller prop type has
* every property this one requires).
*/
export interface DedupFormProps {
config: Record<string, unknown>;
onChange: (patch: Record<string, unknown>) => void;
upstreamOptions?: UpstreamExpressionOption[];
}
export function DedupForm({ config, onChange, upstreamOptions }: DedupFormProps) {
const { t } = useT();
return (
<div className="space-y-3">
<ExpressionField
label={t('flows.nodeConfig.dedup.keyLabel')}
hint={t('flows.nodeConfig.dedup.keyHint')}
value={configString(config, 'key')}
onChange={v => onChange({ key: v })}
placeholder="=item.id"
upstreamOptions={upstreamOptions}
testId="node-config-dedup-key"
/>
</div>
);
}
@@ -16,6 +16,7 @@
* - `trigger` → `trigger_kind` + kind-specific (`schedule`, `toolkit`/`trigger_slug`)
* - `memory` → `operation` + operation-specific (`scope`/`query`/`flavour`/`key`/`value`/
* `limit`/`min_score`), see `memoryFields.tsx`
* - `dedup` → `key` (an `=`-bindable per-item id expression), see `dedupFields.tsx`
*/
import createDebug from 'debug';
import { useEffect, useState } from 'react';
@@ -32,6 +33,7 @@ import {
ComposioTriggerField,
fetchActionSchema,
} from './composioFields';
import { DedupForm } from './dedupFields';
import { MemoryForm } from './memoryFields';
import { NATIVE_TOOL_PREFIX, NativeToolField } from './nativeToolFields';
import {
@@ -496,4 +498,5 @@ export const NODE_CONFIG_FORMS: Partial<Record<NodeKind, NodeConfigForm>> = {
transform: TransformForm,
code: CodeForm,
memory: MemoryForm,
dedup: DedupForm,
};
+15 -6
View File
@@ -1,5 +1,5 @@
/**
* Per-kind visual metadata for the 13 tinyflows `NodeKind`s, shared by the
* Per-kind visual metadata for the 14 tinyflows `NodeKind`s, shared by the
* canvas node renderer (`FlowNodeComponent`) and the editable canvas's node
* palette (`NodePalette`). Kept dependency-free (no React) so both a rendered
* `<Handle>`-bearing card and a plain palette button can pull the same
@@ -8,7 +8,7 @@
* Colors cycle through the four CSS-variable-backed semantic ramps
* (primary/sage/amber/coral) that support Tailwind's `/opacity` modifiers in
* this codebase (see `tailwind.config.js`) so light/dark theming comes for
* free; with 13 kinds and 4 ramps some kinds share a color family — the emoji
* free; with 14 kinds and 4 ramps some kinds share a color family — the emoji
* + name remain the primary distinguishers.
*/
import type { NodeKind } from './types';
@@ -29,12 +29,16 @@ interface NodeKindMeta {
}
/**
* The 13 `NodeKind`s in the order they should appear in the palette. Trigger
* The 14 `NodeKind`s in the order they should appear in the palette. Trigger
* leads (every graph needs exactly one); the rest follow the logical grouping
* of the `tinyflows::model::NodeKind` enum. `memory` (issue #5226) is
* appended last — the design doc (`08-memory-node.md`) sequences it as the
* 13th kind deliberately, so it trails `sub_workflow` here too rather than
* being interleaved with the other `actions`-group kinds.
* appended after `sub_workflow` — the design doc (`08-memory-node.md`)
* sequences it as the 13th kind deliberately, so it trails `sub_workflow`
* here too rather than being interleaved with the other `actions`-group
* kinds. `dedup` (issue #5263) is the 14th kind and is appended last in turn
* — it renders in the `logic` group (alongside `condition`/`split_out`/
* `merge`) regardless of its position here, since {@link PALETTE_ENTRIES_BY_GROUP}
* filters by group rather than relying on interleaved array order.
*/
const NODE_KINDS: NodeKind[] = [
'trigger',
@@ -50,6 +54,7 @@ const NODE_KINDS: NodeKind[] = [
'output_parser',
'sub_workflow',
'memory',
'dedup',
];
/** Per-kind emoji + border/chip color + palette group. See the module doc. */
@@ -70,6 +75,10 @@ const NODE_KIND_META: Record<NodeKind, NodeKindMeta> = {
split_out: { emoji: '📤', color: 'sage', group: 'logic' },
transform: { emoji: '♻️', color: 'primary', group: 'logic' },
output_parser: { emoji: '📋', color: 'amber', group: 'logic' },
// Skips items already seen, keyed by a stable per-item `=`-expression — a
// "logic" node like its neighbours (`condition`/`split_out`/`merge`): it
// routes/filters the item stream rather than calling out or reading state.
dedup: { emoji: '🪪', color: 'coral', group: 'logic' },
};
/** Palette group render order. */
+5
View File
@@ -72,4 +72,9 @@ describe('describeNode', () => {
);
expect(describeNode('memory', { operation: 'search' })).toBe('Searches memory');
});
it('describes dedup by its key expression, and falls back generically when unset', () => {
expect(describeNode('dedup', { key: '=item.id' })).toBe('Skips items already seen by =item.id');
expect(describeNode('dedup', {})).toBe('Skips items already processed');
});
});
+6
View File
@@ -104,6 +104,12 @@ export function describeNode(
// recall
return `Recalls memory${scoped}`;
}
case 'dedup': {
const key = str(config, 'key');
return key
? `Skips items already seen by ${truncate(key, 40)}`
: 'Skips items already processed';
}
default:
return '';
}
+8 -2
View File
@@ -36,7 +36,7 @@ export interface Position {
}
/**
* The 13 node kinds `tinyflows` currently defines (`tinyflows::model::NodeKind`).
* The 14 node kinds `tinyflows` currently defines (`tinyflows::model::NodeKind`).
* Wire values are `snake_case` (`#[serde(rename_all = "snake_case")]`).
*
* `memory` (issue #5226) is the 13th kind — declarative, in-graph memory
@@ -48,6 +48,11 @@ export interface Position {
* (`nodeConfig/memoryFields.tsx`) reads/writes the known keys
* (`operation`, `scope`, `query`, `flavour`, `key`, `value`, `limit`,
* `min_score`) directly off that bag, same as `condition`/`switch`/etc.
*
* `dedup` (issue #5263) is the 14th kind — skips items already seen, keyed
* by a stable per-item `=`-expression (e.g. `=item.id`). Same free-form
* config bag pattern: its one field, `key`, is read/written directly by
* `nodeConfig/dedupFields.tsx`.
*/
export type NodeKind =
| 'trigger'
@@ -62,7 +67,8 @@ export type NodeKind =
| 'transform'
| 'output_parser'
| 'sub_workflow'
| 'memory';
| 'memory'
| 'dedup';
/**
* A named connection point on a node. Mirrors `tinyflows::model::Port`.
+4
View File
@@ -4093,6 +4093,7 @@ const messages: TranslationMap = {
'flows.nodeKind.output_parser': 'محلل المخرجات',
'flows.nodeKind.sub_workflow': 'سير عمل فرعي',
'flows.nodeKind.memory': 'الذاكرة',
'flows.nodeKind.dedup': 'إزالة التكرار',
'flows.palette.title': 'العقد',
'flows.palette.addNode': 'إضافة عقدة {kind}',
'flows.editor.save': 'حفظ',
@@ -4258,6 +4259,9 @@ const messages: TranslationMap = {
'flows.nodeConfig.memory.limitHint': 'أقصى عدد من النتائج.',
'flows.nodeConfig.memory.minScoreLabel': 'الحد الأدنى للنتيجة',
'flows.nodeConfig.memory.minScoreHint': 'عتبة الصلة من 0 إلى 1.',
'flows.nodeConfig.dedup.keyLabel': 'المفتاح',
'flows.nodeConfig.dedup.keyHint':
'تعبير معرف ثابت لكل عنصر، مثل =item.id. يتم تخطي العناصر التي شوهد مفتاحها من قبل.',
'flows.enableApproval.title': 'هل تسمح لسير العمل هذا بالتنفيذ؟',
'flows.enableApproval.intro':
+4
View File
@@ -4195,6 +4195,7 @@ const messages: TranslationMap = {
'flows.nodeKind.output_parser': 'আউটপুট পার্সার',
'flows.nodeKind.sub_workflow': 'সাব-ওয়ার্কফ্লো',
'flows.nodeKind.memory': 'মেমরি',
'flows.nodeKind.dedup': 'ডুপ্লিকেট অপসারণ',
'flows.palette.title': 'নোড',
'flows.palette.addNode': '{kind} নোড যোগ করুন',
'flows.editor.save': 'সংরক্ষণ করুন',
@@ -4365,6 +4366,9 @@ const messages: TranslationMap = {
'flows.nodeConfig.memory.limitHint': 'সর্বোচ্চ ফলাফল সংখ্যা।',
'flows.nodeConfig.memory.minScoreLabel': 'সর্বনিম্ন স্কোর',
'flows.nodeConfig.memory.minScoreHint': '০ থেকে ১ পর্যন্ত প্রাসঙ্গিকতার থ্রেশহোল্ড।',
'flows.nodeConfig.dedup.keyLabel': 'কী',
'flows.nodeConfig.dedup.keyHint':
'প্রতিটি আইটেমের জন্য একটি স্থিতিশীল আইডি এক্সপ্রেশন, যেমন =item.id। যেসব আইটেমের কী আগে দেখা গেছে সেগুলো এড়িয়ে যাওয়া হয়।',
'flows.enableApproval.title': 'এই ওয়ার্কফ্লোকে কাজ করার অনুমতি দেবেন?',
'flows.enableApproval.intro':
+4
View File
@@ -4313,6 +4313,7 @@ const messages: TranslationMap = {
'flows.nodeKind.output_parser': 'Ausgabe-Parser',
'flows.nodeKind.sub_workflow': 'Unter-Workflow',
'flows.nodeKind.memory': 'Gedächtnis',
'flows.nodeKind.dedup': 'Deduplizierung',
'flows.palette.title': 'Knoten',
'flows.palette.addNode': '{kind}-Knoten hinzufügen',
'flows.editor.save': 'Speichern',
@@ -4488,6 +4489,9 @@ const messages: TranslationMap = {
'flows.nodeConfig.memory.limitHint': 'Maximale Anzahl an Ergebnissen.',
'flows.nodeConfig.memory.minScoreLabel': 'Mindestbewertung',
'flows.nodeConfig.memory.minScoreHint': 'Relevanzschwelle von 0 bis 1.',
'flows.nodeConfig.dedup.keyLabel': 'Schlüssel',
'flows.nodeConfig.dedup.keyHint':
'Ein stabiler Id-Ausdruck pro Element, z. B. =item.id. Elemente mit einem bereits gesehenen Schlüssel werden übersprungen.',
'flows.enableApproval.title': 'Darf dieser Workflow Aktionen ausführen?',
'flows.enableApproval.intro':
+5
View File
@@ -4767,6 +4767,7 @@ const en: TranslationMap = {
'flows.nodeKind.output_parser': 'Output parser',
'flows.nodeKind.sub_workflow': 'Sub-workflow',
'flows.nodeKind.memory': 'Memory',
'flows.nodeKind.dedup': 'Dedup',
// ── Editable Workflow Canvas (issue B5b.2 / Phase 3a): the node palette
// and editor toolbar layered on top of the read-only canvas above.
@@ -4950,6 +4951,10 @@ const en: TranslationMap = {
'flows.nodeConfig.memory.limitHint': 'Maximum number of results.',
'flows.nodeConfig.memory.minScoreLabel': 'Minimum score',
'flows.nodeConfig.memory.minScoreHint': 'Relevance threshold from 0 to 1.',
// `dedup` node (issue #5263): skips items already seen by a stable per-item key.
'flows.nodeConfig.dedup.keyLabel': 'Key',
'flows.nodeConfig.dedup.keyHint':
'A stable per-item id expression, e.g. =item.id. Items with a key already seen are skipped.',
// Phase 4a "New workflow" chooser + Phase 4c templates gallery. The chooser
// offers scratch / template / describe; the gallery lists the curated
+4
View File
@@ -4264,6 +4264,7 @@ const messages: TranslationMap = {
'flows.nodeKind.output_parser': 'Analizador de salida',
'flows.nodeKind.sub_workflow': 'Subflujo de trabajo',
'flows.nodeKind.memory': 'Memoria',
'flows.nodeKind.dedup': 'Deduplicación',
'flows.palette.title': 'Nodos',
'flows.palette.addNode': 'Añadir nodo {kind}',
'flows.editor.save': 'Guardar',
@@ -4438,6 +4439,9 @@ const messages: TranslationMap = {
'flows.nodeConfig.memory.limitHint': 'Número máximo de resultados.',
'flows.nodeConfig.memory.minScoreLabel': 'Puntuación mínima',
'flows.nodeConfig.memory.minScoreHint': 'Umbral de relevancia de 0 a 1.',
'flows.nodeConfig.dedup.keyLabel': 'Clave',
'flows.nodeConfig.dedup.keyHint':
'Una expresión de id estable por elemento, p. ej. =item.id. Los elementos con una clave ya vista se omiten.',
'flows.enableApproval.title': '¿Permitir que este flujo de trabajo actúe?',
'flows.enableApproval.intro':
+4
View File
@@ -4292,6 +4292,7 @@ const messages: TranslationMap = {
'flows.nodeKind.output_parser': 'Analyseur de sortie',
'flows.nodeKind.sub_workflow': 'Sous-workflow',
'flows.nodeKind.memory': 'Mémoire',
'flows.nodeKind.dedup': 'Déduplication',
'flows.palette.title': 'Nœuds',
'flows.palette.addNode': 'Ajouter un nœud {kind}',
'flows.editor.save': 'Enregistrer',
@@ -4468,6 +4469,9 @@ const messages: TranslationMap = {
'flows.nodeConfig.memory.limitHint': 'Nombre maximal de résultats.',
'flows.nodeConfig.memory.minScoreLabel': 'Score minimal',
'flows.nodeConfig.memory.minScoreHint': 'Seuil de pertinence de 0 à 1.',
'flows.nodeConfig.dedup.keyLabel': 'Clé',
'flows.nodeConfig.dedup.keyHint':
"Une expression d'identifiant stable par élément, ex. =item.id. Les éléments dont la clé a déjà été vue sont ignorés.",
'flows.enableApproval.title': 'Autoriser ce workflow à agir ?',
'flows.enableApproval.intro':
+4
View File
@@ -4192,6 +4192,7 @@ const messages: TranslationMap = {
'flows.nodeKind.output_parser': 'आउटपुट पार्सर',
'flows.nodeKind.sub_workflow': 'सब-वर्कफ़्लो',
'flows.nodeKind.memory': 'मेमोरी',
'flows.nodeKind.dedup': 'डुप्लिकेट हटाएं',
'flows.palette.title': 'नोड',
'flows.palette.addNode': '{kind} नोड जोड़ें',
'flows.editor.save': 'सहेजें',
@@ -4362,6 +4363,9 @@ const messages: TranslationMap = {
'flows.nodeConfig.memory.limitHint': 'अधिकतम परिणामों की संख्या।',
'flows.nodeConfig.memory.minScoreLabel': 'न्यूनतम स्कोर',
'flows.nodeConfig.memory.minScoreHint': '0 से 1 तक की प्रासंगिकता सीमा।',
'flows.nodeConfig.dedup.keyLabel': 'कुंजी',
'flows.nodeConfig.dedup.keyHint':
'प्रति आइटम एक स्थिर आईडी एक्सप्रेशन, जैसे =item.id। जिन आइटम की कुंजी पहले देखी जा चुकी है उन्हें छोड़ दिया जाता है।',
'flows.enableApproval.title': 'क्या इस वर्कफ़्लो को काम करने की अनुमति दें?',
'flows.enableApproval.intro':
+4
View File
@@ -4208,6 +4208,7 @@ const messages: TranslationMap = {
'flows.nodeKind.output_parser': 'Pengurai keluaran',
'flows.nodeKind.sub_workflow': 'Sub-alur kerja',
'flows.nodeKind.memory': 'Memori',
'flows.nodeKind.dedup': 'Hapus duplikat',
'flows.palette.title': 'Simpul',
'flows.palette.addNode': 'Tambah simpul {kind}',
'flows.editor.save': 'Simpan',
@@ -4380,6 +4381,9 @@ const messages: TranslationMap = {
'flows.nodeConfig.memory.limitHint': 'Jumlah maksimum hasil.',
'flows.nodeConfig.memory.minScoreLabel': 'Skor minimum',
'flows.nodeConfig.memory.minScoreHint': 'Ambang batas relevansi dari 0 hingga 1.',
'flows.nodeConfig.dedup.keyLabel': 'Kunci',
'flows.nodeConfig.dedup.keyHint':
'Ekspresi id stabil per item, mis. =item.id. Item dengan kunci yang sudah pernah dilihat akan dilewati.',
'flows.enableApproval.title': 'Izinkan alur kerja ini bertindak?',
'flows.enableApproval.intro':
+4
View File
@@ -4260,6 +4260,7 @@ const messages: TranslationMap = {
'flows.nodeKind.output_parser': 'Analizzatore di output',
'flows.nodeKind.sub_workflow': 'Sotto-flusso di lavoro',
'flows.nodeKind.memory': 'Memoria',
'flows.nodeKind.dedup': 'Deduplicazione',
'flows.palette.title': 'Nodi',
'flows.palette.addNode': 'Aggiungi nodo {kind}',
'flows.editor.save': 'Salva',
@@ -4436,6 +4437,9 @@ const messages: TranslationMap = {
'flows.nodeConfig.memory.limitHint': 'Numero massimo di risultati.',
'flows.nodeConfig.memory.minScoreLabel': 'Punteggio minimo',
'flows.nodeConfig.memory.minScoreHint': 'Soglia di rilevanza da 0 a 1.',
'flows.nodeConfig.dedup.keyLabel': 'Chiave',
'flows.nodeConfig.dedup.keyHint':
"Un'espressione id stabile per elemento, es. =item.id. Gli elementi con una chiave già vista vengono ignorati.",
'flows.enableApproval.title': 'Consentire a questo workflow di agire?',
'flows.enableApproval.intro':
+4
View File
@@ -4145,6 +4145,7 @@ const messages: TranslationMap = {
'flows.nodeKind.output_parser': '출력 파서',
'flows.nodeKind.sub_workflow': '하위 워크플로',
'flows.nodeKind.memory': '메모리',
'flows.nodeKind.dedup': '중복 제거',
'flows.palette.title': '노드',
'flows.palette.addNode': '{kind} 노드 추가',
'flows.editor.save': '저장',
@@ -4313,6 +4314,9 @@ const messages: TranslationMap = {
'flows.nodeConfig.memory.limitHint': '최대 결과 수입니다.',
'flows.nodeConfig.memory.minScoreLabel': '최소 점수',
'flows.nodeConfig.memory.minScoreHint': '0에서 1 사이의 관련성 임곗값입니다.',
'flows.nodeConfig.dedup.keyLabel': '키',
'flows.nodeConfig.dedup.keyHint':
'항목별 안정적인 id 표현식입니다. 예: =item.id. 이미 본 키를 가진 항목은 건너뜁니다.',
'flows.enableApproval.title': '이 워크플로가 작업을 수행하도록 허용할까요?',
'flows.enableApproval.intro':
+4
View File
@@ -4249,6 +4249,7 @@ const messages: TranslationMap = {
'flows.nodeKind.output_parser': 'Parser wyjścia',
'flows.nodeKind.sub_workflow': 'Podprzepływ pracy',
'flows.nodeKind.memory': 'Pamięć',
'flows.nodeKind.dedup': 'Deduplikacja',
'flows.palette.title': 'Węzły',
'flows.palette.addNode': 'Dodaj węzeł {kind}',
'flows.editor.save': 'Zapisz',
@@ -4422,6 +4423,9 @@ const messages: TranslationMap = {
'flows.nodeConfig.memory.limitHint': 'Maksymalna liczba wyników.',
'flows.nodeConfig.memory.minScoreLabel': 'Minimalny wynik',
'flows.nodeConfig.memory.minScoreHint': 'Próg trafności od 0 do 1.',
'flows.nodeConfig.dedup.keyLabel': 'Klucz',
'flows.nodeConfig.dedup.keyHint':
'Stabilne wyrażenie identyfikatora dla każdego elementu, np. =item.id. Elementy z już widzianym kluczem są pomijane.',
'flows.enableApproval.title': 'Zezwolić temu przepływowi pracy na działanie?',
'flows.enableApproval.intro':
+4
View File
@@ -4253,6 +4253,7 @@ const messages: TranslationMap = {
'flows.nodeKind.output_parser': 'Analisador de saída',
'flows.nodeKind.sub_workflow': 'Subfluxo de trabalho',
'flows.nodeKind.memory': 'Memória',
'flows.nodeKind.dedup': 'Deduplicação',
'flows.palette.title': 'Nós',
'flows.palette.addNode': 'Adicionar nó {kind}',
'flows.editor.save': 'Salvar',
@@ -4426,6 +4427,9 @@ const messages: TranslationMap = {
'flows.nodeConfig.memory.limitHint': 'Número máximo de resultados.',
'flows.nodeConfig.memory.minScoreLabel': 'Pontuação mínima',
'flows.nodeConfig.memory.minScoreHint': 'Limite de relevância de 0 a 1.',
'flows.nodeConfig.dedup.keyLabel': 'Chave',
'flows.nodeConfig.dedup.keyHint':
'Uma expressão de id estável por item, ex.: =item.id. Itens com uma chave já vista são ignorados.',
'flows.enableApproval.title': 'Permitir que este fluxo de trabalho aja?',
'flows.enableApproval.intro':
+4
View File
@@ -4231,6 +4231,7 @@ const messages: TranslationMap = {
'flows.nodeKind.output_parser': 'Парсер вывода',
'flows.nodeKind.sub_workflow': 'Подпроцесс',
'flows.nodeKind.memory': 'Память',
'flows.nodeKind.dedup': 'Дедупликация',
'flows.palette.title': 'Узлы',
'flows.palette.addNode': 'Добавить узел {kind}',
'flows.editor.save': 'Сохранить',
@@ -4406,6 +4407,9 @@ const messages: TranslationMap = {
'flows.nodeConfig.memory.limitHint': 'Максимальное количество результатов.',
'flows.nodeConfig.memory.minScoreLabel': 'Минимальная оценка',
'flows.nodeConfig.memory.minScoreHint': 'Порог релевантности от 0 до 1.',
'flows.nodeConfig.dedup.keyLabel': 'Ключ',
'flows.nodeConfig.dedup.keyHint':
'Стабильное выражение id для каждого элемента, напр. =item.id. Элементы с уже встреченным ключом пропускаются.',
'flows.enableApproval.title': 'Разрешить этому рабочему процессу действовать?',
'flows.enableApproval.intro':
+4
View File
@@ -3973,6 +3973,7 @@ const messages: TranslationMap = {
'flows.nodeKind.output_parser': '输出解析器',
'flows.nodeKind.sub_workflow': '子工作流',
'flows.nodeKind.memory': '记忆',
'flows.nodeKind.dedup': '去重',
'flows.palette.title': '节点',
'flows.palette.addNode': '添加{kind}节点',
'flows.editor.save': '保存',
@@ -4134,6 +4135,9 @@ const messages: TranslationMap = {
'flows.nodeConfig.memory.limitHint': '返回结果的最大数量。',
'flows.nodeConfig.memory.minScoreLabel': '最低分数',
'flows.nodeConfig.memory.minScoreHint': '相关性阈值,范围为 0 到 1。',
'flows.nodeConfig.dedup.keyLabel': '键',
'flows.nodeConfig.dedup.keyHint':
'每个项目的稳定 id 表达式,例如 =item.id。已出现过该键的项目将被跳过。',
'flows.enableApproval.title': '允许此工作流执行操作吗?',
'flows.enableApproval.intro': '此工作流需要您对以下操作的许可。批准仅适用于此工作流。',