@ -50,6 +50,7 @@ const nextConfig = {
|
|||||||
if (nextRuntime === 'edge') {
|
if (nextRuntime === 'edge') {
|
||||||
config.resolve.alias['minio'] = false
|
config.resolve.alias['minio'] = false
|
||||||
config.resolve.alias['got'] = false
|
config.resolve.alias['got'] = false
|
||||||
|
config.resolve.alias['qrcode'] = false
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
// These packages are imports from the integrations definition files that can be ignored for the client.
|
// These packages are imports from the integrations definition files that can be ignored for the client.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Text } from '@chakra-ui/react'
|
import { Text } from '@chakra-ui/react'
|
||||||
import { ScriptBlock } from '@typebot.io/schemas'
|
import { ScriptBlock } from '@typebot.io/schemas'
|
||||||
|
import { defaultScriptOptions } from '@typebot.io/schemas/features/blocks/logic/script/constants'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
options: ScriptBlock['options']
|
options: ScriptBlock['options']
|
||||||
@ -10,6 +11,6 @@ export const ScriptNodeContent = ({
|
|||||||
options: { name, content } = {},
|
options: { name, content } = {},
|
||||||
}: Props) => (
|
}: Props) => (
|
||||||
<Text color={content ? 'currentcolor' : 'gray.500'} noOfLines={1}>
|
<Text color={content ? 'currentcolor' : 'gray.500'} noOfLines={1}>
|
||||||
{content ? `Run ${name}` : 'Configure...'}
|
{content ? `Run ${name ?? defaultScriptOptions.name}` : 'Configure...'}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
|
@ -4,6 +4,7 @@ import React from 'react'
|
|||||||
import { TextInput } from '@/components/inputs'
|
import { TextInput } from '@/components/inputs'
|
||||||
import { ScriptBlock } from '@typebot.io/schemas'
|
import { ScriptBlock } from '@typebot.io/schemas'
|
||||||
import { defaultScriptOptions } from '@typebot.io/schemas/features/blocks/logic/script/constants'
|
import { defaultScriptOptions } from '@typebot.io/schemas/features/blocks/logic/script/constants'
|
||||||
|
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
options: ScriptBlock['options']
|
options: ScriptBlock['options']
|
||||||
@ -13,9 +14,13 @@ type Props = {
|
|||||||
export const ScriptSettings = ({ options, onOptionsChange }: Props) => {
|
export const ScriptSettings = ({ options, onOptionsChange }: Props) => {
|
||||||
const handleNameChange = (name: string) =>
|
const handleNameChange = (name: string) =>
|
||||||
onOptionsChange({ ...options, name })
|
onOptionsChange({ ...options, name })
|
||||||
|
|
||||||
const handleCodeChange = (content: string) =>
|
const handleCodeChange = (content: string) =>
|
||||||
onOptionsChange({ ...options, content })
|
onOptionsChange({ ...options, content })
|
||||||
|
|
||||||
|
const updateClientExecution = (isExecutedOnClient: boolean) =>
|
||||||
|
onOptionsChange({ ...options, isExecutedOnClient })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack spacing={4}>
|
<Stack spacing={4}>
|
||||||
<TextInput
|
<TextInput
|
||||||
@ -31,6 +36,15 @@ export const ScriptSettings = ({ options, onOptionsChange }: Props) => {
|
|||||||
lang="javascript"
|
lang="javascript"
|
||||||
onChange={handleCodeChange}
|
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>
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
)
|
||||||
|
@ -137,11 +137,6 @@ const SetVariableValue = ({
|
|||||||
case undefined:
|
case undefined:
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CodeEditor
|
|
||||||
defaultValue={options?.expressionToEvaluate ?? ''}
|
|
||||||
onChange={updateExpression}
|
|
||||||
lang="javascript"
|
|
||||||
/>
|
|
||||||
<SwitchWithLabel
|
<SwitchWithLabel
|
||||||
label="Execute on client?"
|
label="Execute on client?"
|
||||||
moreInfoContent="Check this if you need access to client-only variables like `window` or `document`."
|
moreInfoContent="Check this if you need access to client-only variables like `window` or `document`."
|
||||||
@ -151,6 +146,11 @@ const SetVariableValue = ({
|
|||||||
}
|
}
|
||||||
onCheckChange={updateClientExecution}
|
onCheckChange={updateClientExecution}
|
||||||
/>
|
/>
|
||||||
|
<CodeEditor
|
||||||
|
defaultValue={options?.expressionToEvaluate ?? ''}
|
||||||
|
onChange={updateExpression}
|
||||||
|
lang="javascript"
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
case 'Map item with same index': {
|
case 'Map item with same index': {
|
||||||
|
@ -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" />
|
<img src="/images/blocks/integrations/openai/tools.png" alt="OpenAI tools" />
|
||||||
</Frame>
|
</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
|
## Ask assistant
|
||||||
|
|
||||||
|
@ -3,9 +3,9 @@ title: Script block
|
|||||||
icon: code
|
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>
|
<Frame>
|
||||||
<img src="/images/blocks/logic/code.png" width="600" alt="Code block" />
|
<img src="/images/blocks/logic/code.png" width="600" alt="Code block" />
|
||||||
@ -18,6 +18,22 @@ You need to write `console.log({{My variable}})` instead of `console.log("{{My v
|
|||||||
|
|
||||||
</Info>
|
</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
|
## Examples
|
||||||
|
|
||||||
### Reload page
|
### Reload page
|
||||||
|
@ -371,6 +371,9 @@
|
|||||||
"content": {
|
"content": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"isExecutedOnClient": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"shouldExecuteInParentContext": {
|
"shouldExecuteInParentContext": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
@ -6580,6 +6583,9 @@
|
|||||||
"content": {
|
"content": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"isExecutedOnClient": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"shouldExecuteInParentContext": {
|
"shouldExecuteInParentContext": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
@ -11101,6 +11107,9 @@
|
|||||||
"content": {
|
"content": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"isExecutedOnClient": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"shouldExecuteInParentContext": {
|
"shouldExecuteInParentContext": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
@ -24814,6 +24823,9 @@
|
|||||||
"content": {
|
"content": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"isExecutedOnClient": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"shouldExecuteInParentContext": {
|
"shouldExecuteInParentContext": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
@ -28073,6 +28085,9 @@
|
|||||||
"content": {
|
"content": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"isExecutedOnClient": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"shouldExecuteInParentContext": {
|
"shouldExecuteInParentContext": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
@ -30882,6 +30897,9 @@
|
|||||||
"content": {
|
"content": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"isExecutedOnClient": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"shouldExecuteInParentContext": {
|
"shouldExecuteInParentContext": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
|
@ -4527,6 +4527,9 @@
|
|||||||
"content": {
|
"content": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"isExecutedOnClient": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"shouldExecuteInParentContext": {
|
"shouldExecuteInParentContext": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
@ -8289,6 +8292,9 @@
|
|||||||
"content": {
|
"content": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"isExecutedOnClient": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"shouldExecuteInParentContext": {
|
"shouldExecuteInParentContext": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ const nextConfig = {
|
|||||||
if (nextRuntime === 'edge') {
|
if (nextRuntime === 'edge') {
|
||||||
config.resolve.alias['minio'] = false
|
config.resolve.alias['minio'] = false
|
||||||
config.resolve.alias['got'] = false
|
config.resolve.alias['got'] = false
|
||||||
|
config.resolve.alias['qrcode'] = false
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
// These packages are imports from the integrations definition files that can be ignored for the client.
|
// These packages are imports from the integrations definition files that can be ignored for the client.
|
||||||
|
@ -134,6 +134,7 @@ export async function POST(req: Request) {
|
|||||||
credentials.iv
|
credentials.iv
|
||||||
)
|
)
|
||||||
const variables: ReadOnlyVariableStore = {
|
const variables: ReadOnlyVariableStore = {
|
||||||
|
list: () => state.typebotsQueue[0].typebot.variables,
|
||||||
get: (id: string) => {
|
get: (id: string) => {
|
||||||
const variable = state.typebotsQueue[0].typebot.variables.find(
|
const variable = state.typebotsQueue[0].typebot.variables.find(
|
||||||
(variable) => variable.id === id
|
(variable) => variable.id === id
|
||||||
|
@ -3,15 +3,38 @@ import { ScriptBlock, SessionState, Variable } from '@typebot.io/schemas'
|
|||||||
import { extractVariablesFromText } from '@typebot.io/variables/extractVariablesFromText'
|
import { extractVariablesFromText } from '@typebot.io/variables/extractVariablesFromText'
|
||||||
import { parseGuessedValueType } from '@typebot.io/variables/parseGuessedValueType'
|
import { parseGuessedValueType } from '@typebot.io/variables/parseGuessedValueType'
|
||||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
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,
|
state: SessionState,
|
||||||
block: ScriptBlock
|
block: ScriptBlock
|
||||||
): ExecuteLogicResponse => {
|
): Promise<ExecuteLogicResponse> => {
|
||||||
const { variables } = state.typebotsQueue[0].typebot
|
const { variables } = state.typebotsQueue[0].typebot
|
||||||
if (!block.options?.content || state.whatsApp)
|
if (!block.options?.content || state.whatsApp)
|
||||||
return { outgoingEdgeId: block.outgoingEdgeId }
|
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(
|
const scriptToExecute = parseScriptToExecuteClientSideAction(
|
||||||
variables,
|
variables,
|
||||||
block.options.content
|
block.options.content
|
||||||
|
@ -101,6 +101,7 @@ export const executeForgedBlock = async (
|
|||||||
newSessionState.typebotsQueue[0].typebot.variables,
|
newSessionState.typebotsQueue[0].typebot.variables,
|
||||||
params
|
params
|
||||||
)(text),
|
)(text),
|
||||||
|
list: () => newSessionState.typebotsQueue[0].typebot.variables,
|
||||||
}
|
}
|
||||||
let logs: NonNullable<ContinueChatResponse['logs']> = []
|
let logs: NonNullable<ContinueChatResponse['logs']> = []
|
||||||
const logsStore: LogsStore = {
|
const logsStore: LogsStore = {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/js",
|
"name": "@typebot.io/js",
|
||||||
"version": "0.2.34",
|
"version": "0.2.35",
|
||||||
"description": "Javascript library to display typebots on your website",
|
"description": "Javascript library to display typebots on your website",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
@ -284,8 +284,7 @@ export const ConversationContainer = (props: Props) => {
|
|||||||
hideAvatar={
|
hideAvatar={
|
||||||
!chatChunk.input &&
|
!chatChunk.input &&
|
||||||
((chatChunks()[index() + 1]?.messages ?? 0).length > 0 ||
|
((chatChunks()[index() + 1]?.messages ?? 0).length > 0 ||
|
||||||
chatChunks()[index() + 1]?.streamingMessageId !== undefined ||
|
chatChunks()[index() + 1]?.streamingMessageId !== undefined)
|
||||||
isSending())
|
|
||||||
}
|
}
|
||||||
hasError={hasError() && index() === chatChunks().length - 1}
|
hasError={hasError() && index() === chatChunks().length - 1}
|
||||||
onNewBubbleDisplayed={handleNewBubbleDisplayed}
|
onNewBubbleDisplayed={handleNewBubbleDisplayed}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/nextjs",
|
"name": "@typebot.io/nextjs",
|
||||||
"version": "0.2.34",
|
"version": "0.2.35",
|
||||||
"description": "Convenient library to display typebots on your Next.js website",
|
"description": "Convenient library to display typebots on your Next.js website",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/react",
|
"name": "@typebot.io/react",
|
||||||
"version": "0.2.34",
|
"version": "0.2.35",
|
||||||
"description": "Convenient library to display typebots on your React app",
|
"description": "Convenient library to display typebots on your React app",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
@ -3,6 +3,7 @@ import { isDefined, isEmpty } from '@typebot.io/lib'
|
|||||||
import { auth } from '../auth'
|
import { auth } from '../auth'
|
||||||
import { ClientOptions, OpenAI } from 'openai'
|
import { ClientOptions, OpenAI } from 'openai'
|
||||||
import { baseOptions } from '../baseOptions'
|
import { baseOptions } from '../baseOptions'
|
||||||
|
import { executeFunction } from '@typebot.io/variables/executeFunction'
|
||||||
|
|
||||||
export const askAssistant = createAction({
|
export const askAssistant = createAction({
|
||||||
auth,
|
auth,
|
||||||
@ -206,12 +207,17 @@ export const askAssistant = createAction({
|
|||||||
if (!functionToExecute) return
|
if (!functionToExecute) return
|
||||||
|
|
||||||
const name = toolCall.function.name
|
const name = toolCall.function.name
|
||||||
if (!name) return
|
if (!name || !functionToExecute.code) return
|
||||||
const func = AsyncFunction(
|
|
||||||
...Object.keys(parameters),
|
const { output, newVariables } = await executeFunction({
|
||||||
functionToExecute.code
|
variables: variables.list(),
|
||||||
)
|
body: functionToExecute.code,
|
||||||
const output = await func(...Object.values(parameters))
|
args: parameters,
|
||||||
|
})
|
||||||
|
|
||||||
|
newVariables?.forEach((variable) => {
|
||||||
|
variables.set(variable.id, variable.value)
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tool_call_id: toolCall.id,
|
tool_call_id: toolCall.id,
|
||||||
@ -262,5 +268,3 @@ export const askAssistant = createAction({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
|
|
||||||
|
@ -8,6 +8,7 @@ import { auth } from '../auth'
|
|||||||
import { baseOptions } from '../baseOptions'
|
import { baseOptions } from '../baseOptions'
|
||||||
import { ChatCompletionTool } from 'openai/resources/chat/completions'
|
import { ChatCompletionTool } from 'openai/resources/chat/completions'
|
||||||
import { parseToolParameters } from '../helpers/parseToolParameters'
|
import { parseToolParameters } from '../helpers/parseToolParameters'
|
||||||
|
import { executeFunction } from '@typebot.io/variables/executeFunction'
|
||||||
|
|
||||||
const nativeMessageContentSchema = {
|
const nativeMessageContentSchema = {
|
||||||
content: option.string.layout({
|
content: option.string.layout({
|
||||||
@ -213,17 +214,21 @@ export const createChatCompletion = createAction({
|
|||||||
if (!name) continue
|
if (!name) continue
|
||||||
const toolDefinition = options.tools?.find((t) => t.name === name)
|
const toolDefinition = options.tools?.find((t) => t.name === name)
|
||||||
if (!toolDefinition?.code || !toolDefinition.parameters) continue
|
if (!toolDefinition?.code || !toolDefinition.parameters) continue
|
||||||
const func = AsyncFunction(
|
const toolArgs = toolCall.function?.arguments
|
||||||
...toolDefinition.parameters?.map((p) => p.name),
|
? JSON.parse(toolCall.function?.arguments)
|
||||||
toolDefinition.code
|
: undefined
|
||||||
)
|
if (!toolArgs) continue
|
||||||
const content = await func(
|
const { output, newVariables } = await executeFunction({
|
||||||
...Object.values(JSON.parse(toolCall.function.arguments))
|
variables: variables.list(),
|
||||||
)
|
args: toolArgs,
|
||||||
|
body: toolDefinition.code,
|
||||||
|
})
|
||||||
|
newVariables?.forEach((v) => variables.set(v.id, v.value))
|
||||||
|
|
||||||
messages.push({
|
messages.push({
|
||||||
tool_call_id: toolCall.id,
|
tool_call_id: toolCall.id,
|
||||||
role: 'tool',
|
role: 'tool',
|
||||||
content,
|
content: output,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,18 +309,23 @@ export const createChatCompletion = createAction({
|
|||||||
if (!name) continue
|
if (!name) continue
|
||||||
const toolDefinition = options.tools?.find((t) => t.name === name)
|
const toolDefinition = options.tools?.find((t) => t.name === name)
|
||||||
if (!toolDefinition?.code || !toolDefinition.parameters) continue
|
if (!toolDefinition?.code || !toolDefinition.parameters) continue
|
||||||
const func = AsyncFunction(
|
|
||||||
...toolDefinition.parameters?.map((p) => p.name),
|
const { output } = await executeFunction({
|
||||||
toolDefinition.code
|
variables: variables.list(),
|
||||||
)
|
args:
|
||||||
const content = await func(
|
typeof toolCall.func.arguments === 'string'
|
||||||
...Object.values(JSON.parse(toolCall.func.arguments as any))
|
? JSON.parse(toolCall.func.arguments)
|
||||||
)
|
: toolCall.func.arguments,
|
||||||
|
body: toolDefinition.code,
|
||||||
|
})
|
||||||
|
|
||||||
|
// TO-DO: enable once we're out of edge runtime.
|
||||||
|
// newVariables?.forEach((v) => variables.set(v.id, v.value))
|
||||||
|
|
||||||
const newMessages = appendToolCallMessage({
|
const newMessages = appendToolCallMessage({
|
||||||
tool_call_id: toolCall.id,
|
tool_call_id: toolCall.id,
|
||||||
function_name: toolCall.func.name,
|
function_name: toolCall.func.name,
|
||||||
tool_call_result: content,
|
tool_call_result: output,
|
||||||
})
|
})
|
||||||
|
|
||||||
return openai.chat.completions.create({
|
return openai.chat.completions.create({
|
||||||
@ -334,5 +344,3 @@ export const createChatCompletion = createAction({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
"@typebot.io/tsconfig": "workspace:*",
|
"@typebot.io/tsconfig": "workspace:*",
|
||||||
"@types/react": "18.2.15",
|
"@types/react": "18.2.15",
|
||||||
"typescript": "5.3.2",
|
"typescript": "5.3.2",
|
||||||
"@typebot.io/lib": "workspace:*"
|
"@typebot.io/lib": "workspace:*",
|
||||||
|
"@typebot.io/variables": "workspace:*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,11 @@ export type VariableStore = {
|
|||||||
get: (variableId: string) => string | (string | null)[] | null | undefined
|
get: (variableId: string) => string | (string | null)[] | null | undefined
|
||||||
set: (variableId: string, value: unknown) => void
|
set: (variableId: string, value: unknown) => void
|
||||||
parse: (value: string) => string
|
parse: (value: string) => string
|
||||||
|
list: () => {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
value?: string | (string | null)[] | null | undefined
|
||||||
|
}[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LogsStore = {
|
export type LogsStore = {
|
||||||
|
@ -2,4 +2,5 @@ import { ScriptBlock } from './schema'
|
|||||||
|
|
||||||
export const defaultScriptOptions = {
|
export const defaultScriptOptions = {
|
||||||
name: 'Script',
|
name: 'Script',
|
||||||
|
isExecutedOnClient: true,
|
||||||
} as const satisfies ScriptBlock['options']
|
} as const satisfies ScriptBlock['options']
|
||||||
|
@ -5,6 +5,7 @@ import { LogicBlockType } from '../constants'
|
|||||||
export const scriptOptionsSchema = z.object({
|
export const scriptOptionsSchema = z.object({
|
||||||
name: z.string().optional(),
|
name: z.string().optional(),
|
||||||
content: z.string().optional(),
|
content: z.string().optional(),
|
||||||
|
isExecutedOnClient: z.boolean().optional(),
|
||||||
shouldExecuteInParentContext: z.boolean().optional(),
|
shouldExecuteInParentContext: z.boolean().optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
109
packages/variables/executeFunction.ts
Normal file
109
packages/variables/executeFunction.ts
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import { Variable } from '@typebot.io/schemas'
|
||||||
|
import { parseVariables } from './parseVariables'
|
||||||
|
import { extractVariablesFromText } from './extractVariablesFromText'
|
||||||
|
import { parseGuessedValueType } from './parseGuessedValueType'
|
||||||
|
import { isDefined } from '@typebot.io/lib'
|
||||||
|
import { defaultTimeout } from '@typebot.io/schemas/features/blocks/integrations/webhook/constants'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
variables: Variable[]
|
||||||
|
body: string
|
||||||
|
args?: Record<string, unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const executeFunction = async ({
|
||||||
|
variables,
|
||||||
|
body,
|
||||||
|
args: initialArgs,
|
||||||
|
}: Props) => {
|
||||||
|
const parsedBody = parseVariables(variables, { fieldToParse: 'id' })(body)
|
||||||
|
|
||||||
|
const args = (
|
||||||
|
extractVariablesFromText(variables)(body).map((variable) => ({
|
||||||
|
id: variable.id,
|
||||||
|
value: parseGuessedValueType(variable.value),
|
||||||
|
})) as { id: string; value: unknown }[]
|
||||||
|
).concat(
|
||||||
|
initialArgs
|
||||||
|
? Object.entries(initialArgs).map(([id, value]) => ({ id, value }))
|
||||||
|
: []
|
||||||
|
)
|
||||||
|
const func = AsyncFunction(
|
||||||
|
...args.map(({ id }) => id),
|
||||||
|
'setVariable',
|
||||||
|
parsedBody
|
||||||
|
)
|
||||||
|
|
||||||
|
let updatedVariables: Record<string, any> = {}
|
||||||
|
|
||||||
|
const setVariable = (key: string, value: any) => {
|
||||||
|
updatedVariables[key] = value
|
||||||
|
}
|
||||||
|
const timeout = new Timeout()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const output = await timeout.wrap(
|
||||||
|
func(...args.map(({ value }) => value), setVariable),
|
||||||
|
defaultTimeout * 1000
|
||||||
|
)
|
||||||
|
timeout.clear()
|
||||||
|
return {
|
||||||
|
output,
|
||||||
|
newVariables: Object.entries(updatedVariables)
|
||||||
|
.map(([name, value]) => {
|
||||||
|
const existingVariable = variables.find((v) => v.name === name)
|
||||||
|
if (!existingVariable) return
|
||||||
|
return {
|
||||||
|
id: existingVariable.id,
|
||||||
|
name: existingVariable.name,
|
||||||
|
value,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(isDefined),
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Error while executing script')
|
||||||
|
console.error(e)
|
||||||
|
|
||||||
|
return {
|
||||||
|
error:
|
||||||
|
typeof e === 'string'
|
||||||
|
? e
|
||||||
|
: e instanceof Error
|
||||||
|
? e.message
|
||||||
|
: JSON.stringify(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
|
||||||
|
|
||||||
|
class Timeout {
|
||||||
|
private ids: NodeJS.Timeout[]
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.ids = []
|
||||||
|
}
|
||||||
|
|
||||||
|
private set = (delay: number) =>
|
||||||
|
new Promise((_, reject) => {
|
||||||
|
const id = setTimeout(() => {
|
||||||
|
reject(`Script ${defaultTimeout}s timeout reached`)
|
||||||
|
this.clear(id)
|
||||||
|
}, delay)
|
||||||
|
this.ids.push(id)
|
||||||
|
})
|
||||||
|
|
||||||
|
wrap = (promise: Promise<any>, delay: number) =>
|
||||||
|
Promise.race([promise, this.set(delay)])
|
||||||
|
|
||||||
|
clear = (...ids: NodeJS.Timeout[]) => {
|
||||||
|
this.ids = this.ids.filter((id) => {
|
||||||
|
if (ids.includes(id)) {
|
||||||
|
clearTimeout(id)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@ -1224,6 +1224,9 @@ importers:
|
|||||||
'@typebot.io/tsconfig':
|
'@typebot.io/tsconfig':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../../tsconfig
|
version: link:../../../tsconfig
|
||||||
|
'@typebot.io/variables':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../../variables
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: 18.2.15
|
specifier: 18.2.15
|
||||||
version: 18.2.15
|
version: 18.2.15
|
||||||
|
Reference in New Issue
Block a user