Functions
Hopfield lets you define validation-driven functions which can be passed to the LLM. This lets you clearly build functions, which get transformed to JSON schema with zod-to-json-schema
, so the LLM can use these as tools.
Usage
Use chat models from OpenAI:
ts
importhop from "hopfield";importopenai from "hopfield/openai";importOpenAI from "openai";importz from "zod";consthopfield =hop .client (openai ).provider (newOpenAI ());constweatherFunction =hopfield .function ({name : "getCurrentWeather",description : "Get the current weather in a given location",parameters :z .object ({location :z .string ().describe ("The city and state, e.g. San Francisco, CA"),unit :z .enum (["celsius", "fahrenheit"]).describe (hopfield .template ().enum ("The unit for the temperature.")),}),});constchat =hopfield .chat ().functions ([weatherFunction ]);constmessages :hop .inferMessageInput <typeofchat >[] = [{role : "user",content : "What's the weather in Phoenix, AZ?",},];constresponse = awaitchat .get ({messages ,temperature : 0,});constchoice =response .choices [0];if (choice .__type === "function_call") {constfunctionParams =choice .message .function_call ;}
ts
importhop from "hopfield";importopenai from "hopfield/openai";importOpenAI from "openai";importz from "zod";consthopfield =hop .client (openai ).provider (newOpenAI ());constweatherFunction =hopfield .function ({name : "getCurrentWeather",description : "Get the current weather in a given location",parameters :z .object ({location :z .string ().describe ("The city and state, e.g. San Francisco, CA"),unit :z .enum (["celsius", "fahrenheit"]).describe (hopfield .template ().enum ("The unit for the temperature.")),}),});constchat =hopfield .chat ().functions ([weatherFunction ]);constmessages :hop .inferMessageInput <typeofchat >[] = [{role : "user",content : "What's the weather in Phoenix, AZ?",},];constresponse = awaitchat .get ({messages ,temperature : 0,});constchoice =response .choices [0];if (choice .__type === "function_call") {constfunctionParams =choice .message .function_call ;}
The input function definition will be validated to make sure that:
- Descriptions are provided for every argument.
- No error-prone types are used in
parameters
(for OpenAI, this includesZodTuple
,ZodBigInt
, andZodAny
). - If a type in the JSON schema performs better with a templated description (like
enum
), it is checked against the template.
All of these checks are entirely customizable and can be overridden/disabled.
Parameters
Function Definition
The function
takes a name
, description
, and a Zod schema for the parameters
which can be passed into it. These are all required fields to define a function, and are used to construct the JSON schema definition for the function, to be passed to the LLM.
ts
constweather =hopfield .function ({name : "getCurrentWeather",description : "Get the current weather in a given location",parameters :z .object ({location :z .string ().describe ("The city and state, e.g. San Francisco, CA"),unit :z .enum (["celsius", "fahrenheit"]).describe (hopfield .template ().enum ("The unit for the temperature.")),}),});constclassifyMessage =hopfield .function ({name : "classifyMessage",description : "Triage an incoming support message.",parameters :z .object ({summary :z .string ().describe ("The summary of the message."),category :SupportCategoryEnum .describe (hopfield .template ().enum ("The category of the message.")),}),});constchat =hopfield .chat ().functions ([weather ,classifyMessage ]);constmessages :hop .inferMessageInput <typeofchat >[] = [{role : "user",content : "How can I get a refund??",},];constresponse = awaitchat .get ({messages ,temperature : 0,});if (response .choices [0].__type === "function_call") {constfunctionName =response .choices [0].message .function_call .name ;}
ts
constweather =hopfield .function ({name : "getCurrentWeather",description : "Get the current weather in a given location",parameters :z .object ({location :z .string ().describe ("The city and state, e.g. San Francisco, CA"),unit :z .enum (["celsius", "fahrenheit"]).describe (hopfield .template ().enum ("The unit for the temperature.")),}),});constclassifyMessage =hopfield .function ({name : "classifyMessage",description : "Triage an incoming support message.",parameters :z .object ({summary :z .string ().describe ("The summary of the message."),category :SupportCategoryEnum .describe (hopfield .template ().enum ("The category of the message.")),}),});constchat =hopfield .chat ().functions ([weather ,classifyMessage ]);constmessages :hop .inferMessageInput <typeofchat >[] = [{role : "user",content : "How can I get a refund??",},];constresponse = awaitchat .get ({messages ,temperature : 0,});if (response .choices [0].__type === "function_call") {constfunctionName =response .choices [0].message .function_call .name ;}
Options
The function
also allows an optional options
parameter, which lets you override the runtime checks for the schema. This includes the checks for requiring descriptions on Zod schema parameters, as well as overriding the list of "disabled types", which are Zod types which typically produce unreliable results from an LLM.
type HopfieldFunctionOptions = {
/**
* Allows descriptions to not be checked on the function parameters. This defaults to `true`.
*/
requireDescriptions?: boolean;
/**
* Allows you override or disable "unstable" types, which are types that do not typically
* produce good results with a given model. These are defined on a per-model basis.
*
* Set to false to allow all "unstable" types.
*/
disabledTypes?: ZodFirstPartyTypeKind[] | false;
};
type HopfieldFunctionOptions = {
/**
* Allows descriptions to not be checked on the function parameters. This defaults to `true`.
*/
requireDescriptions?: boolean;
/**
* Allows you override or disable "unstable" types, which are types that do not typically
* produce good results with a given model. These are defined on a per-model basis.
*
* Set to false to allow all "unstable" types.
*/
disabledTypes?: ZodFirstPartyTypeKind[] | false;
};