mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-16 18:25:17 +02:00
Update tool types to include custom and library tools and add web_search tool
Add web_search tool and change tool definition
This commit is contained in:
parent
6a4579bec5
commit
aad14804ea
12 changed files with 66 additions and 183 deletions
|
|
@ -74,6 +74,8 @@ export async function getCopilotResponse(
|
|||
const test = {
|
||||
name: 'test',
|
||||
description: 'test',
|
||||
type: 'custom' as const,
|
||||
implementation: 'mock' as const,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ export async function fetchMcpTools(projectId: string): Promise<z.infer<typeof W
|
|||
const tool: z.infer<typeof WorkflowTool> = {
|
||||
name: mcpTool.name,
|
||||
description: mcpTool.description ?? "",
|
||||
type: 'library',
|
||||
implementation: 'default',
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: props ?? {},
|
||||
|
|
|
|||
|
|
@ -56,7 +56,18 @@ You are an helpful customer support assistant
|
|||
prompt: "Hello! How can I help you?"
|
||||
}
|
||||
],
|
||||
tools: [],
|
||||
tools: [
|
||||
{
|
||||
"name": "web_search",
|
||||
"description": "Fetch information from the web based on chat context",
|
||||
"type": "library",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
},
|
||||
"implementation": "default",
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,15 @@ export const WorkflowPrompt = z.object({
|
|||
export const WorkflowTool = z.object({
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
mockTool: z.boolean().default(false).optional(),
|
||||
type: z.union([
|
||||
z.literal('library'),
|
||||
z.literal('custom'),
|
||||
]).default('custom'),
|
||||
implementation: z.union([
|
||||
z.literal('mock'),
|
||||
z.literal('default'),
|
||||
z.literal('api')
|
||||
]).default('mock'),
|
||||
autoSubmitMockedResponse: z.boolean().default(false).optional(),
|
||||
mockInstructions: z.string().optional(),
|
||||
parameters: z.object({
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ export function AgentConfig({
|
|||
if (!USE_TRANSFER_CONTROL_OPTIONS && agent.controlType !== 'retain') {
|
||||
handleUpdate({ ...agent, controlType: 'retain' });
|
||||
}
|
||||
}, [USE_TRANSFER_CONTROL_OPTIONS, agent.controlType, agent, handleUpdate]);
|
||||
}, [agent.controlType, agent, handleUpdate]);
|
||||
|
||||
const validateName = (value: string) => {
|
||||
if (value.length === 0) {
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ export function ToolConfig({
|
|||
}
|
||||
>
|
||||
<div className="flex flex-col gap-6 p-4">
|
||||
{!isReadOnly && (
|
||||
{!isReadOnly && tool.type !== 'library' && (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<label className={sectionHeaderStyles}>
|
||||
|
|
@ -310,7 +310,7 @@ export function ToolConfig({
|
|||
description: e.target.value
|
||||
})}
|
||||
placeholder="Describe what this tool does..."
|
||||
disabled={isReadOnly}
|
||||
disabled={isReadOnly || tool.type === 'library'}
|
||||
className={textareaStyles}
|
||||
autoResize
|
||||
/>
|
||||
|
|
@ -324,12 +324,13 @@ export function ToolConfig({
|
|||
</label>
|
||||
|
||||
<RadioGroup
|
||||
defaultValue="mock"
|
||||
value={tool.mockTool ? "mock" : "api"}
|
||||
defaultValue={tool.implementation}
|
||||
value={tool.implementation}
|
||||
onValueChange={(value) => handleUpdate({
|
||||
...tool,
|
||||
mockTool: value === "mock",
|
||||
autoSubmitMockedResponse: value === "mock" ? true : undefined
|
||||
implementation: value as "mock" | "default" | "api",
|
||||
autoSubmitMockedResponse: value === "mock" ? true : undefined,
|
||||
mockInstructions: value === "mock" ? tool.mockInstructions : undefined
|
||||
})}
|
||||
orientation="horizontal"
|
||||
classNames={{
|
||||
|
|
@ -346,20 +347,32 @@ export function ToolConfig({
|
|||
>
|
||||
Mock tool responses
|
||||
</Radio>
|
||||
<Radio
|
||||
value="api"
|
||||
classNames={{
|
||||
base: "p-0 data-[selected=true]:bg-indigo-50 dark:data-[selected=true]:bg-indigo-900/50 rounded-lg transition-colors",
|
||||
label: "text-base font-normal text-gray-900 dark:text-gray-100 px-3 py-1"
|
||||
}}
|
||||
>
|
||||
Connect tool to your API
|
||||
</Radio>
|
||||
{tool.type === "library" ? (
|
||||
<Radio
|
||||
value="default"
|
||||
classNames={{
|
||||
base: "p-0 data-[selected=true]:bg-indigo-50 dark:data-[selected=true]:bg-indigo-900/50 rounded-lg transition-colors",
|
||||
label: "text-base font-normal text-gray-900 dark:text-gray-100 px-3 py-1"
|
||||
}}
|
||||
>
|
||||
Use default implementation
|
||||
</Radio>
|
||||
) : (
|
||||
<Radio
|
||||
value="api"
|
||||
classNames={{
|
||||
base: "p-0 data-[selected=true]:bg-indigo-50 dark:data-[selected=true]:bg-indigo-900/50 rounded-lg transition-colors",
|
||||
label: "text-base font-normal text-gray-900 dark:text-gray-100 px-3 py-1"
|
||||
}}
|
||||
>
|
||||
Connect tool to your API
|
||||
</Radio>
|
||||
)}
|
||||
</RadioGroup>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{tool.mockTool && (
|
||||
{tool.implementation === "mock" && (
|
||||
<div className={`space-y-4 ${dividerStyles} pt-6`}>
|
||||
<div className="space-y-4">
|
||||
<label className={sectionHeaderStyles}>
|
||||
|
|
@ -411,12 +424,12 @@ export function ToolConfig({
|
|||
handleUpdate={handleParamUpdate}
|
||||
handleDelete={handleParamDelete}
|
||||
handleRename={handleParamRename}
|
||||
readOnly={isReadOnly}
|
||||
readOnly={isReadOnly || tool.type === 'library'}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{!isReadOnly && (
|
||||
{!isReadOnly && tool.type !== 'library' && (
|
||||
<div className="pl-3">
|
||||
<Button
|
||||
variant="primary"
|
||||
|
|
@ -434,6 +447,8 @@ export function ToolConfig({
|
|||
|
||||
handleUpdate({
|
||||
...tool,
|
||||
type: 'custom',
|
||||
implementation: 'mock',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: newProperties,
|
||||
|
|
|
|||
|
|
@ -210,7 +210,10 @@ export function EntityList({
|
|||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => onAddTool({})}
|
||||
onClick={() => onAddTool({
|
||||
type: 'custom',
|
||||
implementation: 'mock'
|
||||
})}
|
||||
className={`group ${buttonClasses}`}
|
||||
showHoverContent={true}
|
||||
hoverContent="Add Tool"
|
||||
|
|
|
|||
|
|
@ -290,10 +290,11 @@ function reducer(state: State, action: Action): State {
|
|||
name: newToolName,
|
||||
description: "",
|
||||
parameters: {
|
||||
type: "object",
|
||||
type: 'object',
|
||||
properties: {},
|
||||
},
|
||||
mockTool: true,
|
||||
type: 'custom',
|
||||
implementation: 'mock',
|
||||
autoSubmitMockedResponse: true,
|
||||
...action.tool
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { Project } from "@/app/lib/types/project_types";
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import { z } from "zod";
|
||||
import { createProject, createProjectFromPrompt } from "@/app/actions/project_actions";
|
||||
import { useRouter } from 'next/navigation';
|
||||
import clsx from 'clsx';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { Project } from "@/app/lib/types/project_types";
|
||||
import { z } from "zod";
|
||||
import { ProjectList } from "./project-list";
|
||||
import { SectionHeading } from "@/components/ui/section-heading";
|
||||
import { HorizontalDivider } from "@/components/ui/horizontal-divider";
|
||||
import clsx from 'clsx';
|
||||
import { XMarkIcon } from "@heroicons/react/24/outline";
|
||||
|
|
|
|||
|
|
@ -1,101 +0,0 @@
|
|||
'use client';
|
||||
import clsx from 'clsx';
|
||||
import { CheckIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import React from "react";
|
||||
import { WorkflowTemplate } from "@/app/lib/types/workflow_types";
|
||||
import { z } from "zod";
|
||||
import { tokens } from "@/app/styles/design-tokens";
|
||||
|
||||
interface TemplateCardProps {
|
||||
templateKey: string;
|
||||
template: z.infer<typeof WorkflowTemplate> | string;
|
||||
onSelect: (templateKey: string) => void;
|
||||
selected: boolean;
|
||||
type?: "template" | "prompt";
|
||||
}
|
||||
|
||||
export function TemplateCard({
|
||||
templateKey,
|
||||
template,
|
||||
onSelect,
|
||||
selected,
|
||||
type = "template"
|
||||
}: TemplateCardProps) {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const name = typeof template === "string" ? templateKey : template.name;
|
||||
const description = typeof template === "string" ? template : template.description;
|
||||
|
||||
const textRef = React.useRef<HTMLDivElement>(null);
|
||||
const [needsExpansion, setNeedsExpansion] = useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (textRef.current) {
|
||||
const needsButton = textRef.current.scrollHeight > textRef.current.clientHeight;
|
||||
setNeedsExpansion(needsButton);
|
||||
}
|
||||
}, [description]);
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={() => onSelect(templateKey)}
|
||||
className={clsx(
|
||||
"w-full text-left cursor-pointer",
|
||||
"p-4",
|
||||
tokens.radius.lg,
|
||||
tokens.transitions.default,
|
||||
tokens.shadows.sm,
|
||||
"border",
|
||||
selected ? [
|
||||
"border-indigo-600 dark:border-indigo-400",
|
||||
"bg-indigo-50/50 dark:bg-indigo-500/10",
|
||||
] : [
|
||||
tokens.colors.light.border,
|
||||
tokens.colors.dark.border,
|
||||
tokens.colors.light.surface,
|
||||
tokens.colors.dark.surface,
|
||||
"hover:border-indigo-600/30 dark:hover:border-indigo-400/30",
|
||||
"hover:bg-indigo-50/30 dark:hover:bg-indigo-500/5",
|
||||
"transform hover:scale-[1.01]",
|
||||
tokens.shadows.hover,
|
||||
],
|
||||
tokens.focus.default,
|
||||
tokens.focus.dark
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="flex-1 space-y-2">
|
||||
<h3 className={clsx(
|
||||
tokens.typography.sizes.base,
|
||||
tokens.typography.weights.medium,
|
||||
tokens.colors.light.text.primary,
|
||||
tokens.colors.dark.text.primary
|
||||
)}>
|
||||
{name}
|
||||
</h3>
|
||||
<p className={clsx(
|
||||
tokens.typography.sizes.sm,
|
||||
tokens.colors.light.text.secondary,
|
||||
tokens.colors.dark.text.secondary
|
||||
)}>
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
<div className={clsx(
|
||||
"w-5 h-5 rounded-full border-2",
|
||||
tokens.transitions.default,
|
||||
selected ? [
|
||||
"border-indigo-600 dark:border-indigo-400",
|
||||
"bg-indigo-600 dark:bg-indigo-400",
|
||||
] : [
|
||||
"border-gray-300 dark:border-gray-600",
|
||||
]
|
||||
)}>
|
||||
{selected && (
|
||||
<CheckIcon className="w-4 h-4 text-white" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
import { templates, starting_copilot_prompts } from "@/app/lib/project_templates";
|
||||
import { TemplateCard } from "./template-card";
|
||||
import { WorkflowTemplate } from "@/types/workflow_types";
|
||||
import { z } from "zod";
|
||||
|
||||
// Use the existing template type but make id optional
|
||||
type Template = z.infer<typeof WorkflowTemplate> & {
|
||||
id?: string;
|
||||
prompt?: string;
|
||||
};
|
||||
|
||||
type TemplateCardsListProps = {
|
||||
selectedCard: 'custom' | Template;
|
||||
onSelectCard: (template: Template) => void;
|
||||
};
|
||||
|
||||
export function TemplateCardsList({ selectedCard, onSelectCard }: TemplateCardsListProps) {
|
||||
return (
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{Object.entries(templates).map(([id, template]) => (
|
||||
<TemplateCard
|
||||
key={id}
|
||||
templateKey={id}
|
||||
template={template} // Remove the type assertion
|
||||
selected={selectedCard !== 'custom' && selectedCard.id === id}
|
||||
onSelect={() => onSelectCard({ ...template, id })}
|
||||
/>
|
||||
))}
|
||||
|
||||
{Object.entries(starting_copilot_prompts).map(([name, prompt]) => {
|
||||
// Create a template-compatible object
|
||||
const promptTemplate: Template = {
|
||||
name,
|
||||
description: prompt,
|
||||
prompt,
|
||||
id: name.toLowerCase(),
|
||||
agents: [], // Required by WorkflowTemplate
|
||||
prompts: [], // Required by WorkflowTemplate
|
||||
tools: [], // Required by WorkflowTemplate
|
||||
startAgent: '' // Required by WorkflowTemplate
|
||||
};
|
||||
|
||||
return (
|
||||
<TemplateCard
|
||||
key={name}
|
||||
templateKey={name.toLowerCase()}
|
||||
template={promptTemplate}
|
||||
selected={selectedCard !== 'custom' && selectedCard.id === name.toLowerCase()}
|
||||
onSelect={() => onSelectCard(promptTemplate)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue