-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
faa1835
commit c606690
Showing
61 changed files
with
921 additions
and
1,877 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"extends": ["next/core-web-vitals", "next/typescript"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,40 @@ | ||
# Logs | ||
logs | ||
*.log | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.* | ||
.yarn/* | ||
!.yarn/patches | ||
!.yarn/plugins | ||
!.yarn/releases | ||
!.yarn/versions | ||
|
||
# testing | ||
/coverage | ||
|
||
# next.js | ||
/.next/ | ||
/out/ | ||
|
||
# production | ||
/build | ||
|
||
# misc | ||
.DS_Store | ||
*.pem | ||
|
||
# debug | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
pnpm-debug.log* | ||
lerna-debug.log* | ||
|
||
node_modules | ||
dist | ||
dist-ssr | ||
*.local | ||
|
||
# Editor directories and files | ||
.vscode/* | ||
!.vscode/extensions.json | ||
.idea | ||
.DS_Store | ||
*.suo | ||
*.ntvs* | ||
*.njsproj | ||
*.sln | ||
*.sw? | ||
|
||
# env files (can opt-in for committing if needed) | ||
.env* | ||
|
||
# vercel | ||
.vercel | ||
|
||
# typescript | ||
*.tsbuildinfo | ||
next-env.d.ts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,45 @@ | ||
**[简体中文](README_ZH_CN.md) | [正體中文](README_ZH_TW.md) | English** | ||
**For old version, see `v3` branch.** | ||
|
||
# PainterLeaf | ||
- Text-to-image, supports multiple models | ||
- Image-to-text, convert local images to prompts | ||
- Also supports image-to-image | ||
# Painter Leaf | ||
|
||
- Text-to-image: supports multiple models | ||
- Image-to-text: convert local images to prompts | ||
- Prompt supports Chinese and English (Chinese will automatically call `AI` translation) | ||
- Front-end and back-end separation, front-end based on `React`, back-end based on `Hono`, see [this project](https://github.com/LeafYeeXYZ/MyAPIs) | ||
- `API` provided by `CloudflareAI` and `HuggingFace` | ||
- Internationalization support, currently supports `简体中文`, `正體中文`, and `English` | ||
|
||
|![](./readme/mobile-light.jpeg)|![](./readme/mobile-dark.jpeg)| | ||
|:---:|:---:| | ||
|![](./readme/light.png)|![](./readme/dark.png)| | ||
## TODO | ||
|
||
- [ ] Implement `Image-to-text` feature | ||
- [ ] Add preview images to `README.md` | ||
|
||
## Usage | ||
### Deploy Server | ||
See [this project](https://github.com/LeafYeeXYZ/MyAPIs) | ||
|
||
### Set Server URL | ||
Set `VITE_SERVER` environment variable in `.env` file, `Vercel` or `Cloudflare Pages`, such as `https://api.xxx.workers.dev` | ||
### Config Environment Variables | ||
|
||
Set following environment variables in `.env` file or `Vercel`: | ||
|
||
### Install Bun | ||
Please refer to [Bun.sh](https://bun.sh). Or simply run `npm i -g bun` | ||
| Key | Value | Required | | ||
| :---: | :---: | :---: | | ||
| `CF_USER_ID` | `Cloudflare` user id | ✅ for `Cloudflare AI` | | ||
| `CF_AI_API_KEY` | `Cloudflare AI` api key | ✅ for `Cloudflare AI` | | ||
| `HF_API_KEY` | `HuggingFace` api key | ✅ for `HuggingFace` | | ||
|
||
> If you don't need specific provider, you can leave the key empty. | ||
### Install dependencies | ||
|
||
```bash | ||
bun i | ||
``` | ||
|
||
### Local run | ||
```bash | ||
bun run dev | ||
``` | ||
> If you haven't installed `Bun` yet, please refer to [Bun.sh](https://bun.sh). | ||
### Local Development | ||
|
||
### Build | ||
```bash | ||
bun run build | ||
bun dev | ||
``` | ||
|
||
### Deploy | ||
|
||
It's recommended to deploy to `Vercel` while you can also deploy to other platforms, just make sure to set the environment variables correctly. |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
/** 生成请求地址和请求选项 */ | ||
class PainterRequest { | ||
#cfReg = /^@cf\// | ||
#hfReg = /^@hf\// | ||
url: string | ||
options: { | ||
method: string | ||
headers: HeadersInit | ||
body: string | ||
} | ||
constructor( | ||
model: string, | ||
prompt: string, | ||
env: { CF_USER_ID: string, CF_AI_API_KEY: string, HF_API_KEY: string }, | ||
) { | ||
// 判断模型 | ||
if (this.#cfReg.test(model)) { | ||
// Cloudflare | ||
this.url = `https://api.cloudflare.com/client/v4/accounts/${env.CF_USER_ID}/ai/run/${model}` | ||
this.options = { | ||
method: 'POST', | ||
headers: { | ||
'content-type': 'application/json', | ||
'Authorization': `Bearer ${env.CF_AI_API_KEY}` | ||
}, | ||
body: JSON.stringify({ | ||
prompt: prompt, | ||
negative_prompt: 'lowres, bad, text, error, missing, extra, fewer, cropped, jpeg artifacts, worst quality, bad quality, watermark, bad aesthetic, unfinished, chromatic aberration, scan, scan artifacts', | ||
}) | ||
} | ||
// 针对 Cloudflare 的 FLUX.1 Schnell 模型的特殊处理 | ||
if (model === '@cf/black-forest-labs/flux-1-schnell') { | ||
this.options.body = JSON.stringify({ | ||
num_steps: 8, | ||
...JSON.parse(this.options.body) | ||
}) | ||
} | ||
} else if (this.#hfReg.test(model)) { | ||
// Hugging Face | ||
this.url = `https://api-inference.huggingface.co/models/${model.replace(this.#hfReg, '')}` | ||
this.options = { | ||
method: 'POST', | ||
headers: { | ||
'content-type': 'application/json', | ||
'Authorization': `Bearer ${env.HF_API_KEY}` | ||
}, | ||
body: JSON.stringify({ | ||
inputs: prompt, | ||
prompt: prompt, | ||
negative_prompt: 'lowres, bad, text, error, missing, extra, fewer, cropped, jpeg artifacts, worst quality, bad quality, watermark, bad aesthetic, unfinished, chromatic aberration, scan, scan artifacts', | ||
}) | ||
} | ||
} else { | ||
throw new Error(`Unsupported model: ${model}`) | ||
} | ||
} | ||
} | ||
|
||
export async function POST(req: Request): Promise<Response> { | ||
try { | ||
// 图片 | ||
const { model, prompt } = await req.json() | ||
// 请求参数和请求地址 | ||
const { options, url } = new PainterRequest(model, prompt, { | ||
CF_USER_ID: process.env.CF_USER_ID ?? '', | ||
CF_AI_API_KEY: process.env.CF_AI_API_KEY ?? '', | ||
HF_API_KEY: process.env.HF_API_KEY ?? '', | ||
}) | ||
// 发送请求 | ||
const response = await fetch(url, options) | ||
// 针对 Cloudflare 的 FLUX.1 Schnell 模型的特殊处理 | ||
if (model === '@cf/black-forest-labs/flux-1-schnell') { | ||
const res = await response.json() | ||
const base64 = res.result.image as string | ||
const buffer = new Uint8Array(atob(base64).split('').map(c => c.charCodeAt(0))) | ||
return new Response(buffer, { | ||
status: response.status, | ||
headers: { | ||
'content-type': 'image/png', | ||
} | ||
}) | ||
} | ||
// 返回结果 | ||
return new Response(response.body, { | ||
status: response.status, | ||
headers: { | ||
'content-type': response.headers.get('content-type') ?? 'text/plain', | ||
} | ||
}) | ||
} catch (e) { | ||
return new Response(e instanceof Error ? e.message : 'Unkown Server Error', { status: 500 }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
|
||
// import type { Context } from 'hono' | ||
// export async function painter_genprompt(c: Context): Promise<Response> { | ||
// try { | ||
// // 请求参数 | ||
// const url = `https://api.cloudflare.com/client/v4/accounts/${c.env.CF_USER}/ai/run/@cf/unum/uform-gen2-qwen-500m` | ||
// const req = await c.req.json() | ||
// const body = { | ||
// image: req.image, | ||
// max_tokens: 2048, | ||
// prompt: 'Generate a detailed description in a single paragraph for this image', | ||
// } | ||
// // 发送请求 | ||
// const response = await fetch(url, { | ||
// method: 'POST', | ||
// headers: { | ||
// 'content-type': 'application/json', | ||
// 'Authorization': `Bearer ${c.env.CF_AI_API_KEY}` | ||
// }, | ||
// body: JSON.stringify(body) | ||
// }) | ||
// const result = await response.json() | ||
// // 返回结果 | ||
// console.log(SUCCESS_MESSAGE) | ||
// return new Response(JSON.stringify(result), { | ||
// status: result.success ? 200 : 500, | ||
// headers: { | ||
// 'content-type': 'application/json', | ||
// } | ||
// }) | ||
// } catch(e) { | ||
// const message = e instanceof Error ? e.message : 'Unkown Server Error' | ||
// ERROR_MESSAGE.data!.error = message | ||
// console.error(ERROR_MESSAGE) | ||
// return new Response(message, { status: 500 }) | ||
// } | ||
// } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
export async function POST(req: Request): Promise<Response> { | ||
try { | ||
const url = `https://api.cloudflare.com/client/v4/accounts/${process.env.CF_USER_ID}/ai/run/@cf/meta/m2m100-1.2b` | ||
const { zh } = await req.json() | ||
const res = await fetch(url, { | ||
method: 'POST', | ||
headers: { | ||
'content-type': 'application/json', | ||
'Authorization': `Bearer ${process.env.CF_AI_API_KEY}` | ||
}, | ||
body: JSON.stringify({ | ||
text: zh, | ||
source_lang: 'zh', | ||
target_lang: 'en', | ||
}), | ||
}) | ||
const result = await res.json() | ||
if (result?.success) { | ||
return Response.json(result) | ||
} else { | ||
return new Response('Translate Failed', { status: 500 }) | ||
} | ||
} catch(e) { | ||
return new Response(e instanceof Error ? e.message : 'Unkown Server Error', { status: 500 }) | ||
} | ||
} |
Oops, something went wrong.