feat: AI AnP support working.

This commit is contained in:
mbruzon 2026-06-11 13:17:31 +02:00
parent 36413778e7
commit 20e877eac2
13 changed files with 168 additions and 160 deletions

View File

@ -1,17 +1,17 @@
[
["Qué es AnP", "/MD/PseudoLoRAs/AnP.md", [], false],
["AnP - Gestores", "/MD/AnP.Managers.md", [], false],
["AnP - Gestores - Configuraciones", "/MD/AnP.Settings.md", [], false],
["AnP - Gestores - I18N", "/MD/AnP.i18n.md", [], false],
["AnP - Gestores - Tipos de impresión", "/MD/AnP.PrintTypes.md", [], false],
["AnP - Gestores - Terminal/Consola", "/MD/AnP.Terminal.md", [], false],
["AnP - Gestores - Modelos", "/MD/AnP.Models.md", [], false],
["AnP - Gestores - Controladores de IA", "/MD/AnP.Controllers.md", [], false],
["AnP - Gestores - Índices", "/MD/AnP.Indexes.md", [], false],
["AnP - Gestores - Rutas", "/MD/AnP.Routes.md", [], false],
["AnP - Web Sockets - WebSocketServerDriver", "/MD/AnP.WebSocketServerDriver.md", [], false],
["AnP - Intérpretes de IA - OllamaDriver", "/MD/AnP.OllamaDriver.md", [], false],
["AnP - Servidores HTTP - HTTPDriver", "/MD/AnP.HTTPDriver.md", [], false],
["AnP - Modelos - IA", "/MD/AnP.AIModel.md", [], false],
["AnP - Controladores - IA", "/MD/AnP.AIController.md", [], false]
["Gestión de gestores de AnP", "/MD/AnP.Managers.md", [], false],
["Gestión de configuración de AnP", "/MD/AnP.Settings.md", [], false],
["Gestión de I18N/Idiomas/Textos de AnP", "/MD/AnP.i18n.md", [], false],
["Gestión de tipos de mensajes de consola/terminal de AnP", "/MD/AnP.PrintTypes.md", [], false],
["Gestión de comandos de Terminal/Consola de AnP", "/MD/AnP.Terminal.md", [], false],
["Gestión de modelos genéricos de AnP", "/MD/AnP.Models.md", [], false],
["Gestión de controladores de AnP", "/MD/AnP.Controllers.md", [], false],
["Gestión de archivos índice de AnP", "/MD/AnP.Indexes.md", [], false],
["Gestión de rutas de AnP", "/MD/AnP.Routes.md", [], false],
["Driver de Web Socket Servidor `WebSocketServerDriver` de AnP", "/MD/AnP.WebSocketServerDriver.md", [], false],
["Intérprete de IA `OllamaDriver` de AnP", "/MD/AnP.OllamaDriver.md", [], false],
["Driver de Servidor HTTP `HTTPDriver` de AnP", "/MD/AnP.HTTPDriver.md", [], false],
["Modelo de respuesta para IA de AnP", "/MD/AnP.AIResponseModel.md", [], false],
["Controlador de IA de AnP", "/MD/AnP.AIController.md", [], false]
]

View File

@ -50,9 +50,9 @@
"Debes responder ÚNICAMENTE con un array JSON de booleanos que tenga exactamente {items} elementos.\n",
"Cada posición del array de salida debe corresponder textualmente a la posición del título en la lista original:\n\n",
"- true: Si el título cumple el criterio.\n",
"- false: Si el título NO cumple el criterio.\n",
"No añadas explicaciones, solo el JSON final.\n\n",
"Si ningún título cumple el criterio, responde con un array de booleanos con todos sus valores en false.\n",
"- false: Si el título NO cumple el criterio.\n\n",
"No añadas explicaciones, solo el JSON final.\n",
"Si ningún título cumple el criterio, responde con un array de booleanos con todos sus valores en false.\n\n",
"Lista de títulos a evaluar: \n{list}"
],
"ai_controller_response_system" : "Usa las siguientes guías para responder y dar soporte.{loras}",

View File

@ -1,5 +1,3 @@
## Gestión de configuración
Para gestionar las configuraciones de AnP hay que mandarle dichos parámetros por diccionarios, ya sea JSON como un diccionario a nivel de código. Se pueden apilar en Arrays.
Los parámetros de configuración son los siguientes:

View File

