This commit is contained in:
Sebastian Benz 2024-07-22 22:14:50 +02:00
Родитель a8308782a0
Коммит 8b9fbb3eee
13 изменённых файлов: 321 добавлений и 0 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -4,3 +4,4 @@ node_modules
# Temporary directory for debugging extension samples
_debug
_metadata
*.swp # vim temp files

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

@ -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-in-the-cloud/images/icon128.png Normal file

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

После

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

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

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

После

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

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

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

После

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

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

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

После

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

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

@ -0,0 +1,22 @@
{
"name": "Google Gemini Demo",
"version": "0.1",
"manifest_version": 3,
"description": "Try the Gemini Models.",
"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"
}
}

23
functional-samples/ai.gemini-in-the-cloud/package-lock.json сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,23 @@
{
"name": "Chrome Extensions Gemini Demo",
"version": "1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "Chrome Extensions Gemini Demo",
"version": "1.0",
"dependencies": {
"@google/generative-ai": "^0.15.0"
}
},
"node_modules/@google/generative-ai": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.15.0.tgz",
"integrity": "sha512-zs37judcTYFJf1U7tnuqnh7gdzF6dcWj9pNRxjA5JTONRoiQ0htrRdbefRFiewOIfXwhun5t9hbd2ray7812eQ==",
"engines": {
"node": ">=18.0.0"
}
}
}
}

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

@ -0,0 +1,8 @@
{
"name": "Chrome Extensions Gemini Demo",
"version": "1.0",
"dependencies": {
"@google/generative-ai": "^0.15.0"
},
"private": true
}

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

@ -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,33 @@
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="index.css" />
</head>
<body>
<h1>Google Gemini</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>
<button id="button-prompt" class="primary" disabled>Run</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,135 @@
import {
GoogleGenerativeAI,
HarmBlockThreshold,
HarmCategory
} from '../node_modules/@google/generative-ai/dist/index.mjs';
// Important! Do not expose your API in your extension code. You have to
// options:
//
// 1. Let users provide their own API key.
// 2. Manage API keys in your own server and proxy all calls to the Gemini
// API through your own server, where you can implement additional security
// measures such as authentification.
//
// It is only OK to put your API key into this file if you're the only
// user of your extension or for testing.
const apiKey = ''; // ...;
let genAI = null;
let model = null;
let generationConfig = null;
const inputPrompt = document.body.querySelector('#input-prompt');
const buttonPrompt = document.body.querySelector('#button-prompt');
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 labelTemperature = document.body.querySelector('#label-temperature');
function initModel(generationConfig = {}) {
const safetySettings = [
{
category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
threshold: HarmBlockThreshold.BLOCK_NONE
}
];
genAI = new GoogleGenerativeAI(apiKey);
model = genAI.getGenerativeModel({
model: 'gemini-1.5-flash',
safetySettings,
generationConfig: {
// maxOutputTokens: 1000,
temperature: generationConfig.temperature
}
});
return model.generationConfig;
}
async function runPrompt(prompt) {
try {
const result = await model.generateContent(prompt);
const response = await result.response;
console.log(response);
return response.text();
} catch (e) {
console.log('Prompt failed');
console.error(e);
console.log('Prompt:', prompt);
throw e;
}
}
async function initDefaults() {
generationConfig = initModel();
console.log('Model default:', generationConfig);
sliderTemperature.value = generationConfig.temperature;
labelTemperature.textContent = generationConfig.temperature;
labelTemperature.value = generationConfig.temperature;
}
initDefaults();
sliderTemperature.addEventListener('input', (event) => {
labelTemperature.textContent = event.target.value;
generationConfig.temperature = event.target.value;
});
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 generationConfig = {
temperature: sliderTemperature.value
};
initModel(generationConfig);
const response = await runPrompt(prompt, generationConfig);
showResponse(response);
} catch (e) {
showError(e);
}
});
function showLoading() {
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', '');
}