ugly-app 0.1.105 → 0.1.107
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/README.md +39 -13
- package/dist/cli/version.d.ts +1 -1
- package/dist/cli/version.js +1 -1
- package/dist/server/ai/ImageGenFactory.d.ts +7 -4
- package/dist/server/ai/ImageGenFactory.d.ts.map +1 -1
- package/dist/server/ai/ImageGenFactory.js +54 -39
- package/dist/server/ai/ImageGenFactory.js.map +1 -1
- package/dist/server/ai/providers/FAL.d.ts.map +1 -1
- package/dist/server/ai/providers/FAL.js +16 -6
- package/dist/server/ai/providers/FAL.js.map +1 -1
- package/dist/server/ai/providers/GoogleImage.d.ts.map +1 -1
- package/dist/server/ai/providers/GoogleImage.js +7 -2
- package/dist/server/ai/providers/GoogleImage.js.map +1 -1
- package/dist/server/ai/providers/KieImage.d.ts.map +1 -1
- package/dist/server/ai/providers/KieImage.js +7 -2
- package/dist/server/ai/providers/KieImage.js.map +1 -1
- package/dist/server/ai/providers/TogetherImage.d.ts.map +1 -1
- package/dist/server/ai/providers/TogetherImage.js +8 -1
- package/dist/server/ai/providers/TogetherImage.js.map +1 -1
- package/dist/server/ai/providers/Wavespeed.d.ts.map +1 -1
- package/dist/server/ai/providers/Wavespeed.js +8 -2
- package/dist/server/ai/providers/Wavespeed.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/version.ts +1 -1
- package/src/server/ai/ImageGenFactory.ts +75 -45
- package/src/server/ai/providers/FAL.ts +22 -6
- package/src/server/ai/providers/GoogleImage.ts +10 -2
- package/src/server/ai/providers/KieImage.ts +11 -2
- package/src/server/ai/providers/TogetherImage.ts +13 -1
- package/src/server/ai/providers/Wavespeed.ts +13 -2
package/README.md
CHANGED
|
@@ -84,6 +84,7 @@ The returned `App` object has:
|
|
|
84
84
|
- `registerRoutes(fn)` — mount additional Express routes after creation
|
|
85
85
|
- `httpServer` — the underlying Node.js HTTP server
|
|
86
86
|
- `db` — the `TypedDB` instance for direct database access
|
|
87
|
+
- `wss` — the main `WebSocketServer` instance (path configured via `setWsPath`, default `'/rpc'`)
|
|
87
88
|
- `dispatch(name, input, userId)` — invoke an RPC handler programmatically
|
|
88
89
|
|
|
89
90
|
### `AppConfigurator`
|
|
@@ -99,6 +100,12 @@ The optional fourth argument to `createApp` receives a configurator object:
|
|
|
99
100
|
| `setOnSocketMessage(handler)` | Handle raw WebSocket messages. Return `true` to consume, `false` to let the framework handle it |
|
|
100
101
|
| `registerRoutes(fn)` | Mount custom Express routes — `(router: express.Router) => void` |
|
|
101
102
|
| `setWorkerQueue(queue)` | Register a background worker queue with `start()` and `stop()` |
|
|
103
|
+
| `setWsPath(path)` | Override the WebSocket path (default: `'/rpc'`) |
|
|
104
|
+
| `setOnWsAuth(handler)` | Called after a WebSocket session is authenticated. `(ws, userId, req) => void` |
|
|
105
|
+
| `setOnAfterStart(handler)` | Called once after MongoDB, Redis, and NATS are ready. `(db) => Promise<void>` |
|
|
106
|
+
| `setOnMinuteTick(handler)` | Fires every minute (only when `CLOCK_ENABLED=true`). `() => Promise<void>` |
|
|
107
|
+
| `setOnHourlyTick(handler)` | Fires when the hour changes (only when `CLOCK_ENABLED=true`). `(now, currentHour) => Promise<void>` |
|
|
108
|
+
| `setHealthHandler(handler)` | Override the default `GET /health` endpoint handler. `(req, res) => void` |
|
|
102
109
|
|
|
103
110
|
### Handler signatures
|
|
104
111
|
|
|
@@ -163,11 +170,11 @@ import type { Note } from './types';
|
|
|
163
170
|
export const collections = defineCollections({
|
|
164
171
|
note: {
|
|
165
172
|
type: {} as Note,
|
|
166
|
-
meta: { cache: true, trackable: true, public: false,
|
|
173
|
+
meta: { cache: true, trackable: true, public: false, cascadeFrom: null },
|
|
167
174
|
},
|
|
168
175
|
user: {
|
|
169
176
|
type: {} as User,
|
|
170
|
-
meta: { cache: true, trackable: false, public: false,
|
|
177
|
+
meta: { cache: true, trackable: false, public: false, cascadeFrom: null },
|
|
171
178
|
},
|
|
172
179
|
});
|
|
173
180
|
```
|
|
@@ -175,13 +182,14 @@ export const collections = defineCollections({
|
|
|
175
182
|
Each collection definition has:
|
|
176
183
|
- `type` — phantom field for TypeScript type inference (never read at runtime)
|
|
177
184
|
- `meta` — runtime metadata:
|
|
178
|
-
- `cache` — enable in-memory caching
|
|
179
|
-
- `trackable` — allow real-time `trackDoc`/`trackDocs` subscriptions
|
|
180
|
-
- `public` —
|
|
181
|
-
- `
|
|
182
|
-
- `
|
|
185
|
+
- `cache` — enable in-memory LRU caching for `getDoc`; `setDoc`/`deleteDoc` invalidate it
|
|
186
|
+
- `trackable` — allow real-time `trackDoc`/`trackDocs` subscriptions via Change Streams
|
|
187
|
+
- `public` — allows client reads via `getDoc`/`trackDoc`/`trackDocs` without auth
|
|
188
|
+
- `cascadeFrom` — parent collection name for cascade deletes (or `null`)
|
|
189
|
+
- `trackKeys?` — fields usable as NATS routing keys for `trackDocs`
|
|
190
|
+
- `onDelete?` — optional async callback invoked on document deletion
|
|
183
191
|
|
|
184
|
-
All documents extend `DBObject`: `{
|
|
192
|
+
All documents extend `DBObject`: `{ _id: string, version: number, created: Date, updated: Date }`.
|
|
185
193
|
|
|
186
194
|
### Pages (`shared/pages.ts`)
|
|
187
195
|
|
|
@@ -254,13 +262,30 @@ await socket.connect(token); // returns UserBase
|
|
|
254
262
|
| Method | Description |
|
|
255
263
|
|--------|-------------|
|
|
256
264
|
| `connect(token)` | Authenticate and connect. Returns the user object |
|
|
257
|
-
| `request(name, input)` | Invoke a request (query or mutation) |
|
|
265
|
+
| `request(name, input)` | Invoke a typed request (query or mutation) |
|
|
258
266
|
| `getDoc(collection, id)` | Fetch a single document |
|
|
267
|
+
| `getDocs(collection, filter?, opts?)` | Query documents (filter, sort, limit, skip) |
|
|
268
|
+
| `getQuery(collection, pipeline, opts?)` | Run an aggregation pipeline |
|
|
259
269
|
| `trackDoc(collection, id, cb)` | Subscribe to real-time doc changes. Returns unsubscribe fn |
|
|
260
|
-
| `trackDocs(collection,
|
|
270
|
+
| `trackDocs(collection, params, cb)` | Subscribe to query results (`keys`, `filter`, `sort`, `limit`, `skip`). Returns unsubscribe fn |
|
|
261
271
|
| `uploadFile(file, key)` | Upload a file via presigned URL |
|
|
272
|
+
| `emit(type, data)` | Send a fire-and-forget message over WebSocket |
|
|
273
|
+
| `send(type, data, timeout?)` | Send a message and wait for a response |
|
|
274
|
+
| `waitForConnection(timeout?)` | Wait until the socket is connected |
|
|
275
|
+
| `connectionState` | Current state: `'connecting'` \| `'connected'` \| `'reconnecting'` \| `'disconnected'` \| `'idle-disconnected'` |
|
|
262
276
|
| `disconnect()` | Close the connection |
|
|
263
277
|
|
|
278
|
+
**`createSocket()` options:**
|
|
279
|
+
|
|
280
|
+
| Option | Description |
|
|
281
|
+
|--------|-------------|
|
|
282
|
+
| `requests` | The requests registry from `shared/api.ts` |
|
|
283
|
+
| `url?` | WebSocket path (default: `'/rpc'`) |
|
|
284
|
+
| `buildId?` | Build identifier sent on connect |
|
|
285
|
+
| `onCustomMessage?` | Handle custom server-pushed messages |
|
|
286
|
+
| `getUrlParams?` | Extra query params appended to the WebSocket URL |
|
|
287
|
+
| `messageReviver?` | JSON reviver for incoming messages (e.g. Date parsing) |
|
|
288
|
+
|
|
264
289
|
### `createHttpClient()`
|
|
265
290
|
|
|
266
291
|
Creates a typed HTTP client for RPC communication (no WebSocket needed).
|
|
@@ -524,7 +549,7 @@ The `AuthProvider` interface:
|
|
|
524
549
|
|
|
525
550
|
```typescript
|
|
526
551
|
interface AuthProvider {
|
|
527
|
-
verify(code: string): Promise<{ userId: string; email?: string; phone?: string }>;
|
|
552
|
+
verify(code: string): Promise<{ userId: string; email?: string; phone?: string; token?: string }>;
|
|
528
553
|
authUrl(origin: string): string;
|
|
529
554
|
registerRoutes?(router: express.Router): void;
|
|
530
555
|
}
|
|
@@ -578,8 +603,8 @@ const docs = await db.rawGetDocs(collectionName, filter);
|
|
|
578
603
|
### Deleting
|
|
579
604
|
|
|
580
605
|
```typescript
|
|
581
|
-
await db.deleteDoc(collections.note, id);
|
|
582
|
-
await db.deleteQuery(collections.note, { userId }); // bulk delete by filter
|
|
606
|
+
await db.deleteDoc(collections.note, id); // single doc (cascades via cascadeFrom)
|
|
607
|
+
await db.deleteQuery(collections.note, { userId }); // bulk delete by filter (cascades + calls onDelete)
|
|
583
608
|
```
|
|
584
609
|
|
|
585
610
|
### Caching
|
|
@@ -948,6 +973,7 @@ Run with `npm run db:migrate`. Use `npm run db:migrate -- --status` to preview p
|
|
|
948
973
|
| `KIE_API_KEY` | Kie.ai key |
|
|
949
974
|
| `KIE_BASE_URL` | Kie.ai base URL override (optional) |
|
|
950
975
|
| `MAILGUN_FROM` | Default from address |
|
|
976
|
+
| `CLOCK_ENABLED` | Set to `true` to enable `setOnMinuteTick`/`setOnHourlyTick` handlers |
|
|
951
977
|
|
|
952
978
|
Client-side variables must be prefixed with `VITE_`.
|
|
953
979
|
|
package/dist/cli/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.1.
|
|
1
|
+
export declare const CLI_VERSION = "0.1.107";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/cli/version.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ImageGenModel } from '../../shared/ImageGen.js';
|
|
2
2
|
import type { ImageGenBase } from './ImageGenBase.js';
|
|
3
3
|
/**
|
|
4
|
-
* Create an ImageGen instance for the given
|
|
5
|
-
*
|
|
4
|
+
* Create an ImageGen instance for the given clean model name.
|
|
5
|
+
* Automatically selects the best available provider based on priority.
|
|
6
|
+
*
|
|
7
|
+
* @param model - Clean model name (e.g. 'flux_1_dev', 'seedream', 'nano_banana')
|
|
8
|
+
* @param priority - 'cheap' (default) selects cheapest provider; 'fast' selects lowest latency
|
|
6
9
|
*/
|
|
7
|
-
export declare function imageGenCreate(model:
|
|
10
|
+
export declare function imageGenCreate(model: ImageGenModel, priority?: 'fast' | 'cheap'): ImageGenBase;
|
|
8
11
|
//# sourceMappingURL=ImageGenFactory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageGenFactory.d.ts","sourceRoot":"","sources":["../../../src/server/ai/ImageGenFactory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ImageGenFactory.d.ts","sourceRoot":"","sources":["../../../src/server/ai/ImageGenFactory.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,aAAa,EAGd,MAAM,0BAA0B,CAAC;AAKlC,OAAO,KAAK,EACV,YAAY,EAGb,MAAM,mBAAmB,CAAC;AA8G3B;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,aAAa,EACpB,QAAQ,GAAE,MAAM,GAAG,OAAiB,GACnC,YAAY,CAgBd"}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Image generation factory with automatic provider selection.
|
|
2
|
+
// Callers use clean model names (e.g. 'flux_1_dev') and an optional priority.
|
|
3
|
+
// The factory selects the best available provider and passes the model through.
|
|
4
|
+
import { imageGenMultiProviderModels, } from '../../shared/ImageGen.js';
|
|
2
5
|
import { falImageProvider } from './providers/FAL.js';
|
|
3
6
|
import { googleImageProvider } from './providers/GoogleImage.js';
|
|
4
7
|
import { kieImageProvider } from './providers/KieImage.js';
|
|
@@ -32,10 +35,37 @@ async function urlToBase64(url) {
|
|
|
32
35
|
const buffer = await res.arrayBuffer();
|
|
33
36
|
return Buffer.from(buffer).toString('base64');
|
|
34
37
|
}
|
|
38
|
+
const providerMap = {
|
|
39
|
+
together: togetherImageProvider,
|
|
40
|
+
fireworks: togetherImageProvider, // Fireworks FLUX uses Together's API
|
|
41
|
+
fal: falImageProvider,
|
|
42
|
+
google: googleImageProvider,
|
|
43
|
+
kie: kieImageProvider,
|
|
44
|
+
wavespeed: wavespeedImageProvider,
|
|
45
|
+
};
|
|
46
|
+
function selectOffering(model, priority) {
|
|
47
|
+
const offerings = imageGenMultiProviderModels[model];
|
|
48
|
+
if (!offerings || offerings.length === 0) {
|
|
49
|
+
throw new Error(`[ImageGenFactory] No providers configured for model ${model}`);
|
|
50
|
+
}
|
|
51
|
+
const available = offerings.filter((o) => o.available);
|
|
52
|
+
if (available.length === 0) {
|
|
53
|
+
throw new Error(`[ImageGenFactory] No available provider for model ${model}`);
|
|
54
|
+
}
|
|
55
|
+
if (priority === 'fast') {
|
|
56
|
+
const tierOrder = { fast: 0, standard: 1, slow: 2 };
|
|
57
|
+
const sorted = [...available].sort((a, b) => tierOrder[a.latencyTier] - tierOrder[b.latencyTier]);
|
|
58
|
+
return sorted[0];
|
|
59
|
+
}
|
|
60
|
+
// 'cheap': first entry (array is ordered cheapest first)
|
|
61
|
+
return available[0];
|
|
62
|
+
}
|
|
35
63
|
class ImageGenImpl {
|
|
36
64
|
provider;
|
|
37
|
-
|
|
65
|
+
model;
|
|
66
|
+
constructor(provider, model) {
|
|
38
67
|
this.provider = provider;
|
|
68
|
+
this.model = model;
|
|
39
69
|
}
|
|
40
70
|
async generate(input) {
|
|
41
71
|
if (input.images?.some(Boolean)) {
|
|
@@ -45,7 +75,11 @@ class ImageGenImpl {
|
|
|
45
75
|
const prompt = input.negativePrompt
|
|
46
76
|
? `${input.prompt} --no ${input.negativePrompt}`
|
|
47
77
|
: input.prompt;
|
|
48
|
-
const imageUrl = await this.provider.generate(prompt, {
|
|
78
|
+
const imageUrl = await this.provider.generate(prompt, {
|
|
79
|
+
width,
|
|
80
|
+
height,
|
|
81
|
+
model: this.model,
|
|
82
|
+
});
|
|
49
83
|
const base64 = await urlToBase64(imageUrl);
|
|
50
84
|
return {
|
|
51
85
|
type: 'base64',
|
|
@@ -57,43 +91,24 @@ class ImageGenImpl {
|
|
|
57
91
|
}
|
|
58
92
|
}
|
|
59
93
|
/**
|
|
60
|
-
* Create an ImageGen instance for the given
|
|
61
|
-
*
|
|
94
|
+
* Create an ImageGen instance for the given clean model name.
|
|
95
|
+
* Automatically selects the best available provider based on priority.
|
|
96
|
+
*
|
|
97
|
+
* @param model - Clean model name (e.g. 'flux_1_dev', 'seedream', 'nano_banana')
|
|
98
|
+
* @param priority - 'cheap' (default) selects cheapest provider; 'fast' selects lowest latency
|
|
62
99
|
*/
|
|
63
|
-
export function imageGenCreate(model) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
case 'fal_seedream_v4_5':
|
|
69
|
-
return new ImageGenImpl(falImageProvider);
|
|
70
|
-
// Google models
|
|
71
|
-
case 'google_nano':
|
|
72
|
-
case 'google_nano_pro':
|
|
73
|
-
return new ImageGenImpl(googleImageProvider);
|
|
74
|
-
// Together models
|
|
75
|
-
case 'together_flux_schnell':
|
|
76
|
-
case 'together_flux_dev':
|
|
77
|
-
case 'together_flux_pro':
|
|
78
|
-
case 'together_flux_kontext_dev':
|
|
79
|
-
case 'together_flux_kontext_pro':
|
|
80
|
-
case 'together_flux_kontext_max':
|
|
81
|
-
return new ImageGenImpl(togetherImageProvider);
|
|
82
|
-
// Fireworks models — use Together image provider (same FLUX models)
|
|
83
|
-
case 'fireworks_flux_dev':
|
|
84
|
-
case 'fireworks_flux_pro':
|
|
85
|
-
return new ImageGenImpl(togetherImageProvider);
|
|
86
|
-
// Kie.ai models
|
|
87
|
-
case 'kie_nano_banana':
|
|
88
|
-
case 'kie_nano_banana_pro':
|
|
89
|
-
return new ImageGenImpl(kieImageProvider);
|
|
90
|
-
// WaveSpeed models
|
|
91
|
-
case 'wavespeed_seedream_v4_5':
|
|
92
|
-
case 'wavespeed_flux_dev':
|
|
93
|
-
case 'wavespeed_flux_schnell':
|
|
94
|
-
return new ImageGenImpl(wavespeedImageProvider);
|
|
95
|
-
default:
|
|
96
|
-
throw new Error(`Model does not exist ${model}`);
|
|
100
|
+
export function imageGenCreate(model, priority = 'cheap') {
|
|
101
|
+
const offering = selectOffering(model, priority);
|
|
102
|
+
const provider = providerMap[offering.provider];
|
|
103
|
+
if (!provider) {
|
|
104
|
+
throw new Error(`[ImageGenFactory] Unknown provider ${offering.provider}`);
|
|
97
105
|
}
|
|
106
|
+
console.log('[ImageGenFactory] Selected provider', {
|
|
107
|
+
model,
|
|
108
|
+
provider: offering.provider,
|
|
109
|
+
providerModel: offering.providerModel,
|
|
110
|
+
reason: priority === 'fast' ? 'fastest_available' : 'cheapest_available',
|
|
111
|
+
});
|
|
112
|
+
return new ImageGenImpl(provider, model);
|
|
98
113
|
}
|
|
99
114
|
//# sourceMappingURL=ImageGenFactory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageGenFactory.js","sourceRoot":"","sources":["../../../src/server/ai/ImageGenFactory.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"ImageGenFactory.js","sourceRoot":"","sources":["../../../src/server/ai/ImageGenFactory.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FAL.d.ts","sourceRoot":"","sources":["../../../../src/server/ai/providers/FAL.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FAL.d.ts","sourceRoot":"","sources":["../../../../src/server/ai/providers/FAL.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAmB,gBAAgB,EAAE,MAAM,aAAa,CAAC;AA+CrE,eAAO,MAAM,gBAAgB,EAAE,gBA4B9B,CAAC"}
|
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
1
|
+
const FAL_ENDPOINTS = {
|
|
2
|
+
flux_1_dev: 'fal-ai/flux/dev',
|
|
3
|
+
flux_1_pro: 'fal-ai/flux-pro/v1.1',
|
|
4
|
+
seedream: 'fal-ai/seedream-v4.5',
|
|
5
|
+
};
|
|
6
|
+
const DEFAULT_ENDPOINT = 'fal-ai/flux-pro/v1.1';
|
|
7
|
+
function getBaseUrl(model) {
|
|
8
|
+
const endpoint = (model && FAL_ENDPOINTS[model]) ?? DEFAULT_ENDPOINT;
|
|
9
|
+
return `https://queue.fal.run/${endpoint}`;
|
|
10
|
+
}
|
|
11
|
+
async function poll(apiKey, baseUrl, requestId) {
|
|
3
12
|
for (let i = 0; i < 60; i++) {
|
|
4
13
|
await new Promise((r) => setTimeout(r, 2000));
|
|
5
|
-
const res = await fetch(`${
|
|
14
|
+
const res = await fetch(`${baseUrl}/requests/${requestId}/status`, {
|
|
6
15
|
headers: { Authorization: `Key ${apiKey}` },
|
|
7
16
|
});
|
|
8
17
|
if (!res.ok)
|
|
9
18
|
throw new Error(`[FAL] API error ${res.status}: ${await res.text()}`);
|
|
10
19
|
const data = (await res.json());
|
|
11
20
|
if (data.status === 'COMPLETED') {
|
|
12
|
-
const resultRes = await fetch(`${
|
|
21
|
+
const resultRes = await fetch(`${baseUrl}/requests/${requestId}`, {
|
|
13
22
|
headers: { Authorization: `Key ${apiKey}` },
|
|
14
23
|
});
|
|
15
24
|
if (!resultRes.ok)
|
|
@@ -32,7 +41,8 @@ export const falImageProvider = {
|
|
|
32
41
|
const apiKey = process.env.FAL_API_KEY;
|
|
33
42
|
if (!apiKey)
|
|
34
43
|
throw new Error('[FAL] FAL_API_KEY is required');
|
|
35
|
-
const
|
|
44
|
+
const baseUrl = getBaseUrl(options.model);
|
|
45
|
+
const res = await fetch(baseUrl, {
|
|
36
46
|
method: 'POST',
|
|
37
47
|
headers: {
|
|
38
48
|
'Content-Type': 'application/json',
|
|
@@ -49,7 +59,7 @@ export const falImageProvider = {
|
|
|
49
59
|
if (!res.ok)
|
|
50
60
|
throw new Error(`[FAL] API error ${res.status}: ${await res.text()}`);
|
|
51
61
|
const data = (await res.json());
|
|
52
|
-
return poll(apiKey, data.request_id);
|
|
62
|
+
return poll(apiKey, baseUrl, data.request_id);
|
|
53
63
|
},
|
|
54
64
|
};
|
|
55
65
|
//# sourceMappingURL=FAL.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FAL.js","sourceRoot":"","sources":["../../../../src/server/ai/providers/FAL.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FAL.js","sourceRoot":"","sources":["../../../../src/server/ai/providers/FAL.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GoogleImage.d.ts","sourceRoot":"","sources":["../../../../src/server/ai/providers/GoogleImage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"GoogleImage.d.ts","sourceRoot":"","sources":["../../../../src/server/ai/providers/GoogleImage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAmB,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAOrE,eAAO,MAAM,mBAAmB,EAAE,gBA+BjC,CAAC"}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
const API_MODELS = {
|
|
2
|
+
nano_banana: 'imagen-3.0-generate-001',
|
|
3
|
+
nano_banana_pro: 'imagen-3.0-generate-001',
|
|
4
|
+
};
|
|
1
5
|
export const googleImageProvider = {
|
|
2
6
|
name: 'google',
|
|
3
7
|
apiKeyEnv: 'GOOGLE_API_KEY',
|
|
4
|
-
async generate(prompt,
|
|
8
|
+
async generate(prompt, options = {}) {
|
|
5
9
|
const apiKey = process.env.GOOGLE_API_KEY;
|
|
6
10
|
if (!apiKey)
|
|
7
11
|
throw new Error('[GoogleImage] GOOGLE_API_KEY is required');
|
|
8
|
-
const
|
|
12
|
+
const model = (options.model && API_MODELS[options.model]) ?? 'imagen-3.0-generate-001';
|
|
13
|
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:predict?key=${apiKey}`;
|
|
9
14
|
const res = await fetch(url, {
|
|
10
15
|
method: 'POST',
|
|
11
16
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GoogleImage.js","sourceRoot":"","sources":["../../../../src/server/ai/providers/GoogleImage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"GoogleImage.js","sourceRoot":"","sources":["../../../../src/server/ai/providers/GoogleImage.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,GAA2C;IACzD,WAAW,EAAE,yBAAyB;IACtC,eAAe,EAAE,yBAAyB;CAC3C,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAqB;IACnD,IAAI,EAAE,QAAQ;IACd,SAAS,EAAE,gBAAgB;IAC3B,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,UAA2B,EAAE;QAClD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;QAC1C,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAEzE,MAAM,KAAK,GACT,CAAC,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,yBAAyB,CAAC;QAC5E,MAAM,GAAG,GAAG,2DAA2D,KAAK,gBAAgB,MAAM,EAAE,CAAC;QACrG,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC3B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;gBACvB,UAAU,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE;aAC/B,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE;YACT,MAAM,IAAI,KAAK,CACb,2BAA2B,GAAG,CAAC,MAAM,KAAK,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CAC7D,CAAC;QAEJ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,UAAU,GAAG,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC;QAC1C,IAAI,CAAC,UAAU,EAAE,kBAAkB,IAAI,CAAC,UAAU,EAAE,QAAQ;YAC1D,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAE7D,OAAO,QAAQ,UAAU,CAAC,QAAQ,WAAW,UAAU,CAAC,kBAAkB,EAAE,CAAC;IAC/E,CAAC;CACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KieImage.d.ts","sourceRoot":"","sources":["../../../../src/server/ai/providers/KieImage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"KieImage.d.ts","sourceRoot":"","sources":["../../../../src/server/ai/providers/KieImage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAmB,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAgCrE,eAAO,MAAM,gBAAgB,EAAE,gBA6B9B,CAAC"}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
const
|
|
1
|
+
const API_MODELS = {
|
|
2
|
+
nano_banana: 'kolors',
|
|
3
|
+
nano_banana_pro: 'kolors',
|
|
4
|
+
};
|
|
5
|
+
const DEFAULT_MODEL = 'kolors';
|
|
2
6
|
const BASE_URL = 'https://api.kie.ai/api/v1';
|
|
3
7
|
async function pollKie(apiKey, taskId) {
|
|
4
8
|
for (let i = 0; i < 60; i++) {
|
|
@@ -27,7 +31,8 @@ export const kieImageProvider = {
|
|
|
27
31
|
const apiKey = process.env.KIE_API_KEY;
|
|
28
32
|
if (!apiKey)
|
|
29
33
|
throw new Error('[KieImage] KIE_API_KEY is required');
|
|
30
|
-
const
|
|
34
|
+
const model = (options.model && API_MODELS[options.model]) ?? DEFAULT_MODEL;
|
|
35
|
+
const res = await fetch(`${BASE_URL}/${model}/img2img`, {
|
|
31
36
|
method: 'POST',
|
|
32
37
|
headers: {
|
|
33
38
|
'Content-Type': 'application/json',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KieImage.js","sourceRoot":"","sources":["../../../../src/server/ai/providers/KieImage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"KieImage.js","sourceRoot":"","sources":["../../../../src/server/ai/providers/KieImage.ts"],"names":[],"mappings":"AAIA,MAAM,UAAU,GAA2C;IACzD,WAAW,EAAE,QAAQ;IACrB,eAAe,EAAE,QAAQ;CAC1B,CAAC;AAEF,MAAM,aAAa,GAAG,QAAQ,CAAC;AAC/B,MAAM,QAAQ,GAAG,2BAA2B,CAAC;AAE7C,KAAK,UAAU,OAAO,CAAC,MAAc,EAAE,MAAc;IACnD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QAC9C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,8BAA8B,MAAM,EAAE,EAAE;YACzE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,MAAM,EAAE,EAAE;SAC/C,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACpE,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAG7B,CAAC;QACF,IAAI,IAAI,CAAC,IAAI,EAAE,MAAM,KAAK,WAAW,EAAE,CAAC;YACtC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC;YACxC,IAAI,CAAC,GAAG;gBAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;YACjE,OAAO,GAAG,CAAC;QACb,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,EAAE,MAAM,KAAK,QAAQ;YAChC,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACpD,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAqB;IAChD,IAAI,EAAE,KAAK;IACX,SAAS,EAAE,aAAa;IACxB,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,UAA2B,EAAE;QAClD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;QACvC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACnE,MAAM,KAAK,GACT,CAAC,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,aAAa,CAAC;QAEhE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,IAAI,KAAK,UAAU,EAAE;YACtD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,eAAe,EAAE,UAAU,MAAM,EAAE;aACpC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,MAAM;gBACN,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI;gBAC5B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI;aAC/B,CAAC;SACH,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACtE,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAG7B,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QAC5E,OAAO,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;CACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TogetherImage.d.ts","sourceRoot":"","sources":["../../../../src/server/ai/providers/TogetherImage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TogetherImage.d.ts","sourceRoot":"","sources":["../../../../src/server/ai/providers/TogetherImage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAmB,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAQrE,eAAO,MAAM,qBAAqB,EAAE,gBAmCnC,CAAC"}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
const API_MODELS = {
|
|
2
|
+
flux_1_schnell: 'black-forest-labs/FLUX.1-schnell',
|
|
3
|
+
flux_1_dev: 'black-forest-labs/FLUX.1-dev',
|
|
4
|
+
flux_1_pro: 'black-forest-labs/FLUX.1.1-pro',
|
|
5
|
+
};
|
|
1
6
|
export const togetherImageProvider = {
|
|
2
7
|
name: 'together',
|
|
3
8
|
apiKeyEnv: 'TOGETHER_API_KEY',
|
|
@@ -5,6 +10,8 @@ export const togetherImageProvider = {
|
|
|
5
10
|
const apiKey = process.env.TOGETHER_API_KEY;
|
|
6
11
|
if (!apiKey)
|
|
7
12
|
throw new Error('[TogetherImage] TOGETHER_API_KEY is required');
|
|
13
|
+
const model = (options.model && API_MODELS[options.model]) ??
|
|
14
|
+
'black-forest-labs/FLUX.1-schnell';
|
|
8
15
|
const res = await fetch('https://api.together.xyz/v1/images/generations', {
|
|
9
16
|
method: 'POST',
|
|
10
17
|
headers: {
|
|
@@ -12,7 +19,7 @@ export const togetherImageProvider = {
|
|
|
12
19
|
'Content-Type': 'application/json',
|
|
13
20
|
},
|
|
14
21
|
body: JSON.stringify({
|
|
15
|
-
model
|
|
22
|
+
model,
|
|
16
23
|
prompt,
|
|
17
24
|
n: 1,
|
|
18
25
|
width: options.width ?? 1024,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TogetherImage.js","sourceRoot":"","sources":["../../../../src/server/ai/providers/TogetherImage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TogetherImage.js","sourceRoot":"","sources":["../../../../src/server/ai/providers/TogetherImage.ts"],"names":[],"mappings":"AAIA,MAAM,UAAU,GAA2C;IACzD,cAAc,EAAE,kCAAkC;IAClD,UAAU,EAAE,8BAA8B;IAC1C,UAAU,EAAE,gCAAgC;CAC7C,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAqB;IACrD,IAAI,EAAE,UAAU;IAChB,SAAS,EAAE,kBAAkB;IAC7B,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,UAA2B,EAAE;QAClD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;QAC5C,IAAI,CAAC,MAAM;YACT,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;QAElE,MAAM,KAAK,GACT,CAAC,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC5C,kCAAkC,CAAC;QAErC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,gDAAgD,EAAE;YACxE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,eAAe,EAAE,UAAU,MAAM,EAAE;gBACnC,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK;gBACL,MAAM;gBACN,CAAC,EAAE,CAAC;gBACJ,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI;gBAC5B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI;aAC/B,CAAC;SACH,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YACT,MAAM,IAAI,KAAK,CACb,6BAA6B,GAAG,CAAC,MAAM,KAAK,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CAC/D,CAAC;QACJ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;QAC9B,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QAChE,OAAO,GAAG,CAAC;IACb,CAAC;CACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Wavespeed.d.ts","sourceRoot":"","sources":["../../../../src/server/ai/providers/Wavespeed.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Wavespeed.d.ts","sourceRoot":"","sources":["../../../../src/server/ai/providers/Wavespeed.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAmB,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAgCrE,eAAO,MAAM,sBAAsB,EAAE,gBAmCpC,CAAC"}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
const
|
|
1
|
+
const API_MODELS = {
|
|
2
|
+
flux_1_dev: 'wavespeed-ai/flux-dev',
|
|
3
|
+
flux_1_schnell: 'wavespeed-ai/flux-schnell',
|
|
4
|
+
seedream: 'wavespeed-ai/seedream-v4.5',
|
|
5
|
+
};
|
|
6
|
+
const DEFAULT_MODEL = 'wavespeed-ai/flux-dev';
|
|
2
7
|
const BASE_URL = 'https://api.wavespeed.ai/api/v3';
|
|
3
8
|
async function poll(apiKey, id) {
|
|
4
9
|
for (let i = 0; i < 60; i++) {
|
|
@@ -27,7 +32,8 @@ export const wavespeedImageProvider = {
|
|
|
27
32
|
const apiKey = process.env.WAVESPEED_API_KEY;
|
|
28
33
|
if (!apiKey)
|
|
29
34
|
throw new Error('[Wavespeed] WAVESPEED_API_KEY is required');
|
|
30
|
-
const
|
|
35
|
+
const model = (options.model && API_MODELS[options.model]) ?? DEFAULT_MODEL;
|
|
36
|
+
const res = await fetch(`${BASE_URL}/${model}`, {
|
|
31
37
|
method: 'POST',
|
|
32
38
|
headers: {
|
|
33
39
|
'Content-Type': 'application/json',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Wavespeed.js","sourceRoot":"","sources":["../../../../src/server/ai/providers/Wavespeed.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Wavespeed.js","sourceRoot":"","sources":["../../../../src/server/ai/providers/Wavespeed.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/cli/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by prebuild — do not edit manually
|
|
2
|
-
export const CLI_VERSION = "0.1.
|
|
2
|
+
export const CLI_VERSION = "0.1.107";
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Image generation factory with automatic provider selection.
|
|
2
|
+
// Callers use clean model names (e.g. 'flux_1_dev') and an optional priority.
|
|
3
|
+
// The factory selects the best available provider and passes the model through.
|
|
2
4
|
|
|
3
5
|
import type {
|
|
4
|
-
|
|
6
|
+
ImageGenModel,
|
|
7
|
+
ImageGenProviderOffering,
|
|
5
8
|
ImageGenSize,
|
|
6
9
|
} from '../../shared/ImageGen.js';
|
|
10
|
+
import {
|
|
11
|
+
imageGenMultiProviderModels,
|
|
12
|
+
type ImageGenLatencyTier,
|
|
13
|
+
} from '../../shared/ImageGen.js';
|
|
7
14
|
import type {
|
|
8
15
|
ImageGenBase,
|
|
9
16
|
ImageGenPromptInput,
|
|
@@ -46,8 +53,46 @@ async function urlToBase64(url: string): Promise<string> {
|
|
|
46
53
|
return Buffer.from(buffer).toString('base64');
|
|
47
54
|
}
|
|
48
55
|
|
|
56
|
+
const providerMap: Record<string, ImageGenProvider> = {
|
|
57
|
+
together: togetherImageProvider,
|
|
58
|
+
fireworks: togetherImageProvider, // Fireworks FLUX uses Together's API
|
|
59
|
+
fal: falImageProvider,
|
|
60
|
+
google: googleImageProvider,
|
|
61
|
+
kie: kieImageProvider,
|
|
62
|
+
wavespeed: wavespeedImageProvider,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
function selectOffering(
|
|
66
|
+
model: ImageGenModel,
|
|
67
|
+
priority: 'fast' | 'cheap',
|
|
68
|
+
): ImageGenProviderOffering {
|
|
69
|
+
const offerings = imageGenMultiProviderModels[model];
|
|
70
|
+
if (!offerings || offerings.length === 0) {
|
|
71
|
+
throw new Error(`[ImageGenFactory] No providers configured for model ${model}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const available = offerings.filter((o) => o.available);
|
|
75
|
+
if (available.length === 0) {
|
|
76
|
+
throw new Error(`[ImageGenFactory] No available provider for model ${model}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (priority === 'fast') {
|
|
80
|
+
const tierOrder: Record<ImageGenLatencyTier, number> = { fast: 0, standard: 1, slow: 2 };
|
|
81
|
+
const sorted = [...available].sort(
|
|
82
|
+
(a, b) => tierOrder[a.latencyTier] - tierOrder[b.latencyTier],
|
|
83
|
+
);
|
|
84
|
+
return sorted[0]!;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 'cheap': first entry (array is ordered cheapest first)
|
|
88
|
+
return available[0]!;
|
|
89
|
+
}
|
|
90
|
+
|
|
49
91
|
class ImageGenImpl implements ImageGenBase {
|
|
50
|
-
constructor(
|
|
92
|
+
constructor(
|
|
93
|
+
private readonly provider: ImageGenProvider,
|
|
94
|
+
private readonly model: ImageGenModel,
|
|
95
|
+
) {}
|
|
51
96
|
|
|
52
97
|
async generate(
|
|
53
98
|
input: ImageGenPromptInput,
|
|
@@ -63,7 +108,11 @@ class ImageGenImpl implements ImageGenBase {
|
|
|
63
108
|
? `${input.prompt} --no ${input.negativePrompt}`
|
|
64
109
|
: input.prompt;
|
|
65
110
|
|
|
66
|
-
const imageUrl = await this.provider.generate(prompt, {
|
|
111
|
+
const imageUrl = await this.provider.generate(prompt, {
|
|
112
|
+
width,
|
|
113
|
+
height,
|
|
114
|
+
model: this.model,
|
|
115
|
+
});
|
|
67
116
|
const base64 = await urlToBase64(imageUrl);
|
|
68
117
|
|
|
69
118
|
return {
|
|
@@ -77,48 +126,29 @@ class ImageGenImpl implements ImageGenBase {
|
|
|
77
126
|
}
|
|
78
127
|
|
|
79
128
|
/**
|
|
80
|
-
* Create an ImageGen instance for the given
|
|
81
|
-
*
|
|
129
|
+
* Create an ImageGen instance for the given clean model name.
|
|
130
|
+
* Automatically selects the best available provider based on priority.
|
|
131
|
+
*
|
|
132
|
+
* @param model - Clean model name (e.g. 'flux_1_dev', 'seedream', 'nano_banana')
|
|
133
|
+
* @param priority - 'cheap' (default) selects cheapest provider; 'fast' selects lowest latency
|
|
82
134
|
*/
|
|
83
|
-
export function imageGenCreate(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return new ImageGenImpl(falImageProvider);
|
|
90
|
-
|
|
91
|
-
// Google models
|
|
92
|
-
case 'google_nano':
|
|
93
|
-
case 'google_nano_pro':
|
|
94
|
-
return new ImageGenImpl(googleImageProvider);
|
|
95
|
-
|
|
96
|
-
// Together models
|
|
97
|
-
case 'together_flux_schnell':
|
|
98
|
-
case 'together_flux_dev':
|
|
99
|
-
case 'together_flux_pro':
|
|
100
|
-
case 'together_flux_kontext_dev':
|
|
101
|
-
case 'together_flux_kontext_pro':
|
|
102
|
-
case 'together_flux_kontext_max':
|
|
103
|
-
return new ImageGenImpl(togetherImageProvider);
|
|
104
|
-
|
|
105
|
-
// Fireworks models — use Together image provider (same FLUX models)
|
|
106
|
-
case 'fireworks_flux_dev':
|
|
107
|
-
case 'fireworks_flux_pro':
|
|
108
|
-
return new ImageGenImpl(togetherImageProvider);
|
|
109
|
-
|
|
110
|
-
// Kie.ai models
|
|
111
|
-
case 'kie_nano_banana':
|
|
112
|
-
case 'kie_nano_banana_pro':
|
|
113
|
-
return new ImageGenImpl(kieImageProvider);
|
|
114
|
-
|
|
115
|
-
// WaveSpeed models
|
|
116
|
-
case 'wavespeed_seedream_v4_5':
|
|
117
|
-
case 'wavespeed_flux_dev':
|
|
118
|
-
case 'wavespeed_flux_schnell':
|
|
119
|
-
return new ImageGenImpl(wavespeedImageProvider);
|
|
135
|
+
export function imageGenCreate(
|
|
136
|
+
model: ImageGenModel,
|
|
137
|
+
priority: 'fast' | 'cheap' = 'cheap',
|
|
138
|
+
): ImageGenBase {
|
|
139
|
+
const offering = selectOffering(model, priority);
|
|
140
|
+
const provider = providerMap[offering.provider];
|
|
120
141
|
|
|
121
|
-
|
|
122
|
-
|
|
142
|
+
if (!provider) {
|
|
143
|
+
throw new Error(`[ImageGenFactory] Unknown provider ${offering.provider}`);
|
|
123
144
|
}
|
|
145
|
+
|
|
146
|
+
console.log('[ImageGenFactory] Selected provider', {
|
|
147
|
+
model,
|
|
148
|
+
provider: offering.provider,
|
|
149
|
+
providerModel: offering.providerModel,
|
|
150
|
+
reason: priority === 'fast' ? 'fastest_available' : 'cheapest_available',
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
return new ImageGenImpl(provider, model);
|
|
124
154
|
}
|
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
// src/server/ai/providers/FAL.ts
|
|
2
|
+
import type { ImageGenModel } from '../../../shared/ImageGen.js';
|
|
2
3
|
import type { ImageGenOptions, ImageGenProvider } from '../types.js';
|
|
3
4
|
|
|
4
|
-
const
|
|
5
|
+
const FAL_ENDPOINTS: Partial<Record<ImageGenModel, string>> = {
|
|
6
|
+
flux_1_dev: 'fal-ai/flux/dev',
|
|
7
|
+
flux_1_pro: 'fal-ai/flux-pro/v1.1',
|
|
8
|
+
seedream: 'fal-ai/seedream-v4.5',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const DEFAULT_ENDPOINT = 'fal-ai/flux-pro/v1.1';
|
|
12
|
+
|
|
13
|
+
function getBaseUrl(model?: ImageGenModel): string {
|
|
14
|
+
const endpoint =
|
|
15
|
+
(model && FAL_ENDPOINTS[model]) ?? DEFAULT_ENDPOINT;
|
|
16
|
+
return `https://queue.fal.run/${endpoint}`;
|
|
17
|
+
}
|
|
5
18
|
|
|
6
|
-
async function poll(apiKey: string, requestId: string): Promise<string> {
|
|
19
|
+
async function poll(apiKey: string, baseUrl: string, requestId: string): Promise<string> {
|
|
7
20
|
for (let i = 0; i < 60; i++) {
|
|
8
21
|
await new Promise((r) => setTimeout(r, 2000));
|
|
9
|
-
const res = await fetch(`${
|
|
22
|
+
const res = await fetch(`${baseUrl}/requests/${requestId}/status`, {
|
|
10
23
|
headers: { Authorization: `Key ${apiKey}` },
|
|
11
24
|
});
|
|
12
25
|
if (!res.ok)
|
|
@@ -15,7 +28,7 @@ async function poll(apiKey: string, requestId: string): Promise<string> {
|
|
|
15
28
|
status: 'IN_QUEUE' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED';
|
|
16
29
|
};
|
|
17
30
|
if (data.status === 'COMPLETED') {
|
|
18
|
-
const resultRes = await fetch(`${
|
|
31
|
+
const resultRes = await fetch(`${baseUrl}/requests/${requestId}`, {
|
|
19
32
|
headers: { Authorization: `Key ${apiKey}` },
|
|
20
33
|
});
|
|
21
34
|
if (!resultRes.ok)
|
|
@@ -40,7 +53,10 @@ export const falImageProvider: ImageGenProvider = {
|
|
|
40
53
|
async generate(prompt, options: ImageGenOptions = {}) {
|
|
41
54
|
const apiKey = process.env.FAL_API_KEY;
|
|
42
55
|
if (!apiKey) throw new Error('[FAL] FAL_API_KEY is required');
|
|
43
|
-
|
|
56
|
+
|
|
57
|
+
const baseUrl = getBaseUrl(options.model);
|
|
58
|
+
|
|
59
|
+
const res = await fetch(baseUrl, {
|
|
44
60
|
method: 'POST',
|
|
45
61
|
headers: {
|
|
46
62
|
'Content-Type': 'application/json',
|
|
@@ -57,6 +73,6 @@ export const falImageProvider: ImageGenProvider = {
|
|
|
57
73
|
if (!res.ok)
|
|
58
74
|
throw new Error(`[FAL] API error ${res.status}: ${await res.text()}`);
|
|
59
75
|
const data = (await res.json()) as { request_id: string };
|
|
60
|
-
return poll(apiKey, data.request_id);
|
|
76
|
+
return poll(apiKey, baseUrl, data.request_id);
|
|
61
77
|
},
|
|
62
78
|
};
|
|
@@ -1,13 +1,21 @@
|
|
|
1
|
+
import type { ImageGenModel } from '../../../shared/ImageGen.js';
|
|
1
2
|
import type { ImageGenOptions, ImageGenProvider } from '../types.js';
|
|
2
3
|
|
|
4
|
+
const API_MODELS: Partial<Record<ImageGenModel, string>> = {
|
|
5
|
+
nano_banana: 'imagen-3.0-generate-001',
|
|
6
|
+
nano_banana_pro: 'imagen-3.0-generate-001',
|
|
7
|
+
};
|
|
8
|
+
|
|
3
9
|
export const googleImageProvider: ImageGenProvider = {
|
|
4
10
|
name: 'google',
|
|
5
11
|
apiKeyEnv: 'GOOGLE_API_KEY',
|
|
6
|
-
async generate(prompt,
|
|
12
|
+
async generate(prompt, options: ImageGenOptions = {}) {
|
|
7
13
|
const apiKey = process.env.GOOGLE_API_KEY;
|
|
8
14
|
if (!apiKey) throw new Error('[GoogleImage] GOOGLE_API_KEY is required');
|
|
9
15
|
|
|
10
|
-
const
|
|
16
|
+
const model =
|
|
17
|
+
(options.model && API_MODELS[options.model]) ?? 'imagen-3.0-generate-001';
|
|
18
|
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:predict?key=${apiKey}`;
|
|
11
19
|
const res = await fetch(url, {
|
|
12
20
|
method: 'POST',
|
|
13
21
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
// src/server/ai/providers/KieImage.ts
|
|
2
|
+
import type { ImageGenModel } from '../../../shared/ImageGen.js';
|
|
2
3
|
import type { ImageGenOptions, ImageGenProvider } from '../types.js';
|
|
3
4
|
|
|
4
|
-
const
|
|
5
|
+
const API_MODELS: Partial<Record<ImageGenModel, string>> = {
|
|
6
|
+
nano_banana: 'kolors',
|
|
7
|
+
nano_banana_pro: 'kolors',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const DEFAULT_MODEL = 'kolors';
|
|
5
11
|
const BASE_URL = 'https://api.kie.ai/api/v1';
|
|
6
12
|
|
|
7
13
|
async function pollKie(apiKey: string, taskId: string): Promise<string> {
|
|
@@ -32,7 +38,10 @@ export const kieImageProvider: ImageGenProvider = {
|
|
|
32
38
|
async generate(prompt, options: ImageGenOptions = {}) {
|
|
33
39
|
const apiKey = process.env.KIE_API_KEY;
|
|
34
40
|
if (!apiKey) throw new Error('[KieImage] KIE_API_KEY is required');
|
|
35
|
-
const
|
|
41
|
+
const model =
|
|
42
|
+
(options.model && API_MODELS[options.model]) ?? DEFAULT_MODEL;
|
|
43
|
+
|
|
44
|
+
const res = await fetch(`${BASE_URL}/${model}/img2img`, {
|
|
36
45
|
method: 'POST',
|
|
37
46
|
headers: {
|
|
38
47
|
'Content-Type': 'application/json',
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
// src/server/ai/providers/TogetherImage.ts
|
|
2
|
+
import type { ImageGenModel } from '../../../shared/ImageGen.js';
|
|
2
3
|
import type { ImageGenOptions, ImageGenProvider } from '../types.js';
|
|
3
4
|
|
|
5
|
+
const API_MODELS: Partial<Record<ImageGenModel, string>> = {
|
|
6
|
+
flux_1_schnell: 'black-forest-labs/FLUX.1-schnell',
|
|
7
|
+
flux_1_dev: 'black-forest-labs/FLUX.1-dev',
|
|
8
|
+
flux_1_pro: 'black-forest-labs/FLUX.1.1-pro',
|
|
9
|
+
};
|
|
10
|
+
|
|
4
11
|
export const togetherImageProvider: ImageGenProvider = {
|
|
5
12
|
name: 'together',
|
|
6
13
|
apiKeyEnv: 'TOGETHER_API_KEY',
|
|
@@ -8,6 +15,11 @@ export const togetherImageProvider: ImageGenProvider = {
|
|
|
8
15
|
const apiKey = process.env.TOGETHER_API_KEY;
|
|
9
16
|
if (!apiKey)
|
|
10
17
|
throw new Error('[TogetherImage] TOGETHER_API_KEY is required');
|
|
18
|
+
|
|
19
|
+
const model =
|
|
20
|
+
(options.model && API_MODELS[options.model]) ??
|
|
21
|
+
'black-forest-labs/FLUX.1-schnell';
|
|
22
|
+
|
|
11
23
|
const res = await fetch('https://api.together.xyz/v1/images/generations', {
|
|
12
24
|
method: 'POST',
|
|
13
25
|
headers: {
|
|
@@ -15,7 +27,7 @@ export const togetherImageProvider: ImageGenProvider = {
|
|
|
15
27
|
'Content-Type': 'application/json',
|
|
16
28
|
},
|
|
17
29
|
body: JSON.stringify({
|
|
18
|
-
model
|
|
30
|
+
model,
|
|
19
31
|
prompt,
|
|
20
32
|
n: 1,
|
|
21
33
|
width: options.width ?? 1024,
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
// src/server/ai/providers/Wavespeed.ts
|
|
2
|
+
import type { ImageGenModel } from '../../../shared/ImageGen.js';
|
|
2
3
|
import type { ImageGenOptions, ImageGenProvider } from '../types.js';
|
|
3
4
|
|
|
4
|
-
const
|
|
5
|
+
const API_MODELS: Partial<Record<ImageGenModel, string>> = {
|
|
6
|
+
flux_1_dev: 'wavespeed-ai/flux-dev',
|
|
7
|
+
flux_1_schnell: 'wavespeed-ai/flux-schnell',
|
|
8
|
+
seedream: 'wavespeed-ai/seedream-v4.5',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const DEFAULT_MODEL = 'wavespeed-ai/flux-dev';
|
|
5
12
|
const BASE_URL = 'https://api.wavespeed.ai/api/v3';
|
|
6
13
|
|
|
7
14
|
async function poll(apiKey: string, id: string): Promise<string> {
|
|
@@ -31,7 +38,11 @@ export const wavespeedImageProvider: ImageGenProvider = {
|
|
|
31
38
|
async generate(prompt, options: ImageGenOptions = {}) {
|
|
32
39
|
const apiKey = process.env.WAVESPEED_API_KEY;
|
|
33
40
|
if (!apiKey) throw new Error('[Wavespeed] WAVESPEED_API_KEY is required');
|
|
34
|
-
|
|
41
|
+
|
|
42
|
+
const model =
|
|
43
|
+
(options.model && API_MODELS[options.model]) ?? DEFAULT_MODEL;
|
|
44
|
+
|
|
45
|
+
const res = await fetch(`${BASE_URL}/${model}`, {
|
|
35
46
|
method: 'POST',
|
|
36
47
|
headers: {
|
|
37
48
|
'Content-Type': 'application/json',
|