Spaces:
Running
Running
Commit
·
de2d4cd
0
Parent(s):
migration in svelte
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .eslintignore +13 -0
- .eslintrc.cjs +31 -0
- .gitignore +12 -0
- .npmrc +1 -0
- .prettierignore +13 -0
- .prettierrc +8 -0
- .vscode/settings.json +3 -0
- Dockerfile +25 -0
- README.md +12 -0
- package-lock.json +0 -0
- package.json +42 -0
- postcss.config.js +6 -0
- src/app.d.ts +12 -0
- src/app.html +18 -0
- src/lib/components/CodePreview.svelte +33 -0
- src/lib/components/Input.svelte +59 -0
- src/lib/components/Loading.svelte +23 -0
- src/lib/components/ProTag.svelte +3 -0
- src/lib/components/Prompt.svelte +25 -0
- src/lib/components/SelectModel.svelte +73 -0
- src/lib/components/Toggle.svelte +41 -0
- src/lib/components/image-generation/Form.svelte +118 -0
- src/lib/components/image-generation/Response.svelte +55 -0
- src/lib/components/sidebar/Sidebar.svelte +93 -0
- src/lib/components/sidebar/links/Primary.svelte +13 -0
- src/lib/components/sidebar/links/Secondary.svelte +10 -0
- src/lib/components/snippets/Curl.svelte +52 -0
- src/lib/components/snippets/Javascript.svelte +56 -0
- src/lib/components/snippets/Python.svelte +70 -0
- src/lib/components/text-generation/Form.svelte +123 -0
- src/lib/components/text-generation/Preview.svelte +13 -0
- src/lib/components/text-generation/Response.svelte +66 -0
- src/lib/images/github.svg +16 -0
- src/lib/images/hf-logo.svg +24 -0
- src/lib/images/svelte-logo.svg +1 -0
- src/lib/images/svelte-welcome.png +0 -0
- src/lib/images/svelte-welcome.webp +0 -0
- src/lib/styles/tailwind.css +22 -0
- src/lib/utils/models.ts +23 -0
- src/lib/utils/type.ts +4 -0
- src/routes/+layout.svelte +12 -0
- src/routes/+page.svelte +59 -0
- src/routes/+page.ts +3 -0
- src/routes/Counter.svelte +102 -0
- src/routes/Header.svelte +129 -0
- src/routes/about/+page.svelte +26 -0
- src/routes/about/+page.ts +9 -0
- src/routes/api/image-generation/+server.ts +47 -0
- src/routes/api/text-generation/+server.ts +42 -0
- src/routes/image-generation/+page.svelte +63 -0
.eslintignore
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.DS_Store
|
2 |
+
node_modules
|
3 |
+
/build
|
4 |
+
/.svelte-kit
|
5 |
+
/package
|
6 |
+
.env
|
7 |
+
.env.*
|
8 |
+
!.env.example
|
9 |
+
|
10 |
+
# Ignore files for PNPM, NPM and YARN
|
11 |
+
pnpm-lock.yaml
|
12 |
+
package-lock.json
|
13 |
+
yarn.lock
|
.eslintrc.cjs
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/** @type { import("eslint").Linter.FlatConfig } */
|
2 |
+
module.exports = {
|
3 |
+
root: true,
|
4 |
+
extends: [
|
5 |
+
'eslint:recommended',
|
6 |
+
'plugin:@typescript-eslint/recommended',
|
7 |
+
'plugin:svelte/recommended',
|
8 |
+
'prettier'
|
9 |
+
],
|
10 |
+
parser: '@typescript-eslint/parser',
|
11 |
+
plugins: ['@typescript-eslint'],
|
12 |
+
parserOptions: {
|
13 |
+
sourceType: 'module',
|
14 |
+
ecmaVersion: 2020,
|
15 |
+
extraFileExtensions: ['.svelte']
|
16 |
+
},
|
17 |
+
env: {
|
18 |
+
browser: true,
|
19 |
+
es2017: true,
|
20 |
+
node: true
|
21 |
+
},
|
22 |
+
overrides: [
|
23 |
+
{
|
24 |
+
files: ['*.svelte'],
|
25 |
+
parser: 'svelte-eslint-parser',
|
26 |
+
parserOptions: {
|
27 |
+
parser: '@typescript-eslint/parser'
|
28 |
+
}
|
29 |
+
}
|
30 |
+
]
|
31 |
+
};
|
.gitignore
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.DS_Store
|
2 |
+
node_modules
|
3 |
+
/build
|
4 |
+
/.svelte-kit
|
5 |
+
/package
|
6 |
+
.env
|
7 |
+
.env.*
|
8 |
+
!.env.example
|
9 |
+
.vercel
|
10 |
+
.output
|
11 |
+
vite.config.js.timestamp-*
|
12 |
+
vite.config.ts.timestamp-*
|
.npmrc
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
engine-strict=true
|
.prettierignore
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.DS_Store
|
2 |
+
node_modules
|
3 |
+
/build
|
4 |
+
/.svelte-kit
|
5 |
+
/package
|
6 |
+
.env
|
7 |
+
.env.*
|
8 |
+
!.env.example
|
9 |
+
|
10 |
+
# Ignore files for PNPM, NPM and YARN
|
11 |
+
pnpm-lock.yaml
|
12 |
+
package-lock.json
|
13 |
+
yarn.lock
|
.prettierrc
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"useTabs": true,
|
3 |
+
"singleQuote": true,
|
4 |
+
"trailingComma": "none",
|
5 |
+
"printWidth": 100,
|
6 |
+
"plugins": ["prettier-plugin-svelte"],
|
7 |
+
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
8 |
+
}
|
.vscode/settings.json
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"typescript.tsdk": "node_modules/typescript/lib"
|
3 |
+
}
|
Dockerfile
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Dockerfile
|
2 |
+
|
3 |
+
# Use an official Node.js runtime as the base image
|
4 |
+
FROM node:18
|
5 |
+
|
6 |
+
# Set the working directory in the container
|
7 |
+
WORKDIR /usr/src/app
|
8 |
+
|
9 |
+
# Copy package.json and package-lock.json to the container
|
10 |
+
COPY package.json package-lock.json ./
|
11 |
+
|
12 |
+
# Install dependencies
|
13 |
+
RUN npm install
|
14 |
+
|
15 |
+
# Copy the rest of the application files to the container
|
16 |
+
COPY . .
|
17 |
+
|
18 |
+
# Build the Next.js application for production
|
19 |
+
RUN npm run build
|
20 |
+
|
21 |
+
# Expose the application port (assuming your app runs on port 3000)
|
22 |
+
EXPOSE 3000
|
23 |
+
|
24 |
+
# Start the application
|
25 |
+
CMD ["npm", "preview"]
|
README.md
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: — Inference Api —
|
3 |
+
emoji: 📟
|
4 |
+
colorFrom: green
|
5 |
+
colorTo: yellow
|
6 |
+
sdk: docker
|
7 |
+
app_port: 3000
|
8 |
+
pinned: false
|
9 |
+
license: mit
|
10 |
+
---
|
11 |
+
|
12 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
package-lock.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
package.json
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "inference-playground-svelte",
|
3 |
+
"version": "0.0.1",
|
4 |
+
"scripts": {
|
5 |
+
"dev": "vite dev",
|
6 |
+
"build": "vite build",
|
7 |
+
"preview": "vite preview",
|
8 |
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
9 |
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
10 |
+
"lint": "prettier --check . && eslint .",
|
11 |
+
"format": "prettier --write ."
|
12 |
+
},
|
13 |
+
"devDependencies": {
|
14 |
+
"@fontsource/fira-mono": "^4.5.10",
|
15 |
+
"@iconify/svelte": "^3.1.4",
|
16 |
+
"@neoconfetti/svelte": "^1.0.0",
|
17 |
+
"@sveltejs/adapter-auto": "^2.0.0",
|
18 |
+
"@sveltejs/kit": "^1.27.4",
|
19 |
+
"@types/cookie": "^0.5.1",
|
20 |
+
"@types/node": "^20.10.0",
|
21 |
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
22 |
+
"@typescript-eslint/parser": "^6.0.0",
|
23 |
+
"autoprefixer": "^10.4.16",
|
24 |
+
"eslint": "^8.28.0",
|
25 |
+
"eslint-config-prettier": "^9.0.0",
|
26 |
+
"eslint-plugin-svelte": "^2.30.0",
|
27 |
+
"postcss": "^8.4.31",
|
28 |
+
"prettier": "^3.0.0",
|
29 |
+
"prettier-plugin-svelte": "^3.0.0",
|
30 |
+
"svelte": "^4.2.7",
|
31 |
+
"svelte-check": "^3.6.0",
|
32 |
+
"svelte-highlight": "^7.4.2",
|
33 |
+
"tailwindcss": "^3.3.5",
|
34 |
+
"tslib": "^2.4.1",
|
35 |
+
"typescript": "^5.0.0",
|
36 |
+
"vite": "^4.4.2"
|
37 |
+
},
|
38 |
+
"type": "module",
|
39 |
+
"dependencies": {
|
40 |
+
"iconify-icon": "^1.0.8"
|
41 |
+
}
|
42 |
+
}
|
postcss.config.js
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export default {
|
2 |
+
plugins: {
|
3 |
+
tailwindcss: {},
|
4 |
+
autoprefixer: {},
|
5 |
+
},
|
6 |
+
}
|
src/app.d.ts
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// See https://kit.svelte.dev/docs/types#app
|
2 |
+
// for information about these interfaces
|
3 |
+
declare global {
|
4 |
+
namespace App {
|
5 |
+
// interface Error {}
|
6 |
+
// interface Locals {}
|
7 |
+
// interface PageData {}
|
8 |
+
// interface Platform {}
|
9 |
+
}
|
10 |
+
}
|
11 |
+
|
12 |
+
export {};
|
src/app.html
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="utf-8" />
|
5 |
+
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
7 |
+
%sveltekit.head%
|
8 |
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
9 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
10 |
+
<link
|
11 |
+
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@300;400;500&family=Source+Sans+3:wght@400;500;600;700;800;900&display=swap"
|
12 |
+
rel="stylesheet"
|
13 |
+
/>
|
14 |
+
</head>
|
15 |
+
<body data-sveltekit-preload-data="hover">
|
16 |
+
<div style="display: contents">%sveltekit.body%</div>
|
17 |
+
</body>
|
18 |
+
</html>
|
src/lib/components/CodePreview.svelte
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import Curl from "./snippets/Curl.svelte";
|
3 |
+
import Javascript from "./snippets/Javascript.svelte";
|
4 |
+
import Python from "./snippets/Python.svelte";
|
5 |
+
|
6 |
+
export let body: Record<string, any>;
|
7 |
+
export let endpoint: string;
|
8 |
+
|
9 |
+
let headers = {}
|
10 |
+
|
11 |
+
const handleCopyToClipboard = (e: string) => {}
|
12 |
+
</script>
|
13 |
+
|
14 |
+
<div class="p-6 font-code text-sm text-white flex flex-col gap-6 snippet">
|
15 |
+
<Curl
|
16 |
+
body={body}
|
17 |
+
headers={headers}
|
18 |
+
endpoint={endpoint}
|
19 |
+
onCopyToClipboard={handleCopyToClipboard}
|
20 |
+
/>
|
21 |
+
<Javascript
|
22 |
+
body={body}
|
23 |
+
headers={headers}
|
24 |
+
endpoint={endpoint}
|
25 |
+
onCopyToClipboard={handleCopyToClipboard}
|
26 |
+
/>
|
27 |
+
<Python
|
28 |
+
body={body}
|
29 |
+
headers={headers}
|
30 |
+
endpoint={endpoint}
|
31 |
+
onCopyToClipboard={handleCopyToClipboard}
|
32 |
+
/>
|
33 |
+
</div>
|
src/lib/components/Input.svelte
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import Icon from '@iconify/svelte';
|
3 |
+
|
4 |
+
export let tooltip: string | undefined = undefined;
|
5 |
+
export let label: string;
|
6 |
+
export let subLabel: string | undefined = undefined;
|
7 |
+
|
8 |
+
export let type: "text" | "number" = "text"
|
9 |
+
export let value: string | number;
|
10 |
+
export let min: number | undefined = undefined;
|
11 |
+
export let max: number | undefined = undefined;
|
12 |
+
let clazz: string | undefined = undefined;
|
13 |
+
export { clazz as class };
|
14 |
+
export let placeholder: string | undefined = undefined;
|
15 |
+
export let sanitize: undefined | ((value: string | number) => string | number) = undefined;
|
16 |
+
export let onChange: (value: string | number) => void;
|
17 |
+
|
18 |
+
const handleInputChange = (event: any) => {
|
19 |
+
const newValue = event.target.value;
|
20 |
+
if (type === "number" && /^[0-9]*$/.test(newValue)) {
|
21 |
+
return onChange(newValue)
|
22 |
+
}
|
23 |
+
|
24 |
+
return onChange(newValue)
|
25 |
+
};
|
26 |
+
|
27 |
+
const handleBlur = (event: any) => {
|
28 |
+
const newValue = event.target.value;
|
29 |
+
if (sanitize) return onChange(sanitize(newValue))
|
30 |
+
}
|
31 |
+
|
32 |
+
</script>
|
33 |
+
|
34 |
+
<div class={`w-full relative ${clazz}`}>
|
35 |
+
<div class="flex items-center justify-start gap-2 relative mb-2.5">
|
36 |
+
{#if tooltip}
|
37 |
+
<div class="group cursor-pointer">
|
38 |
+
<Icon icon="lucide:info" class="text-slate-500 group-hover:text-slate-300 text-xl" />
|
39 |
+
<div class="bg-slate-950/90 z-10 rounded-xl p-3 text-white absolute text-xs left-0 bottom-0 translate-y-[calc(100%+8px)] opacity-0 transition-all duration-200 group-hover:opacity-100 pointer-events-none group-hover:pointer-events-auto">
|
40 |
+
{tooltip}
|
41 |
+
</div>
|
42 |
+
</div>
|
43 |
+
{/if}
|
44 |
+
<p class="font-sans text-slate-400 font-regular text-sm">
|
45 |
+
{label}:
|
46 |
+
</p>
|
47 |
+
</div>
|
48 |
+
{#if subLabel} <p class="text-slate-500 text-xs mb-2.5">{subLabel}</p>{/if}
|
49 |
+
<input
|
50 |
+
type={type}
|
51 |
+
value={value}
|
52 |
+
min={min}
|
53 |
+
max={max}
|
54 |
+
placeholder={placeholder}
|
55 |
+
class="shadow-inner font-code border border-slate-800 bg-slate-900/60 focus:bg-slate-900/90 focus:border-slate-700/80 text-white rounded-lg text-sm px-4 py-3.5 w-full transition-all duration-200 leading-relaxed outline-none"
|
56 |
+
on:input={handleInputChange}
|
57 |
+
on:blur={handleBlur}
|
58 |
+
/>
|
59 |
+
</div>
|
src/lib/components/Loading.svelte
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div class="absolute left-0 top-0 w-full h-full flex items-center justify-center bg-slate-950 bg-opacity-50 flex-col backdrop-blur-sm z-10">
|
2 |
+
<svg
|
3 |
+
class="animate-spin -ml-1 mr-3 h-8 w-8 text-white"
|
4 |
+
xmlns="http://www.w3.org/2000/svg"
|
5 |
+
fill="none"
|
6 |
+
viewBox="0 0 24 24"
|
7 |
+
>
|
8 |
+
<circle
|
9 |
+
class="opacity-25"
|
10 |
+
cx="12"
|
11 |
+
cy="12"
|
12 |
+
r="10"
|
13 |
+
stroke="currentColor"
|
14 |
+
stroke-width="4"
|
15 |
+
></circle>
|
16 |
+
<path
|
17 |
+
class="opacity-75"
|
18 |
+
fill="currentColor"
|
19 |
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
20 |
+
></path>
|
21 |
+
</svg>
|
22 |
+
<slot />
|
23 |
+
</div>
|
src/lib/components/ProTag.svelte
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
<div class="inline-block -skew-x-12 bg-gradient-to-br text-sm font-bold shadow-lg from-pink-500 via-green-500 to-yellow-500 text-black shadow-green-500/20 rounded-lg px-2.5 py-0.5">
|
2 |
+
PRO
|
3 |
+
</div>
|
src/lib/components/Prompt.svelte
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
export let value: string;
|
3 |
+
export let placeholder: string = "Ask me anything...";
|
4 |
+
export let onChange: (v: string) => void
|
5 |
+
</script>
|
6 |
+
|
7 |
+
<div class="flex flex-col gap-2.5">
|
8 |
+
<label
|
9 |
+
for="prompt"
|
10 |
+
class="font-sans text-slate-400 font-regular text-sm"
|
11 |
+
>
|
12 |
+
Prompt:
|
13 |
+
</label>
|
14 |
+
<textarea
|
15 |
+
id="prompt"
|
16 |
+
{value}
|
17 |
+
{placeholder}
|
18 |
+
class="shadow-inner font-code border border-slate-800 bg-slate-900/60 focus:bg-slate-900/90 focus:border-slate-700/80 text-white rounded-lg text-sm p-4 w-full transition-all duration-200 h-44 resize-none leading-relaxed outline-none"
|
19 |
+
on:input={(e) => {
|
20 |
+
const target = e.target
|
21 |
+
// @ts-ignore
|
22 |
+
onChange(target.value)
|
23 |
+
}}
|
24 |
+
/>
|
25 |
+
</div>
|
src/lib/components/SelectModel.svelte
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import Icon from '@iconify/svelte';
|
3 |
+
import type { Model } from "$lib/utils/type";
|
4 |
+
|
5 |
+
let open = false;
|
6 |
+
|
7 |
+
export let items: Model[];
|
8 |
+
export let value: string;
|
9 |
+
export let onChange: (value: string) => void;
|
10 |
+
|
11 |
+
$: selectedModel = items.find(({ id }) => id === value)
|
12 |
+
|
13 |
+
const handleSelect = (id: string) => {
|
14 |
+
onChange(id)
|
15 |
+
open = false;
|
16 |
+
}
|
17 |
+
|
18 |
+
const handleOpen = () => open = !open
|
19 |
+
</script>
|
20 |
+
|
21 |
+
<div class="w-full flex flex-col gap-2.5">
|
22 |
+
<label
|
23 |
+
for="model"
|
24 |
+
class="font-sans text-slate-400 font-regular text-sm"
|
25 |
+
>
|
26 |
+
Model:
|
27 |
+
</label>
|
28 |
+
<div
|
29 |
+
id="model"
|
30 |
+
class={`cursor-pointer shadow-inner font-code border hover:bg-slate-900/90 hover:border-slate-700/80 text-white rounded-lg text-sm w-full transition-all duration-200 leading-relaxed outline-none relative ${open ? 'bg-slate-900/90 border-slate-700/80' : 'border-slate-800 bg-slate-900/60'}`}
|
31 |
+
>
|
32 |
+
<button class="w-full flex justify-between items-center px-4 py-3" role="searchbox" on:click={handleOpen}>
|
33 |
+
{#if selectedModel}
|
34 |
+
<div class="flex items-center justify-start gap-3">
|
35 |
+
<img
|
36 |
+
src={selectedModel.logo}
|
37 |
+
alt={selectedModel.id}
|
38 |
+
width={22}
|
39 |
+
height={22}
|
40 |
+
class="rounded-full"
|
41 |
+
/>
|
42 |
+
<p>{selectedModel.id}</p>
|
43 |
+
</div>
|
44 |
+
{:else}
|
45 |
+
<p class="opacity-70">Select a model</p>
|
46 |
+
{/if}
|
47 |
+
<Icon icon="iconamoon:arrow-right-2-light" class={`text-slate-400 w-5 h-5 transition-all duration-150 ${open && 'transform rotate-90'}`} />
|
48 |
+
</button>
|
49 |
+
<div
|
50 |
+
class="absolute bottom-0 left-0 w-full pt-4 translate-y-full transition-all duration-150"
|
51 |
+
class:pointer-events-none={!open}
|
52 |
+
class:opacity-0={!open}
|
53 |
+
>
|
54 |
+
<div class="bg-slate-900 border border-slate-700/80 text-white p-2 font-code rounded-lg">
|
55 |
+
{#each items as { id, logo}}
|
56 |
+
<button
|
57 |
+
class={`w-full flex items-center justify-start gap-3 p-2 cursor-pointer hover:bg-slate-800/60 rounded-lg ${selectedModel?.id === id && 'bg-slate-800/60'}`}
|
58 |
+
on:click={() => handleSelect(id)}
|
59 |
+
>
|
60 |
+
<img
|
61 |
+
src={logo}
|
62 |
+
alt={id}
|
63 |
+
width={22}
|
64 |
+
height={22}
|
65 |
+
class="rounded-full"
|
66 |
+
/>
|
67 |
+
<p>{id}</p>
|
68 |
+
</button>
|
69 |
+
{/each}
|
70 |
+
</div>
|
71 |
+
</div>
|
72 |
+
</div>
|
73 |
+
</div>
|
src/lib/components/Toggle.svelte
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import Icon from '@iconify/svelte';
|
3 |
+
|
4 |
+
export let label: string;
|
5 |
+
export let checked: boolean;
|
6 |
+
export let tooltip: string | undefined = undefined;
|
7 |
+
export let onChange: (c: boolean) => void;
|
8 |
+
</script>
|
9 |
+
|
10 |
+
<div class="w-full flex items-center justify-between gap-2.5 relative">
|
11 |
+
<div class="flex items-center justify-start gap-2">
|
12 |
+
{#if tooltip}
|
13 |
+
<div class="group cursor-pointer">
|
14 |
+
<Icon icon="lucide:info" class="text-slate-500 group-hover:text-slate-300 text-xl" />
|
15 |
+
<div class="bg-slate-950/90 z-10 min-w-[200px] rounded-xl p-3 text-white absolute text-xs left-0 bottom-0 translate-y-[calc(100%+8px)] opacity-0 transition-all duration-200 group-hover:opacity-100 pointer-events-none group-hover:pointer-events-auto leading-relaxed">
|
16 |
+
{tooltip}
|
17 |
+
</div>
|
18 |
+
</div>
|
19 |
+
{/if}
|
20 |
+
<p class="font-sans text-slate-400 font-regular text-sm">
|
21 |
+
{label}
|
22 |
+
</p>
|
23 |
+
</div>
|
24 |
+
<div class="w-[62px] h-[32px] rounded-md relative cursor-pointer grid grid-cols-2 border border-slate-800 bg-slate-900/60 focus:bg-slate-900/90 focus:border-slate-700/80 overflow-hidden z-[1]">
|
25 |
+
<div class={`bg-slate-800/70 h-full w-[31px] absolute top-0 left-0 transition-all duration-150 z-[-1] ${!checked && 'translate-x-[31px]'}`}/>
|
26 |
+
<button
|
27 |
+
class="flex items-center justify-center"
|
28 |
+
class:text-slate-600={!checked}
|
29 |
+
on:click={() => onChange(true)}
|
30 |
+
>
|
31 |
+
<Icon icon="ic:round-check" class={`w-4 h-4 ${checked && 'text-green-500'}`} />
|
32 |
+
</button>
|
33 |
+
<button
|
34 |
+
class="flex items-center justify-center"
|
35 |
+
class:text-slate-600={checked}
|
36 |
+
on:click={() => onChange(false)}
|
37 |
+
>
|
38 |
+
<Icon icon="ic:round-close" class={`w-4 h-4 ${!checked && 'text-red-500'}`} />
|
39 |
+
</button>
|
40 |
+
</div>
|
41 |
+
</div>
|
src/lib/components/image-generation/Form.svelte
ADDED
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import Prompt from "$lib/components/Prompt.svelte";
|
3 |
+
import SelectModel from "$lib/components/SelectModel.svelte";
|
4 |
+
import Input from "$lib/components/Input.svelte";
|
5 |
+
import Toggle from "$lib/components/Toggle.svelte";
|
6 |
+
import { IMAGE_GENERATIONS } from "$lib/utils/models";
|
7 |
+
|
8 |
+
export let form: Record<string, any>;
|
9 |
+
export let onForm: (form: Record<string, any>) => void;
|
10 |
+
</script>
|
11 |
+
|
12 |
+
<SelectModel value={form.model} items={IMAGE_GENERATIONS} onChange={(model) => onForm({ ...form, model })} />
|
13 |
+
<Prompt
|
14 |
+
value={form.inputs}
|
15 |
+
placeholder="A red apple on a table"
|
16 |
+
onChange={(inputs) => onForm({ ...form, inputs })}
|
17 |
+
/>
|
18 |
+
<div class="flex items-center justify-start gap-4 mt-3">
|
19 |
+
<p class="text-slate-500 uppercase font-medium text-sm">
|
20 |
+
Optional parameters
|
21 |
+
</p>
|
22 |
+
<div class="w-full flex-1 h-[1px] bg-slate-600" />
|
23 |
+
</div>
|
24 |
+
<div class="grid grid-cols-2 gap-x-10 gap-y-6">
|
25 |
+
<Input
|
26 |
+
label="Negative prompt"
|
27 |
+
type="text"
|
28 |
+
class="col-span-2"
|
29 |
+
tooltip="A text describing content that you want the model to steer away from."
|
30 |
+
value={form?.parameters?.negative_prompt}
|
31 |
+
onChange={(negative_prompt) =>
|
32 |
+
onForm({
|
33 |
+
...form,
|
34 |
+
parameters: { ...form.parameters, negative_prompt },
|
35 |
+
})
|
36 |
+
}
|
37 |
+
/>
|
38 |
+
<div class="flex flex-col gap-3">
|
39 |
+
<Input
|
40 |
+
label="Guidance scale"
|
41 |
+
type="number"
|
42 |
+
tooltip="How closely you want the model to match the prompt. Lower numbers are less accurate, very high numbers might decrease image quality or generate artifacts."
|
43 |
+
min={1}
|
44 |
+
max={100}
|
45 |
+
sanitize={(value) => {
|
46 |
+
const valueAsNumber = Number(value);
|
47 |
+
if (valueAsNumber > 1) return 1;
|
48 |
+
if (valueAsNumber < 0) return 0;
|
49 |
+
return valueAsNumber;
|
50 |
+
}}
|
51 |
+
value={form?.parameters?.guidance_scale}
|
52 |
+
onChange={(guidance_scale) =>
|
53 |
+
onForm({
|
54 |
+
...form,
|
55 |
+
parameters: { ...form.parameters, guidance_scale },
|
56 |
+
})
|
57 |
+
}
|
58 |
+
/>
|
59 |
+
<Input
|
60 |
+
label="Inference steps"
|
61 |
+
type="number"
|
62 |
+
tooltip="The number of denoising steps to run. Larger numbers may produce better quality but will be slower. Typical values are between 20 and 50 steps."
|
63 |
+
min={20}
|
64 |
+
max={50}
|
65 |
+
sanitize={(value) => {
|
66 |
+
const valueAsNumber = Number(value);
|
67 |
+
if (valueAsNumber > 1) return 1;
|
68 |
+
if (valueAsNumber < 0) return 0;
|
69 |
+
return valueAsNumber;
|
70 |
+
}}
|
71 |
+
value={form?.parameters?.num_inference_steps}
|
72 |
+
onChange={(num_inference_steps) =>
|
73 |
+
onForm({
|
74 |
+
...form,
|
75 |
+
parameters: { ...form.parameters, num_inference_steps },
|
76 |
+
})
|
77 |
+
}
|
78 |
+
/>
|
79 |
+
</div>
|
80 |
+
<div class="flex flex-col gap-3">
|
81 |
+
<Input
|
82 |
+
label="Width"
|
83 |
+
type="number"
|
84 |
+
tooltip="The desired image dimensions. SDXL works best for sizes between 768 and 1024."
|
85 |
+
sanitize={(value) => {
|
86 |
+
const valueAsNumber = Number(value);
|
87 |
+
if (valueAsNumber > 1) return 1;
|
88 |
+
if (valueAsNumber < 0) return 0;
|
89 |
+
return valueAsNumber;
|
90 |
+
}}
|
91 |
+
value={form?.parameters?.width}
|
92 |
+
onChange={(width) =>
|
93 |
+
onForm({
|
94 |
+
...form,
|
95 |
+
parameters: { ...form.parameters, width },
|
96 |
+
})
|
97 |
+
}
|
98 |
+
/>
|
99 |
+
<Input
|
100 |
+
label="Height"
|
101 |
+
type="number"
|
102 |
+
tooltip="The desired image dimensions. SDXL works best for sizes between 768 and 1024."
|
103 |
+
sanitize={(value) => {
|
104 |
+
const valueAsNumber = Number(value);
|
105 |
+
if (valueAsNumber > 1) return 1;
|
106 |
+
if (valueAsNumber < 0) return 0;
|
107 |
+
return valueAsNumber;
|
108 |
+
}}
|
109 |
+
value={form?.parameters?.height}
|
110 |
+
onChange={(height) =>
|
111 |
+
onForm({
|
112 |
+
...form,
|
113 |
+
parameters: { ...form.parameters, height },
|
114 |
+
})
|
115 |
+
}
|
116 |
+
/>
|
117 |
+
</div>
|
118 |
+
</div>
|
src/lib/components/image-generation/Response.svelte
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import Icon from '@iconify/svelte';
|
3 |
+
|
4 |
+
import Loading from "$lib/components/Loading.svelte"
|
5 |
+
import CodePreview from '$lib/components/CodePreview.svelte';
|
6 |
+
import Preview from '$lib/components/text-generation/Preview.svelte';
|
7 |
+
|
8 |
+
export let loading: boolean;
|
9 |
+
export let res: any;
|
10 |
+
export let form: Record<string, any>;
|
11 |
+
export let body: Record<string, any>;
|
12 |
+
export let endpoint: string;
|
13 |
+
|
14 |
+
let tab = 0
|
15 |
+
|
16 |
+
$: code = JSON.stringify(res ?? {}, null, 2)
|
17 |
+
|
18 |
+
let TABS = [
|
19 |
+
{
|
20 |
+
label: "Formatted preview",
|
21 |
+
icon: "material-symbols:preview",
|
22 |
+
},
|
23 |
+
{
|
24 |
+
label: "Code example",
|
25 |
+
icon: "carbon:code",
|
26 |
+
},
|
27 |
+
];
|
28 |
+
</script>
|
29 |
+
|
30 |
+
<div class="h-screen flex flex-col relative">
|
31 |
+
<div class="bg-slate-950 overflow-auto flex-1">
|
32 |
+
<div class="w-full uppercase text-zinc-400 text-sm font-semibold border-t border-slate-900 flex items-start sticky top-0 bg-slate-950">
|
33 |
+
{#each TABS as { label, icon }, idx }
|
34 |
+
<button
|
35 |
+
class={`flex items-center justify-start gap-2 px-5 border-r py-4 border-slate-900 bg-slate-900/40 cursor-pointer hover:bg-slate-900/60 transition-all duration-200 ${tab === idx ? '!bg-slate-950 hover:bg-slate-900/40' : 'border-b'}`}
|
36 |
+
on:click={() => tab = idx}
|
37 |
+
>
|
38 |
+
<Icon icon={icon} class="w-5 h-5" />
|
39 |
+
{label}
|
40 |
+
</button>
|
41 |
+
{/each}
|
42 |
+
<div class="flex flex-1 w-full pointer-events-none text-slate-950 border-b border-slate-900 h-[53px] bg-slate-900/40"></div>
|
43 |
+
</div>
|
44 |
+
{#if tab === 0}
|
45 |
+
<Preview body={body} res={res} />
|
46 |
+
{:else if tab === 1}
|
47 |
+
<CodePreview body={form} endpoint={endpoint} />
|
48 |
+
{/if}
|
49 |
+
</div>
|
50 |
+
{#if loading}
|
51 |
+
<Loading>
|
52 |
+
<p class="text-slate-400 text-lg mt-4">Processing...</p>
|
53 |
+
</Loading>
|
54 |
+
{/if}
|
55 |
+
</div>
|
src/lib/components/sidebar/Sidebar.svelte
ADDED
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script>
|
2 |
+
import Icon from '@iconify/svelte';
|
3 |
+
|
4 |
+
import logo from '$lib/images/hf-logo.svg';
|
5 |
+
import ProTag from '$lib/components/ProTag.svelte';
|
6 |
+
import PrimaryLink from '$lib/components/sidebar/links/Primary.svelte';
|
7 |
+
import SecondaryLink from '$lib/components/sidebar/links/Secondary.svelte';
|
8 |
+
|
9 |
+
const LINKS = [
|
10 |
+
{
|
11 |
+
path: "/text-generation",
|
12 |
+
label: "Text Generation",
|
13 |
+
description: "Generate text from a prompt, using a model of your choice.",
|
14 |
+
icon: "heroicons:chat-bubble-left-right-20-solid",
|
15 |
+
type: "green",
|
16 |
+
},
|
17 |
+
{
|
18 |
+
path: "/image-generation",
|
19 |
+
label: "Image Generation",
|
20 |
+
description: "Generate images from a prompt, using a model of your choice.",
|
21 |
+
icon: "ph:images-square-fill",
|
22 |
+
type: "pink",
|
23 |
+
},
|
24 |
+
];
|
25 |
+
|
26 |
+
const FEW_SHOTS = [
|
27 |
+
{
|
28 |
+
path: "/text-generation/translation",
|
29 |
+
label: "Translation",
|
30 |
+
icon: "bi:translate",
|
31 |
+
},
|
32 |
+
{
|
33 |
+
path: "/text-generation/chat",
|
34 |
+
label: "Chat",
|
35 |
+
icon: "ph:chat-dots-fill",
|
36 |
+
},
|
37 |
+
];
|
38 |
+
|
39 |
+
</script>
|
40 |
+
|
41 |
+
<aside class="hidden xl:block bg-slate-900 w-full max-w-[360px] h-screen border-r border-slate-800 text-white">
|
42 |
+
<header class="flex items-center justify-start p-8 gap-4">
|
43 |
+
<img src={logo} alt="Logo" width={34} height={34} />
|
44 |
+
<div class="flex items-center justify-start gap-2">
|
45 |
+
<p class="text-white font-bold text-xl">Inference API for</p>
|
46 |
+
<ProTag />
|
47 |
+
</div>
|
48 |
+
</header>
|
49 |
+
<nav class="grid grid-cols-1 gap-8 px-4">
|
50 |
+
<a
|
51 |
+
href="https://huggingface.co/pricing#pro"
|
52 |
+
target="_blank"
|
53 |
+
class="px-4"
|
54 |
+
>
|
55 |
+
<button class="group text-base font-bold uppercase tracking-wide bg-white text-slate-950 transition-all duration-200 px-6 py-3 w-full rounded-full relative">
|
56 |
+
Subscribe to Pro
|
57 |
+
</button>
|
58 |
+
</a>
|
59 |
+
<ul class="grid grid-cols-1 gap-4">
|
60 |
+
{#each LINKS as { path, description, label, type, icon }, i}
|
61 |
+
<PrimaryLink href={path} type={type}>
|
62 |
+
<div class="flex items-center justify-start gap-4">
|
63 |
+
<Icon icon={icon} class="w-6 h-6" />
|
64 |
+
<p class="font-semibold text-sm uppercase font-code">
|
65 |
+
{label}
|
66 |
+
</p>
|
67 |
+
</div>
|
68 |
+
<p class="text-sm mt-2 text-white/90">
|
69 |
+
{description ||
|
70 |
+
"Generate text from a prompt, using a model of your choice."}
|
71 |
+
</p>
|
72 |
+
</PrimaryLink>
|
73 |
+
{/each}
|
74 |
+
</ul>
|
75 |
+
<div class="grid grid-cols-1 gap-4">
|
76 |
+
<div class="flex items-center justify-start gap-4 mb-2">
|
77 |
+
<p class="text-slate-600 uppercase font-medium text-sm font-sans">
|
78 |
+
FEW SHOTS
|
79 |
+
</p>
|
80 |
+
<div class="w-full flex-1 h-[1px] bg-slate-800" />
|
81 |
+
</div>
|
82 |
+
{#each FEW_SHOTS as { path, label, icon }, i}
|
83 |
+
<SecondaryLink href={path}>
|
84 |
+
<Icon icon={icon} class="w-5 h-5" />
|
85 |
+
<p class="font-semibold text-sm uppercase font-code">
|
86 |
+
{label}
|
87 |
+
</p>
|
88 |
+
</SecondaryLink>
|
89 |
+
{/each}
|
90 |
+
</div>
|
91 |
+
</nav>
|
92 |
+
</aside>
|
93 |
+
|
src/lib/components/sidebar/links/Primary.svelte
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { page } from '$app/stores';
|
3 |
+
export let href: string;
|
4 |
+
export let type: string = "green"
|
5 |
+
|
6 |
+
$: active_class = $page.url.pathname === href ? type === 'green' ? 'bg-green-600 !text-green-500' : 'bg-pink-500 !text-pink-500' : 'bg-slate-700';
|
7 |
+
</script>
|
8 |
+
|
9 |
+
<a href={href} data-sveltekit-preload-data class="p-5 transition-all duration-200 bg-opacity-20 border-transparent rounded-xl {active_class}">
|
10 |
+
<li>
|
11 |
+
<slot />
|
12 |
+
</li>
|
13 |
+
</a>
|
src/lib/components/sidebar/links/Secondary.svelte
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { page } from '$app/stores';
|
3 |
+
export let href: string;
|
4 |
+
|
5 |
+
$: active_class = $page.url.pathname === href ? '!text-slate-800 !bg-white/90 !from-transparent' : 'hover:text-slate-200 hover:border-slate-800';
|
6 |
+
</script>
|
7 |
+
|
8 |
+
<a href={href} data-sveltekit-preload-data class="px-5 py-3.5 rounded-xl bg-gradient-to-r border from-slate-800/70 to-slate-900 border-slate-800/50 text-slate-400 flex items-center justify-start gap-4 cursor-pointer {active_class}">
|
9 |
+
<slot />
|
10 |
+
</a>
|
src/lib/components/snippets/Curl.svelte
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import Icon from '@iconify/svelte';
|
3 |
+
import Highlight from "svelte-highlight";
|
4 |
+
import powershell from "svelte-highlight/languages/powershell";
|
5 |
+
import { PUBLIC_INFERENCE_API_URL } from '$env/static/public'
|
6 |
+
|
7 |
+
|
8 |
+
export let body: Record<string, any>;
|
9 |
+
export let endpoint: string;
|
10 |
+
export let headers: Record<string, any>;
|
11 |
+
export let onCopyToClipboard: (e: string) => void;
|
12 |
+
|
13 |
+
let isCopied: boolean = false;
|
14 |
+
|
15 |
+
const generateCurlRequestFromEndpoint = (body: Record<string, any>) => {
|
16 |
+
const fullpath = `${PUBLIC_INFERENCE_API_URL}/models/${endpoint}`;
|
17 |
+
delete body.model
|
18 |
+
return `curl -X POST "${fullpath}" \\
|
19 |
+
-H ${JSON.stringify(headers)}
|
20 |
+
-d ${JSON.stringify(body)}
|
21 |
+
`;
|
22 |
+
};
|
23 |
+
|
24 |
+
const handleCopy = () => {
|
25 |
+
onCopyToClipboard(generateCurlRequestFromEndpoint(body));
|
26 |
+
isCopied = true
|
27 |
+
setTimeout(() => {
|
28 |
+
isCopied = false
|
29 |
+
}, 1000);
|
30 |
+
};
|
31 |
+
|
32 |
+
$: code = generateCurlRequestFromEndpoint(body)
|
33 |
+
</script>
|
34 |
+
|
35 |
+
<div class="bg-slate-900/50 rounded-xl overflow-hidden">
|
36 |
+
<header class="bg-slate-900 flex items-center justify-start px-5 py-4 uppercase gap-2 text-slate-300">
|
37 |
+
<Icon icon="icon-park-outline:code-brackets" class="text-xl" />
|
38 |
+
<p class="text-xs font-semibold">Curl</p>
|
39 |
+
</header>
|
40 |
+
<main class="px-6 py-6">
|
41 |
+
<Highlight language={powershell} {code} />
|
42 |
+
<button class="flex justify-end relative w-full" on:click={handleCopy}>
|
43 |
+
<Icon icon="solar:copy-bold-duotone" class="text-slate-500 cursor-pointer hover:text-slate-300 transition-all duration-75 w-5 h-5" />
|
44 |
+
<div
|
45 |
+
class="bg-indigo-500/60 text-slate-100 text-xs font-semibold absolute bottom-0 right-0 z-10 rounded-lg px-2 py-1 pointer-events-none -translate-y-full transition-all duration-200"
|
46 |
+
class:opacity-0={!isCopied}
|
47 |
+
>
|
48 |
+
Copied!
|
49 |
+
</div>
|
50 |
+
</button>
|
51 |
+
</main>
|
52 |
+
</div>
|
src/lib/components/snippets/Javascript.svelte
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import Icon from '@iconify/svelte';
|
3 |
+
import Highlight from "svelte-highlight";
|
4 |
+
import javascript from "svelte-highlight/languages/javascript";
|
5 |
+
import { PUBLIC_INFERENCE_API_URL } from '$env/static/public'
|
6 |
+
|
7 |
+
export let body: Record<string, any>;
|
8 |
+
export let endpoint: string;
|
9 |
+
export let headers: Record<string, any>;
|
10 |
+
export let onCopyToClipboard: (e: string) => void;
|
11 |
+
|
12 |
+
let isCopied: boolean = false;
|
13 |
+
|
14 |
+
const generateCurlRequestFromEndpoint = (body: Record<string, any>) => {
|
15 |
+
const fullpath = `${PUBLIC_INFERENCE_API_URL}/models/${endpoint}`;
|
16 |
+
delete body.model
|
17 |
+
|
18 |
+
return `const response = await fetch(
|
19 |
+
"${fullpath}",
|
20 |
+
{
|
21 |
+
method: "POST",
|
22 |
+
headers: ${JSON.stringify(headers)},
|
23 |
+
body: ${JSON.stringify(body)}
|
24 |
+
}
|
25 |
+
)`;
|
26 |
+
};
|
27 |
+
|
28 |
+
const handleCopy = () => {
|
29 |
+
onCopyToClipboard(generateCurlRequestFromEndpoint(body));
|
30 |
+
isCopied = true
|
31 |
+
setTimeout(() => {
|
32 |
+
isCopied = false
|
33 |
+
}, 1000);
|
34 |
+
};
|
35 |
+
|
36 |
+
$: code = generateCurlRequestFromEndpoint(body)
|
37 |
+
</script>
|
38 |
+
|
39 |
+
<div class="bg-slate-900/50 rounded-xl overflow-hidden">
|
40 |
+
<header class="bg-slate-900 flex items-center justify-start px-5 py-4 uppercase gap-2 text-yellow-500">
|
41 |
+
<Icon icon="ri:javascript-fill" class="text-xl" />
|
42 |
+
<p class="text-xs font-semibold">Javascript</p>
|
43 |
+
</header>
|
44 |
+
<main class="px-6 py-6">
|
45 |
+
<Highlight language={javascript} {code} />
|
46 |
+
<button class="flex justify-end relative w-full" on:click={handleCopy}>
|
47 |
+
<Icon icon="solar:copy-bold-duotone" class="text-slate-500 cursor-pointer hover:text-slate-300 transition-all duration-75 w-5 h-5" />
|
48 |
+
<div
|
49 |
+
class="bg-indigo-500/60 text-slate-100 text-xs font-semibold absolute bottom-0 right-0 z-10 rounded-lg px-2 py-1 pointer-events-none -translate-y-full transition-all duration-200"
|
50 |
+
class:opacity-0={!isCopied}
|
51 |
+
>
|
52 |
+
Copied!
|
53 |
+
</div>
|
54 |
+
</button>
|
55 |
+
</main>
|
56 |
+
</div>
|
src/lib/components/snippets/Python.svelte
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import Icon from '@iconify/svelte';
|
3 |
+
import Highlight from "svelte-highlight";
|
4 |
+
import javascript from "svelte-highlight/languages/javascript";
|
5 |
+
import { PUBLIC_INFERENCE_API_URL } from '$env/static/public'
|
6 |
+
|
7 |
+
export let body: Record<string, any>;
|
8 |
+
export let endpoint: string;
|
9 |
+
export let headers: Record<string, any>;
|
10 |
+
export let onCopyToClipboard: (e: string) => void;
|
11 |
+
|
12 |
+
let isCopied: boolean = false;
|
13 |
+
|
14 |
+
const generateCurlRequestFromEndpoint = (body: Record<string, any>) => {
|
15 |
+
const fullpath = `${PUBLIC_INFERENCE_API_URL}/models/${endpoint}`;
|
16 |
+
delete body.model
|
17 |
+
|
18 |
+
const removeEmptyValues = (data: Record<string, any>) => {
|
19 |
+
const formattedData = { ...data };
|
20 |
+
Object.entries(formattedData).forEach(([key, value]) => {
|
21 |
+
if (typeof value === "object") {
|
22 |
+
const new_value = removeEmptyValues(value);
|
23 |
+
formattedData[key] = new_value
|
24 |
+
} else {
|
25 |
+
if (typeof value === "boolean") {
|
26 |
+
formattedData[key] = value ? "True" : "False";
|
27 |
+
}
|
28 |
+
}
|
29 |
+
});
|
30 |
+
return formattedData;
|
31 |
+
};
|
32 |
+
|
33 |
+
const formattedBody = removeEmptyValues(body ?? {});
|
34 |
+
return `import requests
|
35 |
+
response = requests.post(
|
36 |
+
"${fullpath}",
|
37 |
+
json=${JSON.stringify(formattedBody)},
|
38 |
+
headers=${JSON.stringify(headers)}
|
39 |
+
)`;
|
40 |
+
};
|
41 |
+
|
42 |
+
const handleCopy = () => {
|
43 |
+
onCopyToClipboard(generateCurlRequestFromEndpoint(body));
|
44 |
+
isCopied = true
|
45 |
+
setTimeout(() => {
|
46 |
+
isCopied = false
|
47 |
+
}, 1000);
|
48 |
+
};
|
49 |
+
|
50 |
+
$: code = generateCurlRequestFromEndpoint(body)
|
51 |
+
</script>
|
52 |
+
|
53 |
+
<div class="bg-slate-900/50 rounded-xl overflow-hidden">
|
54 |
+
<header class="bg-slate-900 flex items-center justify-start px-5 py-4 uppercase gap-2 text-blue-500">
|
55 |
+
<Icon icon="logos:python" class="text-xl" />
|
56 |
+
<p class="text-xs font-semibold">Python</p>
|
57 |
+
</header>
|
58 |
+
<main class="px-6 py-6">
|
59 |
+
<Highlight language={javascript} {code} />
|
60 |
+
<button class="flex justify-end relative w-full" on:click={handleCopy}>
|
61 |
+
<Icon icon="solar:copy-bold-duotone" class="text-slate-500 cursor-pointer hover:text-slate-300 transition-all duration-75 w-5 h-5" />
|
62 |
+
<div
|
63 |
+
class="bg-indigo-500/60 text-slate-100 text-xs font-semibold absolute bottom-0 right-0 z-10 rounded-lg px-2 py-1 pointer-events-none -translate-y-full transition-all duration-200"
|
64 |
+
class:opacity-0={!isCopied}
|
65 |
+
>
|
66 |
+
Copied!
|
67 |
+
</div>
|
68 |
+
</button>
|
69 |
+
</main>
|
70 |
+
</div>
|
src/lib/components/text-generation/Form.svelte
ADDED
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import Prompt from "$lib/components/Prompt.svelte";
|
3 |
+
import SelectModel from "$lib/components/SelectModel.svelte";
|
4 |
+
import Input from "$lib/components/Input.svelte";
|
5 |
+
import Toggle from "$lib/components/Toggle.svelte";
|
6 |
+
import { TEXT_GENERATIONS } from "$lib/utils/models";
|
7 |
+
|
8 |
+
export let loading: boolean;
|
9 |
+
export let form: Record<string, any>;
|
10 |
+
export let onForm: (form: Record<string, any>) => void;
|
11 |
+
</script>
|
12 |
+
|
13 |
+
<SelectModel value={form.model} items={TEXT_GENERATIONS} onChange={(model) => onForm({ ...form, model })} />
|
14 |
+
<Prompt
|
15 |
+
value={form.inputs}
|
16 |
+
onChange={(inputs) => onForm({ ...form, inputs })}
|
17 |
+
/>
|
18 |
+
<div class="flex items-center justify-start gap-4 mt-3">
|
19 |
+
<p class="text-slate-500 uppercase font-medium text-sm">
|
20 |
+
Optional parameters
|
21 |
+
</p>
|
22 |
+
<div class="w-full flex-1 h-[1px] bg-slate-600" />
|
23 |
+
</div>
|
24 |
+
<div class="grid grid-cols-2 gap-x-10 gap-y-6">
|
25 |
+
<div class="flex flex-col gap-3">
|
26 |
+
<Toggle
|
27 |
+
label="Do Sample"
|
28 |
+
tooltip="If set to False (the default), the generation method will be greedy search, which selects the most probable continuation sequence after the prompt you provide. Greedy search is deterministic, so the same results will always be returned from the same input. When do_sample is True, tokens will be sampled from a probability distribution and will therefore vary across invocations."
|
29 |
+
checked={form?.parameters?.do_sample}
|
30 |
+
onChange={(do_sample) =>
|
31 |
+
onForm({
|
32 |
+
...form,
|
33 |
+
parameters: { ...form.parameters, do_sample },
|
34 |
+
})
|
35 |
+
}
|
36 |
+
/>
|
37 |
+
<Input
|
38 |
+
label="Max new tokens"
|
39 |
+
type="number"
|
40 |
+
tooltip="Whether to include the input sequence in the output returned by the endpoint. The default used by InferenceClient is False, but the endpoint itself uses True by default."
|
41 |
+
min={1}
|
42 |
+
max={244}
|
43 |
+
sanitize={(value) => {
|
44 |
+
const valueAsNumber = Number(value);
|
45 |
+
if (valueAsNumber < 1) return 1;
|
46 |
+
if (valueAsNumber > 244) return 244;
|
47 |
+
return valueAsNumber;
|
48 |
+
}}
|
49 |
+
value={form?.parameters?.max_new_tokens}
|
50 |
+
onChange={(max_new_tokens) =>
|
51 |
+
onForm({
|
52 |
+
...form,
|
53 |
+
parameters: { ...form.parameters, max_new_tokens },
|
54 |
+
})
|
55 |
+
}
|
56 |
+
/>
|
57 |
+
<Input
|
58 |
+
label="Temperature"
|
59 |
+
type="number"
|
60 |
+
tooltip="Controls the amount of variation we desire from the generation. A temperature of 0 is equivalent to greedy search. If we set a value for temperature, then do_sample will automatically be enabled. The same thing happens for top_k and top_p. When doing code-related tasks, we want less variability and hence recommend a low temperature. For other tasks, such as open-ended text generation, we recommend a higher one."
|
61 |
+
min={0}
|
62 |
+
max={1}
|
63 |
+
sanitize={(value) => {
|
64 |
+
const valueAsNumber = Number(value);
|
65 |
+
if (valueAsNumber > 1) return 1;
|
66 |
+
if (valueAsNumber < 0) return 0;
|
67 |
+
return valueAsNumber;
|
68 |
+
}}
|
69 |
+
value={form?.parameters?.temperature}
|
70 |
+
onChange={(temperature) =>
|
71 |
+
onForm({
|
72 |
+
...form,
|
73 |
+
parameters: { ...form.parameters, temperature },
|
74 |
+
})
|
75 |
+
}
|
76 |
+
/>
|
77 |
+
</div>
|
78 |
+
<div class="flex flex-col gap-3">
|
79 |
+
<Toggle
|
80 |
+
label="Return full Text"
|
81 |
+
tooltip="Whether to include the input sequence in the output returned by the endpoint. The default used by InferenceClient is False, but the endpoint itself uses True by default."
|
82 |
+
checked={form?.parameters?.return_full_text}
|
83 |
+
onChange={(return_full_text) =>
|
84 |
+
onForm({
|
85 |
+
...form,
|
86 |
+
parameters: { ...form.parameters, return_full_text },
|
87 |
+
})
|
88 |
+
}
|
89 |
+
/>
|
90 |
+
<Input
|
91 |
+
label="Top K"
|
92 |
+
type="number"
|
93 |
+
tooltip={`Enables "Top-K" sampling: the model will choose from the K most probable tokens that may occur after the input sequence. Typical values are between 10 to 50.`}
|
94 |
+
min={10}
|
95 |
+
max={50}
|
96 |
+
sanitize={(value) => {
|
97 |
+
const valueAsNumber = Number(value);
|
98 |
+
if (valueAsNumber < 10) return 10;
|
99 |
+
if (valueAsNumber > 50) return 50;
|
100 |
+
return valueAsNumber;
|
101 |
+
}}
|
102 |
+
value={form?.parameters?.top_k}
|
103 |
+
onChange={(top_k) =>
|
104 |
+
onForm({
|
105 |
+
...form,
|
106 |
+
parameters: { ...form.parameters, top_k },
|
107 |
+
})
|
108 |
+
}
|
109 |
+
/>
|
110 |
+
<Input
|
111 |
+
label="Stop sequences"
|
112 |
+
tooltip="A list of sequences that will cause generation to stop when encountered in the output."
|
113 |
+
subLabel="Separate each sequence with a comma"
|
114 |
+
value={form?.parameters?.stop_sequences}
|
115 |
+
onChange={(stop_sequences) =>
|
116 |
+
onForm({
|
117 |
+
...form,
|
118 |
+
parameters: { ...form.parameters, stop_sequences },
|
119 |
+
})
|
120 |
+
}
|
121 |
+
/>
|
122 |
+
</div>
|
123 |
+
</div>
|
src/lib/components/text-generation/Preview.svelte
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
export let body: Record<string, any>;
|
3 |
+
export let res: Record<string, any>;
|
4 |
+
|
5 |
+
console.log(body)
|
6 |
+
</script>
|
7 |
+
|
8 |
+
<div class="text-white p-6 font-code text-xs !leading-loose">
|
9 |
+
{body.inputs ?? "No input provided"}
|
10 |
+
{#if res?.[0]?.generated_text}
|
11 |
+
<span>{res[0]?.generated_text?.replace(body?.inputs, "")}</span>
|
12 |
+
{/if}
|
13 |
+
</div>
|
src/lib/components/text-generation/Response.svelte
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import Icon from '@iconify/svelte';
|
3 |
+
import Highlight, { LineNumbers } from "svelte-highlight";
|
4 |
+
import json from "svelte-highlight/languages/json";
|
5 |
+
import "svelte-highlight/styles/night-owl.css"
|
6 |
+
|
7 |
+
import Loading from "$lib/components/Loading.svelte"
|
8 |
+
import CodePreview from '$lib/components/CodePreview.svelte';
|
9 |
+
import Preview from '$lib/components/text-generation/Preview.svelte';
|
10 |
+
|
11 |
+
export let loading: boolean;
|
12 |
+
export let res: any;
|
13 |
+
export let form: Record<string, any>;
|
14 |
+
export let body: Record<string, any>;
|
15 |
+
export let endpoint: string;
|
16 |
+
|
17 |
+
let tab = 0
|
18 |
+
|
19 |
+
$: code = JSON.stringify(res ?? {}, null, 2)
|
20 |
+
|
21 |
+
let TABS = [
|
22 |
+
{
|
23 |
+
label: "Formatted preview",
|
24 |
+
icon: "material-symbols:preview",
|
25 |
+
},
|
26 |
+
{
|
27 |
+
label: "Code example",
|
28 |
+
icon: "carbon:code",
|
29 |
+
},
|
30 |
+
];
|
31 |
+
</script>
|
32 |
+
|
33 |
+
<div class="h-screen flex flex-col relative">
|
34 |
+
<div class="w-full jsonResponse relative">
|
35 |
+
<div class="bg-slate-950 w-full uppercase px-5 py-4 text-zinc-400 text-sm font-semibold border-b border-slate-900 flex items-center justify-start gap-2">
|
36 |
+
<Icon icon="carbon:json" class="w-5 h-5" />
|
37 |
+
Response
|
38 |
+
</div>
|
39 |
+
<Highlight language={json} {code}>
|
40 |
+
</Highlight>
|
41 |
+
{#if loading}
|
42 |
+
<Loading>
|
43 |
+
<p class="text-slate-400 text-lg mt-4">Processing...</p>
|
44 |
+
</Loading>
|
45 |
+
{/if}
|
46 |
+
</div>
|
47 |
+
<div class="bg-slate-950 overflow-auto flex-1">
|
48 |
+
<div class="w-full uppercase text-zinc-400 text-sm font-semibold border-t border-slate-900 flex items-start sticky top-0 bg-slate-950">
|
49 |
+
{#each TABS as { label, icon }, idx }
|
50 |
+
<button
|
51 |
+
class={`flex items-center justify-start gap-2 px-5 border-r py-4 border-slate-900 bg-slate-900/40 cursor-pointer hover:bg-slate-900/60 transition-all duration-200 ${tab === idx ? '!bg-slate-950 hover:bg-slate-900/40' : 'border-b'}`}
|
52 |
+
on:click={() => tab = idx}
|
53 |
+
>
|
54 |
+
<Icon icon={icon} class="w-5 h-5" />
|
55 |
+
{label}
|
56 |
+
</button>
|
57 |
+
{/each}
|
58 |
+
<div class="flex flex-1 w-full pointer-events-none text-slate-950 border-b border-slate-900 h-[53px] bg-slate-900/40"></div>
|
59 |
+
</div>
|
60 |
+
{#if tab === 0}
|
61 |
+
<Preview body={body} res={res} />
|
62 |
+
{:else if tab === 1}
|
63 |
+
<CodePreview body={form} endpoint={endpoint} />
|
64 |
+
{/if}
|
65 |
+
</div>
|
66 |
+
</div>
|
src/lib/images/github.svg
ADDED
|
src/lib/images/hf-logo.svg
ADDED
|
src/lib/images/svelte-logo.svg
ADDED
|
src/lib/images/svelte-welcome.png
ADDED
![]() |
src/lib/images/svelte-welcome.webp
ADDED
![]() |
src/lib/styles/tailwind.css
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@tailwind base;
|
2 |
+
@tailwind components;
|
3 |
+
@tailwind utilities;
|
4 |
+
|
5 |
+
body, html {
|
6 |
+
@apply bg-slate-950 overflow-hidden min-h-screen;
|
7 |
+
font-family: "Source Sans 3", "sans-serif";
|
8 |
+
}
|
9 |
+
|
10 |
+
.jsonResponse .hljs {
|
11 |
+
@apply !text-sm !bg-black !font-code !whitespace-pre-wrap !p-8 !leading-relaxed;
|
12 |
+
}
|
13 |
+
.jsonResponse tbody.hljs {
|
14 |
+
@apply !h-full !leading-loose
|
15 |
+
}
|
16 |
+
.jsonResponse tbody.hljs tr td:first-of-type code {
|
17 |
+
@apply opacity-60 pl-2
|
18 |
+
}
|
19 |
+
|
20 |
+
.snippet .hljs {
|
21 |
+
@apply text-xs font-code !bg-transparent !p-0 !whitespace-pre-wrap break-all !leading-relaxed
|
22 |
+
}
|
src/lib/utils/models.ts
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import type { Model } from "./type";
|
2 |
+
|
3 |
+
export const TEXT_GENERATIONS: Array<Model> = [
|
4 |
+
{
|
5 |
+
id: "meta-llama/Llama-2-7b-chat-hf",
|
6 |
+
logo: "https://aeiljuispo.cloudimg.io/v7/https://cdn-uploads.huggingface.co/production/uploads/646cf8084eefb026fb8fd8bc/oCTqufkdTkjyGodsx1vo1.png?w=200&h=200&f=face",
|
7 |
+
},
|
8 |
+
{
|
9 |
+
id: "HuggingFaceH4/zephyr-7b-beta",
|
10 |
+
logo: "https://aeiljuispo.cloudimg.io/v7/https://cdn-uploads.huggingface.co/production/uploads/5f0c746619cb630495b814fd/j26aNEdiOgptZxJ6akGCC.png?w=200&h=200&f=face",
|
11 |
+
},
|
12 |
+
{
|
13 |
+
id: "mistralai/Mistral-7B-v0.1",
|
14 |
+
logo: "https://aeiljuispo.cloudimg.io/v7/https://cdn-uploads.huggingface.co/production/uploads/62dac1c7a8ead43d20e3e17a/wrLf5yaGC6ng4XME70w6Z.png?w=200&h=200&f=face",
|
15 |
+
},
|
16 |
+
];
|
17 |
+
|
18 |
+
export const IMAGE_GENERATIONS: Array<Model> = [
|
19 |
+
{
|
20 |
+
id: "stabilityai/stable-diffusion-xl-base-1.0",
|
21 |
+
logo: "https://aeiljuispo.cloudimg.io/v7/https://cdn-uploads.huggingface.co/production/uploads/643feeb67bc3fbde1385cc25/7vmYr2XwVcPtkLzac_jxQ.png?w=200&h=200&f=face"
|
22 |
+
},
|
23 |
+
]
|
src/lib/utils/type.ts
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export interface Model {
|
2 |
+
id: string;
|
3 |
+
logo: string;
|
4 |
+
}
|
src/routes/+layout.svelte
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script>
|
2 |
+
import Sidebar from '$lib/components/sidebar/Sidebar.svelte';
|
3 |
+
import '$lib/styles/tailwind.css';
|
4 |
+
</script>
|
5 |
+
|
6 |
+
<div class="app flex items-start justify-start">
|
7 |
+
<Sidebar />
|
8 |
+
|
9 |
+
<slot />
|
10 |
+
|
11 |
+
</div>
|
12 |
+
|
src/routes/+page.svelte
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script>
|
2 |
+
import Counter from './Counter.svelte';
|
3 |
+
import welcome from '$lib/images/svelte-welcome.webp';
|
4 |
+
import welcome_fallback from '$lib/images/svelte-welcome.png';
|
5 |
+
</script>
|
6 |
+
|
7 |
+
<svelte:head>
|
8 |
+
<title>Home</title>
|
9 |
+
<meta name="description" content="Svelte demo app" />
|
10 |
+
</svelte:head>
|
11 |
+
|
12 |
+
<section>
|
13 |
+
<h1>
|
14 |
+
<span class="welcome">
|
15 |
+
<picture>
|
16 |
+
<source srcset={welcome} type="image/webp" />
|
17 |
+
<img src={welcome_fallback} alt="Welcome" />
|
18 |
+
</picture>
|
19 |
+
</span>
|
20 |
+
|
21 |
+
to your new<br />SvelteKit app
|
22 |
+
</h1>
|
23 |
+
|
24 |
+
<h2>
|
25 |
+
try editing <strong>src/routes/+page.svelte</strong>
|
26 |
+
</h2>
|
27 |
+
|
28 |
+
<Counter />
|
29 |
+
</section>
|
30 |
+
|
31 |
+
<style>
|
32 |
+
section {
|
33 |
+
display: flex;
|
34 |
+
flex-direction: column;
|
35 |
+
justify-content: center;
|
36 |
+
align-items: center;
|
37 |
+
flex: 0.6;
|
38 |
+
}
|
39 |
+
|
40 |
+
h1 {
|
41 |
+
width: 100%;
|
42 |
+
}
|
43 |
+
|
44 |
+
.welcome {
|
45 |
+
display: block;
|
46 |
+
position: relative;
|
47 |
+
width: 100%;
|
48 |
+
height: 0;
|
49 |
+
padding: 0 0 calc(100% * 495 / 2048) 0;
|
50 |
+
}
|
51 |
+
|
52 |
+
.welcome img {
|
53 |
+
position: absolute;
|
54 |
+
width: 100%;
|
55 |
+
height: 100%;
|
56 |
+
top: 0;
|
57 |
+
display: block;
|
58 |
+
}
|
59 |
+
</style>
|
src/routes/+page.ts
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
// since there's no dynamic data here, we can prerender
|
2 |
+
// it so that it gets served as a static asset in production
|
3 |
+
export const prerender = true;
|
src/routes/Counter.svelte
ADDED
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { spring } from 'svelte/motion';
|
3 |
+
|
4 |
+
let count = 0;
|
5 |
+
|
6 |
+
const displayed_count = spring();
|
7 |
+
$: displayed_count.set(count);
|
8 |
+
$: offset = modulo($displayed_count, 1);
|
9 |
+
|
10 |
+
function modulo(n: number, m: number) {
|
11 |
+
// handle negative numbers
|
12 |
+
return ((n % m) + m) % m;
|
13 |
+
}
|
14 |
+
</script>
|
15 |
+
|
16 |
+
<div class="counter">
|
17 |
+
<button on:click={() => (count -= 1)} aria-label="Decrease the counter by one">
|
18 |
+
<svg aria-hidden="true" viewBox="0 0 1 1">
|
19 |
+
<path d="M0,0.5 L1,0.5" />
|
20 |
+
</svg>
|
21 |
+
</button>
|
22 |
+
|
23 |
+
<div class="counter-viewport">
|
24 |
+
<div class="counter-digits" style="transform: translate(0, {100 * offset}%)">
|
25 |
+
<strong class="hidden" aria-hidden="true">{Math.floor($displayed_count + 1)}</strong>
|
26 |
+
<strong>{Math.floor($displayed_count)}</strong>
|
27 |
+
</div>
|
28 |
+
</div>
|
29 |
+
|
30 |
+
<button on:click={() => (count += 1)} aria-label="Increase the counter by one">
|
31 |
+
<svg aria-hidden="true" viewBox="0 0 1 1">
|
32 |
+
<path d="M0,0.5 L1,0.5 M0.5,0 L0.5,1" />
|
33 |
+
</svg>
|
34 |
+
</button>
|
35 |
+
</div>
|
36 |
+
|
37 |
+
<style>
|
38 |
+
.counter {
|
39 |
+
display: flex;
|
40 |
+
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
41 |
+
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
42 |
+
margin: 1rem 0;
|
43 |
+
}
|
44 |
+
|
45 |
+
.counter button {
|
46 |
+
width: 2em;
|
47 |
+
padding: 0;
|
48 |
+
display: flex;
|
49 |
+
align-items: center;
|
50 |
+
justify-content: center;
|
51 |
+
border: 0;
|
52 |
+
background-color: transparent;
|
53 |
+
touch-action: manipulation;
|
54 |
+
font-size: 2rem;
|
55 |
+
}
|
56 |
+
|
57 |
+
.counter button:hover {
|
58 |
+
background-color: var(--color-bg-1);
|
59 |
+
}
|
60 |
+
|
61 |
+
svg {
|
62 |
+
width: 25%;
|
63 |
+
height: 25%;
|
64 |
+
}
|
65 |
+
|
66 |
+
path {
|
67 |
+
vector-effect: non-scaling-stroke;
|
68 |
+
stroke-width: 2px;
|
69 |
+
stroke: #444;
|
70 |
+
}
|
71 |
+
|
72 |
+
.counter-viewport {
|
73 |
+
width: 8em;
|
74 |
+
height: 4em;
|
75 |
+
overflow: hidden;
|
76 |
+
text-align: center;
|
77 |
+
position: relative;
|
78 |
+
}
|
79 |
+
|
80 |
+
.counter-viewport strong {
|
81 |
+
position: absolute;
|
82 |
+
display: flex;
|
83 |
+
width: 100%;
|
84 |
+
height: 100%;
|
85 |
+
font-weight: 400;
|
86 |
+
color: var(--color-theme-1);
|
87 |
+
font-size: 4rem;
|
88 |
+
align-items: center;
|
89 |
+
justify-content: center;
|
90 |
+
}
|
91 |
+
|
92 |
+
.counter-digits {
|
93 |
+
position: absolute;
|
94 |
+
width: 100%;
|
95 |
+
height: 100%;
|
96 |
+
}
|
97 |
+
|
98 |
+
.hidden {
|
99 |
+
top: -100%;
|
100 |
+
user-select: none;
|
101 |
+
}
|
102 |
+
</style>
|
src/routes/Header.svelte
ADDED
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script>
|
2 |
+
import { page } from '$app/stores';
|
3 |
+
import logo from '$lib/images/svelte-logo.svg';
|
4 |
+
import github from '$lib/images/github.svg';
|
5 |
+
</script>
|
6 |
+
|
7 |
+
<header>
|
8 |
+
<div class="corner">
|
9 |
+
<a href="https://kit.svelte.dev">
|
10 |
+
<img src={logo} alt="SvelteKit" />
|
11 |
+
</a>
|
12 |
+
</div>
|
13 |
+
|
14 |
+
<nav>
|
15 |
+
<svg viewBox="0 0 2 3" aria-hidden="true">
|
16 |
+
<path d="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z" />
|
17 |
+
</svg>
|
18 |
+
<ul>
|
19 |
+
<li aria-current={$page.url.pathname === '/' ? 'page' : undefined}>
|
20 |
+
<a href="/">Home</a>
|
21 |
+
</li>
|
22 |
+
<li aria-current={$page.url.pathname === '/about' ? 'page' : undefined}>
|
23 |
+
<a href="/about">About</a>
|
24 |
+
</li>
|
25 |
+
<li aria-current={$page.url.pathname.startsWith('/sverdle') ? 'page' : undefined}>
|
26 |
+
<a href="/sverdle">Sverdle</a>
|
27 |
+
</li>
|
28 |
+
</ul>
|
29 |
+
<svg viewBox="0 0 2 3" aria-hidden="true">
|
30 |
+
<path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" />
|
31 |
+
</svg>
|
32 |
+
</nav>
|
33 |
+
|
34 |
+
<div class="corner">
|
35 |
+
<a href="https://github.com/sveltejs/kit">
|
36 |
+
<img src={github} alt="GitHub" />
|
37 |
+
</a>
|
38 |
+
</div>
|
39 |
+
</header>
|
40 |
+
|
41 |
+
<style>
|
42 |
+
header {
|
43 |
+
display: flex;
|
44 |
+
justify-content: space-between;
|
45 |
+
}
|
46 |
+
|
47 |
+
.corner {
|
48 |
+
width: 3em;
|
49 |
+
height: 3em;
|
50 |
+
}
|
51 |
+
|
52 |
+
.corner a {
|
53 |
+
display: flex;
|
54 |
+
align-items: center;
|
55 |
+
justify-content: center;
|
56 |
+
width: 100%;
|
57 |
+
height: 100%;
|
58 |
+
}
|
59 |
+
|
60 |
+
.corner img {
|
61 |
+
width: 2em;
|
62 |
+
height: 2em;
|
63 |
+
object-fit: contain;
|
64 |
+
}
|
65 |
+
|
66 |
+
nav {
|
67 |
+
display: flex;
|
68 |
+
justify-content: center;
|
69 |
+
--background: rgba(255, 255, 255, 0.7);
|
70 |
+
}
|
71 |
+
|
72 |
+
svg {
|
73 |
+
width: 2em;
|
74 |
+
height: 3em;
|
75 |
+
display: block;
|
76 |
+
}
|
77 |
+
|
78 |
+
path {
|
79 |
+
fill: var(--background);
|
80 |
+
}
|
81 |
+
|
82 |
+
ul {
|
83 |
+
position: relative;
|
84 |
+
padding: 0;
|
85 |
+
margin: 0;
|
86 |
+
height: 3em;
|
87 |
+
display: flex;
|
88 |
+
justify-content: center;
|
89 |
+
align-items: center;
|
90 |
+
list-style: none;
|
91 |
+
background: var(--background);
|
92 |
+
background-size: contain;
|
93 |
+
}
|
94 |
+
|
95 |
+
li {
|
96 |
+
position: relative;
|
97 |
+
height: 100%;
|
98 |
+
}
|
99 |
+
|
100 |
+
li[aria-current='page']::before {
|
101 |
+
--size: 6px;
|
102 |
+
content: '';
|
103 |
+
width: 0;
|
104 |
+
height: 0;
|
105 |
+
position: absolute;
|
106 |
+
top: 0;
|
107 |
+
left: calc(50% - var(--size));
|
108 |
+
border: var(--size) solid transparent;
|
109 |
+
border-top: var(--size) solid var(--color-theme-1);
|
110 |
+
}
|
111 |
+
|
112 |
+
nav a {
|
113 |
+
display: flex;
|
114 |
+
height: 100%;
|
115 |
+
align-items: center;
|
116 |
+
padding: 0 0.5rem;
|
117 |
+
color: var(--color-text);
|
118 |
+
font-weight: 700;
|
119 |
+
font-size: 0.8rem;
|
120 |
+
text-transform: uppercase;
|
121 |
+
letter-spacing: 0.1em;
|
122 |
+
text-decoration: none;
|
123 |
+
transition: color 0.2s linear;
|
124 |
+
}
|
125 |
+
|
126 |
+
a:hover {
|
127 |
+
color: var(--color-theme-1);
|
128 |
+
}
|
129 |
+
</style>
|
src/routes/about/+page.svelte
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svelte:head>
|
2 |
+
<title>About</title>
|
3 |
+
<meta name="description" content="About this app" />
|
4 |
+
</svelte:head>
|
5 |
+
|
6 |
+
<div class="text-column">
|
7 |
+
<h1>About this app</h1>
|
8 |
+
|
9 |
+
<p>
|
10 |
+
This is a <a href="https://kit.svelte.dev">SvelteKit</a> app. You can make your own by typing the
|
11 |
+
following into your command line and following the prompts:
|
12 |
+
</p>
|
13 |
+
|
14 |
+
<pre>npm create svelte@latest</pre>
|
15 |
+
|
16 |
+
<p>
|
17 |
+
The page you're looking at is purely static HTML, with no client-side interactivity needed.
|
18 |
+
Because of that, we don't need to load any JavaScript. Try viewing the page's source, or opening
|
19 |
+
the devtools network panel and reloading.
|
20 |
+
</p>
|
21 |
+
|
22 |
+
<p>
|
23 |
+
The <a href="/sverdle">Sverdle</a> page illustrates SvelteKit's data loading and form handling. Try
|
24 |
+
using it with JavaScript disabled!
|
25 |
+
</p>
|
26 |
+
</div>
|
src/routes/about/+page.ts
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { dev } from '$app/environment';
|
2 |
+
|
3 |
+
// we don't need any JS on this page, though we'll load
|
4 |
+
// it in dev so that we get hot module replacement
|
5 |
+
export const csr = dev;
|
6 |
+
|
7 |
+
// since there's no dynamic data here, we can prerender
|
8 |
+
// it so that it gets served as a static asset in production
|
9 |
+
export const prerender = true;
|
src/routes/api/image-generation/+server.ts
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { error, type RequestEvent } from '@sveltejs/kit';
|
2 |
+
import { SECRET_HF_TOKEN } from '$env/static/private'
|
3 |
+
import { PUBLIC_INFERENCE_API_URL } from '$env/static/public'
|
4 |
+
|
5 |
+
/** @type {import('./$types').RequestHandler} */
|
6 |
+
export async function POST({ request }: RequestEvent) {
|
7 |
+
const body = await request?.json().catch(() => {});
|
8 |
+
|
9 |
+
if (!body?.model) {
|
10 |
+
throw error(400, 'missing model value')
|
11 |
+
}
|
12 |
+
|
13 |
+
if (!body?.inputs || body.inputs === null) {
|
14 |
+
throw error(400, 'missing inputs value')
|
15 |
+
}
|
16 |
+
|
17 |
+
const response = await fetch(PUBLIC_INFERENCE_API_URL + "/models/" + body.model, {
|
18 |
+
method: "POST",
|
19 |
+
headers: {
|
20 |
+
Authorization: `Bearer ${SECRET_HF_TOKEN}`,
|
21 |
+
'Content-Type': 'application/json',
|
22 |
+
['x-use-cache']: "0"
|
23 |
+
},
|
24 |
+
body: JSON.stringify({
|
25 |
+
...body,
|
26 |
+
stop_sequences: body?.stop_sequences?.split(",") ?? undefined,
|
27 |
+
}),
|
28 |
+
})
|
29 |
+
.then((res) => res.blob())
|
30 |
+
.then((blob) => blob)
|
31 |
+
.catch((error) => {
|
32 |
+
return {
|
33 |
+
error: error.message,
|
34 |
+
}
|
35 |
+
})
|
36 |
+
|
37 |
+
if ("error" in response) {
|
38 |
+
error(400, response.error as string)
|
39 |
+
}
|
40 |
+
|
41 |
+
return {
|
42 |
+
headers: {
|
43 |
+
'Content-Type': 'image/png',
|
44 |
+
},
|
45 |
+
body: response,
|
46 |
+
}
|
47 |
+
}
|
src/routes/api/text-generation/+server.ts
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { error, json, type RequestEvent } from '@sveltejs/kit';
|
2 |
+
import { SECRET_HF_TOKEN } from '$env/static/private'
|
3 |
+
import { PUBLIC_INFERENCE_API_URL } from '$env/static/public'
|
4 |
+
|
5 |
+
/** @type {import('./$types').RequestHandler} */
|
6 |
+
export async function POST({ request }: RequestEvent) {
|
7 |
+
const body = await request?.json().catch(() => {});
|
8 |
+
|
9 |
+
if (!body?.model) {
|
10 |
+
throw error(400, 'missing model value')
|
11 |
+
}
|
12 |
+
|
13 |
+
if (!body?.inputs || body.inputs === null) {
|
14 |
+
throw error(400, 'missing inputs value')
|
15 |
+
}
|
16 |
+
|
17 |
+
const response = await fetch(PUBLIC_INFERENCE_API_URL + "/models/" + body.model, {
|
18 |
+
method: "POST",
|
19 |
+
headers: {
|
20 |
+
Authorization: `Bearer ${SECRET_HF_TOKEN}`,
|
21 |
+
'Content-Type': 'application/json',
|
22 |
+
['x-use-cache']: "0"
|
23 |
+
},
|
24 |
+
body: JSON.stringify({
|
25 |
+
...body,
|
26 |
+
stop_sequences: body?.stop_sequences?.split(",") ?? undefined,
|
27 |
+
}),
|
28 |
+
})
|
29 |
+
.then((res) => res.json())
|
30 |
+
.then((res) => res)
|
31 |
+
.catch((error) => {
|
32 |
+
return {
|
33 |
+
error: error.message,
|
34 |
+
}
|
35 |
+
})
|
36 |
+
|
37 |
+
if ("error" in response) {
|
38 |
+
error(400, response.error as string)
|
39 |
+
}
|
40 |
+
|
41 |
+
return json(response)
|
42 |
+
}
|
src/routes/image-generation/+page.svelte
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import Form from "$lib/components/image-generation/Form.svelte";
|
3 |
+
import Response from "$lib/components/image-generation/Response.svelte";
|
4 |
+
|
5 |
+
let loading: boolean = false;
|
6 |
+
let data: string | ArrayBuffer | null = ''
|
7 |
+
let bodyRequest: Record<string, any> = {}
|
8 |
+
|
9 |
+
let form: Record<string, any> = {
|
10 |
+
model: "stabilityai/stable-diffusion-xl-base-1.0",
|
11 |
+
inputs: "A red apple on a table, high definition, realistic, 4k",
|
12 |
+
parameters: {
|
13 |
+
negative_prompt: "not blurry, not low resolution, not pixelated",
|
14 |
+
guidance_scale: 9,
|
15 |
+
width: 768,
|
16 |
+
height: 1024,
|
17 |
+
num_inference_steps: 20,
|
18 |
+
},
|
19 |
+
}
|
20 |
+
|
21 |
+
const onchange = (newForm: Record<string, any>) => form = newForm
|
22 |
+
|
23 |
+
const onsubmit = async () => {
|
24 |
+
loading = true
|
25 |
+
const request = await fetch('/api/image-generation', {
|
26 |
+
method: 'POST',
|
27 |
+
body: JSON.stringify(form),
|
28 |
+
headers: {
|
29 |
+
"Content-Type": "application/json"
|
30 |
+
}
|
31 |
+
})
|
32 |
+
const blob = await request?.clone()?.blob()
|
33 |
+
|
34 |
+
const reader = new FileReader()
|
35 |
+
reader.readAsDataURL(blob)
|
36 |
+
reader.onloadend = () => {
|
37 |
+
const base64data = reader.result
|
38 |
+
data = base64data
|
39 |
+
}
|
40 |
+
|
41 |
+
bodyRequest = form
|
42 |
+
loading = false
|
43 |
+
}
|
44 |
+
</script>
|
45 |
+
|
46 |
+
<main class="min-h-screen w-full grid grid-cols-2">
|
47 |
+
<div class="p-10 w-full flex flex-col gap-6">
|
48 |
+
<Form form={form} onForm={onchange} />
|
49 |
+
<div>
|
50 |
+
<button
|
51 |
+
class="bg-gradient-to-r from-slate-900/50 to-slate-900 border border-slate-800/40 text-white rounded-lg text-base transition-all duration-200 leading-relaxed outline-none relative py-3 px-6"
|
52 |
+
disabled={loading}
|
53 |
+
on:click={onsubmit}
|
54 |
+
>
|
55 |
+
Submit
|
56 |
+
</button>
|
57 |
+
</div>
|
58 |
+
</div>
|
59 |
+
<div class="border-l border-slate-900 bg-slate-950">
|
60 |
+
<Response loading={loading} res={data} body={bodyRequest} form={form} endpoint={form.model} />
|
61 |
+
response
|
62 |
+
</div>
|
63 |
+
</main>
|