mirror of
https://github.com/xavidop/cardex.git
synced 2026-08-29 01:08:52 +00:00
feat: add gemini 3
This commit is contained in:
Generated
+2510
-153
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -15,9 +15,9 @@
|
||||
"dev:full": "concurrently \"npm run emulators\" \"npm run dev\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@genkit-ai/googleai": "^1.15.1",
|
||||
"@genkit-ai/next": "^1.15.1",
|
||||
"@google/genai": "^1.10.0",
|
||||
"@genkit-ai/googleai": "^1.24.0",
|
||||
"@genkit-ai/next": "^1.24.0",
|
||||
"@google/genai": "^1.30.0",
|
||||
"@hookform/resolvers": "^4.1.3",
|
||||
"@radix-ui/react-accordion": "^1.2.3",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.6",
|
||||
@@ -46,7 +46,7 @@
|
||||
"date-fns": "^3.6.0",
|
||||
"dotenv": "^16.5.0",
|
||||
"firebase": "^11.10.0",
|
||||
"genkit": "^1.15.1",
|
||||
"genkit": "^1.24.0",
|
||||
"lucide-react": "^0.475.0",
|
||||
"next": "15.3.3",
|
||||
"openai": "^5.10.1",
|
||||
|
||||
@@ -22,7 +22,7 @@ const GeneratePokemonCardInputSchema = z.object({
|
||||
backgroundDescription: z.string().min(1, "Background description is required"),
|
||||
pokemonDescription: z.string().min(1, "Pokemon description is required"),
|
||||
language: z.enum(['english', 'japanese', 'chinese', 'korean', 'spanish', 'french', 'german', 'italian']),
|
||||
model: z.enum(['imagen-4.0-ultra-generate-001', 'imagen-4.0-generate-001', 'imagen-4.0-fast-generate-001', 'gemini-2.5-flash-image-preview']).default('imagen-4.0-ultra-generate-001'),
|
||||
model: z.enum(['imagen-4.0-ultra-generate-001', 'imagen-4.0-generate-001', 'imagen-4.0-fast-generate-001', 'gemini-2.5-flash-image', 'gemini-3-pro-image-preview']).default('imagen-4.0-ultra-generate-001'),
|
||||
hp: z.number().optional(),
|
||||
attackName1: z.string().optional(),
|
||||
attackDamage1: z.number().optional(),
|
||||
@@ -56,36 +56,85 @@ export async function generatePokemonCard(input: GeneratePokemonCardInput): Prom
|
||||
}
|
||||
|
||||
const genAI = new GoogleGenAI({
|
||||
apiKey: apiKey,
|
||||
apiKey: apiKey,
|
||||
});
|
||||
|
||||
// Generate the detailed prompt for the Pokemon card
|
||||
const prompt = generateCardPrompt(input);
|
||||
console.log("Generated prompt:", prompt);
|
||||
|
||||
const response = await genAI.models.generateImages({
|
||||
model: `models/${input.model}`,
|
||||
prompt: prompt,
|
||||
config: {
|
||||
numberOfImages: 1,
|
||||
outputMimeType: 'image/jpeg',
|
||||
aspectRatio: '3:4' // Pokemon cards are roughly 3:4 aspect ratio
|
||||
},
|
||||
});
|
||||
// Check if using Gemini model (for generateContentStream) or Imagen model (for generateImages)
|
||||
const isGeminiModel = input.model.startsWith('gemini-');
|
||||
|
||||
if (isGeminiModel) {
|
||||
// Use generateContentStream for Gemini models
|
||||
const config = {
|
||||
responseModalities: ['IMAGE', 'TEXT'],
|
||||
};
|
||||
const contents = [
|
||||
{
|
||||
role: 'user' as const,
|
||||
parts: [
|
||||
{
|
||||
text: prompt,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
if (!response?.generatedImages || response.generatedImages.length === 0) {
|
||||
return { error: 'No images generated.' };
|
||||
const response = await genAI.models.generateContentStream({
|
||||
model: input.model,
|
||||
config,
|
||||
contents,
|
||||
});
|
||||
|
||||
let imageBase64: string | undefined;
|
||||
|
||||
for await (const chunk of response) {
|
||||
if (!chunk.candidates || !chunk.candidates[0].content || !chunk.candidates[0].content.parts) {
|
||||
continue;
|
||||
}
|
||||
if (chunk.candidates?.[0]?.content?.parts?.[0]?.inlineData) {
|
||||
const inlineData = chunk.candidates[0].content.parts[0].inlineData;
|
||||
imageBase64 = inlineData.data || '';
|
||||
break; // We only need the first image
|
||||
}
|
||||
}
|
||||
|
||||
if (!imageBase64) {
|
||||
return { error: 'No image generated from Gemini model.' };
|
||||
}
|
||||
|
||||
return {
|
||||
imageBase64,
|
||||
prompt,
|
||||
};
|
||||
} else {
|
||||
// Use generateImages for Imagen models
|
||||
const response = await genAI.models.generateImages({
|
||||
model: input.model,
|
||||
prompt: prompt,
|
||||
config: {
|
||||
numberOfImages: 1,
|
||||
outputMimeType: 'image/jpeg',
|
||||
aspectRatio: '3:4' // Pokemon cards are roughly 3:4 aspect ratio
|
||||
},
|
||||
});
|
||||
|
||||
if (!response?.generatedImages || response.generatedImages.length === 0) {
|
||||
return { error: 'No images generated.' };
|
||||
}
|
||||
|
||||
const generatedImage = response.generatedImages[0];
|
||||
if (!generatedImage?.image?.imageBytes) {
|
||||
return { error: 'Generated image data is missing.' };
|
||||
}
|
||||
|
||||
return {
|
||||
imageBase64: generatedImage.image.imageBytes,
|
||||
prompt: prompt,
|
||||
};
|
||||
}
|
||||
|
||||
const generatedImage = response.generatedImages[0];
|
||||
if (!generatedImage?.image?.imageBytes) {
|
||||
return { error: 'Generated image data is missing.' };
|
||||
}
|
||||
|
||||
return {
|
||||
imageBase64: generatedImage.image.imageBytes,
|
||||
prompt: prompt,
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error('Error generating Pokemon card:', error);
|
||||
return { error: `Failed to generate Pokemon card: ${error?.message || 'Unknown error'}` };
|
||||
|
||||
@@ -26,7 +26,7 @@ export interface GeneratePokemonCardInput {
|
||||
backgroundDescription: string;
|
||||
pokemonDescription: string;
|
||||
language: 'english' | 'japanese' | 'chinese' | 'korean' | 'spanish' | 'french' | 'german' | 'italian';
|
||||
model: 'imagen-4.0-ultra-generate-001' | 'imagen-4.0-generate-001' | 'imagen-4.0-fast-generate-001' | 'gemini-2.5-flash-image-preview';
|
||||
model: 'imagen-4.0-ultra-generate-001' | 'imagen-4.0-generate-001' | 'imagen-4.0-fast-generate-001' | 'gemini-2.5-flash-image' | 'gemini-3-pro-image-preview';
|
||||
hp?: number;
|
||||
attackName1?: string;
|
||||
attackDamage1?: number;
|
||||
@@ -58,7 +58,8 @@ const models = [
|
||||
{ value: 'imagen-4.0-ultra-generate-001', label: 'Imagen 4.0 Ultra' },
|
||||
{ value: 'imagen-4.0-generate-001', label: 'Imagen 4.0 Standard' },
|
||||
{ value: 'imagen-4.0-fast-generate-001', label: 'Imagen 4.0 Fast' },
|
||||
{ value: 'gemini-2.5-flash-image-preview', label: 'Gemini 2.5 Flash' },
|
||||
{ value: 'gemini-2.5-flash-image', label: 'Gemini 2.5 Flash (Nano Banana)' },
|
||||
{ value: 'gemini-3-pro-image-preview', label: 'Gemini 3 Pro (Nano Banana Pro)' },
|
||||
] as const;
|
||||
|
||||
const cardGeneratorSchema = z.object({
|
||||
@@ -69,7 +70,7 @@ const cardGeneratorSchema = z.object({
|
||||
backgroundDescription: z.string().min(10, 'Background description must be at least 10 characters'),
|
||||
pokemonDescription: z.string().min(10, 'Pokemon description must be at least 10 characters'),
|
||||
language: z.enum(['english', 'japanese', 'chinese', 'korean', 'spanish', 'french', 'german', 'italian']),
|
||||
model: z.enum(['imagen-4.0-ultra-generate-001', 'imagen-4.0-generate-001', 'imagen-4.0-fast-generate-001', 'gemini-2.5-flash-image-preview']),
|
||||
model: z.enum(['imagen-4.0-ultra-generate-001', 'imagen-4.0-generate-001', 'imagen-4.0-fast-generate-001', 'gemini-2.5-flash-image', 'gemini-3-pro-image-preview']),
|
||||
hp: z.number().min(10).max(999).optional(),
|
||||
attackName1: z.string().optional(),
|
||||
attackDamage1: z.number().min(0).max(999).optional(),
|
||||
|
||||
@@ -24,7 +24,7 @@ export interface GeneratePokemonCardInput {
|
||||
backgroundDescription: string;
|
||||
pokemonDescription: string;
|
||||
language: 'english' | 'japanese' | 'chinese' | 'korean' | 'spanish' | 'french' | 'german' | 'italian';
|
||||
model: 'imagen-4.0-ultra-generate-001' | 'imagen-4.0-generate-001' | 'imagen-4.0-fast-generate-001' | 'gemini-2.5-flash-image-preview';
|
||||
model: 'imagen-4.0-ultra-generate-001' | 'imagen-4.0-generate-001' | 'imagen-4.0-fast-generate-001' | 'gemini-2.5-flash-image' | 'gemini-3-pro-image-preview';
|
||||
hp?: number;
|
||||
attackName1?: string;
|
||||
attackDamage1?: number;
|
||||
@@ -56,7 +56,8 @@ const models = [
|
||||
{ value: 'imagen-4.0-ultra-generate-001', label: 'Imagen 4.0 Ultra' },
|
||||
{ value: 'imagen-4.0-generate-001', label: 'Imagen 4.0 Standard' },
|
||||
{ value: 'imagen-4.0-fast-generate-001', label: 'Imagen 4.0 Fast' },
|
||||
{ value: 'gemini-2.5-flash-image-preview', label: 'Gemini 2.5 Flash' },
|
||||
{ value: 'gemini-2.5-flash-image', label: 'Gemini 2.5 Flash (Nano Banana)' },
|
||||
{ value: 'gemini-3-pro-image-preview', label: 'Gemini 3 Pro Image Preview' },
|
||||
] as const;
|
||||
|
||||
const cardGeneratorSchema = z.object({
|
||||
@@ -67,7 +68,7 @@ const cardGeneratorSchema = z.object({
|
||||
backgroundDescription: z.string().min(10, 'Background description must be at least 10 characters'),
|
||||
pokemonDescription: z.string().min(10, 'Pokemon description must be at least 10 characters'),
|
||||
language: z.enum(['english', 'japanese', 'chinese', 'korean', 'spanish', 'french', 'german', 'italian']),
|
||||
model: z.enum(['imagen-4.0-ultra-generate-001', 'imagen-4.0-generate-001', 'imagen-4.0-fast-generate-001', 'gemini-2.5-flash-image-preview']),
|
||||
model: z.enum(['imagen-4.0-ultra-generate-001', 'imagen-4.0-generate-001', 'imagen-4.0-fast-generate-001', 'gemini-2.5-flash-image', 'gemini-3-pro-image-preview']),
|
||||
hp: z.number().min(10).max(999).optional(),
|
||||
attackName1: z.string().optional(),
|
||||
attackDamage1: z.number().min(0).max(999).optional(),
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ export interface CardGenerationParams {
|
||||
backgroundDescription: string;
|
||||
pokemonDescription: string;
|
||||
language: 'english' | 'japanese' | 'chinese' | 'korean' | 'spanish' | 'french' | 'german' | 'italian';
|
||||
model: 'imagen-4.0-ultra-generate-001' | 'imagen-4.0-generate-001' | 'imagen-4.0-fast-generate-001' | 'gemini-2.5-flash-image-preview';
|
||||
model: 'imagen-4.0-ultra-generate-001' | 'imagen-4.0-generate-001' | 'imagen-4.0-fast-generate-001' | 'gemini-2.5-flash-image' | 'gemini-3-pro-image-preview';
|
||||
hp?: number;
|
||||
attackName1?: string;
|
||||
attackDamage1?: number;
|
||||
|
||||
Reference in New Issue
Block a user