From c5baba39d3947a21a51aad3b3511babf20f2593d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Demetrius=20Feij=C3=B3o?= Date: Tue, 31 Dec 2024 12:24:40 -0300 Subject: [PATCH] feat: add promptAI action --- packages/demo/src/components/NonModalView.tsx | 2 + packages/demo/src/components/PromptAI.tsx | 66 +++++++++++++++++++ .../createFieldPlugin/FieldPluginActions.ts | 6 +- .../createPluginActions/callbackQueue.ts | 6 +- .../createPluginActions.ts | 23 +++++++ .../createPluginMessageListener.ts | 4 +- .../handlePluginMessage.ts | 3 + .../ContainerToPlugin.ts | 2 + .../PromptAIResponseMessage.ts | 23 +++++++ .../containerToPluginMessage/index.ts | 1 + .../PromptAIMessage.ts | 54 +++++++++++++++ .../pluginToContainerMessage/index.ts | 1 + 12 files changed, 187 insertions(+), 4 deletions(-) create mode 100644 packages/demo/src/components/PromptAI.tsx create mode 100644 packages/field-plugin/src/messaging/pluginMessage/containerToPluginMessage/PromptAIResponseMessage.ts create mode 100644 packages/field-plugin/src/messaging/pluginMessage/pluginToContainerMessage/PromptAIMessage.ts diff --git a/packages/demo/src/components/NonModalView.tsx b/packages/demo/src/components/NonModalView.tsx index b4129144..165f2f3d 100644 --- a/packages/demo/src/components/NonModalView.tsx +++ b/packages/demo/src/components/NonModalView.tsx @@ -6,6 +6,7 @@ import { AssetSelector } from './AssetSelector' import { ContextRequester } from './ContextRequester' import { PluginComponent } from './FieldPluginDemo' import { LanguageView } from './LanguageView' +import { PromptAI } from './PromptAI' export const NonModalView: PluginComponent = (props) => ( @@ -17,6 +18,7 @@ export const NonModalView: PluginComponent = (props) => ( + ) diff --git a/packages/demo/src/components/PromptAI.tsx b/packages/demo/src/components/PromptAI.tsx new file mode 100644 index 00000000..fade2ba4 --- /dev/null +++ b/packages/demo/src/components/PromptAI.tsx @@ -0,0 +1,66 @@ +import { + Button, + MenuItem, + Select, + Stack, + TextField, + Typography, +} from '@mui/material' +import { useState } from 'react' +import { + promptAIActionsList, + type PromptAIAction, +} from '@storyblok/field-plugin' +import type { PluginComponent } from './FieldPluginDemo' + +export const PromptAI: PluginComponent = (props) => { + const { actions } = props + + const [promptQuestion, setPromptQuestion] = useState('') + const [promptType, setPromptType] = useState('prompt') + const [promptOutput, setPromptOutput] = useState('') + + return ( + + Prompt AI + + setPromptQuestion(e.target.value)} + required + /> + + Output: {promptOutput} + + + + ) +} diff --git a/packages/field-plugin/src/createFieldPlugin/FieldPluginActions.ts b/packages/field-plugin/src/createFieldPlugin/FieldPluginActions.ts index d95b8307..d74ad234 100644 --- a/packages/field-plugin/src/createFieldPlugin/FieldPluginActions.ts +++ b/packages/field-plugin/src/createFieldPlugin/FieldPluginActions.ts @@ -1,5 +1,5 @@ -import { Asset, StoryData } from '../messaging' -import { FieldPluginData } from './FieldPluginData' +import type { FieldPluginData } from './FieldPluginData' +import type { Asset, PromptAIPayload, StoryData } from '../messaging' export type SetContent = ( content: Content, @@ -8,6 +8,7 @@ export type SetModalOpen = ( isModalOpen: boolean, ) => Promise> export type RequestContext = () => Promise +export type PromptAI = (payload: PromptAIPayload) => Promise export type SelectAsset = () => Promise export type Initialize = () => Promise> @@ -15,5 +16,6 @@ export type FieldPluginActions = { setContent: SetContent setModalOpen: SetModalOpen requestContext: RequestContext + promptAI: PromptAI selectAsset: SelectAsset } diff --git a/packages/field-plugin/src/createFieldPlugin/createPluginActions/callbackQueue.ts b/packages/field-plugin/src/createFieldPlugin/createPluginActions/callbackQueue.ts index 728b732c..90a13c73 100644 --- a/packages/field-plugin/src/createFieldPlugin/createPluginActions/callbackQueue.ts +++ b/packages/field-plugin/src/createFieldPlugin/createPluginActions/callbackQueue.ts @@ -1,4 +1,5 @@ -import { +import type { + PromptAIResponseMessage, AssetSelectedMessage, ContextRequestMessage, LoadedMessage, @@ -14,6 +15,7 @@ type CallbackMap = { context: Record> stateChanged: Record> loaded: Record> + promptAI: Record> } type CallbackType = keyof CallbackMap @@ -23,7 +25,9 @@ export const callbackQueue = () => { context: {}, stateChanged: {}, loaded: {}, + promptAI: {}, } + const pushCallback = ( callbackType: T, callback: CallbackMap[T][CallbackId], diff --git a/packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginActions.ts b/packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginActions.ts index a830b2da..2b71342f 100644 --- a/packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginActions.ts +++ b/packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginActions.ts @@ -4,6 +4,7 @@ import { assetFromAssetSelectedMessage, assetModalChangeMessage, getContextMessage, + getResponseFromPromptAIMessage, heightChangeMessage, modalChangeMessage, OnAssetSelectMessage, @@ -12,7 +13,10 @@ import { OnStateChangeMessage, OnUnknownPluginMessage, pluginLoadedMessage, + type PromptAIPayload, + getPromptAIMessage, valueChangeMessage, + OnPromptAIMessage, } from '../../messaging' import { FieldPluginActions, Initialize } from '../FieldPluginActions' import { pluginStateFromStateChangeMessage } from './partialPluginStateFromStateChangeMessage' @@ -55,16 +59,24 @@ export const createPluginActions: CreatePluginActions = ({ popCallback('stateChanged', data.callbackId)?.(data) onUpdateState(pluginStateFromStateChangeMessage(data, validateContent)) } + const onLoaded: OnLoadedMessage = (data) => { popCallback('loaded', data.callbackId)?.(data) onUpdateState(pluginStateFromStateChangeMessage(data, validateContent)) } + const onContextRequest: OnContextRequestMessage = (data) => { popCallback('context', data.callbackId)?.(data) } + const onAssetSelect: OnAssetSelectMessage = (data) => { popCallback('asset', data.callbackId)?.(data) } + + const onPromptAI: OnPromptAIMessage = (data) => { + popCallback('promptAI', data.callbackId)?.(data) + } + const onUnknownMessage: OnUnknownPluginMessage = (data) => { // TODO remove side-effect, making functions in this file pure. // perhaps only show this message in development mode? @@ -82,6 +94,7 @@ export const createPluginActions: CreatePluginActions = ({ onLoaded, onContextRequest, onAssetSelect, + onPromptAI, onUnknownMessage, } @@ -135,6 +148,16 @@ export const createPluginActions: CreatePluginActions = ({ postToContainer(getContextMessage({ uid, callbackId })) }) }, + promptAI: (promptAIMessage: PromptAIPayload) => { + return new Promise((resolve) => { + const callbackId = pushCallback('promptAI', (message) => + resolve(getResponseFromPromptAIMessage(message)), + ) + postToContainer( + getPromptAIMessage(promptAIMessage, { uid, callbackId }), + ) + }) + }, }, messageCallbacks, onHeightChange, diff --git a/packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginMessageListener/createPluginMessageListener.ts b/packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginMessageListener/createPluginMessageListener.ts index 7e88b637..692d13d5 100644 --- a/packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginMessageListener/createPluginMessageListener.ts +++ b/packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginMessageListener/createPluginMessageListener.ts @@ -1,7 +1,8 @@ -import { +import type { OnAssetSelectMessage, OnContextRequestMessage, OnLoadedMessage, + OnPromptAIMessage, OnStateChangeMessage, OnUnknownPluginMessage, } from '../../../messaging' @@ -12,6 +13,7 @@ export type PluginMessageCallbacks = { onLoaded: OnLoadedMessage onContextRequest: OnContextRequestMessage onAssetSelect: OnAssetSelectMessage + onPromptAI: OnPromptAIMessage onUnknownMessage: OnUnknownPluginMessage } diff --git a/packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginMessageListener/handlePluginMessage.ts b/packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginMessageListener/handlePluginMessage.ts index 9162de7f..21703d43 100644 --- a/packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginMessageListener/handlePluginMessage.ts +++ b/packages/field-plugin/src/createFieldPlugin/createPluginActions/createPluginMessageListener/handlePluginMessage.ts @@ -2,6 +2,7 @@ import { isAssetSelectedMessage, isMessageToPlugin, isLoadedMessage, + isPromptAIMessage, } from '../../../messaging' import { isContextRequestMessage } from '../../../messaging' import { PluginMessageCallbacks } from './createPluginMessageListener' @@ -32,6 +33,8 @@ export const handlePluginMessage = ( callbacks.onContextRequest(data) } else if (isAssetSelectedMessage(data)) { callbacks.onAssetSelect(data) + } else if (isPromptAIMessage(data)) { + callbacks.onPromptAI(data) } else { callbacks.onUnknownMessage(data) } diff --git a/packages/field-plugin/src/messaging/pluginMessage/containerToPluginMessage/ContainerToPlugin.ts b/packages/field-plugin/src/messaging/pluginMessage/containerToPluginMessage/ContainerToPlugin.ts index 1535bb9d..d3f5f801 100644 --- a/packages/field-plugin/src/messaging/pluginMessage/containerToPluginMessage/ContainerToPlugin.ts +++ b/packages/field-plugin/src/messaging/pluginMessage/containerToPluginMessage/ContainerToPlugin.ts @@ -3,6 +3,7 @@ import { AssetSelectedMessage } from './AssetSelectedMessage' import { ContextRequestMessage } from './ContextRequestMessage' import { MessageToPlugin } from './MessageToPlugin' import { StateChangedMessage } from './StateChangedMessage' +import { PromptAIResponseMessage } from './PromptAIResponseMessage' /** * The plugin container's sends it's state to the plugin @@ -11,5 +12,6 @@ export type OnMessage = (message: Message) => void export type OnStateChangeMessage = (message: StateChangedMessage) => void export type OnLoadedMessage = (message: LoadedMessage) => void export type OnAssetSelectMessage = (message: AssetSelectedMessage) => void +export type OnPromptAIMessage = (message: PromptAIResponseMessage) => void export type OnContextRequestMessage = (message: ContextRequestMessage) => void export type OnUnknownPluginMessage = (message: MessageToPlugin) => void diff --git a/packages/field-plugin/src/messaging/pluginMessage/containerToPluginMessage/PromptAIResponseMessage.ts b/packages/field-plugin/src/messaging/pluginMessage/containerToPluginMessage/PromptAIResponseMessage.ts new file mode 100644 index 00000000..1990781a --- /dev/null +++ b/packages/field-plugin/src/messaging/pluginMessage/containerToPluginMessage/PromptAIResponseMessage.ts @@ -0,0 +1,23 @@ +import { hasKey } from '../../../utils' +import { isMessageToPlugin, type MessageToPlugin } from './MessageToPlugin' + +/** + * The object returned when calling the "prompt-ai" action. + */ +export type PromptAIResponseMessage = MessageToPlugin<'prompt-ai'> & { + output: string +} + +export const isPromptAIMessage = ( + data: unknown, +): data is PromptAIResponseMessage => + isMessageToPlugin(data) && + hasKey(data, 'output') && + typeof data.output === 'string' + +export const getResponseFromPromptAIMessage = ( + message: PromptAIResponseMessage, +): string => { + const { output } = message + return output +} diff --git a/packages/field-plugin/src/messaging/pluginMessage/containerToPluginMessage/index.ts b/packages/field-plugin/src/messaging/pluginMessage/containerToPluginMessage/index.ts index 0e135fad..4d9074a3 100644 --- a/packages/field-plugin/src/messaging/pluginMessage/containerToPluginMessage/index.ts +++ b/packages/field-plugin/src/messaging/pluginMessage/containerToPluginMessage/index.ts @@ -8,3 +8,4 @@ export * from './ContextRequestMessage' export * from './MessageToPlugin' export * from './StoryData' export * from './Asset' +export * from './PromptAIResponseMessage' diff --git a/packages/field-plugin/src/messaging/pluginMessage/pluginToContainerMessage/PromptAIMessage.ts b/packages/field-plugin/src/messaging/pluginMessage/pluginToContainerMessage/PromptAIMessage.ts new file mode 100644 index 00000000..1fa8a59f --- /dev/null +++ b/packages/field-plugin/src/messaging/pluginMessage/pluginToContainerMessage/PromptAIMessage.ts @@ -0,0 +1,54 @@ +import type { MessageToContainer } from './MessageToContainer' + +export type PromptAIAction = + | 'prompt' + | 'complete' + | 'shorten' + | 'extend' + | 'rephrase' + | 'summarize' + | 'simplify' + | 'translate' + | 'tldr' + | 'adjust-tone' + | 'emojify' + | 'fix_spelling_and_grammar' + +export const promptAIActionsList: PromptAIAction[] = [ + 'prompt', + 'complete', + 'shorten', + 'extend', + 'rephrase', + 'summarize', + 'simplify', + 'translate', + 'tldr', + 'adjust-tone', + 'emojify', + 'fix_spelling_and_grammar', +] + +export type PromptAIPayload = { + action: PromptAIAction + text: string + language?: string + textLength?: string + tone?: string + textLengthUnit?: string +} + +export type PromptAIMessage = Omit, 'action'> & { + action: 'prompt-ai' + promptAI: PromptAIPayload +} + +export const getPromptAIMessage = ( + message: PromptAIPayload, + options: Pick, +): PromptAIMessage => ({ + action: 'prompt-ai', + event: 'promptAI', + ...options, + promptAI: { ...message }, +}) diff --git a/packages/field-plugin/src/messaging/pluginMessage/pluginToContainerMessage/index.ts b/packages/field-plugin/src/messaging/pluginMessage/pluginToContainerMessage/index.ts index b65ac861..25512ff8 100644 --- a/packages/field-plugin/src/messaging/pluginMessage/pluginToContainerMessage/index.ts +++ b/packages/field-plugin/src/messaging/pluginMessage/pluginToContainerMessage/index.ts @@ -5,3 +5,4 @@ export * from './PluginLoadedMessage' export * from './HeightChangeMessage' export * from './GetContextMessage' export * from './AssetModalChangeMessage' +export * from './PromptAIMessage'