mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-31 19:15:17 +02:00
Show update instructions CTA to trigger copilot when new data sources are attached to agents
This commit is contained in:
parent
e4cd71e6b1
commit
5bc1dc8b37
3 changed files with 82 additions and 14 deletions
|
|
@ -34,7 +34,7 @@ interface AppProps {
|
||||||
dataSources?: z.infer<typeof DataSource>[];
|
dataSources?: z.infer<typeof DataSource>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const App = forwardRef<{ handleCopyChat: () => void }, AppProps>(function App({
|
const App = forwardRef<{ handleCopyChat: () => void; handleUserMessage: (message: string) => void }, AppProps>(function App({
|
||||||
projectId,
|
projectId,
|
||||||
workflow,
|
workflow,
|
||||||
dispatch,
|
dispatch,
|
||||||
|
|
@ -133,7 +133,8 @@ const App = forwardRef<{ handleCopyChat: () => void }, AppProps>(function App({
|
||||||
}, [messages, onCopyJson]);
|
}, [messages, onCopyJson]);
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
handleCopyChat
|
handleCopyChat,
|
||||||
|
handleUserMessage
|
||||||
}), [handleCopyChat]);
|
}), [handleCopyChat]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -194,25 +195,25 @@ const App = forwardRef<{ handleCopyChat: () => void }, AppProps>(function App({
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export function Copilot({
|
export const Copilot = forwardRef<{ handleUserMessage: (message: string) => void }, {
|
||||||
projectId,
|
|
||||||
workflow,
|
|
||||||
chatContext = undefined,
|
|
||||||
dispatch,
|
|
||||||
isInitialState = false,
|
|
||||||
dataSources,
|
|
||||||
}: {
|
|
||||||
projectId: string;
|
projectId: string;
|
||||||
workflow: z.infer<typeof Workflow>;
|
workflow: z.infer<typeof Workflow>;
|
||||||
chatContext?: z.infer<typeof CopilotChatContext>;
|
chatContext?: z.infer<typeof CopilotChatContext>;
|
||||||
dispatch: (action: WorkflowDispatch) => void;
|
dispatch: (action: WorkflowDispatch) => void;
|
||||||
isInitialState?: boolean;
|
isInitialState?: boolean;
|
||||||
dataSources?: z.infer<typeof DataSource>[];
|
dataSources?: z.infer<typeof DataSource>[];
|
||||||
}) {
|
}>(({
|
||||||
|
projectId,
|
||||||
|
workflow,
|
||||||
|
chatContext = undefined,
|
||||||
|
dispatch,
|
||||||
|
isInitialState = false,
|
||||||
|
dataSources,
|
||||||
|
}, ref) => {
|
||||||
const [copilotKey, setCopilotKey] = useState(0);
|
const [copilotKey, setCopilotKey] = useState(0);
|
||||||
const [showCopySuccess, setShowCopySuccess] = useState(false);
|
const [showCopySuccess, setShowCopySuccess] = useState(false);
|
||||||
const [messages, setMessages] = useState<z.infer<typeof CopilotMessage>[]>([]);
|
const [messages, setMessages] = useState<z.infer<typeof CopilotMessage>[]>([]);
|
||||||
const appRef = useRef<{ handleCopyChat: () => void }>(null);
|
const appRef = useRef<{ handleCopyChat: () => void; handleUserMessage: (message: string) => void }>(null);
|
||||||
|
|
||||||
function handleNewChat() {
|
function handleNewChat() {
|
||||||
setCopilotKey(prev => prev + 1);
|
setCopilotKey(prev => prev + 1);
|
||||||
|
|
@ -228,6 +229,16 @@ export function Copilot({
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expose handleUserMessage through ref
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
handleUserMessage: (message: string) => {
|
||||||
|
const app = appRef.current as any;
|
||||||
|
if (app?.handleUserMessage) {
|
||||||
|
app.handleUserMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}), []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Panel variant="copilot"
|
<Panel variant="copilot"
|
||||||
tourTarget="copilot"
|
tourTarget="copilot"
|
||||||
|
|
@ -288,5 +299,5 @@ export function Copilot({
|
||||||
</div>
|
</div>
|
||||||
</Panel>
|
</Panel>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import { EditableField } from "@/app/lib/components/editable-field";
|
||||||
import { USE_TRANSFER_CONTROL_OPTIONS } from "@/app/lib/feature_flags";
|
import { USE_TRANSFER_CONTROL_OPTIONS } from "@/app/lib/feature_flags";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Info } from "lucide-react";
|
import { Info } from "lucide-react";
|
||||||
|
import { useCopilot } from "../copilot/use-copilot";
|
||||||
|
|
||||||
// Common section header styles
|
// Common section header styles
|
||||||
const sectionHeaderStyles = "text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-400";
|
const sectionHeaderStyles = "text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-400";
|
||||||
|
|
@ -44,6 +45,7 @@ export function AgentConfig({
|
||||||
handleUpdate,
|
handleUpdate,
|
||||||
handleClose,
|
handleClose,
|
||||||
useRag,
|
useRag,
|
||||||
|
triggerCopilotChat,
|
||||||
}: {
|
}: {
|
||||||
projectId: string,
|
projectId: string,
|
||||||
workflow: z.infer<typeof Workflow>,
|
workflow: z.infer<typeof Workflow>,
|
||||||
|
|
@ -56,6 +58,7 @@ export function AgentConfig({
|
||||||
handleUpdate: (agent: z.infer<typeof WorkflowAgent>) => void,
|
handleUpdate: (agent: z.infer<typeof WorkflowAgent>) => void,
|
||||||
handleClose: () => void,
|
handleClose: () => void,
|
||||||
useRag: boolean,
|
useRag: boolean,
|
||||||
|
triggerCopilotChat: (message: string) => void,
|
||||||
}) {
|
}) {
|
||||||
const [isAdvancedConfigOpen, setIsAdvancedConfigOpen] = useState(false);
|
const [isAdvancedConfigOpen, setIsAdvancedConfigOpen] = useState(false);
|
||||||
const [showGenerateModal, setShowGenerateModal] = useState(false);
|
const [showGenerateModal, setShowGenerateModal] = useState(false);
|
||||||
|
|
@ -65,11 +68,42 @@ export function AgentConfig({
|
||||||
const [localName, setLocalName] = useState(agent.name);
|
const [localName, setLocalName] = useState(agent.name);
|
||||||
const [nameError, setNameError] = useState<string | null>(null);
|
const [nameError, setNameError] = useState<string | null>(null);
|
||||||
const [activeTab, setActiveTab] = useState<TabType>('instructions');
|
const [activeTab, setActiveTab] = useState<TabType>('instructions');
|
||||||
|
const [showRagCta, setShowRagCta] = useState(false);
|
||||||
|
const [previousRagSources, setPreviousRagSources] = useState<string[]>([]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
start: startCopilotChat,
|
||||||
|
} = useCopilot({
|
||||||
|
projectId,
|
||||||
|
workflow,
|
||||||
|
context: null,
|
||||||
|
dataSources
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLocalName(agent.name);
|
setLocalName(agent.name);
|
||||||
}, [agent.name]);
|
}, [agent.name]);
|
||||||
|
|
||||||
|
// Track changes in RAG datasources
|
||||||
|
useEffect(() => {
|
||||||
|
const currentSources = agent.ragDataSources || [];
|
||||||
|
// Show CTA when transitioning from 0 to 1 datasource
|
||||||
|
if (currentSources.length === 1 && previousRagSources.length === 0) {
|
||||||
|
setShowRagCta(true);
|
||||||
|
}
|
||||||
|
// Hide CTA when all datasources are deleted
|
||||||
|
if (currentSources.length === 0) {
|
||||||
|
setShowRagCta(false);
|
||||||
|
}
|
||||||
|
setPreviousRagSources(currentSources);
|
||||||
|
}, [agent.ragDataSources]);
|
||||||
|
|
||||||
|
const handleUpdateInstructions = async () => {
|
||||||
|
const message = `Update the instructions for agent "${agent.name}" to use the rag tool (rag_search) since data sources have been added. If this has already been done, do not take any action, but let me know.`;
|
||||||
|
triggerCopilotChat(message);
|
||||||
|
setShowRagCta(false);
|
||||||
|
};
|
||||||
|
|
||||||
// Add effect to handle control type update when transfer control is disabled
|
// Add effect to handle control type update when transfer control is disabled
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!USE_TRANSFER_CONTROL_OPTIONS && agent.controlType !== 'retain') {
|
if (!USE_TRANSFER_CONTROL_OPTIONS && agent.controlType !== 'retain') {
|
||||||
|
|
@ -455,7 +489,7 @@ export function AgentConfig({
|
||||||
RAG
|
RAG
|
||||||
</label>
|
</label>
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
<div>
|
<div className="flex items-center gap-3">
|
||||||
<Select
|
<Select
|
||||||
variant="bordered"
|
variant="bordered"
|
||||||
placeholder="Add data source"
|
placeholder="Add data source"
|
||||||
|
|
@ -481,6 +515,17 @@ export function AgentConfig({
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
|
{showRagCta && (
|
||||||
|
<CustomButton
|
||||||
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
|
onClick={handleUpdateInstructions}
|
||||||
|
className="whitespace-nowrap"
|
||||||
|
>
|
||||||
|
Update Instructions
|
||||||
|
</CustomButton>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
|
|
|
||||||
|
|
@ -611,6 +611,16 @@ export function WorkflowEditor({
|
||||||
const [isMcpImportModalOpen, setIsMcpImportModalOpen] = useState(false);
|
const [isMcpImportModalOpen, setIsMcpImportModalOpen] = useState(false);
|
||||||
const [isInitialState, setIsInitialState] = useState(true);
|
const [isInitialState, setIsInitialState] = useState(true);
|
||||||
const [showTour, setShowTour] = useState(true);
|
const [showTour, setShowTour] = useState(true);
|
||||||
|
const copilotRef = useRef<{ handleUserMessage: (message: string) => void }>(null);
|
||||||
|
|
||||||
|
// Function to trigger copilot chat
|
||||||
|
const triggerCopilotChat = useCallback((message: string) => {
|
||||||
|
setShowCopilot(true);
|
||||||
|
// Small delay to ensure copilot is mounted
|
||||||
|
setTimeout(() => {
|
||||||
|
copilotRef.current?.handleUserMessage(message);
|
||||||
|
}, 100);
|
||||||
|
}, []);
|
||||||
|
|
||||||
console.log(`workflow editor chat key: ${state.present.chatKey}`);
|
console.log(`workflow editor chat key: ${state.present.chatKey}`);
|
||||||
|
|
||||||
|
|
@ -992,6 +1002,7 @@ export function WorkflowEditor({
|
||||||
handleUpdate={handleUpdateAgent.bind(null, state.present.selection.name)}
|
handleUpdate={handleUpdateAgent.bind(null, state.present.selection.name)}
|
||||||
handleClose={handleUnselectAgent}
|
handleClose={handleUnselectAgent}
|
||||||
useRag={useRag}
|
useRag={useRag}
|
||||||
|
triggerCopilotChat={triggerCopilotChat}
|
||||||
/>}
|
/>}
|
||||||
{state.present.selection?.type === "tool" && <ToolConfig
|
{state.present.selection?.type === "tool" && <ToolConfig
|
||||||
key={state.present.selection.name}
|
key={state.present.selection.name}
|
||||||
|
|
@ -1020,6 +1031,7 @@ export function WorkflowEditor({
|
||||||
onResize={(size) => setCopilotWidth(size)}
|
onResize={(size) => setCopilotWidth(size)}
|
||||||
>
|
>
|
||||||
<Copilot
|
<Copilot
|
||||||
|
ref={copilotRef}
|
||||||
projectId={state.present.workflow.projectId}
|
projectId={state.present.workflow.projectId}
|
||||||
workflow={state.present.workflow}
|
workflow={state.present.workflow}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue