Skip to content

Commit 665ed33

Browse files
authored
Merge pull request #1333 from narengogi/chore/image-edits
unified route handler for image edits
2 parents 71f0209 + 27db001 commit 665ed33

File tree

9 files changed

+242
-0
lines changed

9 files changed

+242
-0
lines changed

src/handlers/imageEditsHandler.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { RouterError } from '../errors/RouterError';
2+
import {
3+
constructConfigFromRequestHeaders,
4+
tryTargetsRecursively,
5+
} from './handlerUtils';
6+
import { Context } from 'hono';
7+
8+
/**
9+
* Handles the '/images/edits' API request by selecting the appropriate provider(s) and making the request to them.
10+
*
11+
* @param {Context} c - The Cloudflare Worker context.
12+
* @returns {Promise<Response>} - The response from the provider.
13+
* @throws Will throw an error if no provider options can be determined or if the request to the provider(s) fails.
14+
* @throws Will throw an 500 error if the handler fails due to some reasons
15+
*/
16+
export async function imageEditsHandler(c: Context): Promise<Response> {
17+
try {
18+
let request = await c.req.raw.formData();
19+
let requestHeaders = Object.fromEntries(c.req.raw.headers);
20+
const camelCaseConfig = constructConfigFromRequestHeaders(requestHeaders);
21+
22+
const tryTargetsResponse = await tryTargetsRecursively(
23+
c,
24+
camelCaseConfig,
25+
request,
26+
requestHeaders,
27+
'imageEdit',
28+
'POST',
29+
'config'
30+
);
31+
32+
return tryTargetsResponse;
33+
} catch (err: any) {
34+
console.error('imageEdit error: ', err);
35+
let statusCode = 500;
36+
let errorMessage = 'Something went wrong';
37+
38+
if (err instanceof RouterError) {
39+
statusCode = 400;
40+
errorMessage = err.message;
41+
}
42+
return new Response(
43+
JSON.stringify({
44+
status: 'failure',
45+
message: 'Something went wrong',
46+
}),
47+
{
48+
status: 500,
49+
headers: {
50+
'content-type': 'application/json',
51+
},
52+
}
53+
);
54+
}
55+
}

src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import filesHandler from './handlers/filesHandler';
3232
import batchesHandler from './handlers/batchesHandler';
3333
import finetuneHandler from './handlers/finetuneHandler';
3434
import { messagesHandler } from './handlers/messagesHandler';
35+
import { imageEditsHandler } from './handlers/imageEditsHandler';
3536

3637
// Config
3738
import conf from '../conf.json';
@@ -157,6 +158,12 @@ app.post('/v1/embeddings', requestValidator, embeddingsHandler);
157158
*/
158159
app.post('/v1/images/generations', requestValidator, imageGenerationsHandler);
159160

