Skip to content

Commit

Permalink
⚡ (openai) Enable setVariable function in tools
Browse files Browse the repository at this point in the history
Closes #1178
  • Loading branch information
baptisteArno committed Jan 22, 2024
1 parent b438c17 commit 42008f8
Show file tree
Hide file tree
Showing 24 changed files with 258 additions and 43 deletions.
1 change: 1 addition & 0 deletions apps/builder/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const nextConfig = {
if (nextRuntime === 'edge') {
config.resolve.alias['minio'] = false
config.resolve.alias['got'] = false
config.resolve.alias['qrcode'] = false
return config
}
// These packages are imports from the integrations definition files that can be ignored for the client.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react'
import { Text } from '@chakra-ui/react'
import { ScriptBlock } from '@typebot.io/schemas'
import { defaultScriptOptions } from '@typebot.io/schemas/features/blocks/logic/script/constants'

type Props = {
options: ScriptBlock['options']
Expand All @@ -10,6 +11,6 @@ export const ScriptNodeContent = ({
options: { name, content } = {},
}: Props) => (
<Text color={content ? 'currentcolor' : 'gray.500'} noOfLines={1}>
{content ? `Run ${name}` : 'Configure...'}
{content ? `Run ${name ?? defaultScriptOptions.name}` : 'Configure...'}
</Text>
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React from 'react'
import { TextInput } from '@/components/inputs'
import { ScriptBlock } from '@typebot.io/schemas'
import { defaultScriptOptions } from '@typebot.io/schemas/features/blocks/logic/script/constants'
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'

type Props = {
options: ScriptBlock['options']
Expand All @@ -13,9 +14,13 @@ type Props = {
export const ScriptSettings = ({ options, onOptionsChange }: Props) => {
const handleNameChange = (name: string) =>
onOptionsChange({ ...options, name })

const handleCodeChange = (content: string) =>
onOptionsChange({ ...options, content })

const updateClientExecution = (isExecutedOnClient: boolean) =>
onOptionsChange({ ...options, isExecutedOnClient })

return (
<Stack spacing={4}>
<TextInput
Expand All @@ -31,6 +36,15 @@ export const ScriptSettings = ({ options, onOptionsChange }: Props) => {
lang="javascript"
onChange={handleCodeChange}
/>
<SwitchWithLabel
label="Execute on client?"
moreInfoContent="Check this if you need access to client variables like `window` or `document`."
initialValue={
options?.isExecutedOnClient ??
defaultScriptOptions.isExecutedOnClient
}
onCheckChange={updateClientExecution}
/>
</Stack>
</Stack>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,6 @@ const SetVariableValue = ({
case undefined:
return (
<>
<CodeEditor
defaultValue={options?.expressionToEvaluate ?? ''}
onChange={updateExpression}
lang="javascript"
/>
<SwitchWithLabel
label="Execute on client?"
moreInfoContent="Check this if you need access to client-only variables like `window` or `document`."
Expand All @@ -151,6 +146,11 @@ const SetVariableValue = ({
}
onCheckChange={updateClientExecution}
/>
<CodeEditor
defaultValue={options?.expressionToEvaluate ?? ''}
onChange={updateExpression}
lang="javascript"
/>
</>
)
case 'Map item with same index': {
Expand Down
4 changes: 3 additions & 1 deletion apps/docs/editor/blocks/integrations/openai.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ A more useful example would be, of course, to call an API to get the weather of
<img src="/images/blocks/integrations/openai/tools.png" alt="OpenAI tools" />
</Frame>

As you can see, the code block expects the body of the Javascript function. You can use the `return` keyword to return values.
As you can see, the code block expects the body of the Javascript function. You should use the `return` keyword to return value to give back to OpenAI as the result of the function.

If you'd like to set variables directly in this code block, you can use the [`setVariable` function](../logic/script#setvariable-function).

## Ask assistant

Expand Down
20 changes: 18 additions & 2 deletions apps/docs/editor/blocks/logic/script.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ title: Script block
icon: code
---

The "Script" block allows you to execute Javascript code. If you want to set a variable value with Javascript, use the [Set variable block](./set-variable) instead. You can't set a variable with the script block.
The "Script" block allows you to execute Javascript code.

**It doesn't allow you to create a custom visual block**
<Info>This block doesn't allow you to create a custom visual block</Info>

<Frame>
<img src="/images/blocks/logic/code.png" width="600" alt="Code block" />
Expand All @@ -18,6 +18,22 @@ You need to write `console.log({{My variable}})` instead of `console.log("{{My v

</Info>

## `setVariable` function

If you want to set a variable value with Javascript, the [Set variable block](./set-variable) is more appropriate for most cases.

However, if you'd like to set variables with the script blocks, you can use the `setVariable` function in your script:

```js
if({{My variable}} === 'foo') {
setVariable('My variable', 'bar')
} else {
setVariable('My variable', 'other')
}
```

The `setVariable` function is only available in script executed on the server, so it won't work if the `Execute on client?` is checked.

## Examples

### Reload page
Expand Down
18 changes: 18 additions & 0 deletions apps/docs/openapi/builder.json
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,9 @@
"content": {
"type": "string"
},
"isExecutedOnClient": {
"type": "boolean"
},
"shouldExecuteInParentContext": {
"type": "boolean"
}
Expand Down Expand Up @@ -6580,6 +6583,9 @@
"content": {
"type": "string"
},
"isExecutedOnClient": {
"type": "boolean"
},
"shouldExecuteInParentContext": {
"type": "boolean"
}
Expand Down Expand Up @@ -11101,6 +11107,9 @@
"content": {
"type": "string"
},
"isExecutedOnClient": {
"type": "boolean"
},
"shouldExecuteInParentContext": {
"type": "boolean"
}
Expand Down Expand Up @@ -24814,6 +24823,9 @@
"content": {
"type": "string"
},
"isExecutedOnClient": {
"type": "boolean"
},
"shouldExecuteInParentContext": {
"type": "boolean"
}
Expand Down Expand Up @@ -28073,6 +28085,9 @@
"content": {
"type": "string"
},
"isExecutedOnClient": {
"type": "boolean"
},
"shouldExecuteInParentContext": {
"type": "boolean"
}
Expand Down Expand Up @@ -30882,6 +30897,9 @@
"content": {
"type": "string"
},
"isExecutedOnClient": {
"type": "boolean"
},
"shouldExecuteInParentContext": {
"type": "boolean"
}
Expand Down
6 changes: 6 additions & 0 deletions apps/docs/openapi/viewer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4527,6 +4527,9 @@
"content": {
"type": "string"
},
"isExecutedOnClient": {
"type": "boolean"
},
"shouldExecuteInParentContext": {
"type": "boolean"
}
Expand Down Expand Up @@ -8289,6 +8292,9 @@
"content": {
"type": "string"
},
"isExecutedOnClient": {
"type": "boolean"
},
"shouldExecuteInParentContext": {
"type": "boolean"
}
Expand Down
1 change: 1 addition & 0 deletions apps/viewer/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const nextConfig = {
if (nextRuntime === 'edge') {
config.resolve.alias['minio'] = false
config.resolve.alias['got'] = false
config.resolve.alias['qrcode'] = false
return config
}
// These packages are imports from the integrations definition files that can be ignored for the client.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export async function POST(req: Request) {
credentials.iv
)
const variables: ReadOnlyVariableStore = {
list: () => state.typebotsQueue[0].typebot.variables,
get: (id: string) => {
const variable = state.typebotsQueue[0].typebot.variables.find(
(variable) => variable.id === id
Expand Down
27 changes: 25 additions & 2 deletions packages/bot-engine/blocks/logic/script/executeScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,38 @@ import { ScriptBlock, SessionState, Variable } from '@typebot.io/schemas'
import { extractVariablesFromText } from '@typebot.io/variables/extractVariablesFromText'
import { parseGuessedValueType } from '@typebot.io/variables/parseGuessedValueType'
import { parseVariables } from '@typebot.io/variables/parseVariables'
import { defaultScriptOptions } from '@typebot.io/schemas/features/blocks/logic/script/constants'
import { executeFunction } from '@typebot.io/variables/executeFunction'
import { updateVariablesInSession } from '@typebot.io/variables/updateVariablesInSession'

export const executeScript = (
export const executeScript = async (
state: SessionState,
block: ScriptBlock
): ExecuteLogicResponse => {
): Promise<ExecuteLogicResponse> => {
const { variables } = state.typebotsQueue[0].typebot
if (!block.options?.content || state.whatsApp)
return { outgoingEdgeId: block.outgoingEdgeId }

const isExecutedOnClient =
block.options.isExecutedOnClient ?? defaultScriptOptions.isExecutedOnClient

if (!isExecutedOnClient) {
const { newVariables, error } = await executeFunction({
variables,
body: block.options.content,
})

const newSessionState = newVariables
? updateVariablesInSession(state)(newVariables)
: state

return {
outgoingEdgeId: block.outgoingEdgeId,
logs: error ? [{ status: 'error', description: error }] : [],
newSessionState,
}
}

const scriptToExecute = parseScriptToExecuteClientSideAction(
variables,
block.options.content
Expand Down
1 change: 1 addition & 0 deletions packages/bot-engine/forge/executeForgedBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export const executeForgedBlock = async (
newSessionState.typebotsQueue[0].typebot.variables,
params
)(text),
list: () => newSessionState.typebotsQueue[0].typebot.variables,
}
let logs: NonNullable<ContinueChatResponse['logs']> = []
const logsStore: LogsStore = {
Expand Down
2 changes: 1 addition & 1 deletion packages/embeds/js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@typebot.io/js",
"version": "0.2.34",
"version": "0.2.35",
"description": "Javascript library to display typebots on your website",
"type": "module",
"main": "dist/index.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,7 @@ export const ConversationContainer = (props: Props) => {
hideAvatar={
!chatChunk.input &&
((chatChunks()[index() + 1]?.messages ?? 0).length > 0 ||
chatChunks()[index() + 1]?.streamingMessageId !== undefined ||
isSending())
chatChunks()[index() + 1]?.streamingMessageId !== undefined)
}
hasError={hasError() && index() === chatChunks().length - 1}
onNewBubbleDisplayed={handleNewBubbleDisplayed}
Expand Down
2 changes: 1 addition & 1 deletion packages/embeds/nextjs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@typebot.io/nextjs",
"version": "0.2.34",
"version": "0.2.35",
"description": "Convenient library to display typebots on your Next.js website",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/embeds/react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@typebot.io/react",
"version": "0.2.34",
"version": "0.2.35",
"description": "Convenient library to display typebots on your React app",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
20 changes: 12 additions & 8 deletions packages/forge/blocks/openai/actions/askAssistant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { isDefined, isEmpty } from '@typebot.io/lib'
import { auth } from '../auth'
import { ClientOptions, OpenAI } from 'openai'
import { baseOptions } from '../baseOptions'
import { executeFunction } from '@typebot.io/variables/executeFunction'

export const askAssistant = createAction({
auth,
Expand Down Expand Up @@ -206,12 +207,17 @@ export const askAssistant = createAction({
if (!functionToExecute) return

const name = toolCall.function.name
if (!name) return
const func = AsyncFunction(
...Object.keys(parameters),
functionToExecute.code
)
const output = await func(...Object.values(parameters))
if (!name || !functionToExecute.code) return

const { output, newVariables } = await executeFunction({
variables: variables.list(),
body: functionToExecute.code,
args: parameters,
})

newVariables?.forEach((variable) => {
variables.set(variable.id, variable.value)
})

return {
tool_call_id: toolCall.id,
Expand Down Expand Up @@ -262,5 +268,3 @@ export const askAssistant = createAction({
},
},
})

const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
Loading

2 comments on commit 42008f8

@vercel
Copy link

@vercel vercel bot commented on 42008f8 Jan 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

landing-page-v2 – ./apps/landing-page

landing-page-v2-typebot-io.vercel.app
landing-page-v2-git-main-typebot-io.vercel.app
home.typebot.io

@vercel
Copy link

@vercel vercel bot commented on 42008f8 Jan 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

builder-v2 – ./apps/builder

app.typebot.io
builder-v2-typebot-io.vercel.app
builder-v2-git-main-typebot-io.vercel.app

Please sign in to comment.