Skip to content

Commit 80a2be7

Browse files
feat: Implement Context Merger block with multiple input support and associated UI components
1 parent ca3b31a commit 80a2be7

File tree

10 files changed

+172
-6
lines changed

10 files changed

+172
-6
lines changed
Lines changed: 8 additions & 0 deletions
Loading
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<script lang="ts" setup>
2+
import type { NodeProps } from '@vue-flow/core';
3+
import { NodeResizer } from '@vue-flow/node-resizer';
4+
5+
import type { DataContextMerger } from '@/types/graph';
6+
import { ContextMergerModeEnum } from '@/types/enums';
7+
8+
const emit = defineEmits(['updateNodeInternals', 'update:deleteNode', 'update:unlinkNode']);
9+
10+
// --- Composables ---
11+
const { getBlockById } = useBlocks();
12+
const blockDefinition = getBlockById('primary-context-merger');
13+
14+
// --- Routing ---
15+
const route = useRoute();
16+
const graphId = computed(() => (route.params.id as string) ?? '');
17+
18+
// --- Props ---
19+
const props = defineProps<NodeProps<DataContextMerger>>();
20+
21+
// --- Methods ---
22+
</script>
23+
<template>
24+
<NodeResizer
25+
:is-visible="true"
26+
:min-width="blockDefinition?.minSize?.width"
27+
:min-height="blockDefinition?.minSize?.height"
28+
color="transparent"
29+
:node-id="props.id"
30+
/>
31+
32+
<UiGraphNodeUtilsRunToolbar
33+
:graph-id="graphId"
34+
:node-id="props.id"
35+
:selected="props.selected"
36+
source="input"
37+
:in-group="props.parentNodeId !== undefined"
38+
@update:delete-node="emit('update:deleteNode', props.id)"
39+
@update:unlink-node="emit('update:unlinkNode', props.id)"
40+
/>
41+
42+
<div
43+
class="bg-golden-ochre border-golden-ochre-dark relative flex h-full w-full flex-col
44+
rounded-3xl border-2 p-4 pt-3 shadow-lg transition-all duration-200 ease-in-out"
45+
:class="{
46+
'opacity-50': props.dragging,
47+
'shadow-golden-ochre-dark !shadow-[0px_0px_15px_3px]': props.selected,
48+
}"
49+
>
50+
<!-- Block Header -->
51+
<div class="mb-2 flex w-full items-center justify-center">
52+
<label class="flex w-fit items-center gap-2">
53+
<UiIcon
54+
:name="blockDefinition?.icon || ''"
55+
class="dark:text-soft-silk text-anthracite h-6 w-6 opacity-80"
56+
/>
57+
<span class="dark:text-soft-silk/80 text-anthracite text-lg font-bold">
58+
{{ blockDefinition?.name }}
59+
</span>
60+
</label>
61+
</div>
62+
63+
<!-- Block Content -->
64+
<div class="flex flex-1 items-center justify-center gap-4">
65+
<!-- Choose between multiple input modes : full, summary, last_n -->
66+
<template
67+
v-for="mode in [
68+
ContextMergerModeEnum.FULL,
69+
ContextMergerModeEnum.SUMMARY,
70+
ContextMergerModeEnum.LAST_N,
71+
]"
72+
:key="mode"
73+
>
74+
<div
75+
class="flex w-fit items-center justify-between rounded-lg border bg-white/10
76+
px-2 py-1 hover:bg-white/20"
77+
:class="{
78+
'border-golden-ochre-dark bg-white/20': props.data.mode === mode,
79+
'hover:border-golden-ochre-dark/50 border-transparent':
80+
props.data.mode !== mode,
81+
}"
82+
@click="props.data.mode = mode"
83+
>
84+
<label class="dark:text-soft-silk text-anthracite font-medium capitalize">
85+
{{ mode.replace('_', ' ') }}
86+
</label>
87+
</div>
88+
</template>
89+
</div>
90+
</div>
91+
92+
<UiGraphNodeUtilsHandleContext
93+
:id="props.id"
94+
type="target"
95+
:node-id="props.id"
96+
:options="[]"
97+
:style="{ left: '50%' }"
98+
:is-dragging="props.dragging"
99+
class="handletopcustom"
100+
multiple-input
101+
/>
102+
<UiGraphNodeUtilsHandleContext
103+
:id="props.id"
104+
:node-id="props.id"
105+
:options="[]"
106+
type="source"
107+
:is-dragging="props.dragging"
108+
/>
109+
</template>
110+
111+
<style>
112+
.handletopcustom > .vue-flow__handle {
113+
width: 150px !important;
114+
}
115+
</style>

ui/app/components/ui/graph/node/utils/handleContext.vue

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const props = defineProps<{
1212
id: string;
1313
style?: Record<string, string>;
1414
isDragging: boolean;
15+
multipleInput?: boolean;
1516
}>();
1617
1718
// --- Composables ---
@@ -56,7 +57,13 @@ const compatibleTargetNodeTypes = [
5657
}"
5758
:connectable="
5859
(node, connectedEdges) =>
59-
handleConnectableInput(node, connectedEdges, 'context', props.type)
60+
handleConnectableInput(
61+
node,
62+
connectedEdges,
63+
'context',
64+
props.type,
65+
multipleInput || false,
66+
)
6067
"
6168
/>
6269

