From 204176b33b420a1cd584c40a00478c9c91cb50c0 Mon Sep 17 00:00:00 2001 From: Jakub Vrana Date: Sun, 23 Mar 2025 07:34:33 +0100 Subject: [PATCH] New plugin: AI prompt in SQL command creating the queries with Google Gemini --- CHANGELOG.md | 1 + adminer/include/adminer.inc.php | 5 +++ adminer/include/plugins.inc.php | 5 +++ adminer/sql.inc.php | 4 ++- plugins/sql-gemini.php | 58 +++++++++++++++++++++++++++++++++ 5 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 plugins/sql-gemini.php diff --git a/CHANGELOG.md b/CHANGELOG.md index d05467e4..887efb75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Plugins: autoload plugins from adminer-plugins/ - Plugins: configure plugins with adminer-plugins.php - Plugins: Display loaded plugins in server overview +- New plugin: AI prompt in SQL command generating the queries with Google Gemini - New plugin: IMAP driver created for fun - New plugin: Display links to tables referencing current row - New plugin: Allow switching light and dark mode (bug #926) diff --git a/adminer/include/adminer.inc.php b/adminer/include/adminer.inc.php index 728ce503..fdd80507 100644 --- a/adminer/include/adminer.inc.php +++ b/adminer/include/adminer.inc.php @@ -263,6 +263,11 @@ class Adminer { return shorten_utf8(trim($query), 1000); } + /** Print HTML code just before the Execute button in SQL command + */ + function sqlPrintAfter() { + } + /** Description of a row in a table * @param string * @return string SQL expression, empty string for no description diff --git a/adminer/include/plugins.inc.php b/adminer/include/plugins.inc.php index 2a6eca6a..b72626ec 100644 --- a/adminer/include/plugins.inc.php +++ b/adminer/include/plugins.inc.php @@ -245,6 +245,11 @@ class Plugins extends Adminer { return $this->applyPlugin(__FUNCTION__, $args); } + function sqlPrintAfter() { + $args = func_get_args(); + return $this->applyPlugin(__FUNCTION__, $args); + } + function rowDescription($table) { $args = func_get_args(); return $this->applyPlugin(__FUNCTION__, $args); diff --git a/adminer/sql.inc.php b/adminer/sql.inc.php index 64ef96b6..02db018a 100644 --- a/adminer/sql.inc.php +++ b/adminer/sql.inc.php @@ -235,7 +235,9 @@ if (!isset($_GET["import"])) { echo "

"; textarea("query", $q, 20); echo script(($_POST ? "" : "qs('textarea').focus();\n") . "qs('#form').onsubmit = partial(sqlSubmit, qs('#form'), '" . js_escape(remove_from_uri("sql|limit|error_stops|only_errors|history")) . "');"); - echo "

$execute\n"; + echo "

"; + $adminer->sqlPrintAfter(); + echo "$execute\n"; echo lang('Limit rows') . ": \n"; } else { diff --git a/plugins/sql-gemini.php b/plugins/sql-gemini.php new file mode 100644 index 00000000..9a507d6b --- /dev/null +++ b/plugins/sql-gemini.php @@ -0,0 +1,58 @@ +apiKey = $apiKey; + $this->model = $model; + } + + function headers() { + if ($_POST["gemini"] && !isset($_POST["query"])) { + $prompt = "I have a database with this structure:\n\n"; + foreach (Adminer\tables_list() as $table => $type) { + $prompt .= Adminer\create_sql($table, false, "CREATE") . ";\n\n"; + } + $prompt .= "Give me this SQL query and nothing else:\n\n$_POST[gemini]"; + //~ echo $prompt; exit; + $context = stream_context_create(array("http" => array( + "method" => "POST", + "header" => array("User-Agent: AdminerSqlGemini", "Content-Type: application/json"), + "content" => '{"contents": [{"parts":[{"text": ' . json_encode($prompt) . '}]}]}', + ))); + $response = json_decode(file_get_contents("https://generativelanguage.googleapis.com/v1beta/models/$this->model:generateContent?key=$this->apiKey", false, $context)); + $text = $response->candidates[0]->content->parts[0]->text; + echo preg_replace('~```sql\n(.*\n)```~s', '\1', $text) . "\n"; + exit; + } + } + + function sqlPrintAfter() { + echo "

\n"; + echo "

" . Adminer\script("qsl('input').onclick = function () { ajax( + '', + req => { + qs('textarea.sqlarea').value = req.responseText; + const sqlarea = qs('pre.sqlarea'); + sqlarea.textContent = req.responseText; + sqlarea.oninput(); // syntax highlighting + }, + 'gemini=' + encodeURIComponent(this.form['gemini'].value) + ); }"); + } +}