Files
2026-06-04 11:13:34 +01:00

212 lines
6.8 KiB
PHP

<?php
class GeminiJob extends Job {
public static function jobs() { return 1; }
public $docid;
public $move=false;
public function __construct($docid, $source="unknown") {
parent::__construct($source);
$this->docid = $docid;
}
public function run() {
$pdf = false;
$subs = [
"Installation and Operating Information",
"Installation and Configuration",
"Installing and Getting Started",
"Installation/Operator's Manual",
"Installation/Operator's Guide",
"Programmer's Reference Guide",
"Field Maintenance Print Set",
"Illustrated Parts Breakdown",
"Installation/Owner's Guide",
"Installation Information",
"Installation/User Guide",
"User Documentation Kit",
"Programmer Information",
"Technical Description",
"Operator Information",
"Configuration Guide",
"Upgrade Information",
"Installation Manual",
"Service Information",
"Installation Guide",
"Programming Manual",
"Maintenance Manual",
"Maintenance Guide",
"Technical Summary",
"Operator's Guide",
"System Reference",
"User Information",
"Technical Manual",
"Language Manual",
"Service Manual",
"Service Guide",
"Read Me First",
"Owner's Guide",
"Release Notes",
"Options Guide",
"Users' Manual",
"User's Manual",
"HiTest Notes",
"User's Guide",
"Design Guide",
"User Manual",
"User Guide",
];
$doc = new Document($this->docid);
$minsize = 999999999999999;
foreach ($doc->revisions as $r) {
$p = new PDF($r->path() . "/doc.pdf");
if ($p->size() < $minsize) {
$minsize = $p->size();
$rev = $r;
$pdf = $p;
}
}
if (!$pdf) {
$this->fail("No files to upload");
return;
}
$prods = [];
$db = DB::getInstance();
$q = $db->query("select id, full_path from product");
while ($r = $db->nextRecord($q)) {
$prods[] = $r->id . "," . $r->full_path;
}
if ($doc->is_incoming()) {
$this->move = true;
}
file_put_contents("/tmp/category-list.csv", implode("\n", $prods) . "\n");
$this->status("Processing document with Gemini");
try {
$gemini = new Gemini();
// $gemini->verbose = true;
$p = new GeminiParameter("title", "object", "The title of the document (note: none of these parts should include the order number)", true);
$p->add_child("main_title", "string", "The primary portion of the title, properly recapitalized to fit English grammar", true);
$p->add_child("subtitle", "string", "Any portion of the title that could be used as a subtitle, such as 'user guide' or 'maintenance guide' etc. Remove this from the main title. Should not be the order number.");
$p->add_child("subsubtitle", "string", "Any part of the title that could be extra information such as version number or language. Should not be the order number.");
$gemini->add_parameter($p);
$p = new GeminiParameter("category", "integer", "The category the document best fits in from the provided list of categories in the file category-list.csv", true);
$gemini->add_parameter($p);
$gemini->add_parameter(new GeminiParameter("order_number", "string", "The document order number, often in the format XX-YYYYY-ZZ where YYYYY is based on the product code and ZZ is the type of document. Never more than 15 characters long."));
$gemini->add_parameter(new GeminiParameter("overview", "string", "A brief one-line overview of the document content in plain text format", true));
$gemini->add_parameter(new GeminiParameter("summary", "string", "A longer summary of the document content in markdown format", true));
$gemini->upload_callback([$this, "uploadcb"]);
$gemini->process_callback([$this, "processcb"]);
$gemini->attach($pdf->basename(), $pdf);
$gemini->attach("category-list.csv", new File("/tmp/category-list.csv"));
$lines = $gemini->geminiOverview();
} catch (Exception $e) {
$this->status($e->getMessage());
if ($e->getMessage() == "The request timed out. Please try again.") {
$this->retry("timeout");
} else if ($e->getMessage() == "HTTP Error 0 requesting AI assistance") {
$this->retry("error 0");
} else if (str_starts_with($e->getMessage(), "You exceeded your current quota, please check your plan and billing details.")) {
$this->retries++;
if ($this->retries < 5) {
$this->retry("quota exceeded");
} else {
$this->fail("Too many retries");
}
} else if ($e->getMessage() == "This model is currently experiencing high demand. Spikes in demand are usually temporary. Please try again later.") {
$this->retry("overload");
} else {
$this->fail();
}
return;
}
$data = json_decode($lines, true);
if (!$data) {
$this->fail("No response from AI");
return;
}
// Reload document since this took a while
$doc = new Document($this->docid);
if (array_key_exists("title", $data)) {
$doc->subtitle = "";
$doc->subsubtitle = "";
if (array_key_exists("main_title", $data["title"])) { $doc->title = $data['title']['main_title']; }
if (array_key_exists("subtitle", $data["title"])) { $doc->subtitle = $data['title']['subtitle']; }
if (array_key_exists("subsubtitle", $data["title"])) { $doc->subsubtitle = $data['title']['subsubtitle']; }
if ($doc->subtitle != "") {
$doc->title = str_replace(" " . $doc->subtitle, "", $doc->title);
}
}
if (array_key_exists("order_number", $data)) { $doc->internal_id = substr($data['order_number'], 0, 20); }
if (array_key_exists("summary", $data)) { $doc->overview = $data['summary']; }
if (array_key_exists("overview", $data)) { $doc->oneliner = $data['overview']; }
$doc->save();
if (array_key_exists("category", $data)) {
$cat = $data['category'];
$q = $db->query("select * from product where id=:id", ["id" => $cat]);
if ($r = $db->nextRecord($q)) {
if ($this->move) {
$q = $db->query("delete from docproduct where document=:id", ["id" => $doc->id]);
}
$edp = DocProduct::find([
["product", "=", $r->id],
["document", "=", $doc->id]
])->first();
if (!$edp) {
$dp = new DocProduct;
$dp->document = $doc->id;
$dp->product = $r->id;
$dp->save();
}
} else {
print("--- Incorrect category suggested: " . $cat . "\n");
}
}
$this->status("Finished");
$this->finish();
return;
}
public function uploadcb($percent) {
$this->status("Uploading: " . $percent . "% complete");
}
public function processcb($message) {
$this->status($message);
}
function cleanup($txt) {
$txt = str_replace("Ø", "0", $txt);
$txt = str_replace(".", " ", $txt);
return $txt;
}
}