Initial import
This commit is contained in:
240
lib/Gemini.php
Normal file
240
lib/Gemini.php
Normal file
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
|
||||
require_once(__DIR__ . "/File.php");
|
||||
require_once(__DIR__ . "/HTTPRequest.php");
|
||||
|
||||
class Gemini {
|
||||
|
||||
public $file_uri = "";
|
||||
|
||||
public $root = "https://generativelanguage.googleapis.com";
|
||||
|
||||
public $verbose = false;
|
||||
|
||||
private $_upload_callback = false;
|
||||
private $_process_callback = false;
|
||||
|
||||
public static function get_key() {
|
||||
$i = 0;
|
||||
$keys = [];
|
||||
|
||||
while ($k = Config::get("GEMINI_KEY_" . $i)) {
|
||||
$keys[] = $k;
|
||||
$i++;
|
||||
}
|
||||
|
||||
$keyid = rand(0, count($keys) - 1);
|
||||
|
||||
$key = $keys[$keyid];
|
||||
return $key;
|
||||
}
|
||||
|
||||
public function upload_file(File $file, $key = false) {
|
||||
|
||||
set_time_limit(300);
|
||||
|
||||
if ($this->verbose) {
|
||||
print("Uploading file...\n");
|
||||
flush();
|
||||
}
|
||||
$cb = false;
|
||||
|
||||
if ($this->_upload_callback) {
|
||||
$cb = $this->_upload_callback;
|
||||
}
|
||||
|
||||
if ($cb) $cb(0);
|
||||
|
||||
|
||||
if ($key == false) {
|
||||
$key = Gemini::get_key();
|
||||
}
|
||||
|
||||
$json = "{'file': {'display_name': '" . $file->basename() . "'}}";
|
||||
|
||||
if ($this->verbose) {
|
||||
print("Upload data: " . $json . "\n");
|
||||
flush();
|
||||
}
|
||||
|
||||
|
||||
$h = new HTTPRequest();
|
||||
|
||||
$r = $h->post($this->root . "/upload/v1beta/files", $json, [
|
||||
"x-goog-api-key: " . $key,
|
||||
"Content-Type: application/json",
|
||||
"X-Goog-Upload-Protocol: resumable",
|
||||
"X-Goog-Upload-Command: start",
|
||||
"X-Goog-Upload-Header-Content-Length: " . $file->size(),
|
||||
"X-Goog-Upload-Header-Content-Type: " . $file->mime()
|
||||
]);
|
||||
|
||||
if ($this->verbose) {
|
||||
print("Request data:\n");
|
||||
print_r($h);
|
||||
flush();
|
||||
}
|
||||
|
||||
if ($r != 200) {
|
||||
throw new Exception("Error uploading file to Gemini", $r);
|
||||
}
|
||||
|
||||
$url = $h->headers['x-goog-upload-url'];
|
||||
$chunksize = $h->headers['x-goog-upload-chunk-granularity'];
|
||||
|
||||
$size = $file->size();
|
||||
$s = $size;
|
||||
|
||||
$pos = 0;
|
||||
|
||||
while ($s > 0) {
|
||||
|
||||
$chunk = min($s, $chunksize);
|
||||
|
||||
$pct = round($pos / $size * 100);
|
||||
if ($cb) $cb($pct);
|
||||
|
||||
if ($this->verbose) {
|
||||
printf("***** %d%%\n", round($pct));
|
||||
flush();
|
||||
}
|
||||
|
||||
$data = $file->get_chunk($pos, $chunk);
|
||||
$s -= $chunk;
|
||||
|
||||
$fin = $s == 0 ? ", finalize" : "";
|
||||
|
||||
$r = $h->post($url, $data, [
|
||||
"Content-Length: " . $chunk,
|
||||
"X-Goog-Upload-Offset: " . $pos,
|
||||
"X-Goog-Upload-Command: upload$fin"
|
||||
]);
|
||||
|
||||
if ($r != 200) {
|
||||
throw new Exception("Error uploading chunk $pos to Gemini", $r);
|
||||
}
|
||||
|
||||
if ($this->verbose) {
|
||||
print_r($r);
|
||||
flush();
|
||||
}
|
||||
|
||||
$pos += $chunk;
|
||||
}
|
||||
|
||||
$d = json_decode($h->body);
|
||||
|
||||
if ($this->verbose) {
|
||||
print("Final returned body:\n");
|
||||
print_r($d);
|
||||
}
|
||||
|
||||
if ($cb) $cb(100);
|
||||
return $d->file->uri;
|
||||
}
|
||||
|
||||
|
||||
public function geminiOverview(File $file) {
|
||||
|
||||
$cb = false;
|
||||
if ($this->_process_callback) $cb = $this->_process_callback;
|
||||
|
||||
if ($cb) $cb("Uploading file");
|
||||
|
||||
if ($this->verbose) {
|
||||
print("<pre>");
|
||||
}
|
||||
|
||||
$key = Gemini::get_key();
|
||||
|
||||
$uri = $this->upload_file($file, $key);
|
||||
|
||||
|
||||
$ob = [
|
||||
"contents" => [
|
||||
[
|
||||
"parts" => [
|
||||
[
|
||||
"text" => "Summarize this document. In addition provide me the title of the document as the first line, surrounded by { and }. If it is all in capitals, re-capitalize it to make it easier to read. On the second line give the document order number surrounded by [ and ].",
|
||||
],
|
||||
[
|
||||
"file_data" => [
|
||||
"mime_type" => $file->mime(),
|
||||
"file_uri" => $uri
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$json = json_encode($ob, JSON_PRETTY_PRINT);
|
||||
|
||||
if ($this->verbose) {
|
||||
print("AI reuqest: " . $json . "\n");
|
||||
flush();
|
||||
}
|
||||
set_time_limit(300);
|
||||
|
||||
|
||||
if ($cb) $cb("Sending request");
|
||||
|
||||
$h = new HTTPRequest();
|
||||
$r = $h->post($this->root . "/v1beta/models/gemini-2.5-flash-lite:generateContent", $json, [
|
||||
"x-goog-api-key: " . $key,
|
||||
"Content-Type: application/json"
|
||||
]);
|
||||
|
||||
if ($cb) $cb("Processing response");
|
||||
|
||||
if ($this->verbose) {
|
||||
print("AI Response:\n");
|
||||
print_r($h);
|
||||
flush();
|
||||
}
|
||||
|
||||
$resp = json_decode($h->body);
|
||||
|
||||
if ($r != 200) {
|
||||
if (@$resp->error) {
|
||||
throw new Exception($resp->error->message, $resp->error->code);
|
||||
} else {
|
||||
throw new Exception("HTTP Error $r requesting AI assistance", $r);
|
||||
}
|
||||
}
|
||||
|
||||
if (property_exists($resp, "candidates")) {
|
||||
$text = $resp->candidates[0]->content->parts[0]->text;
|
||||
$lines = explode("\n", $text);
|
||||
|
||||
$lastLine = "";
|
||||
$out = [];
|
||||
foreach ($lines as $line) {
|
||||
if (str_starts_with($line, "* ") and !str_starts_with($lastLine, "* ")) {
|
||||
$lastLine = $line;
|
||||
$line = "\n" . $line;
|
||||
} else if (str_starts_with($line, "1. ") and (trim($lastLine) != "")) {
|
||||
$lastLine = $line;
|
||||
$line = "\n" . $line;
|
||||
} else {
|
||||
$lastLine = $line;
|
||||
}
|
||||
|
||||
$out[] = $line;
|
||||
}
|
||||
|
||||
|
||||
return implode("\n", $out);
|
||||
}
|
||||
|
||||
throw new Exception("Content missing processing AI data", 0);
|
||||
}
|
||||
|
||||
public function upload_callback(callable $cb) {
|
||||
$this->_upload_callback = $cb;
|
||||
}
|
||||
|
||||
public function process_callback(callable $cb) {
|
||||
$this->_process_callback = $cb;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user