vargai 0.3.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/.claude/settings.local.json +7 -0
- package/.env.example +27 -0
- package/.github/workflows/ci.yml +23 -0
- package/.husky/README.md +102 -0
- package/.husky/commit-msg +6 -0
- package/.husky/pre-commit +9 -0
- package/.husky/pre-push +6 -0
- package/.size-limit.json +8 -0
- package/.test-hooks.ts +5 -0
- package/CLAUDE.md +125 -0
- package/CONTRIBUTING.md +150 -0
- package/LICENSE.md +53 -0
- package/README.md +78 -0
- package/SKILLS.md +173 -0
- package/STRUCTURE.md +92 -0
- package/biome.json +34 -0
- package/bun.lock +1254 -0
- package/commitlint.config.js +22 -0
- package/docs/plan.md +66 -0
- package/docs/todo.md +14 -0
- package/docs/varg-sdk.md +812 -0
- package/ffmpeg/CLAUDE.md +68 -0
- package/package.json +69 -0
- package/pipeline/cookbooks/SKILL.md +285 -0
- package/pipeline/cookbooks/remotion-video.md +585 -0
- package/pipeline/cookbooks/round-video-character.md +337 -0
- package/pipeline/cookbooks/scripts/animate-frames-parallel.ts +84 -0
- package/pipeline/cookbooks/scripts/combine-scenes.sh +53 -0
- package/pipeline/cookbooks/scripts/generate-frames-parallel.ts +99 -0
- package/pipeline/cookbooks/scripts/still-to-video.sh +37 -0
- package/pipeline/cookbooks/talking-character.md +59 -0
- package/pipeline/cookbooks/text-to-tiktok.md +669 -0
- package/pipeline/cookbooks/trendwatching.md +156 -0
- package/plan.md +281 -0
- package/scripts/.gitkeep +0 -0
- package/src/ai-sdk/cache.ts +142 -0
- package/src/ai-sdk/examples/cached-generation.ts +53 -0
- package/src/ai-sdk/examples/duet-scene-4.ts +53 -0
- package/src/ai-sdk/examples/duet-scene-5-audio.ts +32 -0
- package/src/ai-sdk/examples/duet-video.ts +56 -0
- package/src/ai-sdk/examples/editly-composition.ts +63 -0
- package/src/ai-sdk/examples/editly-test.ts +57 -0
- package/src/ai-sdk/examples/editly-video-test.ts +52 -0
- package/src/ai-sdk/examples/fal-lipsync.ts +43 -0
- package/src/ai-sdk/examples/higgsfield-image.ts +61 -0
- package/src/ai-sdk/examples/music-generation.ts +19 -0
- package/src/ai-sdk/examples/openai-sora.ts +34 -0
- package/src/ai-sdk/examples/replicate-bg-removal.ts +52 -0
- package/src/ai-sdk/examples/simpsons-scene.ts +61 -0
- package/src/ai-sdk/examples/talking-lion.ts +55 -0
- package/src/ai-sdk/examples/video-generation.ts +39 -0
- package/src/ai-sdk/examples/workflow-animated-girl.ts +104 -0
- package/src/ai-sdk/examples/workflow-before-after.ts +114 -0
- package/src/ai-sdk/examples/workflow-character-grid.ts +112 -0
- package/src/ai-sdk/examples/workflow-slideshow.ts +161 -0
- package/src/ai-sdk/file-cache.ts +112 -0
- package/src/ai-sdk/file.ts +238 -0
- package/src/ai-sdk/generate-element.ts +92 -0
- package/src/ai-sdk/generate-music.ts +46 -0
- package/src/ai-sdk/generate-video.ts +165 -0
- package/src/ai-sdk/index.ts +72 -0
- package/src/ai-sdk/music-model.ts +110 -0
- package/src/ai-sdk/providers/editly/editly.test.ts +1108 -0
- package/src/ai-sdk/providers/editly/ffmpeg.ts +60 -0
- package/src/ai-sdk/providers/editly/index.ts +817 -0
- package/src/ai-sdk/providers/editly/layers.ts +772 -0
- package/src/ai-sdk/providers/editly/plan.md +144 -0
- package/src/ai-sdk/providers/editly/types.ts +328 -0
- package/src/ai-sdk/providers/elevenlabs-provider.ts +255 -0
- package/src/ai-sdk/providers/fal-provider.ts +512 -0
- package/src/ai-sdk/providers/higgsfield.ts +379 -0
- package/src/ai-sdk/providers/openai.ts +251 -0
- package/src/ai-sdk/providers/replicate.ts +16 -0
- package/src/ai-sdk/video-model.ts +185 -0
- package/src/cli/commands/find.tsx +137 -0
- package/src/cli/commands/help.tsx +85 -0
- package/src/cli/commands/index.ts +9 -0
- package/src/cli/commands/list.tsx +238 -0
- package/src/cli/commands/run.tsx +511 -0
- package/src/cli/commands/which.tsx +253 -0
- package/src/cli/index.ts +112 -0
- package/src/cli/quiet.ts +44 -0
- package/src/cli/types.ts +32 -0
- package/src/cli/ui/components/Badge.tsx +29 -0
- package/src/cli/ui/components/DataTable.tsx +51 -0
- package/src/cli/ui/components/Header.tsx +23 -0
- package/src/cli/ui/components/HelpBlock.tsx +44 -0
- package/src/cli/ui/components/KeyValue.tsx +33 -0
- package/src/cli/ui/components/OptionRow.tsx +81 -0
- package/src/cli/ui/components/Separator.tsx +23 -0
- package/src/cli/ui/components/StatusBox.tsx +108 -0
- package/src/cli/ui/components/VargBox.tsx +51 -0
- package/src/cli/ui/components/VargProgress.tsx +36 -0
- package/src/cli/ui/components/VargSpinner.tsx +34 -0
- package/src/cli/ui/components/VargText.tsx +56 -0
- package/src/cli/ui/components/index.ts +19 -0
- package/src/cli/ui/index.ts +12 -0
- package/src/cli/ui/render.ts +35 -0
- package/src/cli/ui/theme.ts +63 -0
- package/src/cli/utils.ts +78 -0
- package/src/core/executor/executor.ts +201 -0
- package/src/core/executor/index.ts +13 -0
- package/src/core/executor/job.ts +214 -0
- package/src/core/executor/pipeline.ts +222 -0
- package/src/core/index.ts +11 -0
- package/src/core/registry/index.ts +9 -0
- package/src/core/registry/loader.ts +149 -0
- package/src/core/registry/registry.ts +221 -0
- package/src/core/registry/resolver.ts +206 -0
- package/src/core/schema/helpers.ts +134 -0
- package/src/core/schema/index.ts +8 -0
- package/src/core/schema/shared.ts +102 -0
- package/src/core/schema/types.ts +279 -0
- package/src/core/schema/validator.ts +92 -0
- package/src/definitions/actions/captions.ts +261 -0
- package/src/definitions/actions/edit.ts +298 -0
- package/src/definitions/actions/image.ts +125 -0
- package/src/definitions/actions/index.ts +114 -0
- package/src/definitions/actions/music.ts +205 -0
- package/src/definitions/actions/sync.ts +128 -0
- package/src/definitions/actions/transcribe.ts +200 -0
- package/src/definitions/actions/upload.ts +111 -0
- package/src/definitions/actions/video.ts +163 -0
- package/src/definitions/actions/voice.ts +119 -0
- package/src/definitions/index.ts +23 -0
- package/src/definitions/models/elevenlabs.ts +50 -0
- package/src/definitions/models/flux.ts +56 -0
- package/src/definitions/models/index.ts +36 -0
- package/src/definitions/models/kling.ts +56 -0
- package/src/definitions/models/llama.ts +54 -0
- package/src/definitions/models/nano-banana-pro.ts +102 -0
- package/src/definitions/models/sonauto.ts +68 -0
- package/src/definitions/models/soul.ts +65 -0
- package/src/definitions/models/wan.ts +54 -0
- package/src/definitions/models/whisper.ts +44 -0
- package/src/definitions/skills/index.ts +12 -0
- package/src/definitions/skills/talking-character.ts +87 -0
- package/src/definitions/skills/text-to-tiktok.ts +97 -0
- package/src/index.ts +118 -0
- package/src/providers/apify.ts +269 -0
- package/src/providers/base.ts +264 -0
- package/src/providers/elevenlabs.ts +217 -0
- package/src/providers/fal.ts +392 -0
- package/src/providers/ffmpeg.ts +544 -0
- package/src/providers/fireworks.ts +193 -0
- package/src/providers/groq.ts +149 -0
- package/src/providers/higgsfield.ts +145 -0
- package/src/providers/index.ts +143 -0
- package/src/providers/replicate.ts +147 -0
- package/src/providers/storage.ts +206 -0
- package/src/tests/all.test.ts +509 -0
- package/src/tests/index.ts +33 -0
- package/src/tests/unit.test.ts +403 -0
- package/tsconfig.json +45 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ImageModelV3,
|
|
3
|
+
ImageModelV3CallOptions,
|
|
4
|
+
SharedV3Warning,
|
|
5
|
+
} from "@ai-sdk/provider";
|
|
6
|
+
|
|
7
|
+
const IMAGE_MODELS = ["soul"] as const;
|
|
8
|
+
type ImageModelId = (typeof IMAGE_MODELS)[number];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Available Soul styles for Higgsfield image generation.
|
|
12
|
+
* Use with `higgsfield.imageModel("soul", { styleId: higgsfield.styles.REALISTIC })`
|
|
13
|
+
*/
|
|
14
|
+
export const SOUL_STYLES = {
|
|
15
|
+
CREATURES: "b3c8075a-cb4c-42de-b8b3-7099dd2df672",
|
|
16
|
+
MEDIEVAL: "1fc861ed-5923-41a6-9963-b9f04681dddd",
|
|
17
|
+
SPOTLIGHT: "40ff999c-f576-443c-b5b3-c7d1391a666e",
|
|
18
|
+
GIANT_PEOPLE: "a5f63c3b-70eb-4979-af5e-98c7ee1e18e8",
|
|
19
|
+
RED_BALLOON: "3de71b9e-3973-4828-b246-a34c606e25a7",
|
|
20
|
+
GREEN_EDITORIAL: "91abc4fe-1cf8-4a77-8ade-d36d46699014",
|
|
21
|
+
SUBWAY: "d2e8ba04-9935-4dee-8bc4-39ac789746fc",
|
|
22
|
+
LIBRARY: "6fb3e1f5-d721-4523-ac38-9902f2b2b850",
|
|
23
|
+
REALISTIC: "1cb4b936-77bf-4f9a-9039-f3d349a4cdbe",
|
|
24
|
+
DIGITALCAM: "ca4e6ad3-3e93-4e03-81a0-d1722d2c128b",
|
|
25
|
+
GRILLZ_SELFIE: "255f4045-d68b-42b1-9e4c-f49d3263a9d7",
|
|
26
|
+
BLEACHED_BROWS: "cc099663-9621-422e-8626-c8ee68953a0c",
|
|
27
|
+
SITTING_ON_THE_STREET: "7696fd45-6e67-47d7-b800-096ce21cd449",
|
|
28
|
+
CROSSING_THE_STREET: "d3e2b71d-b24b-462e-bd96-12f7a22b5142",
|
|
29
|
+
ANGEL_WINGS: "4c24b43b-1984-407a-a0ae-c514f29b7e66",
|
|
30
|
+
DUPLICATE: "88126a43-86fb-4047-a2d6-c9146d6ca6ce",
|
|
31
|
+
QUIET_LUXURY: "ff1ad8a2-94e7-4e70-a12f-e992ca9a0d36",
|
|
32
|
+
FIREPROOF: "373420f7-489e-4a5d-930e-cc4ecfcc23cc",
|
|
33
|
+
ELEVATOR_MIRROR: "524be50a-4388-4ff5-a843-a73d2dd7ef87",
|
|
34
|
+
CAM_360: "294bb3ee-eaef-4d2a-93e3-164268803db4",
|
|
35
|
+
GLITCH: "62355e77-7096-45ae-9bea-e7c5b88c3b70",
|
|
36
|
+
FASHION_SHOW: "86fc814e-856a-4af0-98b0-d4da75d0030b",
|
|
37
|
+
PIXELETED_FACE: "34c50302-83ff-487d-b3a9-e35e501d80a7",
|
|
38
|
+
SUNBATHING: "bc00b419-f8ca-4887-a990-e2760c3cb761",
|
|
39
|
+
PAPER_FACE: "7fa63380-64b7-48b1-b684-4c9ef37560a9",
|
|
40
|
+
GRAIN_90S: "f5c094c7-4671-4d86-90d2-369c8fdbd7a5",
|
|
41
|
+
GEOMINIMAL: "372cc37b-9add-4952-a415-53db3998139f",
|
|
42
|
+
FOGGY_MORNING: "0fe8ad66-ff61-411f-9186-b392e140b18c",
|
|
43
|
+
OVEREXPOSED: "d8a35238-ba42-48a0-a76a-186a97734b9d",
|
|
44
|
+
SUNSET_BEACH: "26241c54-ed78-4ea7-b1bf-d881737c9feb",
|
|
45
|
+
GIANT_ACCESSORY: "70fbb531-5ee2-492e-8c53-5dbd6923e8c2",
|
|
46
|
+
RING_SELFIE: "9de8ed26-c8dd-413c-a5e3-47eec97bc243",
|
|
47
|
+
STREET_VIEW: "a13917c7-02a4-450f-b007-e72d53151980",
|
|
48
|
+
EDITORIAL_90S: "710f9073-f580-48dc-b5c3-9bbc7cbb7f37",
|
|
49
|
+
RHYME_BLUES: "c7ea4e7a-c40c-498d-948c-1f6919631f60",
|
|
50
|
+
CAM_2000S: "181b3796-008a-403b-b31e-a9b760219f17",
|
|
51
|
+
CCTV: "07a85fb3-4407-4122-a4eb-42124e57734c",
|
|
52
|
+
OUTFIT_05: "71fecd8c-6696-42df-b5eb-f69e4150ca01",
|
|
53
|
+
AMALFI_SUMMER: "dab472a6-23f4-4cf8-98fe-f3e256f1b549",
|
|
54
|
+
BIMBOCORE: "f96913e8-2fcf-4358-8545-75dd6c34c518",
|
|
55
|
+
SELFIE_05: "8dd89de9-1cff-402e-88a8-580c29d91473",
|
|
56
|
+
SAND: "ba3d7634-447e-455c-98e3-63705d5403b8",
|
|
57
|
+
VINTAGE_PHOTOBOOTH: "83caff04-691c-468c-b4a0-fd6bbabe062b",
|
|
58
|
+
AFTERPARTY_CAM: "5765d07d-1525-4d4d-ae06-9091e2bdac2d",
|
|
59
|
+
BABYDOLL_MAKEUP: "b7c621b5-9d3c-46a3-8efb-4cdfbc592271",
|
|
60
|
+
THROUGH_THE_GLASS: "1900111a-4ce8-42a7-9394-7367f0e0385c",
|
|
61
|
+
GALLERY: "36061eb7-4907-4cba-afb1-47afcf699873",
|
|
62
|
+
EATING_FOOD: "7df83cc9-1e13-4bd0-b6ff-1e6a456b9e5a",
|
|
63
|
+
SWORDS_HILL: "5b6f467e-f509-4afe-a8db-4c07a6f3770d",
|
|
64
|
+
OFFICE_BEACH: "e454956b-caf2-4913-a398-dbc03f1cbedf",
|
|
65
|
+
HELP_ITS_TOO_BIG: "5ad23bca-4a4b-4316-8c59-b80d7709d8ee",
|
|
66
|
+
JAPANDI: "0089e17c-d0f0-4d0c-b522-6d25c88a29fc",
|
|
67
|
+
IPHONE: "1b798b54-03da-446a-93bf-12fcba1050d7",
|
|
68
|
+
GORPCORE: "96758335-d1d1-42b7-9c21-5ac38c433485",
|
|
69
|
+
INDIE_SLEAZE: "5a72fec7-a12e-43db-8ef3-1d193b4f7ab4",
|
|
70
|
+
FAIRYCORE: "7f21e7bd-4df6-4cef-a9a9-9746bceaea1d",
|
|
71
|
+
TUMBLR: "0367d609-dfa1-4a81-a983-b2b19ecd6480",
|
|
72
|
+
AVANT_GARDE: "0c636e12-3411-4a65-8d86-67858caf2fa7",
|
|
73
|
+
HAIRCLIPS: "ea6f4dc0-d6dd-4bdf-a8cf-94ed1db91ab2",
|
|
74
|
+
BIRTHDAY_MESS: "2d47f079-c021-4b8e-b2c0-3b927a80fc31",
|
|
75
|
+
CLOUDED_DREAM: "493bda5b-bb4b-46fe-9343-7d5e414534ef",
|
|
76
|
+
Y2K_POSTERS: "cbefda85-0f76-49bd-82d7-9bcd65be00ca",
|
|
77
|
+
TOKYO_DRIFT: "ce9a88c2-c962-45e2-abaa-c8979d48f8d5",
|
|
78
|
+
OBJECT_MAKEUP: "b7908955-2868-4e35-87a0-35e50cb92e5d",
|
|
79
|
+
GRAFFITI: "0b4dac9a-f73a-4e5b-a5a7-1a40ee40d6ac",
|
|
80
|
+
SUNBURNT: "e439bd89-8176-4729-b6a4-a9977120507d",
|
|
81
|
+
HALLWAY_NOIR: "a643a36a-85e6-4e3d-80db-13e4997203cc",
|
|
82
|
+
FASHION_2000S: "facaafeb-4ab5-4384-92a1-b4086180e9ac",
|
|
83
|
+
NIGHT_BEACH: "62ba1751-63af-4648-a11c-711ac64e216a",
|
|
84
|
+
MOVIE: "811de7ab-7aaf-4a6b-b352-cdea6c34c8f1",
|
|
85
|
+
LONG_LEGS: "12eda704-18e5-4783-aa0f-deba5296cc83",
|
|
86
|
+
APHEX_TWIN: "5659c554-9367-4a27-8c66-333c072cbbc2",
|
|
87
|
+
GENERAL: "464ea177-8d40-4940-8d9d-b438bab269c7",
|
|
88
|
+
NAIL_CHECK: "4b66c2db-8166-4293-b1aa-5269c9effb07",
|
|
89
|
+
COQUETTE_CORE: "bd78cfc6-9b92-4889-9347-f21dbf0a269c",
|
|
90
|
+
MIXED_MEDIA: "2fcf02e2-919a-4642-8b31-d58bde5e6bd9",
|
|
91
|
+
SELFCARE: "d24c016c-9fb1-47d0-9909-19f57a2830d4",
|
|
92
|
+
GRUNGE: "ad9de607-3941-4540-81ea-ba978ef1550b",
|
|
93
|
+
DOUBLE_TAKE: "2a1898d0-548f-4433-8503-5721157b93a1",
|
|
94
|
+
ROOM_505: "673cf0d4-c193-4fa2-8ad3-b4db4611e3ae",
|
|
95
|
+
FLIGHT_MODE: "3f90dc5b-f474-4259-95c4-d29fbd6be645",
|
|
96
|
+
ESCALATOR: "bab6e4bd-9093-4bb5-a371-01ef6cbd58ad",
|
|
97
|
+
BURGUNDY_SUIT: "84c23cef-7eda-4f8f-9931-e3e6af8192d9",
|
|
98
|
+
FISHEYE: "cc4e7248-dcfe-4c93-b264-2ab418a7556b",
|
|
99
|
+
SHOE_CHECK: "30458874-d9c0-4d5a-b2b7-597e0eee2404",
|
|
100
|
+
RAINY_DAY: "53bdadfa-8eb6-4eaa-8923-ebece4faa91c",
|
|
101
|
+
MT_FUJI: "de0118ba-7c27-49f7-9841-38abe2aae8e1",
|
|
102
|
+
SEA_BREEZE: "71ac929c-4002-4640-9b65-cb06402844c6",
|
|
103
|
+
INVERTETHEREAL: "82edba1e-b093-4484-a25e-9276e0454999",
|
|
104
|
+
Y2K: "6b9e6b4d-325a-4a78-a0fb-a00ddf612380",
|
|
105
|
+
TOKYO_STREETSTYLE: "99de6fc5-1177-49b9-b2e9-19e17d95bcaf",
|
|
106
|
+
CHROME_EXIT: "5e01339d-745e-4bba-96f7-e8ce4af79f17",
|
|
107
|
+
NIGHT_RIDER: "1fba888b-9ab0-447f-a6a4-9ce5251ec2a6",
|
|
108
|
+
ARTWORK: "b9e2d7dc-78e6-4f7d-95dd-b62690e7b200",
|
|
109
|
+
GLAZED_DOLL_SKIN_MAKEUP: "a2a42ada-75cc-42a9-be12-cb16c1dec2a8",
|
|
110
|
+
MOUNT_VIEW: "3c975998-cb5b-4980-80fc-7977e4c60972",
|
|
111
|
+
BLADE_RUNNER_2049: "53959c8a-4323-4b78-9888-e9f6fb0f6b98",
|
|
112
|
+
BLACKOUT_FIT: "f8dac072-d11f-438a-b283-fd52fe8aa744",
|
|
113
|
+
BIKE_MAFIA: "90df2935-3ded-477f-8253-1d67dd939cbe",
|
|
114
|
+
STATIC_GLOW: "fb9cee2b-632f-4fd4-ae4f-4664deecc0f4",
|
|
115
|
+
NICOTINE_GLOW: "5dbb6a20-0541-4f06-8352-a2408d8781dc",
|
|
116
|
+
BRICK_SHADE: "f3968a4f-a125-4c16-8673-45ce9874520e",
|
|
117
|
+
DMV: "923e4fb0-d4ea-480c-876d-ac7cad862b9d",
|
|
118
|
+
FISH_EYE_TWIN: "d4775423-d214-4862-b061-47baa1978208",
|
|
119
|
+
ITS_FRENCH: "79bfaa63-4e12-4ea2-8ada-7d4406eecece",
|
|
120
|
+
COCKTAIL: "aed71142-673c-4908-a6a7-326d5252eb06",
|
|
121
|
+
} as const;
|
|
122
|
+
|
|
123
|
+
export interface HiggsfieldImageModelSettings {
|
|
124
|
+
styleId?: string;
|
|
125
|
+
quality?: "720p" | "1080p";
|
|
126
|
+
enhancePrompt?: boolean;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Maps aspect ratio to width_and_height for Soul API
|
|
130
|
+
const ASPECT_RATIO_TO_SIZE: Record<string, string> = {
|
|
131
|
+
"1:1": "1536x1536",
|
|
132
|
+
"16:9": "2048x1152",
|
|
133
|
+
"9:16": "1152x2048",
|
|
134
|
+
"4:3": "2048x1536",
|
|
135
|
+
"3:4": "1536x2048",
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
class HiggsfieldImageModel implements ImageModelV3 {
|
|
139
|
+
readonly specificationVersion = "v3" as const;
|
|
140
|
+
readonly provider = "higgsfield";
|
|
141
|
+
readonly modelId: string;
|
|
142
|
+
readonly maxImagesPerCall = 4;
|
|
143
|
+
|
|
144
|
+
private apiKey: string;
|
|
145
|
+
private apiSecret: string;
|
|
146
|
+
private baseURL: string;
|
|
147
|
+
private modelSettings: HiggsfieldImageModelSettings;
|
|
148
|
+
|
|
149
|
+
get settings(): HiggsfieldImageModelSettings {
|
|
150
|
+
return this.modelSettings;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
constructor(
|
|
154
|
+
modelId: string,
|
|
155
|
+
options: {
|
|
156
|
+
apiKey?: string;
|
|
157
|
+
apiSecret?: string;
|
|
158
|
+
baseURL?: string;
|
|
159
|
+
} & HiggsfieldImageModelSettings = {},
|
|
160
|
+
) {
|
|
161
|
+
this.modelId = modelId;
|
|
162
|
+
this.apiKey = options.apiKey ?? process.env.HIGGSFIELD_API_KEY ?? "";
|
|
163
|
+
this.apiSecret = options.apiSecret ?? process.env.HIGGSFIELD_SECRET ?? "";
|
|
164
|
+
this.baseURL = options.baseURL ?? "https://platform.higgsfield.ai";
|
|
165
|
+
this.modelSettings = {
|
|
166
|
+
styleId: options.styleId,
|
|
167
|
+
quality: options.quality,
|
|
168
|
+
enhancePrompt: options.enhancePrompt,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async doGenerate(options: ImageModelV3CallOptions) {
|
|
173
|
+
const { prompt, n, aspectRatio, providerOptions, abortSignal } = options;
|
|
174
|
+
const warnings: SharedV3Warning[] = [];
|
|
175
|
+
|
|
176
|
+
// Map aspect ratio to width_and_height
|
|
177
|
+
const widthAndHeight = aspectRatio
|
|
178
|
+
? (ASPECT_RATIO_TO_SIZE[aspectRatio] ?? "1536x1536")
|
|
179
|
+
: "1536x1536";
|
|
180
|
+
|
|
181
|
+
if (aspectRatio && !ASPECT_RATIO_TO_SIZE[aspectRatio]) {
|
|
182
|
+
warnings.push({
|
|
183
|
+
type: "unsupported",
|
|
184
|
+
feature: "aspectRatio",
|
|
185
|
+
details: `Aspect ratio ${aspectRatio} not supported. Using 1:1. Supported: 1:1, 16:9, 9:16, 4:3, 3:4.`,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Build params object - matching working implementation
|
|
190
|
+
const params: Record<string, unknown> = {
|
|
191
|
+
prompt,
|
|
192
|
+
width_and_height: widthAndHeight,
|
|
193
|
+
enhance_prompt: this.modelSettings.enhancePrompt ?? false,
|
|
194
|
+
quality: this.modelSettings.quality ?? "1080p",
|
|
195
|
+
batch_size: n && n <= 4 ? n : 1,
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// Add optional parameters only if provided
|
|
199
|
+
if (this.modelSettings.styleId) {
|
|
200
|
+
params.style_id = this.modelSettings.styleId;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Merge provider options
|
|
204
|
+
const higgsfieldOptions = providerOptions?.higgsfield as
|
|
205
|
+
| Record<string, unknown>
|
|
206
|
+
| undefined;
|
|
207
|
+
if (higgsfieldOptions) {
|
|
208
|
+
for (const [key, value] of Object.entries(higgsfieldOptions)) {
|
|
209
|
+
if (value !== undefined && value !== null) {
|
|
210
|
+
params[key] = value;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Request body wrapped in params
|
|
216
|
+
const requestBody = { params };
|
|
217
|
+
|
|
218
|
+
// Make request to /v1/text2image/soul
|
|
219
|
+
const response = await fetch(`${this.baseURL}/v1/text2image/soul`, {
|
|
220
|
+
method: "POST",
|
|
221
|
+
headers: {
|
|
222
|
+
"hf-api-key": this.apiKey,
|
|
223
|
+
"hf-secret": this.apiSecret,
|
|
224
|
+
"Content-Type": "application/json",
|
|
225
|
+
Accept: "application/json",
|
|
226
|
+
},
|
|
227
|
+
body: JSON.stringify(requestBody),
|
|
228
|
+
signal: abortSignal,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
if (!response.ok) {
|
|
232
|
+
const errorText = await response.text();
|
|
233
|
+
throw new Error(
|
|
234
|
+
`Higgsfield Soul API error (${response.status}): ${errorText}`,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const data = (await response.json()) as { id?: string };
|
|
239
|
+
const jobId = data?.id;
|
|
240
|
+
|
|
241
|
+
if (!jobId) {
|
|
242
|
+
throw new Error("No job ID returned from Higgsfield Soul API");
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Poll for results
|
|
246
|
+
const imageUrl = await this.pollForResult(jobId, abortSignal);
|
|
247
|
+
|
|
248
|
+
// Download image
|
|
249
|
+
const imageResponse = await fetch(imageUrl, { signal: abortSignal });
|
|
250
|
+
const imageBuffer = new Uint8Array(await imageResponse.arrayBuffer());
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
images: [imageBuffer],
|
|
254
|
+
warnings,
|
|
255
|
+
response: {
|
|
256
|
+
timestamp: new Date(),
|
|
257
|
+
modelId: this.modelId,
|
|
258
|
+
headers: undefined,
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private async pollForResult(
|
|
264
|
+
jobId: string,
|
|
265
|
+
abortSignal?: AbortSignal,
|
|
266
|
+
): Promise<string> {
|
|
267
|
+
const maxAttempts = 60;
|
|
268
|
+
const pollInterval = 5000; // 5 seconds
|
|
269
|
+
|
|
270
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
271
|
+
const response = await fetch(`${this.baseURL}/v1/job-sets/${jobId}`, {
|
|
272
|
+
method: "GET",
|
|
273
|
+
headers: {
|
|
274
|
+
"hf-api-key": this.apiKey,
|
|
275
|
+
"hf-secret": this.apiSecret,
|
|
276
|
+
Accept: "application/json",
|
|
277
|
+
},
|
|
278
|
+
signal: abortSignal,
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
if (!response.ok) {
|
|
282
|
+
const errorText = await response.text();
|
|
283
|
+
throw new Error(
|
|
284
|
+
`Higgsfield polling error (${response.status}): ${errorText}`,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const jobSet = (await response.json()) as {
|
|
289
|
+
jobs?: Array<{
|
|
290
|
+
status?: string;
|
|
291
|
+
results?: {
|
|
292
|
+
min?: { url?: string };
|
|
293
|
+
raw?: { url?: string };
|
|
294
|
+
};
|
|
295
|
+
}>;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
// Check if jobs array exists and has at least one job
|
|
299
|
+
if (
|
|
300
|
+
!jobSet?.jobs ||
|
|
301
|
+
!Array.isArray(jobSet.jobs) ||
|
|
302
|
+
jobSet.jobs.length === 0
|
|
303
|
+
) {
|
|
304
|
+
throw new Error("No jobs found in Higgsfield JobSet response");
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const job = jobSet.jobs[0];
|
|
308
|
+
const jobStatus = job?.status;
|
|
309
|
+
|
|
310
|
+
if (jobStatus === "completed") {
|
|
311
|
+
const results = job?.results;
|
|
312
|
+
|
|
313
|
+
if (results) {
|
|
314
|
+
// Try to get URL from results object
|
|
315
|
+
const imageUrl = results.min?.url ?? results.raw?.url;
|
|
316
|
+
|
|
317
|
+
if (imageUrl) {
|
|
318
|
+
return imageUrl;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
throw new Error("No result URL found in completed Higgsfield job");
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (jobStatus === "failed") {
|
|
326
|
+
throw new Error("Higgsfield job failed");
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (jobStatus === "nsfw") {
|
|
330
|
+
throw new Error("Higgsfield job rejected due to NSFW content");
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Still processing, wait before next poll
|
|
334
|
+
if (attempt < maxAttempts - 1) {
|
|
335
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
throw new Error("Higgsfield generation timed out after 5 minutes");
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export interface HiggsfieldProviderSettings {
|
|
344
|
+
apiKey?: string;
|
|
345
|
+
apiSecret?: string;
|
|
346
|
+
baseURL?: string;
|
|
347
|
+
defaultModelSettings?: HiggsfieldImageModelSettings;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export interface HiggsfieldProvider {
|
|
351
|
+
imageModel(
|
|
352
|
+
modelId: ImageModelId | (string & {}),
|
|
353
|
+
settings?: HiggsfieldImageModelSettings,
|
|
354
|
+
): ImageModelV3;
|
|
355
|
+
/** Available Soul styles for image generation */
|
|
356
|
+
styles: typeof SOUL_STYLES;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export function createHiggsfield(
|
|
360
|
+
settings: HiggsfieldProviderSettings = {},
|
|
361
|
+
): HiggsfieldProvider {
|
|
362
|
+
return {
|
|
363
|
+
imageModel(
|
|
364
|
+
modelId: string,
|
|
365
|
+
modelSettings?: HiggsfieldImageModelSettings,
|
|
366
|
+
): ImageModelV3 {
|
|
367
|
+
return new HiggsfieldImageModel(modelId, {
|
|
368
|
+
apiKey: settings.apiKey,
|
|
369
|
+
apiSecret: settings.apiSecret,
|
|
370
|
+
baseURL: settings.baseURL,
|
|
371
|
+
...settings.defaultModelSettings,
|
|
372
|
+
...modelSettings,
|
|
373
|
+
});
|
|
374
|
+
},
|
|
375
|
+
styles: SOUL_STYLES,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export const higgsfield = createHiggsfield();
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createOpenAI as createOpenAIBase,
|
|
3
|
+
type OpenAIProvider as OpenAIProviderBase,
|
|
4
|
+
type OpenAIProviderSettings,
|
|
5
|
+
} from "@ai-sdk/openai";
|
|
6
|
+
import type { SharedV3Warning } from "@ai-sdk/provider";
|
|
7
|
+
import type { VideoModelV3, VideoModelV3CallOptions } from "../video-model";
|
|
8
|
+
|
|
9
|
+
// re-export base types
|
|
10
|
+
export type { OpenAIProviderSettings };
|
|
11
|
+
|
|
12
|
+
const VIDEO_MODELS = ["sora-2", "sora-2-pro"] as const;
|
|
13
|
+
type VideoModelId = (typeof VIDEO_MODELS)[number];
|
|
14
|
+
|
|
15
|
+
const SIZE_MAP: Record<string, string> = {
|
|
16
|
+
"9:16": "720x1280",
|
|
17
|
+
"16:9": "1280x720",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
class OpenAIVideoModel implements VideoModelV3 {
|
|
21
|
+
readonly specificationVersion = "v3" as const;
|
|
22
|
+
readonly provider = "openai";
|
|
23
|
+
readonly modelId: string;
|
|
24
|
+
readonly maxVideosPerCall = 1;
|
|
25
|
+
|
|
26
|
+
private apiKey: string;
|
|
27
|
+
private baseURL: string;
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
modelId: string,
|
|
31
|
+
options: { apiKey?: string; baseURL?: string } = {},
|
|
32
|
+
) {
|
|
33
|
+
this.modelId = modelId;
|
|
34
|
+
this.apiKey = options.apiKey ?? process.env.OPENAI_API_KEY ?? "";
|
|
35
|
+
this.baseURL = options.baseURL ?? "https://api.openai.com/v1";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async doGenerate(options: VideoModelV3CallOptions) {
|
|
39
|
+
const {
|
|
40
|
+
prompt,
|
|
41
|
+
duration,
|
|
42
|
+
aspectRatio,
|
|
43
|
+
files,
|
|
44
|
+
providerOptions,
|
|
45
|
+
abortSignal,
|
|
46
|
+
} = options;
|
|
47
|
+
const warnings: SharedV3Warning[] = [];
|
|
48
|
+
|
|
49
|
+
// build form data
|
|
50
|
+
const formData = new FormData();
|
|
51
|
+
formData.append("model", this.modelId);
|
|
52
|
+
formData.append("prompt", prompt);
|
|
53
|
+
|
|
54
|
+
// duration: sora accepts 4, 8, 12 seconds
|
|
55
|
+
const seconds = duration ?? 4;
|
|
56
|
+
const validSeconds = [4, 8, 12].includes(seconds) ? seconds : 4;
|
|
57
|
+
if (duration && ![4, 8, 12].includes(duration)) {
|
|
58
|
+
warnings.push({
|
|
59
|
+
type: "unsupported",
|
|
60
|
+
feature: "duration",
|
|
61
|
+
details: `Duration ${duration}s not supported. Using ${validSeconds}s. Sora supports: 4, 8, 12 seconds.`,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
formData.append("seconds", String(validSeconds));
|
|
65
|
+
|
|
66
|
+
// size from aspect ratio
|
|
67
|
+
if (aspectRatio) {
|
|
68
|
+
const size = SIZE_MAP[aspectRatio];
|
|
69
|
+
if (size) {
|
|
70
|
+
formData.append("size", size);
|
|
71
|
+
} else {
|
|
72
|
+
warnings.push({
|
|
73
|
+
type: "unsupported",
|
|
74
|
+
feature: "aspectRatio",
|
|
75
|
+
details: `Aspect ratio ${aspectRatio} not directly supported. Use 9:16, 16:9, or 1:1.`,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// image input for i2v
|
|
81
|
+
if (files && files.length > 0) {
|
|
82
|
+
const imageFile = files.find((f) => {
|
|
83
|
+
if (f.type === "file") return f.mediaType?.startsWith("image/");
|
|
84
|
+
return /\.(jpg|jpeg|png|webp)$/i.test(f.url);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (imageFile) {
|
|
88
|
+
let blob: Blob;
|
|
89
|
+
if (imageFile.type === "file") {
|
|
90
|
+
const data =
|
|
91
|
+
typeof imageFile.data === "string"
|
|
92
|
+
? Uint8Array.from(atob(imageFile.data), (c) => c.charCodeAt(0))
|
|
93
|
+
: imageFile.data;
|
|
94
|
+
blob = new Blob([data], { type: imageFile.mediaType });
|
|
95
|
+
} else {
|
|
96
|
+
const response = await fetch(imageFile.url, { signal: abortSignal });
|
|
97
|
+
blob = await response.blob();
|
|
98
|
+
}
|
|
99
|
+
formData.append("input_reference", blob, "input.png");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// provider options passthrough
|
|
104
|
+
const openaiOptions = providerOptions?.openai as
|
|
105
|
+
| Record<string, unknown>
|
|
106
|
+
| undefined;
|
|
107
|
+
if (openaiOptions) {
|
|
108
|
+
for (const [key, value] of Object.entries(openaiOptions)) {
|
|
109
|
+
if (
|
|
110
|
+
value !== undefined &&
|
|
111
|
+
!["model", "prompt", "seconds", "size", "input_reference"].includes(
|
|
112
|
+
key,
|
|
113
|
+
)
|
|
114
|
+
) {
|
|
115
|
+
formData.append(key, String(value));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// unsupported options
|
|
121
|
+
if (options.seed !== undefined) {
|
|
122
|
+
warnings.push({
|
|
123
|
+
type: "unsupported",
|
|
124
|
+
feature: "seed",
|
|
125
|
+
details: "Seed is not supported by OpenAI Sora",
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
if (options.fps !== undefined) {
|
|
129
|
+
warnings.push({
|
|
130
|
+
type: "unsupported",
|
|
131
|
+
feature: "fps",
|
|
132
|
+
details: "FPS is not configurable for Sora",
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
if (options.resolution !== undefined) {
|
|
136
|
+
warnings.push({
|
|
137
|
+
type: "unsupported",
|
|
138
|
+
feature: "resolution",
|
|
139
|
+
details: "Use aspectRatio instead. Sora supports 9:16, 16:9, 1:1.",
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// create video job
|
|
144
|
+
const createResponse = await fetch(`${this.baseURL}/videos`, {
|
|
145
|
+
method: "POST",
|
|
146
|
+
headers: {
|
|
147
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
148
|
+
},
|
|
149
|
+
body: formData,
|
|
150
|
+
signal: abortSignal,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
if (!createResponse.ok) {
|
|
154
|
+
const error = await createResponse.text();
|
|
155
|
+
throw new Error(`OpenAI video creation failed: ${error}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const job = (await createResponse.json()) as {
|
|
159
|
+
id: string;
|
|
160
|
+
status: string;
|
|
161
|
+
progress?: number;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// poll for completion
|
|
165
|
+
let status = job.status;
|
|
166
|
+
const videoId = job.id;
|
|
167
|
+
|
|
168
|
+
while (status === "queued" || status === "in_progress") {
|
|
169
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
170
|
+
|
|
171
|
+
const statusResponse = await fetch(`${this.baseURL}/videos/${videoId}`, {
|
|
172
|
+
headers: {
|
|
173
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
174
|
+
},
|
|
175
|
+
signal: abortSignal,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
if (!statusResponse.ok) {
|
|
179
|
+
throw new Error(
|
|
180
|
+
`Failed to check video status: ${await statusResponse.text()}`,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const statusData = (await statusResponse.json()) as {
|
|
185
|
+
status: string;
|
|
186
|
+
progress?: number;
|
|
187
|
+
};
|
|
188
|
+
status = statusData.status;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (status === "failed") {
|
|
192
|
+
throw new Error("OpenAI video generation failed");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// download completed video
|
|
196
|
+
const contentResponse = await fetch(
|
|
197
|
+
`${this.baseURL}/videos/${videoId}/content`,
|
|
198
|
+
{
|
|
199
|
+
headers: {
|
|
200
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
201
|
+
},
|
|
202
|
+
signal: abortSignal,
|
|
203
|
+
},
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
if (!contentResponse.ok) {
|
|
207
|
+
throw new Error(
|
|
208
|
+
`Failed to download video: ${await contentResponse.text()}`,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const videoBuffer = await contentResponse.arrayBuffer();
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
videos: [new Uint8Array(videoBuffer)],
|
|
216
|
+
warnings,
|
|
217
|
+
response: {
|
|
218
|
+
timestamp: new Date(),
|
|
219
|
+
modelId: this.modelId,
|
|
220
|
+
headers: undefined,
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export interface OpenAIProvider extends OpenAIProviderBase {
|
|
227
|
+
videoModel(modelId: VideoModelId): VideoModelV3;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function createOpenAI(
|
|
231
|
+
settings: OpenAIProviderSettings = {},
|
|
232
|
+
): OpenAIProvider {
|
|
233
|
+
const base = createOpenAIBase(settings);
|
|
234
|
+
|
|
235
|
+
// create a callable function that also has all the methods
|
|
236
|
+
const provider = ((modelId: string) => base(modelId)) as OpenAIProvider;
|
|
237
|
+
|
|
238
|
+
// copy all properties from base
|
|
239
|
+
Object.assign(provider, base);
|
|
240
|
+
|
|
241
|
+
// add videoModel method
|
|
242
|
+
provider.videoModel = (modelId: VideoModelId): VideoModelV3 =>
|
|
243
|
+
new OpenAIVideoModel(modelId, {
|
|
244
|
+
apiKey: settings.apiKey,
|
|
245
|
+
baseURL: settings.baseURL,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
return provider;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export const openai = createOpenAI();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replicate provider - re-exports @ai-sdk/replicate
|
|
3
|
+
*
|
|
4
|
+
* For background removal, use prompt.images:
|
|
5
|
+
*
|
|
6
|
+
* const { image } = await generateImage({
|
|
7
|
+
* model: replicate.image("851-labs/background-remover"),
|
|
8
|
+
* prompt: { images: [imageData] },
|
|
9
|
+
* });
|
|
10
|
+
*/
|
|
11
|
+
export {
|
|
12
|
+
createReplicate,
|
|
13
|
+
type ReplicateProvider,
|
|
14
|
+
type ReplicateProviderSettings,
|
|
15
|
+
replicate,
|
|
16
|
+
} from "@ai-sdk/replicate";
|