mirror of
https://github.com/davidkaya/aryx.git
synced 2026-07-24 05:28:46 +02:00
feat: add descriptive help text to graph designer properties
Add inline descriptions to workflow settings, orchestration mode, edge inspector, node inspectors, and condition editor properties so users understand what each option does at a glance. - Execution Mode: explains off-thread vs lockstep behavior - Max Iterations: clarifies it prevents runaway loops - State Scopes: describes shared state containers - Edge Kind: explains direct, fan-out, fan-in semantics - Loop Edge: describes iterative cycle behavior - Reasoning: explains effort vs answer quality tradeoff - Triage Agent, Tool-Call Filtering, Max Rounds, etc. - Result Variable, Require Approval, Port ID, Condition Also enhances FormField component with optional description prop. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -691,6 +691,9 @@ function WorkflowSettingsPanel({
|
||||
<option value="off-thread">Off-thread</option>
|
||||
<option value="lockstep">Lockstep</option>
|
||||
</select>
|
||||
<p className="text-[11px] leading-relaxed text-[var(--color-text-muted)]">
|
||||
Off-thread executes in the background without blocking. Lockstep waits for each step to finish before proceeding.
|
||||
</p>
|
||||
</label>
|
||||
|
||||
<label className="block space-y-1.5">
|
||||
@@ -712,6 +715,9 @@ function WorkflowSettingsPanel({
|
||||
type="number"
|
||||
value={workflow.settings.maxIterations ?? ''}
|
||||
/>
|
||||
<p className="text-[11px] leading-relaxed text-[var(--color-text-muted)]">
|
||||
Safety limit on total step executions to prevent runaway loops.
|
||||
</p>
|
||||
</label>
|
||||
|
||||
<div className="col-span-2 flex items-center justify-between rounded-lg border border-[var(--color-border)] bg-[var(--color-surface-1)] px-4 py-3">
|
||||
@@ -790,9 +796,14 @@ function WorkflowSettingsPanel({
|
||||
{/* State Scopes */}
|
||||
<div className="col-span-2 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-[12px] font-semibold uppercase tracking-wider text-[var(--color-text-muted)]">
|
||||
State Scopes
|
||||
</span>
|
||||
<div>
|
||||
<span className="text-[12px] font-semibold uppercase tracking-wider text-[var(--color-text-muted)]">
|
||||
State Scopes
|
||||
</span>
|
||||
<p className="mt-0.5 text-[11px] leading-relaxed text-[var(--color-text-muted)]">
|
||||
Named containers for shared state that persists across workflow steps.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
className="flex size-6 items-center justify-center rounded-md text-[var(--color-text-muted)] transition-all duration-200 hover:bg-[var(--color-accent)]/10 hover:text-[var(--color-accent)]"
|
||||
onClick={() => {
|
||||
|
||||
@@ -3,10 +3,12 @@ import type { ReactNode } from 'react';
|
||||
export function FormField({
|
||||
label,
|
||||
required,
|
||||
description,
|
||||
children,
|
||||
}: {
|
||||
label: string;
|
||||
required?: boolean;
|
||||
description?: string;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
@@ -16,6 +18,9 @@ export function FormField({
|
||||
{required && <span className="ml-1 text-[var(--color-status-warning)]">*</span>}
|
||||
</span>
|
||||
{children}
|
||||
{description && (
|
||||
<p className="mt-1.5 text-[11px] leading-relaxed text-[var(--color-text-muted)]">{description}</p>
|
||||
)}
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -241,6 +241,9 @@ export function ConditionEditor({ condition, onChange, disabled }: ConditionEdit
|
||||
<option value="message-type">Message Type</option>
|
||||
<option value="expression">Expression</option>
|
||||
</select>
|
||||
<p className="text-[11px] leading-relaxed text-[var(--color-text-muted)]">
|
||||
Determines when this edge fires. Use property rules or expressions to create conditional branches.
|
||||
</p>
|
||||
</label>
|
||||
|
||||
{condition?.type === 'property' && (
|
||||
|
||||
@@ -134,25 +134,35 @@ export function InvokeFunctionInspector({
|
||||
/>
|
||||
|
||||
{/* Result variable */}
|
||||
<InputField
|
||||
label="Result Variable"
|
||||
onChange={(v) => patchConfig({ resultVariable: v || undefined })}
|
||||
placeholder="e.g. Local.result"
|
||||
value={config.resultVariable ?? ''}
|
||||
/>
|
||||
<div className="space-y-1.5">
|
||||
<InputField
|
||||
label="Result Variable"
|
||||
onChange={(v) => patchConfig({ resultVariable: v || undefined })}
|
||||
placeholder="e.g. Local.result"
|
||||
value={config.resultVariable ?? ''}
|
||||
/>
|
||||
<p className="text-[11px] leading-relaxed text-[var(--color-text-muted)]">
|
||||
State path where the return value is stored for use in subsequent steps.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Require approval */}
|
||||
<label className="flex items-center justify-between">
|
||||
<span className="text-[12px] font-medium text-[var(--color-text-secondary)]">
|
||||
Require Approval
|
||||
</span>
|
||||
<input
|
||||
checked={config.requireApproval === true}
|
||||
className="size-4 accent-[var(--color-accent)]"
|
||||
onChange={(e) => patchConfig({ requireApproval: e.target.checked || undefined })}
|
||||
type="checkbox"
|
||||
/>
|
||||
</label>
|
||||
<div className="space-y-1">
|
||||
<label className="flex items-center justify-between">
|
||||
<span className="text-[12px] font-medium text-[var(--color-text-secondary)]">
|
||||
Require Approval
|
||||
</span>
|
||||
<input
|
||||
checked={config.requireApproval === true}
|
||||
className="size-4 accent-[var(--color-accent)]"
|
||||
onChange={(e) => patchConfig({ requireApproval: e.target.checked || undefined })}
|
||||
type="checkbox"
|
||||
/>
|
||||
</label>
|
||||
<p className="text-[11px] leading-relaxed text-[var(--color-text-muted)]">
|
||||
Pause and require human confirmation before invoking this function.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Arguments editor */}
|
||||
<div className="space-y-1.5">
|
||||
|
||||
@@ -211,7 +211,7 @@ function HandoffSettingsPanel({
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<FormField label="Triage Agent">
|
||||
<FormField label="Triage Agent" description="The entry-point agent that receives requests and decides which specialist to hand off to.">
|
||||
<SelectInput
|
||||
value={settings.triageAgentNodeId ?? ''}
|
||||
options={triageOptions}
|
||||
@@ -219,7 +219,7 @@ function HandoffSettingsPanel({
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<FormField label="Tool-Call Filtering">
|
||||
<FormField label="Tool-Call Filtering" description="Controls which tools each agent can see and invoke during handoff routing.">
|
||||
<SelectInput
|
||||
value={settings.toolCallFiltering}
|
||||
options={filteringOptions}
|
||||
@@ -237,7 +237,7 @@ function HandoffSettingsPanel({
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<FormField label="Custom Handoff Instructions">
|
||||
<FormField label="Custom Handoff Instructions" description="Additional guidance given to agents when performing handoffs between specialists.">
|
||||
<textarea
|
||||
className="w-full rounded-lg border border-[var(--color-border)] bg-[var(--color-surface-2)] px-3 py-2 text-[13px] text-[var(--color-text-primary)] outline-none transition-all duration-200 placeholder:text-[var(--color-text-muted)] focus:border-[var(--color-border-glow)] focus:shadow-[0_0_0_1px_rgba(36,92,249,0.15),0_0_12px_rgba(36,92,249,0.08)]"
|
||||
rows={3}
|
||||
@@ -268,7 +268,7 @@ function GroupChatSettingsPanel({
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<FormField label="Selection Strategy">
|
||||
<FormField label="Selection Strategy" description="Determines which agent speaks next in each conversation turn.">
|
||||
<div className="flex items-center gap-2 rounded-lg border border-[var(--color-border)] bg-[var(--color-surface-2)] px-3.5 py-2.5">
|
||||
<Repeat className="size-3.5 text-[var(--color-text-muted)]" />
|
||||
<span className="text-[13px] text-[var(--color-text-primary)]">Round Robin</span>
|
||||
@@ -276,7 +276,7 @@ function GroupChatSettingsPanel({
|
||||
</div>
|
||||
</FormField>
|
||||
|
||||
<FormField label="Max Rounds">
|
||||
<FormField label="Max Rounds" description="Maximum number of conversation turns across all agents before the group chat terminates.">
|
||||
<input
|
||||
type="number"
|
||||
min={1}
|
||||
|
||||
@@ -101,12 +101,17 @@ export function RequestPortInspector({
|
||||
/>
|
||||
|
||||
{/* Port ID */}
|
||||
<InputField
|
||||
label="Port ID"
|
||||
onChange={(v) => patchConfig({ portId: v })}
|
||||
placeholder="Unique port identifier"
|
||||
value={config.portId}
|
||||
/>
|
||||
<div className="space-y-1.5">
|
||||
<InputField
|
||||
label="Port ID"
|
||||
onChange={(v) => patchConfig({ portId: v })}
|
||||
placeholder="Unique port identifier"
|
||||
value={config.portId}
|
||||
/>
|
||||
<p className="text-[11px] leading-relaxed text-[var(--color-text-muted)]">
|
||||
Unique identifier used to target this port from parent workflows or external callers.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Request Type */}
|
||||
<div className="space-y-1.5">
|
||||
|
||||
@@ -155,12 +155,17 @@ function AgentNodeInspector({
|
||||
}}
|
||||
value={config.model}
|
||||
/>
|
||||
<ReasoningEffortSelect
|
||||
label="Reasoning"
|
||||
onChange={(value) => patchConfig({ reasoningEffort: value })}
|
||||
supportedEfforts={getSupportedReasoningEfforts(model)}
|
||||
value={config.reasoningEffort}
|
||||
/>
|
||||
<div className="space-y-1.5">
|
||||
<ReasoningEffortSelect
|
||||
label="Reasoning"
|
||||
onChange={(value) => patchConfig({ reasoningEffort: value })}
|
||||
supportedEfforts={getSupportedReasoningEfforts(model)}
|
||||
value={config.reasoningEffort}
|
||||
/>
|
||||
<p className="text-[11px] leading-relaxed text-[var(--color-text-muted)]">
|
||||
Controls how much the model reasons before responding. Higher values produce more careful, thorough answers.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -290,27 +295,35 @@ function EdgeInspector({
|
||||
<option value="fan-out">Fan-out</option>
|
||||
<option value="fan-in">Fan-in</option>
|
||||
</select>
|
||||
<p className="text-[11px] leading-relaxed text-[var(--color-text-muted)]">
|
||||
Direct connects nodes sequentially. Fan-out splits into parallel branches. Fan-in waits for all branches to complete.
|
||||
</p>
|
||||
</label>
|
||||
|
||||
{/* Loop controls */}
|
||||
<div className="space-y-2.5">
|
||||
<label className="flex items-center justify-between">
|
||||
<span className="text-[12px] font-medium text-[var(--color-text-secondary)]">
|
||||
Loop Edge
|
||||
</span>
|
||||
<input
|
||||
checked={edge.isLoop === true}
|
||||
className="size-4 accent-[var(--color-accent)]"
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
onEdgeChange(edge.id, { isLoop: true, maxIterations: edge.maxIterations ?? 10 });
|
||||
} else {
|
||||
onEdgeChange(edge.id, { isLoop: undefined, maxIterations: undefined });
|
||||
}
|
||||
}}
|
||||
type="checkbox"
|
||||
/>
|
||||
</label>
|
||||
<div className="space-y-1">
|
||||
<label className="flex items-center justify-between">
|
||||
<span className="text-[12px] font-medium text-[var(--color-text-secondary)]">
|
||||
Loop Edge
|
||||
</span>
|
||||
<input
|
||||
checked={edge.isLoop === true}
|
||||
className="size-4 accent-[var(--color-accent)]"
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
onEdgeChange(edge.id, { isLoop: true, maxIterations: edge.maxIterations ?? 10 });
|
||||
} else {
|
||||
onEdgeChange(edge.id, { isLoop: undefined, maxIterations: undefined });
|
||||
}
|
||||
}}
|
||||
type="checkbox"
|
||||
/>
|
||||
</label>
|
||||
<p className="text-[11px] leading-relaxed text-[var(--color-text-muted)]">
|
||||
Creates an iterative cycle, re-executing the path between these nodes up to a set limit.
|
||||
</p>
|
||||
</div>
|
||||
{edge.isLoop && (
|
||||
<label className="block space-y-1.5">
|
||||
<span className="text-[11px] text-[var(--color-text-muted)]">Max Iterations</span>
|
||||
|
||||
Reference in New Issue
Block a user