Skip to content

Commit e11d0d0

Browse files
authored
Merge pull request #985 from firebase/ddb-imagen-banana
feat(AI): Replace Imagen with Nano Banana usage
2 parents 96f1905 + 2f9bb7a commit e11d0d0

10 files changed

Lines changed: 340 additions & 304 deletions

File tree

ai/ai-react-app/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
src/config/firebase-config.ts

ai/ai-react-app/src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { useEffect, useState } from "react";
22
import MainLayout from "./components/Layout/MainLayout";
33

44
// Defines the primary modes or views available in the application.
5-
export type AppMode = "chat" | "imagenGen" | "live";
5+
export type AppMode = "chat" | "nanobanana" | "live";
66

77
function App() {
8-
// State to manage which main view ('chat' or 'imagenGen') is currently active.
8+
// State to manage which main view ('chat' or 'nanobanana') is currently active.
99
const [activeMode, setActiveMode] = useState<AppMode>("chat");
1010

1111
useEffect(() => {

ai/ai-react-app/src/components/Common/PromptInput.tsx

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
CountTokensResponse,
77
Part,
88
getGenerativeModel,
9-
ImagenModelParams,
109
} from "firebase/ai";
1110
import { fileToGenerativePart } from "../../utils/fileUtils";
1211
import { AppMode } from "../../App";
@@ -23,8 +22,8 @@ interface PromptInputProps {
2322
activeMode: AppMode;
2423
aiInstance: AI;
2524
currentParams?: ModelParams;
26-
currentImagenParams?: ImagenModelParams;
2725
selectedFile: File | null;
26+
selectedAspectRatio?: string;
2827
}
2928

3029
const PromptInput: React.FC<PromptInputProps> = ({
@@ -39,6 +38,7 @@ const PromptInput: React.FC<PromptInputProps> = ({
3938
aiInstance,
4039
currentParams,
4140
selectedFile,
41+
selectedAspectRatio,
4242
}) => {
4343
const [tokenCount, setTokenCount] = useState<CountTokensResponse | null>(
4444
null,
@@ -55,11 +55,15 @@ const PromptInput: React.FC<PromptInputProps> = ({
5555
setTokenCountError(null);
5656

5757
const currentPromptText = prompt.trim();
58+
let textToCount = currentPromptText;
59+
if (selectedAspectRatio) {
60+
textToCount += `\nUse aspect ratio ${selectedAspectRatio}`;
61+
}
5862
const parts: Part[] = [];
5963

6064
// Add text part if present
61-
if (currentPromptText) {
62-
parts.push({ text: currentPromptText });
65+
if (textToCount) {
66+
parts.push({ text: textToCount });
6367
}
6468

6569
// Add file part if present
@@ -137,15 +141,22 @@ const PromptInput: React.FC<PromptInputProps> = ({
137141

138142
{/* Main Input Area */}
139143
<div className={styles.inputArea}>
140-
<textarea
141-
className={styles.promptTextarea}
142-
value={prompt}
143-
onChange={(e) => onPromptChange(e.target.value)}
144-
placeholder={placeholder}
145-
disabled={isLoading || isCountingTokens} // Disable if main loading OR counting
146-
rows={3}
147-
aria-label="Prompt input"
148-
/>
144+
<div style={{ display: 'flex', flexDirection: 'column', flexGrow: 1 }}>
145+
<textarea
146+
className={styles.promptTextarea}
147+
value={prompt}
148+
onChange={(e) => onPromptChange(e.target.value)}
149+
placeholder={placeholder}
150+
disabled={isLoading || isCountingTokens} // Disable if main loading OR counting
151+
rows={3}
152+
aria-label="Prompt input"
153+
/>
154+
{selectedAspectRatio && (
155+
<div style={{ color: '#aaa', fontSize: '0.9em', marginTop: '5px', width: '100%' }}>
156+
Use aspect ratio {selectedAspectRatio}
157+
</div>
158+
)}
159+
</div>
149160
<button
150161
className={styles.runButton}
151162
onClick={onSubmit}

ai/ai-react-app/src/components/Layout/LeftSidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const LeftSidebar: React.FC<LeftSidebarProps> = ({
4040
// Define the available modes and their display names
4141
const modes: { id: AppMode; label: string }[] = [
4242
{ id: "chat", label: "Chat" },
43-
{ id: "imagenGen", label: "Imagen Generation" },
43+
{ id: "nanobanana", label: "Nano Banana" },
4444
{ id: "live", label: "Live Conversation" },
4545
];
4646

ai/ai-react-app/src/components/Layout/MainLayout.tsx

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,23 @@ import TopBar from "./TopBar";
33
import LeftSidebar from "./LeftSidebar";
44
import RightSidebar from "./RightSidebar";
55
import ChatView from "../../views/ChatView";
6-
import ImagenView from "../../views/ImagenView";
6+
import NanoBananaView from "../../views/NanoBananaView";
77
import LiveView from "../../views/LiveView";
88
import { AppMode } from "../../App";
99
import {
1010
UsageMetadata,
1111
ModelParams,
12-
ImagenModelParams,
1312
BackendType,
1413
AI,
1514
VertexAIBackend,
1615
GoogleAIBackend,
1716
getAI,
17+
ResponseModality,
1818
} from "firebase/ai";
1919
import {
2020
AVAILABLE_GENERATIVE_MODELS,
21-
AVAILABLE_IMAGEN_MODELS,
21+
AVAILABLE_NANO_BANANA_MODELS,
2222
defaultGenerativeParams,
23-
defaultImagenParams,
2423
} from "../../services/firebaseAIService";
2524
import styles from "./MainLayout.module.css";
2625
import { getApp } from "firebase/app";
@@ -46,10 +45,13 @@ const MainLayout: React.FC<MainLayoutProps> = ({
4645
model: AVAILABLE_GENERATIVE_MODELS[0],
4746
...defaultGenerativeParams,
4847
});
49-
const [imagenParams, setImagenParams] = useState<ImagenModelParams>({
50-
model: AVAILABLE_IMAGEN_MODELS[0],
51-
...defaultImagenParams,
48+
const [nanoBananaParams, setNanoBananaParams] = useState<ModelParams>({
49+
model: AVAILABLE_NANO_BANANA_MODELS[0],
50+
generationConfig: {
51+
responseModalities: [ResponseModality.TEXT, ResponseModality.IMAGE],
52+
},
5253
});
54+
const [selectedAspectRatio, setSelectedAspectRatio] = useState<string | undefined>();
5355

5456
const [usageMetadata, setUsageMetadata] = useState<UsageMetadata | null>(
5557
null,
@@ -60,7 +62,9 @@ const MainLayout: React.FC<MainLayoutProps> = ({
6062
try {
6163
const backendInstance =
6264
activeBackendType === BackendType.VERTEX_AI
63-
? new VertexAIBackend()
65+
? activeMode === "nanobanana"
66+
? new VertexAIBackend("global")
67+
: new VertexAIBackend()
6468
: new GoogleAIBackend();
6569
const aiInstance = getAI(getApp(), { backend: backendInstance });
6670
setActiveAI(aiInstance);
@@ -75,14 +79,14 @@ const MainLayout: React.FC<MainLayoutProps> = ({
7579
);
7680
setActiveAI(null);
7781
}
78-
}, [activeBackendType]);
82+
}, [activeBackendType, activeMode]);
7983

8084
useEffect(() => {
8185
setUsageMetadata(null);
8286
}, [activeMode]);
8387

8488
useEffect(() => {
85-
const validModes: AppMode[] = ["chat", "imagenGen", "live"];
89+
const validModes: AppMode[] = ["chat", "nanobanana", "live"];
8690
if (!validModes.includes(activeMode)) {
8791
console.warn(`Invalid activeMode "${activeMode}". Resetting to "chat".`);
8892
setActiveMode("chat");
@@ -109,9 +113,9 @@ const MainLayout: React.FC<MainLayoutProps> = ({
109113
activeMode={activeMode}
110114
/>
111115
);
112-
case "imagenGen":
116+
case "nanobanana":
113117
return (
114-
<ImagenView aiInstance={activeAI} currentParams={imagenParams} />
118+
<NanoBananaView aiInstance={activeAI} currentParams={nanoBananaParams} selectedAspectRatio={selectedAspectRatio} />
115119
);
116120
case "live":
117121
return (
@@ -152,8 +156,10 @@ const MainLayout: React.FC<MainLayoutProps> = ({
152156
activeMode={activeMode}
153157
generativeParams={generativeParams}
154158
setGenerativeParams={setGenerativeParams}
155-
imagenParams={imagenParams}
156-
setImagenParams={setImagenParams}
159+
nanoBananaParams={nanoBananaParams}
160+
setNanoBananaParams={setNanoBananaParams}
161+
selectedAspectRatio={selectedAspectRatio}
162+
setSelectedAspectRatio={setSelectedAspectRatio}
157163
/>
158164
</div>
159165
</div>

0 commit comments

Comments
 (0)