Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion vertex-ai-firebase-angular-example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,19 @@ Here's an example of the running application:

## How to get started

1. Create and configure a project in Firebase. Follow the directions for [Step 1](https://firebase.google.com/docs/vertex-ai/get-started?platform=web) to create an project and a web app. Do not follow the instructions for adding the sdks, that has already been done for this repository.
1. Create and configure a project in Firebase. Follow the directions for [Step 1](https://firebase.google.com/docs/vertex-ai/get-started?platform=web) to create a project and a web app. Do not follow the instructions for adding the sdks, that has already been done for this repository.
1. Clone this repository or download the code to your local machine
1. `cd` into the root folder (e.g., `cd vertex-ai-firebase-angular`)
1. Take your project settings from Firebase Console and add them to `src/environments.ts`
```
export const environment = {
production: true,
firebase: {
/* project settings */
},
};
```

1. Install the dependencies with `npm install`
1. Update the Firebase project settings in `environment.ts`.
1. Run this example with `ng serve`
Binary file modified vertex-ai-firebase-angular-example/example-screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
137 changes: 128 additions & 9 deletions vertex-ai-firebase-angular-example/src/app/ai.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,147 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import { Injectable, Inject } from '@angular/core';
import { Injectable, Inject, inject } from "@angular/core";
import { FirebaseApp } from "@angular/fire/app";
import { getVertexAI, getGenerativeModel, GenerativeModel } from "firebase/vertexai";
import {
getVertexAI,
getGenerativeModel,
GenerativeModel,
ChatSession,
FunctionDeclarationsTool,
ObjectSchemaInterface,
Schema,
} from "@angular/fire/vertexai";
import { ProductService } from "./product.service";
import { Product } from "./product";

@Injectable({
providedIn: 'root'
providedIn: "root",
})
export class AiService {
readonly model: GenerativeModel;
private readonly model: GenerativeModel;
private readonly products: ProductService = inject(ProductService);
private readonly chat: ChatSession;

constructor(@Inject("FIREBASE_APP") private firebaseApp: FirebaseApp) {
const productsToolSet: FunctionDeclarationsTool = {
functionDeclarations: [
{
name: "getNumberOfProducts",
description:
"Get a count of the number of products available in the inventory.",
},
{
name: "getProducts",
description:
"Get an array of the products with the name and price of each product.",
},
{
name: "addToCart",
description: "Add one or more products to the cart.",
parameters: Schema.object({
properties: {
productsToAdd: Schema.array({
items: Schema.object({
description: "A single product with its name and price.",
properties: {
name: Schema.string({
description: "The name of the product.",
}),
price: Schema.number({
description: "The numerical price of the product.",
}),
},
// Specify which properties within each product object are required
required: ["name", "price"],
}),
}),
},
}) as ObjectSchemaInterface,
},
],
};

constructor(@Inject('FIREBASE_APP') private firebaseApp: FirebaseApp) {
// Initialize the Vertex AI service
const vertexAI = getVertexAI(this.firebaseApp);
const systemInstruction =
"Welcome to ng-produce. You are a superstar agent for this ecommerce store. you will assist users by answering questions about the inventory and event being able to add items to the cart.";

// Initialize the generative model with a model that supports your use case
this.model = getGenerativeModel(vertexAI, { model: "gemini-2.0-flash" });
this.model = getGenerativeModel(vertexAI, {
model: "gemini-2.0-flash",
systemInstruction: systemInstruction,
tools: [productsToolSet],
});

this.chat = this.model.startChat();
}

async ask(prompt: string) {
const result = await this.model.generateContent(prompt);
let result = await this.chat.sendMessage(prompt);
const functionCalls = result.response.functionCalls();

if (functionCalls && functionCalls.length > 0) {
for (const functionCall of functionCalls) {
switch (functionCall.name) {
case "getNumberOfProducts": {
const functionResult = this.getNumberOfProducts();
result = await this.chat.sendMessage([
{
functionResponse: {
name: functionCall.name,
response: { numberOfItems: functionResult },
},
},
]);
break;
}
case "getProducts": {
const functionResult = this.getProducts();
result = await this.chat.sendMessage([
{
functionResponse: {
name: functionCall.name,
response: { products: functionResult },
},
},
]);
break;
}
case "addToCart": {
console.log(functionCall.args);

const args = functionCall.args as { productsToAdd: Product[]}

const functionResult = this.addToCart(args.productsToAdd);

result = await this.chat.sendMessage([
{
functionResponse: {
name: functionCall.name,
response: { numberOfProductsAdded: functionResult },
},
}
]);
break;
}
}
}
}

return result.response.text();
}

getProducts() {
return this.products.getProducts();
}
getNumberOfProducts() {
return this.getProducts().length;
}

const response = result.response;
return response.text();
addToCart(productsToAdd: Product[]) {
for (let i = 0; i < productsToAdd.length; i++) {
this.products.addToCart(productsToAdd[i]);
}
}
}
155 changes: 155 additions & 0 deletions vertex-ai-firebase-angular-example/src/app/app.component.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
background: var(--primary);
color: white;
}

.page-header h1{
font-weight: bolder;
color: inherit;
}

section {
padding: 20px;
font-family: sans-serif;
}

h1 {
color: #333;
}

ul {
list-style: none;
padding: 0;
display: grid;
grid-template-columns: repeat(
auto-fill,
minmax(200px, 1fr)
);
gap: 16px;
}
.product-listing {
padding-top: 0px;
padding-left: 0px;
}

.product-card {
border: 1px solid #ddd;
padding: 16px;
border-radius: 8px;
background-color: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: transform 0.2s ease-in-out;
text-align: center;
}
.product-card .product-info {
text-align: start;
}

.product-card .product-price {
font-weight: bolder;
font-size: 20px;
}
.product-card .product-name {
margin-bottom: 10px;
}

.product-card h3 {
margin-top: 0;
margin-bottom: 8px;
font-size: 1.1em;
color: #0056b3;
}

.product-card p {
margin: 0;
color: #555;
font-size: 1em;
}

.product-card .add-to-cart-btn {
border-radius: 5px;
background-color: var(--primary);
color: white;
display: block;
width: 100%;
}

.product-card .add-to-cart-btn:hover {
cursor: pointer;
transform: translateY(-5px);
}

.container {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 20px;
padding: 20px;
background: var(--secondary);
}

.agent-window {
border: 1px solid #ccc;
border-radius: 5px;
height: 500px;
display: flex;
flex-direction: column;
background: white;
}

.agent-window .user-question-label {
margin-bottom: 10px;
}

.chat-history {
flex-grow: 1;
overflow-y: auto;
padding: 10px;
}

.chat-input {
padding: 10px;
border: 1px solid #ccc;
width: 60%;
}

.submit-btn {
border-radius: 5px;
background-color: var(--primary);
color: white;
}

.chat-message {
margin-bottom: 10px;
padding: 8px;
border-radius: 5px;
}

.user-message {
background-color: #e0f0ff;
text-align: right;
}

.chat-message, .user-message, .agent-message {
border-radius: 5px;
padding: 15px;
margin-bottom: 10px;
}

.agent-message {
background-color: #f0f0f0;
text-align: left;
}

.control-section {
display: flex;
}
Loading