├── .babelrc.json ├── .browserslistrc ├── .env ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ └── npm-publish.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .prettierignore ├── .prettierrc.js ├── .vscode └── launch.json ├── README.md ├── commitlint.config.js ├── examples ├── cleanup.js ├── createtile.js ├── doodle.js ├── face.jpeg ├── hunyuan-video-fast.js ├── img2img.js ├── img2video.js ├── inpainting.js ├── lcm_txt2img.js ├── mask.png ├── merge_face.js ├── mixpose.js ├── outpainting.js ├── reimagine.js ├── removebg.js ├── removetxt.js ├── replace_object.js ├── replace_sky.js ├── replacebg.js ├── restore_face.js ├── test.png ├── txt2img.js ├── upscale.js ├── utils.js └── wan-t2v.js ├── index.ts ├── jest.config.js ├── package-lock.json ├── package.json ├── rollup.config.mjs ├── src ├── class.ts ├── enum.ts ├── error.ts ├── types.ts └── util.ts ├── test ├── assets │ ├── .DS_Store │ ├── doodle.png │ ├── face1.png │ ├── face2.png │ ├── mask.png │ ├── pose.png │ └── sample.jpeg ├── test_group_1.test.ts ├── test_group_2.test.ts └── utils.ts ├── tsconfig.json └── yarn.lock /.babelrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } 4 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 0.2% 2 | last 2 versions 3 | ie >= 11 4 | not dead 5 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | API_KEY=787ee3fd-ff97-4aac-936e-1b09cf74a559 -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | test/** 2 | lib/** 3 | types/** 4 | dist/** 5 | examples/** -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | module.exports = { 4 | env: { 5 | browser: true, 6 | es2021: true, 7 | node: true, 8 | }, 9 | extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 10 | overrides: [ 11 | { 12 | env: { 13 | node: true, 14 | }, 15 | files: [".eslintrc.{js,cjs}"], 16 | parserOptions: { 17 | sourceType: "script", 18 | }, 19 | }, 20 | ], 21 | parser: "@typescript-eslint/parser", 22 | parserOptions: { 23 | ecmaVersion: "latest", 24 | sourceType: "module", 25 | }, 26 | plugins: ["@typescript-eslint"], 27 | rules: { 28 | "@typescript-eslint/no-explicit-any": "off", 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # @format 2 | 3 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 4 | # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages 5 | 6 | name: Publish NPM Package 7 | 8 | on: 9 | release: 10 | types: [created] 11 | workflow_dispatch: 12 | 13 | jobs: 14 | build-and-publish: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: actions/setup-node@v4 19 | with: 20 | node-version: 20 21 | registry-url: https://registry.npmjs.org/ 22 | - run: npm ci 23 | - run: npm test 24 | - run: npm run build 25 | - run: npm publish 26 | env: 27 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx --no -- commitlint --edit ${1} 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | test/** 2 | lib/** 3 | types/** 4 | dist/** -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | module.exports = { 4 | printWidth: 120, 5 | semi: true, 6 | singleQuote: false, 7 | trailingComma: "all", 8 | insertPragma: true, 9 | }; 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Jest All", 11 | "program": "${workspaceFolder}/node_modules/.bin/jest", 12 | "args": ["--runInBand"], 13 | "internalConsoleOptions": "neverOpen" 14 | }, 15 | { 16 | "type": "node", 17 | "request": "launch", 18 | "name": "Jest Current File", 19 | "program": "${workspaceFolder}/node_modules/.bin/jest", 20 | "args": ["${fileBasenameNoExtension}", "--runInBand"], 21 | "internalConsoleOptions": "neverOpen" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Novita.ai Javascript SDK 4 | 5 | This SDK is based on the official [novita.ai API reference](https://docs.novita.ai/) 6 | 7 | **Join our discord server for help:** 8 | 9 | [![](https://dcbadge.vercel.app/api/server/YyPRAzwp7P)](https://discord.gg/YyPRAzwp7P) 10 | 11 | ## Quick start 12 | 13 | 1. Sign up on [novita.ai](https://novita.ai) and get an API key. Please follow the instructions at [https://novita.ai/get-started](https://novita.ai/get-started/) 14 | 15 | 2. Install the [npm package](https://www.npmjs.com/package/novita-sdk) in your project. 16 | 17 | ```bash 18 | npm i novita-sdk 19 | ``` 20 | 21 | ## Version 3.1.0 Update Notes 22 | 23 | We've made significant changes in version 3.0.0. We removed some APIs and will not serve them in the future. The APIs deprecated are: 24 | 25 | - adetailer 26 | - img2mask 27 | - anymate-anyone 28 | - create-tile 29 | - doodle 30 | - lcm-img2img 31 | - lcm-txt2img 32 | - make-photo 33 | - mix-pose 34 | - relight 35 | - remove-watermark 36 | - replace-sky 37 | - replace-object 38 | - upscale 39 | - img2prompt 40 | - img2video-motion 41 | - outpainting 42 | - reimagine 43 | - restore-face 44 | 45 | ## Usage 46 | 47 | ```javascript 48 | import { NovitaSDK } from "novita-sdk"; 49 | 50 | const novitaClient = new NovitaSDK("your api key"); 51 | 52 | const params = { 53 | request: { 54 | model_name: "majicmixRealistic_v7_134792.safetensors", 55 | prompt: "1girl,sweater,white background", 56 | negative_prompt: "(worst quality:2),(low quality:2),(normal quality:2),lowres,watermark,", 57 | width: 512, 58 | height: 768, 59 | sampler_name: "Euler a", 60 | guidance_scale: 7, 61 | steps: 20, 62 | image_num: 1, 63 | seed: -1, 64 | }, 65 | }; 66 | novitaClient 67 | .txt2Img(params) 68 | .then((res) => { 69 | if (res && res.task_id) { 70 | const timer = setInterval(() => { 71 | novitaClient 72 | .progress({ 73 | task_id: res.task_id, 74 | }) 75 | .then((progressRes) => { 76 | if (progressRes.task.status === TaskStatus.SUCCEED) { 77 | console.log("finished!", progressRes.images); 78 | clearInterval(timer); 79 | onFinish(progressRes.images); 80 | } 81 | if (progressRes.task.status === TaskStatus.FAILED) { 82 | console.warn("failed!", progressRes.task.reason); 83 | clearInterval(timer); 84 | } 85 | if (progressRes.task.status === TaskStatus.QUEUED) { 86 | console.log("queueing"); 87 | } 88 | }) 89 | .catch((err) => { 90 | console.error("progress error:", err); 91 | }); 92 | }, 1000); 93 | } 94 | }) 95 | .catch((err) => { 96 | console.error(err); 97 | }); 98 | ``` 99 | 100 | ## API list and Sample codes 101 | 102 | - [txt2Img](https://github.com/novitalabs/javascript-sdk/blob/main/examples/txt2Img.js) 103 | - [img2Img](https://github.com/novitalabs/javascript-sdk/blob/main/examples/img2Img.js) 104 | - [cleanup](https://github.com/novitalabs/javascript-sdk/blob/main/examples/cleanup.js) 105 | - [outpainting](https://github.com/novitalabs/javascript-sdk/blob/main/examples/outpainting.js) 106 | - [removeBackground](https://github.com/novitalabs/javascript-sdk/blob/main/examples/removebg.js) 107 | - [replaceBackground](https://github.com/novitalabs/javascript-sdk/blob/main/examples/replacebg.js) 108 | - [mergeFace](https://github.com/novitalabs/javascript-sdk/blob/main/examples/merge_face.js) 109 | - [removeText](https://github.com/novitalabs/javascript-sdk/blob/main/examples/removetxt.js) 110 | - [restoreFace](https://github.com/novitalabs/javascript-sdk/blob/main/examples/restore_face.js) 111 | - [reimagine](https://github.com/novitalabs/javascript-sdk/blob/main/examples/reimagine.js) 112 | - [img2video](https://github.com/novitalabs/javascript-sdk/blob/main/examples/controlnet.js) 113 | - [hunyuan-video-fast](https://github.com/novitalabs/javascript-sdk/blob/main/examples/hunyuan-video-fast.js) 114 | - [wan-t2v](https://github.com/novitalabs/javascript-sdk/blob/main/examples/wan-t2v.js) 115 | 116 | ## Type Definitions 117 | 118 | For detailed information on the parameters and return types of each method, please refer to the [types.ts](https://github.com/novitalabs/javascript-sdk/blob/main/src/types.ts) file. 119 | 120 | ## Playground 121 | 122 | You can try all demos at [https://novita.ai/models/image](https://novita.ai/models/image) 123 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | module.exports = { 4 | extends: ["@commitlint/config-conventional"], 5 | }; 6 | -------------------------------------------------------------------------------- /examples/cleanup.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK } = require("novita-sdk"); 4 | const path = require("path"); 5 | const { convertImageToBase64 } = require("./utils.js"); 6 | 7 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 8 | 9 | async function cleanup(onFinish) { 10 | const baseImg = await convertImageToBase64(path.join(__dirname, "test.png")); 11 | const maskImg = await convertImageToBase64(path.join(__dirname, "mask.png")); 12 | const params = { 13 | image_file: baseImg, 14 | mask_file: maskImg, 15 | }; 16 | novitaClient 17 | .cleanup(params) 18 | .then((res) => { 19 | console.log("finished!", res); 20 | onFinish(res); 21 | }) 22 | .catch((err) => { 23 | console.error("error:", err); 24 | }); 25 | } 26 | 27 | cleanup((imgs) => { 28 | console.log(imgs); 29 | }); 30 | -------------------------------------------------------------------------------- /examples/createtile.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK } = require("novita-sdk"); 4 | 5 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 6 | 7 | async function createTile(onFinish) { 8 | const params = { 9 | prompt: "a bird", 10 | negative_prompt: "", 11 | width: 128, 12 | height: 128, 13 | }; 14 | novitaClient 15 | .createTile(params) 16 | .then((res) => { 17 | console.log("finished!", res); 18 | onFinish(res); 19 | }) 20 | .catch((err) => { 21 | console.error("error:", err); 22 | }); 23 | } 24 | 25 | createTile((imgs) => { 26 | console.log(imgs); 27 | }); 28 | -------------------------------------------------------------------------------- /examples/doodle.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK } = require("novita-sdk"); 4 | const path = require("path"); 5 | const { convertImageToBase64 } = require("./utils.js"); 6 | 7 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 8 | 9 | async function doodle(onFinish) { 10 | const maskImg = await convertImageToBase64(path.join(__dirname, "mask.png")); 11 | const params = { 12 | image_file: maskImg, 13 | prompt: "a bird", 14 | similarity: 0.75, 15 | }; 16 | novitaClient 17 | .doodle(params) 18 | .then((res) => { 19 | console.log("finished!", res); 20 | onFinish(res); 21 | }) 22 | .catch((err) => { 23 | console.error("error:", err); 24 | }); 25 | } 26 | 27 | doodle((imgs) => { 28 | console.log(imgs); 29 | }); 30 | -------------------------------------------------------------------------------- /examples/face.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novitalabs/javascript-sdk/58c99a25bf36fbd16d97fed59d82cc6ebbb6a51b/examples/face.jpeg -------------------------------------------------------------------------------- /examples/hunyuan-video-fast.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import { NovitaSDK, TaskStatus } from "novita-sdk"; 4 | 5 | const novitaClient = new NovitaSDK("your_api_key"); 6 | 7 | async function hunyuanVideoFast(onFinish) { 8 | const params = { 9 | model_name: "hunyuan-video-fast", 10 | width: 720, 11 | height: 1280, 12 | steps: 20, 13 | seed: -1, 14 | prompt: 15 | "A close up view of a glass sphere that has a zen garden within it. There is a small dwarf in the sphere who is raking the zen garden and creating patterns in the sand.", 16 | frames: 85, 17 | }; 18 | novitaClient 19 | .hunyuanVideoFast(params) 20 | .then((res) => { 21 | if (res && res.task_id) { 22 | const timer = setInterval(() => { 23 | novitaClient 24 | .progress({ 25 | task_id: res.task_id, 26 | }) 27 | .then((progressRes) => { 28 | if (progressRes.task.status === TaskStatus.SUCCEED) { 29 | console.log("finished!", progressRes.videos); 30 | clearInterval(timer); 31 | onFinish(progressRes.videos); 32 | } 33 | if (progressRes.task.status === TaskStatus.FAILED) { 34 | console.warn("failed!", progressRes.task.reason); 35 | clearInterval(timer); 36 | } 37 | if (progressRes.task.status === TaskStatus.QUEUED) { 38 | console.log("queueing"); 39 | } 40 | }) 41 | .catch((err) => { 42 | console.error("progress error:", err); 43 | }); 44 | }, 1000); 45 | } 46 | }) 47 | .catch((err) => { 48 | console.error("hunyuanVideoFast request error:", err); 49 | }); 50 | } 51 | 52 | hunyuanVideoFast((videos) => { 53 | console.log(videos); 54 | }); 55 | -------------------------------------------------------------------------------- /examples/img2img.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK } = require("novita-sdk"); 4 | const path = require("path"); 5 | const { convertImageToBase64 } = require("./utils.js"); 6 | 7 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 8 | 9 | async function img2img(onFinish) { 10 | const baseImg = await convertImageToBase64(path.join(__dirname, "test.png")); 11 | const params = { 12 | request: { 13 | model_name: "majicmixRealistic_v7_134792.safetensors", 14 | image_base64: baseImg, 15 | prompt: "1girl,sweater,white background", 16 | negative_prompt: "(worst quality:2),(low quality:2),(normal quality:2),lowres,watermark,", 17 | width: 512, 18 | height: 768, 19 | sampler_name: "Euler a", 20 | guidance_scale: 7, 21 | steps: 20, 22 | image_num: 1, 23 | seed: -1, 24 | strength: 0.5, 25 | }, 26 | }; 27 | novitaClient 28 | .img2Img(params) 29 | .then((res) => { 30 | if (res && res.task_id) { 31 | const timer = setInterval(() => { 32 | novitaClient 33 | .progress({ 34 | task_id: res.task_id, 35 | }) 36 | .then((progressRes) => { 37 | if (progressRes.task.status === TaskStatus.SUCCEED) { 38 | console.log("finished!", progressRes.images); 39 | clearInterval(timer); 40 | onFinish(progressRes.images); 41 | } 42 | if (progressRes.task.status === TaskStatus.FAILED) { 43 | console.warn("failed!", progressRes.task.reason); 44 | clearInterval(timer); 45 | } 46 | if (progressRes.task.status === TaskStatus.QUEUED) { 47 | console.log("queueing"); 48 | } 49 | }) 50 | .catch((err) => { 51 | console.error("progress error:", err); 52 | }); 53 | }, 1000); 54 | } 55 | }) 56 | .catch((err) => { 57 | console.error("img2Img error:", err); 58 | }); 59 | } 60 | 61 | img2img((imgs) => { 62 | console.log(imgs); 63 | }); 64 | -------------------------------------------------------------------------------- /examples/img2video.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK } = require("novita-sdk"); 4 | const path = require("path"); 5 | const { convertImageToBase64 } = require("./utils.js"); 6 | 7 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 8 | 9 | async function img2img(onFinish) { 10 | const baseImg = await convertImageToBase64(path.join(__dirname, "test.png")); 11 | const params = { 12 | init_images: [baseImg], 13 | model_name: "majicmixRealistic_v7_134792.safetensors", 14 | prompt: "1girl,sweater,white background", 15 | negative_prompt: "(worst quality:2),(low quality:2),(normal quality:2),lowres,watermark,", 16 | width: 512, 17 | height: 768, 18 | sampler_name: "Euler a", 19 | cfg_scale: 7, 20 | steps: 20, 21 | batch_size: 1, 22 | n_iter: 1, 23 | seed: -1, 24 | }; 25 | novitaClient 26 | .img2img(params) 27 | .then((res) => { 28 | if (res && res.task_id) { 29 | const timer = setInterval(() => { 30 | novitaClient 31 | .progress({ 32 | task_id: res.task_id, 33 | }) 34 | .then((progressRes) => { 35 | if (progressRes.status === 2) { 36 | console.log("finished!", progressRes.imgs); 37 | clearInterval(timer); 38 | onFinish(progressRes.imgs); 39 | } 40 | if (progressRes.status === 3 || progressRes.status === 4) { 41 | console.warn("failed!", progressRes.failed_reason); 42 | clearInterval(timer); 43 | } 44 | if (progressRes.status === 1) { 45 | console.log("progress", progressRes.current_images); 46 | } 47 | }) 48 | .catch((err) => { 49 | console.error("progress error:", err); 50 | }); 51 | }, 1000); 52 | } 53 | }) 54 | .catch((err) => { 55 | console.error("txt2Img error:", err); 56 | }); 57 | } 58 | 59 | img2img((imgs) => { 60 | console.log(imgs); 61 | }); 62 | -------------------------------------------------------------------------------- /examples/inpainting.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK } = require("novita-sdk"); 4 | const path = require("path"); 5 | const { convertImageToBase64 } = require("./utils.js"); 6 | 7 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 8 | 9 | async function inpainting(onFinish) { 10 | const baseImg = await convertImageToBase64(path.join(__dirname, "test.png")); 11 | const maskImg = await convertImageToBase64(path.join(__dirname, "mask.png")); 12 | const params = { 13 | model_name: "majicmixRealistic_v7_134792.safetensors", 14 | init_images: [baseImg], 15 | mask: maskImg, 16 | prompt: "1girl,sweater,white background", 17 | negative_prompt: "(worst quality:2),(low quality:2),(normal quality:2),lowres,watermark,", 18 | width: 512, 19 | height: 768, 20 | sampler_name: "Euler a", 21 | cfg_scale: 7, 22 | steps: 20, 23 | batch_size: 1, 24 | n_iter: 1, 25 | seed: -1, 26 | denoising_strength: 0.7, 27 | inpainting_fill: 0, 28 | }; 29 | novitaClient 30 | .img2img(params) 31 | .then((res) => { 32 | if (res && res.task_id) { 33 | const timer = setInterval(() => { 34 | novitaClient 35 | .progress({ 36 | task_id: res.task_id, 37 | }) 38 | .then((res) => { 39 | if (res.status === 2) { 40 | console.log("finished!", res.imgs); 41 | clearInterval(timer); 42 | onFinish(res.imgs); 43 | } 44 | if (res.status === 3 || res.status === 4) { 45 | console.warn("failed!", res.failed_reason); 46 | clearInterval(timer); 47 | } 48 | if (res.status === 1) { 49 | console.log("progress", res.current_images); 50 | } 51 | }) 52 | .catch((err) => { 53 | console.error("progress error:", err); 54 | }); 55 | }, 1000); 56 | } 57 | }) 58 | .catch((err) => { 59 | console.error("img2img error:", err); 60 | }); 61 | } 62 | 63 | inpainting((imgs) => { 64 | console.log(imgs); 65 | }); 66 | -------------------------------------------------------------------------------- /examples/lcm_txt2img.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK } = require("novita-sdk"); 4 | 5 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 6 | 7 | async function lcm(onFinish) { 8 | const params = { 9 | prompt: "a dog", 10 | width: 512, 11 | height: 512, 12 | image_num: 1, 13 | steps: 8, 14 | guidance_scale: 7.5, 15 | }; 16 | novitaClient 17 | .lcmTxt2Img(params) 18 | .then((res) => { 19 | console.log("finished!", res.images); 20 | onFinish(res.images); 21 | }) 22 | .catch((err) => { 23 | console.error("error:", err); 24 | }); 25 | } 26 | 27 | lcm((imgs) => { 28 | console.log(imgs); 29 | }); 30 | -------------------------------------------------------------------------------- /examples/mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novitalabs/javascript-sdk/58c99a25bf36fbd16d97fed59d82cc6ebbb6a51b/examples/mask.png -------------------------------------------------------------------------------- /examples/merge_face.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK } = require("novita-sdk"); 4 | const path = require("path"); 5 | const { convertImageToBase64 } = require("./utils.js"); 6 | 7 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 8 | 9 | async function mergeFace(onFinish) { 10 | const baseImg = await convertImageToBase64(path.join(__dirname, "test.jpeg")); 11 | const faceImg = await convertImageToBase64(path.join(__dirname, "face.png")); 12 | const params = { 13 | image_file: baseImg, 14 | face_image_file: faceImg, 15 | }; 16 | novitaClient 17 | .mergeFace(params) 18 | .then((res) => { 19 | console.log("finished!", res); 20 | onFinish(res); 21 | }) 22 | .catch((err) => { 23 | console.error("error:", err); 24 | }); 25 | } 26 | 27 | mergeFace((imgs) => { 28 | console.log(imgs); 29 | }); 30 | -------------------------------------------------------------------------------- /examples/mixpose.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK } = require("novita-sdk"); 4 | const path = require("path"); 5 | const { convertImageToBase64 } = require("./utils.js"); 6 | 7 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 8 | 9 | async function mixpose(onFinish) { 10 | const baseImg = await convertImageToBase64(path.join(__dirname, "test.png")); 11 | const faceImg = await convertImageToBase64(path.join(__dirname, "face.jpeg")); 12 | const params = { 13 | image_file: baseImg, 14 | pose_image_file: faceImg, 15 | }; 16 | novitaClient 17 | .mixpose(params) 18 | .then((res) => { 19 | console.log("finished!", res); 20 | }) 21 | .catch((err) => { 22 | console.error("error:", err); 23 | }); 24 | } 25 | 26 | mixpose((imgs) => { 27 | console.log(imgs); 28 | }); 29 | -------------------------------------------------------------------------------- /examples/outpainting.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK } = require("novita-sdk"); 4 | const path = require("path"); 5 | const { convertImageToBase64 } = require("./utils.js"); 6 | 7 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 8 | 9 | async function outpainting(onFinish) { 10 | const baseImg = await convertImageToBase64(path.join(__dirname, "test.png")); 11 | const params = { 12 | image_file: baseImg, 13 | width: 512, 14 | height: 512, 15 | center_x: 100, 16 | center_y: 100, 17 | }; 18 | novitaClient 19 | .outpainting(params) 20 | .then((res) => { 21 | console.log("finished!", res); 22 | onFinish(res); 23 | }) 24 | .catch((err) => { 25 | console.error("error:", err); 26 | }); 27 | } 28 | 29 | outpainting((imgs) => { 30 | console.log(imgs); 31 | }); 32 | -------------------------------------------------------------------------------- /examples/reimagine.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK } = require("novita-sdk"); 4 | const path = require("path"); 5 | const { convertImageToBase64 } = require("./utils.js"); 6 | 7 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 8 | 9 | async function reimagine(onFinish) { 10 | const baseImg = await convertImageToBase64(path.join(__dirname, "test.png")); 11 | const params = { 12 | image_file: baseImg, 13 | }; 14 | novitaClient 15 | .reimagine(params) 16 | .then((res) => { 17 | console.log("finished!", res); 18 | onFinish(res); 19 | }) 20 | .catch((err) => { 21 | console.error("error:", err); 22 | }); 23 | } 24 | 25 | reimagine((imgs) => { 26 | console.log(imgs); 27 | }); 28 | -------------------------------------------------------------------------------- /examples/removebg.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK } = require("novita-sdk"); 4 | const path = require("path"); 5 | const { convertImageToBase64 } = require("./utils.js"); 6 | 7 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 8 | 9 | async function removebg(onFinish) { 10 | const baseImg = await convertImageToBase64(path.join(__dirname, "test.png")); 11 | const params = { 12 | image_file: baseImg, 13 | }; 14 | novitaClient 15 | .removeBackground(params) 16 | .then((res) => { 17 | console.log("finished!", res); 18 | onFinish(res); 19 | }) 20 | .catch((err) => { 21 | console.error("error:", err); 22 | }); 23 | } 24 | 25 | removebg((imgs) => { 26 | console.log(imgs); 27 | }); 28 | -------------------------------------------------------------------------------- /examples/removetxt.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK } = require("novita-sdk"); 4 | const path = require("path"); 5 | const { convertImageToBase64 } = require("./utils.js"); 6 | 7 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 8 | 9 | async function removetxt() { 10 | const baseImg = await convertImageToBase64(path.join(__dirname, "test.png")); 11 | const params = { 12 | image_file: baseImg, 13 | }; 14 | novitaClient 15 | .removeText(params) 16 | .then((res) => { 17 | console.log("finished!", res); 18 | }) 19 | .catch((err) => { 20 | console.error("error:", err); 21 | }); 22 | } 23 | 24 | removetxt((imgs) => { 25 | console.log(imgs); 26 | }); 27 | -------------------------------------------------------------------------------- /examples/replace_object.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK, TaskStatus } = require("novita-sdk"); 4 | const path = require("path"); 5 | const { convertImageToBase64 } = require("./utils.js"); 6 | 7 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 8 | 9 | async function replaceObj(onFinish) { 10 | const baseImg = await convertImageToBase64(path.join(__dirname, "test.png")); 11 | const params = { 12 | image_file: baseImg, 13 | object_prompt: "the head of the girl", 14 | prompt: "a dog head", 15 | negative_prompt: "", 16 | }; 17 | novitaClient 18 | .replaceObject(params) 19 | .then((res) => { 20 | if (res && res.task_id) { 21 | const timer = setInterval(() => { 22 | novitaClient 23 | .progress({ 24 | task_id: res.task_id, 25 | }) 26 | .then((progressRes) => { 27 | if (progressRes.task.status === TaskStatus.SUCCEED) { 28 | console.log("finished!", progressRes.images); 29 | clearInterval(timer); 30 | onFinish(progressRes.images); 31 | } 32 | if (progressRes.task.status === TaskStatus.FAILED) { 33 | console.warn("failed!", progressRes.task.reason); 34 | clearInterval(timer); 35 | } 36 | if (progressRes.task.status === TaskStatus.QUEUED) { 37 | console.log("queueing"); 38 | } 39 | }) 40 | .catch((err) => { 41 | console.error("progress error:", err); 42 | }); 43 | }, 1000); 44 | } 45 | }) 46 | .catch((err) => { 47 | console.error("error:", err); 48 | }); 49 | } 50 | 51 | replaceObj((imgs) => { 52 | console.log(imgs); 53 | }); 54 | -------------------------------------------------------------------------------- /examples/replace_sky.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK, SkyType } = require("novita-sdk"); 4 | const path = require("path"); 5 | const { convertImageToBase64 } = require("./utils.js"); 6 | 7 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 8 | 9 | async function replaceSky(onFinish) { 10 | const baseImg = await convertImageToBase64(path.join(__dirname, "test.png")); 11 | const params = { 12 | image_file: baseImg, 13 | sky: SkyType.galaxy, 14 | }; 15 | novitaClient 16 | .replaceSky(params) 17 | .then((res) => { 18 | console.log("finished!", res); 19 | onFinish(res); 20 | }) 21 | .catch((err) => { 22 | console.error("error:", err); 23 | }); 24 | } 25 | 26 | replaceSky((imgs) => { 27 | console.log(imgs); 28 | }); 29 | -------------------------------------------------------------------------------- /examples/replacebg.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK } = require("novita-sdk"); 4 | const path = require("path"); 5 | const { convertImageToBase64 } = require("./utils.js"); 6 | 7 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 8 | 9 | async function replaceBg(onFinish) { 10 | const baseImg = await convertImageToBase64(path.join(__dirname, "test.png")); 11 | const params = { 12 | image_file: baseImg, 13 | prompt: "sunrise", 14 | }; 15 | novitaClient 16 | .replaceBackground(params) 17 | .then((res) => { 18 | console.log("finished!", res); 19 | onFinish(res); 20 | }) 21 | .catch((err) => { 22 | console.error("error:", err); 23 | }); 24 | } 25 | 26 | replaceBg((imgs) => { 27 | console.log(imgs); 28 | }); 29 | -------------------------------------------------------------------------------- /examples/restore_face.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK } = require("novita-sdk"); 4 | const path = require("path"); 5 | const { convertImageToBase64 } = require("./utils.js"); 6 | 7 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 8 | 9 | async function restoreFace(onFinish) { 10 | const baseImg = await convertImageToBase64(path.join(__dirname, "test.png")); 11 | const params = { 12 | image_file: baseImg, 13 | fidelity: 0.8, 14 | }; 15 | novitaClient 16 | .restoreFace(params) 17 | .then((res) => { 18 | console.log("finished!", res); 19 | onFinish(res); 20 | }) 21 | .catch((err) => { 22 | console.error("error:", err); 23 | }); 24 | } 25 | 26 | restoreFace((imgs) => { 27 | console.log(imgs); 28 | }); 29 | -------------------------------------------------------------------------------- /examples/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novitalabs/javascript-sdk/58c99a25bf36fbd16d97fed59d82cc6ebbb6a51b/examples/test.png -------------------------------------------------------------------------------- /examples/txt2img.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK, TaskStatus } = require("novita-sdk"); 4 | 5 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 6 | 7 | async function txt2img(onFinish) { 8 | const params = { 9 | request: { 10 | model_name: "majicmixRealistic_v7_134792.safetensors", 11 | prompt: "1girl,sweater,white background", 12 | negative_prompt: "(worst quality:2),(low quality:2),(normal quality:2),lowres,watermark,", 13 | width: 512, 14 | height: 768, 15 | sampler_name: "Euler a", 16 | guidance_scale: 7, 17 | steps: 20, 18 | image_num: 1, 19 | seed: -1, 20 | }, 21 | }; 22 | novitaClient 23 | .txt2Img(params) 24 | .then((res) => { 25 | if (res && res.task_id) { 26 | const timer = setInterval(() => { 27 | novitaClient 28 | .progress({ 29 | task_id: res.task_id, 30 | }) 31 | .then((progressRes) => { 32 | if (progressRes.task.status === TaskStatus.SUCCEED) { 33 | console.log("finished!", progressRes.images); 34 | clearInterval(timer); 35 | onFinish(progressRes.images); 36 | } 37 | if (progressRes.task.status === TaskStatus.FAILED) { 38 | console.warn("failed!", progressRes.task.reason); 39 | clearInterval(timer); 40 | } 41 | if (progressRes.task.status === TaskStatus.QUEUED) { 42 | console.log("queueing"); 43 | } 44 | }) 45 | .catch((err) => { 46 | console.error("progress error:", err); 47 | }); 48 | }, 1000); 49 | } 50 | }) 51 | .catch((err) => { 52 | console.error("txt2Img error:", err); 53 | }); 54 | } 55 | 56 | txt2img((imgs) => { 57 | console.log(imgs); 58 | }); 59 | -------------------------------------------------------------------------------- /examples/upscale.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const { NovitaSDK, TaskStatus } = require("novita-sdk"); 4 | const path = require("path"); 5 | const { convertImageToBase64 } = require("./utils.js"); 6 | 7 | const novitaClient = new NovitaSDK(process.env.NOVITA_API_KEY); 8 | 9 | async function upscale(onFinish) { 10 | const baseImg = await convertImageToBase64(path.join(__dirname, "test.png")); 11 | const params = { 12 | request: { 13 | model_name: "RealESRNet_x4plus", 14 | image_base64: baseImg, 15 | scale_factor: "2", 16 | }, 17 | }; 18 | novitaClient 19 | .upscale(params) 20 | .then((res) => { 21 | if (res && res.task_id) { 22 | const timer = setInterval(() => { 23 | novitaClient 24 | .progress({ 25 | task_id: res.task_id, 26 | }) 27 | .then((progressRes) => { 28 | if (progressRes.task.status === TaskStatus.SUCCEED) { 29 | console.log("finished!", progressRes.images); 30 | clearInterval(timer); 31 | } 32 | if (progressRes.task.status === TaskStatus.FAILED) { 33 | console.warn("failed!", progressRes.task.reason); 34 | clearInterval(timer); 35 | } 36 | if (progressRes.task.status === TaskStatus.QUEUED) { 37 | console.log("queueing"); 38 | } 39 | }) 40 | .catch((err) => { 41 | console.error("progress error:", err); 42 | }); 43 | }, 1000); 44 | } 45 | }) 46 | .catch((err) => { 47 | console.error("error:", err); 48 | }); 49 | } 50 | 51 | upscale((imgs) => { 52 | console.log(imgs); 53 | }); 54 | -------------------------------------------------------------------------------- /examples/utils.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | const fs = require("fs"); 4 | 5 | module.exports.convertImageToBase64 = (filePath) => { 6 | return new Promise((resolve, reject) => { 7 | // 异步读取文件 8 | fs.readFile(filePath, (err, data) => { 9 | if (err) { 10 | reject(err); 11 | } else { 12 | // 转换为 base64 编码 13 | const base64Image = Buffer.from(data).toString("base64"); 14 | resolve(base64Image); 15 | } 16 | }); 17 | }); 18 | }; 19 | -------------------------------------------------------------------------------- /examples/wan-t2v.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import { NovitaSDK, TaskStatus } from "novita-sdk"; 4 | 5 | const novitaClient = new NovitaSDK("your_api_key"); 6 | 7 | async function wanT2v(onFinish) { 8 | const params = { 9 | model_name: "wan-t2v", 10 | width: 480, 11 | height: 832, 12 | seed: -1, 13 | prompt: 14 | "A close up view of a glass sphere that has a zen garden within it. There is a small dwarf in the sphere who is raking the zen garden and creating patterns in the sand.", 15 | frames: 85, 16 | }; 17 | novitaClient 18 | .wanT2v(params) 19 | .then((res) => { 20 | if (res && res.task_id) { 21 | const timer = setInterval(() => { 22 | novitaClient 23 | .progress({ 24 | task_id: res.task_id, 25 | }) 26 | .then((progressRes) => { 27 | if (progressRes.task.status === TaskStatus.SUCCEED) { 28 | console.log("finished!", progressRes.videos); 29 | clearInterval(timer); 30 | onFinish(progressRes.videos); 31 | } 32 | if (progressRes.task.status === TaskStatus.FAILED) { 33 | console.warn("failed!", progressRes.task.reason); 34 | clearInterval(timer); 35 | } 36 | if (progressRes.task.status === TaskStatus.QUEUED) { 37 | console.log("queueing"); 38 | } 39 | }) 40 | .catch((err) => { 41 | console.error("progress error:", err); 42 | }); 43 | }, 1000); 44 | } 45 | }) 46 | .catch((err) => { 47 | console.error("wan-t2v request error:", err); 48 | }); 49 | } 50 | 51 | wanT2v((videos) => { 52 | console.log(videos); 53 | }); 54 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | export { NovitaSDK } from "./src/class"; 4 | 5 | export { NovitaError } from "./src/error"; 6 | 7 | export { 8 | ResponseCode, 9 | ProgressResponse, 10 | TaskStatus, 11 | APIErrReason, 12 | Txt2ImgRequest, 13 | Txt2ImgResponse, 14 | ControlnetUnit, 15 | Img2imgRequest, 16 | Img2imgResponse, 17 | GetModelsResponse, 18 | SyncConfig, 19 | MergeFaceRequest, 20 | MergeFaceResponse, 21 | RemoveBackgroundRequest, 22 | RemoveBackgroundResponse, 23 | ReplaceBackgroundRequest, 24 | ReplaceBackgroundResponse, 25 | CleanupRequest, 26 | CleanupResponse, 27 | InpaintingRequest, 28 | InpaintingResponse, 29 | RemoveTextRequest, 30 | RemoveTextResponse, 31 | HunyuanVideoFastRequest, 32 | HunyuanVideoFastResponse, 33 | WanT2vRequest, 34 | WanT2vResponse, 35 | Txt2VideoRequest, 36 | Txt2VideoResponse, 37 | Img2VideoResizeMode, 38 | Img2VideoModel, 39 | Img2VideoRequest, 40 | Img2VideoResponse, 41 | } from "./src/types"; 42 | 43 | export { ControlNetPreprocessor, ControlNetMode, ModelType } from "./src/enum"; 44 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | module.exports = { 4 | preset: "ts-jest", 5 | testEnvironment: "node", 6 | }; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "novita-sdk", 3 | "browser": "dist/index.js", 4 | "version": "3.1.3", 5 | "author": "", 6 | "license": "ISC", 7 | "keywords": [ 8 | "novita", 9 | "novita.ai", 10 | "novitalabs", 11 | "sdk", 12 | "stable diffusion api", 13 | "typescript", 14 | "es" 15 | ], 16 | "description": "Novita api sdk", 17 | "main": "dist/index", 18 | "files": [ 19 | "dist" 20 | ], 21 | "scripts": { 22 | "build": "rollup --config", 23 | "prepare": "husky install", 24 | "lint": "eslint .", 25 | "lint:fix": "eslint --fix .", 26 | "prettier": "prettier --write .", 27 | "test": "jest" 28 | }, 29 | "lint-staged": { 30 | "*": "npm run prettier", 31 | "*.{js,ts}": "npm run lint:fix" 32 | }, 33 | "devDependencies": { 34 | "@babel/core": "^7.22.9", 35 | "@babel/preset-env": "^7.22.9", 36 | "@commitlint/cli": "^18.4.3", 37 | "@commitlint/config-conventional": "^18.4.3", 38 | "@rollup/plugin-babel": "^6.0.3", 39 | "@rollup/plugin-commonjs": "^25.0.3", 40 | "@rollup/plugin-json": "^6.0.0", 41 | "@rollup/plugin-node-resolve": "^15.1.0", 42 | "@rollup/plugin-replace": "^5.0.4", 43 | "@rollup/plugin-typescript": "^11.1.2", 44 | "@types/node": "^20.8.9", 45 | "@typescript-eslint/eslint-plugin": "^6.12.0", 46 | "@typescript-eslint/parser": "^6.12.0", 47 | "eslint": "^8.46.0", 48 | "eslint-config-prettier": "^9.0.0", 49 | "eslint-config-standard-with-typescript": "^40.0.0", 50 | "eslint-plugin-import": "^2.29.0", 51 | "eslint-plugin-n": "^16.3.1", 52 | "eslint-plugin-prettier": "^5.0.1", 53 | "eslint-plugin-promise": "^6.1.1", 54 | "husky": "^8.0.0", 55 | "lint-staged": "^15.1.0", 56 | "prettier": "^3.1.0", 57 | "rollup": "^3.27.1", 58 | "rollup-plugin-polyfill-node": "^0.12.0", 59 | "tslib": "^2.6.2", 60 | "typescript": "^5.1.6" 61 | }, 62 | "dependencies": { 63 | "@types/jest": "^29.5.12", 64 | "axios": "^1.4.0", 65 | "dotenv": "^16.4.5", 66 | "jest": "^29.7.0", 67 | "ts-jest": "^29.1.5" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import resolve from "@rollup/plugin-node-resolve"; 4 | import babel from "@rollup/plugin-babel"; 5 | import commonjs from "@rollup/plugin-commonjs"; 6 | import typescript from "@rollup/plugin-typescript"; 7 | import nodePolyfills from "rollup-plugin-polyfill-node"; 8 | import json from "@rollup/plugin-json"; 9 | import replace from "@rollup/plugin-replace"; 10 | import pkg from "./package.json" assert { type: "json" }; 11 | 12 | export default { 13 | input: "index.ts", 14 | output: { 15 | name: "@novita/sdk", 16 | file: pkg.browser, 17 | format: "umd", 18 | sourcemap: true, 19 | globals: { 20 | axios: "axios", 21 | }, 22 | }, 23 | plugins: [ 24 | resolve(), 25 | commonjs(), 26 | typescript(), 27 | json(), 28 | nodePolyfills(), 29 | babel({ babelHelpers: "bundled" }), 30 | replace({ 31 | "process.env.VERSION": JSON.stringify(pkg.version), 32 | }), 33 | ], 34 | external: ["axios"], 35 | }; 36 | -------------------------------------------------------------------------------- /src/class.ts: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import axios from "axios"; 4 | import { 5 | RequestOpts, 6 | GetModelsQuery, 7 | GetModelsResponse, 8 | ProgressRequest, 9 | ProgressResponse, 10 | Txt2ImgRequest, 11 | Txt2ImgResponse, 12 | Img2ImgRequest, 13 | Img2ImgResponse, 14 | ResponseCode, 15 | RemoveBackgroundRequest, 16 | RemoveBackgroundResponse, 17 | ReplaceBackgroundRequest, 18 | ReplaceBackgroundResponse, 19 | CleanupRequest, 20 | CleanupResponse, 21 | MergeFaceRequest, 22 | MergeFaceResponse, 23 | RemoveTextRequest, 24 | RemoveTextResponse, 25 | Img2VideoRequest, 26 | Img2VideoResponse, 27 | UploadRequest, 28 | UploadResponse, 29 | HunyuanVideoFastRequest, 30 | HunyuanVideoFastResponse, 31 | WanT2vRequest, 32 | WanT2vResponse, 33 | Txt2VideoRequest, 34 | Txt2VideoResponse, 35 | InpaintingRequest, 36 | InpaintingResponse, 37 | } from "./types"; 38 | import { UPLOAD_URL } from "./enum"; 39 | import { NovitaError } from "./error"; 40 | 41 | export class NovitaSDK { 42 | protected key: string; 43 | protected BASE_URL: string; 44 | 45 | constructor(key: string) { 46 | this.key = key; 47 | this.BASE_URL = "https://api.novita.ai"; 48 | } 49 | 50 | setBaseUrl(url: string) { 51 | this.BASE_URL = url; 52 | } 53 | setNovitaKey(key: string) { 54 | this.key = key; 55 | } 56 | 57 | httpFetch({ 58 | url, 59 | method = "GET", 60 | data = undefined, 61 | query = undefined, 62 | opts = undefined, 63 | }: { 64 | url: string; 65 | method?: string; 66 | data?: any; 67 | query?: any; 68 | opts?: RequestOpts; 69 | }) { 70 | const fetchUrl = this.BASE_URL + url; 71 | const headers: HeadersInit = { 72 | "Content-Type": "application/json", 73 | "X-Novita-Source": opts?.source || `js-sdk-novita/${process.env.VERSION}`, 74 | "X-Api-Source": opts?.source || `js-sdk-novita/${process.env.VERSION}`, 75 | }; 76 | if (this.key) { 77 | headers["Authorization"] = this.key; 78 | } else { 79 | return Promise.reject(new NovitaError(-1, "Novita API key is required")); 80 | } 81 | return axios({ 82 | url: fetchUrl, 83 | method: method, 84 | headers: headers, 85 | data: data, 86 | params: query, 87 | signal: opts?.signal, 88 | }) 89 | .then((response) => { 90 | if (response.status !== ResponseCode.OK) { 91 | throw new NovitaError(response.status, response.data.message, response.data.reason, response.data.metadata); 92 | } 93 | return response.data; 94 | }) 95 | .catch((error) => { 96 | if (error instanceof NovitaError) { 97 | throw error; 98 | } 99 | const res = error.response; 100 | if (res) { 101 | throw new NovitaError(res.status, res.data.message, res.data.reason, res.data.metadata, error); 102 | } 103 | throw new NovitaError(ResponseCode.NETWORK, error.message, "", undefined, error); 104 | }); 105 | } 106 | 107 | private _apiRequest(url: string): (p: T, o?: RequestOpts) => Promise { 108 | return (params: T, opts?: any): Promise => { 109 | return this.httpFetch({ 110 | url: url, 111 | method: "POST", 112 | data: params, 113 | opts, 114 | }); 115 | }; 116 | } 117 | 118 | getModels(query?: GetModelsQuery) { 119 | return this.httpFetch({ 120 | url: "/v3/model", 121 | query: query, 122 | }).then((res: GetModelsResponse) => { 123 | return res; 124 | }); 125 | } 126 | 127 | txt2Img: (p: Txt2ImgRequest, opts?: any) => Promise = this._apiRequest< 128 | Txt2ImgRequest, 129 | Txt2ImgResponse 130 | >("/v3/async/txt2img"); 131 | 132 | txt2img = this.txt2Img; 133 | 134 | img2Img: (p: Img2ImgRequest, opts?: any) => Promise = this._apiRequest< 135 | Img2ImgRequest, 136 | Img2ImgResponse 137 | >("/v3/async/img2img"); 138 | 139 | img2img = this.img2Img; 140 | 141 | progress(params: ProgressRequest, opts?: RequestOpts): Promise { 142 | return this.httpFetch({ 143 | url: "/v3/async/task-result", 144 | method: "GET", 145 | query: params, 146 | opts, 147 | }).catch((error) => { 148 | if (error.metadata) { 149 | error.metadata.task_id = params.task_id; 150 | } else { 151 | error.metadata = { task_id: params.task_id }; 152 | } 153 | throw error; 154 | }); 155 | } 156 | 157 | cleanup: (params: CleanupRequest, opts?: any) => Promise = this._apiRequest< 158 | CleanupRequest, 159 | CleanupResponse 160 | >("/v3/cleanup"); 161 | 162 | removeBackground: (params: RemoveBackgroundRequest, opts?: any) => Promise = 163 | this._apiRequest("/v3/remove-background"); 164 | 165 | replaceBackground: (params: ReplaceBackgroundRequest, opts?: any) => Promise = 166 | this._apiRequest("/v3/replace-background"); 167 | 168 | mergeFace: (p: MergeFaceRequest, opts?: any) => Promise = this._apiRequest< 169 | MergeFaceRequest, 170 | MergeFaceResponse 171 | >("/v3/merge-face"); 172 | 173 | removeText: (p: RemoveTextRequest, opts?: any) => Promise = this._apiRequest< 174 | RemoveTextRequest, 175 | RemoveTextResponse 176 | >("/v3/remove-text"); 177 | 178 | hunyuanVideoFast: (p: HunyuanVideoFastRequest, opts?: any) => Promise = this._apiRequest< 179 | HunyuanVideoFastRequest, 180 | HunyuanVideoFastResponse 181 | >("/v3/async/hunyuan-video-fast"); 182 | 183 | wanT2v: (p: WanT2vRequest, opts?: any) => Promise = this._apiRequest( 184 | "/v3/async/wan-t2v", 185 | ); 186 | 187 | txt2Video: (p: Txt2VideoRequest, opts?: any) => Promise = this._apiRequest< 188 | Txt2VideoRequest, 189 | Txt2VideoResponse 190 | >("/v3/async/txt2video"); 191 | 192 | img2Video: (p: Img2VideoRequest, opts?: any) => Promise = this._apiRequest< 193 | Img2VideoRequest, 194 | Img2VideoResponse 195 | >("/v3/async/img2video"); 196 | 197 | upload: (p: UploadRequest, opts?: RequestOpts) => Promise = ( 198 | p: UploadRequest, 199 | opts?: RequestOpts, 200 | ) => { 201 | return axios({ 202 | url: `${UPLOAD_URL}/${p.type}`, 203 | method: "PUT", 204 | data: p.data, 205 | signal: opts?.signal, 206 | }) 207 | .then((response) => { 208 | if (response.status !== ResponseCode.OK) { 209 | throw new NovitaError(response.status, response.data.message, response.data.reason, response.data.metadata); 210 | } 211 | return response.data; 212 | }) 213 | .catch((error) => { 214 | if (error instanceof NovitaError) { 215 | throw error; 216 | } 217 | const res = error.response; 218 | if (res) { 219 | throw new NovitaError(res.status, res.data.message, res.data.reason, res.data.metadata, error); 220 | } 221 | throw new NovitaError(ResponseCode.NETWORK, error.message, "", undefined, error); 222 | }); 223 | }; 224 | 225 | inpainting: (p: InpaintingRequest, opts?: any) => Promise = this._apiRequest< 226 | InpaintingRequest, 227 | InpaintingResponse 228 | >("/v3/async/inpainting"); 229 | } 230 | -------------------------------------------------------------------------------- /src/enum.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Novita 3 | * 4 | * @format 5 | */ 6 | 7 | export const UPLOAD_URL = "https://assets.novitai.com"; 8 | 9 | export const ERROR_BAD_REQUEST = "Bad Request"; 10 | export const ERROR_UNAUTHORIZED = "Unauthorized"; 11 | export const ERROR_FORBIDDEN = "Forbidden"; 12 | export const ERROR_NOT_FOUND = "Not Found"; 13 | export const ERROR_METHOD_NOT_ALLOWED = "Method Not Allowed"; 14 | export const ERROR_SERVER_ERROR = "Internal Server Error"; 15 | export const ERROR_GENERATE_IMG_FAILED = "Generate Image Failed"; 16 | export const ERROR_GENERATE_VIDEO_FAILED = "Generate Video Failed"; 17 | 18 | export const ControlNetPreprocessor = { 19 | SCRIBBLE_HED: "scribble_hed", 20 | SOFTEDGE_HED: "softedge_hed", 21 | SCRIBBLE_HEDSAFE: "scribble_hedsafe", 22 | SOFTEDGE_HEDSAFE: "softedge_hedsafe", 23 | DEPTH_MIDAS: "depth_midas", 24 | MLSD: "mlsd", 25 | OPENPOSE: "openpose", 26 | OPENPOSE_FACE: "openpose_face", 27 | OPENPOSE_FACEONLY: "openpose_faceonly", 28 | OPENPOSE_FULL: "openpose_full", 29 | OPENPOSE_HAND: "openpose_hand", 30 | dwpose: "dwpose", 31 | SCRIBBLE_PIDINET: "scribble_pidinet", 32 | SOFTEDGE_PIDINET: "softedge_pidinet", 33 | SCRIBBLE_PIDSAFE: "scribble_pidsafe", 34 | SOFTEDGE_PIDSAFE: "softedge_pidsafe", 35 | NORMAL_BAE: "normal_bae", 36 | LINEART_COARSE: "lineart_coarse", 37 | LINEART_REALISTIC: "lineart_realistic", 38 | LINEART_ANIME: "lineart_anime", 39 | LINEART: "lineart", 40 | DEPTH_ZOE: "depth_zoe", 41 | SHUFFLE: "shuffle", 42 | MEDIAPIPE_FACE: "mediapipe_face", 43 | CANNY: "canny", 44 | DEPTH: "depth", 45 | DEPTH_LERES: "depth_leres", 46 | "DEPTH_LERES++": "depth_leres++", 47 | } as const; 48 | 49 | export const ControlNetMode = { 50 | BALANCED: 0, 51 | PROMPT_IMPORTANCE: 1, 52 | CONTROLNET_IMPORTANCE: 2, 53 | }; 54 | 55 | export const ModelType = { 56 | CHECKPOINT: "checkpoint", 57 | LORA: "lora", 58 | VAE: "vae", 59 | CONTROLNET: "controlnet", 60 | TEXT_INVERSION: "text_inversion", 61 | }; 62 | -------------------------------------------------------------------------------- /src/error.ts: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import axios from "axios"; 4 | import { ResponseCode } from "./types"; 5 | 6 | export class NovitaError { 7 | code: number = 0; 8 | msg: string = ""; 9 | stack?: string; 10 | reason: string = ""; 11 | metadata?: any; 12 | error?: Error; 13 | 14 | constructor(code: number, msg: string, reason?: string, metadata?: any, error?: Error) { 15 | this.msg = msg; 16 | this.code = code; 17 | this.reason = reason || ""; 18 | this.stack = new Error().stack; 19 | this.metadata = metadata; 20 | this.error = error; 21 | if (error instanceof axios.CanceledError) { 22 | this.code = ResponseCode.CANCELED; 23 | this.msg = "Task canceled"; 24 | } 25 | } 26 | 27 | get isCanceled() { 28 | return this.error instanceof axios.CanceledError; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Novita 3 | * 4 | * Typescript type definitions for Novita 5 | * 6 | * @format 7 | */ 8 | 9 | import { ControlNetPreprocessor } from "./enum"; 10 | 11 | export type NovitaKey = string | undefined; 12 | 13 | export interface NovitaConfig { 14 | BASE_URL: string; 15 | key: NovitaKey; 16 | } 17 | 18 | // Response Code Enum for V3 API 19 | export enum ResponseCode { 20 | NETWORK = -10, 21 | CANCELED = -11, 22 | OK = 200, 23 | TOO_MANY_REQ = 429, 24 | INTERNAL_ERR = 500, 25 | REQUEST_INVALID = 400, 26 | } 27 | 28 | export enum APIErrReason { 29 | ANONYMOUS_ACCESS_QUOTA_EXCEEDS = "ANONYMOUS_ACCESS_QUOTA_EXCEEDS", 30 | BILLING_FAILED = "BILLING_FAILED", 31 | BILLING_AUTH_FAILED = "BILLING_AUTH_FAILED", 32 | BALANCE_NOT_ENOUGH = "BILLING_BALANCE_NOT_ENOUGH", 33 | INVALID_REQUEST_BODY = "INVALID_REQUEST_BODY", 34 | ERR_NETWORK = "ERR_NETWORK", 35 | } 36 | 37 | // getModels dependency status 38 | export enum ModelStatus { 39 | READY = 1, 40 | UNREADY = 0, 41 | } 42 | 43 | export type RequestOpts = { 44 | signal?: AbortSignal; 45 | source?: string; 46 | }; 47 | 48 | export type Model = { 49 | id: number; 50 | name: string; 51 | hash_sha256: string; 52 | sd_name: string; 53 | type: { name: string; display_name: string }; 54 | categories: string[]; 55 | status: ModelStatus; 56 | download_url: string; 57 | tags: string[]; 58 | cover_url: string; 59 | source: string; 60 | base_model: string; 61 | base_model_type: string; 62 | download_url_ttl: number; 63 | sd_name_in_api: string; 64 | }; 65 | 66 | export type GetModelsQuery = { 67 | filter?: { 68 | visibility?: "public" | "private"; 69 | source?: "civitai" | "training" | "uploading"; 70 | types?: "checkpoint" | "lora" | "vae" | "controlnet" | "upscaler" | "textualinversion"; 71 | is_sdxl?: boolean; 72 | query?: string; 73 | is_inpainting?: boolean; 74 | }; 75 | pagination: { 76 | limit?: number; 77 | cursor?: string; 78 | }; 79 | }; 80 | 81 | export type GetModelsResponse = { 82 | models: Array; 83 | pagination: { next_cursor: string }; 84 | }; 85 | 86 | export type Lora = { 87 | sd_name: string; 88 | weight: number; 89 | }; 90 | 91 | export type Refiner = { 92 | checkpoint: string; 93 | switch_at: number; 94 | }; 95 | 96 | type ControlNetPreprocessorValues = (typeof ControlNetPreprocessor)[keyof typeof ControlNetPreprocessor]; 97 | 98 | export type ControlnetUnit = { 99 | model: string; 100 | weight: number | undefined; 101 | control_mode: 0 | 1 | 2; 102 | module: ControlNetPreprocessorValues; 103 | input_image: string; 104 | mask?: string; 105 | resize_mode?: number; 106 | lowvram?: boolean; 107 | processor_res?: number; 108 | threshold_a?: number; 109 | threshold_b?: number; 110 | guidance_start?: number; 111 | guidance_end?: number; 112 | pixel_perfect?: boolean; 113 | }; 114 | 115 | type WebhookSettings = { 116 | url: string; 117 | test_mode: { 118 | enabled: boolean; 119 | return_task_status: "TASK_STATUS_SUCCEED" | "TASK_STATUS_FAILED"; 120 | }; 121 | }; 122 | 123 | type txt2imgExtra = { 124 | response_image_type?: "png" | "webp" | "jpeg"; 125 | webhook?: WebhookSettings; 126 | enable_nsfw_detection?: boolean; 127 | nsfw_detection_level?: number; 128 | custom_storage?: { 129 | aws_s3?: { 130 | region: string; 131 | bucket: string; 132 | path: string; 133 | save_to_path_directly?: boolean; 134 | [key: string]: string | number | boolean | undefined; 135 | }; 136 | }; 137 | enterprise_plan?: { 138 | enabled: boolean; 139 | }; 140 | }; 141 | type img2imgExtra = txt2imgExtra; 142 | 143 | export type Txt2ImgRequest = { 144 | extra?: txt2imgExtra & { 145 | [key: string]: string | number | boolean | txt2imgExtra[keyof txt2imgExtra]; 146 | }; 147 | request: { 148 | model_name: string; 149 | prompt: string; 150 | negative_prompt: string; 151 | sd_vae?: string; 152 | loras?: { model_name: string; strength: number }[]; 153 | embeddings?: { model_name: string }[]; 154 | hires_fix?: { 155 | target_width: number; 156 | target_height: number; 157 | strength: number; 158 | upscaler?: "RealESRGAN_x4plus_anime_6B" | "RealESRNet_x4plus" | "Latent"; 159 | }; 160 | refiner?: { 161 | switch_at: number; 162 | }; 163 | width: number; 164 | height: number; 165 | image_num: number; 166 | steps: number; 167 | seed: number; 168 | clip_skip?: number; 169 | guidance_scale: number; 170 | sampler_name: string; 171 | }; 172 | }; 173 | 174 | export type Txt2ImgResponse = AsyncResponse; 175 | 176 | export type SyncConfig = { 177 | // wait time between each request, default 1000ms 178 | interval?: number; 179 | // img result, base64 or url, default base64 180 | img_type?: "base64" | "url"; 181 | }; 182 | 183 | export type Img2imgRequest = { 184 | extra?: img2imgExtra & { 185 | [key: string]: string | number | boolean | img2imgExtra[keyof img2imgExtra]; 186 | }; 187 | request: { 188 | model_name: string; 189 | image_base64: string; 190 | prompt: string; 191 | negative_prompt?: string; 192 | sd_vae?: string; 193 | controlnet?: { 194 | units: { 195 | model_name: string; 196 | image_base64: string; 197 | strength: number; 198 | preprocessor?: ControlNetPreprocessorValues; 199 | guidance_start?: number; 200 | guidance_end?: number; 201 | }[]; 202 | }; 203 | ip_adapters?: { 204 | model_name: string; 205 | image_base64: string; 206 | strength: number; 207 | }[]; 208 | loras?: { model_name: string; strength: number }[]; 209 | embeddings?: { model_name: string }[]; 210 | width: number; 211 | height: number; 212 | image_num: number; 213 | steps: number; 214 | seed: number; 215 | clip_skip?: number; 216 | guidance_scale: number; 217 | sampler_name: string; 218 | strength?: number; 219 | }; 220 | }; 221 | export type Img2ImgRequest = Img2imgRequest; 222 | export type Img2imgResponse = AsyncResponse; 223 | export type Img2ImgResponse = Img2imgResponse; 224 | 225 | export type InpaintingRequest = { 226 | extra?: img2imgExtra & { 227 | [key: string]: string | number | boolean | img2imgExtra[keyof img2imgExtra]; 228 | }; 229 | request: { 230 | model_name: string; 231 | image_base64: string; 232 | mask_image_base64: string; 233 | mask_blur?: number; 234 | prompt: string; 235 | negative_prompt?: string; 236 | sd_vae?: string; 237 | loras?: { model_name: string; strength: number }[]; 238 | embeddings?: { model_name: string }[]; 239 | image_num: number; 240 | steps: number; 241 | seed: number; 242 | clip_skip?: number; 243 | guidance_scale: number; 244 | sampler_name: string; 245 | strength?: number; 246 | inpainting_full_res?: 0 | 1; 247 | inpainting_full_res_padding?: number; 248 | inpainting_mask_invert?: 0 | 1; 249 | initial_noise_multiplier?: number; 250 | }; 251 | }; 252 | export type InpaintingResponse = AsyncResponse; 253 | 254 | export enum Upscalers { 255 | REALESRGAN_X4PLUS_ANIME_6B = "RealESRGAN_x4plus_anime_6B", 256 | REALESRNET_X4PLUS = "RealESRNet_x4plus", 257 | "4X-ULTRASHARP" = "4x-UltraSharp", 258 | } 259 | 260 | type GenImgExtraParams = { 261 | response_image_type?: "png" | "webp" | "jpeg"; 262 | enterprise_plan?: { 263 | enabled: boolean; 264 | }; 265 | }; 266 | 267 | type GenImgExtraPayload = { 268 | extra?: GenImgExtraParams & { 269 | [key: string]: string | number | boolean | GenImgExtraParams[keyof GenImgExtraParams]; 270 | }; 271 | }; 272 | 273 | export type UpscaleRequest = { 274 | request: { 275 | model_name: Upscalers; 276 | image_base64: string; 277 | scale_factor: number; 278 | }; 279 | } & GenImgExtraPayload; 280 | 281 | export type UpscaleResponse = AsyncResponse; 282 | 283 | export type ProgressRequest = { 284 | task_id: string; 285 | }; 286 | 287 | type FailedResponse = { 288 | code?: ResponseCode; 289 | reason?: string; 290 | message?: string; 291 | metadata?: { [key: string]: string }; 292 | }; 293 | type GenImgResponse = { 294 | image_file: string; 295 | image_type: string; 296 | }; 297 | type GenImgResponseUrl = { 298 | image_url: string; 299 | image_url_ttl: string; 300 | image_type: string; 301 | }; 302 | type AsyncResponse = { 303 | task_id: string; 304 | } & FailedResponse; 305 | 306 | export enum TaskStatus { 307 | UNKNOWN = "TASK_STATUS_UNKNOWN", 308 | QUEUED = "TASK_STATUS_QUEUED", // 排队 309 | SUCCEED = "TASK_STATUS_SUCCEED", // 成功 310 | FAILED = "TASK_STATUS_FAILED", // 失败 311 | PROCESSING = "TASK_STATUS_PROCESSING", // 处理中 312 | } 313 | type Task = { 314 | eta: number; 315 | progress_percent: number; 316 | task_type: string; 317 | task_id: string; 318 | status: TaskStatus; 319 | reason?: string; 320 | }; 321 | 322 | export type ProgressResponse = { 323 | task: Task; 324 | images?: { 325 | image_url: string; 326 | image_type: string; 327 | image_url_ttl: number; 328 | }[]; 329 | videos?: { 330 | video_url: string; 331 | video_url_ttl: string; 332 | video_type: string; 333 | }[]; 334 | }; 335 | 336 | export type CleanupRequest = { 337 | image_file: string; 338 | mask_file: string; 339 | } & GenImgExtraPayload; 340 | export type CleanupResponse = GenImgResponse & FailedResponse; 341 | 342 | export type RemoveBackgroundRequest = { 343 | image_file: string; 344 | } & GenImgExtraPayload; 345 | export type RemoveBackgroundResponse = GenImgResponse & FailedResponse; 346 | 347 | export type ReplaceBackgroundRequest = { 348 | image_file: string; 349 | prompt: string; 350 | } & GenImgExtraPayload; 351 | export type ReplaceBackgroundResponse = GenImgResponse & FailedResponse; 352 | 353 | export type MergeFaceRequest = { 354 | face_image_file: string; 355 | image_file: string; 356 | } & GenImgExtraPayload; 357 | export type MergeFaceResponse = GenImgResponse & FailedResponse; 358 | 359 | export type RemoveTextRequest = { 360 | image_file: string; 361 | } & GenImgExtraPayload; 362 | export type RemoveTextResponse = GenImgResponse & FailedResponse; 363 | 364 | export type LcmTxt2ImgRequest = { 365 | prompt: string; 366 | height: number; 367 | width: number; 368 | image_num: number; 369 | steps: number; 370 | guidance_scale: number; 371 | } & GenImgExtraPayload; 372 | export type LcmTxt2ImgResponse = { 373 | images: GenImgResponse[]; 374 | } & FailedResponse; 375 | 376 | export type LcmImg2ImgRequest = { 377 | /** 378 | * clip skip, must above 0 379 | */ 380 | clip_skip?: number; 381 | /** 382 | * Textual Inversion options. 383 | */ 384 | embeddings?: { model_name: string }[]; 385 | /** 386 | * This setting says how close the model will listen to your prompt. Range: [2.0, 14.0] 387 | */ 388 | guidance_scale: number; 389 | /** 390 | * Image numbers. Range: [1, 16] 391 | */ 392 | image_num: number; 393 | /** 394 | * The base64 of input image, with a maximum resolution of 2048 * 2048 and a max file size 395 | * of 30 Mb, the returned image will be the same with size of input images. 396 | */ 397 | input_image: string; 398 | /** 399 | * Info of lora, 3 loras supported at most. 400 | */ 401 | loras?: { model_name: string; strength: string }[]; 402 | /** 403 | * The results you will get using a prompt might different for different models of Stable 404 | * Diffusion. You can call the https://docs.novita.ai/models-api/query-model endpoint to 405 | * retrieve the `sd_name_in_api` field as the `model_name`. 406 | */ 407 | model_name: string; 408 | /** 409 | * Negtive prompt word, divided by ',', you can also add embedding (textual inversion) 410 | * models like `badhandv4_16755`. 411 | */ 412 | negative_prompt: string; 413 | /** 414 | * Positive prompt word for the doodle, divided by ",",. Range: [1, 1024] 415 | */ 416 | prompt: string; 417 | /** 418 | * VAE(Variational Auto Encoder),sd_vae can be access in endpoint 419 | * https://docs.novita.ai/models-api/query-model with query params 'filter.types=vae', like 420 | * 'sd_name': 'customVAE.safetensors' 421 | */ 422 | sd_vae: string; 423 | /** 424 | * seed of request. 425 | */ 426 | seed: number; 427 | /** 428 | * Iterations of the image creation process. Range: [1, 8] 429 | */ 430 | steps: number; 431 | strength?: number; 432 | } & GenImgExtraPayload; 433 | 434 | export type LcmImg2ImgResponse = { 435 | images: GenImgResponseUrl[]; 436 | } & FailedResponse; 437 | 438 | export enum SkyType { 439 | bluesky = "bluesky", 440 | sunset = "sunset", 441 | sunrise = "sunrise", 442 | galaxy = "galaxy", 443 | } 444 | export type ReplaceSkyRequest = { 445 | image_file: string; 446 | sky: SkyType; 447 | } & GenImgExtraPayload; 448 | export type ReplaceSkyResponse = GenImgResponse & FailedResponse; 449 | 450 | export type ReplaceObjectRequest = { 451 | image_file: string; 452 | prompt: string; 453 | negative_prompt: string; 454 | object_prompt: string; 455 | } & GenImgExtraPayload; 456 | export type ReplaceObjectResponse = AsyncResponse; 457 | 458 | type GenVideoExtraParams = { 459 | response_video_type?: "mp4" | "gif"; 460 | enterprise_plan?: { 461 | enabled: boolean; 462 | }; 463 | }; 464 | 465 | type GenVideoExtraPayload = { 466 | extra?: GenVideoExtraParams & { 467 | [key: string]: string | number | boolean | GenVideoExtraParams[keyof GenVideoExtraParams]; 468 | }; 469 | }; 470 | 471 | export type HunyuanVideoFastRequest = { 472 | model_name: string; 473 | width: number; 474 | height: number; 475 | seed: number; 476 | steps: number; 477 | prompt: string; 478 | frames: number; 479 | }; 480 | export type HunyuanVideoFastResponse = AsyncResponse; 481 | 482 | export type WanT2vRequest = { 483 | model_name: string; 484 | width: number; 485 | height: number; 486 | seed: number; 487 | prompt: string; 488 | frames: number; 489 | }; 490 | export type WanT2vResponse = AsyncResponse; 491 | 492 | export type Txt2VideoPrompt = { 493 | prompt: string; 494 | frames: number; 495 | }; 496 | export type Txt2VideoRequest = { 497 | model_name: string; 498 | width: number; // 256~1024 499 | height: number; // 256~1024 500 | seed: number; 501 | steps: number; 502 | prompts: Txt2VideoPrompt[]; 503 | negative_prompt?: string; 504 | guidance_scale: number; // 1~30, 7.5 505 | } & GenVideoExtraPayload; 506 | export type Txt2VideoResponse = AsyncResponse; 507 | 508 | export enum Img2VideoResizeMode { 509 | ORIGINAL_RESOLUTION = "ORIGINAL_RESOLUTION", 510 | CROP_TO_ASPECT_RATIO = "CROP_TO_ASPECT_RATIO", 511 | } 512 | export enum Img2VideoModel { 513 | SVD_XT = "SVD-XT", 514 | SVD = "SVD", 515 | } 516 | export type Img2VideoRequest = { 517 | model_name: Img2VideoModel; 518 | image_file: string; 519 | frames_num: number; 520 | frames_per_second: number; 521 | seed: number; 522 | image_file_resize_mode: Img2VideoResizeMode; 523 | steps: number; 524 | motion_bucket_id?: number; // 1~255 525 | cond_aug?: number; // 0~1 526 | } & GenVideoExtraPayload; 527 | export type Img2VideoResponse = AsyncResponse; 528 | 529 | export type UploadRequest = { 530 | type: "image" | "video"; 531 | data: Blob; 532 | }; 533 | export type UploadResponse = { 534 | assets_id: string; 535 | }; 536 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import axios from "axios"; 4 | import { Lora } from "./types"; 5 | 6 | export function isNodeEnvironment() { 7 | return typeof window === "undefined"; 8 | } 9 | 10 | export function generateLoraString(params: Array | undefined) { 11 | if (!Array.isArray(params) || params.length === 0) { 12 | return []; 13 | } 14 | return params.map((item) => { 15 | return ``; 16 | }); 17 | } 18 | 19 | export function addLoraPrompt(array: string[], prompt: string) { 20 | if (!Array.isArray(array) || array.length === 0) { 21 | return prompt; 22 | } 23 | array.forEach((str) => { 24 | if (!prompt.includes(str)) { 25 | prompt = prompt + `, ${str}`; 26 | } 27 | }); 28 | return prompt; 29 | } 30 | 31 | export function readImgtoBase64(url: string): Promise { 32 | return axios.get(url, { responseType: "arraybuffer" }).then((response) => { 33 | if (isNodeEnvironment()) { 34 | const buffer = Buffer.from(response.data); 35 | return `data:${response.headers["content-type"]};base64,${buffer.toString("base64")}`; 36 | } else { 37 | // For browsers 38 | const base64 = btoa(new Uint8Array(response.data).reduce((data, byte) => data + String.fromCharCode(byte), "")); 39 | return `data:${response.headers["content-type"]};base64,${base64}`; 40 | } 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /test/assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novitalabs/javascript-sdk/58c99a25bf36fbd16d97fed59d82cc6ebbb6a51b/test/assets/.DS_Store -------------------------------------------------------------------------------- /test/assets/doodle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novitalabs/javascript-sdk/58c99a25bf36fbd16d97fed59d82cc6ebbb6a51b/test/assets/doodle.png -------------------------------------------------------------------------------- /test/assets/face1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novitalabs/javascript-sdk/58c99a25bf36fbd16d97fed59d82cc6ebbb6a51b/test/assets/face1.png -------------------------------------------------------------------------------- /test/assets/face2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novitalabs/javascript-sdk/58c99a25bf36fbd16d97fed59d82cc6ebbb6a51b/test/assets/face2.png -------------------------------------------------------------------------------- /test/assets/mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novitalabs/javascript-sdk/58c99a25bf36fbd16d97fed59d82cc6ebbb6a51b/test/assets/mask.png -------------------------------------------------------------------------------- /test/assets/pose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novitalabs/javascript-sdk/58c99a25bf36fbd16d97fed59d82cc6ebbb6a51b/test/assets/pose.png -------------------------------------------------------------------------------- /test/assets/sample.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/novitalabs/javascript-sdk/58c99a25bf36fbd16d97fed59d82cc6ebbb6a51b/test/assets/sample.jpeg -------------------------------------------------------------------------------- /test/test_group_1.test.ts: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | import { NovitaSDK } from "../src/class" 3 | import { 4 | Txt2ImgRequest, 5 | Img2ImgRequest, 6 | CleanupRequest, 7 | RemoveBackgroundRequest, 8 | ReplaceBackgroundRequest, 9 | } from "../src/types" 10 | import { pollTaskStatus, fileToBase64 } from "./utils" 11 | import path from 'path' 12 | 13 | dotenv.config() 14 | 15 | const novitaClient = new NovitaSDK(process.env.API_KEY || "") 16 | 17 | const testImageBase64 = fileToBase64(path.resolve(__dirname, "./assets/sample.jpeg")) 18 | const maskImageBase64 = fileToBase64(path.resolve(__dirname, "./assets/mask.png")) 19 | 20 | describe("Group 1", () => { 21 | it("should run txt2img", async () => { 22 | const reqBody: Txt2ImgRequest = { 23 | request: { 24 | model_name: "sd_xl_base_1.0.safetensors", 25 | prompt: "Glowing jellyfish floating through a foggy forest at twilight", 26 | negative_prompt: "3d render, smooth,plastic, blurry, grainy, low-resolution,anime, deep-fried, oversaturated", 27 | width: 512, 28 | height: 512, 29 | sampler_name: "DPM++ 2M Karras", 30 | guidance_scale: 7, 31 | steps: 20, 32 | image_num: 1, 33 | seed: -1, 34 | } 35 | } 36 | const res = await novitaClient.txt2Img(reqBody) 37 | expect(res).toHaveProperty("task_id") 38 | const taskId = res.task_id 39 | 40 | const taskResult = await pollTaskStatus(taskId) 41 | expect(taskResult).toHaveProperty("images") 42 | }, 30000); 43 | 44 | it("should run img2Img", async () => { 45 | const reqBody: Img2ImgRequest = { 46 | request: { 47 | model_name: "sd_xl_base_1.0.safetensors", 48 | image_base64: testImageBase64, 49 | image_num: 1, 50 | seed: -1, 51 | prompt: "A futuristic cityscape", 52 | negative_prompt: "old, vintage, blurry", 53 | width: 768, 54 | height: 768, 55 | steps: 30, 56 | sampler_name: "DPM++ 2M Karras", 57 | guidance_scale: 7.5, 58 | strength: 0.75, 59 | } 60 | } 61 | 62 | const res = await novitaClient.img2Img(reqBody) 63 | expect(res).toHaveProperty("task_id") 64 | 65 | const taskResult = await pollTaskStatus(res.task_id) 66 | expect(taskResult).toHaveProperty("images") 67 | }, 30000); 68 | 69 | it("should run cleanup", async () => { 70 | const reqBody: CleanupRequest = { 71 | image_file: testImageBase64, 72 | mask_file: maskImageBase64, 73 | } 74 | 75 | const res = await novitaClient.cleanup(reqBody) 76 | expect(res).toHaveProperty("image_file") 77 | }, 60000); 78 | 79 | it("should run removeBackground", async () => { 80 | const reqBody: RemoveBackgroundRequest = { 81 | image_file: testImageBase64, 82 | } 83 | 84 | const res = await novitaClient.removeBackground(reqBody) 85 | expect(res).toHaveProperty("image_file") 86 | }, 60000); 87 | 88 | it("should run replaceBackground", async () => { 89 | const reqBody: ReplaceBackgroundRequest = { 90 | image_file: testImageBase64, 91 | prompt: "A serene beach at sunset", 92 | } 93 | 94 | const res = await novitaClient.replaceBackground(reqBody) 95 | expect(res).toHaveProperty("image_file") 96 | }, 60000); 97 | }); -------------------------------------------------------------------------------- /test/test_group_2.test.ts: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | import { NovitaSDK } from "../src/class" 3 | import { 4 | MergeFaceRequest, 5 | RemoveTextRequest, 6 | Txt2VideoRequest, 7 | Img2VideoRequest, 8 | Img2VideoResizeMode, 9 | Img2VideoModel, 10 | InpaintingRequest, 11 | } from "../src/types" 12 | import { fileToBase64, pollTaskStatus } from "./utils" 13 | import path from 'path' 14 | 15 | dotenv.config() 16 | 17 | const novitaClient = new NovitaSDK(process.env.API_KEY || "") 18 | 19 | const testImageBase64 = fileToBase64(path.resolve(__dirname, "./assets/sample.jpeg")) 20 | const face1ImageBase64 = fileToBase64(path.resolve(__dirname, "./assets/face1.png")) 21 | const face2ImageBase64 = fileToBase64(path.resolve(__dirname, "./assets/face2.png")) 22 | const img2VideoImageBase64 = fileToBase64(path.resolve(__dirname, "./assets/face1.png")) 23 | 24 | describe("Group 2", () => { 25 | 26 | it("should run mergeFace", async () => { 27 | const reqBody: MergeFaceRequest = { 28 | face_image_file: face1ImageBase64, 29 | image_file: face2ImageBase64, 30 | } 31 | const res = await novitaClient.mergeFace(reqBody) 32 | expect(res).toHaveProperty("image_file") 33 | }, 60000); 34 | 35 | it("should run removeText", async () => { 36 | const reqBody: RemoveTextRequest = { 37 | image_file: testImageBase64 38 | } 39 | 40 | const res = await novitaClient.removeText(reqBody) 41 | expect(res).toHaveProperty("image_file") 42 | }, 60000); 43 | 44 | it("should run txt2Video", async () => { 45 | const reqBody: Txt2VideoRequest = { 46 | model_name: "darkSushiMixMix_225D_64380.safetensors", 47 | width: 640, 48 | height: 480, 49 | seed: -1, 50 | steps: 20, 51 | prompts: [ 52 | { 53 | prompt: "A girl, baby, portrait, 5 years old", 54 | frames: 16, 55 | }, 56 | { 57 | prompt: "A girl, child, portrait, 10 years old", 58 | frames: 16, 59 | }, 60 | { 61 | prompt: "A girl, teen, portrait, 20 years old", 62 | frames: 16, 63 | }, 64 | { 65 | prompt: "A girl, woman, portrait, 30 years old", 66 | frames: 16, 67 | }, 68 | { 69 | prompt: "A girl, woman, portrait, 50 years old", 70 | frames: 16, 71 | }, 72 | { 73 | prompt: "A girl, old woman, portrait, 70 years old", 74 | frames: 16, 75 | }, 76 | ], 77 | guidance_scale: 10, 78 | } 79 | 80 | const res = await novitaClient.txt2Video(reqBody) 81 | expect(res).toHaveProperty("task_id") 82 | }, 120000); 83 | 84 | it("should run img2Video", async () => { 85 | const reqBody: Img2VideoRequest = { 86 | model_name: Img2VideoModel.SVD, 87 | image_file: img2VideoImageBase64, 88 | frames_num: 14, 89 | frames_per_second: 6, 90 | seed: -1, 91 | image_file_resize_mode: Img2VideoResizeMode.ORIGINAL_RESOLUTION, 92 | steps: 20, 93 | } 94 | 95 | const res = await novitaClient.img2Video(reqBody) 96 | expect(res).toHaveProperty("task_id") 97 | }, 120000); 98 | 99 | it("should run inpainting", async () => { 100 | const reqBody: InpaintingRequest = { 101 | request: { 102 | clip_skip: 3, 103 | embeddings: [{ 104 | model_name: "OverallDetail_74591.pt" 105 | }], 106 | guidance_scale: 7, 107 | image_base64: fileToBase64(path.resolve(__dirname, "./assets/sample.jpeg")), 108 | image_num: 1, 109 | initial_noise_multiplier: 0.14, 110 | inpainting_full_res: 1, 111 | inpainting_full_res_padding: 10, 112 | inpainting_mask_invert: 0, 113 | loras: [{model_name: "gender_slider_v1_87782.safetensors", strength: 0.7}], 114 | model_name: "realisticVisionV51_v51VAE-inpainting_94324.safetensors", 115 | mask_blur: 5, 116 | mask_image_base64: fileToBase64(path.resolve(__dirname, "./assets/mask.png")), 117 | negative_prompt: "cat", 118 | prompt: "a cute dog", 119 | sampler_name: "DPM++ 2S a", 120 | sd_vae: "klF8Anime2VAE_klF8Anime2VAE_207314.safetensors", 121 | seed: -1, 122 | steps: 20, 123 | strength: 1, 124 | } 125 | } 126 | const res = await novitaClient.inpainting(reqBody) 127 | expect(res).toHaveProperty("task_id") 128 | const taskId = res.task_id 129 | 130 | const taskResult = await pollTaskStatus(taskId) 131 | expect(taskResult).toHaveProperty("images") 132 | }, 30000); 133 | }); 134 | -------------------------------------------------------------------------------- /test/utils.ts: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv" 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const { NovitaSDK, TaskStatus } = require("../index.ts"); 5 | 6 | dotenv.config() 7 | 8 | const novitaClient = new NovitaSDK(process.env.API_KEY) 9 | 10 | export function fileToBase64(filePath: string): string { 11 | try { 12 | const fileBuffer = fs.readFileSync(path.resolve(filePath)); 13 | const base64 = fileBuffer.toString('base64'); 14 | // const mimeType = getMimeType(filePath); 15 | return base64; 16 | } catch (error) { 17 | console.error('Error converting file to base64:', error); 18 | return "" 19 | } 20 | } 21 | 22 | function getMimeType(filePath: string) { 23 | const ext = path.extname(filePath).toLowerCase(); 24 | switch (ext) { 25 | case '.jpg': 26 | case '.jpeg': 27 | return 'image/jpeg'; 28 | case '.png': 29 | return 'image/png'; 30 | case '.gif': 31 | return 'image/gif'; 32 | case '.bmp': 33 | return 'image/bmp'; 34 | case '.webp': 35 | return 'image/webp'; 36 | default: 37 | return 'application/octet-stream'; // default mimetype 38 | } 39 | } 40 | 41 | function delay(duration: number) { 42 | return new Promise(resolve => setTimeout(resolve, duration)); 43 | } 44 | 45 | export async function pollTaskStatus(taskId: string) { 46 | async function poll() { 47 | const progress = await novitaClient.progress({ task_id: taskId }) 48 | if (progress.task.status === TaskStatus.SUCCEED) { 49 | return progress; 50 | } else if (progress.task.status === TaskStatus.FAILED) { 51 | return progress; 52 | } else { 53 | await delay(1000); 54 | return poll(); // Recursively call poll after a delay 55 | } 56 | } 57 | 58 | return poll(); 59 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ES6", 4 | "strict": true, 5 | "declaration": true, 6 | "esModuleInterop": true, 7 | "moduleResolution": "Node", 8 | "outDir": "./dist" 9 | }, 10 | "include": ["src/**/*", "index.ts", "tests/**/*"], 11 | "exclude": ["src/examples"] 12 | } 13 | --------------------------------------------------------------------------------