Skip to content

Support for Simple Types

We support most basic types out of the box with our InstructMacro macro now. Here's a quick rundown of how to use our macro. Note that the struct name and the description you provide is fed in to the OpenAI call - what we show below is just the function parameter that we provide.

Importing the Macro

You'll need to use the following imports to use the macro

use instruct_macros::{InstructMacro};
use instruct_macros_types::{
    InstructMacro, InstructMacroResult, Parameter, ParameterInfo, StructInfo,
};

 #[derive(InstructMacro, Debug)]
struct User {
    pub name: String,
    pub age: String
}

/*
{
  "type": "object",
  "properties": {
    "age": {
      "type": "string",
      "description": ""
    },
    "name": {
      "type": "string",
      "description": ""
    }
  },
  "required": [
    "name",
    "age"
  ]
}
*/

Adding a Description

We provide a #[description( Description goes here )] annoration that you can add to your struct. This will be included in the function call which we will send over to OpenAI/other inference providers.

This is the same for individual fields or the entire struct with multi-line comments being relatively easy to implement.

#[derive(InstructMacro, Debug)]
#[description("This is a user object")]
struct User {
    #[description("This is the name of the user")]
    pub name: String,
    #[description(
        "This is\
    a multi-line description\
    which can be used"
    )]
    pub age: String,
}

/*
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "This is the name of the user"
    },
    "age": {
      "type": "string",
      "description": "This isa multi-line descriptionwhich can be used"
    }
  },
  "required": [
    "name",
    "age"
  ]
}
*/

Advanced Types

Enums

Enums are supported in the same way. Just declare it as if you would a normal Serde object and it'll work out of the box seamlessly.

#[derive(InstructMacro, Debug)]
#[description("This is an enum representing the status of a person")]
pub enum Status {
    Active,
    Inactive,
    Pending,
}

#[derive(InstructMacro, Debug)]
pub struct User {
    name: String,
    status: Status,
}

/*
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": ""
    },
    "status": {
      "type": "string",
      "description": "This is an enum representing the status of a person",
      "enum_values": [
        "Active",
        "Inactive",
        "Pending"
      ]
    }
  },
  "required": [
    "name",
    "status"
  ]
}
*/

If you'll like to provide a custom description for your enum field in your struct, just use the description annoration and we'll override the default description of the enum when we generate the function parameter.

#[derive(InstructMacro, Debug)]
#[description("This is an enum representing the status of a person")]
pub enum Status {
    Active,
    Inactive,
    Pending,
}

#[derive(InstructMacro, Debug)]
pub struct User {
    name: String,
    #[description("This is the person's status")]
    status: Status,
}

/*
{
  "type": "object",
  "properties": {
    "status": {
      "type": "string",
      "description": "This is the person's status",
      "enum_values": [
        "Active",
        "Inactive",
        "Pending"
      ]
    },
    "name": {
      "type": "string",
      "description": ""
    }
  },
  "required": [
    "name",
    "status"
  ]
}
*/

Vectors

Sometimes you might want to extract a list of objects (Eg. Users). To do so, you can just use a simple Vec object.

#[derive(InstructMacro, Debug)]
#[description("This is a struct with Option types")]
struct Numbers {
    #[description("This is a list of numbers")]
    pub numbers: Vec<i32>,
}

/*
{
  "type": "object",
  "properties": {
    "users": {
      "type": "array",
      "description": "A list of users",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": ""
          }
        }
      }
    }
  },
  "required": [
    "users"
  ]
}
*/

Options

We also support Option types. This is most popular when using a Maybe pattern where we have some form of data that we might want to extract.

#[derive(InstructMacro, Debug)]
#[allow(dead_code)]
#[description("This is a user struct")]
struct User {
    #[description("This is the user's name")]
    pub name: String,
    #[description("This is the user's age")]
    pub age: i32,
}

#[derive(InstructMacro, Debug)]
#[allow(dead_code)]
#[description("This is a struct with Option<user> type")]
struct MaybeUser {
    #[description("This is an optional user field")]
    pub user: Option<User>,
    error_message: Option<String>
}

/*
{
  "type": "object",
  "properties": {
    "user": {
      "type": "object",
      "description": "This is an optional user field. If the user is not present, the field will be null",
      "properties": {
        "age": {
          "type": "number",
          "description": ""
        },
        "name": {
          "type": "string",
          "description": ""
        }
      }
    },
    "error_message": {
      "type": "string",
      "description": ""
    }
  },
  "required": []
}
*/

Nested Structs

We also support Nested Structs out of the box - see example below

#[derive(InstructMacro, Debug, Serialize, Deserialize)]
struct Address {
    location: String,
    distance: i32,
}

#[derive(InstructMacro, Debug, Serialize, Deserialize)]
struct User {
    name: String,
    age: u8,
    address: Address,
}

/*
{
  "type": "object",
  "properties": {
    "address": {
      "type": "object",
      "description": "",
      "properties": {
        "location": {
          "type": "string",
          "description": ""
        },
        "distance": {
          "type": "number",
          "description": ""
        }
      }
    },
    "name": {
      "type": "string",
      "description": ""
    },
    "age": {
      "type": "number",
      "description": ""
    }
  },
  "required": [
    "name",
    "age",
    "address"
  ]
}
*/