Add gemini nano sample

---------

Co-authored-by: Oliver Dunk <oliverdunk@google.com>
This commit is contained in:
Sebastian Benz 2024-07-22 17:44:44 +00:00 коммит произвёл GitHub
Родитель ad20939023
Коммит e1ad9ce8f0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
10 изменённых файлов: 297 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,14 @@
# On-device AI with Gemini Nano
This sample demonstrates how to use the built-in Chrome Prompt API.
## Overview
The extension provides a chat interface for the built-in Chrome prompt API. To learn more about the API and how to sign-up for the preview, head over to [Built-in AI on developer.chrome.com](https://developer.chrome.com/docs/ai/built-in).
## Running this extension
1. Clone this repository.
2. Load this directory in Chrome as an [unpacked extension](https://developer.chrome.com/docs/extensions/mv3/getstarted/development-basics/#load-unpacked).
3. Click the extension icon.
4. Interact with the prompt API in the sidebar.

Просмотреть файл

@ -0,0 +1,3 @@
chrome.sidePanel
.setPanelBehavior({ openPanelOnActionClick: true })
.catch((error) => console.error(error));

Двоичные данные
functional-samples/ai.gemini-on-device/images/icon128.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.9 KiB

Двоичные данные
functional-samples/ai.gemini-on-device/images/icon16.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 267 B

Двоичные данные
functional-samples/ai.gemini-on-device/images/icon32.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 458 B

Двоичные данные
functional-samples/ai.gemini-on-device/images/icon48.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 890 B

Просмотреть файл

@ -0,0 +1,22 @@
{
"name": "Chrome Built-in AI Demo",
"version": "0.1",
"manifest_version": 3,
"description": "Try the built-in AI preview in Chrome.",
"background": {
"service_worker": "background.js"
},
"permissions": ["sidePanel"],
"side_panel": {
"default_path": "sidepanel/index.html"
},
"action": {
"default_icon": {
"16": "images/icon16.png",
"32": "images/icon32.png",
"48": "images/icon48.png",
"128": "images/icon128.png"
},
"default_title": "Open Chat Interface"
}
}

Просмотреть файл

@ -0,0 +1,82 @@
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica,
Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
color: #1f1f1f;
background-color: #f2f2f2;
font-size: 16px;
padding: 8px;
}
input,
button,
textarea,
select {
font-family: inherit;
font-size: inherit;
}
button {
background: #333;
color: white;
border-radius: 8px;
border: none;
min-width: 100px;
padding: 8px;
margin: 16px 0;
cursor: pointer;
}
button.primary {
background: #333;
color: white;
}
button.secondary {
background: #ccc;
color: black;
}
button[disabled] {
background: #ddd;
color: #aaa;
}
input[type='range'] {
margin-top: 16px;
accent-color: black;
}
textarea {
--padding: 32px;
width: calc(100% - var(--padding));
max-width: calc(100% - var(--padding));
}
.text,
textarea {
background-color: white;
padding: 16px;
border-radius: 16px;
box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px, rgb(51, 51, 51) 0px 0px 0px 3px;
outline: none;
}
.blink {
animation: 1s ease-in-out 1s infinite reverse both running blink;
}
@keyframes blink {
25% {
opacity: 0.5;
}
50% {
opacity: 0;
}
75% {
opacity: 0.5;
}
}
[hidden] {
display: none;
}

Просмотреть файл

@ -0,0 +1,38 @@
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="index.css" />
</head>
<body>
<h1>Chrome built-in AI</h1>
<textarea
id="input-prompt"
placeholder='Type something, e.g. "Write a haiku about Chrome Extensions"'
cols="30"
rows="5"
></textarea>
<div>
<input
type="range"
id="temperature"
name="temperature"
min="0"
max="2"
step="0.01"
/>
<label for="temperature"
>Temperature: <span id="label-temperature"></span
></label>
</div>
<div>
<input type="range" id="top-k" name="top-k" min="1" max="50" step="1" />
<label for="top-k">Top-k: <span id="label-top-k"></span></label>
</div>
<button id="button-prompt" class="primary" disabled>Run</button>
<button id="button-reset" class="secondary" disabled>Reset</button>
<div id="response" class="text" hidden></div>
<div id="loading" class="text" hidden><span class="blink">...</span></div>
<div id="error" class="text" hidden></div>
<script src="index.js" type="module"></script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,138 @@
const inputPrompt = document.body.querySelector('#input-prompt');
const buttonPrompt = document.body.querySelector('#button-prompt');
const buttonReset = document.body.querySelector('#button-reset');
const elementResponse = document.body.querySelector('#response');
const elementLoading = document.body.querySelector('#loading');
const elementError = document.body.querySelector('#error');
const sliderTemperature = document.body.querySelector('#temperature');
const sliderTopK = document.body.querySelector('#top-k');
const labelTemperature = document.body.querySelector('#label-temperature');
const labelTopK = document.body.querySelector('#label-top-k');
let session;
async function runPrompt(prompt, params) {
try {
if (!session) {
// Start by checking if it's possible to create a session based on the availability of the model, and the characteristics of the device.
const canCreate = await self.ai.canCreateTextSession();
// canCreate will be one of the following:
// * "readily": the model is available on-device and so creating will happen quickly
// * "after-download": the model is not available on-device, but the device is capable,
// so creating the session will start the download process (which can take a while).
// * "no": the model is not available for this device.
if (canCreate === 'no') {
console.warn('Built-in prompt API not available.');
throw new Error(
'Built-in prompt API not available. Join the preview program to learn how to enable it.'
);
}
console.log('Creating new text session');
session = await self.ai.createTextSession(params);
}
return session.prompt(prompt);
} catch (e) {
console.log('Prompt failed');
console.error(e);
console.log('Prompt:', prompt);
// Reset session
reset();
throw e;
}
}
async function reset() {
if (session) {
session.destroy();
}
session = null;
}
async function initDefaults() {
const defaults = await window.ai.defaultTextSessionOptions();
console.log('Model default:', defaults);
sliderTemperature.value = defaults.temperature;
sliderTopK.value = defaults.topK;
labelTopK.textContent = defaults.topK;
labelTemperature.textContent = defaults.temperature;
labelTemperature.value = defaults.temperature;
}
initDefaults();
buttonReset.addEventListener('click', () => {
hide(elementLoading);
hide(elementError);
hide(elementResponse);
reset();
buttonReset.setAttribute('disabled', '');
});
sliderTemperature.addEventListener('input', (event) => {
labelTemperature.textContent = event.target.value;
reset();
});
sliderTopK.addEventListener('input', (event) => {
labelTopK.textContent = event.target.value;
reset();
});
inputPrompt.addEventListener('input', () => {
if (inputPrompt.value.trim()) {
buttonPrompt.removeAttribute('disabled');
} else {
buttonPrompt.setAttribute('disabled', '');
}
});
buttonPrompt.addEventListener('click', async () => {
const prompt = inputPrompt.value.trim();
showLoading();
try {
const params = {
temperature: sliderTemperature.value,
topK: sliderTopK.value
};
const response = await runPrompt(prompt, params);
showResponse(response);
} catch (e) {
showError(e);
}
});
function showLoading() {
buttonReset.removeAttribute('disabled');
hide(elementResponse);
hide(elementError);
show(elementLoading);
}
function showResponse(response) {
hide(elementLoading);
show(elementResponse);
// Make sure to preserve line breaks in the response
elementResponse.textContent = '';
const paragraphs = response.split(/\r?\n/);
for (const paragraph of paragraphs) {
if (paragraph) {
elementResponse.appendChild(document.createTextNode(paragraph));
}
elementResponse.appendChild(document.createElement('BR'));
}
}
function showError(error) {
show(elementError);
hide(elementResponse);
hide(elementLoading);
elementError.textContent = error;
}
function show(element) {
element.removeAttribute('hidden');
}
function hide(element) {
element.setAttribute('hidden', '');
}