feat(editor): per-field input/output ports + i18n tooltips + 4 jai_client patterns
Phase A of the per-field-output-ports roadmap. FlowRunDriver gains optional moduleInfo(capability) hook that returns a ModuleSpec carrying declared inputs + outputs (each ModuleField has name, type, locale-> description map). Default returns null so legacy hosts that didn't implement the hook keep compiling — the editor falls back to YAML-reference-derived ports. FlowCanvas caches ModuleSpec per step capability, kicked off lazily on each build. When the spec lands, the canvas rebuilds with: - Per-field input ports on the LEFT (declared input names, in stable manifest order) — replaces the with_-keys-as- port-labels shape. - Per-field output ports on the RIGHT — one anchor per declared output field. A flow like summarize that declares response/model_endpoint/model_name/model_digest now exposes four distinct anchors instead of fanning every downstream reference out of one collapsed point. - Tooltips on each port label, picked from the field's description.<locale> with English fallback. NodeGeometry refactored: heightFor(inputs, outputs) takes max(inputs, outputs); outputPortY(index) for the new multi-port output side; outputAnchorY() preserves the legacy single-anchor fallback so edges still draw before the spec resolves. Edges + hit-tester now use _outputPortPosition(stepId, fieldName: edge.fromField). Field index lookup falls through to outputAnchorY when the spec isn't loaded yet. Patterns: ported modern, classic, blueprint, minimal from jai_client's CanvasPatternPainter. Dropdown now has seven choices (dots, grid, modern, classic, blueprint, minimal, blank). Bumps fai_studio_flow_editor to 0.9.0. Signed-off-by: flemming-it <sf@flemming.it>
This commit is contained in:
parent
1f5601a461
commit
b1fe765468
6 changed files with 505 additions and 87 deletions
|
|
@ -38,6 +38,75 @@ abstract class FlowRunDriver {
|
|||
/// may emit events for unrelated runs; the editor filters
|
||||
/// by [FlowRunEvent.flowName] before applying.
|
||||
Stream<FlowRunEvent> events();
|
||||
|
||||
/// Look up an installed module's manifest by capability
|
||||
/// reference. The editor uses this to render per-field
|
||||
/// input/output ports with their declared names + types +
|
||||
/// tooltips, instead of collapsing every downstream reference
|
||||
/// into a single output anchor.
|
||||
///
|
||||
/// Default returns `null` so legacy hosts that didn't update
|
||||
/// their FlowRunDriver implementation keep compiling — the
|
||||
/// editor falls back to the YAML-reference-derived ports.
|
||||
/// A real implementation hits the hub's ModuleInfo RPC and
|
||||
/// maps the result to [ModuleSpec].
|
||||
Future<ModuleSpec?> moduleInfo(String capability) async => null;
|
||||
}
|
||||
|
||||
/// Declared inputs + outputs of one installed module, as seen
|
||||
/// by the editor. The shape mirrors `ModuleInfoResponse.inputs`
|
||||
/// / `outputs` over gRPC but without proto dependencies.
|
||||
class ModuleSpec {
|
||||
/// Capability identifier the host resolved (e.g. `text.summarize`).
|
||||
final String capability;
|
||||
|
||||
/// Declared inputs, in stable alphabetical order. The editor
|
||||
/// draws one input port per entry.
|
||||
final List<ModuleField> inputs;
|
||||
|
||||
/// Declared outputs, in stable alphabetical order. The editor
|
||||
/// draws one output port per entry — this is what makes
|
||||
/// `summarize.response`, `summarize.model_endpoint`,
|
||||
/// `summarize.model_name`, `summarize.model_digest` appear as
|
||||
/// four distinct anchors instead of one.
|
||||
final List<ModuleField> outputs;
|
||||
|
||||
const ModuleSpec({
|
||||
required this.capability,
|
||||
required this.inputs,
|
||||
required this.outputs,
|
||||
});
|
||||
}
|
||||
|
||||
/// One declared input or output field. Carries the type
|
||||
/// descriptor (`text` / `json` / `bytes` / `file`) and a locale
|
||||
/// → description map for tooltip rendering.
|
||||
class ModuleField {
|
||||
/// Field name, e.g. `prompt` or `model_endpoint`.
|
||||
final String name;
|
||||
|
||||
/// Type descriptor.
|
||||
final String type;
|
||||
|
||||
/// IETF locale tag → description string. Empty when the
|
||||
/// manifest used the shorthand form (no description).
|
||||
final Map<String, String> description;
|
||||
|
||||
const ModuleField({
|
||||
required this.name,
|
||||
required this.type,
|
||||
this.description = const {},
|
||||
});
|
||||
|
||||
/// Description in [locale] with English fallback. Returns
|
||||
/// null when the manifest carries no description for either.
|
||||
String? descriptionFor(String locale) {
|
||||
final exact = description[locale];
|
||||
if (exact != null && exact.isNotEmpty) return exact;
|
||||
final en = description['en'];
|
||||
if (en != null && en.isNotEmpty) return en;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// What the editor needs out of a single event tick. Maps
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue