Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions modules/tool/packages/gptImage/children/imgEditing/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { defineTool } from '@tool/type';
import { FlowNodeInputTypeEnum, WorkflowIOValueTypeEnum } from '@tool/type/fastgpt';

export default defineTool({
isWorkerRun: false,
name: {
'zh-CN': 'gpt-image 图像编辑',
en: 'gpt-image Image Editing'
},
description: {
'zh-CN': '使用gpt-image-1模型对现有图像进行编辑和修改',
en: 'Edit and modify existing images using gpt-image-1 model'
},
toolDescription:
'Edit existing images using gpt-image-1 AI model. Supports image modification with optional mask for precise editing.',
versionList: [
{
value: '0.1.0',
description: 'Default version',
inputs: [
{
key: 'image',
label: '原始图片',
toolDescription: 'The original image to be edited (URL or base64)',
renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
required: true,
description: '要编辑的原始图片,支持图片URL或base64编码,文件大小需小于4MB'
},
{
key: 'prompt',
label: '编辑描述',
toolDescription: 'Describe what changes you want to make to the image',
renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
required: true,
description: '描述要对图片进行的修改'
},
{
key: 'size',
label: '图片尺寸',
renderTypeList: [FlowNodeInputTypeEnum.select, FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
required: false,
defaultValue: '1024x1024',
list: [
{ label: '1024x1024', value: '1024x1024' },
{ label: '1024x1536', value: '1024x1536' },
{ label: '1536x1024', value: '1536x1024' }
]
},
{
key: 'quality',
label: '图片质量',
renderTypeList: [FlowNodeInputTypeEnum.select, FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
required: false,
defaultValue: 'medium',
list: [
{ label: '标准质量', value: 'medium' },
{ label: '高质量', value: 'high' },
{ label: '低质量', value: 'low' }
]
}
],
outputs: [
{
valueType: WorkflowIOValueTypeEnum.string,
key: 'imageUrl',
label: '编辑后图片链接',
description: '编辑后的图片文件链接'
}
]
}
]
});
10 changes: 10 additions & 0 deletions modules/tool/packages/gptImage/children/imgEditing/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import config from './config';
import { InputType, OutputType, tool as toolCb } from './src';
import { exportTool } from '@tool/utils/tool';

export default exportTool({
toolCb,
InputType,
OutputType,
config
});
83 changes: 83 additions & 0 deletions modules/tool/packages/gptImage/children/imgEditing/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { z } from 'zod';
import { uploadFile } from '@tool/utils/uploadFile';
import { POST, GET } from '@tool/utils/request';

export const InputType = z.object({
baseUrl: z.string().optional().default('https://api.openai.com/v1'),
apiKey: z.string().nonempty(),
image: z.string().nonempty(),
prompt: z.string().nonempty(),
size: z.string().default('1024x1024'),
quality: z.string().default('medium')
});

export const OutputType = z.object({
imageUrl: z.string()
});

// convert image input (URL or base64) to Blob
async function imageInputToBlob(imageInput: string): Promise<Blob> {
// if input image is url
if (imageInput.startsWith('http://') || imageInput.startsWith('https://')) {
const { data } = await GET(imageInput, { responseType: 'blob' });
return data as Blob;
}

let base64Data = imageInput;
let mimeType = 'image/png';

if (imageInput.startsWith('data:')) {
const match = imageInput.match(/^data:([^;]+);base64,(.+)$/);
if (!match) throw new Error('Invalid data URL format');
[, mimeType, base64Data] = match;
}

// convert base64 to Blob
const binary = atob(base64Data);
const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
return new Blob([bytes], { type: mimeType });
}

export async function tool({
baseUrl,
apiKey,
image,
prompt,
size,
quality
}: z.infer<typeof InputType>): Promise<z.infer<typeof OutputType>> {
const imageBlob = await imageInputToBlob(image);
const formData = new FormData();
formData.append('model', 'gpt-image-1');
formData.append('image', imageBlob, 'image.png');
formData.append('prompt', prompt);
formData.append('size', size);
formData.append('quality', quality);

const { data } = await POST(`${baseUrl}/v1/images/edits`, formData, {
headers: {
Authorization: `Bearer ${apiKey}`
},
timeout: 180000
});
if (!data || !data.data || !Array.isArray(data.data) || data.data.length === 0) {
return Promise.reject('Failed to edit image or no image data returned');
}

// get the first edited image
const imageData = data.data[0];
const imageBuffer = Buffer.from(imageData.b64_json, 'base64');
if (imageBuffer.length === 0) {
return Promise.reject('Failed to convert base64 image data to buffer');
}

const { accessUrl: imageUrl } = await uploadFile({
buffer: imageBuffer,
defaultFilename: 'gpt-image-edited.png'
});
if (!imageUrl) {
return Promise.reject('Failed to upload edited image file');
}

return { imageUrl };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { expect, test } from 'vitest';
import tool from '..';

test(async () => {
expect(tool.name).toBeDefined();
expect(tool.description).toBeDefined();
expect(tool.cb).toBeDefined();
});
92 changes: 92 additions & 0 deletions modules/tool/packages/gptImage/children/imgGeneration/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { defineTool } from '@tool/type';
import { FlowNodeInputTypeEnum, WorkflowIOValueTypeEnum } from '@tool/type/fastgpt';

export default defineTool({
isWorkerRun: false,
name: {
'zh-CN': 'gpt-image 图像生成',
en: 'gpt-image Image Generation'
},
description: {
'zh-CN': '使用gpt-image-1模型根据文本描述生成高质量图像',
en: 'Generate high-quality images from text descriptions using gpt-image-1 model'
},
toolDescription:
'Generate images from text prompts using gpt-image-1 AI model. Supports various sizes and quality settings.',
versionList: [
{
value: '0.1.0',
description: 'Default version',
inputs: [
{
key: 'prompt',
label: '图像描述',
toolDescription: 'Describe the image you want to generate in detail',
renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
required: true,
description: '详细描述要生成的图像内容'
},
{
key: 'size',
label: '图片尺寸',
renderTypeList: [FlowNodeInputTypeEnum.select, FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
required: false,
defaultValue: '1024x1024',
list: [
{ label: '1024x1024', value: '1024x1024' },
{ label: '1024x1536', value: '1024x1536' },
{ label: '1536x1024', value: '1536x1024' }
]
},
{
key: 'quality',
label: '图片质量',
renderTypeList: [FlowNodeInputTypeEnum.select, FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
required: false,
defaultValue: 'medium',
list: [
{ label: '标准质量', value: 'medium' },
{ label: '高质量', value: 'high' },
{ label: '低质量', value: 'low' }
]
},
{
key: 'background',
label: '背景透明度',
renderTypeList: [FlowNodeInputTypeEnum.select, FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
required: false,
defaultValue: 'auto',
list: [
{ label: '自动', value: 'auto' },
{ label: '透明', value: 'transparent' },
{ label: '不透明', value: 'opaque' }
]
},
{
key: 'moderation',
label: '图片审查级别',
renderTypeList: [FlowNodeInputTypeEnum.select, FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
required: false,
defaultValue: 'auto',
list: [
{ label: '自动', value: 'auto' },
{ label: '低', value: 'low' }
]
}
],
outputs: [
{
valueType: WorkflowIOValueTypeEnum.string,
key: 'imageUrl',
label: '图片链接',
description: '生成的图片文件链接'
}
]
}
]
});
10 changes: 10 additions & 0 deletions modules/tool/packages/gptImage/children/imgGeneration/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import config from './config';
import { InputType, OutputType, tool as toolCb } from './src';
import { exportTool } from '@tool/utils/tool';

export default exportTool({
toolCb,
InputType,
OutputType,
config
});
67 changes: 67 additions & 0 deletions modules/tool/packages/gptImage/children/imgGeneration/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { z } from 'zod';
import { POST } from '@tool/utils/request';
import { uploadFile } from '@tool/utils/uploadFile';

export const InputType = z.object({
baseUrl: z.string().optional().default('https://api.openai.com/v1'),
apiKey: z.string().nonempty(),
prompt: z.string().nonempty(),
size: z.string().default('1024x1024'),
quality: z.string().default('medium'),
background: z.string().default('auto'),
moderation: z.string().default('auto')
});

export const OutputType = z.object({
imageUrl: z.string()
});

export async function tool({
baseUrl,
apiKey,
prompt,
size,
quality,
background,
moderation
}: z.infer<typeof InputType>): Promise<z.infer<typeof OutputType>> {
const headers = {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json'
};

const { data } = await POST(
`${baseUrl}/v1/images/generations`,
{
model: 'gpt-image-1',
prompt,
quality,
size,
background,
moderation
},
{
headers,
timeout: 180000
}
);
if (!data || !data.data || !Array.isArray(data.data) || data.data.length === 0) {
return Promise.reject('Failed to generate image or no image data returned');
}

const imageData = data.data[0];
const imageBuffer = Buffer.from(imageData.b64_json, 'base64');
if (imageBuffer.length === 0) {
return Promise.reject('Failed to convert base64 image data to buffer');
}

const { accessUrl: imageUrl } = await uploadFile({
buffer: imageBuffer,
defaultFilename: 'gpt-image-generated.png'
});
if (!imageUrl) {
return Promise.reject('Failed to upload image file');
}

return { imageUrl };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { expect, test } from 'vitest';
import tool from '..';

test(async () => {
expect(tool.name).toBeDefined();
expect(tool.description).toBeDefined();
expect(tool.cb).toBeDefined();
});
33 changes: 33 additions & 0 deletions modules/tool/packages/gptImage/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { defineToolSet } from '@tool/type';
import { ToolTypeEnum } from '@tool/type/tool';

export default defineToolSet({
name: {
'zh-CN': 'gpt-image 绘图',
en: 'gpt-image Image Generation'
},
courseUrl: 'https://platform.openai.com/docs/pricing',
type: ToolTypeEnum.multimodal,
icon: 'common/openai',
description: {
'zh-CN': '这是一个gpt-image 绘图工具集',
en: 'This is a gpt-image image generation tool set'
},
toolDescription: 'gpt-image image generation tool set',
secretInputConfig: [
{
key: 'baseUrl',
label: 'BaseUrl',
inputType: 'input',
description: '默认为:https://api.openai.com/v1',
defaultValue: 'https://api.openai.com/v1'
},
{
key: 'apiKey',
label: 'API Key',
description: '可以在 https://api.gpt.ge/pricing 获取',
required: true,
inputType: 'secret'
}
]
});
Loading