acecalisto3 commited on
Commit
e9674a6
1 Parent(s): 41d9202

Upload 20 files

Browse files
alpine.mts ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ interface FeatureAPI {
3
+ title: string
4
+ description: string
5
+ pattern: string
6
+ }
7
+
8
+ const getPromptFromFeatures = (feats: FeatureAPI[]) =>
9
+ feats.map(({ title, description, pattern }) => `## "${title}": ${description}.\nExample: \`${pattern}\``).join("\n")
10
+
11
+
12
+ export const attributes: FeatureAPI[] = [
13
+ {
14
+ title: "x-data",
15
+ description: "Declare a new Alpine component and its data for a block of HTML",
16
+ pattern:
17
+ `<div x-data="{ open: false }">
18
+ ...
19
+ </div>`
20
+ },
21
+ {
22
+ title: "x-bind",
23
+ description: "Dynamically set HTML attributes on an element",
24
+ pattern:
25
+ `<div x-bind:class="! open ? 'hidden' : ''">
26
+ ...
27
+ </div>`
28
+ },
29
+ {
30
+ title: "x-on",
31
+ description: "Listen for browser events on an element",
32
+ pattern:
33
+ `<button x-on:click="open = ! open">
34
+ Toggle
35
+ </button>`
36
+ },
37
+ {
38
+ title: "x-text",
39
+ description: "Set the text content of an element",
40
+ pattern:
41
+ `<div>
42
+ Copyright ©
43
+
44
+ <span x-text="new Date().getFullYear()"></span>
45
+ </div>`
46
+ },
47
+ {
48
+ title: "x-html",
49
+ description: "Set the inner HTML of an element",
50
+ pattern:
51
+ `<div x-html="(await axios.get('/some/html/partial')).data">
52
+ ...
53
+ </div>`
54
+ },
55
+ {
56
+ title: "x-model",
57
+ description: "Synchronize a piece of data with an input element",
58
+ pattern:
59
+ `<div x-data="{ search: '' }">
60
+ <input type="text" x-model="search">
61
+
62
+ Searching for: <span x-text="search"></span>
63
+ </div>`
64
+ },
65
+ {
66
+ title: "x-show",
67
+ description: "Toggle the visibility of an element",
68
+ pattern:
69
+ `<div x-show="open">
70
+ ...
71
+ </div>`
72
+ },
73
+ {
74
+ title: "x-transition",
75
+ description: "Transition an element in and out using CSS transitions",
76
+ pattern:
77
+ `<div x-show="open" x-transition>
78
+ ...
79
+ </div>`
80
+ },
81
+ {
82
+ title: "x-for",
83
+ description: "Repeat a block of HTML based on a data set",
84
+ pattern:
85
+ `<template x-for="post in posts">
86
+ <h2 x-text="post.title"></h2>
87
+ </template>`
88
+ },
89
+ {
90
+ title: "x-if",
91
+ description: "Conditionally add/remove a block of HTML from the page entirely",
92
+ pattern:
93
+ `<template x-if="open">
94
+ <div>...</div>
95
+ </template>`
96
+ },
97
+ {
98
+ title: "x-init",
99
+ description: "Run code when an element is initialized by Alpine",
100
+ pattern:
101
+ `<div x-init="date = new Date()"></div>`
102
+ },
103
+ {
104
+ title: "x-effect",
105
+ description: "Execute a script each time one of its dependencies change",
106
+ pattern:
107
+ `<div x-effect="console.log('Count is '+count)"></div>`
108
+ },
109
+ {
110
+ title: "x-ref",
111
+ description: "Reference elements directly by their specified keys using the $refs magic property",
112
+ pattern:
113
+ `<input type="text" x-ref="content">
114
+
115
+ <button x-on:click="navigator.clipboard.writeText($refs.content.value)">
116
+ Copy
117
+ </button>`
118
+ },
119
+ {
120
+ title: "x-cloak",
121
+ description: "Hide a block of HTML until after Alpine is finished initializing its contents",
122
+ pattern:
123
+ `<div x-cloak>
124
+ ...
125
+ </div>`
126
+ },
127
+ {
128
+ title: "x-ignore",
129
+ description: "Prevent a block of HTML from being initialized by Alpine",
130
+ pattern:
131
+ `<div x-ignore>
132
+ ...
133
+ </div>`
134
+ },
135
+ ]
136
+
137
+ export const attributesPrompt = getPromptFromFeatures(attributes)
138
+
139
+ export const properties: FeatureAPI[] = [
140
+ {
141
+ title: "$store",
142
+ description: "Access a global store registered using Alpine.store(...)",
143
+ pattern: `<h1 x-text="$store.site.title"></h1>`
144
+ },
145
+ {
146
+ title: "$el",
147
+ description: "Reference the current DOM element",
148
+ pattern:`<div x-init="new Pikaday($el)"></div>`
149
+ },
150
+ {
151
+ title: "$dispatch",
152
+ description: "Dispatch a custom browser event from the current element",
153
+ pattern:
154
+ `<div x-on:notify="...">
155
+ <button x-on:click="$dispatch('notify')">...</button>
156
+ </div>`
157
+ },
158
+ {
159
+ title: "$watch",
160
+ description: "Watch a piece of data and run the provided callback anytime it changes",
161
+ pattern:
162
+ `<div x-init="$watch('count', value => {
163
+ console.log('count is ' + value)
164
+ })">...</div>`
165
+ },
166
+ {
167
+ title: "$refs",
168
+ description: "Reference an element by key (specified using x-ref)",
169
+ pattern:
170
+ `<div x-init="$refs.button.remove()">
171
+ <button x-ref="button">Remove Me</button>
172
+ </div>`
173
+ },
174
+ {
175
+ title: "$nextTick",
176
+ description: "Wait until the next \"tick\" (browser paint) to run a bit of code",
177
+ pattern:
178
+ `<div
179
+ x-text="count"
180
+ x-text="$nextTick(() => {"
181
+ console.log('count is ' + $el.textContent)
182
+ })
183
+ >...</div>`
184
+ },
185
+ ]
186
+
187
+ export const propertiesPrompt = getPromptFromFeatures(properties)
188
+
189
+ export const methods: FeatureAPI[] = [
190
+ {
191
+ title: "Alpine.data",
192
+ description: "Reuse a data object and reference it using x-data",
193
+ pattern:
194
+ `<div x-data="dropdown">
195
+ ...
196
+ </div>`
197
+ },
198
+ {
199
+ title: "Alpine.store",
200
+ description: "Declare a piece of global, reactive, data that can be accessed from anywhere using $store",
201
+ pattern:
202
+ `<button @click="$store.notifications.notify('...')">
203
+ Notify
204
+ </button>
205
+
206
+ ...
207
+
208
+ Alpine.store('notifications', {
209
+ items: [],
210
+
211
+ notify(message) {
212
+ this.items.push(message)
213
+ }
214
+ })`
215
+ },
216
+ ]
217
+
218
+ export const methodsPrompt = getPromptFromFeatures(methods)
219
+
220
+ export const alpine = "# Alpine.js docs\n"+ attributesPrompt // + propertiesPrompt + methodsPrompt
createLlamaPrompt.mts ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // adapted from https://huggingface.co/TheBloke/Llama-2-13B-chat-GPTQ/discussions/5
2
+ export function createLlamaPrompt(messages: Array<{ role: string, content: string }>) {
3
+ const B_INST = "[INST]", E_INST = "[/INST]";
4
+ const B_SYS = "<<SYS>>\n", E_SYS = "\n<</SYS>>\n\n";
5
+ const BOS = "<s>", EOS = "</s>";
6
+ const DEFAULT_SYSTEM_PROMPT = "You are a helpful, respectful and honest assistant. Always answer as helpfully as possible, while being safe. Please ensure that your responses are socially unbiased and positive in nature. If a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. If you don't know the answer to a question, please don't share false information.";
7
+
8
+ if (messages[0].role != "system"){
9
+ messages = [
10
+ {role: "system", content: DEFAULT_SYSTEM_PROMPT}
11
+ ].concat(messages);
12
+ }
13
+ messages = [{role: messages[1].role, content: B_SYS + messages[0].content + E_SYS + messages[1].content}].concat(messages.slice(2));
14
+
15
+ let messages_list = messages.map((value, index, array) => {
16
+ if (index % 2 == 0 && index + 1 < array.length){
17
+ return `${BOS}${B_INST} ${array[index].content.trim()} ${E_INST} ${array[index+1].content.trim()} ${EOS}`
18
+ }
19
+ return '';
20
+ })
21
+
22
+ messages_list.push(`${BOS}${B_INST} ${messages[messages.length-1].content.trim()} ${E_INST}`)
23
+
24
+ return messages_list.join('');
25
+ }
createSpace.mts ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { v4 as uuidv4 } from "uuid"
2
+ import { createRepo, uploadFiles, whoAmI } from "@huggingface/hub"
3
+ import type { RepoDesignation, Credentials } from "@huggingface/hub"
4
+ import slugify from "slugify"
5
+
6
+ import { RepoFile } from "./types.mts"
7
+
8
+ export const createSpace = async (files: RepoFile[], token: string) => {
9
+
10
+ const credentials: Credentials = { accessToken: token }
11
+
12
+ const { name: username } = await whoAmI({ credentials })
13
+
14
+ let slug = ``
15
+ let title = ``
16
+ const readme = files.find(p => p.path === "README.md")
17
+ try {
18
+ const matches = readme.content.match(/title: ([^\n]+)\n/)
19
+ title = matches?.[1] || ""
20
+ slug = (slugify as any)(title) as string
21
+ if (!slug.length) {
22
+ throw new Error("sluggification failed")
23
+ }
24
+ } catch (err) {
25
+ slug = `sf-${uuidv4().slice(0, 3)}`
26
+ }
27
+
28
+ const repoName = `${username}/${slug}`
29
+
30
+ const repo: RepoDesignation = { type: "space", name: repoName }
31
+ console.log(`Creating space at ${repoName}${title ? ` (${title})` : ''}`)
32
+
33
+ await createRepo({
34
+ repo,
35
+ credentials,
36
+ license: "mit",
37
+ sdk:
38
+ files.some(file => file.path.includes("Dockerfile"))
39
+ ? "docker"
40
+ : files.some(file => file.path.includes("app.py"))
41
+ ? "streamlit"
42
+ : "static" // "streamlit" | "gradio" | "docker" | "static";
43
+ });
44
+
45
+ console.log("uploading files..")
46
+ await uploadFiles({
47
+ repo,
48
+ credentials,
49
+ files: files.map(file => ({
50
+ path: file.path,
51
+ content: new Blob([ file.content ])
52
+ })),
53
+ });
54
+
55
+ console.log("upload done!")
56
+
57
+ // TODO we should keep track of the repo and delete it after 30 min
58
+ // or delete it if we reached 20 repos
59
+ // await deleteRepo({ repo, credentials })
60
+ }
daisy.mts ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ export const daisy = `# DaisyUI docs
2
+ ## To create a nice layout, wrap each article in:
3
+ <article class="prose"></article>
4
+ ## Use appropriate CSS classes
5
+ <button class="btn ..">
6
+ <table class="table ..">
7
+ <footer class="footer ..">`
dl.sh ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Directory to save the downloaded files
4
+ destination_dir="/Users/a2014/src"
5
+
6
+ # URLs of the files to be downloaded
7
+ file_urls=(
8
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/alpine.mts?download=true"
9
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/createLlamaPrompt.mts?download=true"
10
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/createSpace.mts?download=true"
11
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/daisy.mts?download=true"
12
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/docker.mts?download=true"
13
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/generateFiles.mts?download=true"
14
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/getGradioApp.mts?download=true"
15
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/getReactApp.mts?download=true"
16
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/getStreamlitApp.mts?download=true"
17
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/getWebApp.mts?download=true"
18
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/gradioDoc.mts?download=true"
19
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/index.mts?download=true"
20
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/isPythonOrGradioAppPrompt.mts?download=true"
21
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/isReactAppPrompt.mts?download=true"
22
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/isStreamlitAppPrompt.mts?download=true"
23
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/parseTutorial.mts?download=true"
24
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/streamlitDoc.mts?download=true"
25
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/types.mts?download=true"
26
+ "https://huggingface.co/spaces/acecalisto3/ai-app-factory/resolve/main/src/typescript.mts?download=true"
27
+ )
28
+
29
+ # Create the destination directory if it doesn't exist
30
+ mkdir -p "$destination_dir"
31
+
32
+ # Download each file
33
+ for url in "${file_urls[@]}"; do
34
+ filename=$(basename "$url")
35
+ wget -P "$destination_dir" "$url"
36
+ done
37
+
38
+ echo "All files have been downloaded to $destination_dir"
docker.mts ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const dockerfile = `
2
+ FROM node:18-alpine AS base
3
+
4
+ # Install dependencies only when needed
5
+ FROM base AS deps
6
+ # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
7
+ RUN apk add --no-cache libc6-compat
8
+ WORKDIR /app
9
+
10
+ # Install dependencies based on the preferred package manager
11
+ COPY package.json package-lock.json* ./
12
+ RUN npm install
13
+
14
+ # Uncomment the following lines if you want to use a secret at buildtime,
15
+ # for example to access your private npm packages
16
+ # RUN --mount=type=secret,id=HF_EXAMPLE_SECRET,mode=0444,required=true \
17
+ # $(cat /run/secrets/HF_EXAMPLE_SECRET)
18
+
19
+ # Rebuild the source code only when needed
20
+ FROM base AS builder
21
+ WORKDIR /app
22
+ COPY --from=deps /app/node_modules ./node_modules
23
+ COPY . .
24
+
25
+ # Next.js collects completely anonymous telemetry data about general usage.
26
+ # Learn more here: https://nextjs.org/telemetry
27
+ # Uncomment the following line in case you want to disable telemetry during the build.
28
+ # ENV NEXT_TELEMETRY_DISABLED 1
29
+
30
+ RUN npm run build
31
+
32
+ # Production image, copy all the files and run next
33
+ FROM base AS runner
34
+ WORKDIR /app
35
+
36
+ ENV NODE_ENV production
37
+ # Uncomment the following line in case you want to disable telemetry during runtime.
38
+ # ENV NEXT_TELEMETRY_DISABLED 1
39
+
40
+ RUN addgroup --system --gid 1001 nodejs
41
+ RUN adduser --system --uid 1001 nextjs
42
+
43
+ COPY --from=builder /app/public ./public
44
+
45
+ # Automatically leverage output traces to reduce image size
46
+ # https://nextjs.org/docs/advanced-features/output-file-tracing
47
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
48
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
49
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/cache ./.next/cache
50
+ # COPY --from=builder --chown=nextjs:nodejs /app/.next/cache/fetch-cache ./.next/cache/fetch-cache
51
+
52
+ USER nextjs
53
+
54
+ EXPOSE 3000
55
+
56
+ ENV PORT 3000
57
+
58
+ CMD ["node", "server.js"]
59
+ `
generateFiles.mts ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { HfInference } from '@huggingface/inference'
2
+ import { RepoFile } from './types.mts'
3
+ import { createLlamaPrompt } from './createLlamaPrompt.mts'
4
+ import { parseTutorial } from './parseTutorial.mts'
5
+ import { getGradioApp } from './getGradioApp.mts'
6
+ import { getStreamlitApp } from './getStreamlitApp.mts'
7
+ import { getWebApp } from './getWebApp.mts'
8
+ import { getReactApp } from './getReactApp.mts'
9
+ import { isStreamlitAppPrompt } from './isStreamlitAppPrompt.mts'
10
+ import { isPythonOrGradioAppPrompt } from './isPythonOrGradioAppPrompt.mts'
11
+ import { isReactAppPrompt } from './isReactAppPrompt.mts'
12
+
13
+ export const generateFiles = async (
14
+ prompt: string,
15
+ token: string,
16
+ onProgress: (chunk: string) => boolean
17
+ ) => {
18
+ if (`${prompt}`.length < 2) {
19
+ throw new Error(`prompt too short, please enter at least ${prompt} characters`)
20
+ }
21
+
22
+ const { prefix, files, instructions } =
23
+ isStreamlitAppPrompt(prompt)
24
+ ? getStreamlitApp(prompt)
25
+ : isPythonOrGradioAppPrompt(prompt)
26
+ ? getGradioApp(prompt)
27
+ : isReactAppPrompt(prompt)
28
+ ? getReactApp(prompt)
29
+ : getWebApp(prompt)
30
+
31
+ const inputs = createLlamaPrompt(instructions) + "\nSure! Here are the source files:\n" + prefix
32
+
33
+ let isAbortedOrFailed = false
34
+
35
+ let tutorial = prefix
36
+
37
+ try {
38
+ const hf = new HfInference(token)
39
+
40
+ onProgress(prefix)
41
+
42
+ for await (const output of hf.textGenerationStream({
43
+ // model: "tiiuae/falcon-180B-chat",
44
+ model: "codellama/CodeLlama-34b-Instruct-hf",
45
+ inputs,
46
+ parameters: {
47
+ do_sample: true,
48
+
49
+ // for "codellama/CodeLlama-34b-Instruct-hf":
50
+ // `inputs` tokens + `max_new_tokens` must be <= 8192
51
+ // error: `inputs` must have less than 4096 tokens.
52
+
53
+ // for "tiiuae/falcon-180B-chat":
54
+ // `inputs` tokens + `max_new_tokens` must be <= 8192
55
+ // error: `inputs` must have less than 4096 tokens.
56
+ max_new_tokens: 4096,
57
+ return_full_text: false,
58
+ }
59
+ })) {
60
+
61
+ tutorial += output.token.text
62
+ process.stdout.write(output.token.text)
63
+ // res.write(output.token.text)
64
+ if (
65
+ tutorial.includes('<|end|>')
66
+ || tutorial.includes('</s>')
67
+ || tutorial.includes('[ENDINSTRUCTION]')
68
+ || tutorial.includes('[/TASK]')
69
+ || tutorial.includes('<|assistant|>')) {
70
+ tutorial = tutorial.replaceAll("</s>", "").replaceAll("<|end|>", "")
71
+ break
72
+ }
73
+ if (!onProgress(output.token.text)) {
74
+ console.log("aborting the LLM generation")
75
+ isAbortedOrFailed = true
76
+ break
77
+ }
78
+ }
79
+
80
+ } catch (e) {
81
+ isAbortedOrFailed = true
82
+ console.log("failed:")
83
+ console.log(e)
84
+ }
85
+
86
+ if (isAbortedOrFailed) {
87
+ console.log("the request was aborted, so we return an empty list")
88
+ return []
89
+ }
90
+
91
+ console.log("analyzing the generated instructions..")
92
+ const generatedFiles = parseTutorial(tutorial).map(({ filename, content }) => ({
93
+ path: `${filename || ""}`.trim().replaceAll(" ", ""),
94
+ content: `${content || ""}`
95
+ } as RepoFile))
96
+ .filter(res => res.path.length && res.content.length)
97
+
98
+ return [...generatedFiles, ...files]
99
+ }
getGradioApp.mts ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { gradioDoc } from "./gradioDoc.mts"
2
+
3
+ export function getGradioApp(prompt: string) {
4
+ const prefix = "# In app.py:\n```"
5
+
6
+ const instructions = [
7
+ {
8
+ role: "system",
9
+ content: [
10
+ `You are a Python developer, expert at crafting Gradio applications to deploy to Hugging Face.`,
11
+ `Here is an example of a minimal Gradio application:`,
12
+ gradioDoc
13
+ ].filter(item => item).join("\n")
14
+ },
15
+ {
16
+ role: "user",
17
+ content: `Please write, file by file, the source code for a Gradio project.
18
+
19
+ You are allowed to use (if necessary) the following Python modules:
20
+ - numpy
21
+ - gradio (version 3.39.0)
22
+ - matplotlib
23
+
24
+ Don't forget to write a README.md with the following header:
25
+ \`\`\`
26
+ ---
27
+ license: apache-2.0
28
+ title: <app name>
29
+ sdk: gradio
30
+ sdk_version: 3.39.0
31
+ app_file: app.py
32
+ emoji: 👀
33
+ colorFrom: green
34
+ colorTo: blue
35
+ ---
36
+ \`\`\`
37
+
38
+ The app is about: ${prompt}`,
39
+ }
40
+ ]
41
+
42
+ return { prefix, files: [], instructions }
43
+ }
getReactApp.mts ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { alpine } from "./alpine.mts"
2
+ import { daisy } from "./daisy.mts"
3
+ import { dockerfile } from "./docker.mts"
4
+ import { tsconfig } from "./typescript.mts"
5
+
6
+ export function getReactApp(prompt: string) {
7
+ const prefix = `# In src/pages/index.tsx:\n\`\`\``
8
+ const files = [
9
+ {
10
+ path: `Dockerfile`,
11
+ content: dockerfile,
12
+ },
13
+ {
14
+ path: "tsconfig.json",
15
+ content: tsconfig
16
+ }
17
+ ]
18
+ const instructions = [
19
+ {
20
+ role: "system",
21
+ content: [
22
+ `You are a TypeScript developer, expert at crafting NextJS and React applications, using TailwindCSS utility classes.
23
+ You usually use the following dependencies:
24
+
25
+ \`\`\`
26
+ "@types/node": "20.4.2",
27
+ "@types/react": "18.2.15",
28
+ "@types/react-dom": "18.2.7",
29
+ "react": "18.2.0",
30
+ "react-dom": "18.2.0",
31
+ "typescript": "5.1.6",
32
+ \`\`\`
33
+
34
+ `,
35
+ ].filter(item => item).join("\n")
36
+ },
37
+ {
38
+ role: "user",
39
+ content: `
40
+ You need to write the list of files for a new NextJS 12 application.
41
+ The app is about: ${prompt}.
42
+ Think step by step, you got this!
43
+ Please write, file by file, the source code for a Next 12 application.
44
+ The app should be buildable when we run this in command line:
45
+ \`\`\`
46
+ npm install
47
+ npm run start
48
+ \`\`\`
49
+
50
+ The project will be deployed to Hugging Face, so it must include a README.md with the following YAML header:
51
+ \`\`\`
52
+ ---
53
+ license: apache-2.0
54
+ title: <APPNAME>
55
+ sdk: docker
56
+ emoji: 👨‍💻
57
+ colorFrom: yellow
58
+ colorTo: green
59
+ ---
60
+ \`\`\`
61
+
62
+ Important rules:
63
+ - you need to leave: "sdk: docker" as-is, but replace: "<APPNAME>" with an actual name, please.
64
+ - Don't forget to write a valid package.json file!
65
+
66
+ Remember, the app is about: ${prompt}.
67
+
68
+ Remember: don't forget to define the README.md and the package.json file!`,
69
+ }
70
+ ]
71
+
72
+ return { prefix, files, instructions }
73
+ }
getStreamlitApp.mts ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { streamlitDoc } from "./streamlitDoc.mts";
2
+
3
+ export function getStreamlitApp(prompt: string) {
4
+ const prefix = "# In app.py:\n```"
5
+
6
+ const instructions = [
7
+ {
8
+ role: "system",
9
+ content: [
10
+ `You are a Python developer, expert at crafting Streamlit applications to deploy to Hugging Face.`,
11
+ `Here is an extract from the Streamlit documentation:`,
12
+ streamlitDoc
13
+ ].filter(item => item).join("\n")
14
+ },
15
+ {
16
+ role: "user",
17
+ content: `Please write, file by file, the source code for a Streamlit app.
18
+
19
+ Please limit yourself to the following Python modules:
20
+ - numpy
21
+ - streamlit
22
+ - matplotlib
23
+
24
+ Don't forget to write a README.md with the following header:
25
+ \`\`\`
26
+ ---
27
+ license: apache-2.0
28
+ title: <app name>
29
+ sdk: streamlit
30
+ emoji: 👀
31
+ colorFrom: green
32
+ colorTo: blue
33
+ ---
34
+ \`\`\`
35
+
36
+ The app is about: ${prompt}`,
37
+ }
38
+ ]
39
+
40
+ return { prefix, files: [], instructions }
41
+ }
getWebApp.mts ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { alpine } from "./alpine.mts"
2
+ import { daisy } from "./daisy.mts"
3
+
4
+ export function getWebApp(prompt: string) {
5
+ const prefix = `# In index.html:\n\`\`\`
6
+ <html><head><link href="https://cdn.jsdelivr.net/npm/daisyui@3.1.6/dist/full.css" rel="stylesheet" type="text/css" /><script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script><script src="https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio"></script><script defer src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.156.1/three.min.js"></script><script type="module" src="main.js"></script><title>`
7
+
8
+ const instructions = [
9
+ {
10
+ role: "system",
11
+ content: [
12
+ `You are a JavaScript developer, expert at crafting applications using AlpineJS, DaisyUI and Tailwind.`,
13
+ `Here is an extract from the AlpineJS documentation:`,
14
+ alpine,
15
+ `Here is an extract from the DaisyUI documentation:`,
16
+ daisy
17
+ ].filter(item => item).join("\n")
18
+ },
19
+ {
20
+ role: "user",
21
+ content: `Please write, file by file, the source code for a HTML JS app.
22
+
23
+ Here are some recommended librairies:
24
+ - AlpineJS (use "https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js")
25
+ - DaisyUI (use "https://cdn.jsdelivr.net/npm/daisyui@3.1.6/dist/full.css")
26
+ - Tailwind (use "https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio")
27
+ - Three.js (use "https://cdnjs.cloudflare.com/ajax/libs/three.js/0.156.1/three.min.js")
28
+
29
+ Those library will be globally exposed thanks to the <script> dependencies, so you do not need to write "import ... from ..".
30
+
31
+ Some remarks:
32
+ - DO NOT USE VUE.JS
33
+
34
+ Remember, you need to write the index.html but also the app.js and/or the style.css files!
35
+ DO NOT WRITE AN EXAMPLE! WRITE THE FULL CODE!!
36
+
37
+ Don't forget to write a README.md with the following header:
38
+ \`\`\`
39
+ ---
40
+ license: apache-2.0
41
+ title: <app name>
42
+ sdk: static
43
+ emoji: 👨‍💻
44
+ colorFrom: yellow
45
+ colorTo: green
46
+ ---
47
+ \`\`\`
48
+
49
+ The app is about: ${prompt}`,
50
+ }
51
+ ]
52
+
53
+ return { prefix, files: [], instructions }
54
+ }
gradioDoc.mts ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const gradioDoc = `
2
+ # Gradio Code Example
3
+
4
+ \`\`\`python
5
+ import gradio as gr
6
+
7
+ def tax_calculator(income, marital_status, assets):
8
+ tax_brackets = [(10, 0), (25, 8), (60, 12), (120, 20), (250, 30)]
9
+ total_deductible = sum(assets["Cost"])
10
+ taxable_income = income - total_deductible
11
+
12
+ total_tax = 0
13
+ for bracket, rate in tax_brackets:
14
+ if taxable_income > bracket:
15
+ total_tax += (taxable_income - bracket) * rate / 100
16
+
17
+ if marital_status == "Married":
18
+ total_tax *= 0.75
19
+ elif marital_status == "Divorced":
20
+ total_tax *= 0.8
21
+
22
+ return round(total_tax)
23
+
24
+ demo = gr.Interface(
25
+ tax_calculator,
26
+ [
27
+ "number",
28
+ gr.Radio(["Single", "Married", "Divorced"]),
29
+ gr.Dataframe(
30
+ headers=["Item", "Cost"],
31
+ datatype=["str", "number"],
32
+ label="Assets Purchased this Year",
33
+ ),
34
+ ],
35
+ "number",
36
+ examples=[
37
+ [10000, "Married", [["Suit", 5000], ["Laptop", 800], ["Car", 1800]]],
38
+ [80000, "Single", [["Suit", 800], ["Watch", 1800], ["Car", 800]]],
39
+ ],
40
+ )
41
+
42
+ demo.launch()
43
+ \`\`\`
44
+ `
index.mts ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import express from 'express'
2
+ import { createSpace } from './createSpace.mts'
3
+ import { generateFiles } from './generateFiles.mts'
4
+
5
+ const app = express()
6
+ const port = 7860
7
+
8
+ const minPromptSize = 16 // if you change this, you will need to also change in public/index.html
9
+ const timeoutInSec = 15 * 60
10
+
11
+ console.log('timeout set to 30 minutes')
12
+
13
+ app.use(express.static('public'))
14
+
15
+ const pending: {
16
+ total: number;
17
+ queue: string[];
18
+ } = {
19
+ total: 0,
20
+ queue: [],
21
+ }
22
+
23
+ const endRequest = (id: string, reason: string) => {
24
+ if (!id || !pending.queue.includes(id)) {
25
+ return
26
+ }
27
+
28
+ pending.queue = pending.queue.filter(i => i !== id)
29
+ console.log(`request ${id} ended (${reason})`)
30
+ }
31
+
32
+ app.get('/debug', (req, res) => {
33
+ res.write(JSON.stringify({
34
+ nbTotal: pending.total,
35
+ nbPending: pending.queue.length,
36
+ queue: pending.queue,
37
+ }))
38
+ res.end()
39
+ })
40
+
41
+ app.get('/app', async (req, res) => {
42
+ if (`${req.query.prompt}`.length < minPromptSize) {
43
+ res.write(`prompt too short, please enter at least ${minPromptSize} characters`)
44
+ res.end()
45
+ return
46
+ }
47
+
48
+ const token = `${req.query.token}`
49
+
50
+ if (!token.startsWith("hf_")) {
51
+ res.write(`the provided token seems to be invalid`)
52
+ res.end()
53
+ return
54
+ }
55
+
56
+ /*
57
+ res.write(`<!doctype html>
58
+ <script src="/markdown-to-html.js"></script>
59
+ <div id="formatted-markdown"></div>
60
+ <script>
61
+ setInterval(
62
+ function fn() {
63
+ try {
64
+ var input = document.getElementById("raw-markdown-stream")
65
+ var output = document.getElementById("formatted-markdown")
66
+ output.innerHTML = MarkdownToHtml.parse(input.innerHTML)
67
+ } catch (err) {
68
+ console.error(err)
69
+ }
70
+ },
71
+ 1000
72
+ )
73
+ </script>
74
+ <div id="raw-markdown-stream" style="display: none">
75
+ `)
76
+ */
77
+
78
+ const id = `${pending.total++}`
79
+ console.log(`new request ${id}`)
80
+
81
+ pending.queue.push(id)
82
+
83
+ req.on('close', function() {
84
+ endRequest(id, 'browser asked to end the connection')
85
+ })
86
+
87
+ setTimeout(() => {
88
+ endRequest(id, `timed out after ${timeoutInSec}s`)
89
+ }, timeoutInSec * 1000)
90
+
91
+ let nbAttempts = 3
92
+
93
+ let files = []
94
+
95
+ while (nbAttempts-- > 0) {
96
+ files = await generateFiles(
97
+ `${req.query.prompt || ""}`,
98
+ token,
99
+ (chunk: string) => {
100
+ res.write(chunk)
101
+
102
+ // return true here as long as our request is still valid
103
+ // but if the user disconnected, the id will be removed from the queue,
104
+ // and we will return false, indicating to generateFiles that we should abort
105
+ return pending.queue.includes(id)
106
+ })
107
+ if (files.length) {
108
+ console.log(`seems like we have ${files.length} files`)
109
+ break
110
+ }
111
+ }
112
+
113
+ if (files.length > 0) {
114
+ console.log("files:", JSON.stringify(files, null, 2))
115
+
116
+ await createSpace(files, token)
117
+ }
118
+
119
+ // res.write(JSON.stringify(files, null, 2))
120
+ // res.write(`</div>`)
121
+ res.end()
122
+ })
123
+
124
+
125
+ app.listen(port, () => { console.log(`Open http://localhost:${port}`) })
126
+
isPythonOrGradioAppPrompt.mts ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ export function isPythonOrGradioAppPrompt(prompt: string) {
2
+ const lowerCasePrompt = prompt.toLocaleLowerCase()
3
+ return lowerCasePrompt.includes("python")
4
+ || lowerCasePrompt.includes("gradio")
5
+ }
isReactAppPrompt.mts ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ export function isReactAppPrompt(prompt: string) {
2
+ const lowerCasePrompt = prompt.toLocaleLowerCase()
3
+ return lowerCasePrompt.includes("react")
4
+ || lowerCasePrompt.includes("create react app")
5
+ || lowerCasePrompt.includes("reactjs")
6
+ || lowerCasePrompt.includes("next app")
7
+ || lowerCasePrompt.includes("nextjs app")
8
+ || lowerCasePrompt.includes("nextjs")
9
+ }
isStreamlitAppPrompt.mts ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ export function isStreamlitAppPrompt(prompt: string) {
2
+ const lowerCasePrompt = prompt.toLocaleLowerCase()
3
+ return lowerCasePrompt.includes("streamlit")
4
+ }
parseTutorial.mts ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export function parseTutorial(text: string): Array<{ filename: string; content: string }> {
2
+ const result: { filename: string; content: string; }[] = [];
3
+ const regex = /#\s+(?:And finally,\s+)?in\s+(?:(?:the|your)\s+)?(.*)(?:, add the following code)?:\n```(?:\w+\n)?([\s\S]*?)```/gi;
4
+ let match: RegExpExecArray | null;
5
+ while ((match = regex.exec(text)) !== null) {
6
+ result.push({
7
+ filename: match[1],
8
+ content: match[2].trim(),
9
+ });
10
+ }
11
+ return result;
12
+ }
streamlitDoc.mts ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const streamlitDoc = `
2
+ # Streamlit Documentation
3
+ ## st.number_input(label, min_value=None, max_value=None, value=, step=None, format=None, key=None, help=None, on_change=None, args=None, kwargs=None, *, disabled=False, label_visibility="visible")
4
+ Display a numeric input widget.
5
+ Parameters
6
+ ----------
7
+ label : str
8
+ A short label explaining to the user what this input is for.
9
+ The label can optionally contain Markdown and supports the following
10
+ elements: Bold, Italics, Strikethroughs, Inline Code, Emojis, and Links.
11
+ This also supports:
12
+ * Emoji shortcodes, such as \`:+1:\` and \`:sunglasses:\`.
13
+ For a list of all supported codes,
14
+ see https://share.streamlit.io/streamlit/emoji-shortcodes.
15
+ * LaTeX expressions, by wrapping them in "$" or "$$" (the "$$"
16
+ must be on their own lines). Supported LaTeX functions are listed
17
+ at https://katex.org/docs/supported.html.
18
+ * Colored text, using the syntax \`:color[text to be colored]\`,
19
+ where \`color\` needs to be replaced with any of the following
20
+ supported colors: blue, green, orange, red, violet, gray/grey, rainbow.
21
+ Unsupported elements are unwrapped so only their children (text contents) render.
22
+ Display unsupported elements as literal characters by
23
+ backslash-escaping them. E.g. \`1\. Not an ordered list\`.
24
+ For accessibility reasons, you should never set an empty label (label="")
25
+ but hide it with label_visibility if needed. In the future, we may disallow
26
+ empty labels by raising an exception.
27
+ min_value : int, float, or None
28
+ The minimum permitted value.
29
+ If None, there will be no minimum.
30
+ max_value : int, float, or None
31
+ The maximum permitted value.
32
+ If None, there will be no maximum.
33
+ value : int, float, or None
34
+ The value of this widget when it first renders.
35
+ Defaults to min_value, or 0.0 if min_value is None
36
+ step : int, float, or None
37
+ The stepping interval.
38
+ Defaults to 1 if the value is an int, 0.01 otherwise.
39
+ If the value is not specified, the format parameter will be used.
40
+ format : str or None
41
+ A printf-style format string controlling how the interface should
42
+ display numbers. Output must be purely numeric. This does not impact
43
+ the return value. Valid formatters: %d %e %f %g %i %u
44
+ key : str or int
45
+ An optional string or integer to use as the unique key for the widget.
46
+ If this is omitted, a key will be generated for the widget
47
+ based on its content. Multiple widgets of the same type may
48
+ not share the same key.
49
+ help : str
50
+ An optional tooltip that gets displayed next to the input.
51
+ on_change : callable
52
+ An optional callback invoked when this number_input's value changes.
53
+ args : tuple
54
+ An optional tuple of args to pass to the callback.
55
+ kwargs : dict
56
+ An optional dict of kwargs to pass to the callback.
57
+ disabled : bool
58
+ An optional boolean, which disables the number input if set to
59
+ True. The default is False. This argument can only be supplied by
60
+ keyword.
61
+ label_visibility : "visible", "hidden", or "collapsed"
62
+ The visibility of the label. If "hidden", the label doesn't show but there
63
+ is still empty space for it above the widget (equivalent to label="").
64
+ If "collapsed", both the label and the space are removed. Default is
65
+ "visible". This argument can only be supplied by keyword.
66
+ Returns
67
+ -------
68
+ int or float
69
+ The current value of the numeric input widget. The return type
70
+ will match the data type of the value parameter.
71
+ Example
72
+ -------
73
+ >>> import streamlit as st
74
+ >>>
75
+ >>> number = st.number_input('Insert a number')
76
+ >>> st.write('The current number is ', number)
77
+ `
types.mts ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ export interface RepoFile {
2
+ path: string
3
+ content: string
4
+ }
typescript.mts ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const tsconfig = `{
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "forceConsistentCasingInFileNames": true,
9
+ "noEmit": true,
10
+ "esModuleInterop": true,
11
+ "module": "esnext",
12
+ "moduleResolution": "node",
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "jsx": "preserve",
16
+ "incremental": true,
17
+ "plugins": [
18
+ {
19
+ "name": "next"
20
+ }
21
+ ],
22
+ "paths": {
23
+ "@/*": ["./src/*"]
24
+ }
25
+ },
26
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
27
+ "exclude": ["node_modules"]
28
+ }`