Skip to content

Commit 42b232b

Browse files
committed
feat(firebase-ai-logic-basics): Web (JavaScript) SDK setup and capability guides
1 parent 7b08fa5 commit 42b232b

11 files changed

Lines changed: 584 additions & 211 deletions

File tree

‎skills/firebase-ai-logic-basics/references/sdk/capabilities/chat.md‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,33 @@ final chat = model.startChat(history: [
8383
final response = await chat.sendMessage(Content.text('What is CBT?'));
8484
print(response.text);
8585
```
86+
87+
## Web (JavaScript)
88+
89+
Maintain history automatically using `startChat`.
90+
91+
```javascript
92+
import { getAI, getGenerativeModel, GoogleAIBackend } from "firebase/ai";
93+
94+
const ai = getAI(app, { backend: new GoogleAIBackend() });
95+
// [AGENT] Replace "<latest_supported_model>" with the latest model from https://firebase.google.com/docs/ai-logic/models.md.txt
96+
const model = getGenerativeModel(ai, { model: "<latest_supported_model>" });
97+
98+
const chat = model.startChat({
99+
history: [
100+
{
101+
role: "user",
102+
parts: [{ type: "text", text: "Hello, I am a developer." }],
103+
},
104+
{
105+
role: "model",
106+
parts: [{ type: "text", text: "Great to meet you. How can I help with code?" }],
107+
},
108+
],
109+
});
110+
111+
async function sendMessage(msg) {
112+
const result = await chat.sendMessage(msg);
113+
return result.response.text();
114+
}
115+
```

‎skills/firebase-ai-logic-basics/references/sdk/capabilities/function_calling.md‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,64 @@ Future<String> askStockPrice() async {
181181
return response.text ?? 'No text in response';
182182
}
183183
```
184+
185+
## Web (JavaScript)
186+
187+
Declare the tool as a plain object with a `functionDeclarations` array (use the
188+
`Schema` helpers for the parameters), read `functionCalls()` from the response,
189+
and send the `functionResponse` part(s) back in a single turn:
190+
191+
```javascript
192+
import { getAI, getGenerativeModel, GoogleAIBackend, Schema } from "firebase/ai";
193+
194+
// Step 1: the app-side function the model can ask you to run.
195+
async function getStockPrice({ symbol }) {
196+
// TODO(developer): Call a real stock price API. Hardcoded for demo purposes.
197+
return { symbol, price: 189.84 };
198+
}
199+
200+
// Step 2: describe the function to the model.
201+
const getStockPriceTool = {
202+
functionDeclarations: [
203+
{
204+
name: "getStockPrice",
205+
description: "Get the current stock price for a given symbol.",
206+
parameters: Schema.object({
207+
properties: {
208+
symbol: Schema.string({ description: "The stock symbol, e.g. AAPL" }),
209+
},
210+
}),
211+
},
212+
],
213+
};
214+
215+
// Step 3: provide the declaration when creating the model.
216+
const ai = getAI(app, { backend: new GoogleAIBackend() });
217+
const model = getGenerativeModel(ai, {
218+
model: "<latest_supported_model>", // [AGENT] Replace with the latest model from https://firebase.google.com/docs/ai-logic/models.md.txt
219+
tools: [getStockPriceTool],
220+
});
221+
222+
async function askStockPrice() {
223+
// Step 4: send the prompt in a chat session and run any requested call(s).
224+
const chat = model.startChat();
225+
let result = await chat.sendMessage("What is the stock price of Apple?");
226+
227+
const functionResponses = [];
228+
for (const call of result.response.functionCalls() ?? []) {
229+
if (call.name === "getStockPrice") {
230+
const functionResult = await getStockPrice(call.args);
231+
functionResponses.push({
232+
type: "functionResponse",
233+
functionResponse: { id: call.id, name: call.name, response: functionResult },
234+
});
235+
}
236+
}
237+
238+
// Step 5: return all function results in a single turn so the model can write its final answer.
239+
if (functionResponses.length > 0) {
240+
result = await chat.sendMessage(functionResponses);
241+
}
242+
return result.response.text();
243+
}
244+
```

‎skills/firebase-ai-logic-basics/references/sdk/capabilities/image_generation.md‎

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,83 @@ if (response.inlineDataParts.isNotEmpty) {
174174
print('Error: No images were generated.');
175175
}
176176
```
177+
178+
## Web (JavaScript)
179+
180+
`ai` is the AI Logic service instance created during setup with
181+
`getAI(app, { backend: new GoogleAIBackend() })`. This sample requests both
182+
`TEXT` and `IMAGE`, so the model may return text parts alongside the image;
183+
`inlineDataParts()` returns only the image parts. Drop `ResponseModality.TEXT`
184+
for image-only output. To display the result, build a data URL from the part:
185+
`` `data:${image.mimeType};base64,${image.data}` ``.
186+
187+
```javascript
188+
import { getGenerativeModel, ResponseModality } from "firebase/ai";
189+
190+
// Create a `GenerativeModel` instance with a model that supports your use case
191+
const model = getGenerativeModel(ai, {
192+
model: "<latest_supported_image_model>", // [AGENT] Replace with the latest image model from https://firebase.google.com/docs/ai-logic/models.md.txt
193+
// Configure the model to respond with text and images (required)
194+
generationConfig: {
195+
responseModalities: [ResponseModality.TEXT, ResponseModality.IMAGE],
196+
},
197+
});
198+
199+
// Provide a text prompt instructing the model to generate an image
200+
const prompt = 'Generate an image of the Eiffel Tower with fireworks in the background.';
201+
202+
// To generate an image, call `generateContent` with the text input
203+
const result = await model.generateContent(prompt);
204+
205+
// Handle the generated image
206+
try {
207+
const inlineDataParts = result.response.inlineDataParts();
208+
if (inlineDataParts?.[0]) {
209+
const image = inlineDataParts[0].inlineData;
210+
console.log(image.mimeType, image.data);
211+
}
212+
} catch (err) {
213+
console.error('Prompt or candidate was blocked:', err);
214+
}
215+
```
216+
217+
### Edit an image
218+
219+
Convert the user's file to an inline data part and pass it with the prompt to
220+
the same image-capable `model`:
221+
222+
```javascript
223+
// Prepare an image for the model to edit
224+
async function fileToGenerativePart(file) {
225+
const base64EncodedDataPromise = new Promise((resolve, reject) => {
226+
const reader = new FileReader();
227+
reader.onloadend = () => resolve(String(reader.result).split(',')[1]);
228+
reader.onerror = reject;
229+
reader.readAsDataURL(file);
230+
});
231+
return {
232+
type: "inlineData",
233+
inlineData: { data: await base64EncodedDataPromise, mimeType: file.type },
234+
};
235+
}
236+
237+
// Provide a text prompt instructing the model to edit the image
238+
const prompt = "Edit this image to make it look like a cartoon";
239+
240+
const fileInputEl = document.querySelector("input[type=file]");
241+
const imagePart = await fileToGenerativePart(fileInputEl.files[0]);
242+
243+
// To edit the image, call `generateContent` with the image and text input
244+
const result = await model.generateContent([imagePart, prompt]);
245+
246+
// Handle the generated image
247+
try {
248+
const inlineDataParts = result.response.inlineDataParts();
249+
if (inlineDataParts?.[0]) {
250+
const image = inlineDataParts[0].inlineData;
251+
console.log(image.mimeType, image.data);
252+
}
253+
} catch (err) {
254+
console.error('Prompt or candidate was blocked:', err);
255+
}
256+
```

‎skills/firebase-ai-logic-basics/references/sdk/capabilities/multimodal_input.md‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,56 @@ final response = await model.generateContent([
121121
Cloud Storage for Firebase URL (Agent Platform Gemini API provider only): use
122122
`FileData('image/jpeg', 'gs://bucket-name/path/image.jpg')` in place of the
123123
`InlineDataPart` inside `Content.multi`.
124+
125+
## Web (JavaScript)
126+
127+
Firebase AI Logic accepts Base64 encoded data or specific file references.
128+
129+
```javascript
130+
import { getAI, getGenerativeModel, GoogleAIBackend } from "firebase/ai";
131+
132+
const ai = getAI(app, { backend: new GoogleAIBackend() });
133+
// [AGENT] Replace "<latest_supported_model>" with the latest model from https://firebase.google.com/docs/ai-logic/models.md.txt
134+
const model = getGenerativeModel(ai, { model: "<latest_supported_model>" });
135+
136+
// Helper to convert file to base64 generic object
137+
async function fileToGenerativePart(file) {
138+
const base64EncodedDataPromise = new Promise((resolve, reject) => {
139+
const reader = new FileReader();
140+
reader.onloadend = () => resolve(String(reader.result).split(',')[1]);
141+
reader.onerror = reject;
142+
reader.readAsDataURL(file);
143+
});
144+
145+
return {
146+
type: "inlineData",
147+
inlineData: {
148+
data: await base64EncodedDataPromise,
149+
mimeType: file.type,
150+
},
151+
};
152+
}
153+
154+
async function analyzeImage(prompt, imageFile) {
155+
const imagePart = await fileToGenerativePart(imageFile);
156+
const result = await model.generateContent([imagePart, prompt]);
157+
return result.response.text();
158+
}
159+
```
160+
161+
The same helper handles audio, video, and PDF `File` objects because it reads
162+
the MIME type from `file.type`. If you already hold base64 data, build the part
163+
directly with an explicit MIME type:
164+
165+
```javascript
166+
const pdfPart = { type: "inlineData", inlineData: { data: base64Pdf, mimeType: "application/pdf" } };
167+
const result = await model.generateContent([
168+
pdfPart,
169+
"Summarize the important results in this report.",
170+
]);
171+
console.log(result.response.text());
172+
```
173+
174+
Cloud Storage for Firebase URL (Agent Platform Gemini API provider only): pass
175+
`{ type: "fileData", fileData: { mimeType: "image/jpeg", fileUri: "gs://bucket-name/path/image.jpg" } }`
176+
in place of the `inlineData` part.

‎skills/firebase-ai-logic-basics/references/sdk/capabilities/on_device_hybrid.md‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,75 @@ print(response.content)
109109
Hybrid on-device inference is not available via Firebase AI Logic on Flutter
110110
(including Flutter web); the `firebase_ai` package always calls the cloud-hosted
111111
Gemini API. See https://firebase.google.com/docs/ai-logic/hybrid.md.txt.
112+
113+
## Web (JavaScript)
114+
115+
On Web, on-device inference uses Chrome's built-in
116+
[Prompt API](https://developer.chrome.com/docs/extensions/ai/prompt-api) on
117+
Chrome Desktop (v139+, with at least 22 GB free storage and >4 GB VRAM or 16 GB
118+
RAM). For local development, enable
119+
`chrome://flags/#prompt-api-for-gemini-nano-multimodal-input` and restart
120+
Chrome; for end users, serve the
121+
[Prompt API Chrome Origin Trial](https://developer.chrome.com/origintrials/#/view_trial/2533837740349325313)
122+
token.
123+
124+
Create the hybrid model with `mode: InferenceMode.PREFER_ON_DEVICE`, call
125+
`await hybridModel.initializeDeviceModel()` on a user gesture to ensure Gemini
126+
Nano is downloaded and initialized, then call `generateContent` and inspect
127+
`result.response.inferenceSource`:
128+
129+
```javascript
130+
import { getGenerativeModel, InferenceMode, InferenceSource } from "firebase/ai";
131+
132+
const hybridModel = getGenerativeModel(ai, {
133+
mode: InferenceMode.PREFER_ON_DEVICE,
134+
});
135+
136+
// Call initializeDeviceModel() on or after an end-user interaction (e.g. button click)
137+
document.querySelector("#generate").addEventListener("click", async () => {
138+
await hybridModel.initializeDeviceModel((progress) => {
139+
console.log(`On-device model download progress: ${Math.round(progress * 100)}%`);
140+
});
141+
142+
const prompt = "Write a story about a magic backpack.";
143+
const result = await hybridModel.generateContent(prompt);
144+
145+
// Compare with InferenceSource.ON_DEVICE ("on_device") or InferenceSource.IN_CLOUD ("in_cloud")
146+
const usedOnDevice = result.response.inferenceSource === InferenceSource.ON_DEVICE;
147+
console.log(`Source: ${result.response.inferenceSource} (onDevice=${usedOnDevice})`);
148+
console.log(result.response.text());
149+
});
150+
```
151+
152+
To override the default cloud model (`gemini-3.5-flash-lite` in
153+
`firebase@13.0.0+`; `gemini-2.5-flash-lite` in `firebase@12.x`) or request
154+
structured output from both paths, configure `inCloudParams` and
155+
`onDeviceParams` together:
156+
157+
```javascript
158+
import { getGenerativeModel, InferenceMode, Schema } from "firebase/ai";
159+
160+
const jsonSchema = Schema.object({
161+
properties: { name: Schema.string(), species: Schema.string() },
162+
});
163+
164+
const hybridModel = getGenerativeModel(ai, {
165+
mode: InferenceMode.PREFER_ON_DEVICE,
166+
// Cloud-hosted model settings (overrides the SDK's default cloud model)
167+
inCloudParams: {
168+
model: "<latest_supported_model>", // [AGENT] Replace with the latest model from https://firebase.google.com/docs/ai-logic/models.md.txt
169+
generationConfig: {
170+
responseMimeType: "application/json",
171+
responseSchema: jsonSchema,
172+
},
173+
},
174+
// On-device model settings (Chrome Prompt API options)
175+
onDeviceParams: {
176+
promptOptions: { responseConstraint: jsonSchema },
177+
},
178+
});
179+
```
180+
181+
For enum output use `Schema.enumString({ enum: [...] })` with
182+
`responseMimeType: "text/x.enum"` in `inCloudParams` and the same schema as the
183+
`responseConstraint`.

‎skills/firebase-ai-logic-basics/references/sdk/capabilities/search_grounding.md‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,44 @@ if (groundingChunks != null) {
127127
}
128128
}
129129
```
130+
131+
## Web (JavaScript)
132+
133+
Pass `{ googleSearch: {} }` in `tools`. `ai` is the instance returned by
134+
`getAI(app, { backend: new GoogleAIBackend() })` during setup. Insert
135+
`renderedContent` into the DOM as-is (it already includes its CSS).
136+
137+
```javascript
138+
import { getGenerativeModel } from "firebase/ai";
139+
140+
const model = getGenerativeModel(ai, {
141+
model: "<latest_supported_model>", // [AGENT] Replace with the latest model from https://firebase.google.com/docs/ai-logic/models.md.txt
142+
// Provide Google Search as a tool that the model can use to generate its response
143+
tools: [{ googleSearch: {} }]
144+
});
145+
146+
const result = await model.generateContent("Who won the euro 2024?");
147+
148+
// Get the model's text response
149+
const text = result.response.text();
150+
151+
// Get the grounding metadata
152+
const groundingMetadata = result.response.candidates?.[0]?.groundingMetadata;
153+
154+
// REQUIRED - display Google Search suggestions
155+
// (renderedContent contains HTML and CSS for the search widget)
156+
const renderedContent = groundingMetadata?.searchEntryPoint?.renderedContent;
157+
if (renderedContent) {
158+
// TODO(developer): render this HTML and CSS in the UI
159+
}
160+
161+
// REQUIRED - display sources
162+
const groundingChunks = groundingMetadata?.groundingChunks;
163+
if (groundingChunks) {
164+
for (const chunk of groundingChunks) {
165+
const title = chunk.web?.title; // for example, "uefa.com"
166+
const uri = chunk.web?.uri; // for example, "https://vertexaisearch.cloud.google.com..."
167+
// TODO(developer): show sources in the UI
168+
}
169+
}
170+
```

‎skills/firebase-ai-logic-basics/references/sdk/capabilities/streaming.md‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,22 @@ await for (final chunk in response) {
6060
print(chunk.text); // Update UI incrementally
6161
}
6262
```
63+
64+
## Web (JavaScript)
65+
66+
For real-time UI updates (like a typing effect), `generateContentStream`
67+
resolves to a result whose `stream` is an async iterable of chunks; read each
68+
chunk's `text()`. After the loop, `await result.response` gives the aggregated
69+
final response if you also need the complete text. For a chat session call
70+
`chat.sendMessageStream(msg)` and iterate `result.stream` the same way.
71+
72+
```javascript
73+
async function streamResponse(prompt) {
74+
const result = await model.generateContentStream(prompt);
75+
for await (const chunk of result.stream) {
76+
const chunkText = chunk.text();
77+
console.log("Stream chunk:", chunkText);
78+
// Update UI here
79+
}
80+
}
81+
```

0 commit comments

Comments
 (0)