mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-30 06:32:24 +00:00
feat(flows): frontend for the memory node — palette, config panel, i18n ×14 (#5228)
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Behavior tests for the `memory` node config form (issue #5226). Covers the
|
||||
* per-operation progressive disclosure (`recall`/`search` vs `flavour` vs
|
||||
* `remember`/`forget`) and the hard UI rule mirroring the engine invariant:
|
||||
* a `remember`/`forget` node's `scope` control must never offer `user`.
|
||||
* `useT()` falls back to the bundled English map with no provider mounted
|
||||
* (same convention as the sibling `nodeConfigForms.test.tsx`).
|
||||
*/
|
||||
import { fireEvent, render, screen, within } from '@testing-library/react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { MemoryForm } from '../memoryFields';
|
||||
|
||||
function renderMemoryForm(config: Record<string, unknown> = {}) {
|
||||
const onChange = vi.fn();
|
||||
render(<MemoryForm config={config} onChange={onChange} />);
|
||||
return { onChange };
|
||||
}
|
||||
|
||||
describe('MemoryForm', () => {
|
||||
it('defaults to recall: shows scope (including user) and query, no key/value/flavour', () => {
|
||||
renderMemoryForm();
|
||||
const scope = screen.getByTestId('node-config-memory-scope');
|
||||
const options = within(scope).getAllByRole('option');
|
||||
expect(options.map(o => o.getAttribute('value'))).toEqual(['user', 'flow', 'flows']);
|
||||
expect(screen.getByTestId('node-config-memory-query')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('node-config-memory-flavour')).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('node-config-memory-key')).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('node-config-memory-value')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('search behaves like recall: scope includes user, and query is shown', () => {
|
||||
renderMemoryForm({ operation: 'search' });
|
||||
const scope = screen.getByTestId('node-config-memory-scope');
|
||||
const options = within(scope).getAllByRole('option');
|
||||
expect(options.map(o => o.getAttribute('value'))).toContain('user');
|
||||
expect(screen.getByTestId('node-config-memory-query')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('flavour shows only the flavour slug field, no scope or query', () => {
|
||||
renderMemoryForm({ operation: 'flavour' });
|
||||
expect(screen.getByTestId('node-config-memory-flavour')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('node-config-memory-scope')).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('node-config-memory-query')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('remember hides the user scope option (only flow) and shows key + value', () => {
|
||||
renderMemoryForm({ operation: 'remember', scope: 'flow' });
|
||||
const scope = screen.getByTestId('node-config-memory-scope');
|
||||
const options = within(scope).getAllByRole('option');
|
||||
expect(options.map(o => o.getAttribute('value'))).toEqual(['flow']);
|
||||
expect(within(scope).queryByRole('option', { name: /read-only/i })).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId('node-config-memory-key')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('node-config-memory-value')).toBeInTheDocument();
|
||||
// Not a read/search operation, so no query field.
|
||||
expect(screen.queryByTestId('node-config-memory-query')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('forget hides the user scope option and shows key but not value', () => {
|
||||
renderMemoryForm({ operation: 'forget', scope: 'flow' });
|
||||
const scope = screen.getByTestId('node-config-memory-scope');
|
||||
const options = within(scope).getAllByRole('option');
|
||||
expect(options.map(o => o.getAttribute('value'))).toEqual(['flow']);
|
||||
expect(screen.getByTestId('node-config-memory-key')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('node-config-memory-value')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('clamps scope to flow when switching from recall(user) to remember', () => {
|
||||
const { onChange } = renderMemoryForm({ operation: 'recall', scope: 'user' });
|
||||
fireEvent.change(screen.getByTestId('node-config-memory-operation'), {
|
||||
target: { value: 'remember' },
|
||||
});
|
||||
expect(onChange).toHaveBeenLastCalledWith({ operation: 'remember', scope: 'flow' });
|
||||
});
|
||||
|
||||
it('self-heals a pre-existing invalid remember+user config on mount', () => {
|
||||
const { onChange } = renderMemoryForm({ operation: 'remember', scope: 'user' });
|
||||
expect(onChange).toHaveBeenCalledWith({ scope: 'flow' });
|
||||
});
|
||||
|
||||
it('emits a query patch as the query is typed for recall', () => {
|
||||
const { onChange } = renderMemoryForm({ operation: 'recall' });
|
||||
fireEvent.change(screen.getByTestId('node-config-memory-query'), {
|
||||
target: { value: '=item.title' },
|
||||
});
|
||||
expect(onChange).toHaveBeenLastCalledWith({ query: '=item.title' });
|
||||
});
|
||||
|
||||
it('emits limit as a number as it is typed', () => {
|
||||
const { onChange } = renderMemoryForm({ operation: 'recall' });
|
||||
fireEvent.change(screen.getByTestId('node-config-memory-limit'), { target: { value: '5' } });
|
||||
expect(onChange).toHaveBeenLastCalledWith({ limit: 5 });
|
||||
});
|
||||
|
||||
it('emits undefined for limit when the field is cleared back to empty', () => {
|
||||
// Start from an already-populated `limit` so clearing it is a genuine
|
||||
// DOM value change (a fresh controlled input already renders blank).
|
||||
const { onChange } = renderMemoryForm({ operation: 'recall', limit: 5 });
|
||||
fireEvent.change(screen.getByTestId('node-config-memory-limit'), { target: { value: '' } });
|
||||
expect(onChange).toHaveBeenLastCalledWith({ limit: undefined });
|
||||
});
|
||||
|
||||
it('emits min_score as a number as it is typed', () => {
|
||||
const { onChange } = renderMemoryForm({ operation: 'recall' });
|
||||
fireEvent.change(screen.getByTestId('node-config-memory-min-score'), {
|
||||
target: { value: '0.5' },
|
||||
});
|
||||
expect(onChange).toHaveBeenLastCalledWith({ min_score: 0.5 });
|
||||
});
|
||||
|
||||
it('emits undefined for min_score when the field is cleared back to empty', () => {
|
||||
// Start from an already-populated `min_score` so clearing it is a genuine
|
||||
// DOM value change (a fresh controlled input already renders blank).
|
||||
const { onChange } = renderMemoryForm({ operation: 'recall', min_score: 0.5 });
|
||||
fireEvent.change(screen.getByTestId('node-config-memory-min-score'), { target: { value: '' } });
|
||||
expect(onChange).toHaveBeenLastCalledWith({ min_score: undefined });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* `memory` node config form (issue #5226 — 13th tinyflows `NodeKind`).
|
||||
* Declarative, in-graph memory access: `recall`/`search`/`flavour`/`people`
|
||||
* read; `remember`/`forget` write. Spec: `my_docs/memory_access_in_workflows/08-memory-node.md`.
|
||||
*
|
||||
* Field keys mirror what the engine actually reads at runtime (per the
|
||||
* design doc's `MemoryProvider` trait + node config table):
|
||||
* - `operation` — always. `recall` · `search` · `flavour` · `people` ·
|
||||
* `remember` · `forget`.
|
||||
* - `scope` — for `recall`/`search`/`remember`/`forget`. `user` and
|
||||
* `flows` are read-only scopes (only offered for the read operations);
|
||||
* `remember`/`forget` may only ever target `flow`.
|
||||
* - `query` — `=`-bindable, for `recall`/`search` (and optionally
|
||||
* `people`, whose `people(query: Option<&str>)` signature takes one too).
|
||||
* - `flavour` — a plain slug (not an expression), for `flavour`.
|
||||
* - `key` — `=`-bindable, for `remember`/`forget`.
|
||||
* - `value` — `=`-bindable, for `remember` only.
|
||||
* - `limit` / `min_score` — optional numeric caps on the read operations.
|
||||
*
|
||||
* Progressive disclosure mirrors the other multi-field forms in
|
||||
* `nodeConfigForms.tsx` (e.g. `TriggerForm`'s `trigger_kind` switch): only
|
||||
* the fields relevant to the selected `operation` render.
|
||||
*/
|
||||
import createDebug from 'debug';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { useT } from '../../../../lib/i18n/I18nContext';
|
||||
import {
|
||||
configNumber,
|
||||
configString,
|
||||
ExpressionField,
|
||||
NumberField,
|
||||
SelectField,
|
||||
} from './nodeConfigFields';
|
||||
import type { UpstreamExpressionOption } from './upstreamOptions';
|
||||
|
||||
const log = createDebug('app:flows:nodeConfig:memory');
|
||||
|
||||
const MEMORY_OPERATIONS = ['recall', 'search', 'flavour', 'people', 'remember', 'forget'] as const;
|
||||
type MemoryOperation = (typeof MEMORY_OPERATIONS)[number];
|
||||
|
||||
/**
|
||||
* The seven persona facets the `flavour` operation's `memory_flavour` engine
|
||||
* reader (`src/openhuman/memory/tools/flavour.rs`) accepts — any other slug
|
||||
* returns "Unknown flavour". Rendered as a dropdown rather than free text so
|
||||
* an author can't type an invalid slug (e.g. `email-tone`) in the first place.
|
||||
*/
|
||||
const MEMORY_FLAVOURS = [
|
||||
'communication',
|
||||
'coding_style',
|
||||
'stack',
|
||||
'workflow',
|
||||
'environment',
|
||||
'directives',
|
||||
'anti_preferences',
|
||||
] as const;
|
||||
|
||||
/** Operations that read/write at a `scope`. `flavour` and `people` don't take one. */
|
||||
const SCOPED_OPERATIONS = new Set<MemoryOperation>(['recall', 'search', 'remember', 'forget']);
|
||||
|
||||
/**
|
||||
* `remember`/`forget` are flow-only writes — the engine rejects a
|
||||
* `user`-scoped write at `validate_all` time (the hard security invariant
|
||||
* from `05-security.md`/`08-memory-node.md`). The UI must never let an
|
||||
* author construct that pair, so these operations get a `scope` control
|
||||
* offering only `flow`, never `user` (or the other read-only scope, `flows`).
|
||||
*/
|
||||
const WRITE_OPERATIONS = new Set<MemoryOperation>(['remember', 'forget']);
|
||||
|
||||
/** Operations that take a free-text `query`. */
|
||||
const QUERY_OPERATIONS = new Set<MemoryOperation>(['recall', 'search', 'people']);
|
||||
|
||||
/** Operations whose result set can be capped/thresholded. */
|
||||
const RESULT_OPERATIONS = new Set<MemoryOperation>(['recall', 'search', 'people']);
|
||||
|
||||
/**
|
||||
* Deliberately narrower than `NodeConfigFormProps` (`nodeConfigForms.tsx`) —
|
||||
* this form needs no `connections` (memory 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 MemoryFormProps {
|
||||
config: Record<string, unknown>;
|
||||
onChange: (patch: Record<string, unknown>) => void;
|
||||
upstreamOptions?: UpstreamExpressionOption[];
|
||||
}
|
||||
|
||||
export function MemoryForm({ config, onChange, upstreamOptions }: MemoryFormProps) {
|
||||
const { t } = useT();
|
||||
const operation = (configString(config, 'operation') || 'recall') as MemoryOperation;
|
||||
const scope = configString(config, 'scope');
|
||||
const isWrite = WRITE_OPERATIONS.has(operation);
|
||||
|
||||
// Self-heal: if this node arrives with an already-invalid `remember`/
|
||||
// `forget` + non-`flow` scope (a raw-JSON edit, an older draft, a
|
||||
// workflow-builder proposal), correct it as soon as the form mounts/updates
|
||||
// rather than only guarding the operation-change handler below — the
|
||||
// invariant must hold no matter how the bad pair got here.
|
||||
useEffect(() => {
|
||||
if (isWrite && scope !== 'flow') {
|
||||
log(
|
||||
'self-heal: clamping scope=%s to flow for write operation=%s',
|
||||
scope || '(empty)',
|
||||
operation
|
||||
);
|
||||
onChange({ scope: 'flow' });
|
||||
}
|
||||
}, [isWrite, scope, operation, onChange]);
|
||||
|
||||
const handleOperationChange = (value: string) => {
|
||||
const next = value as MemoryOperation;
|
||||
const patch: Record<string, unknown> = { operation: next };
|
||||
// Hard UI rule mirroring the engine invariant: switching straight into a
|
||||
// write operation while a read-only scope (`user`/`flows`) is selected
|
||||
// must clamp to `flow` in the same patch, not rely on the effect above
|
||||
// (which would otherwise author one extra, momentarily-invalid patch).
|
||||
if (WRITE_OPERATIONS.has(next) && scope !== 'flow') {
|
||||
patch.scope = 'flow';
|
||||
}
|
||||
log('operation change: %s -> %s (scope patch=%s)', operation, next, patch.scope ?? 'none');
|
||||
onChange(patch);
|
||||
};
|
||||
|
||||
const scopeOptions = isWrite
|
||||
? [{ value: 'flow', label: t('flows.nodeConfig.memory.scope_flow') }]
|
||||
: [
|
||||
{ value: 'user', label: t('flows.nodeConfig.memory.scope_user') },
|
||||
{ value: 'flow', label: t('flows.nodeConfig.memory.scope_flow') },
|
||||
{ value: 'flows', label: t('flows.nodeConfig.memory.scope_flows') },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<SelectField
|
||||
label={t('flows.nodeConfig.memory.operationLabel')}
|
||||
value={operation}
|
||||
onChange={handleOperationChange}
|
||||
testId="node-config-memory-operation"
|
||||
options={MEMORY_OPERATIONS.map(op => ({
|
||||
value: op,
|
||||
label: t(`flows.nodeConfig.memory.operation_${op}`),
|
||||
}))}
|
||||
/>
|
||||
|
||||
{SCOPED_OPERATIONS.has(operation) && (
|
||||
<SelectField
|
||||
label={t('flows.nodeConfig.memory.scopeLabel')}
|
||||
hint={
|
||||
isWrite
|
||||
? t('flows.nodeConfig.memory.scopeWriteHint')
|
||||
: t('flows.nodeConfig.memory.scopeHint')
|
||||
}
|
||||
value={isWrite ? 'flow' : scope || 'user'}
|
||||
onChange={v => onChange({ scope: v })}
|
||||
testId="node-config-memory-scope"
|
||||
options={scopeOptions}
|
||||
/>
|
||||
)}
|
||||
|
||||
{QUERY_OPERATIONS.has(operation) && (
|
||||
<ExpressionField
|
||||
label={t('flows.nodeConfig.memory.queryLabel')}
|
||||
hint={operation === 'people' ? t('flows.nodeConfig.memory.queryOptionalHint') : undefined}
|
||||
value={configString(config, 'query')}
|
||||
onChange={v => onChange({ query: v })}
|
||||
placeholder="=item.title"
|
||||
upstreamOptions={upstreamOptions}
|
||||
testId="node-config-memory-query"
|
||||
/>
|
||||
)}
|
||||
|
||||
{operation === 'flavour' && (
|
||||
<SelectField
|
||||
label={t('flows.nodeConfig.memory.flavourLabel')}
|
||||
hint={t('flows.nodeConfig.memory.flavourHint')}
|
||||
value={configString(config, 'flavour') || MEMORY_FLAVOURS[0]}
|
||||
onChange={v => onChange({ flavour: v })}
|
||||
testId="node-config-memory-flavour"
|
||||
options={MEMORY_FLAVOURS.map(facet => ({
|
||||
value: facet,
|
||||
label: t(`flows.nodeConfig.memory.flavour_${facet}`),
|
||||
}))}
|
||||
/>
|
||||
)}
|
||||
|
||||
{(operation === 'remember' || operation === 'forget') && (
|
||||
<ExpressionField
|
||||
label={t('flows.nodeConfig.memory.keyLabel')}
|
||||
value={configString(config, 'key')}
|
||||
onChange={v => onChange({ key: v })}
|
||||
placeholder="=item.id"
|
||||
upstreamOptions={upstreamOptions}
|
||||
testId="node-config-memory-key"
|
||||
/>
|
||||
)}
|
||||
|
||||
{operation === 'remember' && (
|
||||
<ExpressionField
|
||||
label={t('flows.nodeConfig.memory.valueLabel')}
|
||||
value={configString(config, 'value')}
|
||||
onChange={v => onChange({ value: v })}
|
||||
placeholder="=item"
|
||||
upstreamOptions={upstreamOptions}
|
||||
testId="node-config-memory-value"
|
||||
/>
|
||||
)}
|
||||
|
||||
{RESULT_OPERATIONS.has(operation) && (
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<NumberField
|
||||
label={t('flows.nodeConfig.memory.limitLabel')}
|
||||
hint={t('flows.nodeConfig.memory.limitHint')}
|
||||
value={configNumber(config, 'limit')}
|
||||
onChange={v => onChange({ limit: v })}
|
||||
min={1}
|
||||
step={1}
|
||||
testId="node-config-memory-limit"
|
||||
/>
|
||||
<NumberField
|
||||
label={t('flows.nodeConfig.memory.minScoreLabel')}
|
||||
hint={t('flows.nodeConfig.memory.minScoreHint')}
|
||||
value={configNumber(config, 'min_score')}
|
||||
onChange={v => onChange({ min_score: v })}
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.05}
|
||||
testId="node-config-memory-min-score"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -36,6 +36,12 @@ export function configString(config: Record<string, unknown>, key: string): stri
|
||||
return typeof value === 'string' ? value : '';
|
||||
}
|
||||
|
||||
/** Read a finite numeric field off a free-form config object, defaulting to `undefined`. */
|
||||
export function configNumber(config: Record<string, unknown>, key: string): number | undefined {
|
||||
const value = config[key];
|
||||
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
||||
}
|
||||
|
||||
/** Read a `Record<string,string>` map off config (e.g. HTTP headers / transform set). */
|
||||
export function configStringMap(
|
||||
config: Record<string, unknown>,
|
||||
@@ -167,6 +173,57 @@ export function SelectField({ label, hint, value, onChange, options, testId }: S
|
||||
);
|
||||
}
|
||||
|
||||
interface NumberFieldProps {
|
||||
label: string;
|
||||
hint?: string;
|
||||
value: number | undefined;
|
||||
onChange: (value: number | undefined) => void;
|
||||
placeholder?: string;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
testId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A plain numeric input (e.g. a memory node's `limit` / `min_score`). Unlike
|
||||
* {@link TextField} the empty string round-trips to `undefined` rather than
|
||||
* `''`, so an unset optional numeric config key stays genuinely absent
|
||||
* instead of becoming a stray `""` in the saved graph.
|
||||
*/
|
||||
export function NumberField({
|
||||
label,
|
||||
hint,
|
||||
value,
|
||||
onChange,
|
||||
placeholder,
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
testId,
|
||||
}: NumberFieldProps) {
|
||||
const id = useId();
|
||||
return (
|
||||
<Field label={label} hint={hint} htmlFor={id}>
|
||||
<input
|
||||
id={id}
|
||||
type="number"
|
||||
className={INPUT_CLASS}
|
||||
value={value ?? ''}
|
||||
placeholder={placeholder}
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
data-testid={testId}
|
||||
onChange={e => {
|
||||
const raw = e.target.value;
|
||||
onChange(raw === '' ? undefined : Number(raw));
|
||||
}}
|
||||
/>
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact "insert an upstream value" dropdown (Feature: `nodes` scope picker).
|
||||
* Always renders with the empty placeholder selected; picking an option calls
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
* - `switch` → `expression` (=-expr, precedence) / `field` (fallback)
|
||||
* - `transform` → `set` (key → =-expression map)
|
||||
* - `trigger` → `trigger_kind` + kind-specific (`schedule`, `toolkit`/`trigger_slug`)
|
||||
* - `memory` → `operation` + operation-specific (`scope`/`query`/`flavour`/`key`/`value`/
|
||||
* `limit`/`min_score`), see `memoryFields.tsx`
|
||||
*/
|
||||
import createDebug from 'debug';
|
||||
import { useEffect, useState } from 'react';
|
||||
@@ -30,6 +32,7 @@ import {
|
||||
ComposioTriggerField,
|
||||
fetchActionSchema,
|
||||
} from './composioFields';
|
||||
import { MemoryForm } from './memoryFields';
|
||||
import { NATIVE_TOOL_PREFIX, NativeToolField } from './nativeToolFields';
|
||||
import {
|
||||
configString,
|
||||
@@ -492,4 +495,5 @@ export const NODE_CONFIG_FORMS: Partial<Record<NodeKind, NodeConfigForm>> = {
|
||||
switch: SwitchForm,
|
||||
transform: TransformForm,
|
||||
code: CodeForm,
|
||||
memory: MemoryForm,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Per-kind visual metadata for the 12 tinyflows `NodeKind`s, shared by the
|
||||
* Per-kind visual metadata for the 13 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 12 kinds and 4 ramps some kinds share a color family — the emoji
|
||||
* free; with 13 kinds and 4 ramps some kinds share a color family — the emoji
|
||||
* + name remain the primary distinguishers.
|
||||
*/
|
||||
import type { NodeKind } from './types';
|
||||
@@ -29,9 +29,12 @@ interface NodeKindMeta {
|
||||
}
|
||||
|
||||
/**
|
||||
* The 12 `NodeKind`s in the order they should appear in the palette. Trigger
|
||||
* The 13 `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.
|
||||
* 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.
|
||||
*/
|
||||
const NODE_KINDS: NodeKind[] = [
|
||||
'trigger',
|
||||
@@ -46,6 +49,7 @@ const NODE_KINDS: NodeKind[] = [
|
||||
'transform',
|
||||
'output_parser',
|
||||
'sub_workflow',
|
||||
'memory',
|
||||
];
|
||||
|
||||
/** Per-kind emoji + border/chip color + palette group. See the module doc. */
|
||||
@@ -56,6 +60,10 @@ const NODE_KIND_META: Record<NodeKind, NodeKindMeta> = {
|
||||
http_request: { emoji: '🌐', color: 'coral', group: 'actions' },
|
||||
code: { emoji: '📝', color: 'sage', group: 'actions' },
|
||||
sub_workflow: { emoji: '🧩', color: 'coral', group: 'actions' },
|
||||
// Declarative in-graph memory access (recall/search/flavour/people/
|
||||
// remember/forget) — an "actions" node like `tool_call`/`http_request`
|
||||
// (it reads/writes state), not `logic` (it doesn't itself branch/reshape).
|
||||
memory: { emoji: '🧠', color: 'sage', group: 'actions' },
|
||||
condition: { emoji: '🔀', color: 'primary', group: 'logic' },
|
||||
switch: { emoji: '🔁', color: 'amber', group: 'logic' },
|
||||
merge: { emoji: '🔗', color: 'coral', group: 'logic' },
|
||||
@@ -128,7 +136,7 @@ export const PALETTE_ENTRIES_BY_GROUP: Record<NodeGroup, PaletteEntry[]> = {
|
||||
|
||||
/**
|
||||
* Fallback for any `kind` outside {@link NODE_KIND_META} — a saved graph is
|
||||
* `unknown` on the wire (cast in `FlowCanvasPage.tsx`), so a future 13th
|
||||
* `unknown` on the wire (cast in `FlowCanvasPage.tsx`), so a future 14th
|
||||
* tinyflows kind, or any other value the backend ever emits, can reach the
|
||||
* renderer at runtime even though TypeScript can't see it. Lookups fall back
|
||||
* here so an unrecognized kind renders as a plain neutral node instead of
|
||||
|
||||
@@ -62,4 +62,14 @@ describe('describeNode', () => {
|
||||
it('returns empty string for an unknown kind', () => {
|
||||
expect(describeNode('time_travel', {})).toBe('');
|
||||
});
|
||||
|
||||
it('gives memory search its own summary, distinct from recall', () => {
|
||||
expect(describeNode('memory', { operation: 'search', scope: 'user' })).toBe(
|
||||
'Searches memory (user)'
|
||||
);
|
||||
expect(describeNode('memory', { operation: 'recall', scope: 'user' })).toBe(
|
||||
'Recalls memory (user)'
|
||||
);
|
||||
expect(describeNode('memory', { operation: 'search' })).toBe('Searches memory');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -89,6 +89,21 @@ export function describeNode(
|
||||
return 'Parses the previous output';
|
||||
case 'sub_workflow':
|
||||
return 'Runs a nested workflow';
|
||||
case 'memory': {
|
||||
const operation = str(config, 'operation') || 'recall';
|
||||
const scope = str(config, 'scope');
|
||||
if (operation === 'flavour') {
|
||||
const flavour = str(config, 'flavour');
|
||||
return flavour ? `Reads the "${flavour}" flavour` : 'Reads a memory flavour';
|
||||
}
|
||||
if (operation === 'people') return 'Looks up people memory';
|
||||
if (operation === 'remember') return 'Remembers a value in this workflow';
|
||||
if (operation === 'forget') return 'Forgets a value from this workflow';
|
||||
const scoped = scope ? ` (${scope})` : '';
|
||||
if (operation === 'search') return `Searches memory${scoped}`;
|
||||
// recall
|
||||
return `Recalls memory${scoped}`;
|
||||
}
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -36,8 +36,18 @@ export interface Position {
|
||||
}
|
||||
|
||||
/**
|
||||
* The 12 node kinds `tinyflows` currently defines (`tinyflows::model::NodeKind`).
|
||||
* The 13 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
|
||||
* access (`recall`/`search`/`flavour`/`people`/`remember`/`forget`, see
|
||||
* `my_docs/memory_access_in_workflows/08-memory-node.md`). Its config is
|
||||
* still the same free-form `WorkflowNode.config` bag as every other kind
|
||||
* (see {@link WorkflowNode}); there is no dedicated TS config interface
|
||||
* because no other kind has one either — the node-config form
|
||||
* (`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.
|
||||
*/
|
||||
export type NodeKind =
|
||||
| 'trigger'
|
||||
@@ -51,7 +61,8 @@ export type NodeKind =
|
||||
| 'split_out'
|
||||
| 'transform'
|
||||
| 'output_parser'
|
||||
| 'sub_workflow';
|
||||
| 'sub_workflow'
|
||||
| 'memory';
|
||||
|
||||
/**
|
||||
* A named connection point on a node. Mirrors `tinyflows::model::Port`.
|
||||
|
||||
@@ -4092,6 +4092,7 @@ const messages: TranslationMap = {
|
||||
'flows.nodeKind.transform': 'التحويل',
|
||||
'flows.nodeKind.output_parser': 'محلل المخرجات',
|
||||
'flows.nodeKind.sub_workflow': 'سير عمل فرعي',
|
||||
'flows.nodeKind.memory': 'الذاكرة',
|
||||
'flows.palette.title': 'العقد',
|
||||
'flows.palette.addNode': 'إضافة عقدة {kind}',
|
||||
'flows.editor.save': 'حفظ',
|
||||
@@ -4226,6 +4227,37 @@ const messages: TranslationMap = {
|
||||
'flows.nodeConfig.code.language_javascript': 'JavaScript',
|
||||
'flows.nodeConfig.code.language_python': 'Python',
|
||||
'flows.nodeConfig.code.sourceLabel': 'الكود المصدري',
|
||||
'flows.nodeConfig.memory.operationLabel': 'العملية',
|
||||
'flows.nodeConfig.memory.operation_recall': 'استرجاع',
|
||||
'flows.nodeConfig.memory.operation_search': 'بحث',
|
||||
'flows.nodeConfig.memory.operation_flavour': 'النمط',
|
||||
'flows.nodeConfig.memory.operation_people': 'الأشخاص',
|
||||
'flows.nodeConfig.memory.operation_remember': 'تذكر',
|
||||
'flows.nodeConfig.memory.operation_forget': 'نسيان',
|
||||
'flows.nodeConfig.memory.scopeLabel': 'النطاق',
|
||||
'flows.nodeConfig.memory.scopeHint': 'مكان البحث عن هذه الذاكرة.',
|
||||
'flows.nodeConfig.memory.scopeWriteHint':
|
||||
'يُسمح بالكتابة داخل سير العمل هذا فقط، وليس أبدًا في ذاكرتك الشخصية.',
|
||||
'flows.nodeConfig.memory.scope_user': 'أنت (للقراءة فقط)',
|
||||
'flows.nodeConfig.memory.scope_flow': 'سير العمل هذا',
|
||||
'flows.nodeConfig.memory.scope_flows': 'كل سير العمل (للقراءة فقط)',
|
||||
'flows.nodeConfig.memory.queryLabel': 'الاستعلام',
|
||||
'flows.nodeConfig.memory.queryOptionalHint': 'اختياري: يضيّق نطاق البحث عن الأشخاص.',
|
||||
'flows.nodeConfig.memory.flavourLabel': 'النمط',
|
||||
'flows.nodeConfig.memory.flavourHint': 'أي جانب من ملف الشخصية يجب قراءته، مثل communication.',
|
||||
'flows.nodeConfig.memory.flavour_communication': 'التواصل',
|
||||
'flows.nodeConfig.memory.flavour_coding_style': 'أسلوب البرمجة',
|
||||
'flows.nodeConfig.memory.flavour_stack': 'التقنيات المستخدمة',
|
||||
'flows.nodeConfig.memory.flavour_workflow': 'سير العمل',
|
||||
'flows.nodeConfig.memory.flavour_environment': 'البيئة',
|
||||
'flows.nodeConfig.memory.flavour_directives': 'التوجيهات',
|
||||
'flows.nodeConfig.memory.flavour_anti_preferences': 'التفضيلات السلبية',
|
||||
'flows.nodeConfig.memory.keyLabel': 'المفتاح',
|
||||
'flows.nodeConfig.memory.valueLabel': 'القيمة',
|
||||
'flows.nodeConfig.memory.limitLabel': 'الحد الأقصى',
|
||||
'flows.nodeConfig.memory.limitHint': 'أقصى عدد من النتائج.',
|
||||
'flows.nodeConfig.memory.minScoreLabel': 'الحد الأدنى للنتيجة',
|
||||
'flows.nodeConfig.memory.minScoreHint': 'عتبة الصلة من 0 إلى 1.',
|
||||
|
||||
'flows.chooser.title': 'إنشاء سير عمل',
|
||||
'flows.chooser.subtitle': 'اختر كيف تريد أن تبدأ.',
|
||||
|
||||
@@ -4194,6 +4194,7 @@ const messages: TranslationMap = {
|
||||
'flows.nodeKind.transform': 'রূপান্তর',
|
||||
'flows.nodeKind.output_parser': 'আউটপুট পার্সার',
|
||||
'flows.nodeKind.sub_workflow': 'সাব-ওয়ার্কফ্লো',
|
||||
'flows.nodeKind.memory': 'মেমরি',
|
||||
'flows.palette.title': 'নোড',
|
||||
'flows.palette.addNode': '{kind} নোড যোগ করুন',
|
||||
'flows.editor.save': 'সংরক্ষণ করুন',
|
||||
@@ -4333,6 +4334,37 @@ const messages: TranslationMap = {
|
||||
'flows.nodeConfig.code.language_javascript': 'JavaScript',
|
||||
'flows.nodeConfig.code.language_python': 'Python',
|
||||
'flows.nodeConfig.code.sourceLabel': 'সোর্স কোড',
|
||||
'flows.nodeConfig.memory.operationLabel': 'অপারেশন',
|
||||
'flows.nodeConfig.memory.operation_recall': 'পুনরুদ্ধার',
|
||||
'flows.nodeConfig.memory.operation_search': 'অনুসন্ধান',
|
||||
'flows.nodeConfig.memory.operation_flavour': 'ধরন',
|
||||
'flows.nodeConfig.memory.operation_people': 'ব্যক্তি',
|
||||
'flows.nodeConfig.memory.operation_remember': 'মনে রাখুন',
|
||||
'flows.nodeConfig.memory.operation_forget': 'ভুলে যান',
|
||||
'flows.nodeConfig.memory.scopeLabel': 'পরিধি',
|
||||
'flows.nodeConfig.memory.scopeHint': 'এই মেমরিটি কোথায় খুঁজতে হবে।',
|
||||
'flows.nodeConfig.memory.scopeWriteHint':
|
||||
'শুধু এই ওয়ার্কফ্লোর মধ্যেই লেখার অনুমতি আছে, আপনার ব্যক্তিগত মেমরিতে কখনও নয়।',
|
||||
'flows.nodeConfig.memory.scope_user': 'আপনি (শুধু পড়ার জন্য)',
|
||||
'flows.nodeConfig.memory.scope_flow': 'এই ওয়ার্কফ্লো',
|
||||
'flows.nodeConfig.memory.scope_flows': 'সব ওয়ার্কফ্লো (শুধু পড়ার জন্য)',
|
||||
'flows.nodeConfig.memory.queryLabel': 'কোয়েরি',
|
||||
'flows.nodeConfig.memory.queryOptionalHint': 'ঐচ্ছিক: মানুষ খোঁজার পরিধি সংকুচিত করে।',
|
||||
'flows.nodeConfig.memory.flavourLabel': 'ধরন',
|
||||
'flows.nodeConfig.memory.flavourHint': 'কোন ব্যক্তিত্ব দিক পড়তে হবে, যেমন communication।',
|
||||
'flows.nodeConfig.memory.flavour_communication': 'যোগাযোগ',
|
||||
'flows.nodeConfig.memory.flavour_coding_style': 'কোডিং শৈলী',
|
||||
'flows.nodeConfig.memory.flavour_stack': 'প্রযুক্তি স্ট্যাক',
|
||||
'flows.nodeConfig.memory.flavour_workflow': 'কর্মপ্রবাহ',
|
||||
'flows.nodeConfig.memory.flavour_environment': 'পরিবেশ',
|
||||
'flows.nodeConfig.memory.flavour_directives': 'নির্দেশনা',
|
||||
'flows.nodeConfig.memory.flavour_anti_preferences': 'অপছন্দসমূহ',
|
||||
'flows.nodeConfig.memory.keyLabel': 'কী',
|
||||
'flows.nodeConfig.memory.valueLabel': 'মান',
|
||||
'flows.nodeConfig.memory.limitLabel': 'সীমা',
|
||||
'flows.nodeConfig.memory.limitHint': 'সর্বোচ্চ ফলাফল সংখ্যা।',
|
||||
'flows.nodeConfig.memory.minScoreLabel': 'সর্বনিম্ন স্কোর',
|
||||
'flows.nodeConfig.memory.minScoreHint': '০ থেকে ১ পর্যন্ত প্রাসঙ্গিকতার থ্রেশহোল্ড।',
|
||||
|
||||
'flows.chooser.title': 'ওয়ার্কফ্লো তৈরি করুন',
|
||||
'flows.chooser.subtitle': 'আপনি কীভাবে শুরু করতে চান তা বেছে নিন।',
|
||||
|
||||
@@ -4312,6 +4312,7 @@ const messages: TranslationMap = {
|
||||
'flows.nodeKind.transform': 'Transformation',
|
||||
'flows.nodeKind.output_parser': 'Ausgabe-Parser',
|
||||
'flows.nodeKind.sub_workflow': 'Unter-Workflow',
|
||||
'flows.nodeKind.memory': 'Gedächtnis',
|
||||
'flows.palette.title': 'Knoten',
|
||||
'flows.palette.addNode': '{kind}-Knoten hinzufügen',
|
||||
'flows.editor.save': 'Speichern',
|
||||
@@ -4455,6 +4456,38 @@ const messages: TranslationMap = {
|
||||
'flows.nodeConfig.code.language_javascript': 'JavaScript',
|
||||
'flows.nodeConfig.code.language_python': 'Python',
|
||||
'flows.nodeConfig.code.sourceLabel': 'Quellcode',
|
||||
'flows.nodeConfig.memory.operationLabel': 'Vorgang',
|
||||
'flows.nodeConfig.memory.operation_recall': 'Abrufen',
|
||||
'flows.nodeConfig.memory.operation_search': 'Suchen',
|
||||
'flows.nodeConfig.memory.operation_flavour': 'Stilprofil',
|
||||
'flows.nodeConfig.memory.operation_people': 'Personen',
|
||||
'flows.nodeConfig.memory.operation_remember': 'Merken',
|
||||
'flows.nodeConfig.memory.operation_forget': 'Vergessen',
|
||||
'flows.nodeConfig.memory.scopeLabel': 'Bereich',
|
||||
'flows.nodeConfig.memory.scopeHint': 'Wo nach diesem Gedächtniseintrag gesucht werden soll.',
|
||||
'flows.nodeConfig.memory.scopeWriteHint':
|
||||
'Schreibzugriffe sind nur innerhalb dieses Workflows erlaubt, niemals in deinem persönlichen Gedächtnis.',
|
||||
'flows.nodeConfig.memory.scope_user': 'Du (nur lesend)',
|
||||
'flows.nodeConfig.memory.scope_flow': 'Dieser Workflow',
|
||||
'flows.nodeConfig.memory.scope_flows': 'Alle Workflows (nur lesend)',
|
||||
'flows.nodeConfig.memory.queryLabel': 'Abfrage',
|
||||
'flows.nodeConfig.memory.queryOptionalHint': 'Optional: grenzt die Personensuche ein.',
|
||||
'flows.nodeConfig.memory.flavourLabel': 'Stilprofil',
|
||||
'flows.nodeConfig.memory.flavourHint':
|
||||
'Welche Persona-Facette gelesen werden soll, z. B. communication.',
|
||||
'flows.nodeConfig.memory.flavour_communication': 'Kommunikation',
|
||||
'flows.nodeConfig.memory.flavour_coding_style': 'Coding-Stil',
|
||||
'flows.nodeConfig.memory.flavour_stack': 'Stack',
|
||||
'flows.nodeConfig.memory.flavour_workflow': 'Workflow',
|
||||
'flows.nodeConfig.memory.flavour_environment': 'Umgebung',
|
||||
'flows.nodeConfig.memory.flavour_directives': 'Vorgaben',
|
||||
'flows.nodeConfig.memory.flavour_anti_preferences': 'Ablehnungen',
|
||||
'flows.nodeConfig.memory.keyLabel': 'Schlüssel',
|
||||
'flows.nodeConfig.memory.valueLabel': 'Wert',
|
||||
'flows.nodeConfig.memory.limitLabel': 'Limit',
|
||||
'flows.nodeConfig.memory.limitHint': 'Maximale Anzahl an Ergebnissen.',
|
||||
'flows.nodeConfig.memory.minScoreLabel': 'Mindestbewertung',
|
||||
'flows.nodeConfig.memory.minScoreHint': 'Relevanzschwelle von 0 bis 1.',
|
||||
|
||||
'flows.chooser.title': 'Workflow erstellen',
|
||||
'flows.chooser.subtitle': 'Wählen Sie, wie Sie beginnen möchten.',
|
||||
|
||||
+34
-1
@@ -4741,7 +4741,7 @@ const en: TranslationMap = {
|
||||
'flows.copilot.continueBuilding': 'Continue building',
|
||||
|
||||
// ── Workflow Canvas (issue B5b.1): the read-only graph view of a saved
|
||||
// flow at /flows/:id. `flows.nodeKind.*` labels the 12 tinyflows node
|
||||
// flow at /flows/:id. `flows.nodeKind.*` labels the 13 tinyflows node
|
||||
// kinds (`tinyflows::model::NodeKind`) shown in each canvas node card.
|
||||
'flows.canvas.title': 'Workflow',
|
||||
'flows.canvas.loading': 'Loading workflow…',
|
||||
@@ -4766,6 +4766,7 @@ const en: TranslationMap = {
|
||||
'flows.nodeKind.transform': 'Transform',
|
||||
'flows.nodeKind.output_parser': 'Output parser',
|
||||
'flows.nodeKind.sub_workflow': 'Sub-workflow',
|
||||
'flows.nodeKind.memory': 'Memory',
|
||||
|
||||
// ── Editable Workflow Canvas (issue B5b.2 / Phase 3a): the node palette
|
||||
// and editor toolbar layered on top of the read-only canvas above.
|
||||
@@ -4917,6 +4918,38 @@ const en: TranslationMap = {
|
||||
'flows.nodeConfig.code.language_javascript': 'JavaScript',
|
||||
'flows.nodeConfig.code.language_python': 'Python',
|
||||
'flows.nodeConfig.code.sourceLabel': 'Source',
|
||||
// `memory` node (issue #5226): recall/search/flavour/people read; remember/forget write.
|
||||
'flows.nodeConfig.memory.operationLabel': 'Operation',
|
||||
'flows.nodeConfig.memory.operation_recall': 'Recall',
|
||||
'flows.nodeConfig.memory.operation_search': 'Search',
|
||||
'flows.nodeConfig.memory.operation_flavour': 'Flavour',
|
||||
'flows.nodeConfig.memory.operation_people': 'People',
|
||||
'flows.nodeConfig.memory.operation_remember': 'Remember',
|
||||
'flows.nodeConfig.memory.operation_forget': 'Forget',
|
||||
'flows.nodeConfig.memory.scopeLabel': 'Scope',
|
||||
'flows.nodeConfig.memory.scopeHint': 'Where to look for this memory.',
|
||||
'flows.nodeConfig.memory.scopeWriteHint':
|
||||
'Writes are only allowed within this workflow, never to your personal memory.',
|
||||
'flows.nodeConfig.memory.scope_user': 'You (read-only)',
|
||||
'flows.nodeConfig.memory.scope_flow': 'This workflow',
|
||||
'flows.nodeConfig.memory.scope_flows': 'All workflows (read-only)',
|
||||
'flows.nodeConfig.memory.queryLabel': 'Query',
|
||||
'flows.nodeConfig.memory.queryOptionalHint': 'Optional: narrows the people lookup.',
|
||||
'flows.nodeConfig.memory.flavourLabel': 'Flavour',
|
||||
'flows.nodeConfig.memory.flavourHint': 'Which persona facet to read, e.g. communication.',
|
||||
'flows.nodeConfig.memory.flavour_communication': 'Communication',
|
||||
'flows.nodeConfig.memory.flavour_coding_style': 'Coding style',
|
||||
'flows.nodeConfig.memory.flavour_stack': 'Stack',
|
||||
'flows.nodeConfig.memory.flavour_workflow': 'Workflow',
|
||||
'flows.nodeConfig.memory.flavour_environment': 'Environment',
|
||||
'flows.nodeConfig.memory.flavour_directives': 'Directives',
|
||||
'flows.nodeConfig.memory.flavour_anti_preferences': 'Anti-preferences',
|
||||
'flows.nodeConfig.memory.keyLabel': 'Key',
|
||||
'flows.nodeConfig.memory.valueLabel': 'Value',
|
||||
'flows.nodeConfig.memory.limitLabel': 'Limit',
|
||||
'flows.nodeConfig.memory.limitHint': 'Maximum number of results.',
|
||||
'flows.nodeConfig.memory.minScoreLabel': 'Minimum score',
|
||||
'flows.nodeConfig.memory.minScoreHint': 'Relevance threshold from 0 to 1.',
|
||||
|
||||
// Phase 4a "New workflow" chooser + Phase 4c templates gallery. The chooser
|
||||
// offers scratch / template / describe; the gallery lists the curated
|
||||
|
||||
@@ -4263,6 +4263,7 @@ const messages: TranslationMap = {
|
||||
'flows.nodeKind.transform': 'Transformar',
|
||||
'flows.nodeKind.output_parser': 'Analizador de salida',
|
||||
'flows.nodeKind.sub_workflow': 'Subflujo de trabajo',
|
||||
'flows.nodeKind.memory': 'Memoria',
|
||||
'flows.palette.title': 'Nodos',
|
||||
'flows.palette.addNode': 'Añadir nodo {kind}',
|
||||
'flows.editor.save': 'Guardar',
|
||||
@@ -4405,6 +4406,38 @@ const messages: TranslationMap = {
|
||||
'flows.nodeConfig.code.language_javascript': 'JavaScript',
|
||||
'flows.nodeConfig.code.language_python': 'Python',
|
||||
'flows.nodeConfig.code.sourceLabel': 'Código fuente',
|
||||
'flows.nodeConfig.memory.operationLabel': 'Operación',
|
||||
'flows.nodeConfig.memory.operation_recall': 'Recordar',
|
||||
'flows.nodeConfig.memory.operation_search': 'Buscar',
|
||||
'flows.nodeConfig.memory.operation_flavour': 'Estilo',
|
||||
'flows.nodeConfig.memory.operation_people': 'Personas',
|
||||
'flows.nodeConfig.memory.operation_remember': 'Guardar',
|
||||
'flows.nodeConfig.memory.operation_forget': 'Olvidar',
|
||||
'flows.nodeConfig.memory.scopeLabel': 'Alcance',
|
||||
'flows.nodeConfig.memory.scopeHint': 'Dónde buscar esta memoria.',
|
||||
'flows.nodeConfig.memory.scopeWriteHint':
|
||||
'Solo se permite escribir dentro de este flujo de trabajo, nunca en tu memoria personal.',
|
||||
'flows.nodeConfig.memory.scope_user': 'Tú (solo lectura)',
|
||||
'flows.nodeConfig.memory.scope_flow': 'Este flujo de trabajo',
|
||||
'flows.nodeConfig.memory.scope_flows': 'Todos los flujos de trabajo (solo lectura)',
|
||||
'flows.nodeConfig.memory.queryLabel': 'Consulta',
|
||||
'flows.nodeConfig.memory.queryOptionalHint': 'Opcional: acota la búsqueda de personas.',
|
||||
'flows.nodeConfig.memory.flavourLabel': 'Estilo',
|
||||
'flows.nodeConfig.memory.flavourHint':
|
||||
'Qué faceta de la persona leer, por ejemplo communication.',
|
||||
'flows.nodeConfig.memory.flavour_communication': 'Comunicación',
|
||||
'flows.nodeConfig.memory.flavour_coding_style': 'Estilo de código',
|
||||
'flows.nodeConfig.memory.flavour_stack': 'Stack',
|
||||
'flows.nodeConfig.memory.flavour_workflow': 'Flujo de trabajo',
|
||||
'flows.nodeConfig.memory.flavour_environment': 'Entorno',
|
||||
'flows.nodeConfig.memory.flavour_directives': 'Directivas',
|
||||
'flows.nodeConfig.memory.flavour_anti_preferences': 'Preferencias negativas',
|
||||
'flows.nodeConfig.memory.keyLabel': 'Clave',
|
||||
'flows.nodeConfig.memory.valueLabel': 'Valor',
|
||||
'flows.nodeConfig.memory.limitLabel': 'Límite',
|
||||
'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.chooser.title': 'Crear un flujo de trabajo',
|
||||
'flows.chooser.subtitle': 'Elige cómo quieres empezar.',
|
||||
|
||||
@@ -4291,6 +4291,7 @@ const messages: TranslationMap = {
|
||||
'flows.nodeKind.transform': 'Transformation',
|
||||
'flows.nodeKind.output_parser': 'Analyseur de sortie',
|
||||
'flows.nodeKind.sub_workflow': 'Sous-workflow',
|
||||
'flows.nodeKind.memory': 'Mémoire',
|
||||
'flows.palette.title': 'Nœuds',
|
||||
'flows.palette.addNode': 'Ajouter un nœud {kind}',
|
||||
'flows.editor.save': 'Enregistrer',
|
||||
@@ -4435,6 +4436,38 @@ const messages: TranslationMap = {
|
||||
'flows.nodeConfig.code.language_javascript': 'JavaScript',
|
||||
'flows.nodeConfig.code.language_python': 'Python',
|
||||
'flows.nodeConfig.code.sourceLabel': 'Code source',
|
||||
'flows.nodeConfig.memory.operationLabel': 'Opération',
|
||||
'flows.nodeConfig.memory.operation_recall': 'Rappel',
|
||||
'flows.nodeConfig.memory.operation_search': 'Recherche',
|
||||
'flows.nodeConfig.memory.operation_flavour': 'Style',
|
||||
'flows.nodeConfig.memory.operation_people': 'Personnes',
|
||||
'flows.nodeConfig.memory.operation_remember': 'Mémoriser',
|
||||
'flows.nodeConfig.memory.operation_forget': 'Oublier',
|
||||
'flows.nodeConfig.memory.scopeLabel': 'Portée',
|
||||
'flows.nodeConfig.memory.scopeHint': 'Où chercher cette mémoire.',
|
||||
'flows.nodeConfig.memory.scopeWriteHint':
|
||||
"L'écriture n'est autorisée qu'au sein de ce workflow, jamais dans votre mémoire personnelle.",
|
||||
'flows.nodeConfig.memory.scope_user': 'Vous (lecture seule)',
|
||||
'flows.nodeConfig.memory.scope_flow': 'Ce workflow',
|
||||
'flows.nodeConfig.memory.scope_flows': 'Tous les workflows (lecture seule)',
|
||||
'flows.nodeConfig.memory.queryLabel': 'Requête',
|
||||
'flows.nodeConfig.memory.queryOptionalHint': 'Optionnel: affine la recherche de personnes.',
|
||||
'flows.nodeConfig.memory.flavourLabel': 'Style',
|
||||
'flows.nodeConfig.memory.flavourHint':
|
||||
'Quelle facette de la persona lire, par exemple communication.',
|
||||
'flows.nodeConfig.memory.flavour_communication': 'Communication',
|
||||
'flows.nodeConfig.memory.flavour_coding_style': 'Style de code',
|
||||
'flows.nodeConfig.memory.flavour_stack': 'Stack technique',
|
||||
'flows.nodeConfig.memory.flavour_workflow': 'Flux de travail',
|
||||
'flows.nodeConfig.memory.flavour_environment': 'Environnement',
|
||||
'flows.nodeConfig.memory.flavour_directives': 'Directives',
|
||||
'flows.nodeConfig.memory.flavour_anti_preferences': 'Préférences négatives',
|
||||
'flows.nodeConfig.memory.keyLabel': 'Clé',
|
||||
'flows.nodeConfig.memory.valueLabel': 'Valeur',
|
||||
'flows.nodeConfig.memory.limitLabel': 'Limite',
|
||||
'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.chooser.title': 'Créer un workflow',
|
||||
'flows.chooser.subtitle': 'Choisissez comment commencer.',
|
||||
|
||||
@@ -4191,6 +4191,7 @@ const messages: TranslationMap = {
|
||||
'flows.nodeKind.transform': 'रूपांतरण',
|
||||
'flows.nodeKind.output_parser': 'आउटपुट पार्सर',
|
||||
'flows.nodeKind.sub_workflow': 'सब-वर्कफ़्लो',
|
||||
'flows.nodeKind.memory': 'मेमोरी',
|
||||
'flows.palette.title': 'नोड',
|
||||
'flows.palette.addNode': '{kind} नोड जोड़ें',
|
||||
'flows.editor.save': 'सहेजें',
|
||||
@@ -4330,6 +4331,37 @@ const messages: TranslationMap = {
|
||||
'flows.nodeConfig.code.language_javascript': 'JavaScript',
|
||||
'flows.nodeConfig.code.language_python': 'Python',
|
||||
'flows.nodeConfig.code.sourceLabel': 'सोर्स कोड',
|
||||
'flows.nodeConfig.memory.operationLabel': 'ऑपरेशन',
|
||||
'flows.nodeConfig.memory.operation_recall': 'याद करें',
|
||||
'flows.nodeConfig.memory.operation_search': 'खोजें',
|
||||
'flows.nodeConfig.memory.operation_flavour': 'शैली',
|
||||
'flows.nodeConfig.memory.operation_people': 'लोग',
|
||||
'flows.nodeConfig.memory.operation_remember': 'याद रखें',
|
||||
'flows.nodeConfig.memory.operation_forget': 'भूल जाएँ',
|
||||
'flows.nodeConfig.memory.scopeLabel': 'दायरा',
|
||||
'flows.nodeConfig.memory.scopeHint': 'यह मेमोरी कहाँ खोजनी है।',
|
||||
'flows.nodeConfig.memory.scopeWriteHint':
|
||||
'लिखने की अनुमति केवल इसी वर्कफ़्लो के भीतर है, आपकी निजी मेमोरी में कभी नहीं।',
|
||||
'flows.nodeConfig.memory.scope_user': 'आप (केवल पढ़ने के लिए)',
|
||||
'flows.nodeConfig.memory.scope_flow': 'यह वर्कफ़्लो',
|
||||
'flows.nodeConfig.memory.scope_flows': 'सभी वर्कफ़्लो (केवल पढ़ने के लिए)',
|
||||
'flows.nodeConfig.memory.queryLabel': 'क्वेरी',
|
||||
'flows.nodeConfig.memory.queryOptionalHint': 'वैकल्पिक: लोगों की खोज को सीमित करता है।',
|
||||
'flows.nodeConfig.memory.flavourLabel': 'शैली',
|
||||
'flows.nodeConfig.memory.flavourHint': 'कौन सा व्यक्तित्व पहलू पढ़ना है, जैसे communication।',
|
||||
'flows.nodeConfig.memory.flavour_communication': 'संचार',
|
||||
'flows.nodeConfig.memory.flavour_coding_style': 'कोडिंग शैली',
|
||||
'flows.nodeConfig.memory.flavour_stack': 'तकनीकी स्टैक',
|
||||
'flows.nodeConfig.memory.flavour_workflow': 'वर्कफ़्लो',
|
||||
'flows.nodeConfig.memory.flavour_environment': 'वातावरण',
|
||||
'flows.nodeConfig.memory.flavour_directives': 'निर्देश',
|
||||
'flows.nodeConfig.memory.flavour_anti_preferences': 'नापसंद',
|
||||
'flows.nodeConfig.memory.keyLabel': 'कुंजी',
|
||||
'flows.nodeConfig.memory.valueLabel': 'मान',
|
||||
'flows.nodeConfig.memory.limitLabel': 'सीमा',
|
||||
'flows.nodeConfig.memory.limitHint': 'अधिकतम परिणामों की संख्या।',
|
||||
'flows.nodeConfig.memory.minScoreLabel': 'न्यूनतम स्कोर',
|
||||
'flows.nodeConfig.memory.minScoreHint': '0 से 1 तक की प्रासंगिकता सीमा।',
|
||||
|
||||
'flows.chooser.title': 'वर्कफ़्लो बनाएँ',
|
||||
'flows.chooser.subtitle': 'चुनें कि आप कैसे शुरू करना चाहते हैं।',
|
||||
|
||||
@@ -4207,6 +4207,7 @@ const messages: TranslationMap = {
|
||||
'flows.nodeKind.transform': 'Transformasi',
|
||||
'flows.nodeKind.output_parser': 'Pengurai keluaran',
|
||||
'flows.nodeKind.sub_workflow': 'Sub-alur kerja',
|
||||
'flows.nodeKind.memory': 'Memori',
|
||||
'flows.palette.title': 'Simpul',
|
||||
'flows.palette.addNode': 'Tambah simpul {kind}',
|
||||
'flows.editor.save': 'Simpan',
|
||||
@@ -4347,6 +4348,38 @@ const messages: TranslationMap = {
|
||||
'flows.nodeConfig.code.language_javascript': 'JavaScript',
|
||||
'flows.nodeConfig.code.language_python': 'Python',
|
||||
'flows.nodeConfig.code.sourceLabel': 'Kode sumber',
|
||||
'flows.nodeConfig.memory.operationLabel': 'Operasi',
|
||||
'flows.nodeConfig.memory.operation_recall': 'Ingat kembali',
|
||||
'flows.nodeConfig.memory.operation_search': 'Cari',
|
||||
'flows.nodeConfig.memory.operation_flavour': 'Gaya',
|
||||
'flows.nodeConfig.memory.operation_people': 'Orang',
|
||||
'flows.nodeConfig.memory.operation_remember': 'Simpan',
|
||||
'flows.nodeConfig.memory.operation_forget': 'Lupakan',
|
||||
'flows.nodeConfig.memory.scopeLabel': 'Cakupan',
|
||||
'flows.nodeConfig.memory.scopeHint': 'Tempat mencari memori ini.',
|
||||
'flows.nodeConfig.memory.scopeWriteHint':
|
||||
'Penulisan hanya diizinkan dalam alur kerja ini, tidak pernah ke memori pribadi Anda.',
|
||||
'flows.nodeConfig.memory.scope_user': 'Anda (hanya baca)',
|
||||
'flows.nodeConfig.memory.scope_flow': 'Alur kerja ini',
|
||||
'flows.nodeConfig.memory.scope_flows': 'Semua alur kerja (hanya baca)',
|
||||
'flows.nodeConfig.memory.queryLabel': 'Kueri',
|
||||
'flows.nodeConfig.memory.queryOptionalHint': 'Opsional: mempersempit pencarian orang.',
|
||||
'flows.nodeConfig.memory.flavourLabel': 'Gaya',
|
||||
'flows.nodeConfig.memory.flavourHint':
|
||||
'Facet persona mana yang akan dibaca, misalnya communication.',
|
||||
'flows.nodeConfig.memory.flavour_communication': 'Komunikasi',
|
||||
'flows.nodeConfig.memory.flavour_coding_style': 'Gaya coding',
|
||||
'flows.nodeConfig.memory.flavour_stack': 'Stack teknologi',
|
||||
'flows.nodeConfig.memory.flavour_workflow': 'Alur kerja',
|
||||
'flows.nodeConfig.memory.flavour_environment': 'Lingkungan',
|
||||
'flows.nodeConfig.memory.flavour_directives': 'Arahan',
|
||||
'flows.nodeConfig.memory.flavour_anti_preferences': 'Preferensi negatif',
|
||||
'flows.nodeConfig.memory.keyLabel': 'Kunci',
|
||||
'flows.nodeConfig.memory.valueLabel': 'Nilai',
|
||||
'flows.nodeConfig.memory.limitLabel': 'Batas',
|
||||
'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.chooser.title': 'Buat alur kerja',
|
||||
'flows.chooser.subtitle': 'Pilih cara Anda ingin memulai.',
|
||||
|
||||
@@ -4259,6 +4259,7 @@ const messages: TranslationMap = {
|
||||
'flows.nodeKind.transform': 'Trasformazione',
|
||||
'flows.nodeKind.output_parser': 'Analizzatore di output',
|
||||
'flows.nodeKind.sub_workflow': 'Sotto-flusso di lavoro',
|
||||
'flows.nodeKind.memory': 'Memoria',
|
||||
'flows.palette.title': 'Nodi',
|
||||
'flows.palette.addNode': 'Aggiungi nodo {kind}',
|
||||
'flows.editor.save': 'Salva',
|
||||
@@ -4403,6 +4404,38 @@ const messages: TranslationMap = {
|
||||
'flows.nodeConfig.code.language_javascript': 'JavaScript',
|
||||
'flows.nodeConfig.code.language_python': 'Python',
|
||||
'flows.nodeConfig.code.sourceLabel': 'Codice sorgente',
|
||||
'flows.nodeConfig.memory.operationLabel': 'Operazione',
|
||||
'flows.nodeConfig.memory.operation_recall': 'Richiamo',
|
||||
'flows.nodeConfig.memory.operation_search': 'Ricerca',
|
||||
'flows.nodeConfig.memory.operation_flavour': 'Stile',
|
||||
'flows.nodeConfig.memory.operation_people': 'Persone',
|
||||
'flows.nodeConfig.memory.operation_remember': 'Memorizza',
|
||||
'flows.nodeConfig.memory.operation_forget': 'Dimentica',
|
||||
'flows.nodeConfig.memory.scopeLabel': 'Ambito',
|
||||
'flows.nodeConfig.memory.scopeHint': 'Dove cercare questa memoria.',
|
||||
'flows.nodeConfig.memory.scopeWriteHint':
|
||||
"La scrittura è consentita solo all'interno di questo flusso di lavoro, mai nella tua memoria personale.",
|
||||
'flows.nodeConfig.memory.scope_user': 'Tu (sola lettura)',
|
||||
'flows.nodeConfig.memory.scope_flow': 'Questo flusso di lavoro',
|
||||
'flows.nodeConfig.memory.scope_flows': 'Tutti i flussi di lavoro (sola lettura)',
|
||||
'flows.nodeConfig.memory.queryLabel': 'Query',
|
||||
'flows.nodeConfig.memory.queryOptionalHint': 'Facoltativo: restringe la ricerca di persone.',
|
||||
'flows.nodeConfig.memory.flavourLabel': 'Stile',
|
||||
'flows.nodeConfig.memory.flavourHint':
|
||||
'Quale sfaccettatura della persona leggere, ad esempio communication.',
|
||||
'flows.nodeConfig.memory.flavour_communication': 'Comunicazione',
|
||||
'flows.nodeConfig.memory.flavour_coding_style': 'Stile di codifica',
|
||||
'flows.nodeConfig.memory.flavour_stack': 'Stack tecnologico',
|
||||
'flows.nodeConfig.memory.flavour_workflow': 'Flusso di lavoro',
|
||||
'flows.nodeConfig.memory.flavour_environment': 'Ambiente',
|
||||
'flows.nodeConfig.memory.flavour_directives': 'Direttive',
|
||||
'flows.nodeConfig.memory.flavour_anti_preferences': 'Preferenze negative',
|
||||
'flows.nodeConfig.memory.keyLabel': 'Chiave',
|
||||
'flows.nodeConfig.memory.valueLabel': 'Valore',
|
||||
'flows.nodeConfig.memory.limitLabel': 'Limite',
|
||||
'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.chooser.title': 'Crea un flusso di lavoro',
|
||||
'flows.chooser.subtitle': 'Scegli come iniziare.',
|
||||
|
||||
@@ -4144,6 +4144,7 @@ const messages: TranslationMap = {
|
||||
'flows.nodeKind.transform': '변환',
|
||||
'flows.nodeKind.output_parser': '출력 파서',
|
||||
'flows.nodeKind.sub_workflow': '하위 워크플로',
|
||||
'flows.nodeKind.memory': '메모리',
|
||||
'flows.palette.title': '노드',
|
||||
'flows.palette.addNode': '{kind} 노드 추가',
|
||||
'flows.editor.save': '저장',
|
||||
@@ -4281,6 +4282,37 @@ const messages: TranslationMap = {
|
||||
'flows.nodeConfig.code.language_javascript': 'JavaScript',
|
||||
'flows.nodeConfig.code.language_python': 'Python',
|
||||
'flows.nodeConfig.code.sourceLabel': '소스 코드',
|
||||
'flows.nodeConfig.memory.operationLabel': '작업',
|
||||
'flows.nodeConfig.memory.operation_recall': '회상',
|
||||
'flows.nodeConfig.memory.operation_search': '검색',
|
||||
'flows.nodeConfig.memory.operation_flavour': '스타일',
|
||||
'flows.nodeConfig.memory.operation_people': '사람',
|
||||
'flows.nodeConfig.memory.operation_remember': '기억하기',
|
||||
'flows.nodeConfig.memory.operation_forget': '잊기',
|
||||
'flows.nodeConfig.memory.scopeLabel': '범위',
|
||||
'flows.nodeConfig.memory.scopeHint': '이 메모리를 어디에서 찾을지 지정합니다.',
|
||||
'flows.nodeConfig.memory.scopeWriteHint':
|
||||
'쓰기는 이 워크플로 내에서만 허용되며, 개인 메모리에는 절대 저장되지 않습니다.',
|
||||
'flows.nodeConfig.memory.scope_user': '나 (읽기 전용)',
|
||||
'flows.nodeConfig.memory.scope_flow': '이 워크플로',
|
||||
'flows.nodeConfig.memory.scope_flows': '모든 워크플로 (읽기 전용)',
|
||||
'flows.nodeConfig.memory.queryLabel': '쿼리',
|
||||
'flows.nodeConfig.memory.queryOptionalHint': '선택 사항: 사람 조회 범위를 좁힙니다.',
|
||||
'flows.nodeConfig.memory.flavourLabel': '스타일',
|
||||
'flows.nodeConfig.memory.flavourHint': '읽어올 페르소나 특성입니다. 예: communication.',
|
||||
'flows.nodeConfig.memory.flavour_communication': '커뮤니케이션',
|
||||
'flows.nodeConfig.memory.flavour_coding_style': '코딩 스타일',
|
||||
'flows.nodeConfig.memory.flavour_stack': '기술 스택',
|
||||
'flows.nodeConfig.memory.flavour_workflow': '워크플로',
|
||||
'flows.nodeConfig.memory.flavour_environment': '환경',
|
||||
'flows.nodeConfig.memory.flavour_directives': '지시사항',
|
||||
'flows.nodeConfig.memory.flavour_anti_preferences': '비선호 항목',
|
||||
'flows.nodeConfig.memory.keyLabel': '키',
|
||||
'flows.nodeConfig.memory.valueLabel': '값',
|
||||
'flows.nodeConfig.memory.limitLabel': '제한',
|
||||
'flows.nodeConfig.memory.limitHint': '최대 결과 수입니다.',
|
||||
'flows.nodeConfig.memory.minScoreLabel': '최소 점수',
|
||||
'flows.nodeConfig.memory.minScoreHint': '0에서 1 사이의 관련성 임곗값입니다.',
|
||||
|
||||
'flows.chooser.title': '워크플로 만들기',
|
||||
'flows.chooser.subtitle': '시작 방법을 선택하세요.',
|
||||
|
||||
@@ -4248,6 +4248,7 @@ const messages: TranslationMap = {
|
||||
'flows.nodeKind.transform': 'Transformacja',
|
||||
'flows.nodeKind.output_parser': 'Parser wyjścia',
|
||||
'flows.nodeKind.sub_workflow': 'Podprzepływ pracy',
|
||||
'flows.nodeKind.memory': 'Pamięć',
|
||||
'flows.palette.title': 'Węzły',
|
||||
'flows.palette.addNode': 'Dodaj węzeł {kind}',
|
||||
'flows.editor.save': 'Zapisz',
|
||||
@@ -4390,6 +4391,37 @@ const messages: TranslationMap = {
|
||||
'flows.nodeConfig.code.language_javascript': 'JavaScript',
|
||||
'flows.nodeConfig.code.language_python': 'Python',
|
||||
'flows.nodeConfig.code.sourceLabel': 'Kod źródłowy',
|
||||
'flows.nodeConfig.memory.operationLabel': 'Operacja',
|
||||
'flows.nodeConfig.memory.operation_recall': 'Przywołanie',
|
||||
'flows.nodeConfig.memory.operation_search': 'Wyszukiwanie',
|
||||
'flows.nodeConfig.memory.operation_flavour': 'Styl',
|
||||
'flows.nodeConfig.memory.operation_people': 'Osoby',
|
||||
'flows.nodeConfig.memory.operation_remember': 'Zapamiętaj',
|
||||
'flows.nodeConfig.memory.operation_forget': 'Zapomnij',
|
||||
'flows.nodeConfig.memory.scopeLabel': 'Zakres',
|
||||
'flows.nodeConfig.memory.scopeHint': 'Gdzie szukać tej pamięci.',
|
||||
'flows.nodeConfig.memory.scopeWriteHint':
|
||||
'Zapis jest dozwolony wyłącznie w obrębie tego przepływu pracy, nigdy w pamięci osobistej.',
|
||||
'flows.nodeConfig.memory.scope_user': 'Ty (tylko do odczytu)',
|
||||
'flows.nodeConfig.memory.scope_flow': 'Ten przepływ pracy',
|
||||
'flows.nodeConfig.memory.scope_flows': 'Wszystkie przepływy pracy (tylko do odczytu)',
|
||||
'flows.nodeConfig.memory.queryLabel': 'Zapytanie',
|
||||
'flows.nodeConfig.memory.queryOptionalHint': 'Opcjonalnie: zawęża wyszukiwanie osób.',
|
||||
'flows.nodeConfig.memory.flavourLabel': 'Styl',
|
||||
'flows.nodeConfig.memory.flavourHint': 'Który aspekt persony odczytać, np. communication.',
|
||||
'flows.nodeConfig.memory.flavour_communication': 'Komunikacja',
|
||||
'flows.nodeConfig.memory.flavour_coding_style': 'Styl kodowania',
|
||||
'flows.nodeConfig.memory.flavour_stack': 'Stos technologiczny',
|
||||
'flows.nodeConfig.memory.flavour_workflow': 'Przepływ pracy',
|
||||
'flows.nodeConfig.memory.flavour_environment': 'Środowisko',
|
||||
'flows.nodeConfig.memory.flavour_directives': 'Wytyczne',
|
||||
'flows.nodeConfig.memory.flavour_anti_preferences': 'Preferencje negatywne',
|
||||
'flows.nodeConfig.memory.keyLabel': 'Klucz',
|
||||
'flows.nodeConfig.memory.valueLabel': 'Wartość',
|
||||
'flows.nodeConfig.memory.limitLabel': 'Limit',
|
||||
'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.chooser.title': 'Utwórz przepływ pracy',
|
||||
'flows.chooser.subtitle': 'Wybierz, jak chcesz zacząć.',
|
||||
|
||||
@@ -4252,6 +4252,7 @@ const messages: TranslationMap = {
|
||||
'flows.nodeKind.transform': 'Transformação',
|
||||
'flows.nodeKind.output_parser': 'Analisador de saída',
|
||||
'flows.nodeKind.sub_workflow': 'Subfluxo de trabalho',
|
||||
'flows.nodeKind.memory': 'Memória',
|
||||
'flows.palette.title': 'Nós',
|
||||
'flows.palette.addNode': 'Adicionar nó {kind}',
|
||||
'flows.editor.save': 'Salvar',
|
||||
@@ -4394,6 +4395,37 @@ const messages: TranslationMap = {
|
||||
'flows.nodeConfig.code.language_javascript': 'JavaScript',
|
||||
'flows.nodeConfig.code.language_python': 'Python',
|
||||
'flows.nodeConfig.code.sourceLabel': 'Código-fonte',
|
||||
'flows.nodeConfig.memory.operationLabel': 'Operação',
|
||||
'flows.nodeConfig.memory.operation_recall': 'Recordar',
|
||||
'flows.nodeConfig.memory.operation_search': 'Pesquisar',
|
||||
'flows.nodeConfig.memory.operation_flavour': 'Estilo',
|
||||
'flows.nodeConfig.memory.operation_people': 'Pessoas',
|
||||
'flows.nodeConfig.memory.operation_remember': 'Lembrar',
|
||||
'flows.nodeConfig.memory.operation_forget': 'Esquecer',
|
||||
'flows.nodeConfig.memory.scopeLabel': 'Escopo',
|
||||
'flows.nodeConfig.memory.scopeHint': 'Onde procurar esta memória.',
|
||||
'flows.nodeConfig.memory.scopeWriteHint':
|
||||
'A escrita só é permitida dentro deste fluxo de trabalho, nunca na sua memória pessoal.',
|
||||
'flows.nodeConfig.memory.scope_user': 'Você (somente leitura)',
|
||||
'flows.nodeConfig.memory.scope_flow': 'Este fluxo de trabalho',
|
||||
'flows.nodeConfig.memory.scope_flows': 'Todos os fluxos de trabalho (somente leitura)',
|
||||
'flows.nodeConfig.memory.queryLabel': 'Consulta',
|
||||
'flows.nodeConfig.memory.queryOptionalHint': 'Opcional: restringe a busca de pessoas.',
|
||||
'flows.nodeConfig.memory.flavourLabel': 'Estilo',
|
||||
'flows.nodeConfig.memory.flavourHint': 'Qual faceta da persona ler, por exemplo communication.',
|
||||
'flows.nodeConfig.memory.flavour_communication': 'Comunicação',
|
||||
'flows.nodeConfig.memory.flavour_coding_style': 'Estilo de código',
|
||||
'flows.nodeConfig.memory.flavour_stack': 'Stack técnica',
|
||||
'flows.nodeConfig.memory.flavour_workflow': 'Fluxo de trabalho',
|
||||
'flows.nodeConfig.memory.flavour_environment': 'Ambiente',
|
||||
'flows.nodeConfig.memory.flavour_directives': 'Diretrizes',
|
||||
'flows.nodeConfig.memory.flavour_anti_preferences': 'Preferências negativas',
|
||||
'flows.nodeConfig.memory.keyLabel': 'Chave',
|
||||
'flows.nodeConfig.memory.valueLabel': 'Valor',
|
||||
'flows.nodeConfig.memory.limitLabel': 'Limite',
|
||||
'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.chooser.title': 'Criar um fluxo de trabalho',
|
||||
'flows.chooser.subtitle': 'Escolha como quer começar.',
|
||||
|
||||
@@ -4230,6 +4230,7 @@ const messages: TranslationMap = {
|
||||
'flows.nodeKind.transform': 'Преобразование',
|
||||
'flows.nodeKind.output_parser': 'Парсер вывода',
|
||||
'flows.nodeKind.sub_workflow': 'Подпроцесс',
|
||||
'flows.nodeKind.memory': 'Память',
|
||||
'flows.palette.title': 'Узлы',
|
||||
'flows.palette.addNode': 'Добавить узел {kind}',
|
||||
'flows.editor.save': 'Сохранить',
|
||||
@@ -4374,6 +4375,37 @@ const messages: TranslationMap = {
|
||||
'flows.nodeConfig.code.language_javascript': 'JavaScript',
|
||||
'flows.nodeConfig.code.language_python': 'Python',
|
||||
'flows.nodeConfig.code.sourceLabel': 'Исходный код',
|
||||
'flows.nodeConfig.memory.operationLabel': 'Операция',
|
||||
'flows.nodeConfig.memory.operation_recall': 'Вспомнить',
|
||||
'flows.nodeConfig.memory.operation_search': 'Поиск',
|
||||
'flows.nodeConfig.memory.operation_flavour': 'Стиль',
|
||||
'flows.nodeConfig.memory.operation_people': 'Люди',
|
||||
'flows.nodeConfig.memory.operation_remember': 'Запомнить',
|
||||
'flows.nodeConfig.memory.operation_forget': 'Забыть',
|
||||
'flows.nodeConfig.memory.scopeLabel': 'Область',
|
||||
'flows.nodeConfig.memory.scopeHint': 'Где искать эту память.',
|
||||
'flows.nodeConfig.memory.scopeWriteHint':
|
||||
'Запись разрешена только в пределах этого рабочего процесса, но никогда в вашей личной памяти.',
|
||||
'flows.nodeConfig.memory.scope_user': 'Вы (только чтение)',
|
||||
'flows.nodeConfig.memory.scope_flow': 'Этот рабочий процесс',
|
||||
'flows.nodeConfig.memory.scope_flows': 'Все рабочие процессы (только чтение)',
|
||||
'flows.nodeConfig.memory.queryLabel': 'Запрос',
|
||||
'flows.nodeConfig.memory.queryOptionalHint': 'Необязательно: сужает поиск людей.',
|
||||
'flows.nodeConfig.memory.flavourLabel': 'Стиль',
|
||||
'flows.nodeConfig.memory.flavourHint': 'Какую грань личности прочитать, например communication.',
|
||||
'flows.nodeConfig.memory.flavour_communication': 'Общение',
|
||||
'flows.nodeConfig.memory.flavour_coding_style': 'Стиль кода',
|
||||
'flows.nodeConfig.memory.flavour_stack': 'Технологический стек',
|
||||
'flows.nodeConfig.memory.flavour_workflow': 'Рабочий процесс',
|
||||
'flows.nodeConfig.memory.flavour_environment': 'Окружение',
|
||||
'flows.nodeConfig.memory.flavour_directives': 'Директивы',
|
||||
'flows.nodeConfig.memory.flavour_anti_preferences': 'Нежелательные предпочтения',
|
||||
'flows.nodeConfig.memory.keyLabel': 'Ключ',
|
||||
'flows.nodeConfig.memory.valueLabel': 'Значение',
|
||||
'flows.nodeConfig.memory.limitLabel': 'Лимит',
|
||||
'flows.nodeConfig.memory.limitHint': 'Максимальное количество результатов.',
|
||||
'flows.nodeConfig.memory.minScoreLabel': 'Минимальная оценка',
|
||||
'flows.nodeConfig.memory.minScoreHint': 'Порог релевантности от 0 до 1.',
|
||||
|
||||
'flows.chooser.title': 'Создать рабочий процесс',
|
||||
'flows.chooser.subtitle': 'Выберите, с чего начать.',
|
||||
|
||||
@@ -3972,6 +3972,7 @@ const messages: TranslationMap = {
|
||||
'flows.nodeKind.transform': '转换',
|
||||
'flows.nodeKind.output_parser': '输出解析器',
|
||||
'flows.nodeKind.sub_workflow': '子工作流',
|
||||
'flows.nodeKind.memory': '记忆',
|
||||
'flows.palette.title': '节点',
|
||||
'flows.palette.addNode': '添加{kind}节点',
|
||||
'flows.editor.save': '保存',
|
||||
@@ -4103,6 +4104,36 @@ const messages: TranslationMap = {
|
||||
'flows.nodeConfig.code.language_javascript': 'JavaScript',
|
||||
'flows.nodeConfig.code.language_python': 'Python',
|
||||
'flows.nodeConfig.code.sourceLabel': '源代码',
|
||||
'flows.nodeConfig.memory.operationLabel': '操作',
|
||||
'flows.nodeConfig.memory.operation_recall': '回忆',
|
||||
'flows.nodeConfig.memory.operation_search': '搜索',
|
||||
'flows.nodeConfig.memory.operation_flavour': '风格',
|
||||
'flows.nodeConfig.memory.operation_people': '人物',
|
||||
'flows.nodeConfig.memory.operation_remember': '记住',
|
||||
'flows.nodeConfig.memory.operation_forget': '忘记',
|
||||
'flows.nodeConfig.memory.scopeLabel': '范围',
|
||||
'flows.nodeConfig.memory.scopeHint': '在哪里查找这条记忆。',
|
||||
'flows.nodeConfig.memory.scopeWriteHint': '写入仅允许在此工作流内进行,绝不会写入你的个人记忆。',
|
||||
'flows.nodeConfig.memory.scope_user': '你(只读)',
|
||||
'flows.nodeConfig.memory.scope_flow': '此工作流',
|
||||
'flows.nodeConfig.memory.scope_flows': '所有工作流(只读)',
|
||||
'flows.nodeConfig.memory.queryLabel': '查询',
|
||||
'flows.nodeConfig.memory.queryOptionalHint': '可选:缩小人物查找范围。',
|
||||
'flows.nodeConfig.memory.flavourLabel': '风格',
|
||||
'flows.nodeConfig.memory.flavourHint': '要读取哪个人格面向,例如 communication。',
|
||||
'flows.nodeConfig.memory.flavour_communication': '沟通',
|
||||
'flows.nodeConfig.memory.flavour_coding_style': '编码风格',
|
||||
'flows.nodeConfig.memory.flavour_stack': '技术栈',
|
||||
'flows.nodeConfig.memory.flavour_workflow': '工作流程',
|
||||
'flows.nodeConfig.memory.flavour_environment': '环境',
|
||||
'flows.nodeConfig.memory.flavour_directives': '指令',
|
||||
'flows.nodeConfig.memory.flavour_anti_preferences': '负向偏好',
|
||||
'flows.nodeConfig.memory.keyLabel': '键',
|
||||
'flows.nodeConfig.memory.valueLabel': '值',
|
||||
'flows.nodeConfig.memory.limitLabel': '数量上限',
|
||||
'flows.nodeConfig.memory.limitHint': '返回结果的最大数量。',
|
||||
'flows.nodeConfig.memory.minScoreLabel': '最低分数',
|
||||
'flows.nodeConfig.memory.minScoreHint': '相关性阈值,范围为 0 到 1。',
|
||||
|
||||
'flows.chooser.title': '创建工作流',
|
||||
'flows.chooser.subtitle': '选择你想要的开始方式。',
|
||||
|
||||
Reference in New Issue
Block a user