diff --git a/apps/rowboat/app/actions/project_actions.ts b/apps/rowboat/app/actions/project_actions.ts index 163501cf..1945751c 100644 --- a/apps/rowboat/app/actions/project_actions.ts +++ b/apps/rowboat/app/actions/project_actions.ts @@ -21,11 +21,9 @@ export async function projectAuthCheck(projectId: string) { throw new Error('User not a member of project'); } } -export async function createProject(formData: FormData) { - const user = await authCheck(); - // ensure that projects created by this user is less than - // configured limit +async function createBaseProject(name: string, user: any) { + // Check project limits const projectsLimit = Number(process.env.MAX_PROJECTS_PER_USER) || 0; if (projectsLimit > 0) { const count = await projectsCollection.countDocuments({ @@ -36,16 +34,14 @@ export async function createProject(formData: FormData) { } } - const name = formData.get('name') as string; - const templateKey = formData.get('template') as string; const projectId = crypto.randomUUID(); const chatClientId = crypto.randomBytes(16).toString('base64url'); const secret = crypto.randomBytes(32).toString('hex'); - // create project + // Create project await projectsCollection.insertOne({ _id: projectId, - name: name, + name, createdAt: (new Date()).toISOString(), lastUpdatedAt: (new Date()).toISOString(), createdByUserId: user.sub, @@ -55,7 +51,28 @@ export async function createProject(formData: FormData) { testRunCounter: 0, }); - // add first workflow version + // Add user to project + await projectMembersCollection.insertOne({ + userId: user.sub, + projectId: projectId, + createdAt: (new Date()).toISOString(), + lastUpdatedAt: (new Date()).toISOString(), + }); + + // Add first api key + await createApiKey(projectId); + + return projectId; +} + +export async function createProject(formData: FormData) { + const user = await authCheck(); + const name = formData.get('name') as string; + const templateKey = formData.get('template') as string; + + const projectId = await createBaseProject(name, user); + + // Add first workflow version with specified template const { agents, prompts, tools, startAgent } = templates[templateKey]; await agentWorkflowsCollection.insertOne({ projectId, @@ -68,17 +85,6 @@ export async function createProject(formData: FormData) { name: `Version 1`, }); - // add user to project - await projectMembersCollection.insertOne({ - userId: user.sub, - projectId: projectId, - createdAt: (new Date()).toISOString(), - lastUpdatedAt: (new Date()).toISOString(), - }); - - // add first api key - await createApiKey(projectId); - redirect(`/projects/${projectId}/workflow`); } @@ -212,3 +218,25 @@ export async function deleteProject(projectId: string) { redirect('/projects'); } + +export async function createProjectFromPrompt(formData: FormData) { + const user = await authCheck(); + const name = formData.get('name') as string; + + const projectId = await createBaseProject(name, user); + + // Add first workflow version with default template + const { agents, prompts, tools, startAgent } = templates['default']; + await agentWorkflowsCollection.insertOne({ + projectId, + agents, + prompts, + tools, + startAgent, + createdAt: (new Date()).toISOString(), + lastUpdatedAt: (new Date()).toISOString(), + name: `Version 1`, + }); + + return { id: projectId }; +} diff --git a/apps/rowboat/app/lib/project_templates.ts b/apps/rowboat/app/lib/project_templates.ts index 900a48dd..9fe37ec4 100644 --- a/apps/rowboat/app/lib/project_templates.ts +++ b/apps/rowboat/app/lib/project_templates.ts @@ -347,4 +347,12 @@ You are an helpful customer support assistant } ], } +} + +export const starting_copilot_prompts: { [key: string]: string } = { + "Credit Card Assistant": "Create a credit card assistant that helps users with credit card related queries like card recommendations, benefits, rewards, application process, and general credit card advice. Provide accurate and helpful information while maintaining a professional and friendly tone.", + + "Scheduling Assistant": "Create an appointment scheduling assistant that helps users schedule, modify, and manage their appointments efficiently. Help with finding available time slots, sending reminders, rescheduling appointments, and answering questions about scheduling policies and procedures. Maintain a professional and organized approach.", + + "Banking Assistant": "Create a banking assistant focused on helping customers with their banking needs. Help with account inquiries, banking products and services, transaction information, and general banking guidance. Prioritize accuracy and security while providing clear and helpful responses to banking-related questions." } \ No newline at end of file diff --git a/apps/rowboat/app/projects/[projectId]/playground/app.tsx b/apps/rowboat/app/projects/[projectId]/playground/app.tsx index 17d304d4..bab4dce6 100644 --- a/apps/rowboat/app/projects/[projectId]/playground/app.tsx +++ b/apps/rowboat/app/projects/[projectId]/playground/app.tsx @@ -44,35 +44,6 @@ export function App({ setCounter(counter + 1); } - // const beginSimulation = useCallback((scenario: string) => { - // setExistingChatId(null); - // setLoadingChat(true); - // setCounter(counter + 1); - // setChat({ - // projectId, - // createdAt: new Date().toISOString(), - // messages: [], - // simulated: true, - // simulationScenario: scenario, - // systemMessage: '', - // }); - // }, [counter, projectId]); - - // useEffect(() => { - // const scenarioId = localStorage.getItem('pendingScenarioId'); - // if (scenarioId && projectId) { - // console.log('Scenario Effect triggered:', { scenarioId, projectId }); - // getScenario(projectId, scenarioId).then((scenario) => { - // console.log('Scenario data received:', scenario); - // beginSimulation(scenario.description); - // localStorage.removeItem('pendingScenarioId'); - // }).catch(error => { - // console.error('Error fetching scenario:', error); - // localStorage.removeItem('pendingScenarioId'); - // }); - // } - // }, [projectId, beginSimulation]); - if (hidden) { return <>; } diff --git a/apps/rowboat/app/projects/[projectId]/workflow/copilot.tsx b/apps/rowboat/app/projects/[projectId]/workflow/copilot.tsx index c2d267c1..c7c1bd12 100644 --- a/apps/rowboat/app/projects/[projectId]/workflow/copilot.tsx +++ b/apps/rowboat/app/projects/[projectId]/workflow/copilot.tsx @@ -16,6 +16,7 @@ import { Action as WorkflowDispatch } from "./workflow_editor"; import MarkdownContent from "../../../lib/components/markdown-content"; import { CopyAsJsonButton } from "../playground/copy-as-json-button"; import { CornerDownLeftIcon, SendIcon } from "lucide-react"; +import { useSearchParams } from 'next/navigation'; const CopilotContext = createContext<{ @@ -528,6 +529,24 @@ export function Copilot({ responseError: string | null; setResponseError: (error: string | null) => void; }) { + const searchParams = useSearchParams(); + + // Check for initial prompt in URL and send it + useEffect(() => { + const prompt = searchParams.get('prompt'); + if (prompt && messages.length === 0) { + setMessages([{ + role: 'user', + content: prompt + }]); + + // Clean up the URL + const url = new URL(window.location.href); + url.searchParams.delete('prompt'); + window.history.replaceState({}, '', url); + } + }, [searchParams, messages.length, setMessages]); + return ( (null); + const searchParams = useSearchParams(); const [isMcpImportModalOpen, setIsMcpImportModalOpen] = useState(false); console.log(`workflow editor chat key: ${state.present.chatKey}`); + // Auto-show copilot and increment key when prompt is present + useEffect(() => { + const prompt = searchParams.get('prompt'); + if (prompt) { + setShowCopilot(true); + setCopilotKey(prev => prev + 1); // Force copilot to reset + + // Clean up the URL + const url = new URL(window.location.href); + url.searchParams.delete('prompt'); + window.history.replaceState({}, '', url); + } + }, [searchParams]); + function handleSelectAgent(name: string) { dispatch({ type: "select_agent", name }); } diff --git a/apps/rowboat/app/projects/new/app.tsx b/apps/rowboat/app/projects/new/app.tsx index eb2de1b5..739c23e9 100644 --- a/apps/rowboat/app/projects/new/app.tsx +++ b/apps/rowboat/app/projects/new/app.tsx @@ -1,42 +1,155 @@ 'use client'; -import { cn, Input } from "@heroui/react"; -import { createProject } from "../../actions/project_actions"; -import { templates } from "../../lib/project_templates"; +import { cn, Input, Textarea } from "@heroui/react"; +import { createProject, createProjectFromPrompt } from "../../actions/project_actions"; +import { templates, starting_copilot_prompts } from "../../lib/project_templates"; import { WorkflowTemplate } from "../../lib/types/workflow_types"; import { FormStatusButton } from "../../lib/components/form-status-button"; import { useFormStatus } from "react-dom"; import { z } from "zod"; import { useState } from "react"; -import { CheckIcon, PlusIcon } from "lucide-react"; +import { CheckIcon, PlusIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"; +import { useRouter } from 'next/navigation'; +import React from "react"; -function TemplateCard({ - templateKey, - template, +function CustomPromptCard({ onSelect, - selected + selected, + onPromptChange, + customPrompt }: { - templateKey: string, - template: z.infer, - onSelect: (templateKey: string) => void, - selected: boolean + onSelect: () => void, + selected: boolean, + onPromptChange: (prompt: string) => void, + customPrompt: string }) { return