chore: Update function calling add menu (#40139)
## Description Update the add menu responsibilites <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced an enhanced menu for adding function calls with options to create new queries or JavaScript objects and a curated list of available items. - **Refactor** - Updated the interface for function call configuration by streamlining legacy options, resulting in a more consistent and responsive user experience. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
df34a10205
commit
326023d0ab
|
|
@ -1,10 +1,34 @@
|
||||||
import { Button, Text } from "@appsmith/ads";
|
import {
|
||||||
import React, { useCallback, useState } from "react";
|
Button,
|
||||||
|
Flex,
|
||||||
|
Icon,
|
||||||
|
Menu,
|
||||||
|
MenuContent,
|
||||||
|
MenuGroup,
|
||||||
|
MenuItem,
|
||||||
|
MenuSub,
|
||||||
|
MenuSubContent,
|
||||||
|
MenuSubTrigger,
|
||||||
|
MenuTrigger,
|
||||||
|
Text,
|
||||||
|
} from "@appsmith/ads";
|
||||||
|
import React, { useCallback, useMemo, useState } from "react";
|
||||||
import type { FieldArrayFieldsProps } from "redux-form";
|
import type { FieldArrayFieldsProps } from "redux-form";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { v4 as uuid } from "uuid";
|
import { v4 as uuid } from "uuid";
|
||||||
import type { FunctionCallingConfigFormToolField } from "../types";
|
import type {
|
||||||
|
FunctionCallingConfigFormToolField,
|
||||||
|
FunctionCallingEntityType,
|
||||||
|
FunctionCallingEntityTypeOption,
|
||||||
|
} from "../types";
|
||||||
import { FunctionCallingConfigToolField } from "./FunctionCallingConfigToolField";
|
import { FunctionCallingConfigToolField } from "./FunctionCallingConfigToolField";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { selectEntityOptions } from "./selectors";
|
||||||
|
import { createNewJSCollection } from "actions/jsPaneActions";
|
||||||
|
import { queryAddURL } from "ee/RouteBuilder";
|
||||||
|
import history from "utils/history";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { getCurrentPageId } from "selectors/editorSelectors";
|
||||||
|
|
||||||
export interface FunctionCallingConfigFormProps {
|
export interface FunctionCallingConfigFormProps {
|
||||||
formName: string;
|
formName: string;
|
||||||
|
|
@ -29,19 +53,48 @@ export const FunctionCallingConfigForm = ({
|
||||||
formName,
|
formName,
|
||||||
}: FunctionCallingConfigFormProps) => {
|
}: FunctionCallingConfigFormProps) => {
|
||||||
const [newlyAddedId, setNewlyAddedId] = useState<string | null>(null);
|
const [newlyAddedId, setNewlyAddedId] = useState<string | null>(null);
|
||||||
|
const options = useSelector(selectEntityOptions);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const pageId = useSelector(getCurrentPageId);
|
||||||
|
|
||||||
const handleAddFunctionButtonClick = useCallback(() => {
|
// Get existing entity IDs from the agentFunctions, excluding the currently selected value
|
||||||
|
const existingEntityIds = useMemo(() => {
|
||||||
|
const agentFunctionIds = new Set(Object.keys(options.agentFunctions));
|
||||||
|
|
||||||
|
return agentFunctionIds;
|
||||||
|
}, [options.agentFunctions]);
|
||||||
|
|
||||||
|
// Filter query items to exclude existing entities
|
||||||
|
const filteredQueryItems = useMemo(() => {
|
||||||
|
return options.Query.filter((item) => !existingEntityIds.has(item.value));
|
||||||
|
}, [options.Query, existingEntityIds]);
|
||||||
|
|
||||||
|
// Filter JS collection items
|
||||||
|
const filteredJSCollections = useMemo(() => {
|
||||||
|
return options.JSCollections.map((collection) => ({
|
||||||
|
...collection,
|
||||||
|
// Filter out functions that are already used
|
||||||
|
functions: collection.functions.filter(
|
||||||
|
(func) => !existingEntityIds.has(func.value),
|
||||||
|
),
|
||||||
|
})).filter((collection) => collection.functions.length > 0);
|
||||||
|
}, [options.JSCollections, existingEntityIds]);
|
||||||
|
|
||||||
|
const handleAddFunctionButtonClick = useCallback(
|
||||||
|
(option: FunctionCallingEntityTypeOption) => {
|
||||||
const id = uuid();
|
const id = uuid();
|
||||||
|
|
||||||
fields.push({
|
fields.push({
|
||||||
id,
|
id,
|
||||||
description: "",
|
description: "",
|
||||||
entityId: "",
|
entityId: option.value,
|
||||||
isApprovalRequired: false,
|
isApprovalRequired: false,
|
||||||
entityType: "Query",
|
entityType: option.optionGroupType as FunctionCallingEntityType,
|
||||||
});
|
});
|
||||||
setNewlyAddedId(id);
|
setNewlyAddedId(id);
|
||||||
}, [fields]);
|
},
|
||||||
|
[fields],
|
||||||
|
);
|
||||||
|
|
||||||
const handleRemoveToolButtonClick = useCallback(
|
const handleRemoveToolButtonClick = useCallback(
|
||||||
(index: number) => {
|
(index: number) => {
|
||||||
|
|
@ -54,17 +107,100 @@ export const FunctionCallingConfigForm = ({
|
||||||
<>
|
<>
|
||||||
<Header>
|
<Header>
|
||||||
<Text isBold kind="heading-s" renderAs="p">
|
<Text isBold kind="heading-s" renderAs="p">
|
||||||
Function calls
|
Function Calls
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Button
|
<Menu>
|
||||||
UNSAFE_width="110px"
|
<MenuTrigger>
|
||||||
kind="secondary"
|
<Button UNSAFE_width="110px" kind="secondary" startIcon="plus">
|
||||||
onClick={handleAddFunctionButtonClick}
|
|
||||||
startIcon="plus"
|
|
||||||
>
|
|
||||||
Add Function
|
Add Function
|
||||||
</Button>
|
</Button>
|
||||||
|
</MenuTrigger>
|
||||||
|
<MenuContent align="end" loop width="235px">
|
||||||
|
{/* Create new options */}
|
||||||
|
<MenuGroup>
|
||||||
|
<MenuItem onSelect={() => history.push(queryAddURL({}))}>
|
||||||
|
<Flex alignItems="center" gap="spaces-2">
|
||||||
|
<Icon name="plus" size="md" />
|
||||||
|
New Query
|
||||||
|
</Flex>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
onSelect={() =>
|
||||||
|
dispatch(
|
||||||
|
createNewJSCollection(
|
||||||
|
pageId,
|
||||||
|
"AI_QUERY_FUNCTION_CALLING_CONFIG",
|
||||||
|
"onToolCall",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Flex alignItems="center" gap="spaces-2">
|
||||||
|
<Icon name="plus" size="md" />
|
||||||
|
New JS Object
|
||||||
|
</Flex>
|
||||||
|
</MenuItem>
|
||||||
|
</MenuGroup>
|
||||||
|
|
||||||
|
{/* Query options group */}
|
||||||
|
{options.Query.length > 0 && (
|
||||||
|
<MenuGroup>
|
||||||
|
{filteredQueryItems.map((option) => (
|
||||||
|
<MenuItem
|
||||||
|
key={option.value}
|
||||||
|
onSelect={() => handleAddFunctionButtonClick(option)}
|
||||||
|
>
|
||||||
|
<Flex alignItems="center" gap="spaces-2">
|
||||||
|
{option.icon &&
|
||||||
|
(typeof option.icon === "string" ? (
|
||||||
|
<Icon name={option.icon} size="md" />
|
||||||
|
) : (
|
||||||
|
option.icon
|
||||||
|
))}
|
||||||
|
{option.label}
|
||||||
|
</Flex>
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</MenuGroup>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* JS Collections group with nested functions */}
|
||||||
|
{options.JSCollections.length > 0 && (
|
||||||
|
<MenuGroup>
|
||||||
|
<MenuGroup>
|
||||||
|
{filteredJSCollections.map((collection) => (
|
||||||
|
<MenuSub key={collection.id}>
|
||||||
|
<MenuSubTrigger>
|
||||||
|
<Flex alignItems="center" gap="spaces-2">
|
||||||
|
{collection.icon &&
|
||||||
|
(typeof collection.icon === "string" ? (
|
||||||
|
<Icon name={collection.icon} size="md" />
|
||||||
|
) : (
|
||||||
|
collection.icon
|
||||||
|
))}
|
||||||
|
{collection.name}
|
||||||
|
</Flex>
|
||||||
|
</MenuSubTrigger>
|
||||||
|
<MenuSubContent>
|
||||||
|
{collection.functions.map((jsFunction) => (
|
||||||
|
<MenuItem
|
||||||
|
key={jsFunction.value}
|
||||||
|
onSelect={() =>
|
||||||
|
handleAddFunctionButtonClick(jsFunction)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{jsFunction.label}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</MenuSubContent>
|
||||||
|
</MenuSub>
|
||||||
|
))}
|
||||||
|
</MenuGroup>
|
||||||
|
</MenuGroup>
|
||||||
|
)}
|
||||||
|
</MenuContent>
|
||||||
|
</Menu>
|
||||||
</Header>
|
</Header>
|
||||||
|
|
||||||
{fields.length > 0 && (
|
{fields.length > 0 && (
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,8 @@ import type {
|
||||||
FunctionCallingEntityType,
|
FunctionCallingEntityType,
|
||||||
FunctionCallingEntityTypeOption,
|
FunctionCallingEntityTypeOption,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
import { useSelector, useDispatch } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { selectEntityOptions } from "./selectors";
|
import { selectEntityOptions } from "./selectors";
|
||||||
import history from "utils/history";
|
|
||||||
import { queryAddURL } from "ee/RouteBuilder";
|
|
||||||
import { createNewJSCollection } from "actions/jsPaneActions";
|
|
||||||
import { getCurrentPageId } from "selectors/editorSelectors";
|
|
||||||
|
|
||||||
interface FunctionCallingMenuFieldProps {
|
interface FunctionCallingMenuFieldProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
|
|
@ -86,9 +82,6 @@ const FunctionCallingMenuFieldRender = (props: FieldRenderProps) => {
|
||||||
const { autoFocus, children, disabled, input, onValueChange } = props;
|
const { autoFocus, children, disabled, input, onValueChange } = props;
|
||||||
const options = useSelector(selectEntityOptions);
|
const options = useSelector(selectEntityOptions);
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const pageId = useSelector(getCurrentPageId);
|
|
||||||
|
|
||||||
// Get existing entity IDs from the agentFunctions, excluding the currently selected value
|
// Get existing entity IDs from the agentFunctions, excluding the currently selected value
|
||||||
const existingEntityIds = useMemo(() => {
|
const existingEntityIds = useMemo(() => {
|
||||||
const agentFunctionIds = new Set(Object.keys(options.agentFunctions));
|
const agentFunctionIds = new Set(Object.keys(options.agentFunctions));
|
||||||
|
|
@ -166,32 +159,6 @@ const FunctionCallingMenuFieldRender = (props: FieldRenderProps) => {
|
||||||
</Button>
|
</Button>
|
||||||
</MenuTrigger>
|
</MenuTrigger>
|
||||||
<MenuContent align="start" loop width="235px">
|
<MenuContent align="start" loop width="235px">
|
||||||
{/* Create new options */}
|
|
||||||
<MenuGroup>
|
|
||||||
<MenuItem onSelect={() => history.push(queryAddURL({}))}>
|
|
||||||
<Flex alignItems="center" gap="spaces-2">
|
|
||||||
<Icon name="plus" size="md" />
|
|
||||||
New Query
|
|
||||||
</Flex>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
onSelect={() =>
|
|
||||||
dispatch(
|
|
||||||
createNewJSCollection(
|
|
||||||
pageId,
|
|
||||||
"AI_QUERY_FUNCTION_CALLING_CONFIG",
|
|
||||||
"onToolCall",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Flex alignItems="center" gap="spaces-2">
|
|
||||||
<Icon name="plus" size="md" />
|
|
||||||
New JS Object
|
|
||||||
</Flex>
|
|
||||||
</MenuItem>
|
|
||||||
</MenuGroup>
|
|
||||||
|
|
||||||
{/* Query options group */}
|
{/* Query options group */}
|
||||||
{filteredQueryItems.length > 0 && (
|
{filteredQueryItems.length > 0 && (
|
||||||
<MenuGroup>
|
<MenuGroup>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user