@animeapi/ai-sdk-provider 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +89 -2
- package/dist/index.cjs +168 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +56 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +144 -0
- package/dist/index.js.map +1 -0
- package/package.json +72 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 FROMYOU, LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,90 @@
|
|
|
1
|
-
#
|
|
1
|
+
# AnimeAPI provider for the AI SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Anime image generation for the [Vercel AI SDK](https://ai-sdk.dev) at **$0.003 per image**, one of the cheapest anime image APIs. `generateImage` with [AnimeAPI](https://animeapi.ai): one call, a finished anime illustration in about 4 seconds. Prepaid balance from $5 that never expires, no subscription; new accounts get 3 free images without a card.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @animeapi/ai-sdk-provider ai
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Requires AI SDK 7 (`ai@7`).
|
|
10
|
+
|
|
11
|
+
## Use
|
|
12
|
+
|
|
13
|
+
Create a key at https://animeapi.ai/dashboard/api-keys and set `ANIMEAPI_KEY`.
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { generateImage } from "ai";
|
|
17
|
+
import { animeapi } from "@animeapi/ai-sdk-provider";
|
|
18
|
+
import { writeFile } from "node:fs/promises";
|
|
19
|
+
|
|
20
|
+
const { image } = await generateImage({
|
|
21
|
+
model: animeapi.image("anime"), // $0.003 per image
|
|
22
|
+
prompt: "a knight cat drinking tea in a sunflower field",
|
|
23
|
+
aspectRatio: "16:9",
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
await writeFile("knight-cat.webp", image.uint8Array);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Models and prices
|
|
30
|
+
|
|
31
|
+
| Model | Price per image | Use it for |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| `anime` | **$0.003** | Characters, portraits, avatars, scenes. About 4 seconds. |
|
|
34
|
+
| `ultra` | $0.01 | Long, detailed prompts; legible text in the image. About 11 seconds. |
|
|
35
|
+
| `ultra-max` | $0.02 | Highest detail. |
|
|
36
|
+
|
|
37
|
+
Every shape costs the same.
|
|
38
|
+
|
|
39
|
+
## Settings
|
|
40
|
+
|
|
41
|
+
| `generateImage` setting | Support |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| `prompt` | Plain English, up to 1,000 characters. |
|
|
44
|
+
| `n` | 1 to 4 per request; larger numbers are split into several requests by the AI SDK. Each image is charged. |
|
|
45
|
+
| `aspectRatio` / `size` | Mapped to one of three shapes: wider than tall is `landscape`, taller is `portrait`, equal is `square`. Exact pixel sizes are chosen by the model. Default: portrait. |
|
|
46
|
+
| `seed` | Not supported (a warning is returned). |
|
|
47
|
+
| image inputs, masks | Not supported; generation is from text. |
|
|
48
|
+
|
|
49
|
+
Provider options, under `providerOptions.animeapi`:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
await generateImage({
|
|
53
|
+
model: animeapi.image("anime"),
|
|
54
|
+
prompt: "a shrine maiden sweeping autumn leaves",
|
|
55
|
+
providerOptions: {
|
|
56
|
+
animeapi: {
|
|
57
|
+
orientation: "landscape", // "portrait" | "square" | "landscape"; overrides size and aspectRatio
|
|
58
|
+
enhance: true, // rewrite plain English into tags for the anime model (API default: true)
|
|
59
|
+
allowNsfw: false, // API default: true. Set false where images land in shared spaces.
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The result's `providerMetadata.animeapi` holds `images[i].revisedPrompt` (what the model read) and `balanceUsd` (balance left after the call).
|
|
66
|
+
|
|
67
|
+
## Custom instance
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { createAnimeAPI } from "@animeapi/ai-sdk-provider";
|
|
71
|
+
|
|
72
|
+
const animeapi = createAnimeAPI({
|
|
73
|
+
apiKey: process.env.MY_ANIMEAPI_KEY, // default: ANIMEAPI_KEY
|
|
74
|
+
headers: { "X-My-App": "1" },
|
|
75
|
+
// baseURL, fetch
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Errors
|
|
80
|
+
|
|
81
|
+
Failed calls throw the AI SDK's `APICallError` with the HTTP status and AnimeAPI's message: 401 for a bad key, 402 for an empty balance, 400 when a prompt is refused. Rate limits (429) and temporary failures (5xx) are marked retryable, so the AI SDK's `maxRetries` handles them; failed generations are not charged.
|
|
82
|
+
|
|
83
|
+
## Good to know
|
|
84
|
+
|
|
85
|
+
- Images come back as WebP bytes. Image URLs on AnimeAPI are not permanent storage, which is why this provider returns the bytes for you to keep.
|
|
86
|
+
- AnimeAPI also has an OpenAI-compatible endpoint (`https://animeapi.ai/v1`) and a remote MCP server (`https://animeapi.ai/mcp`). Docs: https://animeapi.ai/docs
|
|
87
|
+
|
|
88
|
+
## Licence
|
|
89
|
+
|
|
90
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AnimeAPIImageModel: () => AnimeAPIImageModel,
|
|
24
|
+
VERSION: () => VERSION,
|
|
25
|
+
animeapi: () => animeapi,
|
|
26
|
+
createAnimeAPI: () => createAnimeAPI
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
|
|
30
|
+
// src/animeapi-provider.ts
|
|
31
|
+
var import_provider = require("@ai-sdk/provider");
|
|
32
|
+
var import_provider_utils2 = require("@ai-sdk/provider-utils");
|
|
33
|
+
|
|
34
|
+
// src/animeapi-image-model.ts
|
|
35
|
+
var import_provider_utils = require("@ai-sdk/provider-utils");
|
|
36
|
+
var import_zod = require("zod");
|
|
37
|
+
var providerOptionsSchema = import_zod.z.object({
|
|
38
|
+
orientation: import_zod.z.enum(["portrait", "square", "landscape"]).optional(),
|
|
39
|
+
enhance: import_zod.z.boolean().optional(),
|
|
40
|
+
allowNsfw: import_zod.z.boolean().optional()
|
|
41
|
+
});
|
|
42
|
+
var responseSchema = import_zod.z.object({
|
|
43
|
+
created: import_zod.z.number().optional(),
|
|
44
|
+
model: import_zod.z.string().optional(),
|
|
45
|
+
balance_usd: import_zod.z.number().optional(),
|
|
46
|
+
data: import_zod.z.array(import_zod.z.object({ b64_json: import_zod.z.string(), revised_prompt: import_zod.z.string().nullish() }))
|
|
47
|
+
});
|
|
48
|
+
var errorSchema = import_zod.z.object({
|
|
49
|
+
error: import_zod.z.object({
|
|
50
|
+
message: import_zod.z.string(),
|
|
51
|
+
type: import_zod.z.string().nullish(),
|
|
52
|
+
param: import_zod.z.string().nullish(),
|
|
53
|
+
code: import_zod.z.string().nullish()
|
|
54
|
+
})
|
|
55
|
+
});
|
|
56
|
+
var failedResponseHandler = (0, import_provider_utils.createJsonErrorResponseHandler)({
|
|
57
|
+
errorSchema,
|
|
58
|
+
errorToMessage: (data) => data.error.message,
|
|
59
|
+
// 429 and 5xx are worth retrying; a refused prompt, a bad key or an empty balance are not.
|
|
60
|
+
isRetryable: (response) => response.status === 429 || response.status >= 500
|
|
61
|
+
});
|
|
62
|
+
function ratioToOrientation(width, height) {
|
|
63
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width === height) return "square";
|
|
64
|
+
return width > height ? "landscape" : "portrait";
|
|
65
|
+
}
|
|
66
|
+
var AnimeAPIImageModel = class {
|
|
67
|
+
constructor(modelId, config) {
|
|
68
|
+
this.modelId = modelId;
|
|
69
|
+
this.config = config;
|
|
70
|
+
}
|
|
71
|
+
modelId;
|
|
72
|
+
config;
|
|
73
|
+
specificationVersion = "v4";
|
|
74
|
+
/** The API returns up to 4 images per request. */
|
|
75
|
+
maxImagesPerCall = 4;
|
|
76
|
+
supportsFileInputs = false;
|
|
77
|
+
supportsMaskInputs = false;
|
|
78
|
+
get provider() {
|
|
79
|
+
return this.config.provider;
|
|
80
|
+
}
|
|
81
|
+
async doGenerate(options) {
|
|
82
|
+
const { prompt, n, size, aspectRatio, seed, files, mask, providerOptions, headers, abortSignal } = options;
|
|
83
|
+
const warnings = [];
|
|
84
|
+
if (seed != null) warnings.push({ type: "unsupported", feature: "seed" });
|
|
85
|
+
if (files != null && files.length > 0) {
|
|
86
|
+
warnings.push({ type: "unsupported", feature: "files", details: "AnimeAPI generates from text only." });
|
|
87
|
+
}
|
|
88
|
+
if (mask != null) warnings.push({ type: "unsupported", feature: "mask" });
|
|
89
|
+
const animeapi2 = await (0, import_provider_utils.parseProviderOptions)({
|
|
90
|
+
provider: "animeapi",
|
|
91
|
+
providerOptions,
|
|
92
|
+
schema: providerOptionsSchema
|
|
93
|
+
});
|
|
94
|
+
let orientation = animeapi2?.orientation;
|
|
95
|
+
if (!orientation && size) {
|
|
96
|
+
const [w, h] = size.split("x").map(Number);
|
|
97
|
+
orientation = ratioToOrientation(w ?? NaN, h ?? NaN);
|
|
98
|
+
} else if (!orientation && aspectRatio) {
|
|
99
|
+
const [w, h] = aspectRatio.split(":").map(Number);
|
|
100
|
+
orientation = ratioToOrientation(w ?? NaN, h ?? NaN);
|
|
101
|
+
}
|
|
102
|
+
const timestamp = /* @__PURE__ */ new Date();
|
|
103
|
+
const { value, responseHeaders } = await (0, import_provider_utils.postJsonToApi)({
|
|
104
|
+
url: `${this.config.baseURL}/v1/images/generations`,
|
|
105
|
+
headers: (0, import_provider_utils.combineHeaders)(this.config.headers(), headers),
|
|
106
|
+
body: {
|
|
107
|
+
model: this.modelId,
|
|
108
|
+
prompt,
|
|
109
|
+
n,
|
|
110
|
+
response_format: "b64_json",
|
|
111
|
+
...orientation ? { orientation } : {},
|
|
112
|
+
...animeapi2?.enhance !== void 0 ? { enhance: animeapi2.enhance } : {},
|
|
113
|
+
...animeapi2?.allowNsfw !== void 0 ? { allow_nsfw: animeapi2.allowNsfw } : {}
|
|
114
|
+
},
|
|
115
|
+
failedResponseHandler,
|
|
116
|
+
successfulResponseHandler: (0, import_provider_utils.createJsonResponseHandler)(responseSchema),
|
|
117
|
+
abortSignal,
|
|
118
|
+
fetch: this.config.fetch
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
images: value.data.map((item) => item.b64_json),
|
|
122
|
+
warnings,
|
|
123
|
+
providerMetadata: {
|
|
124
|
+
animeapi: {
|
|
125
|
+
images: value.data.map((item) => ({ revisedPrompt: item.revised_prompt ?? null })),
|
|
126
|
+
...value.balance_usd !== void 0 ? { balanceUsd: value.balance_usd } : {}
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
response: { timestamp, modelId: value.model ?? this.modelId, headers: responseHeaders }
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// src/animeapi-provider.ts
|
|
135
|
+
var VERSION = true ? "1.0.0" : "0.0.0-dev";
|
|
136
|
+
function createAnimeAPI(options = {}) {
|
|
137
|
+
const baseURL = (0, import_provider_utils2.withoutTrailingSlash)(options.baseURL) ?? "https://animeapi.ai";
|
|
138
|
+
const getHeaders = () => ({
|
|
139
|
+
Authorization: `Bearer ${(0, import_provider_utils2.loadApiKey)({
|
|
140
|
+
apiKey: options.apiKey,
|
|
141
|
+
environmentVariableName: "ANIMEAPI_KEY",
|
|
142
|
+
description: "AnimeAPI"
|
|
143
|
+
})}`,
|
|
144
|
+
"User-Agent": `animeapi-ai-sdk-provider/${VERSION}`,
|
|
145
|
+
...options.headers
|
|
146
|
+
});
|
|
147
|
+
const createImageModel = (modelId) => new AnimeAPIImageModel(modelId, { provider: "animeapi.image", baseURL, headers: getHeaders, fetch: options.fetch });
|
|
148
|
+
return {
|
|
149
|
+
specificationVersion: "v4",
|
|
150
|
+
image: createImageModel,
|
|
151
|
+
imageModel: createImageModel,
|
|
152
|
+
languageModel: (modelId) => {
|
|
153
|
+
throw new import_provider.NoSuchModelError({ modelId, modelType: "languageModel" });
|
|
154
|
+
},
|
|
155
|
+
embeddingModel: (modelId) => {
|
|
156
|
+
throw new import_provider.NoSuchModelError({ modelId, modelType: "embeddingModel" });
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
var animeapi = createAnimeAPI();
|
|
161
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
162
|
+
0 && (module.exports = {
|
|
163
|
+
AnimeAPIImageModel,
|
|
164
|
+
VERSION,
|
|
165
|
+
animeapi,
|
|
166
|
+
createAnimeAPI
|
|
167
|
+
});
|
|
168
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/animeapi-provider.ts","../src/animeapi-image-model.ts"],"sourcesContent":["export { animeapi, createAnimeAPI, VERSION } from \"./animeapi-provider\";\nexport type { AnimeAPIProvider, AnimeAPIProviderSettings } from \"./animeapi-provider\";\nexport { AnimeAPIImageModel } from \"./animeapi-image-model\";\nexport type { AnimeAPIImageModelId, AnimeAPIImageProviderOptions, AnimeAPIOrientation } from \"./animeapi-image-model\";\n","import { NoSuchModelError, type ImageModelV4, type ProviderV4 } from \"@ai-sdk/provider\";\nimport { loadApiKey, withoutTrailingSlash, type FetchFunction } from \"@ai-sdk/provider-utils\";\nimport { AnimeAPIImageModel, type AnimeAPIImageModelId } from \"./animeapi-image-model\";\n\ndeclare const __PACKAGE_VERSION__: string;\nexport const VERSION: string = typeof __PACKAGE_VERSION__ === \"string\" ? __PACKAGE_VERSION__ : \"0.0.0-dev\";\n\nexport interface AnimeAPIProviderSettings {\n /** API key (starts with `ank_`). Defaults to the `ANIMEAPI_KEY` environment variable. */\n apiKey?: string;\n /** Defaults to `https://animeapi.ai`. */\n baseURL?: string;\n /** Extra headers sent with every request. */\n headers?: Record<string, string>;\n /** Custom fetch, e.g. for a proxy. */\n fetch?: FetchFunction;\n}\n\nexport interface AnimeAPIProvider extends ProviderV4 {\n /** Image model: `anime` ($0.003 per image), `ultra` ($0.01) or `ultra-max` ($0.02). */\n image(modelId: AnimeAPIImageModelId): ImageModelV4;\n imageModel(modelId: AnimeAPIImageModelId): ImageModelV4;\n}\n\nexport function createAnimeAPI(options: AnimeAPIProviderSettings = {}): AnimeAPIProvider {\n const baseURL = withoutTrailingSlash(options.baseURL) ?? \"https://animeapi.ai\";\n const getHeaders = () => ({\n Authorization: `Bearer ${loadApiKey({\n apiKey: options.apiKey,\n environmentVariableName: \"ANIMEAPI_KEY\",\n description: \"AnimeAPI\",\n })}`,\n \"User-Agent\": `animeapi-ai-sdk-provider/${VERSION}`,\n ...options.headers,\n });\n\n const createImageModel = (modelId: AnimeAPIImageModelId) =>\n new AnimeAPIImageModel(modelId, { provider: \"animeapi.image\", baseURL, headers: getHeaders, fetch: options.fetch });\n\n return {\n specificationVersion: \"v4\",\n image: createImageModel,\n imageModel: createImageModel,\n languageModel: (modelId: string) => {\n throw new NoSuchModelError({ modelId, modelType: \"languageModel\" });\n },\n embeddingModel: (modelId: string) => {\n throw new NoSuchModelError({ modelId, modelType: \"embeddingModel\" });\n },\n };\n}\n\n/** Default provider instance; reads the key from `ANIMEAPI_KEY`. */\nexport const animeapi: AnimeAPIProvider = createAnimeAPI();\n","import type { ImageModelV4, ImageModelV4CallOptions, ImageModelV4Result, SharedV4Warning } from \"@ai-sdk/provider\";\nimport {\n combineHeaders,\n createJsonErrorResponseHandler,\n createJsonResponseHandler,\n parseProviderOptions,\n postJsonToApi,\n type FetchFunction,\n} from \"@ai-sdk/provider-utils\";\nimport { z } from \"zod\";\n\n/** `anime` $0.003, `ultra` $0.01, `ultra-max` $0.02 per image. */\nexport type AnimeAPIImageModelId = \"anime\" | \"ultra\" | \"ultra-max\" | (string & {});\n\nexport type AnimeAPIOrientation = \"portrait\" | \"square\" | \"landscape\";\n\n/** Options passed as `providerOptions.animeapi` to `generateImage`. */\nexport interface AnimeAPIImageProviderOptions {\n /** Shape of the image. Overrides `size` and `aspectRatio`. Default: portrait. */\n orientation?: AnimeAPIOrientation;\n /** Rewrite a plain-English prompt into tags for the anime model. API default: true. */\n enhance?: boolean;\n /** Allow mature output. API default: true. Set false where images are shown in shared spaces. */\n allowNsfw?: boolean;\n}\n\nconst providerOptionsSchema = z.object({\n orientation: z.enum([\"portrait\", \"square\", \"landscape\"]).optional(),\n enhance: z.boolean().optional(),\n allowNsfw: z.boolean().optional(),\n});\n\nconst responseSchema = z.object({\n created: z.number().optional(),\n model: z.string().optional(),\n balance_usd: z.number().optional(),\n data: z.array(z.object({ b64_json: z.string(), revised_prompt: z.string().nullish() })),\n});\n\nconst errorSchema = z.object({\n error: z.object({\n message: z.string(),\n type: z.string().nullish(),\n param: z.string().nullish(),\n code: z.string().nullish(),\n }),\n});\n\nconst failedResponseHandler = createJsonErrorResponseHandler({\n errorSchema,\n errorToMessage: (data) => data.error.message,\n // 429 and 5xx are worth retrying; a refused prompt, a bad key or an empty balance are not.\n isRetryable: (response) => response.status === 429 || response.status >= 500,\n});\n\nexport interface AnimeAPIImageModelConfig {\n provider: string;\n baseURL: string;\n headers: () => Record<string, string | undefined>;\n fetch?: FetchFunction;\n}\n\nfunction ratioToOrientation(width: number, height: number): AnimeAPIOrientation {\n if (!Number.isFinite(width) || !Number.isFinite(height) || width === height) return \"square\";\n return width > height ? \"landscape\" : \"portrait\";\n}\n\nexport class AnimeAPIImageModel implements ImageModelV4 {\n readonly specificationVersion = \"v4\";\n /** The API returns up to 4 images per request. */\n readonly maxImagesPerCall = 4;\n readonly supportsFileInputs = false;\n readonly supportsMaskInputs = false;\n\n constructor(\n readonly modelId: AnimeAPIImageModelId,\n private readonly config: AnimeAPIImageModelConfig,\n ) {}\n\n get provider(): string {\n return this.config.provider;\n }\n\n async doGenerate(options: ImageModelV4CallOptions): Promise<ImageModelV4Result> {\n const { prompt, n, size, aspectRatio, seed, files, mask, providerOptions, headers, abortSignal } = options;\n const warnings: SharedV4Warning[] = [];\n\n if (seed != null) warnings.push({ type: \"unsupported\", feature: \"seed\" });\n if (files != null && files.length > 0) {\n warnings.push({ type: \"unsupported\", feature: \"files\", details: \"AnimeAPI generates from text only.\" });\n }\n if (mask != null) warnings.push({ type: \"unsupported\", feature: \"mask\" });\n\n const animeapi = await parseProviderOptions({\n provider: \"animeapi\",\n providerOptions,\n schema: providerOptionsSchema,\n });\n\n // AnimeAPI has three shapes at one price each; exact pixel sizes are chosen by the model.\n let orientation: AnimeAPIOrientation | undefined = animeapi?.orientation;\n if (!orientation && size) {\n const [w, h] = size.split(\"x\").map(Number);\n orientation = ratioToOrientation(w ?? NaN, h ?? NaN);\n } else if (!orientation && aspectRatio) {\n const [w, h] = aspectRatio.split(\":\").map(Number);\n orientation = ratioToOrientation(w ?? NaN, h ?? NaN);\n }\n\n const timestamp = new Date();\n const { value, responseHeaders } = await postJsonToApi({\n url: `${this.config.baseURL}/v1/images/generations`,\n headers: combineHeaders(this.config.headers(), headers),\n body: {\n model: this.modelId,\n prompt,\n n,\n response_format: \"b64_json\",\n ...(orientation ? { orientation } : {}),\n ...(animeapi?.enhance !== undefined ? { enhance: animeapi.enhance } : {}),\n ...(animeapi?.allowNsfw !== undefined ? { allow_nsfw: animeapi.allowNsfw } : {}),\n },\n failedResponseHandler,\n successfulResponseHandler: createJsonResponseHandler(responseSchema),\n abortSignal,\n fetch: this.config.fetch,\n });\n\n return {\n images: value.data.map((item) => item.b64_json),\n warnings,\n providerMetadata: {\n animeapi: {\n images: value.data.map((item) => ({ revisedPrompt: item.revised_prompt ?? null })),\n ...(value.balance_usd !== undefined ? { balanceUsd: value.balance_usd } : {}),\n },\n },\n response: { timestamp, modelId: value.model ?? this.modelId, headers: responseHeaders },\n };\n }\n}\n"],"mappings":"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","names":["import_provider_utils","animeapi"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ImageModelV4, ImageModelV4CallOptions, ImageModelV4Result, ProviderV4 } from '@ai-sdk/provider';
|
|
2
|
+
import { FetchFunction } from '@ai-sdk/provider-utils';
|
|
3
|
+
|
|
4
|
+
/** `anime` $0.003, `ultra` $0.01, `ultra-max` $0.02 per image. */
|
|
5
|
+
type AnimeAPIImageModelId = "anime" | "ultra" | "ultra-max" | (string & {});
|
|
6
|
+
type AnimeAPIOrientation = "portrait" | "square" | "landscape";
|
|
7
|
+
/** Options passed as `providerOptions.animeapi` to `generateImage`. */
|
|
8
|
+
interface AnimeAPIImageProviderOptions {
|
|
9
|
+
/** Shape of the image. Overrides `size` and `aspectRatio`. Default: portrait. */
|
|
10
|
+
orientation?: AnimeAPIOrientation;
|
|
11
|
+
/** Rewrite a plain-English prompt into tags for the anime model. API default: true. */
|
|
12
|
+
enhance?: boolean;
|
|
13
|
+
/** Allow mature output. API default: true. Set false where images are shown in shared spaces. */
|
|
14
|
+
allowNsfw?: boolean;
|
|
15
|
+
}
|
|
16
|
+
interface AnimeAPIImageModelConfig {
|
|
17
|
+
provider: string;
|
|
18
|
+
baseURL: string;
|
|
19
|
+
headers: () => Record<string, string | undefined>;
|
|
20
|
+
fetch?: FetchFunction;
|
|
21
|
+
}
|
|
22
|
+
declare class AnimeAPIImageModel implements ImageModelV4 {
|
|
23
|
+
readonly modelId: AnimeAPIImageModelId;
|
|
24
|
+
private readonly config;
|
|
25
|
+
readonly specificationVersion = "v4";
|
|
26
|
+
/** The API returns up to 4 images per request. */
|
|
27
|
+
readonly maxImagesPerCall = 4;
|
|
28
|
+
readonly supportsFileInputs = false;
|
|
29
|
+
readonly supportsMaskInputs = false;
|
|
30
|
+
constructor(modelId: AnimeAPIImageModelId, config: AnimeAPIImageModelConfig);
|
|
31
|
+
get provider(): string;
|
|
32
|
+
doGenerate(options: ImageModelV4CallOptions): Promise<ImageModelV4Result>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
declare const VERSION: string;
|
|
36
|
+
interface AnimeAPIProviderSettings {
|
|
37
|
+
/** API key (starts with `ank_`). Defaults to the `ANIMEAPI_KEY` environment variable. */
|
|
38
|
+
apiKey?: string;
|
|
39
|
+
/** Defaults to `https://animeapi.ai`. */
|
|
40
|
+
baseURL?: string;
|
|
41
|
+
/** Extra headers sent with every request. */
|
|
42
|
+
headers?: Record<string, string>;
|
|
43
|
+
/** Custom fetch, e.g. for a proxy. */
|
|
44
|
+
fetch?: FetchFunction;
|
|
45
|
+
}
|
|
46
|
+
interface AnimeAPIProvider extends ProviderV4 {
|
|
47
|
+
/** Image model: `anime` ($0.003 per image), `ultra` ($0.01) or `ultra-max` ($0.02). */
|
|
48
|
+
image(modelId: AnimeAPIImageModelId): ImageModelV4;
|
|
49
|
+
imageModel(modelId: AnimeAPIImageModelId): ImageModelV4;
|
|
50
|
+
}
|
|
51
|
+
declare function createAnimeAPI(options?: AnimeAPIProviderSettings): AnimeAPIProvider;
|
|
52
|
+
/** Default provider instance; reads the key from `ANIMEAPI_KEY`. */
|
|
53
|
+
declare const animeapi: AnimeAPIProvider;
|
|
54
|
+
|
|
55
|
+
export { AnimeAPIImageModel, VERSION, animeapi, createAnimeAPI };
|
|
56
|
+
export type { AnimeAPIImageModelId, AnimeAPIImageProviderOptions, AnimeAPIOrientation, AnimeAPIProvider, AnimeAPIProviderSettings };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ImageModelV4, ImageModelV4CallOptions, ImageModelV4Result, ProviderV4 } from '@ai-sdk/provider';
|
|
2
|
+
import { FetchFunction } from '@ai-sdk/provider-utils';
|
|
3
|
+
|
|
4
|
+
/** `anime` $0.003, `ultra` $0.01, `ultra-max` $0.02 per image. */
|
|
5
|
+
type AnimeAPIImageModelId = "anime" | "ultra" | "ultra-max" | (string & {});
|
|
6
|
+
type AnimeAPIOrientation = "portrait" | "square" | "landscape";
|
|
7
|
+
/** Options passed as `providerOptions.animeapi` to `generateImage`. */
|
|
8
|
+
interface AnimeAPIImageProviderOptions {
|
|
9
|
+
/** Shape of the image. Overrides `size` and `aspectRatio`. Default: portrait. */
|
|
10
|
+
orientation?: AnimeAPIOrientation;
|
|
11
|
+
/** Rewrite a plain-English prompt into tags for the anime model. API default: true. */
|
|
12
|
+
enhance?: boolean;
|
|
13
|
+
/** Allow mature output. API default: true. Set false where images are shown in shared spaces. */
|
|
14
|
+
allowNsfw?: boolean;
|
|
15
|
+
}
|
|
16
|
+
interface AnimeAPIImageModelConfig {
|
|
17
|
+
provider: string;
|
|
18
|
+
baseURL: string;
|
|
19
|
+
headers: () => Record<string, string | undefined>;
|
|
20
|
+
fetch?: FetchFunction;
|
|
21
|
+
}
|
|
22
|
+
declare class AnimeAPIImageModel implements ImageModelV4 {
|
|
23
|
+
readonly modelId: AnimeAPIImageModelId;
|
|
24
|
+
private readonly config;
|
|
25
|
+
readonly specificationVersion = "v4";
|
|
26
|
+
/** The API returns up to 4 images per request. */
|
|
27
|
+
readonly maxImagesPerCall = 4;
|
|
28
|
+
readonly supportsFileInputs = false;
|
|
29
|
+
readonly supportsMaskInputs = false;
|
|
30
|
+
constructor(modelId: AnimeAPIImageModelId, config: AnimeAPIImageModelConfig);
|
|
31
|
+
get provider(): string;
|
|
32
|
+
doGenerate(options: ImageModelV4CallOptions): Promise<ImageModelV4Result>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
declare const VERSION: string;
|
|
36
|
+
interface AnimeAPIProviderSettings {
|
|
37
|
+
/** API key (starts with `ank_`). Defaults to the `ANIMEAPI_KEY` environment variable. */
|
|
38
|
+
apiKey?: string;
|
|
39
|
+
/** Defaults to `https://animeapi.ai`. */
|
|
40
|
+
baseURL?: string;
|
|
41
|
+
/** Extra headers sent with every request. */
|
|
42
|
+
headers?: Record<string, string>;
|
|
43
|
+
/** Custom fetch, e.g. for a proxy. */
|
|
44
|
+
fetch?: FetchFunction;
|
|
45
|
+
}
|
|
46
|
+
interface AnimeAPIProvider extends ProviderV4 {
|
|
47
|
+
/** Image model: `anime` ($0.003 per image), `ultra` ($0.01) or `ultra-max` ($0.02). */
|
|
48
|
+
image(modelId: AnimeAPIImageModelId): ImageModelV4;
|
|
49
|
+
imageModel(modelId: AnimeAPIImageModelId): ImageModelV4;
|
|
50
|
+
}
|
|
51
|
+
declare function createAnimeAPI(options?: AnimeAPIProviderSettings): AnimeAPIProvider;
|
|
52
|
+
/** Default provider instance; reads the key from `ANIMEAPI_KEY`. */
|
|
53
|
+
declare const animeapi: AnimeAPIProvider;
|
|
54
|
+
|
|
55
|
+
export { AnimeAPIImageModel, VERSION, animeapi, createAnimeAPI };
|
|
56
|
+
export type { AnimeAPIImageModelId, AnimeAPIImageProviderOptions, AnimeAPIOrientation, AnimeAPIProvider, AnimeAPIProviderSettings };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// src/animeapi-provider.ts
|
|
2
|
+
import { NoSuchModelError } from "@ai-sdk/provider";
|
|
3
|
+
import { loadApiKey, withoutTrailingSlash } from "@ai-sdk/provider-utils";
|
|
4
|
+
|
|
5
|
+
// src/animeapi-image-model.ts
|
|
6
|
+
import {
|
|
7
|
+
combineHeaders,
|
|
8
|
+
createJsonErrorResponseHandler,
|
|
9
|
+
createJsonResponseHandler,
|
|
10
|
+
parseProviderOptions,
|
|
11
|
+
postJsonToApi
|
|
12
|
+
} from "@ai-sdk/provider-utils";
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
var providerOptionsSchema = z.object({
|
|
15
|
+
orientation: z.enum(["portrait", "square", "landscape"]).optional(),
|
|
16
|
+
enhance: z.boolean().optional(),
|
|
17
|
+
allowNsfw: z.boolean().optional()
|
|
18
|
+
});
|
|
19
|
+
var responseSchema = z.object({
|
|
20
|
+
created: z.number().optional(),
|
|
21
|
+
model: z.string().optional(),
|
|
22
|
+
balance_usd: z.number().optional(),
|
|
23
|
+
data: z.array(z.object({ b64_json: z.string(), revised_prompt: z.string().nullish() }))
|
|
24
|
+
});
|
|
25
|
+
var errorSchema = z.object({
|
|
26
|
+
error: z.object({
|
|
27
|
+
message: z.string(),
|
|
28
|
+
type: z.string().nullish(),
|
|
29
|
+
param: z.string().nullish(),
|
|
30
|
+
code: z.string().nullish()
|
|
31
|
+
})
|
|
32
|
+
});
|
|
33
|
+
var failedResponseHandler = createJsonErrorResponseHandler({
|
|
34
|
+
errorSchema,
|
|
35
|
+
errorToMessage: (data) => data.error.message,
|
|
36
|
+
// 429 and 5xx are worth retrying; a refused prompt, a bad key or an empty balance are not.
|
|
37
|
+
isRetryable: (response) => response.status === 429 || response.status >= 500
|
|
38
|
+
});
|
|
39
|
+
function ratioToOrientation(width, height) {
|
|
40
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width === height) return "square";
|
|
41
|
+
return width > height ? "landscape" : "portrait";
|
|
42
|
+
}
|
|
43
|
+
var AnimeAPIImageModel = class {
|
|
44
|
+
constructor(modelId, config) {
|
|
45
|
+
this.modelId = modelId;
|
|
46
|
+
this.config = config;
|
|
47
|
+
}
|
|
48
|
+
modelId;
|
|
49
|
+
config;
|
|
50
|
+
specificationVersion = "v4";
|
|
51
|
+
/** The API returns up to 4 images per request. */
|
|
52
|
+
maxImagesPerCall = 4;
|
|
53
|
+
supportsFileInputs = false;
|
|
54
|
+
supportsMaskInputs = false;
|
|
55
|
+
get provider() {
|
|
56
|
+
return this.config.provider;
|
|
57
|
+
}
|
|
58
|
+
async doGenerate(options) {
|
|
59
|
+
const { prompt, n, size, aspectRatio, seed, files, mask, providerOptions, headers, abortSignal } = options;
|
|
60
|
+
const warnings = [];
|
|
61
|
+
if (seed != null) warnings.push({ type: "unsupported", feature: "seed" });
|
|
62
|
+
if (files != null && files.length > 0) {
|
|
63
|
+
warnings.push({ type: "unsupported", feature: "files", details: "AnimeAPI generates from text only." });
|
|
64
|
+
}
|
|
65
|
+
if (mask != null) warnings.push({ type: "unsupported", feature: "mask" });
|
|
66
|
+
const animeapi2 = await parseProviderOptions({
|
|
67
|
+
provider: "animeapi",
|
|
68
|
+
providerOptions,
|
|
69
|
+
schema: providerOptionsSchema
|
|
70
|
+
});
|
|
71
|
+
let orientation = animeapi2?.orientation;
|
|
72
|
+
if (!orientation && size) {
|
|
73
|
+
const [w, h] = size.split("x").map(Number);
|
|
74
|
+
orientation = ratioToOrientation(w ?? NaN, h ?? NaN);
|
|
75
|
+
} else if (!orientation && aspectRatio) {
|
|
76
|
+
const [w, h] = aspectRatio.split(":").map(Number);
|
|
77
|
+
orientation = ratioToOrientation(w ?? NaN, h ?? NaN);
|
|
78
|
+
}
|
|
79
|
+
const timestamp = /* @__PURE__ */ new Date();
|
|
80
|
+
const { value, responseHeaders } = await postJsonToApi({
|
|
81
|
+
url: `${this.config.baseURL}/v1/images/generations`,
|
|
82
|
+
headers: combineHeaders(this.config.headers(), headers),
|
|
83
|
+
body: {
|
|
84
|
+
model: this.modelId,
|
|
85
|
+
prompt,
|
|
86
|
+
n,
|
|
87
|
+
response_format: "b64_json",
|
|
88
|
+
...orientation ? { orientation } : {},
|
|
89
|
+
...animeapi2?.enhance !== void 0 ? { enhance: animeapi2.enhance } : {},
|
|
90
|
+
...animeapi2?.allowNsfw !== void 0 ? { allow_nsfw: animeapi2.allowNsfw } : {}
|
|
91
|
+
},
|
|
92
|
+
failedResponseHandler,
|
|
93
|
+
successfulResponseHandler: createJsonResponseHandler(responseSchema),
|
|
94
|
+
abortSignal,
|
|
95
|
+
fetch: this.config.fetch
|
|
96
|
+
});
|
|
97
|
+
return {
|
|
98
|
+
images: value.data.map((item) => item.b64_json),
|
|
99
|
+
warnings,
|
|
100
|
+
providerMetadata: {
|
|
101
|
+
animeapi: {
|
|
102
|
+
images: value.data.map((item) => ({ revisedPrompt: item.revised_prompt ?? null })),
|
|
103
|
+
...value.balance_usd !== void 0 ? { balanceUsd: value.balance_usd } : {}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
response: { timestamp, modelId: value.model ?? this.modelId, headers: responseHeaders }
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// src/animeapi-provider.ts
|
|
112
|
+
var VERSION = true ? "1.0.0" : "0.0.0-dev";
|
|
113
|
+
function createAnimeAPI(options = {}) {
|
|
114
|
+
const baseURL = withoutTrailingSlash(options.baseURL) ?? "https://animeapi.ai";
|
|
115
|
+
const getHeaders = () => ({
|
|
116
|
+
Authorization: `Bearer ${loadApiKey({
|
|
117
|
+
apiKey: options.apiKey,
|
|
118
|
+
environmentVariableName: "ANIMEAPI_KEY",
|
|
119
|
+
description: "AnimeAPI"
|
|
120
|
+
})}`,
|
|
121
|
+
"User-Agent": `animeapi-ai-sdk-provider/${VERSION}`,
|
|
122
|
+
...options.headers
|
|
123
|
+
});
|
|
124
|
+
const createImageModel = (modelId) => new AnimeAPIImageModel(modelId, { provider: "animeapi.image", baseURL, headers: getHeaders, fetch: options.fetch });
|
|
125
|
+
return {
|
|
126
|
+
specificationVersion: "v4",
|
|
127
|
+
image: createImageModel,
|
|
128
|
+
imageModel: createImageModel,
|
|
129
|
+
languageModel: (modelId) => {
|
|
130
|
+
throw new NoSuchModelError({ modelId, modelType: "languageModel" });
|
|
131
|
+
},
|
|
132
|
+
embeddingModel: (modelId) => {
|
|
133
|
+
throw new NoSuchModelError({ modelId, modelType: "embeddingModel" });
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
var animeapi = createAnimeAPI();
|
|
138
|
+
export {
|
|
139
|
+
AnimeAPIImageModel,
|
|
140
|
+
VERSION,
|
|
141
|
+
animeapi,
|
|
142
|
+
createAnimeAPI
|
|
143
|
+
};
|
|
144
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/animeapi-provider.ts","../src/animeapi-image-model.ts"],"sourcesContent":["import { NoSuchModelError, type ImageModelV4, type ProviderV4 } from \"@ai-sdk/provider\";\nimport { loadApiKey, withoutTrailingSlash, type FetchFunction } from \"@ai-sdk/provider-utils\";\nimport { AnimeAPIImageModel, type AnimeAPIImageModelId } from \"./animeapi-image-model\";\n\ndeclare const __PACKAGE_VERSION__: string;\nexport const VERSION: string = typeof __PACKAGE_VERSION__ === \"string\" ? __PACKAGE_VERSION__ : \"0.0.0-dev\";\n\nexport interface AnimeAPIProviderSettings {\n /** API key (starts with `ank_`). Defaults to the `ANIMEAPI_KEY` environment variable. */\n apiKey?: string;\n /** Defaults to `https://animeapi.ai`. */\n baseURL?: string;\n /** Extra headers sent with every request. */\n headers?: Record<string, string>;\n /** Custom fetch, e.g. for a proxy. */\n fetch?: FetchFunction;\n}\n\nexport interface AnimeAPIProvider extends ProviderV4 {\n /** Image model: `anime` ($0.003 per image), `ultra` ($0.01) or `ultra-max` ($0.02). */\n image(modelId: AnimeAPIImageModelId): ImageModelV4;\n imageModel(modelId: AnimeAPIImageModelId): ImageModelV4;\n}\n\nexport function createAnimeAPI(options: AnimeAPIProviderSettings = {}): AnimeAPIProvider {\n const baseURL = withoutTrailingSlash(options.baseURL) ?? \"https://animeapi.ai\";\n const getHeaders = () => ({\n Authorization: `Bearer ${loadApiKey({\n apiKey: options.apiKey,\n environmentVariableName: \"ANIMEAPI_KEY\",\n description: \"AnimeAPI\",\n })}`,\n \"User-Agent\": `animeapi-ai-sdk-provider/${VERSION}`,\n ...options.headers,\n });\n\n const createImageModel = (modelId: AnimeAPIImageModelId) =>\n new AnimeAPIImageModel(modelId, { provider: \"animeapi.image\", baseURL, headers: getHeaders, fetch: options.fetch });\n\n return {\n specificationVersion: \"v4\",\n image: createImageModel,\n imageModel: createImageModel,\n languageModel: (modelId: string) => {\n throw new NoSuchModelError({ modelId, modelType: \"languageModel\" });\n },\n embeddingModel: (modelId: string) => {\n throw new NoSuchModelError({ modelId, modelType: \"embeddingModel\" });\n },\n };\n}\n\n/** Default provider instance; reads the key from `ANIMEAPI_KEY`. */\nexport const animeapi: AnimeAPIProvider = createAnimeAPI();\n","import type { ImageModelV4, ImageModelV4CallOptions, ImageModelV4Result, SharedV4Warning } from \"@ai-sdk/provider\";\nimport {\n combineHeaders,\n createJsonErrorResponseHandler,\n createJsonResponseHandler,\n parseProviderOptions,\n postJsonToApi,\n type FetchFunction,\n} from \"@ai-sdk/provider-utils\";\nimport { z } from \"zod\";\n\n/** `anime` $0.003, `ultra` $0.01, `ultra-max` $0.02 per image. */\nexport type AnimeAPIImageModelId = \"anime\" | \"ultra\" | \"ultra-max\" | (string & {});\n\nexport type AnimeAPIOrientation = \"portrait\" | \"square\" | \"landscape\";\n\n/** Options passed as `providerOptions.animeapi` to `generateImage`. */\nexport interface AnimeAPIImageProviderOptions {\n /** Shape of the image. Overrides `size` and `aspectRatio`. Default: portrait. */\n orientation?: AnimeAPIOrientation;\n /** Rewrite a plain-English prompt into tags for the anime model. API default: true. */\n enhance?: boolean;\n /** Allow mature output. API default: true. Set false where images are shown in shared spaces. */\n allowNsfw?: boolean;\n}\n\nconst providerOptionsSchema = z.object({\n orientation: z.enum([\"portrait\", \"square\", \"landscape\"]).optional(),\n enhance: z.boolean().optional(),\n allowNsfw: z.boolean().optional(),\n});\n\nconst responseSchema = z.object({\n created: z.number().optional(),\n model: z.string().optional(),\n balance_usd: z.number().optional(),\n data: z.array(z.object({ b64_json: z.string(), revised_prompt: z.string().nullish() })),\n});\n\nconst errorSchema = z.object({\n error: z.object({\n message: z.string(),\n type: z.string().nullish(),\n param: z.string().nullish(),\n code: z.string().nullish(),\n }),\n});\n\nconst failedResponseHandler = createJsonErrorResponseHandler({\n errorSchema,\n errorToMessage: (data) => data.error.message,\n // 429 and 5xx are worth retrying; a refused prompt, a bad key or an empty balance are not.\n isRetryable: (response) => response.status === 429 || response.status >= 500,\n});\n\nexport interface AnimeAPIImageModelConfig {\n provider: string;\n baseURL: string;\n headers: () => Record<string, string | undefined>;\n fetch?: FetchFunction;\n}\n\nfunction ratioToOrientation(width: number, height: number): AnimeAPIOrientation {\n if (!Number.isFinite(width) || !Number.isFinite(height) || width === height) return \"square\";\n return width > height ? \"landscape\" : \"portrait\";\n}\n\nexport class AnimeAPIImageModel implements ImageModelV4 {\n readonly specificationVersion = \"v4\";\n /** The API returns up to 4 images per request. */\n readonly maxImagesPerCall = 4;\n readonly supportsFileInputs = false;\n readonly supportsMaskInputs = false;\n\n constructor(\n readonly modelId: AnimeAPIImageModelId,\n private readonly config: AnimeAPIImageModelConfig,\n ) {}\n\n get provider(): string {\n return this.config.provider;\n }\n\n async doGenerate(options: ImageModelV4CallOptions): Promise<ImageModelV4Result> {\n const { prompt, n, size, aspectRatio, seed, files, mask, providerOptions, headers, abortSignal } = options;\n const warnings: SharedV4Warning[] = [];\n\n if (seed != null) warnings.push({ type: \"unsupported\", feature: \"seed\" });\n if (files != null && files.length > 0) {\n warnings.push({ type: \"unsupported\", feature: \"files\", details: \"AnimeAPI generates from text only.\" });\n }\n if (mask != null) warnings.push({ type: \"unsupported\", feature: \"mask\" });\n\n const animeapi = await parseProviderOptions({\n provider: \"animeapi\",\n providerOptions,\n schema: providerOptionsSchema,\n });\n\n // AnimeAPI has three shapes at one price each; exact pixel sizes are chosen by the model.\n let orientation: AnimeAPIOrientation | undefined = animeapi?.orientation;\n if (!orientation && size) {\n const [w, h] = size.split(\"x\").map(Number);\n orientation = ratioToOrientation(w ?? NaN, h ?? NaN);\n } else if (!orientation && aspectRatio) {\n const [w, h] = aspectRatio.split(\":\").map(Number);\n orientation = ratioToOrientation(w ?? NaN, h ?? NaN);\n }\n\n const timestamp = new Date();\n const { value, responseHeaders } = await postJsonToApi({\n url: `${this.config.baseURL}/v1/images/generations`,\n headers: combineHeaders(this.config.headers(), headers),\n body: {\n model: this.modelId,\n prompt,\n n,\n response_format: \"b64_json\",\n ...(orientation ? { orientation } : {}),\n ...(animeapi?.enhance !== undefined ? { enhance: animeapi.enhance } : {}),\n ...(animeapi?.allowNsfw !== undefined ? { allow_nsfw: animeapi.allowNsfw } : {}),\n },\n failedResponseHandler,\n successfulResponseHandler: createJsonResponseHandler(responseSchema),\n abortSignal,\n fetch: this.config.fetch,\n });\n\n return {\n images: value.data.map((item) => item.b64_json),\n warnings,\n providerMetadata: {\n animeapi: {\n images: value.data.map((item) => ({ revisedPrompt: item.revised_prompt ?? null })),\n ...(value.balance_usd !== undefined ? { balanceUsd: value.balance_usd } : {}),\n },\n },\n response: { timestamp, modelId: value.model ?? this.modelId, headers: responseHeaders },\n };\n }\n}\n"],"mappings":"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","names":["animeapi"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,74 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@animeapi/ai-sdk-provider",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AnimeAPI provider for the Vercel AI SDK: anime image generation with generateImage at $0.003 per image.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "AnimeAPI <contact@animeapi.ai> (https://animeapi.ai)",
|
|
7
|
+
"homepage": "https://animeapi.ai",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/animeapi-ai/ai-sdk-provider.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/animeapi-ai/ai-sdk-provider/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ai",
|
|
17
|
+
"ai-sdk",
|
|
18
|
+
"vercel-ai",
|
|
19
|
+
"ai-sdk-provider",
|
|
20
|
+
"anime",
|
|
21
|
+
"image-generation",
|
|
22
|
+
"text-to-image",
|
|
23
|
+
"animeapi",
|
|
24
|
+
"generateImage"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"main": "./dist/index.cjs",
|
|
29
|
+
"module": "./dist/index.js",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
"./package.json": "./package.json",
|
|
33
|
+
".": {
|
|
34
|
+
"import": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"default": "./dist/index.js"
|
|
37
|
+
},
|
|
38
|
+
"require": {
|
|
39
|
+
"types": "./dist/index.d.cts",
|
|
40
|
+
"default": "./dist/index.cjs"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"files": [
|
|
45
|
+
"dist",
|
|
46
|
+
"README.md",
|
|
47
|
+
"LICENSE"
|
|
48
|
+
],
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup",
|
|
54
|
+
"typecheck": "tsc --noEmit",
|
|
55
|
+
"prepublishOnly": "npm run typecheck && npm run build"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@ai-sdk/provider": "^4.0.0",
|
|
59
|
+
"@ai-sdk/provider-utils": "^5.0.0"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"zod": "^3.25.76 || ^4.1.8"
|
|
63
|
+
},
|
|
64
|
+
"publishConfig": {
|
|
65
|
+
"access": "public"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@types/node": "^26.6.6",
|
|
69
|
+
"ai": "^7.0.166",
|
|
70
|
+
"tsup": "^9.0.0",
|
|
71
|
+
"typescript": "^6.0.3",
|
|
72
|
+
"zod": "^4.6.7"
|
|
73
|
+
}
|
|
74
|
+
}
|