-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: adding concept doc for concepts (#168)
Adds a conceptual doc on concepts and a simple example. Also makes minor tweaks to the basic concepts section on contexts.
- Loading branch information
1 parent
a27722a
commit bb46bbe
Showing
14 changed files
with
357 additions
and
117 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
--- | ||
title: Context and providers | ||
description: Learn how to use contexts and providers in GenSX to share data across components. | ||
sidebar: | ||
order: 3 | ||
--- | ||
|
||
Contexts and providers are powerful tools in GenSX for sharing data and managing configuration across components without explicitly passing props through every level of your component tree. They work similarly to [React's Context API](https://react.dev/reference/react/useContext) but are adapted to work with GenSX workflows. | ||
|
||
## What are contexts and providers? | ||
|
||
Contexts and providers work together to share data and manage dependencies across components. | ||
|
||
- **Contexts** give you a way to share data (like state, configuration, or dependencies) across components without manually passing props down the component tree. | ||
- **Providers** are components that supply data or services to a context. Any component within a provider's subtree can access the context. | ||
|
||
The two concepts are interdependent so you can't use one without the other. Combined, they're great for: | ||
|
||
- Providing data to components without prop drilling | ||
- Sharing configuration and dependencies, such as clients, for your workflow | ||
- Managing state that needs to be accessed by multiple components | ||
|
||
The remainder of this document will show you how to create and use both contexts and providers in GenSX. | ||
|
||
## Creating and using contexts | ||
|
||
This next section walks through the steps needed to create and use a context in your GenSX workflow. | ||
|
||
### Step 1: Create a context | ||
|
||
To create a context, start by defining its interface and then use `gsx.createContext<T>()` to initialize it along with a default value. For example, here's how to create a `User` context: | ||
|
||
```tsx | ||
import { gsx } from "gensx"; | ||
|
||
// Define the interface | ||
interface User { | ||
name: string; | ||
} | ||
|
||
// Create a context with a default value | ||
const UserContext = gsx.createContext<User>({ | ||
name: "", | ||
}); | ||
``` | ||
|
||
### Step 2: Use the context in a component | ||
|
||
To use the context, call the `gsx.useContext(context)` hook inside of a component. Here a `Greeting` component is created that uses the `UserContext` to get the user's name: | ||
|
||
```tsx | ||
const Greeting = gsx.Component<Record<never, never>, GreetingOutput>( | ||
"Greeting", | ||
() => { | ||
const user = gsx.useContext(UserContext); | ||
return `Hello, ${user.name}!`; | ||
}, | ||
); | ||
``` | ||
|
||
### Step 3: Provide the context value | ||
|
||
To make the context value available to your components, you need to wrap your component in a `Provider` component and pass in a value via the `value` prop: | ||
|
||
```tsx | ||
const result = await gsx.execute( | ||
<UserContext.Provider value={{ name: "John" }}> | ||
<Greeting /> | ||
</UserContext.Provider>, | ||
); | ||
``` | ||
|
||
## Using providers for configuration and dependencies | ||
|
||
Providers are a specialized way to use contexts that focus on managing configuration and dependencies for your workflow. They simplify the process of sharing data like API keys, client instances, or feature flags across your components. | ||
|
||
### Built-in providers | ||
|
||
The main provider available today is the `OpenAIProvider`, which manages your OpenAI API key and client: | ||
|
||
```tsx | ||
const result = await gsx.execute( | ||
<OpenAIProvider apiKey={process.env.OPENAI_API_KEY}> | ||
<ChatCompletion | ||
model="gpt-4" | ||
messages={[{ role: "user", content: "Hello!" }]} | ||
/> | ||
</OpenAIProvider>, | ||
); | ||
``` | ||
|
||
### Creating a Custom Provider | ||
|
||
If you need a provider that isn't available out of the box, you can easily create your own. The example below shows how to create a provider for the [Firecrawl](https://www.firecrawl.dev/) API. | ||
|
||
#### Step 1: Create a context | ||
|
||
Start by importing `gsx` and the package you want to use: | ||
|
||
```tsx | ||
import { gsx } from "gensx"; | ||
import FirecrawlApp, { FirecrawlAppConfig } from "@mendable/firecrawl-js"; | ||
``` | ||
|
||
Then, create the context: | ||
|
||
```tsx | ||
// Create a context | ||
export const FirecrawlContext = gsx.createContext<{ | ||
client?: FirecrawlApp; | ||
}>({}); | ||
``` | ||
|
||
The context contains the `client` that you'll use to interact with the Firecrawl API. | ||
|
||
#### Step 2: Create the provider | ||
|
||
Next, wrap your context in a provider component: | ||
|
||
```tsx | ||
// Create the provider | ||
export const FirecrawlProvider = gsx.Component<FirecrawlAppConfig, never>( | ||
"FirecrawlProvider", | ||
(args: FirecrawlAppConfig) => { | ||
const client = new FirecrawlApp({ | ||
apiKey: args.apiKey, | ||
}); | ||
return <FirecrawlContext.Provider value={{ client }} />; | ||
}, | ||
); | ||
``` | ||
|
||
The provider will take in the `apiKey` as a prop and use it to initialize the Firecrawl client. | ||
|
||
#### Step 3: Use the provider in a component | ||
|
||
Finally, you can build components that consume the context supplied by the provider: | ||
|
||
```tsx | ||
export const ScrapePage = gsx.Component<ScrapePageProps, string>( | ||
"ScrapePage", | ||
async ({ url }) => { | ||
const context = gsx.useContext(FirecrawlContext); | ||
|
||
if (!context.client) { | ||
throw new Error( | ||
"Firecrawl client not found. Please wrap your component with FirecrawlProvider.", | ||
); | ||
} | ||
const result = await context.client.scrapeUrl(url, { | ||
formats: ["markdown"], | ||
timeout: 30000, | ||
}); | ||
|
||
if (!result.success || !result.markdown) { | ||
throw new Error(`Failed to scrape url: ${url}`); | ||
} | ||
|
||
return result.markdown; | ||
}, | ||
); | ||
``` | ||
|
||
#### Step 4: Use the provider in your workflow | ||
|
||
Now when you use the `ScrapePage` component in your workflow, you'll wrap it in the `FirecrawlProvider` and pass in the `apiKey`: | ||
|
||
```tsx | ||
const markdown = await gsx.execute( | ||
<FirecrawlProvider apiKey={process.env.FIRECRAWL_API_KEY}> | ||
<ScrapePage url="https://gensx.com/overview/" /> | ||
</FirecrawlProvider>, | ||
); | ||
``` | ||
|
||
## Nesting Providers | ||
|
||
You can nest multiple providers to combine different services or configurations in your workflow. This is useful when a component needs access to multiple contexts. Here's an example that combines the OpenAI provider with our custom Firecrawl provider: | ||
|
||
```tsx | ||
const result = await gsx.execute( | ||
<OpenAIProvider apiKey={process.env.OPENAI_API_KEY}> | ||
<FirecrawlProvider apiKey={process.env.FIRECRAWL_API_KEY}> | ||
<WebPageSummarizer url="https://gensx.com/overview/" /> | ||
</FirecrawlProvider> | ||
</OpenAIProvider>, | ||
); | ||
``` | ||
|
||
In this example, the `WebPageSummarizer` component can access both the OpenAI client and Firecrawl client through their respective contexts. | ||
|
||
The order of nesting doesn't matter as long as the component using a context is wrapped by its corresponding provider somewhere up the tree. | ||
|
||
## Additional Resources | ||
|
||
You can find the full example code demonstrating these concepts on GitHub: | ||
|
||
- [Context examples](https://github.com/gensx-inc/gensx/tree/main/examples/contexts) | ||
- [Provider examples](https://github.com/gensx-inc/gensx/tree/main/examples/providers) |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Contexts Example | ||
|
||
This example shows how to create and use contexts in GenSX. For more details on contexts, see the [Contexts](https://gensx.dev/concepts/context) page. | ||
|
||
## Usage | ||
|
||
```bash | ||
# Install dependencies | ||
pnpm install | ||
|
||
# Run the example | ||
pnpm run start | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import rootConfig from "../../eslint.config.mjs"; | ||
|
||
export default [ | ||
...rootConfig, | ||
{ | ||
files: ["**/*.{js,mjs,cjs,jsx,ts,tsx}"], | ||
languageOptions: { | ||
parserOptions: { | ||
tsconfigRootDir: import.meta.dirname, | ||
project: "./tsconfig.json", | ||
}, | ||
}, | ||
rules: { | ||
"@typescript-eslint/no-unsafe-assignment": "off", | ||
"@typescript-eslint/no-unsafe-call": "off", | ||
"@typescript-eslint/no-unsafe-member-access": "off", | ||
"@typescript-eslint/no-unsafe-return": "off", | ||
"@typescript-eslint/no-unsafe-argument": "off", | ||
"node/no-unsupported-features/es-syntax": "off", | ||
}, | ||
}, | ||
]; |
Oops, something went wrong.