#wip: PseudoLoRAs building...

This commit is contained in:
mbruzon 2026-06-10 14:53:32 +02:00
parent b4a1d09e3d
commit 36413778e7
27 changed files with 424 additions and 138 deletions

View File

@ -1,4 +1,17 @@
[ [
["Qué es AnP", "/MD/PseudoLoRAs/AnP.md", [], false], ["Qué es AnP", "/MD/PseudoLoRAs/AnP.md", [], false],
["Gestión de configuración", "/MD/AnP.Settings.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]
] ]

View File

@ -41,7 +41,22 @@
"AnP_TitlesManager_end" : null, "AnP_TitlesManager_end" : null,
"AnP_AIModel_start" : null, "AnP_AIModel_start" : null,
"AnP_AIModel_end" : null "AnP_AIModel_end" : null,
"AnP_AIController_start" : null,
"ai_controller_loras_system" : [
"Actúa como un clasificador estricto. Se te proporcionará una lista de títulos y un Prompt.\n",
"Debes evaluar cada título uno por uno en base al Prompt del usuario.\n",
"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",
"Lista de títulos a evaluar: \n{list}"
],
"ai_controller_response_system" : "Usa las siguientes guías para responder y dar soporte.{loras}",
"AnP_AIController_end" : null
} }
} }

View File