161+
/**
162+
* POST route for '/v1/images/edits'.
163+
* Handles requests by passing them to the imageGenerations handler.
164+
*/
165+
app.post('/v1/images/edits', requestValidator, imageEditsHandler);
166+
160167
/**
161168
* POST route for '/v1/audio/speech'.
162169
* Handles requests by passing them to the createSpeechHandler.

src/providers/azure-openai/api.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ const AzureOpenAIAPIConfig: ProviderAPIConfig = {
9696
case 'imageGenerate': {
9797
return `/deployments/${deploymentId}/images/generations?api-version=${apiVersion}`;
9898
}
99+
case 'imageEdit': {
100+
return `/deployments/${deploymentId}/images/edits?api-version=${apiVersion}`;
101+
}
99102
case 'createSpeech': {
100103
return `/deployments/${deploymentId}/audio/speech?api-version=${apiVersion}`;
101104
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { AZURE_OPEN_AI } from '../../globals';
2+
import { OpenAIErrorResponseTransform } from '../openai/utils';
3+
import { ErrorResponse, ImageGenerateResponse, ProviderConfig } from '../types';
4+
5+
export const AzureOpenAIImageEditConfig: ProviderConfig = {
6+
image: {
7+
param: 'image',
8+
required: true,
9+
},
10+
prompt: {
11+
param: 'prompt',
12+
required: true,
13+
},
14+
background: {
15+
param: 'background',
16+
},
17+
input_fidelity: {
18+
param: 'input_fidelity',
19+
},
20+
mask: {
21+
param: 'mask',
22+
},
23+
model: {
24+
param: 'model',
25+
default: 'dall-e-2',
26+
},
27+
n: {
28+
param: 'n',
29+
min: 1,
30+
max: 10,
31+
},
32+
output_compression: {
33+
param: 'output_compression',
34+
min: 0,
35+
max: 100,
36+
},
37+
output_format: {
38+
param: 'output_format',
39+
},
40+
partial_images: {
41+
param: 'partial_images',
42+
min: 0,
43+
max: 3,
44+
},
45+
quality: {
46+
param: 'quality',
47+
},
48+
response_format: {
49+
param: 'response_format',
50+
},
51+
size: {
52+
param: 'size',
53+
},
54+
stream: {
55+
param: 'stream',
56+
},
57+
user: {
58+
param: 'user',
59+
},
60+
};
61+
62+
interface AzureOpenAIImageObject {
63+
b64_json?: string; // The base64-encoded JSON of the generated image, if response_format is b64_json.
64+
url?: string; // The URL of the generated image, if response_format is url (default).
65+
revised_prompt?: string; // The prompt that was used to generate the image, if there was any revision to the prompt.
66+
}
67+
68+
interface AzureOpenAIImageGenerateResponse extends ImageGenerateResponse {
69+
data: AzureOpenAIImageObject[];
70+
}
71+
72+
export const AzureOpenAIImageEditResponseTransform: (
73+
response: AzureOpenAIImageGenerateResponse | ErrorResponse,
74+
responseStatus: number
75+
) => ImageGenerateResponse | ErrorResponse = (response, responseStatus) => {
76+
if (responseStatus !== 200 && 'error' in response) {
77+
return OpenAIErrorResponseTransform(response, AZURE_OPEN_AI);
78+
}
79+
80+
return response;
81+
};

src/providers/azure-openai/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,17 @@ import {
3636
OpenAIListInputItemsResponseTransformer,
3737
} from '../open-ai-base';
3838
import { AZURE_OPEN_AI } from '../../globals';
39+
import {
40+
AzureOpenAIImageEditConfig,
41+
AzureOpenAIImageEditResponseTransform,
42+
} from './imageEdits';
3943

4044
const AzureOpenAIConfig: ProviderConfigs = {
4145
complete: AzureOpenAICompleteConfig,
4246
embed: AzureOpenAIEmbedConfig,
4347
api: AzureOpenAIAPIConfig,
4448
imageGenerate: AzureOpenAIImageGenerateConfig,
49+
imageEdit: AzureOpenAIImageEditConfig,
4550
chatComplete: AzureOpenAIChatCompleteConfig,
4651
createSpeech: AzureOpenAICreateSpeechConfig,
4752
createFinetune: OpenAICreateFinetuneConfig,
@@ -63,6 +68,7 @@ const AzureOpenAIConfig: ProviderConfigs = {
6368
chatComplete: AzureOpenAIResponseTransform,
6469
embed: AzureOpenAIEmbedResponseTransform,
6570
imageGenerate: AzureOpenAIImageGenerateResponseTransform,
71+
imageEdit: AzureOpenAIImageEditResponseTransform,
6672
createSpeech: AzureOpenAICreateSpeechResponseTransform,
6773
createTranscription: AzureOpenAICreateTranscriptionResponseTransform,
6874
createTranslation: AzureOpenAICreateTranslationResponseTransform,

src/providers/openai/api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ const OpenAIAPIConfig: ProviderAPIConfig = {
3838
return '/embeddings';
3939
case 'imageGenerate':
4040
return '/images/generations';
41+
case 'imageEdit':
42+
return '/images/edits';
4143
case 'createSpeech':
4244
return '/audio/speech';
4345
case 'createTranscription':

src/providers/openai/imageEdits.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { OPEN_AI } from '../../globals';
2+
import { ErrorResponse, ImageGenerateResponse, ProviderConfig } from '../types';
3+
import { OpenAIErrorResponseTransform } from './utils';
4+
5+
export const OpenAIImageEditConfig: ProviderConfig = {
6+
image: {
7+
param: 'image',
8+
required: true,
9+
},
10+
prompt: {
11+
param: 'prompt',
12+
required: true,
13+
},
14+
background: {
15+
param: 'background',
16+
},
17+
input_fidelity: {
18+
param: 'input_fidelity',
19+
},
20+
mask: {
21+
param: 'mask',
22+
},
23+
model: {
24+
param: 'model',
25+
default: 'dall-e-2',
26+
},
27+
n: {
28+
param: 'n',
29+
min: 1,
30+
max: 10,
31+
},
32+
output_compression: {
33+
param: 'output_compression',
34+
min: 0,
35+
max: 100,
36+
},
37+
output_format: {
38+
param: 'output_format',
39+
},
40+
partial_images: {
41+
param: 'partial_images',
42+
min: 0,
43+
max: 3,
44+
},
45+
quality: {
46+
param: 'quality',
47+
},
48+
response_format: {
49+
param: 'response_format',
50+
},
51+
size: {
52+
param: 'size',
53+
},
54+
stream: {
55+
param: 'stream',
56+
},
57+
user: {
58+
param: 'user',
59+
},
60+
};
61+
62+
interface OpenAIImageObject {
63+
b64_json?: string; // The base64-encoded JSON of the generated image, if response_format is b64_json.
64+
url?: string; // The URL of the generated image, if response_format is url (default).
65+
revised_prompt?: string; // The prompt that was used to generate the image, if there was any revision to the prompt.
66+
}
67+
68+
interface OpenAIImageGenerateResponse extends ImageGenerateResponse {
69+
data: OpenAIImageObject[];
70+
}
71+
72+
export const OpenAIImageEditResponseTransform: (
73+
response: OpenAIImageGenerateResponse | ErrorResponse,
74+
responseStatus: number
75+
) => ImageGenerateResponse | ErrorResponse = (response, responseStatus) => {
76+
if (responseStatus !== 200 && 'error' in response) {
77+
return OpenAIErrorResponseTransform(response, OPEN_AI);
78+
}
79+
80+
return response;
81+
};

src/providers/openai/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,18 @@ import {
4646
OpenAIListInputItemsResponseTransformer,
4747
} from '../open-ai-base';
4848
import { OPEN_AI } from '../../globals';
49+
import {
50+
OpenAIImageEditConfig,
51+
OpenAIImageEditResponseTransform,
52+
} from './imageEdits';
4953

5054
const OpenAIConfig: ProviderConfigs = {
5155
complete: OpenAICompleteConfig,
5256
embed: OpenAIEmbedConfig,
5357
api: OpenAIAPIConfig,
5458
chatComplete: OpenAIChatCompleteConfig,
5559
imageGenerate: OpenAIImageGenerateConfig,
60+
imageEdit: OpenAIImageEditConfig,
5661
createSpeech: OpenAICreateSpeechConfig,
5762
createTranscription: {},
5863
createTranslation: {},
@@ -77,6 +82,7 @@ const OpenAIConfig: ProviderConfigs = {
7782
chatComplete: OpenAIChatCompleteResponseTransform,
7883
// 'stream-chatComplete': OpenAIChatCompleteResponseTransform,
7984
imageGenerate: OpenAIImageGenerateResponseTransform,
85+
imageEdit: OpenAIImageEditResponseTransform,
8086
createSpeech: OpenAICreateSpeechResponseTransform,
8187
createTranscription: OpenAICreateTranscriptionResponseTransform,
8288
createTranslation: OpenAICreateTranslationResponseTransform,

src/providers/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ export type endpointStrings =
9191
| 'stream-messages'
9292
| 'proxy'
9393
| 'imageGenerate'
94+
| 'imageEdit'
9495
| 'createSpeech'
9596
| 'createTranscription'
9697
| 'createTranslation'

0 commit comments

Comments
 (0)