Update: By request of Amazon's legal team, the original name 'PartySmith' has been changed to GenStack.
Amazon PartyRock is a really cool concept, allowing you to build Generative AI applications using a clean visual UI, and removing all coding from the process, except the key parts of handling prompts. But it's had one problem - you can't take it with you.
As part of Developer Experience month, I've built ๐จGenStack (formerly known as PartySmith) - a platform that enables you to take the applications you've built with PartyRock, and host them yourself, on your own platform.
๐ฆ How it Works
First, go to genstack.ssennett.net. Drop in the URL of your public PartyRock application, and that's pretty much it! You can download a ZIP file with your application ready-to-go.
Inside you'll find the application, built on SvelteKit, which can be readily deployed to AWS using SST, or even other providers like Vercel with other SvelteKit adapters. There's even a README file with all the details built right into the template.
That's it! In fact, you can see the Demonstration App built around the Costume Theme Designer PartyRock app by AWS Community Builder Nadia Reyhani
For engineers who want to experiment with Amazon Bedrock that they can host themselves, but skip having to build a whole application, this is a handy intermediate step.
๐ง How it REALLY works
If you're a technical person, you probably want some of the inner-workings. So, let's talk details:
๐บ๏ธ Defining the Application
When you build an application on PartyRock, everything you've setup is defined into a JSON file, which looks something like this.
{
"result": {
"data": {
"appId": "<uniqueAppId>",
"username": "ssennettau",
"creationDate": "2024-01-01T00:00:00.0000",
"definition": {
"version": 1,
"widgets": [
{
"x": 0,
"y": 0,
"type": "text-input",
"title": "Input Thing",
"width": 12,
"height": 5,
"placeholder": "Write stuff here with the placeholder",
"defaultValue": ""
},
{
"x": 0,
"y": 5,
"type": "inferred-text",
"title": "Summarized Thing",
"width": 12,
"height": 5,
"prompt": "Summarize the following thing:\n\n[Input Thing]\n",
"parameters": {
"model": "bedrock-claude-v2"
}
}
]
},
"title": "Random Testing App",
"lastModifiedDate": "2024-01-01T00:00:00.0000",
"version": 10
}
}
}
The widgets
lists all of the different components you've added to your PartyRock app. In this case, it accepts a text input from the user, and uses Claude to output the summarized results.
This is the basis of how the app works, using this as a blueprint for the various widgets.
๐จ Styling
GenStack apps come with a stylesheet built on Tailwind CSS, so it's very easy to adapt both to a similar theme to PartyRock, but also to whatever you'd prefer to use for your application.
Tailwind CSS does result in a lot of really lengthy class names, which can be a pain to work with.
<div class="z-10 max-w-5xl w-fit items-center justify-between text-3xl font-bold lg:font-semibold pb-16 lg:flex lg:text-5xl">
To solve this, I've used @apply
rule's to consolidate the Tailwind Features into a single CSS stylesheet so we can use shorter and more meaningful class names.
<div class="party-header">
Another bonus is if you want to modify the way your app looks, it's a simple update in one place in the CSS. There's probably more best-practice ways to do this, but this is what happens when you let an ex-sysadmin write a web app - be glad I'm not using <table>
tags. ๐
Looking forward to seeing some of the things people are able to dream up!
๐ State Management
Unsurprisingly there's a lot of passing information back-and-forth in these apps, so I've had to try and plan the best way to do that. And my experience in front-end development isn't extensive, so if you have thoughts on my approach, please drop them in the comments!
Typically you want to keep state management at the highest-level possible, leaving your components stateless, just populating the contents being passed to them.
This doesn't work so well here...
The components in our app aren't quite as simple as they appear. Between tracking the different dependent inputs, calling Amazon Bedrock, and passing the data forward to any other components, there's a few things going on. In this case, I've flipped the concept around, with the state almost exclusively being managed within each widget.
This means each widget completely keeps track of what inputs it's expecting, how it should be processing it, making the call to Amazon Bedrock via our backend, displaying the result, and storing it for other widgets to use.
This made development a constant game of whack-a-mole when dealing with complex apps, trying to track what was causing any particular type of problem.
The main reason I opted for this approach was because of it's intended use for different applications. If it was a single-use app, I could code all the logic at the parent, per usual. But since this could be used with any PartyRock app, the design has to be a lot more flexible than usual.
๐ ๏ธ Building the Builder
This project really turned into two projects - first building the prototype target app which runs a PartyRock definition, and secondly the app to build those apps. Let's talk about the latter.
Code Generators aren't anything new, but it was something I haven't done before. Breaking the problem down into pieces, apps are generated effectively in layers.
The primary
layer is the basic app in SvelteKit, absent the app definition file. In any app, first a copy of this template layer is taken and copied to a temp directory, and a copy of the definition file from the PartyRock source app is loaded into the necessary folder.
The additional features, like stylesheets or SST being baked-in have their own template layers, which will be copied in over-the-top of any existing files.
// Get the templated package.json
const packageJsonFile = path.join(tempPath, 'package.json');
const packageJsonContents = await fs.promises.readFile(
packageJsonFile,
'utf8');
let packageJson = JSON.parse(packageJsonContents);
// Add the necessary packages
packageJson.dependencies['postcss'] = '^8.4.32';
packageJson.dependencies['tailwindcss'] = '^3.4.0';
// Commit the updated contents of the file
await writeFile(packageJsonFile, JSON.stringify(
packageJson,
null,
4));
Finally, any files like package.json
and README.md
will be updated to reflect the necessary updates. This was less intensive than I'd expected, and often accomplished with some simple regex and file manipulation.
All of the code is wrapped up in builder.ts, with buildGenStackApp
orchestrating the steps of the process. I had plans to refactor this code to make it a bit cleaner, but this project has taken far longer than I'd planned, so it's not been as high on the priority list.
๐ Limitations
Unsurprisingly, this doesn't give the whole PartyRock experience that you know and love. Namely:
- ๐ผ๏ธ You can't do any visual editing - Everything is down to the code.
- ๐ฆ Layouts aren't retained - Just a single column layout by default.
- ๐ Source app needs to be public - Unable to handle private apps.
- ๐ฐ Amazon Bedrock isn't free - Your costs are your own, so take care.
Partly that's by design, because PartyRock is an awesome platform and I don't want to take away from that - this is just a bonus. The other part is that it'd take a long time to build, so I'm not going to re-invent (hah) AWS's perfectly good wheel.
For the layout, keeping it to a single column makes the code a lot easier to follow and modify. I'm sure there's ways to build it a lot more flexibly, but my attempts made the code a lot harder to follow.
Since the apps will run using Amazon Bedrock in your account, you will also need to ensure Model Access has been setup for the necessary foundation models.
๐ซต Wrap-up
The goal is to take your experiments to the next-level with minimal friction. Here's a few things that came to mind:
- Deploy an internal-facing corporate app in a VPC
- Build an MVP for a more extensive public app
- Highly customized personal productivity apps
- Add payment processing and auth for monetization
PartyRock is an awesome creation by the team at AWS, and I hope this fills a niche to make it even more useful. And if the team want to take this as an idea to build an official version, I call that a win.
Lastly, this was also a chance to stretch my skills deeper into frontend development, something I'm not the best at. If anyone wants to make improvements, forks, or branch of a whole separate version into React/Next.Js or whatever, please do so, and let me know so I can follow along!
Top comments (0)