ui/app/components/ui/graph/sidebar/blocks.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const { onDragStart, onDragEnd } = useGraphDragAndDrop();
1414
<template>
1515
<div class="hide-scrollbar max-h-full w-full overflow-x-hidden overflow-y-auto">
1616
<div
17-
class="flex w-[28rem] flex-col items-center px-4 pb-10 transition-opacity duration-300
17+
class="flex w-[28rem] flex-col items-center px-4 transition-opacity duration-300
1818
ease-in-out"
1919
:class="{
2020
'opacity-0': !isRightOpen,

ui/app/components/ui/graph/sidebar/nodeData/nodeTypeChip.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const block = computed(
2929
}"
3030
>
3131
<UiIcon v-if="block" :name="block.icon" class="h-4 w-4" />
32-
{{ block.name || 'Unknown' }}
32+
{{ block?.name || 'Unknown' }}
3333
</div>
3434
</template>
3535

ui/app/composables/useBlocks.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,22 @@ export function useBlocks() {
119119
color: 'var(--color-sunbaked-sand-dark)',
120120
},
121121
],
122+
utility: [
123+
{
124+
id: 'primary-context-merger',
125+
name: 'Context Merger',
126+
desc: 'In this block, you can merge multiple context inputs into a single output context to be used by downstream blocks.',
127+
icon: 'TablerArrowMerge',
128+
nodeType: NodeTypeEnum.CONTEXT_MERGER,
129+
defaultData: {
130+
function: '',
131+
args: [],
132+
reply: '',
133+
},
134+
minSize: { width: 300, height: 100 },
135+
color: 'var(--color-golden-ochre)',
136+
},
137+
],
122138
};
123139
});
124140

ui/app/composables/useEdgeCompatibility.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { NodeCategoryEnum } from '@/types/enums';
33

44
const acceptedMapping: Record<string, string[]> = {
55
prompt: ['prompt'],
6-
context: ['textToText', 'parallelization', 'routing'],
6+
context: ['textToText', 'parallelization', 'routing', 'contextMerger'],
77
attachment: ['filePrompt', 'github'],
88
};
99

@@ -62,11 +62,12 @@ export const useEdgeCompatibility = () => {
6262
connectedEdges: GraphEdge[],
6363
handleCategory: string,
6464
handleType: 'source' | 'target',
65+
multiple = false,
6566
): boolean => {
6667
if (handleType !== 'target') return true;
6768

6869
const isMultipleAccepted = acceptMultipleInputEdges[handleCategory as NodeCategoryEnum];
69-
if (isMultipleAccepted) return true;
70+
if (isMultipleAccepted || multiple) return true;
7071

7172
const handleId = `${handleCategory}_${node.id}`;
7273
return !connectedEdges.some((edge) => edge.targetHandle === handleId);

ui/app/pages/graph/[id].vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,13 @@ onUnmounted(() => {
476476
@update:unlink-node="unlinkNodeFromGroup"
477477
/>
478478
</template>
479+
<template #node-contextMerger="contextMergerNodeProps">
480+
<UiGraphNodeContextMerger
481+
v-bind="contextMergerNodeProps"
482+
@update:delete-node="deleteNode"
483+
@update:unlink-node="unlinkNodeFromGroup"
484+
/>
485+
</template>
479486
<template #node-group="groupNodeProps">
480487
<UiGraphNodeGroup
481488
v-bind="groupNodeProps"

ui/app/types/enums.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export enum NodeTypeEnum {
3030
PARALLELIZATION_MODELS = 'parallelizationModels',
3131
ROUTING = 'routing',
3232
STREAMING = 'streaming',
33+
CONTEXT_MERGER = 'contextMerger',
3334
}
3435

3536
export enum NodeCategoryEnum {
@@ -75,3 +76,9 @@ export enum ToolEnum {
7576
WEB_SEARCH = 'web_search',
7677
LINK_EXTRACTION = 'link_extraction',
7778
}
79+
80+
export enum ContextMergerModeEnum {
81+
FULL = 'full',
82+
SUMMARY = 'summary',
83+
LAST_N = 'last_n',
84+
}

ui/app/types/graph.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ export interface BlockDefinition {
125125
| DataTextToText
126126
| DataParallelization
127127
| DataRouting
128-
| DataGithub;
128+
| DataGithub
129+
| DataContextMerger;
129130
minSize: Record<string, number>;
130131
forcedInitialDimensions?: boolean;
131132
color?: string;
@@ -174,6 +175,10 @@ export interface DataGithub {
174175
branch: string | undefined;
175176
}
176177

178+
export interface DataContextMerger {
179+
mode: ContextMergerModeEnum;
180+
}
181+
177182
export interface BlockCategories {
178183
[category: string]: BlockDefinition[];
179184
}

0 commit comments

Comments
 (0)