#wip: Building the projects builder.

This commit is contained in:
mbruzon 2026-06-13 09:50:59 +02:00
parent 20e877eac2
commit 711e347346
23 changed files with 285 additions and 37 deletions

View File

@ -97,26 +97,27 @@
"type" : "OllamaDriver", "type" : "OllamaDriver",
"url" : "http://localhost:11434/api/generate/", "url" : "http://localhost:11434/api/generate/",
"model" : "gemma3:1b", "model" : "gemma3:1b",
"pool" : "anp", "pool" : "anp",
"stream" : false,
"format" : { "format" : {
"type" : "array", "type": "array",
"items" : { "items": {
"type" : "string" "type": "boolean"
} }
}, },
"orders" : [ "stream" : false,
"En base al mensaje del usuario y los Tokens que tengas de la conversación, selecciona posibles títulos que tengan que ver con ello.", "think" : false,
"Devuelve una lista con los títulos seleccionados, sin ningún otro texto.", "temperature" : 0.0,
"Si el mensaje del usuario no tiene relación con ningún título, devuelve una lista vacía." "allow_contexts" : true
]
}, },
"anp_responses" : { "anp_responses" : {
"type" : "OllamaDriver", "type" : "OllamaDriver",
"url" : "http://localhost:11434/api/generate/", "url" : "http://localhost:11434/api/generate/",
"model" : "gemma4:e4b", "model" : "gemma4:e4b",
"stream" : true,
"pool" : "anp", "pool" : "anp",
"stream" : true "think" : false,
"temperature" : 0.7,
"allow_contexts" : true
} }
}, },
"AnP_AIInterpretersManager_end" : null, "AnP_AIInterpretersManager_end" : null,

View File

@ -0,0 +1,2 @@
- **end_print_types**: `array[string]`. Opcional. Por defecto `["UNKN", "EXCE", "ERRO"]`. Determina qué mensajes de consola tienen valor `end`, el cual es un valor para mostrar trazas, listas como los errores, etc.
- **root_projects_paths**: `array[string]`. Opcional. Por defecto `[]`.

View File

@ -2,6 +2,6 @@ AnP, de Attach & Play en inglés, es un Framework basado en Gestores y su consum
Está desarrollado en Python para el servidor; y JavaScript para el entorno cliente Web. Está desarrollado en Python para el servidor; y JavaScript para el entorno cliente Web.
Para crear un objecto AnP simplemente hemos de crearlo sin ningún argumento si queremos dejar todo por defecto o si queremos personalizar algo, podemos hacer uso del argumento `inputs`. Para crear un objecto AnP simplemente hemos de crearlo sin ningún argumento si queremos dejar todo por defecto o si queremos personalizar algo, podemos hacer uso del primer argumento llamado `inputs`, el cual tiene los siguientes parámetros:
> Para saber qué puede contener el argumento `inputs` mirar la configuración. [[!include /MD/PseudoLoRAs/AnP.inputs.md]]

View File

