Spaces:
Runtime error
Runtime error
acecalisto3
commited on
Commit
•
e9674a6
1
Parent(s):
41d9202
Upload 20 files
Browse files- alpine.mts +220 -0
- createLlamaPrompt.mts +25 -0
- createSpace.mts +60 -0
- daisy.mts +7 -0
- dl.sh +38 -0
- docker.mts +59 -0
- generateFiles.mts +99 -0
- getGradioApp.mts +43 -0
- getReactApp.mts +73 -0
- getStreamlitApp.mts +41 -0
- getWebApp.mts +54 -0
- gradioDoc.mts +44 -0
- index.mts +126 -0
- isPythonOrGradioAppPrompt.mts +5 -0
- isReactAppPrompt.mts +9 -0
- isStreamlitAppPrompt.mts +4 -0
- parseTutorial.mts +12 -0
- streamlitDoc.mts +77 -0
- types.mts +4 -0
- typescript.mts +28 -0
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 |
+
}`
|