feat: refactor node spec and add mcp tools (#244)

* refactor: carve out extraction panel

* refactor: create spec versions for node types

* refactor: create a GenericNode and remove custom nodes

* feat: add python and typescript sdk

* add dograh sdk

* fix: fetch draft workflow definition over published one

* fix: fix routes of SDKs to use code gen

* chore: remove doclink dependency to reduce image size

* chore: format files

* chore: bump pipecat

* feat: let mcp fetch archived workflows on demand

* chore: fix tests

* feat: add sdk documentation

* chore: change banner and add badge
This commit is contained in:
Abhishek 2026-04-21 07:56:16 +05:30 committed by GitHub
parent 0a61ef295f
commit 00a1a22b74
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
162 changed files with 14355 additions and 3554 deletions

View file

@ -1,118 +1,91 @@
import { ClipboardCheck, ExternalLink, Globe, Headset, Link2, LucideIcon, OctagonX, Play, Webhook, X } from 'lucide-react';
import { useEffect } from 'react';
import * as LucideIcons from 'lucide-react';
import { Circle, ExternalLink, type LucideIcon, X } from 'lucide-react';
import { useEffect, useMemo } from 'react';
import type { NodeSpec } from '@/client/types.gen';
import { useNodeSpecs } from '@/components/flow/renderer';
import { Button } from '@/components/ui/button';
import { NodeType } from './types';
type NodeTypeConfig = {
type: NodeType;
label: string;
description: string;
icon: LucideIcon;
};
type AddNodePanelProps = {
isOpen: boolean;
onClose: () => void;
onNodeSelect: (nodeType: NodeType) => void;
};
const NODE_TYPES: NodeTypeConfig[] = [
{
type: NodeType.START_CALL,
label: 'Start Call',
description: 'Create a start call node',
icon: Play
},
{
type: NodeType.AGENT_NODE,
label: 'Agent Node',
description: 'Create an agent node',
icon: Headset
},
{
type: NodeType.END_CALL,
label: 'End Call',
description: 'Create an end call node',
icon: OctagonX
}
// Section ordering and labels. Drives both the category → section title
// mapping and the rendering order.
const SECTION_ORDER: Array<{ category: NodeSpec['category']; title: string }> = [
{ category: 'trigger', title: 'Triggers' },
{ category: 'call_node', title: 'Agent Nodes' },
{ category: 'global_node', title: 'Global Nodes' },
{ category: 'integration', title: 'Integrations' },
];
const GLOBAL_NODE_TYPES: NodeTypeConfig[] = [
{
type: NodeType.GLOBAL_NODE,
label: 'Global Node',
description: 'Create a global node',
icon: Globe
}
];
const TRIGGER_NODE_TYPES: NodeTypeConfig[] = [
{
type: NodeType.TRIGGER,
label: 'API Trigger',
description: 'Enable API-based call triggering',
icon: Webhook
}
];
const INTEGRATION_NODE_TYPES: NodeTypeConfig[] = [
{
type: NodeType.WEBHOOK,
label: 'Webhook',
description: 'Send HTTP request after workflow completion',
icon: Link2
},
{
type: NodeType.QA,
label: 'QA Analysis',
description: 'Run LLM quality analysis after each call',
icon: ClipboardCheck
}
];
function resolveIcon(name: string): LucideIcon {
const icons = LucideIcons as unknown as Record<string, LucideIcon>;
return icons[name] ?? Circle;
}
function NodeSection({
title,
nodes,
onNodeSelect
specs,
onNodeSelect,
}: {
title: string;
nodes: NodeTypeConfig[];
specs: NodeSpec[];
onNodeSelect: (nodeType: NodeType) => void;
}) {
if (specs.length === 0) return null;
return (
<div className="space-y-3">
<h3 className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
{title}
</h3>
<div className="space-y-2">
{nodes.map((node) => (
<Button
key={node.type}
variant="outline"
className="w-full justify-start p-4 h-auto hover:bg-accent/50 transition-colors"
onClick={() => onNodeSelect(node.type)}
>
<div className="flex items-center">
<div className="bg-muted p-2 rounded-lg mr-3 border border-border">
<node.icon className="h-5 w-5" />
{specs.map((spec) => {
const Icon = resolveIcon(spec.icon);
return (
<Button
key={spec.name}
variant="outline"
className="w-full justify-start p-4 h-auto hover:bg-accent/50 transition-colors"
onClick={() => onNodeSelect(spec.name as NodeType)}
>
<div className="flex items-center">
<div className="bg-muted p-2 rounded-lg mr-3 border border-border">
<Icon className="h-5 w-5" />
</div>
<div className="flex flex-col items-start text-left min-w-0">
<span className="font-medium text-sm">
{spec.display_name}
</span>
<span className="text-xs text-muted-foreground whitespace-normal">
{spec.description}
</span>
</div>
</div>
<div className="flex flex-col items-start text-left min-w-0">
<span className="font-medium text-sm">{node.label}</span>
<span className="text-xs text-muted-foreground whitespace-normal">
{node.description}
</span>
</div>
</div>
</Button>
))}
</Button>
);
})}
</div>
</div>
);
}
export default function AddNodePanel({ isOpen, onNodeSelect, onClose }: AddNodePanelProps) {
const { specs } = useNodeSpecs();
// Group registered specs by category, preserving the SECTION_ORDER.
// Adding a new node type with a new spec.category just shows up here.
const sections = useMemo(() => {
return SECTION_ORDER.map(({ category, title }) => ({
title,
specs: specs.filter((s) => s.category === category),
}));
}, [specs]);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape' && isOpen) {
@ -149,29 +122,14 @@ export default function AddNodePanel({ isOpen, onNodeSelect, onClose }: AddNodeP
</div>
<div className="space-y-6">
<NodeSection
title="Triggers"
nodes={TRIGGER_NODE_TYPES}
onNodeSelect={onNodeSelect}
/>
<NodeSection
title="Agent Nodes"
nodes={NODE_TYPES}
onNodeSelect={onNodeSelect}
/>
<NodeSection
title="Global Nodes"
nodes={GLOBAL_NODE_TYPES}
onNodeSelect={onNodeSelect}
/>
<NodeSection
title="Integrations"
nodes={INTEGRATION_NODE_TYPES}
onNodeSelect={onNodeSelect}
/>
{sections.map(({ title, specs }) => (
<NodeSection
key={title}
title={title}
specs={specs}
onNodeSelect={onNodeSelect}
/>
))}
</div>
</div>
</div>