Skip to content

Commit

Permalink
feat: Custom JSX runtime (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
jmoseley authored Dec 20, 2024
1 parent 95eaa2b commit 14d417c
Show file tree
Hide file tree
Showing 26 changed files with 262 additions and 740 deletions.
13 changes: 0 additions & 13 deletions .github/workflows/conventional-label.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"source.fixAll.eslint": "always",
"source.removeUnusedImports": "always"
},
"cSpell.words": ["gensx"]
"cSpell.words": ["gensx", "jsxs"]
}
16 changes: 11 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -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"
}
}
}
27 changes: 0 additions & 27 deletions playground/blog/BlogWritingWorkflow.tsx

This file was deleted.

178 changes: 106 additions & 72 deletions playground/index.tsx
Original file line number Diff line number Diff line change
@@ -1,85 +1,119 @@
import { Workflow } from "@/src/components/Workflow";
import { WorkflowContext } from "@/src/components/Workflow";
import { createWorkflowOutput } from "@/src/hooks/useWorkflowOutput";
import * as gsx from "@/index";

import { BlogWritingWorkflow } from "./blog/BlogWritingWorkflow";
import { TweetWritingWorkflow } from "./tweet/TweetWritingWorkflow";

async function runParallelWorkflow() {
const title = "Programmatic Secrets with ESC";
const prompt = "Write an article...";

const [blogPost, setBlogPost] = createWorkflowOutput("");
const [tweet, setTweet] = createWorkflowOutput("");

const workflow = (
<Workflow>
<BlogWritingWorkflow
title={title}
prompt={prompt}
setOutput={setBlogPost}
/>
<TweetWritingWorkflow content={blogPost} setOutput={setTweet} />
</Workflow>
);

const context = new WorkflowContext(workflow);
await context.execute();
interface LLMResearchBrainstormProps {
prompt: string;
}
type LLMResearchBrainstormOutput = string[];
const LLMResearchBrainstorm = gsx.Component<
LLMResearchBrainstormProps,
LLMResearchBrainstormOutput
>(async ({ prompt }) => {
console.log("🔍 Starting research for:", prompt);
const topics = await Promise.resolve(["topic 1", "topic 2", "topic 3"]);
return topics;
});

console.log("\n=== Parallel Workflow Results ===");
console.log("Blog Post:", await blogPost);
console.log("Tweet:", await tweet);
interface LLMResearchProps {
topic: string;
}
type LLMResearchOutput = string;
const LLMResearch = gsx.Component<LLMResearchProps, LLMResearchOutput>(
async ({ topic }) => {
console.log("📚 Researching topic:", topic);
return await Promise.resolve(`research results for ${topic}`);
},
);

async function runNestedWorkflow() {
const title = "Programmatic Secrets with ESC";
const prompt = "Write an article...";
interface LLMWriterProps {
research: string;
prompt: string;
}
type LLMWriterOutput = string;
const LLMWriter = gsx.Component<LLMWriterProps, LLMWriterOutput>(
async ({ research, prompt }) => {
console.log("✍️ Writing draft based on research");
return await Promise.resolve(
`**draft\n${research}\n${prompt}\n**end draft`,
);
},
);

let blogPost = "";
let tweet = "";
interface LLMEditorProps {
draft: string;
}
type LLMEditorOutput = string;
const LLMEditor = gsx.Component<LLMEditorProps, LLMEditorOutput>(
async ({ draft }) => {
console.log("✨ Polishing final draft");
return await Promise.resolve(`edited result: ${draft}`);
},
);

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>
);
interface WebResearcherProps {
prompt: string;
}
type WebResearcherOutput = string[];
const WebResearcher = gsx.Component<WebResearcherProps, WebResearcherOutput>(
async ({ prompt }) => {
console.log("🌐 Researching web for:", prompt);
const results = await Promise.resolve([
"web result 1",
"web result 2",
"web result 3",
]);
return results;
},
);

const context = new WorkflowContext(workflow);
await context.execute();
type ParallelResearchOutput = [string[], string[]];
interface ParallelResearchComponentProps {
prompt: string;
}
const ParallelResearch = gsx.Component<
ParallelResearchComponentProps,
ParallelResearchOutput
>(({ prompt }) => (
<>
<LLMResearchBrainstorm prompt={prompt}>
{topics => topics.map(topic => <LLMResearch topic={topic} />)}
</LLMResearchBrainstorm>
<WebResearcher prompt={prompt} />
</>
));

console.log("\n=== Nested Workflow Results ===");
console.log("Blog Post:", blogPost);
console.log("Tweet:", tweet);
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 (
<LLMWriter
research={[catalogResearch.join("\n"), webResearch.join("\n")].join(
"\n\n",
)}
prompt={prompt}
>
{draft => <LLMEditor draft={draft} />}
</LLMWriter>
);
}}
</ParallelResearch>
));

async function main() {
try {
await runParallelWorkflow();
await runNestedWorkflow();
} catch (error) {
console.error("Workflow execution failed:", error);
process.exit(1);
}
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<string>(
<BlogWritingWorkflow prompt="Write a blog post about the future of AI" />,
);
console.log("✅ Final result:", { result });
}

main().catch((error: unknown) => {
console.error("Unhandled error:", error);
process.exit(1);
});
await main();
12 changes: 0 additions & 12 deletions playground/shared/components/LLMEditor.tsx

This file was deleted.

25 changes: 0 additions & 25 deletions playground/shared/components/LLMResearcher.tsx

This file was deleted.

32 changes: 0 additions & 32 deletions playground/shared/components/LLMWriter.tsx

This file was deleted.

21 changes: 0 additions & 21 deletions playground/tweet/TweetWritingWorkflow.tsx

This file was deleted.

Loading

0 comments on commit 14d417c

Please sign in to comment.