diff --git a/src/common/beam/BeamView.tsx b/src/common/beam/BeamView.tsx
index cc026f9c0..99dac24e9 100644
--- a/src/common/beam/BeamView.tsx
+++ b/src/common/beam/BeamView.tsx
@@ -29,7 +29,7 @@ export function BeamView(props: {
const {
/* root */ editInputHistoryMessage,
/* scatter */ setRayCount, startScatteringAll, stopScatteringAll,
- /* gather */ setFusionIndex, setFusionLlmId, startFusion, stopFusion,
+ /* gather */ setFusionIndex, setFusionLlmId, fusionCustomize, fusionStart, fusionStop,
} = props.beamStore.getState();
const {
/* root */ inputHistory, inputIssues, inputReady,
@@ -140,8 +140,9 @@ export function BeamView(props: {
isMobile={props.isMobile}
fusionIndex={fusionIndex}
setFusionIndex={setFusionIndex}
- onStartFusion={startFusion}
- onStopFusion={stopFusion}
+ onFusionCustomize={fusionCustomize}
+ onFusionStart={fusionStart}
+ onFusionStop={fusionStop}
/>
{/* Fusion Output */}
diff --git a/src/common/beam/gather/BeamGatherConfig.tsx b/src/common/beam/gather/BeamGatherConfig.tsx
index 25595fb2d..e67dc88ca 100644
--- a/src/common/beam/gather/BeamGatherConfig.tsx
+++ b/src/common/beam/gather/BeamGatherConfig.tsx
@@ -6,8 +6,10 @@ import { Box, Typography } from '@mui/joy';
import { ChatMessageMemo } from '../../../apps/chat/components/message/ChatMessage';
import { createDMessage } from '~/common/state/store-chats';
-import { BeamStoreApi, useBeamStore } from '~/common/beam/store-beam.hooks';
-import { GATHER_DEBUG_NONCUSTOM } from '~/common/beam/beam.config';
+
+import type { TInstruction } from './beam.gather';
+import { BeamStoreApi, useBeamStore } from '../store-beam.hooks';
+import { GATHER_DEBUG_NONCUSTOM } from '../beam.config';
const gatherConfigWrapperSx: SxProps = {
@@ -34,6 +36,79 @@ const configChatInstructionSx: SxProps = {
flex: 1,
};
+function InstructionWrapper(props: { children: React.ReactNode }) {
+ return (
+
+ {props.children}
+
+ );
+}
+
+
+function ReadOnlyInstruction(props: { instruction: TInstruction, isMobile: boolean }) {
+ const { instruction } = props;
+
+ // render 'chat-generate'
+ if (instruction.type === 'chat-generate') {
+ return (
+
+
+
+ System Prompt:
+
+
+
+
+
+ User Prompt:
+
+
+
+
+ );
+ }
+
+ // render 'user-input-checklist'
+ if (instruction.type === 'user-input-checklist') {
+ return (
+
+
+
+ Checklist:
+
+
+
+
+ );
+ }
+
+ return (
+
+
+ Unknown Instruction
+
+
+ );
+}
+
export function BeamGatherConfig(props: {
beamStore: BeamStoreApi
@@ -41,7 +116,7 @@ export function BeamGatherConfig(props: {
}) {
// state
- const [viewInstructionIndex, setViewInstructionIndex] = React.useState(0);
+ // const [viewInstructionIndex, setViewInstructionIndex] = React.useState(0);
// external state
const fusion = useBeamStore(props.beamStore, store => {
@@ -49,64 +124,23 @@ export function BeamGatherConfig(props: {
return (fusion?.isEditable || GATHER_DEBUG_NONCUSTOM) ? fusion : null;
});
-
- // [effect] sync the fusion program index to the viewInstructionIndex
- React.useEffect(() => {
- fusion && setViewInstructionIndex(fusion.currentInstructionIndex);
- }, [fusion]);
-
+ // // [effect] sync the fusion program index to the viewInstructionIndex
+ // React.useEffect(() => {
+ // fusion && setViewInstructionIndex(fusion.currentInstructionIndex);
+ // }, [fusion]);
// derived state
- const instruction = React.useMemo(() => {
- return fusion?.instructions[viewInstructionIndex] ?? null;
- }, [fusion, viewInstructionIndex]);
+ // const instructions = React.useMemo(() => {
+ // return fusion?.instructions ?? null;
+ // }, [fusion]);
+ const instructions = fusion?.instructions ?? null;
- // render instruction
- const instructionComponent = React.useMemo(() => {
- if (instruction && instruction.type === 'chat-generate') {
- return <>
- {instruction.systemPrompt && (
-
-
- System Prompt:
-
-
-
- )}
- {instruction.userPrompt && (
-
-
- User Prompt:
-
-
-
-
- )}
- >;
- }
- return null;
- }, [instruction, props.isMobile]);
-
-
- if (!instructionComponent)
- return null;
-
- return (
+ return !!instructions?.length ? (
- {instructionComponent}
+ {instructions.map((instruction, stepIndex) =>
+ ,
+ )}
- );
+ ) : null;
}
\ No newline at end of file
diff --git a/src/common/beam/gather/BeamGatherPane.tsx b/src/common/beam/gather/BeamGatherPane.tsx
index 4f2e0056f..ac220e608 100644
--- a/src/common/beam/gather/BeamGatherPane.tsx
+++ b/src/common/beam/gather/BeamGatherPane.tsx
@@ -1,9 +1,10 @@
import * as React from 'react';
import type { SxProps } from '@mui/joy/styles/types';
-import { Box, Button, ButtonGroup, FormControl, SvgIconProps, Typography } from '@mui/joy';
+import { Box, Button, ButtonGroup, FormControl, IconButton, SvgIconProps, Tooltip, Typography } from '@mui/joy';
import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome';
import AutoAwesomeOutlinedIcon from '@mui/icons-material/AutoAwesomeOutlined';
+import EditRoundedIcon from '@mui/icons-material/EditRounded';
import MergeRoundedIcon from '@mui/icons-material/MergeRounded';
import StopRoundedIcon from '@mui/icons-material/StopRounded';
@@ -15,7 +16,7 @@ import { useScrollToBottom } from '~/common/scroll-to-bottom/useScrollToBottom';
import { GATHER_COLOR } from '../beam.config';
import { beamPaneSx } from '../BeamCard';
-import { FUSION_PROGRAMS } from './beam.gather';
+import { FUSION_FACTORIES } from './beam.gather';
const mobileBeamGatherPane: SxProps = {
@@ -47,21 +48,25 @@ export function BeamGatherPane(props: {
gatherLlmIcon?: React.FunctionComponent,
fusionIndex: number | null,
setFusionIndex: (index: number | null) => void
- onStartFusion: () => void,
- onStopFusion: () => void,
+ onFusionCustomize: (index: number) => void,
+ onFusionStart: () => void,
+ onFusionStop: () => void,
}) {
// external state
const { setStickToBottom } = useScrollToBottom();
// derived state
- const { gatherCount, gatherEnabled, gatherBusy, setFusionIndex } = props;
+ const { gatherCount, gatherEnabled, gatherBusy, setFusionIndex, onFusionCustomize } = props;
const handleFusionActivate = React.useCallback((idx: number, shiftPressed: boolean) => {
setStickToBottom(true);
setFusionIndex((idx !== props.fusionIndex || !shiftPressed) ? idx : null);
}, [props.fusionIndex, setFusionIndex, setStickToBottom]);
+ const handleFusionCustomize = React.useCallback(() => {
+ props.fusionIndex !== null && onFusionCustomize(props.fusionIndex);
+ }, [onFusionCustomize, props.fusionIndex]);
const Icon = props.gatherLlmIcon || (gatherBusy ? AutoAwesomeIcon : AutoAwesomeOutlinedIcon);
@@ -85,10 +90,13 @@ export function BeamGatherPane(props: {
{/* Method */}
- Method>} sx={{ mb: '0.25rem' /* orig: 6px */ }} />
-
+ Method>}
+ sx={{ mb: '0.25rem' /* orig: 6px */ }}
+ />
+
- {FUSION_PROGRAMS.map((fusion, idx) => {
+ {FUSION_FACTORIES.map((fusion, idx) => {
const isActive = idx === props.fusionIndex;
return (
@@ -123,7 +138,7 @@ export function BeamGatherPane(props: {
variant='solid' color={GATHER_COLOR}
disabled={!gatherEnabled || gatherBusy} loading={gatherBusy}
endDecorator={}
- onClick={props.onStartFusion}
+ onClick={props.onFusionStart}
sx={{ minWidth: 120 }}
>
Merge
@@ -133,7 +148,7 @@ export function BeamGatherPane(props: {
// key='gather-stop'
variant='solid' color='danger'
endDecorator={}
- onClick={props.onStopFusion}
+ onClick={props.onFusionStop}
sx={{ minWidth: 120 }}
>
Stop
diff --git a/src/common/beam/gather/beam.gather.ts b/src/common/beam/gather/beam.gather.ts
index af03323b8..d541c43c3 100644
--- a/src/common/beam/gather/beam.gather.ts
+++ b/src/common/beam/gather/beam.gather.ts
@@ -9,45 +9,53 @@ import { GATHER_PLACEHOLDER } from '../beam.config';
// Choose, Improve, Fuse, Manual
-export const FUSION_PROGRAMS: { label: string, factory: () => BFusion }[] = [
+const commonInitialization = (isEditable: boolean): Pick => ({
+ isEditable,
+ currentInstructionIndex: 0,
+ llmId: null,
+ status: 'idle',
+ outputMessage: createDMessage('assistant', GATHER_PLACEHOLDER),
+});
+
+export const FUSION_FACTORIES: { label: string, factory: () => BFusion }[] = [
{
- label: 'Guided', factory: () => ({
+ label: 'Guided',
+ factory: () => ({
instructions: [{
type: 'chat-generate',
- systemPrompt: 'You are',
- userPrompt: 'Perform this',
+ systemPrompt: 'You arfe',
+ userPrompt: 'Perform thiws',
outputType: 'fin',
+ }, {
+ type: 'user-input-checklist',
}],
- currentInstructionIndex: 0,
- isEditable: false,
- llmId: null,
- status: 'idle',
- outputMessage: createDMessage('assistant', GATHER_PLACEHOLDER),
+ ...commonInitialization(false),
}),
},
{
- label: 'Fuse', factory: () => ({
+ label: 'Fuse',
+ factory: () => ({
instructions: [{
type: 'chat-generate',
systemPrompt: 'You are an editor',
userPrompt: 'Best of all',
outputType: 'fin',
}],
- currentInstructionIndex: 0,
- isEditable: false,
- llmId: null,
- status: 'idle',
- outputMessage: createDMessage('assistant', GATHER_PLACEHOLDER + '2'),
+ ...commonInitialization(false),
}),
},
{
- label: 'Custom', factory: () => ({
- instructions: [],
- currentInstructionIndex: 0,
- isEditable: true,
- llmId: null,
- status: 'idle',
- outputMessage: createDMessage('assistant', GATHER_PLACEHOLDER + '3'),
+ label: 'Custom',
+ factory: () => ({
+ instructions: [{
+ type: 'chat-generate',
+ systemPrompt: 'You are a custom editor',
+ userPrompt: 'Best of all',
+ outputType: 'fin',
+ }],
+ ...commonInitialization(true),
}),
},
];
@@ -73,7 +81,7 @@ export function fusionGatherStop(fusion: BFusion): BFusion {
/// Gather Store Slice ///
-type TInstruction = {
+export type TInstruction = {
type: 'chat-generate',
systemPrompt: string;
userPrompt: string;
@@ -84,14 +92,14 @@ type TInstruction = {
export interface BFusion {
// set at creation, adjusted later if this is a custom fusion (and only when idle)
+ isEditable: boolean; // only true on a single custom fusion
instructions: TInstruction[];
- currentInstructionIndex: number;
- isEditable: boolean;
- // set at lifecycle
+ // set at start
llmId: DLLMId | null;
// variable
+ currentInstructionIndex: number; // points to the next instruction to execute
status: 'idle' | 'fusing' | 'success' | 'stopped' | 'error';
outputMessage: DMessage;
issue?: string;
@@ -115,7 +123,7 @@ export const reInitGatherStateSlice = (prevFusions: BFusion[]): GatherStateSlice
return {
// recreate all fusions (no recycle)
- fusions: FUSION_PROGRAMS.map(spec => spec.factory()),
+ fusions: FUSION_FACTORIES.map(spec => spec.factory()),
fusionIndex: null,
fusionLlmId: null,
isGathering: false,
@@ -126,8 +134,12 @@ export interface GatherStoreSlice extends GatherStateSlice {
setFusionIndex: (index: number | null) => void;
setFusionLlmId: (llmId: DLLMId | null) => void;
- startFusion: () => void;
- stopFusion: () => void;
+
+ fusionCustomize: (sourceIndex: number) => void;
+ fusionStart: () => void;
+ fusionStop: () => void;
+
+ _fusionUpdate: (fusionIndex: number, update: Partial | ((fusion: BFusion) => (Partial | null))) => void;
}
@@ -147,12 +159,37 @@ export const createGatherSlice: StateCreator {
+ fusionCustomize: (sourceIndex: number) => {
+ const { fusions, setFusionIndex, _fusionUpdate } = _get();
+ const editableFusionIndex = fusions.findIndex(fusion => fusion.isEditable);
+ const fusionFactory = FUSION_FACTORIES[sourceIndex];
+ if (editableFusionIndex === -1 || editableFusionIndex === sourceIndex || !fusionFactory)
+ return;
+ _fusionUpdate(editableFusionIndex, customFusion => {
+ // Terminate current custom fusion, if any
+ fusionGatherStop(customFusion);
+ return {
+ ...fusionFactory.factory(),
+ isEditable: true,
+ };
+ });
+ setFusionIndex(editableFusionIndex);
+ },
+
+ fusionStart: () => {
console.log('startGatheringCurrent');
},
- stopFusion: () => {
+ fusionStop: () => {
console.log('stopGatheringCurrent');
},
+ _fusionUpdate: (fusionIndex: number, update: Partial | ((fusion: BFusion) => (Partial | null))) =>
+ _set(state => ({
+ fusions: state.fusions.map((fusion, index) => (index === fusionIndex)
+ ? { ...fusion, ...(typeof update === 'function' ? update(fusion) : update) }
+ : fusion,
+ ),
+ })),
+
});
diff --git a/src/common/beam/scatter/beam.scatter.ts b/src/common/beam/scatter/beam.scatter.ts
index 700fbe322..cbf96e7ee 100644
--- a/src/common/beam/scatter/beam.scatter.ts
+++ b/src/common/beam/scatter/beam.scatter.ts
@@ -264,12 +264,13 @@ export const createScatterSlice: StateCreator | ((ray: BRay) => Partial)) => _set(state => ({
- rays: state.rays.map(ray => (ray.rayId === rayId)
- ? { ...ray, ...(typeof update === 'function' ? update(ray) : update) }
- : ray,
- ),
- })),
+ _rayUpdate: (rayId: BRayId, update: Partial | ((ray: BRay) => Partial)) =>
+ _set(state => ({
+ rays: state.rays.map(ray => (ray.rayId === rayId)
+ ? { ...ray, ...(typeof update === 'function' ? update(ray) : update) }
+ : ray,
+ ),
+ })),
syncRaysStateToBeam: () => {