Skip to content

Commit

Permalink
feat: Add some basic documentation to the README. (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
jmoseley authored Dec 30, 2024
1 parent 5e2b3df commit de6c2ec
Show file tree
Hide file tree
Showing 2 changed files with 220 additions and 19 deletions.
31 changes: 31 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Contributing

## ⚙️ Developing the library

### 📦 Building

```bash
pnpm build
```

### 🧪 Testing

If you want to run the tests of the project, you can execute the following command:

```bash
pnpm test
```

### 💅 Linting

To run the linter you can execute:

```bash
pnpm lint
```

And for trying to fix lint issues automatically, you can run:

```bash
pnpm lint:fix
```
208 changes: 189 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<h1 align="center">GenSX</h1>
<h1 align="center">&lt;GenSX /&gt;</h1>

<p align="center">
Make LLMs work good
Create LLM workflows from components.
</p>

## LLM + JSX = ⚡️

GenSX is a library for building LLM workflows, using JSX for simple and fast development.
`<GenSX />` is a framework for building LLM workflows and AI agents with JSX on the backend. Every `<GenSX />` component is a pure function, and thus easily shareable by default.

```jsx
import * as gsx from "gensx";
Expand All @@ -27,42 +27,212 @@ const [tweet, blogPost] = await gsx.execute(
);
```

## 📦 Installing
## Getting started

### 📦 Installing

```bash
pnpm install gensx
```

```bash
yarn add gensx
```

```bash
npm install gensx
```

yarn add gensx
#### Dependencies

This project does not have a dependency on `react`, or any other JSX-based library. It provides a custom JSX runtime that can be used by the Typescript compiler, or whichever bundler you're using to bundle your code.

### Configure your project

```ts
// tsconfig.json
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "gensx"
}
}
```

## ⚙️ Developing
#### For a React project with Typescript

### 📦 Building
If you're using React, and can't change the `jsxImportSource` in your `tsconfig.json` without breaking your project, you can use a [per-file pragma](https://www.typescriptlang.org/tsconfig/#jsxImportSource) to enable the `gensx` runtime for specific files.

```bash
pnpm build
```tsx
/** @jsxImportSource gensx */

// ...
```

### 🧪 Testing
Unfortunately, you cannot use both `gensx` and `react` in the same file, as they require different `jsxImportSource` values.

#### For a Babel project

If you want to run the tests of the project, you can execute the following command:
If you're using babel to bundle JSX into Javascript, you can use the `@babel/plugin-transform-react-jsx` plugin to enable JSX support, and use the pragma to specify the `gensx` runtime.

```bash
pnpm test
pnpm install @babel/plugin-transform-react-jsx
```

### 💅 Linting
```js
// babel.config.js
module.exports = {
plugins: ["@babel/plugin-transform-react-jsx"],
};
```

To run the linter you can execute:
```jsx
/** @jsxImportSource gensx */

```bash
pnpm lint
// ...
```

And for trying to fix lint issues automatically, you can run:
## Building a workflow, the declarative way!

```bash
pnpm lint:fix
```jsx
import * as gsx from "gensx";

interface ResearchBrainstormProps {
prompt: string;
}
type ResearchBrainstormOutput = string[];

/**
* A `gsx.Component` is just function. Within them you can do things like make calls to your vector DB, call APIs, or invoke models like OpenAI, Claude, Perplexity, and more.
*
* Every `gsx.Component` automatically supports accessing it's outputs by nesting a `child` function with no additional work required. For instance:
*/
const ResearchBrainstorm = gsx.Component<
ResearchBrainstormProps,
ResearchBrainstormOutput
>(async ({ prompt }) => {
console.log("🔍 Starting research for:", prompt);
const topics = await Promise.resolve(["topic 1", "topic 2", "topic 3"]);
return topics;
});

interface PerformResearchProps {
topic: string;
}
type PerformResearchOutput = string;
const PerformResearch = gsx.Component<ResearchProps, ResearchOutput>(
async ({ topic }) => {
console.log("📚 Researching topic:", topic);
// Make a call to your vector DB, or an API, or invoke a model like OpenAI, Anthropic, Perplexity, and more.
const results = await Promise.resolve([
"research result 1",
"research result 2",
"research result 3",
]);
return results;
},
);

interface WriteDraftProps {
research: string;
prompt: string;
}
type WriteDraftOutput = string;
const WriteDraft = gsx.Component<WriteDraftProps, WriteDraftOutput>(
async ({ research, prompt }) => {
console.log("✍️ Writing draft based on research");
// Invoke a model like OpenAI, Anthropic, Perplexity, and more.
const draft = await Promise.resolve(
`**draft\n${research}\n${prompt}\n**end draft`,
);
return draft;
},
);

interface EditDraftProps {
draft: string;
}
type EditDraftOutput = string;
const EditDraft = gsx.Component<EditDraftProps, EditDraftOutput>(
async ({ draft }) => {
console.log("✨ Polishing final draft");
// Invoke a model like OpenAI, Anthropic, Perplexity, and more.
const editedDraft = await Promise.resolve(`edited result: ${draft}`);
return editedDraft;
},
);

interface WebResearcherProps {
prompt: string;
}
type WebResearcherOutput = string[];
const WebResearcher = gsx.Component<WebResearcherProps, WebResearcherOutput>(
async ({ prompt }) => {
console.log("🌐 Researching web for:", prompt);
// Make a call to your vector DB, or an API, or invoke a model like OpenAI, Anthropic, Perplexity, and more.
const results = await Promise.resolve([
"web result 1",
"web result 2",
"web result 3",
]);
return results;
},
);

type ParallelResearchOutput = [string[], string[]];
interface ParallelResearchComponentProps {
prompt: string;
}

// You can build complex workflows by nesting components. When you pass a child function to a component, it will be called with the output of that component, and you can use that output inside any child components. If you don't specify a function as a child, the result from that leaf node will be bubbled up as the final result.
//
// We again wrap using the gsx.Component function, and we annotate the output type with the type of the final result.
const ParallelResearch = gsx.Component<
ParallelResearchComponentProps,
ParallelResearchOutput
>(({ prompt }) => (
<>
<ResearchBrainstorm prompt={prompt}>
{topics => topics.map(topic => <PerformResearch topic={topic} />)}
</ResearchBrainstorm>
<WebResearcher prompt={prompt} />
</>
));

interface BlogWritingWorkflowProps {
prompt: string;
}
type BlogWritingWorkflowOutput = string;
const BlogWritingWorkflow = gsx.Component<
BlogWritingWorkflowProps,
BlogWritingWorkflowOutput
>(async ({ prompt }) => (
<ParallelResearch prompt={prompt}>
{([catalogResearch, webResearch]) => {
console.log("🧠 Research:", { catalogResearch, webResearch });
return (
<WriteDraft
research={[catalogResearch.join("\n"), webResearch.join("\n")].join(
"\n\n",
)}
prompt={prompt}
>
{draft => <EditDraft draft={draft} />}
</WriteDraft>
);
}}
</ParallelResearch>
));

async function main() {
console.log("🚀 Starting blog writing workflow");

// Use the gensx function to execute the workflow and annotate with the output type.
const result = await gsx.execute<BlogWritingWorkflowOutput>(
<BlogWritingWorkflow prompt="Write a blog post about the future of AI" />,
);
console.log("✅ Final result:", { result });
}

await main();
```

0 comments on commit de6c2ec

Please sign in to comment.