@ -95,7 +95,6 @@ export const WebSocketsClientsAbstract = (function(){
switch(data.controller + "." + data.action){ switch(data.controller + "." + data.action){
case "web_socket_client.set_id": case "web_socket_client.set_id":
self.id = data.data; self.id = data.data;
console.log(self.id);
break; break;
default: default:
anp.controllers.execute(data.controller, data.action, data, data.code); anp.controllers.execute(data.controller, data.action, data, data.code);

View File

@ -1,7 +1,7 @@
"use strict"; "use strict";
import { import {
Fieldset, Section, Form, Div, Nav, UL, LI, Span, Pre Fieldset, Section, Form, Div, Nav, UL, LI, Span, Pre, Article
} from "../Utils/HTMLDSL.ecma.js"; } from "../Utils/HTMLDSL.ecma.js";
import {MarkDown} from "../Utils/MarkDown.ecma.js"; import {MarkDown} from "../Utils/MarkDown.ecma.js";
import {Common} from "../Utils/Common.ecma.js"; import {Common} from "../Utils/Common.ecma.js";
@ -46,7 +46,13 @@ export const AIChatComponent = (function(){
return Fieldset({class : "aichat"}, [ return Fieldset({class : "aichat"}, [
anp.components.i18n(name, "legend"), anp.components.i18n(name, "legend"),
Section({class : "messages"}), Section({class : "conversations"}, [
Article({
class : "messages",
data_conversation : anp.unique_keys.get(),
data_selected : true
})
]),
Form({ Form({
method : "post", method : "post",
action : "#", action : "#",
@ -144,7 +150,8 @@ export const AIChatComponent = (function(){
const send = (item, event) => { const send = (item, event) => {
const text_box = item.querySelector("[name=message]"), const text_box = item.querySelector("[name=message]"),
text = text_box.value.trim(); text = text_box.value.trim(),
conversation = document.querySelector(".aichat [data-conversation][data-selected=true]").getAttribute("data-conversation");
event.preventDefault(); event.preventDefault();
@ -153,7 +160,7 @@ export const AIChatComponent = (function(){
const data_id = anp.unique_keys.get(); const data_id = anp.unique_keys.get();
Common.HTML( Common.HTML(
".aichat .messages", ".aichat [data-conversation=" + conversation + "]",
build_message(data_id, "user", text, "done"), build_message(data_id, "user", text, "done"),
build_message(data_id, "bot") build_message(data_id, "bot")
); );
@ -161,7 +168,8 @@ export const AIChatComponent = (function(){
anp.web_sockets_clients.send("anp", "ai", "message", { anp.web_sockets_clients.send("anp", "ai", "message", {
message_id : data_id, message_id : data_id,
message : text message : text,
conversation : conversation
}); });
}; };
@ -174,7 +182,7 @@ export const AIChatComponent = (function(){
}; };
this.write_response = (id, fragment, ok, done) => { this.write_response = (id, fragment, ok, done) => {
console.log([id, fragment]); // console.log([id, fragment]);
const box = document.querySelector(".aichat .messages>[data-type=bot][data-id='" + id + "']"), const box = document.querySelector(".aichat .messages>[data-type=bot][data-id='" + id + "']"),
status = ( status = (

View File

@ -161,7 +161,7 @@ export const WebSocketsClientsDriver = (function(){
controller : controller, controller : controller,
action : action, action : action,
data : data, data : data,
id : self.id id : _super.id
})); }));
}; };

View File

@ -1,6 +1,6 @@
"use strict"; "use strict";
import {Check} from "../Utils/Check.ecma.js"; import {Check} from "../Utils/Checks.ecma.js";
import {Common} from "../Utils/Common.ecma.js"; import {Common} from "../Utils/Common.ecma.js";
/** /**

View File

@ -213,6 +213,17 @@ export const UniqueKeysManager = (function(){
}); });
}; };
/**
* @param {!(string|Array.<string>)} new_keys
* @returns {void}
* @access public
*/
this.set = new_keys => {
Common.get_keys(new_keys).forEach(key => {
keys.includes(key) || keys.push(key);
});
};
constructor(); constructor();
}; };

View File

@ -32,6 +32,7 @@ class AIInterpretersAbstract(ABC):
self.sessions_i:int = 0 self.sessions_i:int = 0
self.think:bool = self.anp.settings.get(("ai_interpreter_think", "ai_think", "think"), inputs, False) self.think:bool = self.anp.settings.get(("ai_interpreter_think", "ai_think", "think"), inputs, False)
self.allow_contexts:bool = self.anp.settings.get(("ai_interpreter_allow_contexts", "ai_allow_contexts", "allow_contexts"), inputs, True) self.allow_contexts:bool = self.anp.settings.get(("ai_interpreter_allow_contexts", "ai_allow_contexts", "allow_contexts"), inputs, True)
self.temperature:float = self.anp.settings.get(("ai_interpreter_temperature", "ai_temperature", "temperature"), inputs, 0.7)
def start(self:Self) -> None: def start(self:Self) -> None:
pass pass
@ -39,18 +40,26 @@ class AIInterpretersAbstract(ABC):
def close(self:Self) -> None: def close(self:Self) -> None:
self.sessions = {} self.sessions = {}
def get_session(self:Self, id:int|None = None) -> tuple[int, list[int]]: def get_session(self:Self, id:int|None = None, conversation:str|None = None) -> tuple[int, list[int]]:
if id is None or id not in self.sessions: if id is None or id not in self.sessions:
id = self.sessions_i id = self.sessions_i
self.sessions_i += 1 self.sessions_i += 1
self.sessions[id] = [] self.sessions[id] = {}
return id, self.sessions[id] return id, self.sessions[id][conversation] if conversation in self.sessions[id] else []
def save_context(self:Self, id:int, context:list[int]) -> None: def save_context(self:Self, id:int, conversation:str, context:list[int]) -> None:
if self.allow_contexts and id in self.sessions: if self.allow_contexts and id in self.sessions:
self.sessions[id] = context if conversation not in self.sessions[id]:
self.sessions[id][conversation] = context
self.sessions[id][conversation] = context
def close_conversation(self:Self, id:int, conversation:str) -> bool:
if id in self.sessions and conversation in self.sessions[id]:
del self.sessions[id][conversation]
return True
return False
def close_session(self:Self, id:int) -> bool: def close_session(self:Self, id:int) -> bool:
if id in self.sessions: if id in self.sessions:
@ -62,29 +71,34 @@ class AIInterpretersAbstract(ABC):
return self.sessions[id] if id in self.sessions else [] return self.sessions[id] if id in self.sessions else []
def get_orders(self:Self, orders:Optional[str|list[str]] = None) -> str: def get_orders(self:Self, orders:Optional[str|list[str]] = None) -> str:
return (
orders if Check.is_string(orders) else
"\n".join(orders) if Check.is_array(orders) else
"")
results:str = "" # results:str = ""
i:int = 0 # i:int = 0
for block in (self.orders, orders): # for block in (self.orders, orders):
if block: # if block:
if Check.is_array(block): # if Check.is_array(block):
block_string:str = "".join(str(i + 1 + j) + ". " + str(order) + "\n" for j, order in enumerate(block)) # block_string:str = "".join(str(i + 1 + j) + ". " + str(order) + "\n" for j, order in enumerate(block))
i += len(block) # i += len(block)
results += ("\n\n" if results else "") + block_string.strip() # results += ("\n\n" if results else "") + block_string.strip()
else: # else:
results += ("\n\n" if results else "") + str(block).strip() # results += ("\n\n" if results else "") + str(block).strip()
return results # return results
@abstractmethod @abstractmethod
def request(self:Self, def request(self:Self,
session:int|None, session:int|None,
conversation:str|None,
message:str, message:str,
orders:list[str] = [], orders:str|list[str] = [],
callback:Optional[Callable[[int, AIResponseModel], None]] = None, callback:Optional[Callable[[int, AIResponseModel], None]] = None,
context:list[int] = [] custom_context:list[int] = []
) -> tuple[int|None, AIResponseModel]:pass ) -> tuple[int|None, AIResponseModel]:pass

View File

@ -3,6 +3,7 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Any, Self, Optional, Sequence from typing import Any, Self, Optional, Sequence
from re import Match as REMath
from Interfaces.Application.AnPInterface import AnPInterface from Interfaces.Application.AnPInterface import AnPInterface
from Utils.Common import Common from Utils.Common import Common
@ -20,6 +21,7 @@ class HTTPServersAbstract(ABC):
"port": self.port, "port": self.port,
"host": self.host "host": self.host
} }
self._session_timeout:int = anp.settings.get(("sessions_timeout", "timeout"), inputs, 3600)
self.key:str = key self.key:str = key
self.update() self.update()
@ -50,4 +52,42 @@ class HTTPServersAbstract(ABC):
self.host = self.DEFAULT_HOST self.host = self.DEFAULT_HOST
self.__update_print_data() self.__update_print_data()
self.update() self.update()
def load_cookies(self:Self, cookies:str|None) -> dict[str, Any|None]:
if not cookies:
return {}
cookie:str
results:dict[str, Any|None] = {}
for cookie in cookies.split(";"):
if "=" in cookie:
key:str
value:str
key, value = cookie.split("=", 1)
results[key.strip()] = value.strip()
return results
@staticmethod
def get_variables_from(source:str|None) -> dict[str, Any|None]:
if not source:
return {}
json:dict[str, Any|None] = Common.data_decode(source)
if json is not None:
return Common.get_dictionary(json)
json = {}
def callback(matches:REMath) -> str:
json[matches.group(1)] = matches.group(2)
return matches.group(0)
Common.replace(self.HTTP_VARIABLE, source, callback)
return json

View File

@ -14,6 +14,7 @@ class WebSocketServersAbstract(ABC):
self.key:str = key self.key:str = key
self.host:str = self.anp.settings.get(("web_socket_server_host", "host"), inputs, "") self.host:str = self.anp.settings.get(("web_socket_server_host", "host"), inputs, "")
self.port:int = self.anp.settings.get(("web_socket_server_port", "port"), inputs, 18765) self.port:int = self.anp.settings.get(("web_socket_server_port", "port"), inputs, 18765)
self.__sessions:dict[str, str] = {}
self.on_new_client:Event = Event() self.on_new_client:Event = Event()
self.on_message:Event = Event() self.on_message:Event = Event()
self.on_close:Event = Event() self.on_close:Event = Event()
@ -39,4 +40,15 @@ class WebSocketServersAbstract(ABC):
}) })
@abstractmethod @abstractmethod
def send(self:Self, controller:str, action:str, data:Any|None, ids:Optional[str|Sequence[str]] = None, code:int = 200) -> None:pass def send(self:Self, controller:str, action:str, data:Any|None, ids:Optional[str|Sequence[str]] = None, code:int = 200) -> None:pass
def set_session(self:Self, id:str, session:str) -> None:
if id not in self.__sessions:
self.__sessions[id] = session
def get_session(self:Self, id:str) -> str|None:
return self.__sessions.get(id)
def remove_session(self:Self, id:str) -> None:
if id in self.__sessions:
del self.__sessions[id]

View File

@ -5,6 +5,7 @@ from typing import Self, Any, Optional, Sequence
import datetime import datetime
from re import Match as REMatch from re import Match as REMatch
from traceback import extract_tb as extract_traceback, format_stack as trace_format_stack from traceback import extract_tb as extract_traceback, format_stack as trace_format_stack
from time import time as timestamp, sleep
from Managers.I18NManager import I18NManager from Managers.I18NManager import I18NManager
from Managers.SettingsManager import SettingsManager from Managers.SettingsManager import SettingsManager
from Managers.PrintTypesManager import PrintTypesManager from Managers.PrintTypesManager import PrintTypesManager
@ -170,4 +171,11 @@ class AnP:
data["end"] = Common.string_variables(self.__exception_format, data) data["end"] = Common.string_variables(self.__exception_format, data)
message and self.print("exception", message, data, i + 2) message and self.print("exception", message, data, i + 2)
def wait(self:Self, time:str|float) -> None:
date:float = timestamp()
while self.__working and timestamp() - date < float(time):
sleep(.1)

View File

@ -9,54 +9,128 @@ from Models.PseudoLoRAModel import PseudoLoRAModel
class AIController(ControllerAbstract, ModelAbstract): class AIController(ControllerAbstract, ModelAbstract):
def __test_execution(self:Self, end:Callable[[], None], request:RequestModel) -> None: # def __message_execution(self:Self, end:Callable[[], None], request:RequestModel) -> None:
self.anp.ai_interpreters.request(
"anp_responses",
None,
request.get("message", "Hola, Gemma. ¿Me puedes ayudar a instalar una impresora Canon?"),
# lambda id, response: print((id, response.response, request.get("client_id"))),
lambda id, response: self.anp.web_socket_servers.send("anp", "ai", "test", {
"id" : id,
"response" : response.response
}, request.get("client_id")),
# [
# "# Títulos\n" + "".join(
# "\n - " + title for title in [
# "Información, gestión e instalación de Cividas",
# "Información, gestión e instalación de Impresoras/Fotocopiadoras/Multifuncionales Canon"
# ]
# ),
# ]
)
end()
def test(self:Self, request:RequestModel) -> None: # session:str|None = request.session.get("anp_ai_session")
self.anp.queues.add("anp", self.__test_execution, request) # message_id:str|None = request.get("message_id")
request.set_response({ # client_id:str|None = request.get("client_id")
"ok" : True,
"code" : 200, # session, _ = self.anp.ai_interpreters.request(
"message" : "ok" # "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]]:
session:str|None = request.session.get("anp_loras_session")
results:list[tuple[str, str]] = []
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_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)
return results
def __message_execution(self:Self, end:Callable[[], None], request:RequestModel) -> None: def __message_execution(self:Self, end:Callable[[], None], request:RequestModel) -> None:
session:str|None = 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")
conversation:str = request.get("conversation")
message:str = request.get("message")
loras:list[tuple[str, str]]
loras_data:dict[str, Any] = {
"conversation" : conversation,
"message" : message,
"i" : 0
}
loras = self.anp.pseudoloras.get(lambda loras:self.__analyse_loras(loras_data, request, loras))
print([title for title, _ in loras])
# 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)
session, _ = self.anp.ai_interpreters.request(
"anp_responses",
session,
request.get("message"),
lambda id, response: self.anp.web_socket_servers.send("anp", "ai", "message", {
"id" : id,
"response" : response.response,
"ok" : response.ok,
"done" : response.done,
"data_id" : request.get("message_id")
}, request.get("client_id"))
)
end() 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: def message(self:Self, request:RequestModel) -> None:
self.anp.queues.add("anp", self.__message_execution, request) self.anp.queues.add("anp", self.__message_execution, request)
request.set_response({ request.set_response({

View File

@ -7,6 +7,7 @@ from Abstracts.ModelAbstract import ModelAbstract
from Models.RequestModel import RequestModel from Models.RequestModel import RequestModel
from threading import Thread from threading import Thread
from http.server import BaseHTTPRequestHandler, HTTPServer from http.server import BaseHTTPRequestHandler, HTTPServer
from http.cookies import SimpleCookie
from Abstracts.HTTPServersAbstract import HTTPServersAbstract from Abstracts.HTTPServersAbstract import HTTPServersAbstract
class HTTPDriver(HTTPServersAbstract, ModelAbstract): class HTTPDriver(HTTPServersAbstract, ModelAbstract):
@ -16,19 +17,50 @@ class HTTPDriver(HTTPServersAbstract, ModelAbstract):
def __process(self:Self, method:str) -> None: def __process(self:Self, method:str) -> None:
anp:AnPInterface = self.server.anp anp:AnPInterface = self.server.anp
server:HTTPServersAbstract = self.server.itself
request:RequestModel = RequestModel() request:RequestModel = RequestModel()
session_id:str|None = None
cookies:SimpleCookie|None = None
request.method = method request.method = method
request.path = self.path request.path = self.path.split("?")[0]
request.headers = dict(self.headers) request.headers = dict(self.headers)
request.client_host = self.client_address[0] request.client_host = self.client_address[0]
request.client_port = self.client_address[1] request.client_port = self.client_address[1]
request.get_variables = server.get_variables_from(self.path[self.path.find("?") + 1:] if "?" in self.path else None)
request.post_variables = server.get_variables_from(self.rfile.read(int(self.headers.get("Content-Length", 0))))
request.cookies = server.load_cookies(self.headers.get("Cookie"))
session_id = request.get("session")
request.session = anp.sessions._get_instance(session_id)
if session_id is None or request.session is None:
cookies = SimpleCookie()
session_id = anp.sessions.create()
cookies["session"] = session_id
cookies["session"]["path"] = "/"
cookies["session"]["max-age"] = server._session_timeout
# cookies["session"]["HttpOnly"] = True
# cookies["session"]["Secure"] = True
request.session = anp.sessions._get_instance(session_id)
anp.routes.go([self.server.key], method, self.path, request) anp.routes.go([self.server.key], method, self.path, request)
self.send_response(request.response_code) self.send_response(request.response_code)
self.send_header("Content-Type", request.response_mime) self.send_header("Content-Type", request.response_mime)
self.send_header("Content-Length", str(len(request.response))) self.send_header("Content-Length", str(len(request.response)))
if cookies is not None:
morsel:str
for morsel in cookies.values():
self.send_header("Set-Cookie", morsel.OutputString())
self.end_headers() self.end_headers()
self.wfile.write( self.wfile.write(
@ -113,5 +145,6 @@ class HTTPDriver(HTTPServersAbstract, ModelAbstract):
def __run_service(self:Self) -> None: def __run_service(self:Self) -> None:
self.__server = HTTPServer((self.host, self.port), self.HTTPRequestHandler) self.__server = HTTPServer((self.host, self.port), self.HTTPRequestHandler)
self.__server.anp = self.anp self.__server.anp = self.anp
self.__server.itself = self
self.__server.key = self.key self.__server.key = self.key
self.__server.serve_forever() self.__server.serve_forever()

View File

@ -22,24 +22,24 @@ class OllamaDriver(AIInterpretersAbstract, ModelAbstract):
def request(self:Self, def request(self:Self,
session:int|None, session:int|None,
conversation:str|None,
message:str, message:str,
callback:Optional[Callable[[int, AIResponseModel], None]] = None,
orders:str|list[str] = [], orders:str|list[str] = [],
custom_context:Optional[list[int]] = None callback:Optional[Callable[[int, AIResponseModel], None]] = None,
custom_context:Optional[list[int]] = None,
save_session:bool = True
) -> tuple[int|None, AIResponseModel]: ) -> tuple[int|None, AIResponseModel]:
response:Response response:Response
context:list[int] context:list[int]
options:dict[str, Any] = {} options:dict[str, Any] = {key : value for key, value in {
results:AIResponseModel = AIResponseModel() "num_predict" : self.maximum_response_tokens,
"num_ctx" : self.maximum_tokens_per_session,
"temperature" : self.temperature
}.items() if value is not None}
results:AIResponseModel = AIResponseModel(conversation)
if self.maximum_response_tokens is not None: session, context = custom_context or self.get_session(session, conversation)
options["num_predict"] = self.maximum_response_tokens
if self.maximum_tokens_per_session is not None:
options["num_ctx"] = self.maximum_tokens_per_session
session, context = custom_context or self.get_session(session)
orders = self.get_orders(orders) orders = self.get_orders(orders)
if custom_context: if custom_context:
@ -75,7 +75,7 @@ class OllamaDriver(AIInterpretersAbstract, ModelAbstract):
if chunk: if chunk:
results.update(Common.json_decode(chunk)) results.update(Common.json_decode(chunk))
if results.done: if results.done:
self.save_context(session, results.context) save_session and self.save_context(session, conversation, results.context)
break break
Common.execute(callback, session, results) Common.execute(callback, session, results)

View File

@ -3,6 +3,7 @@
from threading import Thread from threading import Thread
from typing import Any, Self, Sequence, Optional from typing import Any, Self, Sequence, Optional
from http.cookies import SimpleCookie
from Abstracts.WebSocketServersAbstract import WebSocketServersAbstract from Abstracts.WebSocketServersAbstract import WebSocketServersAbstract
from Abstracts.ModelAbstract import ModelAbstract from Abstracts.ModelAbstract import ModelAbstract
from websockets.sync.server import serve as server_serve from websockets.sync.server import serve as server_serve
@ -60,10 +61,14 @@ class WebSocketServerDriver(WebSocketServersAbstract, ModelAbstract):
def __handler(self:Self, client:WebSocketClient) -> None: def __handler(self:Self, client:WebSocketClient) -> None:
id:str = self.anp.unique_keys.get() id:str = self.anp.unique_keys.get()
cookies:SimpleCookie = SimpleCookie()
self.__clients[id] = client self.__clients[id] = client
self.on_new_client.execute(id) self.on_new_client.execute(id)
cookies.load(client.request.headers.get("Cookie", ''))
self.set_session(id, cookies.get("session").value)
self.anp.print("info", "web_socket_server_client_connected", { self.anp.print("info", "web_socket_server_client_connected", {
"id": id, "id": id,
"port": self.port, "port": self.port,
@ -92,6 +97,7 @@ class WebSocketServerDriver(WebSocketServersAbstract, ModelAbstract):
self.__clients[id].close() self.__clients[id].close()
except Exception as _: except Exception as _:
pass pass
self.remove_session(id)
del self.__clients[id] del self.__clients[id]
self.anp.unique_keys.remove(id) self.anp.unique_keys.remove(id)
self.on_close.execute(id) self.on_close.execute(id)
@ -106,6 +112,8 @@ class WebSocketServerDriver(WebSocketServersAbstract, ModelAbstract):
success:bool = True success:bool = True
id:str id:str
# print([ids, list(self.__clients.keys())])
for id in ( for id in (
list(self.__clients.keys()) if ids is None else list(self.__clients.keys()) if ids is None else
ids if Check.is_array(ids) else ids if Check.is_array(ids) else
@ -113,6 +121,7 @@ class WebSocketServerDriver(WebSocketServersAbstract, ModelAbstract):
if id in self.__clients: if id in self.__clients:
try: try:
self.__clients[id].send(self.format_data(controller, action, data, id, code)) self.__clients[id].send(self.format_data(controller, action, data, id, code))
# print(["WS", controller, action, data, ids, code, list(self.__clients.keys())])
except Exception as exception: except Exception as exception:
self.anp.exception(exception, "web_socket_server_client_send_exception", { self.anp.exception(exception, "web_socket_server_client_send_exception", {
"id": id, "id": id,

View File

@ -66,4 +66,7 @@ class AnPInterface(ABC):
message:str|Sequence[str], message:str|Sequence[str],
inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None,
i:int = 0 i:int = 0
) -> None:pass ) -> None:pass
@abstractmethod
def wait(self:Self, time:str|float) -> None:pass

View File

@ -27,8 +27,9 @@ class AIInterpretersManagerInterface(ABC):
key:str, key:str,
session:int|None, session:int|None,
message:str, message:str,
callback:Callable[[int, AIResponseModel], None], orders:list[str] = [],
orders:list[str] = [] callback:Optional[Callable[[int, AIResponseModel], None]] = None,
custom_context:list[int] = []
) -> int|None:pass ) -> int|None:pass
@abstractmethod @abstractmethod

View File

@ -3,8 +3,9 @@
from typing import Self, Optional, Any from typing import Self, Optional, Any
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from Models.SessionModel import SessionModel
class SessionsManagerInterfaces(ABC): class SessionsManagerInterface(ABC):
@abstractmethod @abstractmethod
def update(self:Self) -> None:pass def update(self:Self) -> None:pass
@ -12,6 +13,12 @@ class SessionsManagerInterfaces(ABC):
@abstractmethod @abstractmethod
def reset(self:Self) -> None:pass def reset(self:Self) -> None:pass
@abstractmethod
def create(self:Self) -> SessionModel:pass
@abstractmethod
def _get_instance(self:Self, id:str) -> SessionModel|None:pass
@abstractmethod @abstractmethod
def get(self:Self, id:str, key:str, default:Optional[Any] = None) -> Any|None:pass def get(self:Self, id:str, key:str, default:Optional[Any] = None) -> Any|None:pass

View File

@ -75,13 +75,14 @@ class AIInterpretersManager:
key:str, key:str,
session:int|None, session:int|None,
message:str, message:str,
callback:Callable[[int, AIResponseModel], None], orders:str|list[str] = [],
orders:list[str] = [] callback:Optional[Callable[[int, AIResponseModel], None]] = None,
custom_context:list[int] = []
) -> tuple[int|None, AIResponseModel|None]: ) -> tuple[int|None, AIResponseModel|None]:
response:AIResponseModel|None = None response:AIResponseModel|None = None
if key in self.__interpreters: if key in self.__interpreters:
session, response = self.__interpreters[key].request(session, message, callback, orders) session, response = self.__interpreters[key].request(session, message, orders, callback, custom_context)
return session, response return session, response

View File

@ -15,6 +15,7 @@ class PseudoLoRAsManager:
self.__maximum_cache:int = (1 << 20) * 100 self.__maximum_cache:int = (1 << 20) * 100
self.__memory_cached:int = 0 self.__memory_cached:int = 0
self.__cache:dict[int, PseudoLoRAModel] = {} self.__cache:dict[int, PseudoLoRAModel] = {}
self.__cache_i:int = 0
self.__loras:list[PseudoLoRAModel] = [] self.__loras:list[PseudoLoRAModel] = []
self.update() self.update()
@ -89,58 +90,62 @@ class PseudoLoRAsManager:
for lora in self.__loras: for lora in self.__loras:
lora.clean_cache() lora.clean_cache()
def get(self:Self, callback:Callable[[list[PseudoLoRAModel]], bool], keys:list[str] = []) -> list[tuple[str, str]]: def get(self:Self,
callback:Callable[[list[PseudoLoRAModel]], bool],
loras:Optional[list[PseudoLoRAModel]] = None
) -> list[tuple[str, str]]:
next:list[PseudoLoRAModel] = []
results:list[tuple[str, str]] = [] results:list[tuple[str, str]] = []
i:int i:int
ok:bool ok:bool
has_keys:bool = len(keys) > 0
for i, ok in enumerate(callback( if loras is None:
lora.title for lora in self.__loras if not has_keys or any( loras = self.__loras.copy()
key in lora.keys for key in keys
) loras_is:list[bool] = [(i, callback([lora.title])) for i, lora in enumerate(loras)]
)):
new_loras:list[PseudoLoRAModel] = []
for i, ok in enumerate(loras_is):
if not ok: if not ok:
continue continue
lora:PseudoLoRAModel = self.__loras[i] lora:PseudoLoRAModel = loras[i]
if lora.path is not None: if lora.path is not None:
results.append((lora.title, Common.load_file(lora.path, "r")))
data:str|None # data:str|None
if lora.cacheable: # if lora.cacheable:
if not lora.cache: # if not lora.cache:
lora.cache = Common.load_file(lora.path, "r") # lora.cache = Common.load_file(lora.path, "r")
lora.memory = len(lora.cache) # lora.memory = len(lora.cache)
self.__memory_cached += lora.memory # self.__memory_cached += lora.memory
if self.__cache[lora.i]: # if self.__cache[lora.i]:
del self.__cache[lora.i] # del self.__cache[lora.i]
lora.i += 1
if lora.i in self.__cache:
self.__cache[lora.i].append(lora)
else:
self.__cache[lora.i] = [lora]
if self.__memory_cached > self.__maximum_cache: # lora.i = self.__cache_i
# self.__cache_i += 1
# self.__cache[lora.i] = lora
i:int = min(self.__cache.keys()) # if self.__memory_cached > self.__maximum_cache:
for lora in self.__cache[i]: # i:int = min(self.__cache.keys())
lora.clean_cache()
del self.__cache[i]
if (data := lora.cache or Common.load_file(lora.path, "r")): # for lora in self.__cache[i]:
results.append((lora.title, lora.path)) # lora.clean_cache()
# del self.__cache[i]
# if (data := lora.cache or Common.load_file(lora.path, "r")):
# results.append((lora.title, lora.path))
else: else:
next.extend(lora.nested) new_loras.extend(lora.nested)
if len(next): if len(new_loras):
results.extend(callback(next)) results.extend(self.get(callback, new_loras))
return results return results

View File

@ -35,6 +35,9 @@ class RoutesManager:
route:RouteModel route:RouteModel
if request.session is None:
request.session = self.anp.sessions._get_instance(request.get("session")) or None
for route in self.__routes: for route in self.__routes:
if route.match(key, method, path, request): if route.match(key, method, path, request):
if not request.response: if not request.response:

View File

@ -3,7 +3,7 @@
from typing import Self, Optional, Any from typing import Self, Optional, Any
from threading import Thread from threading import Thread
from time import sleep, time as timestamp from time import time as timestamp
from Interfaces.Application.AnPInterface import AnPInterface from Interfaces.Application.AnPInterface import AnPInterface
from Models.SessionModel import SessionModel from Models.SessionModel import SessionModel
@ -39,21 +39,36 @@ class SessionsManager:
if time - session.get("date_last", time) > self.__timeout: if time - session.get("date_last", time) > self.__timeout:
self.remove(id) self.remove(id)
sleep(self.__clean_waiter) self.anp.wait(self.__clean_waiter)
def create(self:Self) -> SessionModel:
id:str = self.anp.unique_keys.get()
self.__sessions[id] = SessionModel(id)
return id
def _get_instance(self:Self, id:str) -> SessionModel|None:
if id in self.__sessions and not self.__sessions[id].removed:
return self.__sessions[id]
return None
def get(self:Self, id:str, key:str, default:Optional[Any] = None) -> Any|None: def get(self:Self, id:str, key:str, default:Optional[Any] = None) -> Any|None:
if id in self.__sessions: if id in self.__sessions and not self.__sessions[id].removed:
return self.__sessions[id].get(key, default) return self.__sessions[id].get(key, default)
return default return default
def set(self:Self, id:str, key:str, value:Any|None) -> bool: def set(self:Self, id:str, key:str, value:Any|None) -> bool:
if id in self.__sessions: if id in self.__sessions and not self.__sessions[id].removed:
self.__sessions[id].set(key, value) self.__sessions[id].set(key, value)
return True return True
return False return False
def remove(self:Self, id:str) -> bool: def remove(self:Self, id:str) -> bool:
if id in self.__sessions: if id in self.__sessions:
self.__sessions[id].remove()
del self.__sessions[id] del self.__sessions[id]
return True return True
return False return False

View File

@ -105,7 +105,7 @@ class WebSocketServersManager:
except Exception as exception: except Exception as exception:
self.anp.exception(exception, "web_socket_server_send_exception", {"name": name}) self.anp.exception(exception, "web_socket_server_send_exception", {"name": name})
def __receive(self:Self, web_socket:WebSocketServersAbstract, client:int, raw_data:str, name:str) -> None: def __receive(self:Self, web_socket:WebSocketServersAbstract, client:str, raw_data:str, name:str) -> None:
data:dict[str, Any|None] = Common.data_decode(raw_data) data:dict[str, Any|None] = Common.data_decode(raw_data)
@ -119,6 +119,7 @@ class WebSocketServersManager:
request.variables["web_socket"] = web_socket request.variables["web_socket"] = web_socket
request.variables["client_id"] = client request.variables["client_id"] = client
request.variables["web_socket_name"] = name request.variables["web_socket_name"] = name
request.session = self.anp.sessions._get_instance(web_socket.get_session(client))
self.anp.controllers.execute( self.anp.controllers.execute(
data["controller"], data["controller"],

View File

@ -6,7 +6,8 @@ from time import time as timestamp
class AIResponseModel: class AIResponseModel:
def __init__(self:Self) -> None: def __init__(self:Self, conversation:str) -> None:
self.conversation:str = conversation
self.start:float = timestamp() self.start:float = timestamp()
self.model:str = "" self.model:str = ""
self.response:str = "" self.response:str = ""

View File

@ -9,10 +9,11 @@ from Utils.Common import Common
class RequestModel: class RequestModel:
def __init__(self:Self) -> None: def __init__(self:Self, session:Optional[SessionModel] = None) -> None:
self.post_variables:dict[str, Any|None] = {} self.post_variables:dict[str, Any|None] = {}
self.get_variables:dict[str, Any|None] = {} self.get_variables:dict[str, Any|None] = {}
self.url_variables:dict[str, Any|None] = {} self.url_variables:dict[str, Any|None] = {}
self.cookies:dict[str, Any|None] = {}
self.variables:dict[str, Any|None] = {} self.variables:dict[str, Any|None] = {}
self.request_headers:dict[str, Any|None] = {} self.request_headers:dict[str, Any|None] = {}
self.method:str|None = None self.method:str|None = None
@ -23,15 +24,22 @@ class RequestModel:
self.response_headers:dict[str, Any|None] = {} self.response_headers:dict[str, Any|None] = {}
self.callback:Callable[[RequestModel, str|bytes|None], None]|None = None self.callback:Callable[[RequestModel, str|bytes|None], None]|None = None
self.data:Any|None = None self.data:Any|None = None
self.session:SessionModel|None = None self.session:SessionModel|None = session
def get(self:Self, key:str|Sequence[str], default:Optional[Any] = None) -> Any|None: def get(self:Self, key:str|Sequence[str], default:Optional[Any] = None) -> Any|None:
return self.session.get(key, Common.get_value(key, ( if self.session is not None:
self.url_variables, self.get_variables, self.post_variables, self.variables
), default)) results:Any|None = self.session.get(key)
if results is not None:
return results
return Common.get_value(key, (
self.cookies, self.url_variables, self.get_variables, self.post_variables, self.variables
), default)
def set_variables(self:Self, inputs:dict[str, Any|None], on:Optional[str] = None) -> None: def set_variables(self:Self, inputs:dict[str, Any|None], on:Optional[str] = None) -> None:
( (
self.cookies if on == "cookies" else
self.url_variables if on == "url" else self.url_variables if on == "url" else
self.get_variables if on == "get" else self.get_variables if on == "get" else
self.post_variables if on == "post" else self.post_variables if on == "post" else

View File

@ -11,6 +11,7 @@ class SessionModel:
self.date_from:float = timestamp() self.date_from:float = timestamp()
self.date_last:float = timestamp() self.date_last:float = timestamp()
self.variables:dict[str, Any|None] = {} self.variables:dict[str, Any|None] = {}
self.removed:bool = False
def set(self:Self, key:str, value:Any|None) -> None: def set(self:Self, key:str, value:Any|None) -> None:
self.date_last = timestamp() self.date_last = timestamp()
@ -18,4 +19,7 @@ class SessionModel:
def get(self:Self, key:str, default:Any|None = None) -> Any|None: def get(self:Self, key:str, default:Any|None = None) -> Any|None:
self.date_last = timestamp() self.date_last = timestamp()
return self.variables.get(key, default) return self.variables.get(key, default)
def remove(self:Self) -> None:
self.removed = True

View File

@ -13,4 +13,5 @@ class RE:
TO_REGULAR_EXPRESSION:REPattern = re_compile(r'[\(\)\{\}\/\\\.\-\+\*\^\$\?\|\!\<\>\r\n\t]') TO_REGULAR_EXPRESSION:REPattern = re_compile(r'[\(\)\{\}\/\\\.\-\+\*\^\$\?\|\!\<\>\r\n\t]')
ROUTE_KEY:REPattern = re_compile(r'\\\{([a-z_][a-z0-9_]*)\\\}', RE_IGNORECASE) ROUTE_KEY:REPattern = re_compile(r'\\\{([a-z_][a-z0-9_]*)\\\}', RE_IGNORECASE)
EXCEPTION:REPattern = re_compile(r'^\s*File "([^"]+)", line ([0-9]+), in ([^\n]+)(.*|[\r\n]*)*$') EXCEPTION:REPattern = re_compile(r'^\s*File "([^"]+)", line ([0-9]+), in ([^\n]+)(.*|[\r\n]*)*$')
NEW_LINE:REPattern = re_compile(r'\r\n|[\r\n]') NEW_LINE:REPattern = re_compile(r'\r\n|[\r\n]')
HTTP_VARIABLE:REPattern = re_compile(r'([^=&]+)=([^&]*)')