add Gemini API sample
This commit is contained in:
Родитель
a8308782a0
Коммит
8b9fbb3eee
|
@ -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));
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 3.9 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 267 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 458 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 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"
|
||||
}
|
||||
}
|
|
@ -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', '');
|
||||
}
|
Загрузка…
Ссылка в новой задаче