@ -182,7 +182,7 @@ export const AIChatComponent = (function(){
}; };
const set_status = (conversation, message, status) => { const set_status = (conversation, message, status) => {
console.log([conversation, message, status]); // console.log([conversation, message, status]);
const box = document.querySelector(".aichat [data-conversation='" + conversation + "']>[data-type=bot][data-id='" + message + "']"), const box = document.querySelector(".aichat [data-conversation='" + conversation + "']>[data-type=bot][data-id='" + message + "']"),
status_box = box.querySelector("[data-name=status]"), status_box = box.querySelector("[data-name=status]"),

View File

@ -0,0 +1,95 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Any, Optional, Self, Sequence
from abc import ABC, abstractmethod
from os.path import dirname as directory_name, abspath as absolute_path
from Interfaces.Application.AnPInterface import AnPInterface
from Utils.Common import Common
from Utils.Checks import Check
from Utils.Patterns import RE
class FilesAbstract(ABC):
def __init__(self:Self, anp:AnPInterface, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
self.anp:AnPInterface = anp
root_path:str = directory_name(absolute_path(__file__))
self._slash:str = Common.get_value("slash", inputs, "/" if "/" in root_path else "\\\\")
self._root_paths:list[str] = Common.get_value(
"root_paths",
inputs,
[""] + self.get_parents(root_path, 4)
)
def set_root_paths(self:Self, paths:str|Sequence[str]) -> None:
path:str
for path in Common.get_array(paths):
if path not in self._root_paths:
self._root_paths.append(path)
def set_slash(self:Self, slash:str) -> None:
self._slash = slash
def fix_path(self:Self, path:str) -> str:
return RE.SLASHES.sub(self._slash, path)
def get_parent(self:Self, path:str) -> str:
return RE.PARENT_PATH.sub(r'\1', self.fix_path(path))
def get_parents(self:Self, path:str, levels:int = 1) -> list[str]:
parents:list[str] = [self.fix_path(path)]
while len(parents[-1]) > 2 and len(parents) < levels:
parents.append(RE.PARENT_PATH.sub(r'\1', parents[-1]))
return parents
@abstractmethod
def get_absolute_path(self:Self, path:str) -> str|None:pass
@abstractmethod
def exists(self:Self, path:str) -> bool:pass
@abstractmethod
def is_file(self:Self, path:str) -> bool:pass
@abstractmethod
def is_directory(self:Self, path:str) -> bool:pass
@abstractmethod
def load(self:Self, path:str, mode:str = "r") -> str|bytes|None:pass
@abstractmethod
def make_directory(self:Self, path:str) -> bool:pass
@abstractmethod
def save(self:Self, path:str, content:str|bytes) -> bool:pass
@abstractmethod
def delete(self:Self, path:str) -> bool:pass
def load_json(self:Self, inputs:Any|None, only_dictionaries:bool = True) -> list[dict[str, Any|None]|Sequence[Any|None]]:
results:list[dict[str, Any|None]|Sequence[Any|None]] = []
if Check.is_dictionary(inputs):
results.append(inputs)
elif Check.is_array(inputs):
if only_dictionaries:
for item in inputs:
results.extend(self.load_json(item, only_dictionaries))
else:
results.append(inputs)
elif Check.is_string(inputs):
json:dict[str, Any|None]|Sequence[Any|None]|None = Common.json_decode(inputs)
if json is None:
results.extend(self.load_json(self.load(inputs), only_dictionaries))
else:
results.extend(self.load_json(json, only_dictionaries))
return results

View File

@ -6,6 +6,8 @@ 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 time import time as timestamp, sleep
from Abstracts.FilesAbstract import FilesAbstract
from Drivers.FilesDriver import FilesDriver
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
@ -38,6 +40,7 @@ class AnP:
def __init__(self:Self, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None: def __init__(self:Self, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
self.__working:bool = True self.__working:bool = True
self.files:FilesAbstract = FilesDriver(self)
self.settings:SettingsManager = SettingsManager(self, inputs) self.settings:SettingsManager = SettingsManager(self, inputs)
self.i18n:I18NManager = I18NManager(self) self.i18n:I18NManager = I18NManager(self)
self.print_types:PrintTypesManager = PrintTypesManager(self) self.print_types:PrintTypesManager = PrintTypesManager(self)

View File

@ -0,0 +1,97 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Self
from os.path import isfile as is_file, isdir as is_directory, exists as path_exists
from os import unlink as delete_path, mkdir as make_directory
from Abstracts.FilesAbstract import FilesAbstract
from Abstracts.ModelAbstract import ModelAbstract
from Utils.Checks import Check
class FilesDriver(FilesAbstract, ModelAbstract):
def get_absolute_path(self:Self, path:str) -> str|None:
root_path:str
for root_path in self._root_paths:
absolute_path:str = self.fix_path((root_path + self._slash + path) if root_path else path)
if path_exists(absolute_path):
return absolute_path
return None
def exists(self:Self, path:str) -> bool:
return self.get_absolute_path(path) is not None
def is_file(self:Self, path:str) -> bool:
absolute_path:str|None = self.get_absolute_path(path)
return is_file(absolute_path) if absolute_path else False
def is_directory(self:Self, path:str) -> bool:
absolute_path:str|None = self.get_absolute_path(path)
return is_directory(absolute_path) if absolute_path else False
def load(self:Self, path:str, mode:str = "r") -> str|bytes|None:
absolute_path:str|None = self.get_absolute_path(path)
if absolute_path:
try:
with open(absolute_path, mode) as file:
return file.read()
except Exception as exception:
self.anp.exception(exception, "anp_files_driver_load_exception", {
"path" : path,
"absolute_path" : absolute_path,
"mode" : mode
})
return None
def make_directory(self:Self, path:str) -> bool:
try:
make_directory(path)
return True
except Exception as exception:
self.anp.exception(exception, "anp_files_driver_make_directory_exception", {
"path" : path
})
return False
def save(self:Self, path:str, content:str|bytes) -> bool:
directory:str = self.get_parent(path)
if self.exists(directory) or self.make_directory(directory):
try:
with open(self.fix_path(path), (
"wb" if Check.is_binary(content) else
"w")) as file:
file.write(content)
return True
except Exception as exception:
self.anp.exception(exception, "anp_files_driver_save_exception", {
"path" : path,
"mode" : "wb" if Check.is_binary(content) else "w"
})
return False
def delete(self:Self, path:str) -> bool:
absolute_path:str|None = self.get_absolute_path(path)
if absolute_path:
try:
delete_path(absolute_path)
return True
except Exception as exception:
self.anp.exception(exception, "anp_files_driver_delete_exception", {
"path" : path,
"absolute_path" : absolute_path
})
return False

View File

@ -0,0 +1,46 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Self, Sequence
from abc import ABC, abstractmethod
class FilesAbstractInterface(ABC):
@abstractmethod
def get_parents(path:str, levels:int = 1) -> list[str]:pass
@abstractmethod
def set_root_paths(self:Self, paths:str|Sequence[str]) -> None:pass
@abstractmethod
def set_slash(self:Self, slash:str) -> None:pass
@abstractmethod
def fix_path(self:Self, path:str) -> str:pass
@abstractmethod
def get_parent(self:Self, path:str) -> str:pass
@abstractmethod
def get_absolute_path(self:Self, path:str) -> str|None:pass
@abstractmethod
def exists(self:Self, path:str) -> bool:pass
@abstractmethod
def is_file(self:Self, path:str) -> bool:pass
@abstractmethod
def is_directory(self:Self, path:str) -> bool:pass
@abstractmethod
def load(self:Self, path:str, mode:str = "r") -> str|bytes|None:pass
@abstractmethod
def make_directory(self:Self, path:str) -> bool:pass
@abstractmethod
def save(self:Self, path:str, content:str|bytes) -> bool:pass
@abstractmethod
def delete(self:Self, path:str) -> bool:pass

View File

@ -7,6 +7,7 @@ from Interfaces.Managers.SettingsManagerInterface import SettingsManagerInterfac
from Interfaces.Managers.I18NManagerInterface import I18NManagerInterface from Interfaces.Managers.I18NManagerInterface import I18NManagerInterface
from Interfaces.Managers.PrintTypesManagerInterface import PrintTypesManagerInterface from Interfaces.Managers.PrintTypesManagerInterface import PrintTypesManagerInterface
from Interfaces.Managers.TerminalManagerInterface import TerminalManagerInterface from Interfaces.Managers.TerminalManagerInterface import TerminalManagerInterface
from Interfaces.Abstracts.FilesAbstractInterface import FilesAbstractInterface
from Interfaces.Managers.ModelsManagerInterface import ModelsManagerInterface from Interfaces.Managers.ModelsManagerInterface import ModelsManagerInterface
from Interfaces.Managers.UniqueKeysManagerInterface import UniqueKeysManagerInterface from Interfaces.Managers.UniqueKeysManagerInterface import UniqueKeysManagerInterface
from Interfaces.Managers.QueusManagerInterface import QueusManagerInterface from Interfaces.Managers.QueusManagerInterface import QueusManagerInterface
@ -23,6 +24,7 @@ from Interfaces.Managers.AIInterpretersManagerInterface import AIInterpretersMan
class AnPInterface(ABC): class AnPInterface(ABC):
def __init__(self:Self, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None: def __init__(self:Self, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
self.files:FilesAbstractInterface = None
self.settings:SettingsManagerInterface = None self.settings:SettingsManagerInterface = None
self.i18n:I18NManagerInterface = None self.i18n:I18NManagerInterface = None
self.print_types:PrintTypesManagerInterface = None self.print_types:PrintTypesManagerInterface = None

View File

@ -38,7 +38,7 @@ class AIInterpretersManager:
subinputs:dict[str, Any|None] subinputs:dict[str, Any|None]
for subinputs in Common.load_json(inputs, True): for subinputs in self.anp.files.load_json(inputs, True):
key:str key:str
value:Any|None value:Any|None

View File

@ -34,7 +34,7 @@ class ControllersManager:
subinputs:dict[str, Any|None] subinputs:dict[str, Any|None]
for subinputs in Common.load_json(inputs, True): for subinputs in self.anp.files.load_json(inputs, True):
for key, controller in subinputs.items(): for key, controller in subinputs.items():
if Common.is_mark_key(key) and controller is None: if Common.is_mark_key(key) and controller is None:
continue continue

View File

@ -33,7 +33,7 @@ class DispatchesManager:
subinputs:dict[str, Any|None] subinputs:dict[str, Any|None]
for subinputs in Common.load_json(inputs, True): for subinputs in self.anp.files.load_json(inputs, True):
for key, dispatch in subinputs.items(): for key, dispatch in subinputs.items():
if Common.is_mark_key(key) and dispatch is None: if Common.is_mark_key(key) and dispatch is None:
continue continue

View File

@ -40,7 +40,7 @@ class HTTPServersManager:
subinputs:dict[str, Any|None] subinputs:dict[str, Any|None]
for subinputs in Common.load_json(inputs, True): for subinputs in self.anp.files.load_json(inputs, True):
key:str key:str
value:Any|None value:Any|None

View File

@ -80,7 +80,7 @@ class I18NManager:
subinputs:dict[str, Any|None] subinputs:dict[str, Any|None]
for subinputs in Common.load_json(inputs, True): for subinputs in self.anp.files.load_json(inputs, True):
language:str language:str
sentences:dict[str, str|list[str]] sentences:dict[str, str|list[str]]

View File

@ -46,7 +46,7 @@ class ModelsManager:
subinputs:dict[str, ModelAbstract] subinputs:dict[str, ModelAbstract]
for subinputs in Common.load_json(inputs, True): for subinputs in self.anp.files.load_json(inputs, True):
for key, model in subinputs.items(): for key, model in subinputs.items():
if Common.is_mark_key(key) and model is None: if Common.is_mark_key(key) and model is None:
continue continue

View File

@ -75,4 +75,4 @@ class PrintTypesManager:
subinputs:Any|None subinputs:Any|None
for subinputs in item: for subinputs in item:
self.add(Common.load_json(subinputs)) self.add(self.anp.files.load_json(subinputs))

View File

@ -4,7 +4,6 @@
from typing import Self, Any, Sequence, Callable, Optional from typing import Self, Any, Sequence, Callable, Optional
from Interfaces.Application.AnPInterface import AnPInterface from Interfaces.Application.AnPInterface import AnPInterface
from Models.PseudoLoRAModel import PseudoLoRAModel from Models.PseudoLoRAModel import PseudoLoRAModel
from Utils.Common import Common
from Utils.Checks import Check from Utils.Checks import Check
class PseudoLoRAsManager: class PseudoLoRAsManager:
@ -35,7 +34,7 @@ class PseudoLoRAsManager:
def __try_load_file(self:Self, path:str) -> bool: def __try_load_file(self:Self, path:str) -> bool:
data:list[dict[str, Any|None]|Sequence[Any|None]] = Common.load_json(path) data:list[dict[str, Any|None]|Sequence[Any|None]] = self.anp.files.load_json(path)
if len(data): if len(data):
self.add(data) self.add(data)
@ -49,7 +48,7 @@ class PseudoLoRAsManager:
subinputs:dict[str, Any|None]|Sequence[Any|None] subinputs:dict[str, Any|None]|Sequence[Any|None]
for subinputs in Common.load_json(inputs, False): for subinputs in self.anp.files.load_json(inputs, False):
if Check.is_string(subinputs): if Check.is_string(subinputs):
self.__try_load_file(subinputs) self.__try_load_file(subinputs)
elif Check.is_array(subinputs): elif Check.is_array(subinputs):
@ -113,14 +112,14 @@ class PseudoLoRAsManager:
lora:PseudoLoRAModel = 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"))) results.append((lora.title, self.anp.files.load(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 = self.anp.files.load(lora.path, "r")
# lora.memory = len(lora.cache) # lora.memory = len(lora.cache)
# self.__memory_cached += lora.memory # self.__memory_cached += lora.memory
@ -139,7 +138,7 @@ class PseudoLoRAsManager:
# lora.clean_cache() # lora.clean_cache()
# del self.__cache[i] # del self.__cache[i]
# if (data := lora.cache or Common.load_file(lora.path, "r")): # if (data := lora.cache or self.anp.files.load(lora.path, "r")):
# results.append((lora.title, lora.path)) # results.append((lora.title, lora.path))
else: else:

View File

@ -31,7 +31,7 @@ class QueuesManager:
subinputs:dict[str, Any|None] subinputs:dict[str, Any|None]
for subinputs in Common.load_json(inputs, True): for subinputs in self.anp.files.load_json(inputs, True):
key:str key:str
queue:Any|None queue:Any|None

View File

@ -49,10 +49,10 @@ class RoutesManager:
for index in [""] + self.anp.indexes.get(): for index in [""] + self.anp.indexes.get():
path_indexed:str|None = Common.get_absolute_path(route.path + "/" + path + ("/" + index if index else "")) path_indexed:str|None = self.anp.files.get_absolute_path(route.path + "/" + path + ("/" + index if index else ""))
if path_indexed is not None and Common.is_file(path_indexed): if path_indexed is not None and self.anp.files.is_file(path_indexed):
request.set_response(Common.load_file(path_indexed, "rb")) request.set_response(self.anp.files.load(path_indexed, "rb"))
request.response_mime = Common.get_mime_from_path(path_indexed) request.response_mime = Common.get_mime_from_path(path_indexed)
return return
request.set_response({ request.set_response({
@ -101,7 +101,7 @@ class RoutesManager:
subinputs:Any|None subinputs:Any|None
for subinputs in Common.load_json(inputs, False): for subinputs in self.anp.files.load_json(inputs, False):
if isinstance(subinputs, RouteModel): if isinstance(subinputs, RouteModel):
self.__add_new_route(subinputs, overwrite) self.__add_new_route(subinputs, overwrite)
elif ( elif (

View File

@ -52,7 +52,7 @@ class SettingsManager:
subinputs:dict[str, Any|None] subinputs:dict[str, Any|None]
for subinputs in Common.load_json(inputs, True): for subinputs in self.anp.files.load_json(inputs, True):
key:str key:str
value:Any|None value:Any|None
@ -67,7 +67,7 @@ class SettingsManager:
subinputs:dict[str, Any|None] subinputs:dict[str, Any|None]
for subinputs in Common.load_json(inputs, True): for subinputs in self.anp.files.load_json(inputs, True):
key:str key:str
value:Any|None value:Any|None

View File

@ -58,7 +58,7 @@ class WebSocketServersManager:
subinputs:dict[str, Any|None] subinputs:dict[str, Any|None]
for subinputs in Common.load_json(inputs, True): for subinputs in self.anp.files.load_json(inputs, True):
key:str key:str
value:Any|None value:Any|None

View File

@ -14,4 +14,5 @@ class RE:
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'([^=&]+)=([^&]*)') HTTP_VARIABLE:REPattern = re_compile(r'([^=&]+)=([^&]*)')
PARENT_PATH:REPattern = re_compile(r'^(.*)[\/\\][^\/\\]+[\/\\]?$')

View File

@ -7,13 +7,15 @@ from Controllers.AIController import AIController
from Drivers.WebSocketServerDriver import WebSocketServerDriver from Drivers.WebSocketServerDriver import WebSocketServerDriver
from Drivers.HTTPDriver import HTTPDriver from Drivers.HTTPDriver import HTTPDriver
from Drivers.OllamaDriver import OllamaDriver from Drivers.OllamaDriver import OllamaDriver
from Drivers.FilesDriver import FilesDriver
inputs:dict[str, dict[str, Any|None]] = { inputs:dict[str, dict[str, Any|None]] = {
"default_models" : { "default_models" : {
"AIController" : AIController, "AIController" : AIController,
"WebSocketServerDriver" : WebSocketServerDriver, "WebSocketServerDriver" : WebSocketServerDriver,
"HTTPDriver" : HTTPDriver, "HTTPDriver" : HTTPDriver,
"OllamaDriver" : OllamaDriver "OllamaDriver" : OllamaDriver,
"FilesDriver" : FilesDriver
} }
} }