mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
fix(flows): show plain-language step labels in the workflow proposal card (#5088)
This commit is contained in:
@@ -56,17 +56,89 @@ describe('WorkflowProposalCard', () => {
|
||||
mockNavigate.mockReset();
|
||||
});
|
||||
|
||||
it('renders the name, trigger, and steps with node-kind badges', () => {
|
||||
it('renders the name, trigger, and steps with plain-language node-kind badges', () => {
|
||||
render(<WorkflowProposalCard threadId="t1" proposal={proposal()} />);
|
||||
expect(screen.getByText('Daily standup summary')).toBeInTheDocument();
|
||||
expect(screen.getByText('schedule: 0 9 * * *')).toBeInTheDocument();
|
||||
expect(screen.getByText('Summarize')).toBeInTheDocument();
|
||||
expect(screen.getByText('Post to Slack')).toBeInTheDocument();
|
||||
expect(screen.getByText('agent')).toBeInTheDocument();
|
||||
expect(screen.getByText('tool_call')).toBeInTheDocument();
|
||||
// Badges show the friendly i18n key (useT is mocked to echo the key),
|
||||
// never the raw snake_case wire `kind`.
|
||||
expect(screen.getByText('chat.flowProposal.stepKind.agent')).toBeInTheDocument();
|
||||
expect(screen.getByText('chat.flowProposal.stepKind.toolCall')).toBeInTheDocument();
|
||||
expect(screen.queryByText('agent')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('tool_call')).not.toBeInTheDocument();
|
||||
expect(screen.getAllByTestId('workflow-proposal-step-kind')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('hides config_hint from the card even when present on the step', () => {
|
||||
render(<WorkflowProposalCard threadId="t1" proposal={proposal()} />);
|
||||
// `proposal()` sets a config_hint on the first step; the card must not
|
||||
// surface it (it can leak internal identifiers like tool slugs or URLs).
|
||||
expect(screen.queryByText("Summarize yesterday's messages")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('falls back to a humanized label for an unrecognized step kind', () => {
|
||||
render(
|
||||
<WorkflowProposalCard
|
||||
threadId="t1"
|
||||
proposal={proposal({
|
||||
summary: {
|
||||
trigger: 'manual',
|
||||
steps: [{ kind: 'future_thing', name: 'Do the new thing' }],
|
||||
},
|
||||
})}
|
||||
/>
|
||||
);
|
||||
// Not in STEP_KIND_I18N_KEYS, so it must fall back to the pure
|
||||
// capitalize + underscores-to-spaces helper rather than the raw kind.
|
||||
expect(screen.getByText('Future thing')).toBeInTheDocument();
|
||||
expect(screen.queryByText('future_thing')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
// `step.kind` is arbitrary wire data. A plain bracket index would resolve
|
||||
// inherited Object members (e.g. `constructor` -> the Object function) and
|
||||
// hand a non-string to t(), breaking the badge render. The own-property
|
||||
// guard must send these through the humanized fallback instead. `constructor`
|
||||
// and `toString` have no underscores, so the humanized label is just the
|
||||
// capitalized kind.
|
||||
it.each(['constructor', 'toString'])(
|
||||
'humanizes the inherited-property kind %s instead of resolving it on the prototype',
|
||||
kind => {
|
||||
render(
|
||||
<WorkflowProposalCard
|
||||
threadId="t1"
|
||||
proposal={proposal({
|
||||
summary: { trigger: 'manual', steps: [{ kind, name: 'Edge-case step' }] },
|
||||
})}
|
||||
/>
|
||||
);
|
||||
const expected = kind.charAt(0).toUpperCase() + kind.slice(1);
|
||||
expect(screen.getByText(expected)).toBeInTheDocument();
|
||||
expect(screen.getByText('Edge-case step')).toBeInTheDocument();
|
||||
}
|
||||
);
|
||||
|
||||
it('renders a __proto__ step kind without leaking an inherited property', () => {
|
||||
render(
|
||||
<WorkflowProposalCard
|
||||
threadId="t1"
|
||||
proposal={proposal({
|
||||
summary: { trigger: 'manual', steps: [{ kind: '__proto__', name: 'Proto step' }] },
|
||||
})}
|
||||
/>
|
||||
);
|
||||
// The own-property guard treats __proto__ as unknown and humanizes it, so
|
||||
// the row still renders (step name present) and the badge shows a real
|
||||
// string. Assert the badge's exact (whitespace-normalized) content directly
|
||||
// — not just the step name / absence of "function" — so a missing badge or
|
||||
// an inherited-member leak like `[object Object]` would fail here too.
|
||||
// `__proto__` humanizes (underscores -> spaces, first char already a space)
|
||||
// to a lowercase "proto" badge.
|
||||
expect(screen.getByText('Proto step')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('workflow-proposal-step-kind')).toHaveTextContent(/^proto$/);
|
||||
});
|
||||
|
||||
it('has the expected root test id', () => {
|
||||
render(<WorkflowProposalCard threadId="t1" proposal={proposal()} />);
|
||||
expect(screen.getByTestId('workflow-proposal-card')).toBeInTheDocument();
|
||||
|
||||
@@ -16,6 +16,50 @@ import Button from '../ui/Button';
|
||||
|
||||
const log = debug('openhuman:chat:workflow-proposal-card');
|
||||
|
||||
// Maps the wire `step.kind` (a `tinyflows` node type, snake_case, e.g.
|
||||
// `tool_call`) to the i18n key for its plain-language badge label. Kinds not
|
||||
// in this map (e.g. a future node type the frontend doesn't know about yet)
|
||||
// fall back to `humanizeUnknownStepKind` below rather than showing the raw
|
||||
// snake_case identifier to a non-technical user.
|
||||
const STEP_KIND_I18N_KEYS: Record<string, string> = {
|
||||
agent: 'chat.flowProposal.stepKind.agent',
|
||||
tool_call: 'chat.flowProposal.stepKind.toolCall',
|
||||
http_request: 'chat.flowProposal.stepKind.httpRequest',
|
||||
code: 'chat.flowProposal.stepKind.code',
|
||||
condition: 'chat.flowProposal.stepKind.condition',
|
||||
switch: 'chat.flowProposal.stepKind.switch',
|
||||
merge: 'chat.flowProposal.stepKind.merge',
|
||||
split_out: 'chat.flowProposal.stepKind.splitOut',
|
||||
transform: 'chat.flowProposal.stepKind.transform',
|
||||
output_parser: 'chat.flowProposal.stepKind.outputParser',
|
||||
sub_workflow: 'chat.flowProposal.stepKind.subWorkflow',
|
||||
};
|
||||
|
||||
/**
|
||||
* Pure fallback for a `step.kind` the frontend doesn't recognize: capitalize
|
||||
* the first letter and turn `_` into spaces (e.g. `future_thing` ->
|
||||
* `Future thing`) so an unmapped kind still reads as plain language instead
|
||||
* of a raw snake_case identifier.
|
||||
*/
|
||||
function humanizeUnknownStepKind(kind: string): string {
|
||||
const spaced = kind.replace(/_/g, ' ');
|
||||
return spaced.charAt(0).toUpperCase() + spaced.slice(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the i18n key for a mapped `step.kind`, guarding against inherited
|
||||
* Object properties. `step.kind` is arbitrary wire data, so a plain bracket
|
||||
* index (`STEP_KIND_I18N_KEYS[step.kind]`) would resolve inherited members for
|
||||
* values like `constructor` or `__proto__` — handing a function/object to
|
||||
* `t()` and breaking the badge render. Only own keys count; anything else
|
||||
* returns `undefined` so the caller falls back to `humanizeUnknownStepKind`.
|
||||
*/
|
||||
function stepKindI18nKey(kind: string): string | undefined {
|
||||
return Object.prototype.hasOwnProperty.call(STEP_KIND_I18N_KEYS, kind)
|
||||
? STEP_KIND_I18N_KEYS[kind]
|
||||
: undefined;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
threadId: string;
|
||||
proposal: WorkflowProposal;
|
||||
@@ -183,23 +227,22 @@ export const WorkflowProposalCard: React.FC<Props> = ({ threadId, proposal, onSa
|
||||
</p>
|
||||
{proposal.summary.steps.length > 0 ? (
|
||||
<ol className="mt-1 max-h-56 list-decimal overflow-y-auto pl-6 text-content-secondary">
|
||||
{proposal.summary.steps.map((step, i) => (
|
||||
<li key={i} className="break-words">
|
||||
<span
|
||||
data-testid="workflow-proposal-step-kind"
|
||||
className="mr-1.5 inline-block rounded-full bg-ocean-100 px-1.5 py-0.5 text-[10px] font-medium text-ocean-700 dark:bg-ocean-500/15 dark:text-ocean-300">
|
||||
{step.kind}
|
||||
</span>
|
||||
<span>{step.name}</span>
|
||||
{step.config_hint ? (
|
||||
{proposal.summary.steps.map((step, i) => {
|
||||
const kindI18nKey = stepKindI18nKey(step.kind);
|
||||
const kindLabel = kindI18nKey
|
||||
? t(kindI18nKey)
|
||||
: humanizeUnknownStepKind(step.kind);
|
||||
return (
|
||||
<li key={i} className="break-words">
|
||||
<span
|
||||
title={step.config_hint}
|
||||
className="ml-1.5 inline-block max-w-full truncate rounded bg-surface-subtle px-1 py-px align-middle font-mono text-[11px] text-content-muted">
|
||||
{step.config_hint}
|
||||
data-testid="workflow-proposal-step-kind"
|
||||
className="mr-1.5 inline-block rounded-full bg-ocean-100 px-1.5 py-0.5 text-[10px] font-medium text-ocean-700 dark:bg-ocean-500/15 dark:text-ocean-300">
|
||||
{kindLabel}
|
||||
</span>
|
||||
) : null}
|
||||
</li>
|
||||
))}
|
||||
<span>{step.name}</span>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
) : (
|
||||
<p className="mt-1 text-xs text-content-faint">{t('chat.flowProposal.noSteps')}</p>
|
||||
|
||||
@@ -3415,6 +3415,17 @@ const messages: TranslationMap = {
|
||||
'chat.flowProposal.error': 'تعذّر حفظ سير العمل. حاول مرة أخرى.',
|
||||
'chat.flowProposal.enableError':
|
||||
'تم حفظ سير العمل، لكن تعذّر تفعيله. حاول مرة أخرى، أو فعّله من صفحة سير العمل.',
|
||||
'chat.flowProposal.stepKind.agent': 'وكيل',
|
||||
'chat.flowProposal.stepKind.toolCall': 'إجراء',
|
||||
'chat.flowProposal.stepKind.httpRequest': 'طلب ويب',
|
||||
'chat.flowProposal.stepKind.code': 'تشغيل الكود',
|
||||
'chat.flowProposal.stepKind.condition': 'شرط',
|
||||
'chat.flowProposal.stepKind.switch': 'تبديل',
|
||||
'chat.flowProposal.stepKind.merge': 'دمج',
|
||||
'chat.flowProposal.stepKind.splitOut': 'تقسيم',
|
||||
'chat.flowProposal.stepKind.transform': 'تحويل',
|
||||
'chat.flowProposal.stepKind.outputParser': 'تحليل الإخراج',
|
||||
'chat.flowProposal.stepKind.subWorkflow': 'مسار عمل فرعي',
|
||||
'channels.authMode.managed_dm': 'قم بتسجيل الدخول باستخدام OpenHuman',
|
||||
'channels.authMode.oauth': 'OAuth تسجيل الدخول',
|
||||
'channels.authMode.bot_token': 'استخدم رمز الروبوت الخاص بك',
|
||||
|
||||
@@ -3494,6 +3494,17 @@ const messages: TranslationMap = {
|
||||
'chat.flowProposal.error': 'ওয়ার্কফ্লো সংরক্ষণ করা যায়নি। আবার চেষ্টা করুন।',
|
||||
'chat.flowProposal.enableError':
|
||||
'ওয়ার্কফ্লো সংরক্ষিত হয়েছে, কিন্তু সক্ষম করা যায়নি। আবার চেষ্টা করুন, বা Workflows পৃষ্ঠা থেকে সক্ষম করুন।',
|
||||
'chat.flowProposal.stepKind.agent': 'এজেন্ট',
|
||||
'chat.flowProposal.stepKind.toolCall': 'কার্যক্রম',
|
||||
'chat.flowProposal.stepKind.httpRequest': 'ওয়েব অনুরোধ',
|
||||
'chat.flowProposal.stepKind.code': 'কোড চালান',
|
||||
'chat.flowProposal.stepKind.condition': 'শর্ত',
|
||||
'chat.flowProposal.stepKind.switch': 'সুইচ',
|
||||
'chat.flowProposal.stepKind.merge': 'একত্রিত করুন',
|
||||
'chat.flowProposal.stepKind.splitOut': 'বিভক্ত করুন',
|
||||
'chat.flowProposal.stepKind.transform': 'রূপান্তর করুন',
|
||||
'chat.flowProposal.stepKind.outputParser': 'আউটপুট পার্স করুন',
|
||||
'chat.flowProposal.stepKind.subWorkflow': 'সাব-ওয়ার্কফ্লো',
|
||||
'channels.authMode.managed_dm': 'OpenHuman দিয়ে লগইন করুন',
|
||||
'channels.authMode.oauth': 'OAuth সাইন-ইন করুন',
|
||||
'channels.authMode.bot_token': 'আপনার নিজের বট টোকেন ব্যবহার করুন',
|
||||
|
||||
@@ -3596,6 +3596,17 @@ const messages: TranslationMap = {
|
||||
'Der Workflow konnte nicht gespeichert werden. Bitte versuchen Sie es erneut.',
|
||||
'chat.flowProposal.enableError':
|
||||
'Workflow gespeichert, konnte aber nicht aktiviert werden. Versuchen Sie es erneut oder aktivieren Sie ihn auf der Workflows-Seite.',
|
||||
'chat.flowProposal.stepKind.agent': 'Agent',
|
||||
'chat.flowProposal.stepKind.toolCall': 'Aktion',
|
||||
'chat.flowProposal.stepKind.httpRequest': 'Webanfrage',
|
||||
'chat.flowProposal.stepKind.code': 'Code ausführen',
|
||||
'chat.flowProposal.stepKind.condition': 'Bedingung',
|
||||
'chat.flowProposal.stepKind.switch': 'Weiche',
|
||||
'chat.flowProposal.stepKind.merge': 'Zusammenführen',
|
||||
'chat.flowProposal.stepKind.splitOut': 'Aufteilen',
|
||||
'chat.flowProposal.stepKind.transform': 'Transformieren',
|
||||
'chat.flowProposal.stepKind.outputParser': 'Ausgabe auswerten',
|
||||
'chat.flowProposal.stepKind.subWorkflow': 'Unter-Workflow',
|
||||
'channels.authMode.managed_dm': 'Mit OpenHuman anmelden',
|
||||
'channels.authMode.oauth': 'OAuth-Anmeldung',
|
||||
'channels.authMode.bot_token': 'Eigenen Bot-Token verwenden',
|
||||
|
||||
@@ -3899,6 +3899,20 @@ const en: TranslationMap = {
|
||||
'chat.flowProposal.error': 'Could not save the workflow. Please try again.',
|
||||
'chat.flowProposal.enableError':
|
||||
'Workflow saved, but could not enable it. Try again, or enable it from the Workflows page.',
|
||||
// Plain-language labels for each `tinyflows` node kind, shown as the badge
|
||||
// next to each step in the proposal card's step list. Keep names short:
|
||||
// they render as small pill badges.
|
||||
'chat.flowProposal.stepKind.agent': 'Agent',
|
||||
'chat.flowProposal.stepKind.toolCall': 'Action',
|
||||
'chat.flowProposal.stepKind.httpRequest': 'Web request',
|
||||
'chat.flowProposal.stepKind.code': 'Run code',
|
||||
'chat.flowProposal.stepKind.condition': 'Condition',
|
||||
'chat.flowProposal.stepKind.switch': 'Switch',
|
||||
'chat.flowProposal.stepKind.merge': 'Merge',
|
||||
'chat.flowProposal.stepKind.splitOut': 'Split',
|
||||
'chat.flowProposal.stepKind.transform': 'Transform',
|
||||
'chat.flowProposal.stepKind.outputParser': 'Parse output',
|
||||
'chat.flowProposal.stepKind.subWorkflow': 'Sub-workflow',
|
||||
|
||||
// Auth mode labels
|
||||
'channels.authMode.managed_dm': 'Login with OpenHuman',
|
||||
|
||||
@@ -3558,6 +3558,17 @@ const messages: TranslationMap = {
|
||||
'chat.flowProposal.error': 'No se pudo guardar el flujo de trabajo. Inténtalo de nuevo.',
|
||||
'chat.flowProposal.enableError':
|
||||
'Flujo de trabajo guardado, pero no se pudo activar. Inténtalo de nuevo o actívalo desde la página de Workflows.',
|
||||
'chat.flowProposal.stepKind.agent': 'Agente',
|
||||
'chat.flowProposal.stepKind.toolCall': 'Acción',
|
||||
'chat.flowProposal.stepKind.httpRequest': 'Solicitud web',
|
||||
'chat.flowProposal.stepKind.code': 'Ejecutar código',
|
||||
'chat.flowProposal.stepKind.condition': 'Condición',
|
||||
'chat.flowProposal.stepKind.switch': 'Selector',
|
||||
'chat.flowProposal.stepKind.merge': 'Combinar',
|
||||
'chat.flowProposal.stepKind.splitOut': 'Dividir',
|
||||
'chat.flowProposal.stepKind.transform': 'Transformar',
|
||||
'chat.flowProposal.stepKind.outputParser': 'Interpretar resultado',
|
||||
'chat.flowProposal.stepKind.subWorkflow': 'Subflujo de trabajo',
|
||||
'channels.authMode.managed_dm': 'Iniciar sesión con OpenHuman',
|
||||
'channels.authMode.oauth': 'OAuth Iniciar sesión',
|
||||
'channels.authMode.bot_token': 'Utilice su propio token de bot',
|
||||
|
||||
@@ -3581,6 +3581,17 @@ const messages: TranslationMap = {
|
||||
'chat.flowProposal.error': "Impossible d'enregistrer le workflow. Veuillez réessayer.",
|
||||
'chat.flowProposal.enableError':
|
||||
"Workflow enregistré, mais impossible de l'activer. Réessayez, ou activez-le depuis la page Workflows.",
|
||||
'chat.flowProposal.stepKind.agent': 'Agent',
|
||||
'chat.flowProposal.stepKind.toolCall': 'Action',
|
||||
'chat.flowProposal.stepKind.httpRequest': 'Requête web',
|
||||
'chat.flowProposal.stepKind.code': 'Exécuter du code',
|
||||
'chat.flowProposal.stepKind.condition': 'Condition',
|
||||
'chat.flowProposal.stepKind.switch': 'Sélecteur',
|
||||
'chat.flowProposal.stepKind.merge': 'Fusionner',
|
||||
'chat.flowProposal.stepKind.splitOut': 'Diviser',
|
||||
'chat.flowProposal.stepKind.transform': 'Transformer',
|
||||
'chat.flowProposal.stepKind.outputParser': 'Analyser le résultat',
|
||||
'chat.flowProposal.stepKind.subWorkflow': 'Sous-workflow',
|
||||
'channels.authMode.managed_dm': 'Connectez-vous avec OpenHuman',
|
||||
'channels.authMode.oauth': 'OAuth Connectez-vous',
|
||||
'channels.authMode.bot_token': 'Utiliser votre propre jeton de robot',
|
||||
|
||||
@@ -3493,6 +3493,17 @@ const messages: TranslationMap = {
|
||||
'chat.flowProposal.error': 'वर्कफ़्लो सहेजा नहीं जा सका। कृपया फिर से प्रयास करें।',
|
||||
'chat.flowProposal.enableError':
|
||||
'वर्कफ़्लो सहेजा गया, लेकिन सक्षम नहीं किया जा सका। फिर से प्रयास करें, या Workflows पेज से इसे सक्षम करें।',
|
||||
'chat.flowProposal.stepKind.agent': 'एजेंट',
|
||||
'chat.flowProposal.stepKind.toolCall': 'कार्रवाई',
|
||||
'chat.flowProposal.stepKind.httpRequest': 'वेब अनुरोध',
|
||||
'chat.flowProposal.stepKind.code': 'कोड चलाएं',
|
||||
'chat.flowProposal.stepKind.condition': 'शर्त',
|
||||
'chat.flowProposal.stepKind.switch': 'स्विच',
|
||||
'chat.flowProposal.stepKind.merge': 'मर्ज करें',
|
||||
'chat.flowProposal.stepKind.splitOut': 'विभाजित करें',
|
||||
'chat.flowProposal.stepKind.transform': 'रूपांतरित करें',
|
||||
'chat.flowProposal.stepKind.outputParser': 'आउटपुट पार्स करें',
|
||||
'chat.flowProposal.stepKind.subWorkflow': 'सब-वर्कफ़्लो',
|
||||
'channels.authMode.managed_dm': 'OpenHuman से लॉगिन करें',
|
||||
'channels.authMode.oauth': 'OAuth साइन-इन करें',
|
||||
'channels.authMode.bot_token': 'अपने स्वयं के बॉट टोकन का उपयोग करें',
|
||||
|
||||
@@ -3510,6 +3510,17 @@ const messages: TranslationMap = {
|
||||
'chat.flowProposal.error': 'Alur kerja tidak dapat disimpan. Silakan coba lagi.',
|
||||
'chat.flowProposal.enableError':
|
||||
'Alur kerja disimpan, tetapi tidak dapat diaktifkan. Coba lagi, atau aktifkan dari halaman Workflows.',
|
||||
'chat.flowProposal.stepKind.agent': 'Agen',
|
||||
'chat.flowProposal.stepKind.toolCall': 'Tindakan',
|
||||
'chat.flowProposal.stepKind.httpRequest': 'Permintaan web',
|
||||
'chat.flowProposal.stepKind.code': 'Jalankan kode',
|
||||
'chat.flowProposal.stepKind.condition': 'Kondisi',
|
||||
'chat.flowProposal.stepKind.switch': 'Pengalih',
|
||||
'chat.flowProposal.stepKind.merge': 'Gabungkan',
|
||||
'chat.flowProposal.stepKind.splitOut': 'Pisahkan',
|
||||
'chat.flowProposal.stepKind.transform': 'Transformasikan',
|
||||
'chat.flowProposal.stepKind.outputParser': 'Uraikan output',
|
||||
'chat.flowProposal.stepKind.subWorkflow': 'Sub-alur kerja',
|
||||
'channels.authMode.managed_dm': 'Masuk dengan OpenHuman',
|
||||
'channels.authMode.oauth': 'OAuth Masuk',
|
||||
'channels.authMode.bot_token': 'Gunakan Token Bot Anda sendiri',
|
||||
|
||||
@@ -3557,6 +3557,17 @@ const messages: TranslationMap = {
|
||||
'chat.flowProposal.error': 'Impossibile salvare il workflow. Riprova.',
|
||||
'chat.flowProposal.enableError':
|
||||
'Workflow salvato, ma non è stato possibile attivarlo. Riprova, oppure attivalo dalla pagina Workflows.',
|
||||
'chat.flowProposal.stepKind.agent': 'Agente',
|
||||
'chat.flowProposal.stepKind.toolCall': 'Azione',
|
||||
'chat.flowProposal.stepKind.httpRequest': 'Richiesta web',
|
||||
'chat.flowProposal.stepKind.code': 'Esegui codice',
|
||||
'chat.flowProposal.stepKind.condition': 'Condizione',
|
||||
'chat.flowProposal.stepKind.switch': 'Selettore',
|
||||
'chat.flowProposal.stepKind.merge': 'Unisci',
|
||||
'chat.flowProposal.stepKind.splitOut': 'Dividi',
|
||||
'chat.flowProposal.stepKind.transform': 'Trasforma',
|
||||
'chat.flowProposal.stepKind.outputParser': 'Analizza risultato',
|
||||
'chat.flowProposal.stepKind.subWorkflow': 'Sotto-workflow',
|
||||
'channels.authMode.managed_dm': 'Accedi con OpenHuman',
|
||||
'channels.authMode.oauth': 'OAuth Accedi',
|
||||
'channels.authMode.bot_token': 'Usa il tuo token Bot',
|
||||
|
||||
@@ -3458,6 +3458,17 @@ const messages: TranslationMap = {
|
||||
'chat.flowProposal.error': '워크플로를 저장할 수 없습니다. 다시 시도하세요.',
|
||||
'chat.flowProposal.enableError':
|
||||
'워크플로는 저장되었지만 활성화할 수 없습니다. 다시 시도하거나 Workflows 페이지에서 활성화하세요.',
|
||||
'chat.flowProposal.stepKind.agent': '에이전트',
|
||||
'chat.flowProposal.stepKind.toolCall': '작업',
|
||||
'chat.flowProposal.stepKind.httpRequest': '웹 요청',
|
||||
'chat.flowProposal.stepKind.code': '코드 실행',
|
||||
'chat.flowProposal.stepKind.condition': '조건',
|
||||
'chat.flowProposal.stepKind.switch': '스위치',
|
||||
'chat.flowProposal.stepKind.merge': '병합',
|
||||
'chat.flowProposal.stepKind.splitOut': '분할',
|
||||
'chat.flowProposal.stepKind.transform': '변환',
|
||||
'chat.flowProposal.stepKind.outputParser': '출력 파싱',
|
||||
'chat.flowProposal.stepKind.subWorkflow': '하위 워크플로',
|
||||
'channels.authMode.managed_dm': 'OpenHuman로 로그인',
|
||||
'channels.authMode.oauth': 'OAuth 로그인',
|
||||
'channels.authMode.bot_token': '자체 봇 토큰 사용',
|
||||
|
||||
@@ -3538,6 +3538,17 @@ const messages: TranslationMap = {
|
||||
'chat.flowProposal.error': 'Nie udało się zapisać przepływu pracy. Spróbuj ponownie.',
|
||||
'chat.flowProposal.enableError':
|
||||
'Przepływ pracy zapisany, ale nie udało się go włączyć. Spróbuj ponownie lub włącz go na stronie Workflows.',
|
||||
'chat.flowProposal.stepKind.agent': 'Agent',
|
||||
'chat.flowProposal.stepKind.toolCall': 'Akcja',
|
||||
'chat.flowProposal.stepKind.httpRequest': 'Żądanie sieciowe',
|
||||
'chat.flowProposal.stepKind.code': 'Uruchom kod',
|
||||
'chat.flowProposal.stepKind.condition': 'Warunek',
|
||||
'chat.flowProposal.stepKind.switch': 'Przełącznik',
|
||||
'chat.flowProposal.stepKind.merge': 'Scal',
|
||||
'chat.flowProposal.stepKind.splitOut': 'Podziel',
|
||||
'chat.flowProposal.stepKind.transform': 'Przekształć',
|
||||
'chat.flowProposal.stepKind.outputParser': 'Przetwórz wynik',
|
||||
'chat.flowProposal.stepKind.subWorkflow': 'Podprzepływ pracy',
|
||||
'channels.authMode.managed_dm': 'Zaloguj się z OpenHuman',
|
||||
'channels.authMode.oauth': 'Logowanie OAuth',
|
||||
'channels.authMode.bot_token': 'Użyj własnego tokena bota',
|
||||
|
||||
@@ -3550,6 +3550,17 @@ const messages: TranslationMap = {
|
||||
'chat.flowProposal.error': 'Não foi possível salvar o fluxo de trabalho. Tente novamente.',
|
||||
'chat.flowProposal.enableError':
|
||||
'Fluxo de trabalho salvo, mas não foi possível ativá-lo. Tente novamente ou ative-o na página Workflows.',
|
||||
'chat.flowProposal.stepKind.agent': 'Agente',
|
||||
'chat.flowProposal.stepKind.toolCall': 'Ação',
|
||||
'chat.flowProposal.stepKind.httpRequest': 'Pedido web',
|
||||
'chat.flowProposal.stepKind.code': 'Executar código',
|
||||
'chat.flowProposal.stepKind.condition': 'Condição',
|
||||
'chat.flowProposal.stepKind.switch': 'Seletor',
|
||||
'chat.flowProposal.stepKind.merge': 'Mesclar',
|
||||
'chat.flowProposal.stepKind.splitOut': 'Dividir',
|
||||
'chat.flowProposal.stepKind.transform': 'Transformar',
|
||||
'chat.flowProposal.stepKind.outputParser': 'Analisar resultado',
|
||||
'chat.flowProposal.stepKind.subWorkflow': 'Subfluxo de trabalho',
|
||||
'channels.authMode.managed_dm': 'Faça login com OpenHuman',
|
||||
'channels.authMode.oauth': 'OAuth Faça login',
|
||||
'channels.authMode.bot_token': 'Use seu próprio token de bot',
|
||||
|
||||
@@ -3522,6 +3522,17 @@ const messages: TranslationMap = {
|
||||
'chat.flowProposal.error': 'Не удалось сохранить рабочий процесс. Попробуйте еще раз.',
|
||||
'chat.flowProposal.enableError':
|
||||
'Рабочий процесс сохранён, но не удалось его включить. Попробуйте ещё раз или включите его на странице Workflows.',
|
||||
'chat.flowProposal.stepKind.agent': 'Агент',
|
||||
'chat.flowProposal.stepKind.toolCall': 'Действие',
|
||||
'chat.flowProposal.stepKind.httpRequest': 'Веб-запрос',
|
||||
'chat.flowProposal.stepKind.code': 'Выполнить код',
|
||||
'chat.flowProposal.stepKind.condition': 'Условие',
|
||||
'chat.flowProposal.stepKind.switch': 'Переключатель',
|
||||
'chat.flowProposal.stepKind.merge': 'Объединить',
|
||||
'chat.flowProposal.stepKind.splitOut': 'Разделить',
|
||||
'chat.flowProposal.stepKind.transform': 'Преобразовать',
|
||||
'chat.flowProposal.stepKind.outputParser': 'Обработать вывод',
|
||||
'chat.flowProposal.stepKind.subWorkflow': 'Подпроцесс',
|
||||
'channels.authMode.managed_dm': 'Войдите с помощью OpenHuman',
|
||||
'channels.authMode.oauth': 'OAuth Вход в систему',
|
||||
'channels.authMode.bot_token': 'Используйте свой собственный токен бота',
|
||||
|
||||
@@ -3313,6 +3313,17 @@ const messages: TranslationMap = {
|
||||
'chat.flowProposal.dismiss': '忽略',
|
||||
'chat.flowProposal.error': '无法保存该工作流。请重试。',
|
||||
'chat.flowProposal.enableError': '工作流已保存,但无法启用。请重试,或在“工作流”页面手动启用。',
|
||||
'chat.flowProposal.stepKind.agent': '智能体',
|
||||
'chat.flowProposal.stepKind.toolCall': '操作',
|
||||
'chat.flowProposal.stepKind.httpRequest': '网络请求',
|
||||
'chat.flowProposal.stepKind.code': '运行代码',
|
||||
'chat.flowProposal.stepKind.condition': '条件',
|
||||
'chat.flowProposal.stepKind.switch': '分支选择',
|
||||
'chat.flowProposal.stepKind.merge': '合并',
|
||||
'chat.flowProposal.stepKind.splitOut': '拆分',
|
||||
'chat.flowProposal.stepKind.transform': '转换',
|
||||
'chat.flowProposal.stepKind.outputParser': '解析输出',
|
||||
'chat.flowProposal.stepKind.subWorkflow': '子工作流',
|
||||
'channels.authMode.managed_dm': '使用 OpenHuman 登录',
|
||||
'channels.authMode.oauth': 'OAuth 登录',
|
||||
'channels.authMode.bot_token': '使用你自己的 Bot Token',
|
||||
|
||||
Reference in New Issue
Block a user