mirror of
https://github.com/HeyPuter/puter
synced 2024-11-14 22:06:00 +00:00
feat: add image generation driver to puterai module
This commit is contained in:
parent
ec767e3152
commit
56a8f26ab1
@ -38,6 +38,38 @@ class AIInterfaceService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
col_interfaces.set('puter-image-generation', {
|
||||||
|
description: 'AI Image Generation.',
|
||||||
|
methods: {
|
||||||
|
generate: {
|
||||||
|
description: 'Generate an image from a prompt.',
|
||||||
|
parameters: {
|
||||||
|
prompt: { type: 'string' },
|
||||||
|
},
|
||||||
|
result_choices: [
|
||||||
|
{
|
||||||
|
names: ['image'],
|
||||||
|
type: {
|
||||||
|
$: 'stream',
|
||||||
|
content_type: 'image',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
names: ['url'],
|
||||||
|
type: {
|
||||||
|
$: 'string:url:web',
|
||||||
|
content_type: 'image',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
result: {
|
||||||
|
description: 'URL of the generated image.',
|
||||||
|
type: 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
100
src/backend/src/modules/puterai/OpenAIImageGenerationService.js
Normal file
100
src/backend/src/modules/puterai/OpenAIImageGenerationService.js
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
const BaseService = require("../../services/BaseService");
|
||||||
|
const { TypedValue } = require("../../services/drivers/meta/Runtime");
|
||||||
|
const { Context } = require("../../util/context");
|
||||||
|
|
||||||
|
class OpenAIImageGenerationService extends BaseService {
|
||||||
|
static MODULES = {
|
||||||
|
openai: require('openai'),
|
||||||
|
}
|
||||||
|
async _init () {
|
||||||
|
const sk_key =
|
||||||
|
this.config?.openai?.secret_key ??
|
||||||
|
this.global_config.openai?.secret_key;
|
||||||
|
|
||||||
|
this.openai = new this.modules.openai.OpenAI({
|
||||||
|
apiKey: sk_key
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static IMPLEMENTS = {
|
||||||
|
['puter-image-generation']: {
|
||||||
|
async generate ({ prompt, test_mode }) {
|
||||||
|
const url = await this.generate(prompt, {
|
||||||
|
ratio: this.constructor.RATIO_SQUARE,
|
||||||
|
});
|
||||||
|
|
||||||
|
if ( test_mode ) {
|
||||||
|
return new TypedValue({
|
||||||
|
$: 'string:url:web',
|
||||||
|
content_type: 'image',
|
||||||
|
}, 'https://puter-sample-data.puter.site/image_example.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
const image = new TypedValue({
|
||||||
|
$: 'string:url:web',
|
||||||
|
content_type: 'image'
|
||||||
|
}, url);
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static RATIO_SQUARE = { w: 1024, h: 1024 };
|
||||||
|
static RATIO_PORTRAIT = { w: 1024, h: 1792 };
|
||||||
|
static RATIO_LANDSCAPE = { w: 1792, h: 1024 };
|
||||||
|
|
||||||
|
async generate (prompt, {
|
||||||
|
ratio,
|
||||||
|
model,
|
||||||
|
}) {
|
||||||
|
if ( typeof prompt !== 'string' ) {
|
||||||
|
throw new Error('`prompt` must be a string');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! ratio || ! this._validate_ratio(ratio) ) {
|
||||||
|
throw new Error('`ratio` must be a valid ratio');
|
||||||
|
}
|
||||||
|
|
||||||
|
model = model ?? 'dall-e-3';
|
||||||
|
|
||||||
|
const user_private_uid = Context.get('actor')?.private_uid ?? 'UNKNOWN';
|
||||||
|
if ( user_private_uid === 'UNKNOWN' ) {
|
||||||
|
this.errors.report('chat-completion-service:unknown-user', {
|
||||||
|
message: 'failed to get a user ID for an OpenAI request',
|
||||||
|
alarm: true,
|
||||||
|
trace: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const result =
|
||||||
|
await this.openai.images.generate({
|
||||||
|
user: user_private_uid,
|
||||||
|
prompt,
|
||||||
|
size: `${ratio.w}x${ratio.h}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const spending_meta = {
|
||||||
|
model,
|
||||||
|
size: `${ratio.w}x${ratio.h}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const svc_spending = Context.get('services').get('spending');
|
||||||
|
svc_spending.record_spending('openai', 'image-generation', spending_meta);
|
||||||
|
|
||||||
|
const url = result.data?.[0]?.url;
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
_validate_ratio (ratio) {
|
||||||
|
return false
|
||||||
|
|| ratio === this.constructor.RATIO_SQUARE
|
||||||
|
|| ratio === this.constructor.RATIO_PORTRAIT
|
||||||
|
|| ratio === this.constructor.RATIO_LANDSCAPE
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
OpenAIImageGenerationService,
|
||||||
|
};
|
@ -12,6 +12,9 @@ class PuterAIModule extends AdvancedBase {
|
|||||||
|
|
||||||
const { OpenAICompletionService } = require('./OpenAICompletionService');
|
const { OpenAICompletionService } = require('./OpenAICompletionService');
|
||||||
services.registerService('openai-completion', OpenAICompletionService);
|
services.registerService('openai-completion', OpenAICompletionService);
|
||||||
|
|
||||||
|
const { OpenAIImageGenerationService } = require('./OpenAIImageGenerationService');
|
||||||
|
services.registerService('openai-image-generation', OpenAIImageGenerationService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,4 +41,22 @@ await (await fetch("http://api.puter.localhost:4100/drivers/call", {
|
|||||||
}),
|
}),
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
})).json();
|
})).json();
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
URL.createObjectURL(await (await fetch("http://api.puter.localhost:4100/drivers/call", {
|
||||||
|
"headers": {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": `Bearer ${puter.authToken}`,
|
||||||
|
},
|
||||||
|
"body": JSON.stringify({
|
||||||
|
interface: 'puter-image-generation',
|
||||||
|
driver: 'openai-image-generation',
|
||||||
|
method: 'generate',
|
||||||
|
args: {
|
||||||
|
prompt: 'photorealistic teapot made of swiss cheese',
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"method": "POST",
|
||||||
|
})).blob());
|
||||||
```
|
```
|
Loading…
Reference in New Issue
Block a user