feat: directional edges and provider icons on agent nodes

- Add ArrowClosed markers to all graph edges for clear directional flow
- Replace generic Bot icon with AI provider logos (OpenAI, Anthropic,
  Google) on agent nodes, inferred from the agent's model id
- Show model display name as subtitle on agent nodes (e.g. 'GPT-5.4',
  'Claude Opus 4.5')
- Thread availableModels catalog from PatternEditor through canvas to
  resolve friendly model names; falls back to raw model id
- Add 3 new tests for arrow markers, provider icons, and model labels

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-03-24 23:16:04 +01:00
co-authored by Copilot
parent c6c8e79e9f
commit eb2c8450e3
5 changed files with 110 additions and 26 deletions
+42
View File
@@ -258,4 +258,46 @@ describe('edge deletion rules', () => {
expect(inputNode!.type).toBe('userInputNode');
expect(outputNode!.type).toBe('userOutputNode');
});
test('edges have directional arrow markers', () => {
const pattern = findPattern('sequential');
const graph = resolvePatternGraph(pattern);
const edges = toCanvasEdges(graph, pattern.mode);
expect(edges.length).toBeGreaterThan(0);
for (const edge of edges) {
expect(edge.markerEnd).toBeDefined();
expect((edge.markerEnd as { type: string }).type).toBe('arrowclosed');
}
});
test('agent nodes include provider and model label when models catalog is provided', () => {
const { modelCatalog } = require('@shared/domain/models');
const pattern = findPattern('sequential');
const graph = resolvePatternGraph(pattern);
const nodes = toCanvasNodes(graph, pattern.agents, modelCatalog);
const agentNodes = nodes.filter((n) => n.data.kind === 'agent');
expect(agentNodes.length).toBeGreaterThan(0);
for (const node of agentNodes) {
expect(node.data.provider).toBeDefined();
expect(node.data.modelLabel).toBeDefined();
expect(node.data.modelLabel!.length).toBeGreaterThan(0);
}
});
test('agent nodes infer provider from model id without models catalog', () => {
const pattern = findPattern('sequential');
const graph = resolvePatternGraph(pattern);
const nodes = toCanvasNodes(graph, pattern.agents);
const agentNodes = nodes.filter((n) => n.data.kind === 'agent');
for (const node of agentNodes) {
// Provider should still be inferred from model id prefix
expect(node.data.provider).toBeDefined();
// Without catalog, modelLabel falls back to the raw model id
expect(node.data.modelLabel).toBeDefined();
}
});
});