-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1a60eaa
commit 989b568
Showing
10 changed files
with
477 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
...src/components/formControls/FunctionCallingConfigControl/FunctionCallingConfigControl.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import type { ControlType } from "constants/PropertyControlConstants"; | ||
import React from "react"; | ||
import { FieldArray } from "redux-form"; | ||
import type { ControlProps } from "../BaseControl"; | ||
import BaseControl from "../BaseControl"; | ||
import { FunctionCallingConfigForm } from "./components/FunctionCallingConfigForm"; | ||
|
||
/** | ||
* This component is used to configure the function calling for the AI assistant. | ||
* It allows the user to add, edit and delete functions that can be used by the AI assistant. | ||
*/ | ||
export default class FunctionCallingConfigControl extends BaseControl<ControlProps> { | ||
render() { | ||
return ( | ||
<FieldArray | ||
component={FunctionCallingConfigForm} | ||
key={this.props.configProperty} | ||
name={this.props.configProperty} | ||
props={{ | ||
formName: this.props.formName, | ||
configProperty: this.props.configProperty, | ||
}} | ||
rerenderOnEveryChange={false} | ||
/> | ||
); | ||
} | ||
|
||
getControlType(): ControlType { | ||
return "FUNCTION_CALLING_CONFIG_FORM"; | ||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
...onents/formControls/FunctionCallingConfigControl/components/FunctionCallingConfigForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { Button, Text } from "@appsmith/ads"; | ||
import { default as React, useCallback } from "react"; | ||
import type { FieldArrayFieldsProps } from "redux-form"; | ||
import styled from "styled-components"; | ||
import { v4 as uuid } from "uuid"; | ||
import type { FunctionCallingConfigFormToolField } from "../types"; | ||
import { FunctionCallingConfigToolField } from "./FunctionCallingConfigToolField"; | ||
import { FunctionCallingEmpty } from "./FunctionCallingEmpty"; | ||
|
||
export interface FunctionCallingConfigFormProps { | ||
formName: string; | ||
fields: FieldArrayFieldsProps<FunctionCallingConfigFormToolField>; | ||
} | ||
|
||
const Header = styled.div` | ||
display: flex; | ||
gap: var(--ads-v2-spaces-4); | ||
justify-content: space-between; | ||
margin-bottom: var(--ads-v2-spaces-4); | ||
`; | ||
|
||
const ConfigItems = styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
gap: var(--ads-v2-spaces-4); | ||
`; | ||
|
||
export const FunctionCallingConfigForm = ({ | ||
fields, | ||
formName, | ||
}: FunctionCallingConfigFormProps) => { | ||
const handleAddFunctionButtonClick = useCallback(() => { | ||
fields.push({ | ||
id: uuid(), | ||
description: "", | ||
entityId: "", | ||
requiresApproval: false, | ||
entityType: "Query", | ||
}); | ||
}, [fields]); | ||
|
||
const handleRemoveToolButtonClick = useCallback( | ||
(index: number) => { | ||
fields.remove(index); | ||
}, | ||
[fields], | ||
); | ||
|
||
return ( | ||
<div> | ||
<Header> | ||
<div> | ||
<Text isBold kind="heading-s" renderAs="p"> | ||
Function Calls | ||
</Text> | ||
<Text renderAs="p"> | ||
Add functions for the model to execute dynamically. | ||
</Text> | ||
</div> | ||
|
||
<Button | ||
kind="secondary" | ||
onClick={handleAddFunctionButtonClick} | ||
startIcon="plus" | ||
> | ||
Add Function | ||
</Button> | ||
</Header> | ||
|
||
{fields.length === 0 ? ( | ||
<FunctionCallingEmpty /> | ||
) : ( | ||
<ConfigItems> | ||
{fields.map((field, index) => { | ||
return ( | ||
<FunctionCallingConfigToolField | ||
fieldPath={field} | ||
formName={formName} | ||
index={index} | ||
key={field} | ||
onRemove={handleRemoveToolButtonClick} | ||
/> | ||
); | ||
})} | ||
</ConfigItems> | ||
)} | ||
</div> | ||
); | ||
}; |
175 changes: 175 additions & 0 deletions
175
...s/formControls/FunctionCallingConfigControl/components/FunctionCallingConfigToolField.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
import { Button, Text } from "@appsmith/ads"; | ||
import type { AppState } from "ee/reducers"; | ||
import FormControl from "pages/Editor/FormControl"; | ||
import React, { useCallback, useEffect } from "react"; | ||
import { useDispatch, useSelector } from "react-redux"; | ||
import { change, formValueSelector } from "redux-form"; | ||
import styled from "styled-components"; | ||
import type { DropDownGroupedOptions } from "../../DropDownControl"; | ||
import type { | ||
FunctionCallingEntityTypeOption, | ||
FunctionCallingEntityType, | ||
} from "../types"; | ||
import { selectEntityOptions } from "./selectors"; | ||
|
||
interface FunctionCallingConfigToolFieldProps { | ||
fieldPath: string; | ||
formName: string; | ||
index: number; | ||
onRemove: (index: number) => void; | ||
} | ||
|
||
const ConfigItemRoot = styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
padding: var(--ads-v2-spaces-3); | ||
border-radius: var(--ads-v2-border-radius); | ||
border: 1px solid var(--ads-v2-color-border); | ||
background: var(--colors-semantics-bg-inset, #f8fafc); | ||
`; | ||
|
||
const ConfigItemRow = styled.div` | ||
display: flex; | ||
flex-direction: row; | ||
align-items: center; | ||
gap: var(--ads-v2-spaces-4); | ||
justify-content: space-between; | ||
margin-bottom: var(--ads-v2-spaces-4); | ||
`; | ||
|
||
const ConfigItemSelectWrapper = styled.div` | ||
width: 50%; | ||
min-width: 160px; | ||
`; | ||
|
||
export const FunctionCallingConfigToolField = ({ | ||
index, | ||
onRemove, | ||
...props | ||
}: FunctionCallingConfigToolFieldProps) => { | ||
const dispatch = useDispatch(); | ||
const fieldValue = useSelector((state: AppState) => | ||
formValueSelector(props.formName)(state, props.fieldPath), | ||
); | ||
const entityOptions = useSelector(selectEntityOptions); | ||
|
||
const handleRemoveButtonClick = useCallback(() => { | ||
onRemove(index); | ||
}, [onRemove, index]); | ||
|
||
const findEntityOption = useCallback( | ||
(entityId: string, items: FunctionCallingEntityTypeOption[]): boolean => { | ||
return items.find(({ value }) => value === entityId) !== undefined; | ||
}, | ||
[entityOptions], | ||
); | ||
|
||
const findEntityType = useCallback( | ||
(entityId: string): string => { | ||
switch (true) { | ||
case findEntityOption(entityId, entityOptions.Query): | ||
return "Query"; | ||
case findEntityOption(entityId, entityOptions.JSFunction): | ||
return "JSFunction"; | ||
case findEntityOption(entityId, entityOptions.SystemFunction): | ||
return "SystemFunction"; | ||
} | ||
|
||
return ""; | ||
}, | ||
[fieldValue.entityId, entityOptions], | ||
); | ||
|
||
useEffect( | ||
// entityType is dependent on entityId | ||
// Every time entityId changes, we need to find the new entityType | ||
function handleEntityTypeChange() { | ||
const entityType = findEntityType(fieldValue.entityId); | ||
|
||
dispatch( | ||
change(props.formName, `${props.fieldPath}.entityType`, entityType), | ||
); | ||
}, | ||
[fieldValue.entityId], | ||
); | ||
|
||
return ( | ||
<ConfigItemRoot> | ||
<ConfigItemRow> | ||
<Text isBold renderAs="p"> | ||
Function | ||
</Text> | ||
<Button | ||
isIconButton | ||
kind="tertiary" | ||
onClick={handleRemoveButtonClick} | ||
startIcon="trash" | ||
/> | ||
</ConfigItemRow> | ||
<ConfigItemRow> | ||
<ConfigItemSelectWrapper> | ||
<FormControl | ||
config={{ | ||
controlType: "DROP_DOWN", | ||
configProperty: `${props.fieldPath}.entityId`, | ||
formName: props.formName, | ||
id: props.fieldPath, | ||
label: "", | ||
isValid: true, | ||
// FormControl component has incomplete TypeScript definitions for some valid properties | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-expect-error | ||
isSearchable: true, | ||
options: [ | ||
...entityOptions.Query, | ||
...entityOptions.JSFunction, | ||
...entityOptions.SystemFunction, | ||
], | ||
optionGroupConfig: { | ||
Query: { | ||
label: "Queries", | ||
children: [], | ||
}, | ||
JSFunction: { | ||
label: "JS Functions", | ||
children: [], | ||
}, | ||
SystemFunction: { | ||
label: "System Functions", | ||
children: [], | ||
}, | ||
} satisfies Record< | ||
FunctionCallingEntityType, | ||
DropDownGroupedOptions | ||
>, | ||
}} | ||
formName={props.formName} | ||
/> | ||
</ConfigItemSelectWrapper> | ||
<FormControl | ||
config={{ | ||
controlType: "SWITCH", | ||
configProperty: `${props.fieldPath}.requiresApproval`, | ||
formName: props.formName, | ||
id: props.fieldPath, | ||
label: "Requires approval", | ||
isValid: true, | ||
}} | ||
formName={props.formName} | ||
/> | ||
</ConfigItemRow> | ||
<FormControl | ||
config={{ | ||
controlType: "QUERY_DYNAMIC_INPUT_TEXT", | ||
configProperty: `${props.fieldPath}.description`, | ||
formName: props.formName, | ||
id: props.fieldPath, | ||
placeholderText: "Describe how this function should be used...", | ||
label: "Description", | ||
isValid: true, | ||
}} | ||
formName={props.formName} | ||
/> | ||
</ConfigItemRoot> | ||
); | ||
}; |
23 changes: 23 additions & 0 deletions
23
.../components/formControls/FunctionCallingConfigControl/components/FunctionCallingEmpty.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { Text } from "@appsmith/ads"; | ||
import { ASSETS_CDN_URL } from "constants/ThirdPartyConstants"; | ||
import { getAssetUrl } from "ee/utils/airgapHelpers"; | ||
import React, { memo } from "react"; | ||
import styled from "styled-components"; | ||
|
||
const Root = styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: center; | ||
align-items: center; | ||
gap: var(--ads-v2-spaces-4); | ||
height: 300px; | ||
`; | ||
|
||
export const FunctionCallingEmpty = memo(() => { | ||
return ( | ||
<Root> | ||
<img alt="" src={getAssetUrl(`${ASSETS_CDN_URL}/empty-state.svg`)} /> | ||
<Text>Function Calls will be displayed here</Text> | ||
</Root> | ||
); | ||
}); |
Oops, something went wrong.