@ -1,5 +1,3 @@
## ¿Qué es AnP?
AnP, de Attach & Play en inglés, es un Framework basado en Gestores y su consumo sobre un sistema de Controladores.
Está desarrollado en Python para el servidor; y JavaScript para el entorno cliente Web.

View File

@ -181,17 +181,36 @@ export const AIChatComponent = (function(){
event.key == "Enter" && !event.shiftKey && send(item, event);
};
this.write_response = (id, fragment, ok, done) => {
// console.log([id, fragment]);
const set_status = (conversation, message, status) => {
console.log([conversation, message, status]);
const box = document.querySelector(".aichat .messages>[data-type=bot][data-id='" + id + "']"),
status = (
!ok ? "error" :
done ? "done" :
"loading"),
status_text = anp.i18n.get(status),
const box = document.querySelector(".aichat [data-conversation='" + conversation + "']>[data-type=bot][data-id='" + message + "']"),
status_box = box.querySelector("[data-name=status]"),
status_i18n_box = status_box.querySelector("[data-i18n]"),
status_text = anp.i18n.get(status);
box.setAttribute("data-status", status);
status_box.setAttribute("data-i18n", status);
status_box.setAttribute("title", status_text);
status_box.querySelector("[data-icon]").setAttribute("data-icon", status);
status_i18n_box.setAttribute("data-i18n", status_text);
status_i18n_box.innerHTML = status_text;
};
this.write_response = (conversation, message, fragment, ok, done) => {
// console.log([id, fragment]);
const box = document.querySelector(".aichat .messages>[data-type=bot][data-id='" + message + "']"),
// status = (
// !ok ? "error" :
// done ? "done" :
// "loading"),
// status_text = anp.i18n.get(status),
// status_box = box.querySelector("[data-name=status]"),
// status_i18n_box = status_box.querySelector("[data-i18n]"),
date = Date.now(),
tokens_box = box.querySelector("[data-name=response_tokens] .value"),
[html, raw_html, md] = format(box.querySelector(".md-content").textContent += fragment);
@ -203,13 +222,18 @@ export const AIChatComponent = (function(){
box.setAttribute("data-ok", ok);
box.setAttribute("data-done", done);
box.setAttribute("data-status", status);
// box.setAttribute("data-status", status);
status_box.setAttribute("data-i18n", status);
status_box.setAttribute("title", status_text);
status_box.querySelector("[data-icon]").setAttribute("data-icon", status);
status_i18n_box.setAttribute("data-i18n", status_text);
status_i18n_box.innerHTML = status_text;
// status_box.setAttribute("data-i18n", status);
// status_box.setAttribute("title", status_text);
// status_box.querySelector("[data-icon]").setAttribute("data-icon", status);
// status_i18n_box.setAttribute("data-i18n", status_text);
// status_i18n_box.innerHTML = status_text;
set_status(conversation, message, (
!ok ? "error" :
done ? "done" :
"loading"));
box.querySelector("[data-name=date_to]").querySelector(".value").innerHTML = date;
box.querySelector("[data-name=time]").querySelector(".value").innerHTML = date - Number(box.querySelector("[data-name=date_from] .value").innerText);
@ -217,6 +241,10 @@ export const AIChatComponent = (function(){
};
this.change_status = (conversation, message, status) => {
set_status(conversation, message, status);
};
constructor();
};

View File

@ -32,20 +32,34 @@ export const AIController = (function(){
*/
const constructor = () => {};
this.test = (...parameters) => {
console.log(parameters);
/**
* @param {!Object.<string, any|null>} data
* @param {!number} code
* @return {void}
* @access private
*/
this.message = (data, code) => {
anp.components.aichat.write_response(
data.data.conversation,
data.data.message,
data.data.response,
data.data.ok,
data.data.done
);
};
this.message = (data, code) => {
anp.components.aichat.write_response(data.data.data_id, data.data.response, data.data.ok, data.data.done);
// const box = document.querySelector(".aichat .messages [data-type=bot][data-id=" + data.data.data_id + "]");
// box.querySelector(".content").innerHTML += data.data.response;
// box.setAttribute("data-done", data.data.done);
// box.setAttribute("data-ok", data.data.ok);
/**
* @param {!Object.<string, any|null>} data
* @param {!number} code
* @return {void}
* @access private
*/
this.status = (data, code) => {
anp.components.aichat.change_status(
data.data.conversation,
data.data.message,
data.data.status
);
};
constructor();

View File

@ -1277,6 +1277,9 @@
content: "\f164";
font-family: "FA6FR";
}
.anp [data-icon=analyzing]::before {
content: "\e522";
}
.anp [data-icon=think]::before {
content: "\f731";
font-family: "FA6FB";

File diff suppressed because one or more lines are too long

View File

@ -25,6 +25,7 @@
[data-icon=raw_html]::before{content : unicode("f121");}
[data-icon=md]::before{content : unicode("f1c9");}
[data-icon=done]::before{content : unicode("f164"); font-family : $FA6FR;}
[data-icon=analyzing]::before{content : unicode("e522");}
[data-icon=think]::before{content : unicode("f731"); font-family : $FA6FB;}
[data-icon=loading]::before{content : unicode("f110");}
[data-icon=waiting]::before{content : unicode("f252");}
@ -37,4 +38,5 @@
[data-icon=time]::before{content : unicode("f017"); font-family : $FA6FR;}
[data-icon=length]::before{content : unicode("f546");}
[data-icon=response_tokens]::before{content : unicode("f4ad"); font-family : $FA6FR;}
}

View File

@ -1,65 +1,41 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Self, Any, Callable
from typing import Self, Callable
from Abstracts.ModelAbstract import ModelAbstract
from Abstracts.ControllerAbstract import ControllerAbstract
from Models.RequestModel import RequestModel
from Models.PseudoLoRAModel import PseudoLoRAModel
from Models.AIResponseModel import AIResponseModel
from Utils.Common import Common
class AIController(ControllerAbstract, ModelAbstract):
# def __message_execution(self:Self, end:Callable[[], None], request:RequestModel) -> None:
# session:str|None = request.session.get("anp_ai_session")
# message_id:str|None = request.get("message_id")
# client_id:str|None = request.get("client_id")
# session, _ = self.anp.ai_interpreters.request(
# "anp_responses",
# session,
# request.get("conversation"),
# request.get("message"),
# [],
# lambda id, response: self.anp.web_socket_servers.send("anp", "ai", "message", {
# "id" : id,
# "conversation" : response.conversation,
# "response" : response.response,
# "ok" : response.ok,
# "done" : response.done,
# "data_id" : message_id
# }, client_id)
# )
# if session is not None:
# request.session.set("anp_ai_session", session)
# end()
def __analyse_loras(self:Self, data:dict[str, Any], request:RequestModel, loras:list[str]) -> list[tuple[str, str]]:
def __analyse_loras(self:Self,
iterations:dict[str, int],
conversation:str,
message:str,
request:RequestModel,
loras:list[str]
) -> list[tuple[str, str]]:
session:str|None = request.session.get("anp_loras_session")
results:list[tuple[str, str]] = []
results:AIResponseModel
print(self.anp.i18n.get("ai_controller_loras_system", {
"items" : len(loras),
"list" : "".join("\n" + str(i) + ". " + title for i, title in enumerate(loras))
}))
session, results = self.anp.ai_interpreters.request(
"anp_titles",
session,
conversation,
message,
self.anp.i18n.get("ai_controller_loras_system", {
"items" : len(loras),
"list" : "".join("\n" + str(i) + ". " + title for i, title in enumerate(loras))
})
)
results = Common.json_decode(results.response)
iterations["i"] += 1
# session, results = self.anp.ai_interpreters.request(
# "anp_loras",
# session,
# data["conversation"],
# data["message"],
# self.anp.i18n.get("ai_controller_loras_system", {
# "items" : len(loras),
# "list" : "".join("\n" + str(i) + ". " + title for i, title in enumerate(loras))
# })
# )
data["i"] += 1
# if session is not None or data["i"] == 1:
# request.session.set("anp_loras_session", session)
if session is not None or iterations["i"] == 1:
request.session.set("anp_loras_session", session)
return results
@ -71,66 +47,40 @@ class AIController(ControllerAbstract, ModelAbstract):
conversation:str = request.get("conversation")
message:str = request.get("message")
loras:list[tuple[str, str]]
loras_data:dict[str, Any] = {
iterations:dict[str, int] = {"i" : 0}
self.anp.web_socket_servers.send("anp", "ai", "status", {
"message" : message_id,
"conversation" : conversation,
"message" : message,
"i" : 0
}
"status" : "analyzing"
}, client_id)
loras = self.anp.pseudoloras.get(lambda loras:self.__analyse_loras(loras_data, request, loras))
loras = self.anp.pseudoloras.get(lambda loras:self.__analyse_loras(iterations, conversation, message, request, loras))
print([title for title, _ in loras])
session, _ = self.anp.ai_interpreters.request(
"anp_responses",
session,
conversation,
message,
self.anp.i18n.get("ai_controller_response_system", {
"loras" : "".join("\n\n# " + title + "\n\n" + markdown for title, markdown in loras if markdown is not None)
}) if len(loras) else [],
lambda id, response: self.anp.web_socket_servers.send("anp", "ai", "message", {
"id" : id,
"conversation" : response.conversation,
"response" : response.response,
"ok" : response.ok,
"done" : response.done,
"message" : message_id,
"conversation" : conversation,
}, client_id)
)
# session, _ = self.anp.ai_interpreters.request(
# "anp_responses",
# session,
# conversation,
# message,
# ("Usa las siguientes guías para responder y dar soporte.\n\n" + "".join(
# "# " + title + "\n\n" + text + "\n\n" for title, text in loras
# ) if len(loras) else []),
# lambda id, response: self.anp.web_socket_servers.send("anp", "ai", "message", {
# "id" : id,
# "conversation" : response.conversation,
# "response" : response.response,
# "ok" : response.ok,
# "done" : response.done,
# "data_id" : message_id
# }, client_id)
# )
# if session is not None:
# request.session.set("anp_ai_session", session)
if session is not None:
request.session.set("anp_ai_session", session)
end()
# def __message_by_themes_execution(self:Self, end:Callable[[], None], request:RequestModel) -> None:
# session:str|None = request.session.get("anp_ai_session")
# message_id:str|None = request.get("message_id")
# client_id:str|None = request.get("client_id")
# session, _ = self.anp.ai_interpreters.request(
# "anp_responses",
# session,
# request.get("conversation"),
# request.get("message"),
# [],
# lambda id, response: self.anp.web_socket_servers.send("anp", "ai", "message", {
# "id" : id,
# "conversation" : response.conversation,
# "response" : response.response,
# "ok" : response.ok,
# "done" : response.done,
# "data_id" : message_id
# }, client_id)
# )
# if session is not None:
# request.session.set("anp_ai_session", session)
# end()
def message(self:Self, request:RequestModel) -> None:
self.anp.queues.add("anp", self.__message_execution, request)
request.set_response({

View File

@ -91,7 +91,7 @@ class PseudoLoRAsManager:
lora.clean_cache()
def get(self:Self,
callback:Callable[[list[PseudoLoRAModel]], bool],
callback:Callable[[list[PseudoLoRAModel]], list[bool]],
loras:Optional[list[PseudoLoRAModel]] = None
) -> list[tuple[str, str]]:
@ -102,7 +102,7 @@ class PseudoLoRAsManager:
if loras is None:
loras = self.__loras.copy()
loras_is:list[bool] = [(i, callback([lora.title])) for i, lora in enumerate(loras)]
loras_is:list[bool] = callback([lora.title for lora in loras])
new_loras:list[PseudoLoRAModel] = []

View File

@ -45,9 +45,7 @@ class Check:
return isinstance(item, REPattern)
@staticmethod
def is_json_data(item:Any|None, full:bool = False) -> bool:
if Check.is_dictionary(item) or Check.is_array(item):
return True
def is_json_string(item:Any|None, full:bool = False) -> bool:
if Check.is_string(item) and len(item = item.strip()) >= 2 and item[0] + item[-1] in ("{}", "[]"):
if not full:
return True
@ -57,3 +55,15 @@ class Check:
except Exception as _:
pass
return False
@staticmethod
def is_json(item:Any|None) -> bool:
return Check.is_dictionary(item) or Check.is_array(item)
@staticmethod
def is_json_data(item:Any|None, full:bool = False) -> bool:
if Check.is_json(item):
return True
if Check.is_string(item):
return Check.is_json_string(item, full)
return False

View File

@ -286,7 +286,7 @@ class Common:
try:
return json_encode(data)
except Exception as exception:
pass
print(exception)
return None
@staticmethod
@ -322,10 +322,15 @@ class Common:
@classmethod
def data_encode(cls:type[Self], data:Any|None) -> str|None:
if Check.is_json_data(data):
data = cls.json_encode(data)
elif not Check.is_string(data):
data = str(data)
if data is None:
return None
if not Check.is_json_string(data, True):
if Check.is_json(data):
data = cls.json_encode(data)
elif not Check.is_string(data):
data = str(data)
if data is None:
return None
return cls.base64_encode(data.encode("utf-8"))
@classmethod