Skip to content

Commit c5fedc3

Browse files
committed
Function: OpenAI Image Generation Support
1 parent b1d5179 commit c5fedc3

File tree

4 files changed

+69
-82
lines changed

4 files changed

+69
-82
lines changed

src/components/pages/ImageGenerationPage.tsx

Lines changed: 44 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ import {
55
} from "../../services/settings-service";
66
import { ChevronDown, RefreshCw } from "lucide-react";
77
import { useTranslation } from "react-i18next";
8+
import { AIService } from "../../services/ai-service";
9+
import { OPENAI_PROVIDER_NAME } from "../../services/providers/openai-service";
810

911
export const ImageGenerationPage = () => {
1012
const { t } = useTranslation();
1113
const [prompt, setPrompt] = useState("");
1214
const [isGenerating, setIsGenerating] = useState(false);
1315
const [imageResult, setImageResult] = useState<string | null>(null);
1416
const [error, setError] = useState<Error | null>(null);
15-
const [selectedModel, setSelectedModel] = useState("");
16-
const [selectedProvider, setSelectedProvider] = useState("");
1717
const [isApiKeyMissing, setIsApiKeyMissing] = useState(true);
1818
const [aspectRatio, setAspectRatio] = useState("1:1");
1919
const [imageCount, setImageCount] = useState(1);
@@ -24,12 +24,8 @@ export const ImageGenerationPage = () => {
2424
// Check if API key is available
2525
useEffect(() => {
2626
setIsApiKeyMissing(!SettingsService.getInstance().getApiKey());
27-
setSelectedProvider(SettingsService.getInstance().getSelectedProvider());
28-
setSelectedModel(SettingsService.getInstance().getSelectedModel());
2927

3028
const handleSettingsChange = () => {
31-
setSelectedProvider(SettingsService.getInstance().getSelectedProvider());
32-
setSelectedModel(SettingsService.getInstance().getSelectedModel());
3329
setIsApiKeyMissing(!SettingsService.getInstance().getApiKey());
3430
};
3531

@@ -40,24 +36,51 @@ export const ImageGenerationPage = () => {
4036
};
4137
}, []);
4238

43-
// Handle generating an image (mock function)
39+
// Handle generating an image using OpenAI's DALL-E 3
4440
const handleGenerateImage = async () => {
4541
if (!prompt.trim()) return;
4642

4743
setIsGenerating(true);
4844
setError(null);
4945

5046
try {
51-
// In a real implementation, this would call an actual image generation service
52-
// For now, just simulate the process with a timeout
53-
await new Promise((resolve) => setTimeout(resolve, 2000));
47+
// Get the OpenAI service from AIService
48+
const openaiService = AIService.getInstance().getProvider(OPENAI_PROVIDER_NAME);
49+
50+
if (!openaiService) {
51+
throw new Error("OpenAI service not available");
52+
}
5453

55-
// For demo purposes, just show a placeholder result
56-
setImageResult(
57-
`https://placehold.co/512x512/eee/999?text=${encodeURIComponent(
58-
prompt
59-
)}`
60-
);
54+
// Map aspect ratio to size dimensions
55+
const sizeMap: Record<string, `${number}x${number}`> = {
56+
"1:1": "1024x1024",
57+
"1:2": "512x1024",
58+
"3:2": "1024x768",
59+
"3:4": "768x1024",
60+
"16:9": "1792x1024",
61+
"9:16": "1024x1792"
62+
};
63+
64+
// Generate the image
65+
const images = await openaiService.getImageGeneration(prompt, {
66+
size: sizeMap[aspectRatio] || "1024x1024",
67+
aspectRatio: aspectRatio as `${number}:${number}`,
68+
style: "vivid"
69+
});
70+
71+
// Set the result image
72+
if (images && images.length > 0) {
73+
// Check if the image is already a full data URL
74+
const base64Data = images[0] as string;
75+
if (base64Data.startsWith('data:image')) {
76+
setImageResult(base64Data);
77+
} else {
78+
// If it's just a base64 string without the data URI prefix, add it
79+
setImageResult(`data:image/png;base64,${base64Data}`);
80+
}
81+
} else {
82+
throw new Error("No images generated");
83+
}
6184
} catch (err) {
6285
setError(err as Error);
6386
} finally {
@@ -70,27 +93,6 @@ export const ImageGenerationPage = () => {
7093
setRandomSeed(Math.floor(Math.random() * 1000000).toString());
7194
};
7295

73-
// Get model name
74-
const getModelName = (modelID: string, provider: string) => {
75-
const providerSettings =
76-
SettingsService.getInstance().getProviderSettings(provider);
77-
if (!providerSettings.models) return modelID;
78-
79-
const model = providerSettings.models?.find((m) => m.modelId === modelID);
80-
if (!model) return modelID;
81-
82-
return model.modelName;
83-
};
84-
85-
// Get display provider name
86-
const getDisplayProviderName = () => {
87-
if (!selectedProvider) return "硅基流动";
88-
89-
const providerSettings =
90-
SettingsService.getInstance().getProviderSettings(selectedProvider);
91-
return providerSettings.providerName || selectedProvider;
92-
};
93-
9496
return (
9597
<div className="flex flex-col w-full h-full bg-white">
9698
{isApiKeyMissing && (
@@ -120,19 +122,6 @@ export const ImageGenerationPage = () => {
120122
/>
121123
</div>
122124

123-
{/* Negative prompt */}
124-
{/* <div className="mb-6">
125-
<label className="flex items-center block mb-2 text-sm font-medium text-gray-700">
126-
反向提示词
127-
<div className="flex items-center justify-center w-4 h-4 ml-1 text-xs text-gray-500 bg-gray-200 rounded-full cursor-help" title="Elements to avoid in the image">?</div>
128-
</label>
129-
<textarea
130-
placeholder="输入不希望在生成的图像中出现的元素"
131-
className="w-full p-3 input-box"
132-
rows={3}
133-
/>
134-
</div> */}
135-
136125
{/* Generation button */}
137126
<div className="flex justify-center mb-6">
138127
<button
@@ -208,7 +197,7 @@ export const ImageGenerationPage = () => {
208197
className="flex items-center justify-between w-full p-3 text-left input-box"
209198
disabled={true}
210199
>
211-
<span>{getDisplayProviderName()}</span>
200+
<span>OpenAI</span>
212201
<ChevronDown size={18} className="text-gray-500" />
213202
</button>
214203
</div>
@@ -224,10 +213,7 @@ export const ImageGenerationPage = () => {
224213
className="flex items-center justify-between w-full p-3 text-left input-box"
225214
disabled={true}
226215
>
227-
<span>
228-
{getModelName(selectedModel, selectedProvider) ||
229-
"FLUX.1 Schnell"}
230-
</span>
216+
<span>DALL-E 3</span>
231217
<ChevronDown size={18} className="text-gray-500" />
232218
</button>
233219
</div>
@@ -326,6 +312,7 @@ export const ImageGenerationPage = () => {
326312
min="1"
327313
max="4"
328314
className="w-full p-3 input-box"
315+
disabled={true}
329316
/>
330317
</div>
331318

@@ -346,11 +333,13 @@ export const ImageGenerationPage = () => {
346333
value={randomSeed}
347334
onChange={(e) => setRandomSeed(e.target.value)}
348335
className="flex-grow p-3 mr-2 input-box"
336+
disabled={true}
349337
/>
350338
<button
351339
onClick={generateNewSeed}
352340
className="p-2 rounded-lg image-generation-refresh-button"
353341
title={t("imageGeneration.randomSeed")}
342+
disabled={true}
354343
>
355344
<RefreshCw size={20} />
356345
</button>

src/services/core/ai-service-provider.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,13 @@ export interface AiServiceProvider {
8383
/**
8484
* Generate an image
8585
*/
86-
getImageGeneration?(
86+
getImageGeneration(
8787
prompt: string,
88-
options?: {
89-
size?: string;
88+
options: {
89+
size?: `${number}x${number}`;
90+
aspectRatio?: `${number}:${number}`;
9091
style?: string;
9192
quality?: string;
9293
}
93-
): Promise<string[]>;
94+
): Promise<string[] | Uint8Array<ArrayBufferLike>[]>;
9495
}

src/services/providers/openai-service.ts

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -154,33 +154,29 @@ export class OpenAIService implements AiServiceProvider {
154154
*/
155155
public async getImageGeneration(
156156
prompt: string,
157-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
158157
options: {
159158
size?: `${number}x${number}`;
160159
aspectRatio?: `${number}:${number}`;
161160
style?: string;
162161
quality?: string;
163-
} = {}
164-
165-
): Promise<string[]> {
166-
167-
// const imageModel = openai.imageModel('dall-e-3');
168-
169-
// const result = await imageModel.doGenerate({
170-
// prompt: prompt,
171-
// n: 1,
172-
// size: options.size || '1024x1024',
173-
// aspectRatio: options.aspectRatio || '1:1',
174-
// seed: 42,
175-
// providerOptions: {
176-
// "openai": {
177-
// "style": options.style || 'vivid'
178-
// }
179-
// }
180-
// });
181-
182-
// return result.images;
183-
184-
return [];
162+
}
163+
): Promise<string[] | Uint8Array<ArrayBufferLike>[]> {
164+
165+
const imageModel = this.commonProviderHelper.ProviderInstance.imageModel('dall-e-3');
166+
167+
const result = await imageModel.doGenerate({
168+
prompt: prompt,
169+
n: 1,
170+
size: options.size || '1024x1024',
171+
aspectRatio: options.aspectRatio || '1:1',
172+
seed: 42,
173+
providerOptions: {
174+
"openai": {
175+
"style": options.style || 'vivid'
176+
}
177+
}
178+
});
179+
180+
return result.images;
185181
}
186182
}

src/types/chat.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export interface Conversation {
4343
messages: Map<string, Message>;
4444
createdAt: Date;
4545
updatedAt: Date;
46+
messageInput: string;
4647
}
4748

4849
export interface ConversationFolder{

0 commit comments

Comments
 (0)