Skip to content

Commit 14d417c

Browse files
authored
feat: Custom JSX runtime (#20)
1 parent 95eaa2b commit 14d417c

26 files changed

+262
-740
lines changed

.github/workflows/conventional-label.yml

Lines changed: 0 additions & 13 deletions
This file was deleted.

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
"source.fixAll.eslint": "always",
44
"source.removeUnusedImports": "always"
55
},
6-
"cSpell.words": ["gensx"]
6+
"cSpell.words": ["gensx", "jsxs"]
77
}

package.json

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"packageManager": "[email protected]",
2929
"type": "module",
3030
"scripts": {
31+
"build:watch": "pnpm build:clean && pnpm generate-dist --watch",
3132
"dev": "nodemon",
3233
"prepublishOnly": "pnpm i && pnpm build",
3334
"build": "pnpm validate-typescript && pnpm build:clean && pnpm generate-dist",
@@ -38,22 +39,19 @@
3839
"lint:fix": "eslint --ignore-path .gitignore . --ext .js,.ts --fix",
3940
"lint:file": "eslint --ignore-path .gitignore",
4041
"validate-typescript": "tsc -p tsconfig.prod.json --noEmit",
41-
"generate-dist": "tsup src/index.ts --minify --tsconfig tsconfig.prod.json --dts --format cjs,esm --out-dir dist",
42+
"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",
4243
"build:clean": "rimraf dist; exit 0",
4344
"prepare": "[ -f .husky/install.mjs ] && node .husky/install.mjs || true"
4445
},
4546
"dependencies": {
46-
"openai": "^4.76.0",
47-
"react": "^19.0.0",
48-
"react-dom": "^19.0.0"
47+
"openai": "^4.76.0"
4948
},
5049
"devDependencies": {
5150
"@swc/cli": "^0.5.2",
5251
"@swc/core": "^1.10.1",
5352
"@types/fs-extra": "^11.0.4",
5453
"@types/node": "^22.10.2",
5554
"@types/react": "^19.0.1",
56-
"@types/react-dom": "^18.3.2",
5755
"@typescript-eslint/eslint-plugin": "^8.18.1",
5856
"@typescript-eslint/parser": "^8.18.1",
5957
"@vitest/coverage-istanbul": "^2.1.8",
@@ -94,6 +92,14 @@
9492
"types": "./dist/index.d.cts",
9593
"default": "./dist/index.cjs"
9694
}
95+
},
96+
"./jsx-runtime": {
97+
"import": "./dist/jsx-runtime.js",
98+
"require": "./dist/jsx-runtime.cjs"
99+
},
100+
"./jsx-dev-runtime": {
101+
"import": "./dist/jsx-dev-runtime.js",
102+
"require": "./dist/jsx-dev-runtime.cjs"
97103
}
98104
}
99105
}

playground/blog/BlogWritingWorkflow.tsx

Lines changed: 0 additions & 27 deletions
This file was deleted.

playground/index.tsx

Lines changed: 106 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,119 @@
1-
import { Workflow } from "@/src/components/Workflow";
2-
import { WorkflowContext } from "@/src/components/Workflow";
3-
import { createWorkflowOutput } from "@/src/hooks/useWorkflowOutput";
1+
import * as gsx from "@/index";
42

