Crafting a Typescript NPM package with Bun - Function Agents
Here's how I crafted a Typescript NPM package with Bun called Function Agents:
The idea behind this package
I keep finding myself solving the same problems across different projects and found myself copying a lot of code inbetween them to save time. I figured that I could save myself a lot of time by creating a package that I could import into my projects and use to solve these problems, while also inviting others to contribute their OpenAI Function Agents (as I like to call them).
What is a Function Agent?
First, let's define what a Function Agent is.
Function - In this case I am referring to an OpenAI Function Call. This is an API call using OpenAI's Chat Completion API and passing in well-defined function definitons in the functions
argument. The gpt model will, based upon the user's message, system message, and the function definition descriptions, figure out which functions, if any, it should call. This gives you perfect results nearly everytime, by taking the arguments JSON object. It's a neat trick, and I've found it to be very useful in my projects. Each of these Function Agent's are abusing this trick in some way or another to get very consistent results without the use of LangChain or much prompt engineering at all.
Agent - This is a term I use loosely to the collection of Classes in the package. Each has a particular job to do and a persona to go along with it. We're letting OpenAI determine when to call a function, and with what parameters. The Agent guides the way with specific promptings and functions available in its toolset.
How to build a Typescript NPM Package with Bun
With that out of the way let's get into the meat of this article. I'll be going over how I built this package using Bun.
Setting up the project
mkdir new-project
cd new-project
bun init
# bun init helps you get started with a minimal project and tries to guess sensible defaults. Press ^C anytime to quit
# package name (test-project):
# entry point (index.ts):
# Done! A package.json file was saved in the current directory.
# + index.ts
# + .gitignore
# + tsconfig.json (for editor auto-complete)
# + README.md
# To get started, run:
# bun run index.ts
I moved the index.ts
file to src/index.ts
and updated the package.json
file to reflect this change.
The package.json that Bun makes looks like this:
{
"name": "test-project",
"module": "index.ts",
"type": "module",
"devDependencies": {
"bun-types": "latest"
},
"peerDependencies": {
"typescript": "^5.0.0"
}
}
I'm going to share the package.json
from function-agents
and I'll highlight key pieces that are necessary:
{
"name": "function-agents",
"module": "index.ts",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"type": "module",
"version": "0.0.17",
"description": "A collection of Function Agents with specific purposes, utilizing the OpenAI API.",
"scripts": {
"build": "bun build --target=node ./src/index.ts --outfile=dist/index.js && bun run build:declaration",
"build:declaration": "tsc --emitDeclarationOnly --project tsconfig.types.json",
"postbuild": "rimraf tsconfig.types.tsbuildinfo"
},
"devDependencies": {
"bun-types": "latest",
"prettier": "^3.0.3",
"rimraf": "^5.0.5",
"typescript": "^5.2.2"
},
"peerDependencies": {
"typescript": "^5.0.0"
},
"dependencies": {
"openai": "^4.11.0"
},
"repository": {
"type": "git",
"url": "https://github.com/mattlgroff/function-agents.git"
},
"keywords": [
"OpenAI",
"agents",
"functions",
"function calling"
],
"author": "Matthew Groff",
"license": "MIT",
"files": [
"dist/*.js",
"dist/*.d.ts"
]
}
Scripts
"scripts": {
"build": "bun build --target=node ./src/index.ts --outfile=dist/index.js && bun run build:declaration",
"build:declaration": "tsc --emitDeclarationOnly --project tsconfig.types.json",
"postbuild": "rimraf tsconfig.types.tsbuildinfo"
}
build
- This is the script that builds the package. It uses Bun to build the package and then runs thebuild:declaration
script. It does not include Type declaration, and Bun doesn't so this right now as of 1.0.3. So we added the following command to usetsc
to build typeDefs into our/dist
folder.build:declaration
- This script usestsc
to build the typeDefs into our/dist
folder.postbuild
- This script removes thetsconfig.types.tsbuildinfo
file that is created when we runbuild:declaration
. This is a file that is created bytsc
and is not needed in our package. If it's not deleted, then runningbun run build
will not generate type definitions.
Main and Types
"main": "dist/index.js",
"types": "dist/index.d.ts",
main
- This is the entry point for our package. It's the file that will be run when someone imports our package. It's pointed towards the/dist
folder, which is where our built code will be.types
- This is the entry point for our type definitions. It's pointed towards the/dist
folder, which is where our built type definitions will be.
Files
"files": [
"dist/*.js",
"dist/*.d.ts"
]
For whatever reason, npm publish
wasn't picking up all of the files in the /dist
folder (it was missing type defs), so I had to add this to the package.json
file. This tells npm publish
to include all of the .js
and .d.ts
files in the /dist
folder.
tsconfig nightmare
Since Bun doesn't support generating type declaration files, I needed to make a separate tsconfig.types.json. Here's my two files below and I hope that they help you avoid the troubleshooting I had to get the types building.
tsconfig.json
{
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
},
"lib": ["ESNext"],
"module": "esnext",
"target": "esnext",
"moduleResolution": "bundler",
"moduleDetection": "force",
"allowImportingTsExtensions": true,
"noEmit": true,
"composite": true,
"strict": true,
"downlevelIteration": true,
"skipLibCheck": true,
"jsx": "react-jsx",
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"allowJs": true,
"types": [
"bun-types"
]
},
"include": [
"src/**/*.ts"
]
}
tsconfig.types.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": false,
"emitDeclarationOnly": true,
"declaration": true,
"outDir": "./dist",
"rootDir": "./src",
},
"include": [
"src/**/*.ts"
]
}
The index.ts file
// Export Types
export {
ChatCompletionMessageWithFunction,
FunctionAgentCodeResponse,
FunctionAgentJsonResponse,
FunctionAgentMessageResponse,
FunctionAgentMessageResponseWithCitation,
} from './types';
export { MathInput, MathResult } from './agents/math';
export { Intent } from './agents/intent-classification';
// Export Agents
export { default as CitationAgent } from './agents/citation';
export { default as DataTransformationAgent } from './agents/data-transformation';
export { default as IntentClassificationAgent } from './agents/intent-classification';
export { default as JavaScriptCodeInterpreterAgent } from './agents/javascript-code-interpreter';
export { default as JavaScriptFunctionCallTransformationAgent } from './agents/javascript-function-call-transformation';
export { default as JavascriptDeveloperAgent } from './agents/javascript-developer';
export { default as MathAgent } from './agents/math';
export { default as SentimentClassificationAgent } from './agents/sentiment-classification';
This is the central file, the entry point for our package. It's where we export all of our types and agents. Think of it as a table of content. If I make a new agent or type that I want consumes of function-agents
to be able to import, I have to add it here to index.ts
and run the build command (bun run build
) again.
Publish to NPM
npm login
npm publish
Conclusion
Combining my full-stack Typescript experience with Generative AI has been a lot of fun. I'm excited to see what others do with this package and what Function Agents they create. Feel free to connect with me on LinkedIn.