DEV Community

Cover image for GenStack - Bring PartyRock apps to your place
Stephen Sennett for AWS Community Builders

Posted on • Updated on • Originally published at community.aws

GenStack - Bring PartyRock apps to your place

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.

Diagram showing PartyRock app being processed into a generated app

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.

Animated GIF showing the process of creating and downloading the ZIP file

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.

Animated GIF showing the process of setting up and running the generated app

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
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

Comparison of app without CSS, and the default provided CSS

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">
Enter fullscreen mode Exit fullscreen mode

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">
Enter fullscreen mode Exit fullscreen mode

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. πŸ˜…

Alternative theme for Haiku Creator app

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.

Diagram showing a parent component interacting with a State, and passing the details to the stateless child components

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.

Diagram showing each component independently interacting with the State directly

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.

Diagram showing the flow of the different parts of a PartySmith app widget

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.

Diagram showing the layers that are used to build the apps

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));
Enter fullscreen mode Exit fullscreen mode

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)