Calling functions from GPT
GPT Functions
Using GPT functions with DSL can give you a powerfull conversation model.
When you writing any function with JavaDoc like
context { output customerName: string? = null; output customerPhone: string? = null; } /** * Store collected customer information such as name and phone number * @param customerName customer name provided * @param customerPhone customer phone provided * @return true */ function storeCustomerInfo(customerName: string?, customerPhone: string?): boolean { set $this.customerName = $this.customerName ?? customerName; set $this.customerPhone = $this.customerPhone ?? customerPhone; return true; }
Our Dialog Engine passes information about a function storeCustomerInfo
and it's description to the OpenAI inside a request.
And this function will be called when user provides any information described in arguments.
Result of function call will be passed to GPT on next call of #answerWithGPT
/** * Get price of fruit in USD * @param fruit name of fruit * @returns price in USD or `No such fruit` if fruit is not found */ function getFruitPrice(fruit: string) : number|string { if (fruit == "apples") { return 6.29; } if (fruit == "oranges") { return 1.49; } return "No such fruit"; }
Examples
Full DSL example
context { input endpoint: string; } /** * Get price of fruit in USD * @param fruit name of fruit * @returns price in USD or `No such fruit` if fruit is not found */ function getFruitPrice(fruit: string) : number|string { if (fruit == "apples") { return 6.29; } if (fruit == "oranges") { return 1.49; } return "No such fruit"; } start node root { do { #connectSafe($endpoint); #sayText("Hi, this is Mary! How can I help you today?"); wait *; } transitions { gpt: goto gpt on true; } } node gpt { do { // We will be here when user says something, or retry is required var a = #answerWithGPT(`Your name is Mary. You are working in fruit seller contact center. You can only tell customer a price of requested fruit. If you have no price for fruit tell: We have no such fruit `, interruptible:true, gptOptions: { model:"openai/gpt-4" }, sayOptions: { interruptDelay: 1.0 }); // Call answerWithGPT one more time for passing result to the GPT if (a.functionCalled) { #log("Called a function, retry"); goto retry; } wait *; } transitions { gpt: goto gpt on true; retry: goto gpt; } } digression @exit { conditions { on true tags: onclosed; } do { exit; } }
Forcing a function call
You can use an gptOption
named function_call
with name of the function for forcing a function call.
Restricting functions passed to the GPT
You can use an gptOption
named provided_functions
with string array as value with functions, that a visible to the GPT.
For example:
{ model: "openai/gpt-4o-mini", provided_functions: ["functionA", "functionB"] }
It can help you, when you want to disable part of functionality for the conversation at a runtime.
Excluding specific functions
You can use an gptOption
named except_functions
to explicitly exclude specific functions from being available to GPT, regardless of other inclusion rules.
{ model: "openai/gpt-4o-mini", except_functions: ["deleteCustomer", "resetDatabase"] }
How except_functions works
The except_functions
option has the highest priority and will exclude functions even if they would normally be included by:
provided_functions
provided_scopes
- Default availability (functions without scopes)
Use cases for except_functions
-
Security: Exclude dangerous functions in certain conversation contexts
{ provided_scopes: ["admin"], except_functions: ["deleteAllData"] // Admin scope but exclude dangerous operations }
-
Feature toggling: Temporarily disable specific functionality
{ except_functions: ["experimentalFeature"] // Disable while testing }
-
Context-specific restrictions: Remove functions not relevant to current conversation flow
{ provided_scopes: ["customer", "support"], except_functions: ["billingOperations"] // Customer support without billing access }
Order of evaluation
The system applies function filtering in this order:
- Start with all available functions
- Apply
provided_scopes
filtering (if specified) - Apply
provided_functions
filtering (if specified) - Remove functions listed in
except_functions
(final step)
This means except_functions
always wins - if a function is listed there, it will be excluded regardless of other options.
Controlling function access with scopes
You can organize your functions into logical groups using scopes and then control which functions are available to GPT by specifying allowed scopes. This provides a more flexible way to manage function access compared to listing individual functions.
Defining function scopes in DSL
To assign scopes to a function, use multiple @scope
tags in the function's JavaDoc comment:
Important syntax restrictions:
- Scope names can only contain alphabetic characters (a-z, A-Z)
- No numbers, underscores, hyphens, or special characters are allowed
- Use separate
@scope
tags for each scope (not brackets or commas) - Each
@scope
tag should contain only one scope name
/** * Store collected customer information such as name and phone number * @scope customer * @scope management * @param customerName customer name provided * @param customerPhone customer phone provided * @return true */ function storeCustomerInfo(customerName: string, customerPhone: string): boolean { set $this.customerName = $this.customerName ?? customerName; set $this.customerPhone = $this.customerPhone ?? customerPhone; return true; } /** * Get price of fruit in USD * @scope inventory * @scope pricing * @param fruit name of fruit * @returns price in USD or `No such fruit` if fruit is not found */ function getFruitPrice(fruit: string) : number|string { if (fruit == "apples") { return 6.29; } if (fruit == "oranges") { return 1.49; } return "No such fruit"; } /** * Function without scopes - available when scopes are specified (non-empty) or in legacy mode * @param text message to log * @return true */ function logMessage(text: string): boolean { #log(text); return true; }
Using provided_scopes option
You can control which functions are available to GPT by specifying allowed scopes using the provided_scopes
option:
var response = #answerWithGPT("Your prompt here", gptOptions: { model: "openai/gpt-4o-mini", provided_scopes: ["customer", "inventory"] });
How scope-based filtering works
The system applies the following logic when determining which functions are available:
- No scopes specified (legacy behavior): All functions are available, unless
provided_functions
is specified - Empty scopes array
[]
: Completely restrictive mode - NO functions are available, not even common/utility functions. Only functions explicitly listed inprovided_functions
are allowed - Non-empty scopes array: Functions are available if:
- The function has no scopes defined (common/utility functions) - these are always included when scopes are specified
- The function has scopes and at least one scope matches the provided scopes
- The function is explicitly listed in
provided_functions
(overrides scope restrictions)
Combining scopes with other options
You can combine provided_scopes
with other function control options:
{ model: "openai/gpt-4o-mini", provided_scopes: ["customer"], provided_functions: ["logMessage"], // Always include this function except_functions: ["deleteCustomer"] // Always exclude this function }
Dynamic scopes from SDK
When using dynamic tools from the SDK (version 0.11.5 and higher), you can also specify scopes:
await conv.setDynamicTool({ name: "getUserInfo", description: 'Get user information from database', scopes: ["user", "database"], // Specify scopes for this tool schema: Type.Object({ userId: Type.String({ description: "User ID to lookup"}) }) }, async (args, conv) => { // Implementation return await getUserFromDatabase(args.userId); });
Then control access using the same provided_scopes
option:
{ provided_scopes: ["user"] // This will include the getUserInfo dynamic tool }
GPT is saying a function name instead of calling it.
You can use an gptOption
named allow_function_name_in_response
with false
value.
In this case, our platform will handle it, block the sentence and enforce GPT to call this function.
GPT is saying in json
format instead of pronouncable sayText
You can us an gptOption
named ignore_json_output
with false
value.
Our platform will handle such output and drop it before response to the human.
Providing functions from the SDK (SDK 0.11.2 and higher)
You can dynamically register functions through the JavaScript SDK that will be available for GPT to call during conversations. This approach provides more flexibility than defining functions in DSL and allows you to leverage external services or databases in your function implementations.
import { Type } from "@sinclair/typebox"; app.queue.on("ready", async (id, conv, info) => { // Register a dynamic tool that GPT can call during the conversation await conv.setDynamicTool({ // Function name that GPT will use to call this tool name: "getFruitPrice", // Clear description of what the function does - this helps GPT understand when to use it description: 'Returns the price of a fruit based on its name and optional quantity', // Schema definition using TypeBox to define parameters and their types schema: Type.Object({ name: Type.String({ description: "Name of the fruit"}), count: Type.Optional(Type.Number({ description: "Count of the fruits, defaults to 1 if not provided"})) }) }, async (args, conv) => { // Implementation of the function - this code runs when GPT calls getFruitPrice console.log(`getFruitPrice called with args: ${JSON.stringify(args)}`); if (args.name === "apple") { return 3.25 * (args.count ?? 1); } if (args.name === "orange") { return 6 * (args.count ?? 1); } return "No such fruit"; }); const result = await conv.execute(); }
This approach gives you several advantages:
- Functions can be created dynamically based on conversation context
- Implementation can connect to external systems, databases, or APIs
- TypeBox provides strong typing and validation for function parameters
- Return values are automatically passed back to GPT for continuation of the conversation
You can use an gptOption
named use_dynamic_tools
to disable dynamic functions passed from the SDK for a specific call:
{ use_dynamic_tools: false }