@uploadista/flow-images-replicate 0.0.3
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/.turbo/turbo-build.log +5 -0
- package/LICENSE +21 -0
- package/README.md +236 -0
- package/dist/image-ai-plugin.d.ts +42 -0
- package/dist/image-ai-plugin.d.ts.map +1 -0
- package/dist/image-ai-plugin.js +147 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/package.json +31 -0
- package/src/image-ai-plugin.ts +211 -0
- package/src/index.ts +1 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsbuildinfo +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 uploadista
|
|
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
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# @uploadista/flow-images-replicate
|
|
2
|
+
|
|
3
|
+
AI-powered image operations for Uploadista flows. Use Replicate's ML models for advanced image manipulation.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Replicate integration enables AI image operations:
|
|
8
|
+
|
|
9
|
+
- **Background Removal**: AI-powered background removal
|
|
10
|
+
- **Upscaling**: Enhance image resolution
|
|
11
|
+
- **Style Transfer**: Apply artistic styles
|
|
12
|
+
- **Custom Models**: Use any Replicate model
|
|
13
|
+
- **Serverless**: No GPU infrastructure needed
|
|
14
|
+
|
|
15
|
+
Perfect for advanced image processing workflows.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @uploadista/flow-images-replicate
|
|
21
|
+
# or
|
|
22
|
+
pnpm add @uploadista/flow-images-replicate
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Prerequisites
|
|
26
|
+
|
|
27
|
+
- Replicate API token (get from replicate.com)
|
|
28
|
+
- Node.js 18+
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { imageAiPlugin } from "@uploadista/flow-images-replicate";
|
|
34
|
+
|
|
35
|
+
const flow = {
|
|
36
|
+
nodes: [
|
|
37
|
+
{ id: "input", type: "input" },
|
|
38
|
+
{
|
|
39
|
+
id: "remove-bg",
|
|
40
|
+
type: "background-removal",
|
|
41
|
+
params: { model: "rembg" },
|
|
42
|
+
},
|
|
43
|
+
{ id: "s3", type: "s3" },
|
|
44
|
+
{ id: "output", type: "output" },
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Features
|
|
50
|
+
|
|
51
|
+
- ✅ **AI Models**: Background removal, upscaling, effects
|
|
52
|
+
- ✅ **No GPU Needed**: Replicate handles compute
|
|
53
|
+
- ✅ **Serverless**: Pay per request
|
|
54
|
+
- ✅ **Webhook Support**: Async processing
|
|
55
|
+
- ✅ **Custom Models**: Use any Replicate model
|
|
56
|
+
|
|
57
|
+
## Node Types
|
|
58
|
+
|
|
59
|
+
### Background Removal
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
{
|
|
63
|
+
type: "remove-background",
|
|
64
|
+
params: {
|
|
65
|
+
model: "rembg", // AI model
|
|
66
|
+
returnFormat: "png",
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Upscaling
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
{
|
|
75
|
+
type: "upscale",
|
|
76
|
+
params: {
|
|
77
|
+
scale: 4, // 2x, 4x upscaling
|
|
78
|
+
model: "real-esrgan",
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Style Transfer
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
{
|
|
87
|
+
type: "style-transfer",
|
|
88
|
+
params: {
|
|
89
|
+
style: "oil-painting",
|
|
90
|
+
model: "arbitrary-style-transfer",
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Configuration
|
|
96
|
+
|
|
97
|
+
Set API token:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
export const config = {
|
|
101
|
+
replicate: {
|
|
102
|
+
apiToken: process.env.REPLICATE_API_TOKEN,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Use Cases
|
|
108
|
+
|
|
109
|
+
- Remove backgrounds from product photos
|
|
110
|
+
- Upscale low-resolution images
|
|
111
|
+
- Apply artistic effects
|
|
112
|
+
- Custom ML image processing
|
|
113
|
+
- Batch AI operations
|
|
114
|
+
|
|
115
|
+
## Examples
|
|
116
|
+
|
|
117
|
+
### Product Image Processing
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
const productFlow = {
|
|
121
|
+
nodes: [
|
|
122
|
+
{ id: "input", type: "input" },
|
|
123
|
+
{
|
|
124
|
+
id: "remove-bg",
|
|
125
|
+
type: "remove-background",
|
|
126
|
+
params: { model: "rembg" },
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: "upscale",
|
|
130
|
+
type: "upscale",
|
|
131
|
+
params: { scale: 2, model: "real-esrgan" },
|
|
132
|
+
},
|
|
133
|
+
{ id: "s3", type: "s3", params: { bucket: "products" } },
|
|
134
|
+
{ id: "output", type: "output" },
|
|
135
|
+
],
|
|
136
|
+
edges: [
|
|
137
|
+
{ from: "input", to: "remove-bg" },
|
|
138
|
+
{ from: "remove-bg", to: "upscale" },
|
|
139
|
+
{ from: "upscale", to: "s3" },
|
|
140
|
+
{ from: "s3", to: "output" },
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Creative Effects
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
const creativeFlow = {
|
|
149
|
+
nodes: [
|
|
150
|
+
{ id: "input", type: "input" },
|
|
151
|
+
{
|
|
152
|
+
id: "style",
|
|
153
|
+
type: "style-transfer",
|
|
154
|
+
params: {
|
|
155
|
+
style: "oil-painting",
|
|
156
|
+
model: "arbitrary-style-transfer",
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
{ id: "upscale", type: "upscale", params: { scale: 2 } },
|
|
160
|
+
{ id: "s3", type: "s3" },
|
|
161
|
+
{ id: "output", type: "output" },
|
|
162
|
+
],
|
|
163
|
+
};
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Performance
|
|
167
|
+
|
|
168
|
+
| Operation | Time |
|
|
169
|
+
|-----------|------|
|
|
170
|
+
| Background removal | 5-15s |
|
|
171
|
+
| Upscaling (2x) | 10-20s |
|
|
172
|
+
| Style transfer | 15-30s |
|
|
173
|
+
| Async webhook | Variable |
|
|
174
|
+
|
|
175
|
+
All operations asynchronous via webhook.
|
|
176
|
+
|
|
177
|
+
## Cost
|
|
178
|
+
|
|
179
|
+
Replicate pricing (varies by model):
|
|
180
|
+
- Background removal: ~$0.001-0.002 per image
|
|
181
|
+
- Upscaling: ~$0.01-0.05 per image
|
|
182
|
+
- Style transfer: ~$0.02-0.10 per image
|
|
183
|
+
|
|
184
|
+
## Best Practices
|
|
185
|
+
|
|
186
|
+
### 1. Async Processing
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// Use webhooks for long-running AI ops
|
|
190
|
+
{
|
|
191
|
+
type: "remove-background",
|
|
192
|
+
params: {
|
|
193
|
+
model: "rembg",
|
|
194
|
+
webhook: "https://yourapi.com/callbacks",
|
|
195
|
+
async: true,
|
|
196
|
+
},
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### 2. Batch Operations
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// Process multiple images in parallel
|
|
204
|
+
const batch = [image1, image2, image3];
|
|
205
|
+
const results = yield* Effect.all(
|
|
206
|
+
batch.map((img) => removeBackground(img))
|
|
207
|
+
);
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### 3. Error Handling
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
// AI models may fail or timeout
|
|
214
|
+
yield* removeBackground(image).pipe(
|
|
215
|
+
Effect.catch((error) => {
|
|
216
|
+
// Fallback to standard processing
|
|
217
|
+
return standardBackgroundRemoval(image);
|
|
218
|
+
})
|
|
219
|
+
);
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Related Packages
|
|
223
|
+
|
|
224
|
+
- [@uploadista/flow-images-nodes](../nodes) - Base types
|
|
225
|
+
- [@uploadista/flow-images-sharp](../sharp) - Standard processing
|
|
226
|
+
- [@uploadista/server](../../servers/server) - Upload server
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
See [LICENSE](../../../LICENSE) in the main repository.
|
|
231
|
+
|
|
232
|
+
## See Also
|
|
233
|
+
|
|
234
|
+
- [Replicate Documentation](https://replicate.com/docs) - Replicate API
|
|
235
|
+
- [FLOW_NODES.md](../FLOW_NODES.md) - All available nodes
|
|
236
|
+
- [Replicate Models](https://replicate.com/explore) - Available AI models
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { UploadistaError } from "@uploadista/core/errors";
|
|
2
|
+
import { type ImageAiContext, ImageAiPlugin } from "@uploadista/core/flow";
|
|
3
|
+
import { Effect, Layer } from "effect";
|
|
4
|
+
type ModelId = `${string}/${string}` | `${string}/${string}:${string}`;
|
|
5
|
+
type ReplicateCredentials = {
|
|
6
|
+
apiKey: string;
|
|
7
|
+
};
|
|
8
|
+
type CredentialProvider = (context: ImageAiContext & {
|
|
9
|
+
serviceType: "replicate";
|
|
10
|
+
}) => Effect.Effect<ReplicateCredentials, UploadistaError>;
|
|
11
|
+
type PluginConfig = string | {
|
|
12
|
+
credentialProvider?: CredentialProvider;
|
|
13
|
+
useCredentialProviderService?: boolean;
|
|
14
|
+
removeBackgroundModelId?: ModelId;
|
|
15
|
+
describeImageModelId?: ModelId;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Create the Replicate ImageAI plugin
|
|
19
|
+
* Supports both static credentials (OSS) and dynamic credential providers (SaaS)
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // Static credentials (OSS)
|
|
23
|
+
* imageAiPlugin(process.env.REPLICATE_API_TOKEN)
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // Dynamic credentials with function (SaaS)
|
|
27
|
+
* imageAiPlugin({
|
|
28
|
+
* credentialProvider: (context) => Effect.succeed({ apiKey: "..." })
|
|
29
|
+
* })
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Dynamic credentials with Effect service (SaaS)
|
|
33
|
+
* imageAiPlugin({
|
|
34
|
+
* useCredentialProviderService: true
|
|
35
|
+
* })
|
|
36
|
+
*/
|
|
37
|
+
export declare const imageAiPlugin: (config: PluginConfig, options?: {
|
|
38
|
+
removeBackgroundModelId?: ModelId;
|
|
39
|
+
describeImageModelId?: ModelId;
|
|
40
|
+
}) => Layer.Layer<ImageAiPlugin, never, never>;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=image-ai-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-ai-plugin.d.ts","sourceRoot":"","sources":["../src/image-ai-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAEL,KAAK,cAAc,EACnB,aAAa,EACd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,KAAK,EAAU,MAAM,QAAQ,CAAC;AAG/C,KAAK,OAAO,GAAG,GAAG,MAAM,IAAI,MAAM,EAAE,GAAG,GAAG,MAAM,IAAI,MAAM,IAAI,MAAM,EAAE,CAAC;AAMvE,KAAK,oBAAoB,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAGF,KAAK,kBAAkB,GAAG,CACxB,OAAO,EAAE,cAAc,GAAG;IAAE,WAAW,EAAE,WAAW,CAAA;CAAE,KACnD,MAAM,CAAC,MAAM,CAAC,oBAAoB,EAAE,eAAe,CAAC,CAAC;AAG1D,KAAK,YAAY,GACb,MAAM,GACN;IACE,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEN;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,aAAa,GACxB,QAAQ,YAAY,EACpB,UAAU;IACR,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,6CAuJF,CAAC"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { UploadistaError } from "@uploadista/core/errors";
|
|
2
|
+
import { CredentialProvider as CredentialProviderService, ImageAiPlugin, } from "@uploadista/core/flow";
|
|
3
|
+
import { Effect, Layer, Option } from "effect";
|
|
4
|
+
import Replicate from "replicate";
|
|
5
|
+
/**
|
|
6
|
+
* Create the Replicate ImageAI plugin
|
|
7
|
+
* Supports both static credentials (OSS) and dynamic credential providers (SaaS)
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // Static credentials (OSS)
|
|
11
|
+
* imageAiPlugin(process.env.REPLICATE_API_TOKEN)
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // Dynamic credentials with function (SaaS)
|
|
15
|
+
* imageAiPlugin({
|
|
16
|
+
* credentialProvider: (context) => Effect.succeed({ apiKey: "..." })
|
|
17
|
+
* })
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // Dynamic credentials with Effect service (SaaS)
|
|
21
|
+
* imageAiPlugin({
|
|
22
|
+
* useCredentialProviderService: true
|
|
23
|
+
* })
|
|
24
|
+
*/
|
|
25
|
+
export const imageAiPlugin = (config, options) => {
|
|
26
|
+
// Parse configuration
|
|
27
|
+
const isStatic = typeof config === "string";
|
|
28
|
+
const staticApiKey = isStatic ? config : null;
|
|
29
|
+
const credentialProvider = isStatic ? null : config.credentialProvider;
|
|
30
|
+
const useCredentialProviderService = isStatic
|
|
31
|
+
? false
|
|
32
|
+
: config.useCredentialProviderService;
|
|
33
|
+
// Model IDs can come from either the config object or the options parameter
|
|
34
|
+
const removeBackgroundModelId = (isStatic
|
|
35
|
+
? options?.removeBackgroundModelId
|
|
36
|
+
: config.removeBackgroundModelId) ||
|
|
37
|
+
"lucataco/remove-bg:95fcc2a26d3899cd6c2691c900465aaeff466285a65c14638cc5f36f34befaf1";
|
|
38
|
+
const describeImageModelId = (isStatic ? options?.describeImageModelId : config.describeImageModelId) ||
|
|
39
|
+
"zsxkib/blip-3:499bec581d8f64060fd695ec0c34d7595c6824c4118259aa8b0788e0d2d903e1";
|
|
40
|
+
// Helper to get API token (either static, from provider function, or from service)
|
|
41
|
+
const getApiToken = (context) => {
|
|
42
|
+
if (staticApiKey) {
|
|
43
|
+
return Effect.succeed(staticApiKey);
|
|
44
|
+
}
|
|
45
|
+
if (useCredentialProviderService) {
|
|
46
|
+
return Effect.gen(function* () {
|
|
47
|
+
const credentialProviderService = yield* Effect.serviceOption(CredentialProviderService);
|
|
48
|
+
if (Option.isNone(credentialProviderService)) {
|
|
49
|
+
return yield* Effect.fail(UploadistaError.fromCode("UNKNOWN_ERROR", {
|
|
50
|
+
cause: new Error("Credential provider service not found"),
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const credentials = yield* credentialProviderService.value.getCredential({
|
|
55
|
+
clientId: context.clientId,
|
|
56
|
+
serviceType: "replicate",
|
|
57
|
+
});
|
|
58
|
+
if (typeof credentials === "object" &&
|
|
59
|
+
credentials !== null &&
|
|
60
|
+
"apiKey" in credentials &&
|
|
61
|
+
typeof credentials.apiKey === "string") {
|
|
62
|
+
return credentials.apiKey;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return yield* Effect.fail(UploadistaError.fromCode("UNKNOWN_ERROR", {
|
|
66
|
+
cause: new Error("Invalid credential format from service"),
|
|
67
|
+
}));
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (credentialProvider) {
|
|
71
|
+
return Effect.gen(function* () {
|
|
72
|
+
const credentials = yield* credentialProvider({
|
|
73
|
+
...context,
|
|
74
|
+
serviceType: "replicate",
|
|
75
|
+
});
|
|
76
|
+
return credentials.apiKey;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return Effect.fail(UploadistaError.fromCode("UNKNOWN_ERROR", {
|
|
80
|
+
cause: new Error("No API credentials configured"),
|
|
81
|
+
}));
|
|
82
|
+
};
|
|
83
|
+
return Layer.succeed(ImageAiPlugin, ImageAiPlugin.of({
|
|
84
|
+
removeBackground: (inputUrl, context) => {
|
|
85
|
+
return Effect.gen(function* () {
|
|
86
|
+
// Get API token (static or from credential provider)
|
|
87
|
+
const apiToken = yield* getApiToken(context);
|
|
88
|
+
const output = yield* Effect.tryPromise({
|
|
89
|
+
try: async () => {
|
|
90
|
+
const replicate = new Replicate({
|
|
91
|
+
auth: apiToken,
|
|
92
|
+
});
|
|
93
|
+
const input = {
|
|
94
|
+
image: inputUrl,
|
|
95
|
+
};
|
|
96
|
+
console.log("input", input);
|
|
97
|
+
return (await replicate.run(removeBackgroundModelId, {
|
|
98
|
+
input,
|
|
99
|
+
}));
|
|
100
|
+
},
|
|
101
|
+
catch: (error) => {
|
|
102
|
+
console.log("error", error);
|
|
103
|
+
return UploadistaError.fromCode("UNKNOWN_ERROR", {
|
|
104
|
+
cause: error,
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
return { outputUrl: output.url() };
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
describeImage: (inputUrl, context) => {
|
|
112
|
+
return Effect.gen(function* () {
|
|
113
|
+
// Get API token (static or from credential provider)
|
|
114
|
+
const apiToken = yield* getApiToken(context);
|
|
115
|
+
const output = yield* Effect.tryPromise({
|
|
116
|
+
try: async () => {
|
|
117
|
+
const replicate = new Replicate({
|
|
118
|
+
auth: apiToken,
|
|
119
|
+
});
|
|
120
|
+
return (await replicate.run(describeImageModelId, {
|
|
121
|
+
input: {
|
|
122
|
+
image: inputUrl,
|
|
123
|
+
top_k: 50,
|
|
124
|
+
top_p: 1,
|
|
125
|
+
caption: false,
|
|
126
|
+
question: "What is shown in the image?",
|
|
127
|
+
do_sample: false,
|
|
128
|
+
num_beams: 1,
|
|
129
|
+
temperature: 1,
|
|
130
|
+
system_prompt: "A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions.",
|
|
131
|
+
length_penalty: 1,
|
|
132
|
+
max_new_tokens: 768,
|
|
133
|
+
repetition_penalty: 1,
|
|
134
|
+
},
|
|
135
|
+
}));
|
|
136
|
+
},
|
|
137
|
+
catch: (error) => {
|
|
138
|
+
return UploadistaError.fromCode("UNKNOWN_ERROR", {
|
|
139
|
+
cause: error,
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
return { description: output };
|
|
144
|
+
});
|
|
145
|
+
},
|
|
146
|
+
}));
|
|
147
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./image-ai-plugin";
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@uploadista/flow-images-replicate",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.3",
|
|
5
|
+
"description": "Replicate image AI processing service for Uploadista Flow",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Uploadista",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"replicate": "1.3.0",
|
|
17
|
+
"effect": "3.18.4",
|
|
18
|
+
"zod": "4.1.12",
|
|
19
|
+
"@uploadista/core": "0.0.3"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/node": "24.8.1",
|
|
23
|
+
"@uploadista/typescript-config": "0.0.3"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc -b",
|
|
27
|
+
"format": "biome format --write ./src",
|
|
28
|
+
"lint": "biome lint --write ./src",
|
|
29
|
+
"check": "biome check --write ./src"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { UploadistaError } from "@uploadista/core/errors";
|
|
2
|
+
import {
|
|
3
|
+
CredentialProvider as CredentialProviderService,
|
|
4
|
+
type ImageAiContext,
|
|
5
|
+
ImageAiPlugin,
|
|
6
|
+
} from "@uploadista/core/flow";
|
|
7
|
+
import { Effect, Layer, Option } from "effect";
|
|
8
|
+
import Replicate from "replicate";
|
|
9
|
+
|
|
10
|
+
type ModelId = `${string}/${string}` | `${string}/${string}:${string}`;
|
|
11
|
+
|
|
12
|
+
type RemoveBackgroundOutput = {
|
|
13
|
+
url: () => string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type ReplicateCredentials = {
|
|
17
|
+
apiKey: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Credential provider function type
|
|
21
|
+
type CredentialProvider = (
|
|
22
|
+
context: ImageAiContext & { serviceType: "replicate" },
|
|
23
|
+
) => Effect.Effect<ReplicateCredentials, UploadistaError>;
|
|
24
|
+
|
|
25
|
+
// Plugin configuration can be either a static API key or options with credential provider or service
|
|
26
|
+
type PluginConfig =
|
|
27
|
+
| string
|
|
28
|
+
| {
|
|
29
|
+
credentialProvider?: CredentialProvider;
|
|
30
|
+
useCredentialProviderService?: boolean;
|
|
31
|
+
removeBackgroundModelId?: ModelId;
|
|
32
|
+
describeImageModelId?: ModelId;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Create the Replicate ImageAI plugin
|
|
37
|
+
* Supports both static credentials (OSS) and dynamic credential providers (SaaS)
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* // Static credentials (OSS)
|
|
41
|
+
* imageAiPlugin(process.env.REPLICATE_API_TOKEN)
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* // Dynamic credentials with function (SaaS)
|
|
45
|
+
* imageAiPlugin({
|
|
46
|
+
* credentialProvider: (context) => Effect.succeed({ apiKey: "..." })
|
|
47
|
+
* })
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* // Dynamic credentials with Effect service (SaaS)
|
|
51
|
+
* imageAiPlugin({
|
|
52
|
+
* useCredentialProviderService: true
|
|
53
|
+
* })
|
|
54
|
+
*/
|
|
55
|
+
export const imageAiPlugin = (
|
|
56
|
+
config: PluginConfig,
|
|
57
|
+
options?: {
|
|
58
|
+
removeBackgroundModelId?: ModelId;
|
|
59
|
+
describeImageModelId?: ModelId;
|
|
60
|
+
},
|
|
61
|
+
) => {
|
|
62
|
+
// Parse configuration
|
|
63
|
+
const isStatic = typeof config === "string";
|
|
64
|
+
const staticApiKey = isStatic ? config : null;
|
|
65
|
+
const credentialProvider = isStatic ? null : config.credentialProvider;
|
|
66
|
+
const useCredentialProviderService = isStatic
|
|
67
|
+
? false
|
|
68
|
+
: config.useCredentialProviderService;
|
|
69
|
+
|
|
70
|
+
// Model IDs can come from either the config object or the options parameter
|
|
71
|
+
const removeBackgroundModelId =
|
|
72
|
+
(isStatic
|
|
73
|
+
? options?.removeBackgroundModelId
|
|
74
|
+
: config.removeBackgroundModelId) ||
|
|
75
|
+
"lucataco/remove-bg:95fcc2a26d3899cd6c2691c900465aaeff466285a65c14638cc5f36f34befaf1";
|
|
76
|
+
const describeImageModelId =
|
|
77
|
+
(isStatic ? options?.describeImageModelId : config.describeImageModelId) ||
|
|
78
|
+
"zsxkib/blip-3:499bec581d8f64060fd695ec0c34d7595c6824c4118259aa8b0788e0d2d903e1";
|
|
79
|
+
|
|
80
|
+
// Helper to get API token (either static, from provider function, or from service)
|
|
81
|
+
const getApiToken = (context: ImageAiContext) => {
|
|
82
|
+
if (staticApiKey) {
|
|
83
|
+
return Effect.succeed(staticApiKey);
|
|
84
|
+
}
|
|
85
|
+
if (useCredentialProviderService) {
|
|
86
|
+
return Effect.gen(function* () {
|
|
87
|
+
const credentialProviderService = yield* Effect.serviceOption(
|
|
88
|
+
CredentialProviderService,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (Option.isNone(credentialProviderService)) {
|
|
92
|
+
return yield* Effect.fail(
|
|
93
|
+
UploadistaError.fromCode("UNKNOWN_ERROR", {
|
|
94
|
+
cause: new Error("Credential provider service not found"),
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
} else {
|
|
98
|
+
const credentials =
|
|
99
|
+
yield* credentialProviderService.value.getCredential({
|
|
100
|
+
clientId: context.clientId,
|
|
101
|
+
serviceType: "replicate",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (
|
|
105
|
+
typeof credentials === "object" &&
|
|
106
|
+
credentials !== null &&
|
|
107
|
+
"apiKey" in credentials &&
|
|
108
|
+
typeof credentials.apiKey === "string"
|
|
109
|
+
) {
|
|
110
|
+
return credentials.apiKey;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return yield* Effect.fail(
|
|
115
|
+
UploadistaError.fromCode("UNKNOWN_ERROR", {
|
|
116
|
+
cause: new Error("Invalid credential format from service"),
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (credentialProvider) {
|
|
122
|
+
return Effect.gen(function* () {
|
|
123
|
+
const credentials = yield* credentialProvider({
|
|
124
|
+
...context,
|
|
125
|
+
serviceType: "replicate",
|
|
126
|
+
});
|
|
127
|
+
return credentials.apiKey;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return Effect.fail(
|
|
131
|
+
UploadistaError.fromCode("UNKNOWN_ERROR", {
|
|
132
|
+
cause: new Error("No API credentials configured"),
|
|
133
|
+
}),
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
return Layer.succeed(
|
|
138
|
+
ImageAiPlugin,
|
|
139
|
+
ImageAiPlugin.of({
|
|
140
|
+
removeBackground: (inputUrl, context) => {
|
|
141
|
+
return Effect.gen(function* () {
|
|
142
|
+
// Get API token (static or from credential provider)
|
|
143
|
+
const apiToken = yield* getApiToken(context);
|
|
144
|
+
|
|
145
|
+
const output = yield* Effect.tryPromise({
|
|
146
|
+
try: async () => {
|
|
147
|
+
const replicate = new Replicate({
|
|
148
|
+
auth: apiToken,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const input = {
|
|
152
|
+
image: inputUrl,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
console.log("input", input);
|
|
156
|
+
|
|
157
|
+
return (await replicate.run(removeBackgroundModelId, {
|
|
158
|
+
input,
|
|
159
|
+
})) as RemoveBackgroundOutput;
|
|
160
|
+
},
|
|
161
|
+
catch: (error) => {
|
|
162
|
+
console.log("error", error);
|
|
163
|
+
return UploadistaError.fromCode("UNKNOWN_ERROR", {
|
|
164
|
+
cause: error,
|
|
165
|
+
});
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
return { outputUrl: output.url() };
|
|
169
|
+
});
|
|
170
|
+
},
|
|
171
|
+
describeImage: (inputUrl, context) => {
|
|
172
|
+
return Effect.gen(function* () {
|
|
173
|
+
// Get API token (static or from credential provider)
|
|
174
|
+
const apiToken = yield* getApiToken(context);
|
|
175
|
+
|
|
176
|
+
const output = yield* Effect.tryPromise({
|
|
177
|
+
try: async () => {
|
|
178
|
+
const replicate = new Replicate({
|
|
179
|
+
auth: apiToken,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
return (await replicate.run(describeImageModelId, {
|
|
183
|
+
input: {
|
|
184
|
+
image: inputUrl,
|
|
185
|
+
top_k: 50,
|
|
186
|
+
top_p: 1,
|
|
187
|
+
caption: false,
|
|
188
|
+
question: "What is shown in the image?",
|
|
189
|
+
do_sample: false,
|
|
190
|
+
num_beams: 1,
|
|
191
|
+
temperature: 1,
|
|
192
|
+
system_prompt:
|
|
193
|
+
"A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions.",
|
|
194
|
+
length_penalty: 1,
|
|
195
|
+
max_new_tokens: 768,
|
|
196
|
+
repetition_penalty: 1,
|
|
197
|
+
},
|
|
198
|
+
})) as unknown as string;
|
|
199
|
+
},
|
|
200
|
+
catch: (error) => {
|
|
201
|
+
return UploadistaError.fromCode("UNKNOWN_ERROR", {
|
|
202
|
+
cause: error,
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
return { description: output };
|
|
207
|
+
});
|
|
208
|
+
},
|
|
209
|
+
}),
|
|
210
|
+
);
|
|
211
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./image-ai-plugin";
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "@uploadista/typescript-config/server.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"baseUrl": "./",
|
|
5
|
+
"paths": {
|
|
6
|
+
"@/*": ["./src/*"]
|
|
7
|
+
},
|
|
8
|
+
"outDir": "./dist",
|
|
9
|
+
"rootDir": "./src",
|
|
10
|
+
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
|
11
|
+
"types": []
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["./src/image-ai-plugin.ts","./src/index.ts"],"version":"5.9.3"}
|