5-
import { BlogWritingWorkflow } from "./blog/BlogWritingWorkflow";
6-
import { TweetWritingWorkflow } from "./tweet/TweetWritingWorkflow";
7-
8-
async function runParallelWorkflow() {
9-
const title = "Programmatic Secrets with ESC";
10-
const prompt = "Write an article...";
11-
12-
const [blogPost, setBlogPost] = createWorkflowOutput("");
13-
const [tweet, setTweet] = createWorkflowOutput("");
14-
15-
const workflow = (
16-
<Workflow>
17-
<BlogWritingWorkflow
18-
title={title}
19-
prompt={prompt}
20-
setOutput={setBlogPost}
21-
/>
22-
<TweetWritingWorkflow content={blogPost} setOutput={setTweet} />
23-
</Workflow>
24-
);
25-
26-
const context = new WorkflowContext(workflow);
27-
await context.execute();
3+
interface LLMResearchBrainstormProps {
4+
prompt: string;
5+
}
6+
type LLMResearchBrainstormOutput = string[];
7+
const LLMResearchBrainstorm = gsx.Component<
8+
LLMResearchBrainstormProps,
9+
LLMResearchBrainstormOutput
10+
>(async ({ prompt }) => {
11+
console.log("🔍 Starting research for:", prompt);
12+
const topics = await Promise.resolve(["topic 1", "topic 2", "topic 3"]);
13+
return topics;
14+
});
2815

29-
console.log("\n=== Parallel Workflow Results ===");
30-
console.log("Blog Post:", await blogPost);
31-
console.log("Tweet:", await tweet);
16+
interface LLMResearchProps {
17+
topic: string;
3218
}
19+
type LLMResearchOutput = string;
20+
const LLMResearch = gsx.Component<LLMResearchProps, LLMResearchOutput>(
21+
async ({ topic }) => {
22+
console.log("📚 Researching topic:", topic);
23+
return await Promise.resolve(`research results for ${topic}`);
24+
},
25+
);
3326

34-
async function runNestedWorkflow() {
35-
const title = "Programmatic Secrets with ESC";
36-
const prompt = "Write an article...";
27+
interface LLMWriterProps {
28+
research: string;
29+
prompt: string;
30+
}
31+
type LLMWriterOutput = string;
32+
const LLMWriter = gsx.Component<LLMWriterProps, LLMWriterOutput>(
33+
async ({ research, prompt }) => {
34+
console.log("✍️ Writing draft based on research");
35+
return await Promise.resolve(
36+
`**draft\n${research}\n${prompt}\n**end draft`,
37+
);
38+
},
39+
);
3740

38-
let blogPost = "";
39-
let tweet = "";
41+
interface LLMEditorProps {
42+
draft: string;
43+
}
44+
type LLMEditorOutput = string;
45+
const LLMEditor = gsx.Component<LLMEditorProps, LLMEditorOutput>(
46+
async ({ draft }) => {
47+
console.log("✨ Polishing final draft");
48+
return await Promise.resolve(`edited result: ${draft}`);
49+
},
50+
);
4051

41-
const workflow = (
42-
<Workflow>
43-
<BlogWritingWorkflow
44-
title={
45-
new Promise(resolve => {
46-
resolve(title);
47-
})
48-
}
49-
prompt={prompt}
50-
>
51-
{blogPostResult => (
52-
<TweetWritingWorkflow content={blogPostResult}>
53-
{tweetResult => {
54-
blogPost = blogPostResult;
55-
tweet = tweetResult;
56-
return null;
57-
}}
58-
</TweetWritingWorkflow>
59-
)}
60-
</BlogWritingWorkflow>
61-
</Workflow>
62-
);
52+
interface WebResearcherProps {
53+
prompt: string;
54+
}
55+
type WebResearcherOutput = string[];
56+
const WebResearcher = gsx.Component<WebResearcherProps, WebResearcherOutput>(
57+
async ({ prompt }) => {
58+
console.log("🌐 Researching web for:", prompt);
59+
const results = await Promise.resolve([
60+
"web result 1",
61+
"web result 2",
62+
"web result 3",
63+
]);
64+
return results;
65+
},
66+
);
6367

64-
const context = new WorkflowContext(workflow);
65-
await context.execute();
68+
type ParallelResearchOutput = [string[], string[]];
69+
interface ParallelResearchComponentProps {
70+
prompt: string;
71+
}
72+
const ParallelResearch = gsx.Component<
73+
ParallelResearchComponentProps,
74+
ParallelResearchOutput
75+
>(({ prompt }) => (
76+
<>
77+
<LLMResearchBrainstorm prompt={prompt}>
78+
{topics => topics.map(topic => <LLMResearch topic={topic} />)}
79+
</LLMResearchBrainstorm>
80+
<WebResearcher prompt={prompt} />
81+
</>
82+
));
6683

67-
console.log("\n=== Nested Workflow Results ===");
68-
console.log("Blog Post:", blogPost);
69-
console.log("Tweet:", tweet);
84+
interface BlogWritingWorkflowProps {
85+
prompt: string;
7086
}
87+
type BlogWritingWorkflowOutput = string;
88+
const BlogWritingWorkflow = gsx.Component<
89+
BlogWritingWorkflowProps,
90+
BlogWritingWorkflowOutput
91+
>(async ({ prompt }) => (
92+
<ParallelResearch prompt={prompt}>
93+
{([catalogResearch, webResearch]) => {
94+
console.log("🧠 Research:", { catalogResearch, webResearch });
95+
return (
96+
<LLMWriter
97+
research={[catalogResearch.join("\n"), webResearch.join("\n")].join(
98+
"\n\n",
99+
)}
100+
prompt={prompt}
101+
>
102+
{draft => <LLMEditor draft={draft} />}
103+
</LLMWriter>
104+
);
105+
}}
106+
</ParallelResearch>
107+
));
71108

72109
async function main() {
73-
try {
74-
await runParallelWorkflow();
75-
await runNestedWorkflow();
76-
} catch (error) {
77-
console.error("Workflow execution failed:", error);
78-
process.exit(1);
79-
}
110+
console.log("🚀 Starting blog writing workflow");
111+
112+
// Use the gensx function to execute the workflow and annotate with the output type.
113+
const result = await gsx.execute<string>(
114+
<BlogWritingWorkflow prompt="Write a blog post about the future of AI" />,
115+
);
116+
console.log("✅ Final result:", { result });
80117
}
81118

82-
main().catch((error: unknown) => {
83-
console.error("Unhandled error:", error);
84-
process.exit(1);
85-
});
119+
await main();

playground/shared/components/LLMEditor.tsx

Lines changed: 0 additions & 12 deletions
This file was deleted.

playground/shared/components/LLMResearcher.tsx

Lines changed: 0 additions & 25 deletions
This file was deleted.

playground/shared/components/LLMWriter.tsx

Lines changed: 0 additions & 32 deletions
This file was deleted.

playground/tweet/TweetWritingWorkflow.tsx

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)