I'm excited to announce Hashbrown v0.2 — our first major release since introducing the project. This version delivers more‑ergonomic tool‑calling APIs, a stable JavaScript runtime, improved chat‑state management, and a ground‑up React overhaul. Let's dive in!
Tool Calling
Hashbrown v0.1 supported tool calling, but the implementation was lacking polish. Now, with Hashbrown v0.2, Angular developers have a single API for creating tools:
import { createTool } from '@hashbrownai/angular';
export const getLightsTool = createTool({
name: 'getLights',
description: 'Gets the list of lights configured in the smart home',
handler: async () => {
const smartHome = inject(SmartHome);
const lights = await smartHome.getLights();
return lights;
},
});
We have removed createToolWithArgs
and have instead implemented it as an overload of createTool
:
import { createTool } from '@hashbrownai/angular';
export const controlLightTool = createTool({
name: 'controlLight',
description: 'Sets the brightness of a light',
schema: s.object('Control Light Input', {
id: s.string('The UUID of the light to control'),
brightness: s.number('The updated brightness of the light between 0-100'),
}),
handler: async (input) => {
const smartHome = inject(SmartHome);
const updatedLight = await smartHome.controlLight(input.id, input.brightness);
return updatedLight;
},
});
JavaScript Runtime
Part of Hashbrown's vision for generative UI includes generating glue code dynamically to connect user intent to your user interface. With Hashbrown v0.2, we're pleased to launch the first set of APIs for integrating Hashbrown's JavaScript runtime directly into your application code.
At a high level, here's how Hashbrown's JavaScript runtime works:
- We use QuickJS, a C-based JavaScript runtime that implements everything in ES2023.
- QuickJS runs in WebAssembly, giving us a perfect container for safely executing untrusted JavaScript in the browser.
- Hashbrown lets developers expose functions inside of the JavaScript runtime that LLMs can target when generating code.
- Functions exposed in the runtime run synchronously within the container, but can be implemented asynchronously on the host.
Here's how you define a runtime function with Hashbrown's Angular SDK:
import { createRuntimeFunction } from '@hashbrownai/angular';
@Injectable({ providedIn: 'root' })
class SmartHomeJavaScriptRuntime {
createLight = createRuntimeFunction({
name: 'createLight',
description: 'Adds a light to the smart home',
args: s.object('Create Light Input', {
name: s.string('The name of the light to create'),
brightness: s.number('The initial brightness of the light between 0-100'),
}),
result: s.object('The Newly Created Light', {
id: s.string('The UUID of the light'),
name: s.string('The name of the light'),
brightness: s.number('The brightness of the light'),
}),
handler: async (input) => {
const smartHome = inject(SmartHome);
const light = await smartHome.createLight(input);
return light;
},
});
}
Next, we create a runtime with the list of functions we want to expose inside of it:
import { createRuntime } from '@hashbrownai/angular';
@Injectable({ providedIn: 'root' })
class SmartHomeJavaScriptRuntime {
runtime = createRuntime({
functions: [this.createLight],
});
}
Now we can safely execute a script inside of the runtime:
const result = await this.runtime.run(
`
createLight({
name: 'Guest Bedroom Ceiling Light',
brightness: 100
});
`,
AbortSignal.timeout(10_000),
);
A few things to note:
- Running a script is always asynchronous on the host, and needs to be awaited.
- Developers must supply an abort signal when calling
run
to control how long the script can run. run
will return a Promise containing the evaluation result of the script. In the example above, it will return the newly created light.
Describing Runtimes
A key feature of Hashbrown's JavaScript runtime is the ability to generate a description of the runtime for an LLM to consume:
const description: string = this.runtime.describe();
This produces a description suitable for your system prompt that describes how the runtime works. We use the schemas of each function to create TypeScript type signatures as part of the description, making it clear to LLMs what functions exist, how to call those functions, and what those functions will respond with.
Tool JavaScript
The easiest way to get generated JavaScript into your runtime is to expose it to your LLM as a tool:
import { createToolJavaScript } from '@hashbrownai/angular';
chat = chatResource({
tools: [
createToolJavaScript({
runtime: this.runtime,
}),
],
});
Tool JavaScript leverages training instructions for major LLMs that encourages the LLM to reach for code execution as a means of grounding answers. We think plenty of developers will want to use Tool JavaScript with no exposed functions just to let the AI ground its responses.
Important: The JavaScript runtime is heavy. Bringing in the runtime will add at least 1 MB to your bundle. We intentionally load the WASM and implementation asynchronously, and it should tree shake away if you choose to build Hashbrown features without the runtime.
Improved Chat State Management
Thanks to contributions from U.G. Wilson and Jake Harris, you can now stop message generation:
class Chat {
resource = chatResource({
model: 'gpt-4.1',
system: 'You are a helpful assistant',
});
sendMessage(content: string) {
this.resource.sendMessage({ role: 'user', message: content });
}
cancel() {
this.resource.stop();
}
}
This will be a big help in creating ChatGPT-style chat apps, where you want to give your users control over message generation. The intermediary message is held in state until the message is regenerated.
React Overhaul
Building libraries for React is hard. Hashbrown brings in a full reactivity system, managing its own internal state, while wanting to call userland code. It's essentially a framework in this way, and we've struggled to get the React implementation working nicely within React's own reactivity system. With v0.2, we've made significant progress on this front. React developers should experience increased stability with React API and parity with the Angular SDK. We have also written up a first draft of our React documentation, breaking down Hashbrown for React developers.
Tool Calling
React developers have a brand new API for creating tools that play nicely within React:
import { useTool } from '@hashbrownai/react';
export function Chat() {
const smartHome = useSmartHome();
const getLightsTool = useTool({
name: 'getLights',
description: 'Gets the list of lights configured in the smart home',
deps: [smartHome],
handler: async () => {
const lights = await smartHome.getLights();
return lights;
},
});
}
If you are a React developer, we'd love to hear your feedback on our tool calling APIs. We expect further improvements on these APIs over the next few releases.
JavaScript Runtime
React developers can take advantage of the JavaScript runtime using similar hook-based APIs:
Defining Runtime Functions
Like tool calling, React developers can leverage Hashbrown's useRuntimeFunction
to define runtime functions:
import { useRuntimeFunction } from '@hashbrownai/react';
const useCreateLightFunction = () => {
const smartHome = useSmartHome();
return useRuntimeFunction({
name: 'createLight',
description: 'Adds a light to the smart home',
args: s.object('Create Light Input', {
name: s.string('The name of the light to create'),
brightness: s.number('The initial brightness of the light between 0-100'),
}),
result: s.object('The Newly Created Light', {
id: s.string('The UUID of the light'),
name: s.string('The name of the light'),
brightness: s.number('The brightness of the light'),
}),
deps: [smartHome],
handler: async (input) => {
const light = await smartHome.createLight(input);
return light;
},
});
};
Defining Runtimes
You then pass runtime functions to our useRuntime
hook:
import { useRuntime } from '@hashbrownai/react';
const useSmartHomeJavaScriptRuntime = () => {
const createLightFunction = useCreateLightFunction();
return useRuntime({
functions: [createLightFunction],
});
};
Creating Tool JavaScript
Finally, you can create Tool JavaScript with the useToolJavaScript
hook:
import { useToolJavaScript } from '@hashbrownai/react';
const useSmartHomeToolJavaScript = () => {
const runtime = useSmartHomeJavaScriptRuntime();
return useToolJavaScript({
runtime,
});
};
What comes next?
Hashbrown v0.2 is our first major release since introducing Hashbrown. In addition to all of these great new features, it includes numerous bug fixes and quality improvements. I suspect we will be spending the next few weeks hardening this release, with v0.3 just around the corner. Up next on our radar:
- Expanding our JavaScript Runtime - We have an incredible Finance demo that you are welcome to run in Hashbrown's GitHub repository. In developing it, we discovered that LLMs often generate scripts that leverage browser APIs for localization. For example, scripts generated in the Finance demo often include calls to
Intl.NumberFormat
. These APIs are not ECMAScript standards, and are therefore absent from QuickJS. We suspect there's a huge opportunity for us to polyfill these browser APIs inside of the runtime to improve the LLM's ability to generate scripts that are correctly localized. - Testing - "How do you test this?" is easily the #1 question we've received since showing Hashbrown off to the world. We have some ideas on how to build out a frontend-focused eval framework for proving quality on AI features. I'm excited to land our first testing APIs in the coming months.
- Expanded Modalities - We're still heads down improving the quality and consistency of our text modality, but we haven't forgotten image and audio. It's still a tossup which one we'll tackle first, but we won't cut v1.0 of Hashbrown without support for multiple modalities.
We need your help! There's so much work to do in Hashbrown, from tackling big features to improving our documentation. If you're looking for an open source project to get involved in, there's no better time than now to get involved in Hashbrown. Pick up issues on our GitHub repository, or drop me a line at mike@liveloveapp.com and I'll personally get you onboarded. A huge "thank you" to all of the contributors of this release, including: U.G. Wilson, Jake Harris, Hayden, Brandon Roberts, and Justin Schwartzenberger.
Also, shout out to "Let God Sort Em Out" by Clipse. The album is incredible, and I've been spinning it on repeat this weekend as we were landing the finishing touches on this release.
✌️