-
Notifications
You must be signed in to change notification settings - Fork 24
feat: Custom JSX runtime #20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 10 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
b2e954c
SAVEPOINT
jmoseley 5612543
Collect
jmoseley cd98491
SAVEPOINT
jmoseley 60d2954
Strings, everything is strings
jmoseley 69cc955
Use type annotations to pass around some type safety.
jmoseley 86d1607
Simplify the GenSX api.
jmoseley 8f9bcb1
Support jsx-dev-runtime.
jmoseley 1648656
Add exports for jsx-runtime.
jmoseley b1a6138
Clarify comment.
jmoseley 997481d
Remove label workflow.
jmoseley 0e77b10
Improve the examples.
jmoseley 1db4393
Support arrays of child elements returned from the child function wit…
jmoseley 87a7e97
Simplify the example to remove the endted fragment.
jmoseley de14ac8
Remove console.logs
jmoseley File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains hidden or 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 hidden or 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 |
---|---|---|
|
@@ -28,6 +28,7 @@ | |
"packageManager": "[email protected]", | ||
"type": "module", | ||
"scripts": { | ||
"build:watch": "pnpm build:clean && pnpm generate-dist --watch", | ||
"dev": "nodemon", | ||
"prepublishOnly": "pnpm i && pnpm build", | ||
"build": "pnpm validate-typescript && pnpm build:clean && pnpm generate-dist", | ||
|
@@ -38,22 +39,19 @@ | |
"lint:fix": "eslint --ignore-path .gitignore . --ext .js,.ts --fix", | ||
"lint:file": "eslint --ignore-path .gitignore", | ||
"validate-typescript": "tsc -p tsconfig.prod.json --noEmit", | ||
"generate-dist": "tsup src/index.ts --minify --tsconfig tsconfig.prod.json --dts --format cjs,esm --out-dir dist", | ||
"generate-dist": "tsup src/index.ts --minify --tsconfig tsconfig.prod.json --dts --format cjs,esm --out-dir dist --entry.jsx-runtime=src/jsx-runtime.ts --entry.jsx-dev-runtime=src/jsx-dev-runtime.ts --entry.index=src/index.ts", | ||
"build:clean": "rimraf dist; exit 0", | ||
"prepare": "[ -f .husky/install.mjs ] && node .husky/install.mjs || true" | ||
}, | ||
"dependencies": { | ||
"openai": "^4.76.0", | ||
"react": "^19.0.0", | ||
"react-dom": "^19.0.0" | ||
"openai": "^4.76.0" | ||
}, | ||
"devDependencies": { | ||
"@swc/cli": "^0.5.2", | ||
"@swc/core": "^1.10.1", | ||
"@types/fs-extra": "^11.0.4", | ||
"@types/node": "^22.10.2", | ||
"@types/react": "^19.0.1", | ||
"@types/react-dom": "^18.3.2", | ||
"@typescript-eslint/eslint-plugin": "^8.18.1", | ||
"@typescript-eslint/parser": "^8.18.1", | ||
"@vitest/coverage-istanbul": "^2.1.8", | ||
|
@@ -94,6 +92,14 @@ | |
"types": "./dist/index.d.cts", | ||
"default": "./dist/index.cjs" | ||
} | ||
}, | ||
"./jsx-runtime": { | ||
"import": "./dist/jsx-runtime.js", | ||
"require": "./dist/jsx-runtime.cjs" | ||
}, | ||
"./jsx-dev-runtime": { | ||
"import": "./dist/jsx-dev-runtime.js", | ||
"require": "./dist/jsx-dev-runtime.cjs" | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains hidden or 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 |
---|---|---|
@@ -1,85 +1,103 @@ | ||
import { Workflow } from "@/src/components/Workflow"; | ||
import { WorkflowContext } from "@/src/components/Workflow"; | ||
import { createWorkflowOutput } from "@/src/hooks/useWorkflowOutput"; | ||
import { Component, gensx } from "@/index"; | ||
|
||
import { BlogWritingWorkflow } from "./blog/BlogWritingWorkflow"; | ||
import { TweetWritingWorkflow } from "./tweet/TweetWritingWorkflow"; | ||
// Pure workflow steps | ||
const pureResearchBrainstorm = async ({ prompt }: { prompt: string }) => { | ||
console.log("🔍 Starting research for:", prompt); | ||
const topics = await Promise.resolve(["topic 1", "topic 2", "topic 3"]); | ||
return topics; | ||
}; | ||
|
||
async function runParallelWorkflow() { | ||
const title = "Programmatic Secrets with ESC"; | ||
const prompt = "Write an article..."; | ||
const pureWriter = async ({ | ||
research, | ||
prompt, | ||
}: { | ||
research: string; | ||
prompt: string; | ||
}): Promise<string> => { | ||
console.log("✍️ Writing draft based on research"); | ||
return await Promise.resolve(`**draft\n${research}\n${prompt}\n**end draft`); | ||
}; | ||
|
||
const [blogPost, setBlogPost] = createWorkflowOutput(""); | ||
const [tweet, setTweet] = createWorkflowOutput(""); | ||
const pureEditor = async ({ draft }: { draft: string }) => { | ||
console.log("✨ Polishing final draft"); | ||
return await Promise.resolve(`edited result: ${draft}`); | ||
}; | ||
|
||
const workflow = ( | ||
<Workflow> | ||
<BlogWritingWorkflow | ||
title={title} | ||
prompt={prompt} | ||
setOutput={setBlogPost} | ||
/> | ||
<TweetWritingWorkflow content={blogPost} setOutput={setTweet} /> | ||
</Workflow> | ||
); | ||
|
||
const context = new WorkflowContext(workflow); | ||
await context.execute(); | ||
// Wrapped workflow steps | ||
const LLMResearchBrainstorm = Component(pureResearchBrainstorm); | ||
const LLMResearch = Component(async ({ topic }: { topic: string }) => { | ||
console.log("📚 Researching topic:", topic); | ||
return await Promise.resolve(`research results for ${topic}`); | ||
}); | ||
const LLMWriter = Component(pureWriter); | ||
const LLMEditor = Component(pureEditor); | ||
const WebResearcher = Component(async ({ prompt }: { prompt: string }) => { | ||
console.log("🌐 Researching web for:", prompt); | ||
const results = await Promise.resolve([ | ||
"web result 1", | ||
"web result 2", | ||
"web result 3", | ||
]); | ||
return results; | ||
}); | ||
|
||
console.log("\n=== Parallel Workflow Results ==="); | ||
console.log("Blog Post:", await blogPost); | ||
console.log("Tweet:", await tweet); | ||
} | ||
// When building a workflow out of components, there are two options: | ||
// 1. Use the Component function to wrap it and specify the input and output types. This gets you a function with type safe inputs and outputs (if you just call it as a function). | ||
// 2. Don't wrap it in the Component function, and do not specify the output type (see BlogWritingWorkflow below). You get a function that is the same type as the JSX.Element signature, so it has an unknown output type. If you try to specify the output type on the function signature, you get a type error (unknown is not assignable to X). | ||
// If you choose not to wrap it in a Component, you can't pass children to it, but we could easily expose the types for that to enable it, similar to the React.PropsWithChildren type. | ||
const ParallelResearch = Component<{ prompt: string }, [string[], string[]]>( | ||
({ prompt }: { prompt: string }) => ( | ||
<> | ||
jmoseley marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<LLMResearchBrainstorm prompt={prompt}> | ||
{topics => ( | ||
<> | ||
{topics.map(topic => ( | ||
<LLMResearch topic={topic} /> | ||
))} | ||
</> | ||
)} | ||
</LLMResearchBrainstorm> | ||
<WebResearcher prompt={prompt} /> | ||
</> | ||
), | ||
); | ||
|
||
async function runNestedWorkflow() { | ||
const title = "Programmatic Secrets with ESC"; | ||
const prompt = "Write an article..."; | ||
const BlogWritingWorkflow = async ({ prompt }: { prompt: string }) => ( | ||
<ParallelResearch prompt={prompt}> | ||
{([catalogResearch, webResearch]) => { | ||
console.log("🧠 Research:", { catalogResearch, webResearch }); | ||
return ( | ||
<LLMWriter research={catalogResearch.join("\n")} prompt={prompt}> | ||
{draft => <LLMEditor draft={draft} />} | ||
</LLMWriter> | ||
); | ||
}} | ||
</ParallelResearch> | ||
); | ||
|
||
let blogPost = ""; | ||
let tweet = ""; | ||
async function main() { | ||
console.log("🚀 Starting blog writing workflow"); | ||
|
||
const workflow = ( | ||
<Workflow> | ||
<BlogWritingWorkflow | ||
title={ | ||
new Promise(resolve => { | ||
resolve(title); | ||
}) | ||
} | ||
prompt={prompt} | ||
> | ||
{blogPostResult => ( | ||
<TweetWritingWorkflow content={blogPostResult}> | ||
{tweetResult => { | ||
blogPost = blogPostResult; | ||
tweet = tweetResult; | ||
return null; | ||
}} | ||
</TweetWritingWorkflow> | ||
)} | ||
</BlogWritingWorkflow> | ||
</Workflow> | ||
// Use the gensx function to execute the workflow and annotate with the output type. | ||
const result = await gensx<string>( | ||
<BlogWritingWorkflow prompt="Write a blog post about the future of AI" />, | ||
); | ||
|
||
const context = new WorkflowContext(workflow); | ||
await context.execute(); | ||
// Or just call the workflow as a function, and cast to the output type. | ||
const result2 = (await ( | ||
<BlogWritingWorkflow prompt="Write a blog post about using GenSX to 10x your productivity." /> | ||
)) as string; | ||
|
||
console.log("\n=== Nested Workflow Results ==="); | ||
console.log("Blog Post:", blogPost); | ||
console.log("Tweet:", tweet); | ||
} | ||
// Still need to cast here, because we didn't use the Component helper to wrap the workflow. | ||
const result3 = (await BlogWritingWorkflow({ | ||
prompt: "Write a blog post about the future of AI", | ||
})) as string; | ||
|
||
async function main() { | ||
try { | ||
await runParallelWorkflow(); | ||
await runNestedWorkflow(); | ||
} catch (error) { | ||
console.error("Workflow execution failed:", error); | ||
process.exit(1); | ||
} | ||
// Don't need to cast here, because we used the Component helper to wrap the workflow. | ||
const researchResult = await ParallelResearch({ | ||
prompt: "Write a blog post about the future of AI", | ||
}); | ||
console.log("✅ Final result:", { result, result2, result3, researchResult }); | ||
} | ||
jmoseley marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
main().catch((error: unknown) => { | ||
console.error("Unhandled error:", error); | ||
process.exit(1); | ||
}); | ||
await main(); |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.