Creando su propio plugin de AI prompt
Puede crear su propio AI prompt.
Condiciones:
- La nueva clase de AI prompt debe implementar la interfaz “AIPrompt”.
- La nueva clase de AI prompt debe declararse bajo el paquete “com.openkm.plugin.ai”.
- La nueva clase de AI prompt debe anotarse con “@PluginImplementation”.
- La nueva clase de AI prompt debe extender “BasePlugin”.
Interfaz AI prompt:
package com.openkm.plugin.ai;
import com.openkm.ws.common.util.AIResult;import net.xeoh.plugins.base.Plugin;
public interface AIPrompt extends Plugin { String getName();
AIResult executeNodePrompt(String uuid) throws AIPromptException;
AIResult executeTextPrompt(String text) throws AIPromptException;
String[] getContentTypes();
boolean isManageNodePrompt();
boolean isManageTextPrompt();}La nueva clase debe cargarse en el paquete com.openkm.plugin.ai porque el sistema de plugins de la aplicación intentará cargarla desde ahí.
Descripción de métodos
Sección titulada «Descripción de métodos»| Método | Tipo | Descripción |
|---|---|---|
| AIResult executeNodePrompt(String uuid) | AIResult | El método ejecuta un AI prompt sobre un nodo concreto. |
| AIResult executeTextPrompt(String text) | AIResult | El método ejecuta un AI prompt sobre un fragmento de texto concreto. |
| getName() | String | Devuelve el nombre mostrado en la lista de selección de la interfaz de usuario. |
| getContentTypes() | String[] | Devuelve una lista de tipos MIME de documentos en los que se permite ejecutar el prompt. Si la lista está vacía, el prompt puede aplicarse a cualquier documento del que pueda extraerse contenido en formato texto. Esta restricción solo se aplica a los AI prompts para nodos. |
| isManageNodePrompt() | boolean | Cuando el valor es true, indica que el plugin puede aplicarse a nodos. |
| isManageTextPrompt() | boolean | Cuando el valor es true, indica que el plugin puede aplicarse a texto. |
Ejemplo de imagen a texto
Sección titulada «Ejemplo de imagen a texto»Clase ImageToTextAIPrompt:
package com.openkm.plugin.ai;
import com.openkm.ai.AIProviderUtils;import com.openkm.bean.Document;import com.openkm.core.MimeTypeConfig;import com.openkm.module.db.DbDocumentModule;import com.openkm.plugin.BasePlugin;import com.openkm.ws.common.util.AIResult;import com.vladsch.flexmark.html.HtmlRenderer;import com.vladsch.flexmark.parser.Parser;import com.vladsch.flexmark.util.ast.Node;import com.vladsch.flexmark.util.data.MutableDataSet;import lombok.extern.slf4j.Slf4j;import net.xeoh.plugins.base.annotations.PluginImplementation;import org.apache.commons.io.IOUtils;import org.apache.commons.lang3.StringUtils;import org.springframework.beans.factory.annotation.Autowired;
import java.io.InputStream;
@Slf4j@PluginImplementationpublic class ImageToTextAIPrompt extends BasePlugin implements AIPrompt {
@Autowired private DbDocumentModule dbDocumentModule;
@Autowired private AIProviderUtils aiProviderUtils;
@Override public String getName() { return "Image to Text"; }
@Override public AIResult executeNodePrompt(String uuid) throws AIPromptException { log.debug("executeNodePrompt({})", uuid);
try { Document doc = dbDocumentModule.getProperties(null, uuid); AIResult result = new AIResult(); String text;
try (InputStream is = dbDocumentModule.getContent(null, uuid, false)) { text = aiProviderUtils.extractTextFromImage(doc.getMimeType(), IOUtils.toByteArray(is)); }
if (StringUtils.isNotEmpty(text)) { result.setText(text);
// HTML response MutableDataSet options = new MutableDataSet(); Parser parser = Parser.builder(options).build(); HtmlRenderer renderer = HtmlRenderer.builder(options).build(); Node document = parser.parse(text); result.setHtml(renderer.render(document)); } else { result.setError("Not being able to extract text from image"); result.setErrorCode(AIResult.ErrorCodes.OTHER); }
return result; } catch (Exception e) { throw new AIPromptException(e); } }
@Override public AIResult executeTextPrompt(String text) throws AIPromptException { throw new AIPromptException("Not implemented"); }
@Override public String[] getContentTypes() { return new String[]{MimeTypeConfig.MIME_JPEG, MimeTypeConfig.MIME_PNG, MimeTypeConfig.MIME_GIF}; }
@Override public boolean isManageNodePrompt() { return true; }
@Override public boolean isManageTextPrompt() { return false; }}Ejemplo de extracción de metadatos
Sección titulada «Ejemplo de extracción de metadatos»Definición XML de metadatos de factura:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE property-groups PUBLIC "-//OpenKM//DTD Property Groups 3.11//EN" "https://www.openkm.com/dtd/property-groups-3.11.dtd"><property-groups> <property-group label="Invoice" name="okg:invoice"> <input label="Invoice Number" name="okp:invoice.number"/> <input label="Invoice Date" type="date" name="okp:invoice.date"/> <input label="Invoice Amount" name="okp:invoice.amount"/> <input label="Customer Name" name="okp:invoice.customer_name"/> <input label="Customer CIF" name="okp:invoice.customer_cif"/> <textarea label="Customer Address" name="okp:invoice.customer_address"/> </property-group></property-groups>Clase SampleMetadataAIPrompt:
package com.openkm.plugin.ai;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;import com.fasterxml.jackson.annotation.JsonProperty;import com.fasterxml.jackson.databind.ObjectMapper;import com.openkm.ai.AIProviderUtils;import com.openkm.core.MimeTypeConfig;import com.openkm.module.db.DbDocumentModule;import com.openkm.module.db.DbPropertyGroupModule;import com.openkm.plugin.BasePlugin;import com.openkm.util.ISO8601;import com.openkm.ws.common.util.AIResult;import lombok.extern.slf4j.Slf4j;import net.xeoh.plugins.base.annotations.PluginImplementation;import org.apache.commons.lang3.StringUtils;import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashMap;import java.util.Map;
@Slf4j@PluginImplementationpublic class SampleMetadataAIPrompt extends BasePlugin implements AIPrompt { private static final ObjectMapper objectMapper = new ObjectMapper(); private static final String METADATA_GROUP = "okg:invoice";
@Autowired private DbPropertyGroupModule dbPropertyGroupModule;
@Autowired private DbDocumentModule dbDocumentModule;
@Autowired private AIProviderUtils aiProviderUtils;
@Override public String getName() { return "Sample Metadata AI"; }
@Override public AIResult executeNodePrompt(String uuid) throws AIPromptException { log.debug("executePrompt({})", uuid);
try { AIResult result = new AIResult(); String text = dbDocumentModule.getExtractedText(null, uuid); String json = "";
if (StringUtils.isNotEmpty(text)) { json = aiProviderUtils.extractMetadataFromText(text, InvoiceMetadataSimple.class); } else { result.setError("The document has no content or it has not been extracted yet"); result.setErrorCode(AIResult.ErrorCodes.TEXT_EMPTY); }
if (StringUtils.isNotEmpty(json)) { InvoiceMetadataSimple metadata = objectMapper.readValue(json, InvoiceMetadataSimple.class); log.info("JSON: {} => InvoiceMetadata: {}", json, metadata); StringBuilder sb = new StringBuilder();
if (metadata.isValid()) { Map<String, String> props = metadata.getProperties();
// Add or update metadata group if (dbPropertyGroupModule.hasGroup(null, uuid, METADATA_GROUP)) { dbPropertyGroupModule.setProperties(null, uuid, METADATA_GROUP, props); sb.append("Metadata updated for document"); } else { dbPropertyGroupModule.addGroup(null, uuid, METADATA_GROUP, props); sb.append("Metadata added to document"); } } else { sb.append("Invoice not detected"); }
// HTML response text response String html = "<p class=\"text-success\">" + sb + "</p>\n"; html += metadata.toHtml(); result.setHtml(html);
// Plain text response sb.append("\n"); sb.append(metadata.toText()); result.setText(sb.toString()); }
return result; } catch (Exception e) { throw new AIPromptException(e); } }
@Override public AIResult executeTextPrompt(String text) throws AIPromptException { throw new AIPromptException("Not implemented"); }
@Override public String[] getContentTypes() { return new String[]{MimeTypeConfig.MIME_TEXT}; }
@Override public boolean isManageNodePrompt() { return true; }
@Override public boolean isManageTextPrompt() { return false; }
@JsonIgnoreProperties(ignoreUnknown = true) public static class InvoiceMetadataSimple { public String invoice_number; public String invoice_date; public String invoice_amount; public String customer_cif; public String customer_name; public String customer_address;
public Map<String, String> getProperties() { Map<String, String> props = new HashMap<>(); props.put("okp:invoice.number", this.invoice_number); props.put("okp:invoice.amount", this.invoice_amount); props.put("okp:invoice.customer_cif", this.customer_cif); props.put("okp:invoice.customer_name", this.customer_name); props.put("okp:invoice.customer_address", this.customer_address);
if (ISO8601.parseBasic(this.invoice_date) != null) { props.put("okp:invoice.date", this.invoice_date); } else { log.warn("Wrong invoice date format: {}", this.invoice_date); }
return props; }
public boolean isValid() { return StringUtils.isNotEmpty(this.invoice_number) && StringUtils.isNotEmpty(this.invoice_date) && StringUtils.isNotEmpty(this.invoice_amount); }
public String toHtml() { StringBuilder builder = new StringBuilder(); builder.append("<ul>"); builder.append("<li><b>invoice_number</b>: ").append(invoice_number).append("</li>\n"); builder.append("<li><b>invoice_date</b>: ").append(invoice_date).append("</li>\n"); builder.append("<li><b>invoice_amount</b>: ").append(invoice_amount).append("</li>\n"); builder.append("<li><b>customer_cif</b>: ").append(customer_cif).append("</li>\n"); builder.append("<li><b>customer_name</b>: ").append(customer_name).append("</li>\n"); builder.append("<li><b>customer_address</b>: ").append(customer_address).append("</li>\n"); builder.append("</ul>"); return builder.toString(); }
public String toText() { StringBuilder builder = new StringBuilder(); builder.append("invoice_number: ").append(invoice_number).append("\n"); builder.append("invoice_date: ").append(invoice_date).append("\n"); builder.append("invoice_amount: ").append(invoice_amount).append("\n"); builder.append("customer_cif: ").append(customer_cif).append("\n"); builder.append("customer_name: ").append(customer_name).append("\n"); builder.append("customer_address: ").append(customer_address).append("\n"); return builder.toString(); } }}Ejemplo básico de resumen
Sección titulada «Ejemplo básico de resumen»Clase SummarizeTextAIPrompt:
package com.openkm.plugin.ai;
import com.openkm.ai.AIProviderUtils;import com.openkm.core.MimeTypeConfig;import com.openkm.module.db.DbDocumentModule;import com.openkm.plugin.BasePlugin;import com.openkm.ws.common.util.AIResult;import lombok.extern.slf4j.Slf4j;import net.xeoh.plugins.base.annotations.PluginImplementation;import org.apache.commons.lang3.StringUtils;import org.springframework.beans.factory.annotation.Autowired;
@Slf4j@PluginImplementationpublic class SummarizeTextAIPrompt extends BasePlugin implements AIPrompt { private static final String PROMPT = "Summarize the provided text responding in text language using the content. " + "Returned response in plain text with a maximum of 1000 characters.";
@Autowired private DbDocumentModule dbDocumentModule;
@Autowired private AIProviderUtils aiProviderUtils;
@Override public String getName() { return "Summarize Text AI"; }
@Override public AIResult executeNodePrompt(String uuid) throws AIPromptException { log.debug("executeNodePrompt({})", uuid);
try { AIResult result = new AIResult(); String text = dbDocumentModule.getExtractedText(null, uuid);
if (StringUtils.isNotEmpty(text)) { return generateSummary(text); } else { result.setError("The document has no content or it has not been extracted yet"); result.setErrorCode(AIResult.ErrorCodes.TEXT_EMPTY); }
return result; } catch (Exception e) { throw new AIPromptException(e); } }
@Override public AIResult executeTextPrompt(String text) throws AIPromptException { log.debug("executeTextPrompt({})", text);
try { return generateSummary(text); } catch (Exception e) { throw new AIPromptException(e); } }
/** * Generate summary from text */ private AIResult generateSummary(String text) throws Exception { AIResult result = new AIResult(); String summary = "";
if (StringUtils.isNotEmpty(text)) { summary = aiProviderUtils.processText(PROMPT, text); }
if (StringUtils.isNotEmpty(summary)) { // HTML response text response String html = "<p>" + summary + "</p>\n"; result.setHtml(html);
// Plain text response result.setText(summary); }
return result; }
@Override public String[] getContentTypes() { return new String[]{MimeTypeConfig.MIME_TEXT}; }
@Override public boolean isManageNodePrompt() { return true; }
@Override public boolean isManageTextPrompt() { return true; }}