#wip: AI client working.
This commit is contained in:
parent
f1371e7389
commit
c73884ef35
@ -68,7 +68,7 @@
|
||||
"AnP_RoutesManager_end" : null,
|
||||
|
||||
"AnP_WebSocketsServersManager_start" : null,
|
||||
"default_web_sockets_servers2" : {
|
||||
"default_web_sockets_servers" : {
|
||||
"anp" : {
|
||||
"type" : "WebSocketServerDriver",
|
||||
"host" : "",
|
||||
|
||||
@ -31,10 +31,10 @@
|
||||
"AnP_RoutesManager_end" : null,
|
||||
|
||||
"AnP_WebSocketServerDriver_start" : null,
|
||||
"web_socket_server_client_close_exception" : "El Web Socket Servidor '{host}:{port}' ha cerrado la conexión con el cliente '{key}'.",
|
||||
"web_socket_server_client_connected" : "El cliente '{key}' se ha conectado al Web Socket Servidor '{host}:{port}' desde '{client_host}:{client_port}'.",
|
||||
"web_socket_server_client_exception" : "Excepción al intentar procesar un mensaje de recepción del cliente '{key}' en el Web Socket Servidor '{host}:{port}'.",
|
||||
"web_socket_server_client_disconnected" : "El cliente '{key}' se ha desconectado del Web Socket Servidor '{host}:{port}'.",
|
||||
"web_socket_server_client_close_exception" : "El Web Socket Servidor '{host}:{port}' ha cerrado la conexión con el cliente '{id}'.",
|
||||
"web_socket_server_client_connected" : "El cliente '{id}' se ha conectado al Web Socket Servidor '{host}:{port}' desde '{client_host}:{client_port}'.",
|
||||
"web_socket_server_client_exception" : "Excepción al intentar procesar un mensaje de recepción del cliente '{id}' en el Web Socket Servidor '{host}:{port}'.",
|
||||
"web_socket_server_client_disconnected" : "El cliente '{id}' se ha desconectado del Web Socket Servidor '{host}:{port}'.",
|
||||
"AnP_WebSocketServerDriver_end" : null,
|
||||
|
||||
"AnP_TitlesManager_start" : null,
|
||||
|
||||
146
Public/ecma/Abstracts/WebSocketsClientsAbstract.ecma.js
Normal file
146
Public/ecma/Abstracts/WebSocketsClientsAbstract.ecma.js
Normal file
@ -0,0 +1,146 @@
|
||||
"use strict";
|
||||
|
||||
import {Event} from "../Application/Event.ecma.js";
|
||||
import {Check} from "../Utils/Checks.ecma.js";
|
||||
import {Common} from "../Utils/Common.ecma.js";
|
||||
|
||||
/**
|
||||
* @typedef {import("../Application/AnP.ecma.js").AnP} AnP
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class WebSocketsClientsAbstract
|
||||
* @constructor
|
||||
* @param {!AnP} anp
|
||||
* @param {!string} key
|
||||
* @param {!(Object.<string, any|null>|Array.<any|null>)} inputs
|
||||
* @return {void}
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
export const WebSocketsClientsAbstract = (function(){
|
||||
|
||||
/**
|
||||
* @constructs WebSocketsClientsAbstract
|
||||
* @param {!AnP} anp
|
||||
* @param {!string} key
|
||||
* @param {!(Object.<string, any|null>|Array.<any|null>)} inputs
|
||||
* @return {void}
|
||||
* @access private
|
||||
* @static
|
||||
*/
|
||||
const WebSocketsClientsAbstract = function(anp, key, inputs){
|
||||
|
||||
/** @type {WebSocketsClientsAbstract|Object.<string, any|null>} */
|
||||
let self = this;
|
||||
|
||||
/** @type {string|null} */
|
||||
this.key = null;
|
||||
/** @type {string|null} */
|
||||
this.url = null;
|
||||
/** @type {string|null} */
|
||||
this.id = null;
|
||||
/** @type {Event} */
|
||||
this.on_open = new Event();
|
||||
/** @type {Event} */
|
||||
this.on_message = new Event();
|
||||
/** @type {Event} */
|
||||
this.on_close = new Event();
|
||||
/** @type {Event} */
|
||||
this.on_error = new Event();
|
||||
|
||||
/**
|
||||
* @param {!Object.<string, any|null>} item
|
||||
* @returns {void}
|
||||
* @access public
|
||||
*/
|
||||
this.change_self = item => {
|
||||
self = item;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {void}
|
||||
* @access private
|
||||
*/
|
||||
const constructor = () => {
|
||||
|
||||
self.key = key;
|
||||
self.url = Common.get_value("url", (
|
||||
Check.is_string(inputs) ? inputs = {url : inputs} :
|
||||
inputs), "");
|
||||
|
||||
self.on_open.add(on_opened);
|
||||
self.on_message.add(on_received);
|
||||
self.on_close.add(on_closed);
|
||||
self.on_error.add(on_errored);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
* @access private
|
||||
*/
|
||||
const on_opened = () => {};
|
||||
|
||||
/**
|
||||
* @param {!string} inputs
|
||||
* @returns {void}
|
||||
* @access private
|
||||
*/
|
||||
const on_received = inputs => {
|
||||
|
||||
/** @type {Object.<string, any|null>} */
|
||||
const data = Common.data_decode(inputs);
|
||||
|
||||
switch(data.controller + "." + data.action){
|
||||
case "web_socket_client.set_id":
|
||||
self.id = data.data;
|
||||
console.log(self.id);
|
||||
break;
|
||||
default:
|
||||
anp.controllers.execute(data.controller, data.action, data, data.code);
|
||||
break;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!string} controller
|
||||
* @param {!string} action
|
||||
* @param {?any} [data = null]
|
||||
* @param {!number} [code = 200]
|
||||
* @return {void}
|
||||
* @access public
|
||||
*/
|
||||
this.send = (controller, action, data, code = 200) => {};
|
||||
|
||||
/**
|
||||
* @return {void}
|
||||
* @access public
|
||||
*/
|
||||
this.close = () => {};
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
* @access private
|
||||
*/
|
||||
const on_closed = () => {
|
||||
self.id = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!string} error
|
||||
* @returns {void}
|
||||
* @access private
|
||||
*/
|
||||
const on_errored = error => {
|
||||
console.error(error);
|
||||
self.close();
|
||||
};
|
||||
|
||||
constructor();
|
||||
|
||||
};
|
||||
|
||||
return WebSocketsClientsAbstract;
|
||||
})();
|
||||
@ -8,6 +8,7 @@ import {ThreadsManager} from "../Managers/ThreadsManager.ecma.js";
|
||||
import {UniqueKeysManager} from "../Managers/UniqueKeysManager.ecma.js";
|
||||
import {SessionsManager} from "../Managers/SessionsManager.ecma.js";
|
||||
import {ModelsManager} from "../Managers/ModelsManager.ecma.js";
|
||||
import {ControllersManager} from "../Managers/ControllersManager.ecma.js";
|
||||
import {ViewsManager} from "../Managers/ViewsManager.ecma.js";
|
||||
import {RoutesManager} from "../Managers/RoutesManager.ecma.js";
|
||||
import {WebSocketsClientsManager} from "../Managers/WebSocketsClientsManager.ecma.js";
|
||||
@ -70,6 +71,8 @@ export const AnP = (function(){
|
||||
this.models = new ModelsManager(self);
|
||||
/** @type {Components} */
|
||||
this.components = new Components(self);
|
||||
/** @type {ControllersManager} */
|
||||
this.controllers = new ControllersManager(self);
|
||||
/** @type {ViewsManager} */
|
||||
this.views = new ViewsManager(self);
|
||||
/** @type {RoutesManager} */
|
||||
@ -94,7 +97,7 @@ export const AnP = (function(){
|
||||
*/
|
||||
this.update = (callback = null) => {
|
||||
Common.execute_array([
|
||||
"files", "settings", "print_types", "i18n", "threads", "models", "views", "routes"
|
||||
"files", "settings", "print_types", "i18n", "threads", "models", "controllers", "views", "routes", "web_sockets_clients"
|
||||
], (key, next) => {
|
||||
self[key].update(next);
|
||||
}, callback, true);
|
||||
@ -107,7 +110,7 @@ export const AnP = (function(){
|
||||
*/
|
||||
this.reset = (callback = null) => {
|
||||
Common.execute_array([
|
||||
"files", "settings", "print_types", "i18n", "threads", "models", "views", "routes"
|
||||
"files", "settings", "print_types", "i18n", "threads", "models", "controllers", "views", "routes", "web_sockets_clients"
|
||||
], (key, next) => {
|
||||
self[key].reset(next);
|
||||
}, callback, true);
|
||||
@ -161,7 +164,11 @@ export const AnP = (function(){
|
||||
};
|
||||
started = false;
|
||||
|
||||
end(true);
|
||||
Common.execute_array(["web_sockets_clients"], (key, next) => {
|
||||
self[key].close(next);
|
||||
}, () => {
|
||||
end(true);
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
@ -47,6 +47,8 @@ export const Components = (function(){
|
||||
|
||||
/** @type {Components} */
|
||||
const self = this;
|
||||
/** @type {number|null} */
|
||||
let thread = null;
|
||||
|
||||
/** @type {BaseComponent} */
|
||||
this.base = new BaseComponent(anp);
|
||||
@ -71,7 +73,32 @@ export const Components = (function(){
|
||||
* @returns {void}
|
||||
* @access private
|
||||
*/
|
||||
const constructor = () => {};
|
||||
const constructor = () => {
|
||||
|
||||
thread = anp.threads.add(thread_method, {
|
||||
autostart : true,
|
||||
bucle : true,
|
||||
timer : 100
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
* @access private
|
||||
*/
|
||||
const thread_method = () => {
|
||||
document.querySelectorAll(".anp .input .field-information[data-i18n=length]").forEach(item => {
|
||||
|
||||
/** @type {HTMLSpanElement} */
|
||||
const field = item.querySelector(".value"),
|
||||
/** @type {HTMLInputElement|HTMLTextAreaElement} */
|
||||
input = item.parentNode.parentNode.querySelector("input,textarea");
|
||||
|
||||
field.textContent != input.value.length && (field.textContent = input.value.length);
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!(string|Array.<string>)} keys
|
||||
@ -117,6 +144,7 @@ export const Components = (function(){
|
||||
},
|
||||
on_error : (item, event) => {
|
||||
|
||||
/** @type {number} */
|
||||
const i = Number(item.parentNode.getAttribute("data_i")) + 1;
|
||||
|
||||
item.setAttribute("src", Common.json_decode(Common.base64_decode(item.parentNode.getAttribute("data-data")))[i]);
|
||||
@ -129,21 +157,45 @@ export const Components = (function(){
|
||||
]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!string} i18n
|
||||
* @param {!string} [tag = "span"]
|
||||
* @param {?(Object.<string, any|null>|Array.<any|null>)} [variables = null]
|
||||
* @returns {Array.<any|null>}
|
||||
* @access public
|
||||
*/
|
||||
this.i18n = (i18n, tag = "span", variables = null) => [tag, {
|
||||
data_i18n : i18n,
|
||||
...(variables ? {data_i18n_variables : Common.data_encode(variables)} : {})
|
||||
}, anp.i18n.get(i18n, variables)];
|
||||
|
||||
/**
|
||||
* @param {!string} icon
|
||||
* @param {!string} [tag = "span"]
|
||||
* @returns {Array.<any|null>}
|
||||
* @access public
|
||||
*/
|
||||
this.icon = (icon, tag = "span") => [tag, {data_icon : icon}];
|
||||
|
||||
/**
|
||||
* @param {!string} name
|
||||
* @param {!(string|event_callback)} action
|
||||
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
|
||||
* @returns {Array.<any|null>}
|
||||
* @access public
|
||||
*/
|
||||
this.button = (name, action, inputs = null) => {
|
||||
|
||||
/** @type {string} */
|
||||
const text = anp.i18n.get(name, (
|
||||
Check.is_bool(inputs) ? {toogled : inputs} :
|
||||
Check.is_string(inputs) ? {type : inputs} :
|
||||
inputs)),
|
||||
/** @type {boolean} */
|
||||
has_action = Check.is_function(action),
|
||||
/** @type {string} */
|
||||
type = Check.is_string(action) ? action : Common.get_value("type", inputs, "button"),
|
||||
/** @type {boolean|null} */
|
||||
toggled = Common.get_value("toggled", inputs, null);
|
||||
|
||||
has_action || (action = null);
|
||||
@ -169,6 +221,12 @@ export const Components = (function(){
|
||||
]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!string} name
|
||||
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
|
||||
* @returns {Array.<any|null>}
|
||||
* @access public
|
||||
*/
|
||||
this.checkbox = (name, inputs = null) => Label({
|
||||
class : "checkbox",
|
||||
data_i18n : name,
|
||||
@ -188,8 +246,15 @@ export const Components = (function(){
|
||||
self.i18n(name)
|
||||
]);
|
||||
|
||||
/**
|
||||
* @param {!string} name
|
||||
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
|
||||
* @returns {Array.<any|null>}
|
||||
* @access public
|
||||
*/
|
||||
this.radio = (name, inputs = null) => {
|
||||
|
||||
/** @type {string|null} */
|
||||
const set = Common.get_value("set", inputs);
|
||||
|
||||
return Label({
|
||||
@ -213,10 +278,36 @@ export const Components = (function(){
|
||||
]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!string} name
|
||||
* @param {?any} value
|
||||
* @returns {Array.<any|null>}
|
||||
* @access public
|
||||
*/
|
||||
const field_information = (name, value) => Span({
|
||||
class : "field-information",
|
||||
data_i18n : name,
|
||||
data_i18n_without : true,
|
||||
title : anp.i18n.get(name),
|
||||
data_value : "" + value
|
||||
}, [
|
||||
self.i18n(name),
|
||||
Span({class : "value"}, "" + value)
|
||||
]);
|
||||
|
||||
/**
|
||||
* @param {!string} name
|
||||
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
|
||||
* @returns {Array.<any|null>}
|
||||
* @access public
|
||||
*/
|
||||
this.text = (name, inputs = null) => {
|
||||
|
||||
/** @type {string} */
|
||||
const text = anp.i18n.get(name),
|
||||
/** @type {number|null} */
|
||||
minimum_length = Common.get_value("minimum_length", inputs, null),
|
||||
/** @type {number|null} */
|
||||
maximum_length = Common.get_value("maximum_length", inputs, null);
|
||||
|
||||
return Span({
|
||||
@ -249,17 +340,28 @@ export const Components = (function(){
|
||||
"value"
|
||||
], inputs)
|
||||
}),
|
||||
Span({class : "minimum", data_minimum : minimum_length}, "" + minimum_length),
|
||||
Span({class : "length"}, "" + Common.get_value("value", inputs, "").length),
|
||||
Span({class : "maximum", data_maximum : maximum_length}, "" + maximum_length)
|
||||
Span({class : "information"}, [
|
||||
field_information("minimum", minimum_length),
|
||||
field_information("length", Common.get_value("value", inputs, "").length),
|
||||
field_information("maximum", maximum_length),
|
||||
])
|
||||
]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!string} name
|
||||
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
|
||||
* @returns {Array.<any|null>}
|
||||
* @access public
|
||||
*/
|
||||
this.password = (name, inputs = null) => {
|
||||
|
||||
/** @type {string} */
|
||||
const text = anp.i18n.get(name),
|
||||
minimum_length = Common.get_value("minimum_length", inputs, 0),
|
||||
maximum_length = Common.get_value("maximum_length", inputs, 0);
|
||||
/** @type {number|null} */
|
||||
minimum_length = Common.get_value("minimum_length", inputs, null),
|
||||
/** @type {number|null} */
|
||||
maximum_length = Common.get_value("maximum_length", inputs, null);
|
||||
|
||||
return Span({
|
||||
data_i18n : name,
|
||||
@ -279,14 +381,23 @@ export const Components = (function(){
|
||||
"value"
|
||||
], inputs)
|
||||
}),
|
||||
Span({class : "minimum", data_minimum : minimum_length}, "" + minimum_length),
|
||||
Span({class : "length"}, "" + Common.get_value("value", inputs, "").length),
|
||||
Span({class : "maximum", data_maximum : maximum_length}, "" + maximum_length)
|
||||
Span({class : "information"}, [
|
||||
field_information("minimum", minimum_length),
|
||||
field_information("length", Common.get_value("value", inputs, "").length),
|
||||
field_information("maximum", maximum_length),
|
||||
])
|
||||
]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!string} name
|
||||
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
|
||||
* @returns {Array.<any|null>}
|
||||
* @access public
|
||||
*/
|
||||
this.email = (name, inputs = null) => {
|
||||
|
||||
/** @type {string} */
|
||||
const text = anp.i18n.get(name);
|
||||
|
||||
return Span({
|
||||
@ -305,12 +416,22 @@ export const Components = (function(){
|
||||
]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!string} name
|
||||
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
|
||||
* @returns {Array.<any|null>}
|
||||
* @access public
|
||||
*/
|
||||
this.number = (name, inputs = null) => {
|
||||
|
||||
/** @type {string} */
|
||||
const text = anp.i18n.get(name),
|
||||
minimum = Common.get_value("minimum", inputs, 0),
|
||||
maximum = Common.get_value("maximum", inputs, 0),
|
||||
value = Common.get_value("value", inputs, 0);
|
||||
/** @type {number|null} */
|
||||
minimum = Common.get_value("minimum", inputs, null),
|
||||
/** @type {number|null} */
|
||||
maximum = Common.get_value("maximum", inputs, null),
|
||||
/** @type {number|null} */
|
||||
value = Common.get_value("value", inputs, null);
|
||||
|
||||
return Span({
|
||||
data_i18n : name,
|
||||
@ -331,9 +452,11 @@ export const Components = (function(){
|
||||
"value"
|
||||
], inputs)
|
||||
}),
|
||||
Span({class : "minimum", data_minimum : minimum}, "" + minimum),
|
||||
Span({class : "value"}, "" + value),
|
||||
Span({class : "maximum", data_maximum : maximum}, "" + maximum)
|
||||
Span({class : "information"}, [
|
||||
field_information("minimum", minimum),
|
||||
// field_information("value", value),
|
||||
field_information("maximum", maximum),
|
||||
])
|
||||
]);
|
||||
};
|
||||
|
||||
@ -341,6 +464,7 @@ export const Components = (function(){
|
||||
* @param {!Array.<Array.<[string, string|event_callback, Object.<string, any|null>|Array.<any|null>]>>} buttons
|
||||
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
|
||||
* @returns {Array.<any|null>}
|
||||
* @access public
|
||||
*/
|
||||
this.buttons = (buttons, inputs = null) => Div({
|
||||
class : ["buttons"].concat(Common.get_array(Common.get_value("class", inputs, []))).join(" ").trim(),
|
||||
|
||||
@ -33,9 +33,9 @@ export const Event = (function(){
|
||||
/** @type {Event} */
|
||||
const self = this,
|
||||
/** @type {Object.<number, event_callback>} */
|
||||
events = {},
|
||||
/** @type {number} */
|
||||
id_i = 0;
|
||||
events = {};
|
||||
/** @type {number} */
|
||||
let id_i = 0;
|
||||
/** @type {boolean} */
|
||||
this.once = once;
|
||||
/** @type {boolean} */
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
import {Fieldset, Section, Form, Div} from "../Utils/HTMLDSL.ecma.js";
|
||||
import {
|
||||
Fieldset, Section, Form, Div, Nav, UL, LI, Span, Pre
|
||||
} from "../Utils/HTMLDSL.ecma.js";
|
||||
import {MarkDown} from "../Utils/MarkDown.ecma.js";
|
||||
import {Common} from "../Utils/Common.ecma.js";
|
||||
|
||||
/**
|
||||
@ -41,8 +44,6 @@ export const AIChatComponent = (function(){
|
||||
|
||||
const name = Common.get_value("name", inputs, "aichat");
|
||||
|
||||
web_socket_id = anp.web_sockets_clients.add("AAA", "https://localhost:18000/");
|
||||
|
||||
return Fieldset({class : "aichat"}, [
|
||||
anp.components.i18n(name, "legend"),
|
||||
Section({class : "messages"}),
|
||||
@ -60,6 +61,86 @@ export const AIChatComponent = (function(){
|
||||
]);
|
||||
};
|
||||
|
||||
const build_data_icon = (name, value) => LI({
|
||||
data_name : name,
|
||||
data_i18n : value,
|
||||
data_i18n_without : true,
|
||||
title : anp.components.i18n(value)
|
||||
}, [
|
||||
anp.components.icon(value),
|
||||
anp.components.i18n(value)
|
||||
]);
|
||||
|
||||
const build_data_item = (name, value = null) => LI({
|
||||
data_name : name,
|
||||
data_i18n : name,
|
||||
data_i18n_without : true,
|
||||
title : anp.components.i18n(name)
|
||||
}, [
|
||||
anp.components.icon(name),
|
||||
anp.components.i18n(name),
|
||||
value === null ? null : Span({class : "value"}, "" + value)
|
||||
]);
|
||||
|
||||
const get_message_box = item => {
|
||||
|
||||
while((!item.classList || !item.classList.contains("message")) && (item = item.parentElement));
|
||||
|
||||
return item;
|
||||
};
|
||||
|
||||
const set_view = (item, event) => {
|
||||
|
||||
const box = get_message_box(item),
|
||||
mode = item.getAttribute("data-i18n");
|
||||
|
||||
box.getAttribute("data-mode") != mode && box.setAttribute("data-mode", mode);
|
||||
|
||||
};
|
||||
|
||||
const format = content => {
|
||||
|
||||
const html = MarkDown.to_html(content);
|
||||
|
||||
return [html, html.replace(/</g, "<").replace(/>/g, ">"), content];
|
||||
};
|
||||
|
||||
const build_message = (id, type, content = "", mode = "waiting") => {
|
||||
|
||||
const [html, raw_html, md] = format(content),
|
||||
date = Date.now();
|
||||
|
||||
return Fieldset({
|
||||
class : "message",
|
||||
data_id : id,
|
||||
data_type : type,
|
||||
data_ok : true,
|
||||
data_status : mode,
|
||||
data_done : mode == "done",
|
||||
data_mode : "html"
|
||||
}, [
|
||||
anp.components.i18n(type, "legend"),
|
||||
Div({class : "message-box html-content"}, html),
|
||||
Pre({class : "message-box raw-html-content"}, raw_html),
|
||||
Pre({class : "message-box md-content"}, md),
|
||||
Nav({class : "view buttons"}, [
|
||||
anp.components.button("html", set_view),
|
||||
anp.components.button("raw_html", set_view),
|
||||
anp.components.button("md", set_view),
|
||||
]),
|
||||
UL({class : "data"}, [
|
||||
build_data_icon("status", mode),
|
||||
build_data_item("ok"),
|
||||
build_data_item("done"),
|
||||
build_data_item("date_from", date),
|
||||
build_data_item("date_to", date),
|
||||
build_data_item("time", 0),
|
||||
build_data_item("length", content.length),
|
||||
build_data_item("response_tokens", content.replace(/(?:[^a-z0-9]+|[\r\n]+)+/gi, " ").trim().split(" ").length)
|
||||
])
|
||||
]);
|
||||
};
|
||||
|
||||
const send = (item, event) => {
|
||||
|
||||
const text_box = item.querySelector("[name=message]"),
|
||||
@ -68,15 +149,21 @@ export const AIChatComponent = (function(){
|
||||
event.preventDefault();
|
||||
|
||||
if(text){
|
||||
Common.HTML(".aichat .messages", Fieldset({
|
||||
class : "message",
|
||||
data_type : "user",
|
||||
data_id : anp.unique_keys.create()
|
||||
}, [
|
||||
anp.components.i18n("user", "legend"),
|
||||
Div({class : "content"}, text)
|
||||
]));
|
||||
|
||||
const data_id = anp.unique_keys.get();
|
||||
|
||||
Common.HTML(
|
||||
".aichat .messages",
|
||||
build_message(data_id, "user", text, "done"),
|
||||
build_message(data_id, "bot")
|
||||
);
|
||||
text_box.value = "";
|
||||
|
||||
anp.web_sockets_clients.send("anp", "ai", "message", {
|
||||
message_id : data_id,
|
||||
message : text
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
return false;
|
||||
@ -86,25 +173,36 @@ export const AIChatComponent = (function(){
|
||||
event.key == "Enter" && !event.shiftKey && send(item, event);
|
||||
};
|
||||
|
||||
this.write_response = (id, fragment) => {
|
||||
|
||||
let box = document.querySelector(".aichat .messages>[data-type=bot][data-id='" + id + "']");
|
||||
|
||||
if(!box){
|
||||
if(!document.querySelector(".aichat .messages>[data-type=user][data-id='" + id + "']"))
|
||||
return;
|
||||
box = Common.HTML(".aichat .messages", Fieldset({
|
||||
class : "message",
|
||||
data_type : "box",
|
||||
data_id : id
|
||||
}, [
|
||||
anp.components.i18n("assistant", "legend"),
|
||||
Div({class : "content"}, "")
|
||||
]), true)[0];
|
||||
};
|
||||
|
||||
box.querySelector(".content").innerHTML += fragment;
|
||||
|
||||
this.write_response = (id, fragment, ok, done) => {
|
||||
|
||||
const box = document.querySelector(".aichat .messages>[data-type=bot][data-id='" + id + "']"),
|
||||
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").innerText += fragment);
|
||||
|
||||
box.querySelector(".html-content").innerHTML = html;
|
||||
box.querySelector(".raw-html-content").innerHTML = raw_html;
|
||||
|
||||
box.setAttribute("data-ok", ok);
|
||||
box.setAttribute("data-done", done);
|
||||
box.setAttribute("data-status", status);
|
||||
|
||||
status_box.setAttribute("data-i18n", status);
|
||||
status_box.setAttribute("title", status_text);
|
||||
status_i18n_box.setAttribute("data-i18n", status_text);
|
||||
status_i18n_box.innerHTML = status_text;
|
||||
|
||||
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);
|
||||
tokens_box.innerText = Number(tokens_box.innerText) + 1;
|
||||
|
||||
};
|
||||
|
||||
constructor();
|
||||
|
||||
@ -123,7 +123,7 @@ export const BaseComponent = (function(){
|
||||
/** @type {string} */
|
||||
logo = anp.settings.get(["application_logo", "logo"], inputs, "images/logo.webp"),
|
||||
/** @type {string} */
|
||||
id = anp.unique_keys.create(),
|
||||
id = anp.unique_keys.get(),
|
||||
/** @type {string} */
|
||||
gui_mode = Check.is_dark_mode() ? "dark" : "light",
|
||||
/** @type {boolean} */
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @typedef {import("../Application/AnP.ecma.js").AnP} AnP
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class AIChat
|
||||
* @constructor
|
||||
* @param {!AnP} anp
|
||||
* @returns {void}
|
||||
* @access private
|
||||
* @static
|
||||
*/
|
||||
export const AIChat = (function(){
|
||||
|
||||
/**
|
||||
* @constructs AIChat
|
||||
* @param {!AnP} anp
|
||||
* @returns {void}
|
||||
* @access private
|
||||
* @static
|
||||
*/
|
||||
const AIChat = function(anp){
|
||||
|
||||
/** @type {AIChat} */
|
||||
const self = this;
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
* @access private
|
||||
*/
|
||||
const constructor = () => {};
|
||||
|
||||
constructor();
|
||||
};
|
||||
|
||||
return AIChat;
|
||||
})();
|
||||
55
Public/ecma/Controllers/AIController.ecma.js
Normal file
55
Public/ecma/Controllers/AIController.ecma.js
Normal file
@ -0,0 +1,55 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @typedef {import("../Application/AnP.ecma.js").AnP} AnP
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class AIController
|
||||
* @constructor
|
||||
* @param {!AnP} anp
|
||||
* @returns {void}
|
||||
* @access private
|
||||
* @static
|
||||
*/
|
||||
export const AIController = (function(){
|
||||
|
||||
/**
|
||||
* @constructs AIController
|
||||
* @param {!AnP} anp
|
||||
* @returns {void}
|
||||
* @access private
|
||||
* @static
|
||||
*/
|
||||
const AIController = function(anp){
|
||||
|
||||
/** @type {AIChat} */
|
||||
const self = this;
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
* @access private
|
||||
*/
|
||||
const constructor = () => {};
|
||||
|
||||
this.test = (...parameters) => {
|
||||
console.log(parameters);
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
};
|
||||
|
||||
constructor();
|
||||
};
|
||||
|
||||
return AIController;
|
||||
})();
|
||||
@ -196,6 +196,7 @@ export const FilesDriver = (function(){
|
||||
date = Date.now();
|
||||
|
||||
ajax.open(method, root_urls[i] + url, asynchronous);
|
||||
ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
|
||||
ajax.timeout = timeout;
|
||||
ajax.onreadystatechange = () => {
|
||||
if(ended)
|
||||
|
||||
173
Public/ecma/Drivers/WebSocketsClientsDriver.ecma.js
Normal file
173
Public/ecma/Drivers/WebSocketsClientsDriver.ecma.js
Normal file
@ -0,0 +1,173 @@
|
||||
"use strict";
|
||||
|
||||
import {WebSocketsClientsAbstract} from "../Abstracts/WebSocketsClientsAbstract.ecma.js";
|
||||
import {Common} from "../Utils/Common.ecma.js";
|
||||
import {Event} from "../Application/Event.ecma.js";
|
||||
|
||||
/**
|
||||
* @typedef {import("../Application/AnP.ecma.js").AnP} AnP
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class WebSocketsDriver
|
||||
* @constructor
|
||||
* @extends WebSocketsClientsAbstract
|
||||
* @param {!AnP} anp
|
||||
* @param {!string} key
|
||||
* @param {!(Object.<string, any|null>|Array.<any|null>)} inputs
|
||||
* @returns {void}
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
export const WebSocketsClientsDriver = (function(){
|
||||
|
||||
/**
|
||||
* @callback continue_callback
|
||||
* @param {boolean} ok
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @constructs WebSocketsClientsDriver
|
||||
* @extends WebSocketsClientsAbstract
|
||||
* @param {!AnP} anp
|
||||
* @param {!string} key
|
||||
* @param {!(Object.<string, any|null>|Array.<any|null>)} inputs
|
||||
* @returns {void}
|
||||
* @access private
|
||||
* @static
|
||||
*/
|
||||
const WebSocketsClientsDriver = function(anp, key, inputs){
|
||||
|
||||
/** @type {WebSocketsClientsDriver} */
|
||||
const self = this;
|
||||
/** @type {WebSocketsClientsAbstract|null} */
|
||||
let _super = null,
|
||||
/** @type {WebSocket|null} */
|
||||
client = null,
|
||||
/** @type {boolean} */
|
||||
started = false;
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
* @access private
|
||||
*/
|
||||
const constructor = () => {
|
||||
|
||||
Common.extends(self, _super = new WebSocketsClientsAbstract(anp, key, inputs), false);
|
||||
|
||||
Common.get_value(["web_socket_autostart", "autostart"], inputs, true) && self.start();
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?continue_callback} callback
|
||||
* @returns {boolean}
|
||||
* @access public
|
||||
*/
|
||||
this.start = (callback = null) => {
|
||||
|
||||
/** @type {continue_callback} */
|
||||
const end = ok => Common.execute(callback, ok);
|
||||
|
||||
if(started){
|
||||
end(false);
|
||||
return false;
|
||||
};
|
||||
started = true;
|
||||
|
||||
client = new WebSocket(self.url);
|
||||
|
||||
client.onopen = on_open;
|
||||
client.onmessage = on_message
|
||||
client.onerror = on_error;
|
||||
client.onclose = on_close;
|
||||
|
||||
end(true);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?continue_callback} callback
|
||||
* @returns {boolean}
|
||||
* @access public
|
||||
*/
|
||||
this.close = (callback = null) => {
|
||||
|
||||
/** @type {continue_callback} */
|
||||
const end = ok => Common.execute(callback, ok);
|
||||
|
||||
if(!started){
|
||||
end(false);
|
||||
return false;
|
||||
};
|
||||
started = false;
|
||||
|
||||
client.close();
|
||||
client = null;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!Event} _
|
||||
* @returns {void}
|
||||
* @access private
|
||||
*/
|
||||
const on_open = _ => {
|
||||
self.on_open.execute();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!Event} event
|
||||
* @returns {void}
|
||||
* @access private
|
||||
*/
|
||||
const on_message = event => {
|
||||
self.on_message.execute(event.data);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!Event} event
|
||||
* @returns {void}
|
||||
* @access private
|
||||
*/
|
||||
const on_error = event => {
|
||||
self.on_error.execute(event.error);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!Event} _
|
||||
* @returns {void}
|
||||
* @access private
|
||||
*/
|
||||
const on_close = _ => {
|
||||
self.on_close.execute();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!string} controller
|
||||
* @param {!string} action
|
||||
* @param {?any} [data = null]
|
||||
* @param {!number} [code = 200]
|
||||
* @return {void}
|
||||
* @access public
|
||||
*/
|
||||
this.send = (controller, action, data = null, code = 200) => {
|
||||
client.send(Common.data_encode({
|
||||
ok : code >= 200 && code < 300,
|
||||
code : code,
|
||||
controller : controller,
|
||||
action : action,
|
||||
data : data,
|
||||
id : self.id
|
||||
}));
|
||||
};
|
||||
|
||||
constructor();
|
||||
|
||||
};
|
||||
|
||||
return WebSocketsClientsDriver;
|
||||
})();
|
||||
@ -1,131 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
import {Common} from "../Utils/Common.ecma.js";
|
||||
import {Event} from "../Application/Event.ecma.js";
|
||||
|
||||
/**
|
||||
* @class WebSocketsDriver
|
||||
* @constructor
|
||||
* @param {!(Object.<string, any|null>|Array.<any|null>)} inputs
|
||||
* @returns {void}
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
export const WebSocketsDriver = (function(){
|
||||
|
||||
/**
|
||||
* @callback continue_callback
|
||||
* @param {boolean} ok
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @constructs WebSocketsDriver
|
||||
* @param {!(Object.<string, any|null>|Array.<any|null>)} inputs
|
||||
* @returns {void}
|
||||
* @access private
|
||||
* @static
|
||||
*/
|
||||
const WebSocketsDriver = function(inputs){
|
||||
|
||||
/** @type {WebSocketsDriver} */
|
||||
const self = this;
|
||||
/** @type {WebSocket|null} */
|
||||
let client = null,
|
||||
/** @type {string|null} */
|
||||
url = null,
|
||||
/** @type {boolean} */
|
||||
started = false;
|
||||
|
||||
/** @type {Event} */
|
||||
this.on_open = new Event();
|
||||
/** @type {Event} */
|
||||
this.on_message = new Event();
|
||||
/** @type {Event} */
|
||||
this.on_error = new Event();
|
||||
/** @type {Event} */
|
||||
this.on_close = new Event();
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
* @access private
|
||||
*/
|
||||
const constructor = () => {
|
||||
|
||||
url = Common.get_string(inputs.url, "ws://localhost:8080");
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?continue_callback} callback
|
||||
* @returns {boolean}
|
||||
* @access public
|
||||
*/
|
||||
this.start = (callback = null) => {
|
||||
|
||||
/** @type {continue_callback} */
|
||||
const end = ok => Common.execute(callback, ok);
|
||||
|
||||
if(started){
|
||||
end(false);
|
||||
return false;
|
||||
};
|
||||
started = true;
|
||||
|
||||
client = new WebSocket(url);
|
||||
|
||||
client.onopen = on_open;
|
||||
client.onmessage = on_message
|
||||
client.onerror = on_error;
|
||||
client.onclose = on_close;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?continue_callback} callback
|
||||
* @returns {boolean}
|
||||
* @access public
|
||||
*/
|
||||
this.close = (callback = null) => {
|
||||
|
||||
/** @type {continue_callback} */
|
||||
const end = ok => Common.execute(callback, ok);
|
||||
|
||||
if(!started){
|
||||
end(false);
|
||||
return false;
|
||||
};
|
||||
started = false;
|
||||
|
||||
client.close();
|
||||
client = null;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const on_open = event => {
|
||||
console.log(["client", event]);
|
||||
};
|
||||
|
||||
const on_message = event => {
|
||||
console.log(["message", event]);
|
||||
};
|
||||
|
||||
const on_error = event => {
|
||||
console.log(["error", event]);
|
||||
};
|
||||
|
||||
const on_close = event => {
|
||||
console.log(["close", event]);
|
||||
};
|
||||
|
||||
this.send = message => {
|
||||
client.send(message);
|
||||
};
|
||||
|
||||
constructor();
|
||||
|
||||
};
|
||||
|
||||
return WebSocketsDriver;
|
||||
})();
|
||||
@ -55,9 +55,9 @@ export const ControllersManager = (function(){
|
||||
* @access public
|
||||
*/
|
||||
this.update = (callback = null) => {
|
||||
|
||||
Common.execute(callback);
|
||||
|
||||
Common.execute_array(["default_controllers_files", "controllers_files", "default_controllers", "controllers"], (key, next) => {
|
||||
self.add(anp.settings.get(key), true, next);
|
||||
}, callback, true);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -116,37 +116,50 @@ export const ControllersManager = (function(){
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {?any} inputs
|
||||
* @param {!boolean} [overwrite = false]
|
||||
* @param {?default_callback} [callback = null]
|
||||
* @return {void}
|
||||
* @access public
|
||||
*/
|
||||
this.add = (inputs, overwrite = false, callback = null) => {
|
||||
anp.files.load_json(inputs, data => {
|
||||
Object.entries(data).forEach(([key, Controller]) => {
|
||||
if(!Controller || (!overwrite && controllers[key]))
|
||||
return;
|
||||
if(Check.is_function(Controller))
|
||||
controllers[key] = new Controller(anp);
|
||||
else if(Check.is_object(Controller))
|
||||
controllers[key] = Controller;
|
||||
else if(Check.is_string(Controller)){
|
||||
data.forEach(subinputs => {
|
||||
Object.entries(subinputs).forEach(([key, Controller]) => {
|
||||
if(!Controller || (!overwrite && controllers[key]))
|
||||
return;
|
||||
if(Check.is_function(Controller))
|
||||
controllers[key] = new Controller(anp);
|
||||
else if(Check.is_object(Controller))
|
||||
controllers[key] = Controller;
|
||||
else if(Check.is_string(Controller)){
|
||||
|
||||
/** @type {Object.<string, any|null>|Function|null} */
|
||||
const Model = anp.models.get(Controller);
|
||||
/** @type {Object.<string, any|null>|Function|null} */
|
||||
const Model = anp.models.get(Controller);
|
||||
|
||||
if(Check.is_object(Model))
|
||||
controllers[key] = Model;
|
||||
else if(Check.is_function(Model))
|
||||
controllers[key] = new Model(anp);
|
||||
if(Check.is_object(Model))
|
||||
controllers[key] = Model;
|
||||
else if(Check.is_function(Model))
|
||||
controllers[key] = new Model(anp);
|
||||
|
||||
};
|
||||
};
|
||||
});
|
||||
});
|
||||
Common.execute(callback);
|
||||
}, true);
|
||||
};
|
||||
|
||||
this.execute = (name, action, ...parameters) => {};
|
||||
/**
|
||||
* @param {!string} controller
|
||||
* @param {!string} action
|
||||
* @param {?any} [data = null]
|
||||
* @param {!number} [code = 200]
|
||||
* @return {void}
|
||||
* @access public
|
||||
*/
|
||||
this.execute = (controller, action, data = null, code = 200) => {
|
||||
controllers[controller] && controllers[controller][action] && controllers[controller][action](data, code);
|
||||
};
|
||||
|
||||
constructor();
|
||||
|
||||
|
||||
@ -54,9 +54,9 @@ export const ModelsManager = (function(){
|
||||
* @access public
|
||||
*/
|
||||
this.update = (callback = null) => {
|
||||
|
||||
Common.execute(callback);
|
||||
|
||||
Common.execute_array(["default_models_files", "models_files", "default_models", "models"], (key, next) => {
|
||||
self.add(anp.settings.get(key), true, next);
|
||||
}, callback, true);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -123,11 +123,13 @@ export const ModelsManager = (function(){
|
||||
*/
|
||||
this.add = (inputs, overwrite = false, callback = null) => {
|
||||
anp.files.load_json(inputs, data => {
|
||||
Object.entries(data).forEach(([key, Model]) => {
|
||||
Model &&
|
||||
(overwrite || !models[key]) &&
|
||||
(Check.is_function(Model) || Check.is_object(Model)) &&
|
||||
(models[key] = Model);
|
||||
data.forEach(subdata => {
|
||||
Object.entries(subdata).forEach(([key, Model]) => {
|
||||
Model &&
|
||||
(overwrite || !models[key]) &&
|
||||
(Check.is_function(Model) || Check.is_object(Model)) &&
|
||||
(models[key] = Model);
|
||||
});
|
||||
});
|
||||
Common.execute(callback);
|
||||
}, true);
|
||||
|
||||
@ -168,7 +168,7 @@ export const UniqueKeysManager = (function(){
|
||||
* @return {string}
|
||||
* @access public
|
||||
*/
|
||||
this.create = (is_html_item = false) => {
|
||||
this.get = (is_html_item = false) => {
|
||||
|
||||
/** @type {string} */
|
||||
let key;
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
"use strict";
|
||||
|
||||
import {WebSocketClientModel} from "../Models/WebSocketClientModel.ecma.js";
|
||||
import {WebSocketsClientsAbstract} from "../Abstracts/WebSocketsClientsAbstract.ecma.js";
|
||||
import {Common} from "../Utils/Common.ecma.js";
|
||||
import {Check} from "../Utils/Checks.ecma.js";
|
||||
|
||||
/**
|
||||
* @typedef {import("../Application/AnP.ecma.js").AnP} AnP
|
||||
@ -18,6 +20,24 @@ import {WebSocketClientModel} from "../Models/WebSocketClientModel.ecma.js";
|
||||
*/
|
||||
export const WebSocketsClientsManager = (function(){
|
||||
|
||||
/**
|
||||
* @callback continue_callback
|
||||
* @param {boolean} ok
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback simple_callback
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback web_socket_constructor_callback
|
||||
* @param {!AnP} anp
|
||||
* @param {!(Object.<string, any|null>|Array.<any|null>)} inputs
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @constructs WebSocketsClientsManager
|
||||
* @param {!AnP} anp
|
||||
@ -31,7 +51,7 @@ export const WebSocketsClientsManager = (function(){
|
||||
|
||||
/** @type {WebSocketsClientsManager} */
|
||||
const self = this,
|
||||
/** @type {Object.<string, WebSocketClientModel} */
|
||||
/** @type {Object.<string, WebSocketsClientsAbstract>} */
|
||||
clients = {};
|
||||
|
||||
/**
|
||||
@ -39,11 +59,111 @@ export const WebSocketsClientsManager = (function(){
|
||||
* @access private
|
||||
*/
|
||||
const constructor = () => {};
|
||||
|
||||
/**
|
||||
* @param {?continue_callback} callback
|
||||
* @returns {boolean}
|
||||
* @access public
|
||||
*/
|
||||
this.update = (callback = null) => {
|
||||
Common.execute_array(["default_web_sockets_clients_files", "web_sockets_clients_files", "default_web_sockets_clients", "web_sockets_clients"], (key, next) => {
|
||||
self.add(anp.settings.get(key), true, next);
|
||||
}, callback, true);
|
||||
};
|
||||
|
||||
this.add = (key, url) => {
|
||||
/**
|
||||
* @param {?continue_callback} callback
|
||||
* @returns {boolean}
|
||||
* @access public
|
||||
*/
|
||||
this.reset = (callback = null) => {
|
||||
|
||||
clients[key] = new WebSocketClientModel(anp, url);
|
||||
[settings, secrets].forEach(Common.clear_dictionary);
|
||||
|
||||
self.update(callback);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?continue_callback} callback
|
||||
* @returns {boolean}
|
||||
* @access public
|
||||
*/
|
||||
this.start = (callback = null) => {
|
||||
|
||||
/** @type {continue_callback} */
|
||||
const end = ok => Common.execute(callback, ok);
|
||||
|
||||
if(started){
|
||||
end(false);
|
||||
return false;
|
||||
};
|
||||
started = true;
|
||||
|
||||
self.update(() => end(true));
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?continue_callback} callback
|
||||
* @returns {boolean}
|
||||
* @access public
|
||||
*/
|
||||
this.close = (callback = null) => {
|
||||
|
||||
/** @type {continue_callback} */
|
||||
const end = ok => Common.execute(callback, ok);
|
||||
|
||||
if(!started){
|
||||
end(false);
|
||||
return false;
|
||||
};
|
||||
started = false;
|
||||
|
||||
end(true);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {?any} inputs
|
||||
* @param {!boolean} [overwrite = false]
|
||||
* @param {?simple_callback} [callback = null]
|
||||
* @return {void}
|
||||
* @access public
|
||||
*/
|
||||
this.add = (inputs, overwrite = false, callback = null) => {
|
||||
anp.files.load_json(inputs, data => {
|
||||
for(let subinputs of data)
|
||||
Object.entries(subinputs).filter(([key, _]) => (
|
||||
overwrite || clients[key] === undefined
|
||||
)).forEach(([key, value]) => {
|
||||
try{
|
||||
|
||||
/** @type {string|web_socket_constructor_callback|WebSocketsClientsAbstract} */
|
||||
const Type = Common.get_value("type", (
|
||||
Check.is_string(value) ? value = {url : value} :
|
||||
value), "WebSocketsDriver"),
|
||||
/** @type {web_socket_constructor_callback|null} */
|
||||
Model = Check.is_string(Type) ? anp.models.get(Type) : null,
|
||||
/** @type {WebSocketsClientsAbstract} */
|
||||
web_socket = (
|
||||
Model ? new Model(anp, key, value) :
|
||||
Check.is_function(Type) ? new Type(anp, key, value) :
|
||||
Type instanceof WebSocketsClientsAbstract ? Type :
|
||||
null);
|
||||
|
||||
web_socket && (clients[key] = web_socket);
|
||||
|
||||
}catch(exception){
|
||||
anp.exception(exception, "anp_web_sockets_clients_manager_add", {
|
||||
key : key
|
||||
});
|
||||
};
|
||||
});
|
||||
Common.execute(callback);
|
||||
}, true);
|
||||
};
|
||||
|
||||
this.remove = key => {
|
||||
@ -55,7 +175,45 @@ export const WebSocketsClientsManager = (function(){
|
||||
return false;
|
||||
};
|
||||
|
||||
this.send = (key, data) => {};
|
||||
this.send = (key, controller, action, data = null, code = 200) => {
|
||||
if(key in clients){
|
||||
clients[key].send(controller, action, data, code);
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
this.on_open = (key, callback) => {
|
||||
if(key in clients){
|
||||
clients[key].on_open.add(callback);
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
this.on_message = (key, callback) => {
|
||||
if(key in clients){
|
||||
clients[key].on_message.add(callback);
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
this.on_error = (key, callback) => {
|
||||
if(key in clients){
|
||||
clients[key].on_error.add(callback);
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
this.on_close = (key, callback) => {
|
||||
if(key in clients){
|
||||
clients[key].on_close.add(callback);
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
constructor();
|
||||
|
||||
|
||||
@ -1,92 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
import {Event} from "../Application/Event.ecma.js";
|
||||
import {Check} from "../Utils/Checks.ecma.js";
|
||||
import {Common} from "../Utils/Common.ecma.js";
|
||||
|
||||
/**
|
||||
* @typedef {import("../Application/AnP.ecma.js").AnP} AnP
|
||||
*/
|
||||
|
||||
export const WebSocketClientModel = (function(){
|
||||
|
||||
const WebSocketClientModel = function(anp, inputs){
|
||||
|
||||
const self = this;
|
||||
let web_socket = null,
|
||||
url = null,
|
||||
id = null;
|
||||
|
||||
this.on_open = new Event();
|
||||
this.on_message = new Event();
|
||||
this.on_close = new Event();
|
||||
this.on_error = new Event();
|
||||
|
||||
const constructor = () => {
|
||||
|
||||
Check.is_string(inputs) && (inputs = {url : inputs});
|
||||
|
||||
web_socket = new WebSocket(url = Common.get_value("url", inputs, ""));
|
||||
|
||||
web_socket.onopen = self.on_open.execute;
|
||||
web_socket.onmessage = self.on_message.execute;
|
||||
web_socket.onclose = self.on_close.execute;
|
||||
web_socket.onerror = self.on_error.execute;
|
||||
|
||||
// self.on_open.add(opened);
|
||||
self.on_message.add(reveive_message);
|
||||
self.on_close.add(closed);
|
||||
self.on_error.add(error);
|
||||
|
||||
};
|
||||
|
||||
// const opened = event => {
|
||||
// self.send("web_socket_client", "get_id", null, 200);
|
||||
// };
|
||||
|
||||
const reveive_message = event => {
|
||||
console.log(event);
|
||||
|
||||
const data = Common.data_decode(event.data);
|
||||
|
||||
switch(data.controller + "." + data.method){
|
||||
case "web_socket_client.set_id":
|
||||
id = data.data;
|
||||
break;
|
||||
default:
|
||||
anp.controllers.execute(data.controller, data.method, data.data, data);
|
||||
break;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
this.send = (controller, method, data, code = 200) => {
|
||||
web_socket.send(Common.data_encode({
|
||||
ok : code >= 200 && code < 300,
|
||||
code : code,
|
||||
id : id,
|
||||
controller : controller,
|
||||
method : method,
|
||||
data : data
|
||||
}));
|
||||
};
|
||||
|
||||
this.close = () => {
|
||||
web_socket.close();
|
||||
};
|
||||
|
||||
const closed = event => {
|
||||
id = null;
|
||||
};
|
||||
|
||||
const error = event => {
|
||||
console.error(event);
|
||||
self.close();
|
||||
};
|
||||
|
||||
constructor();
|
||||
|
||||
};
|
||||
|
||||
return WebSocketClientModel;
|
||||
})();
|
||||
@ -467,5 +467,19 @@ export const Common = (function(){
|
||||
"\\" + (Common.REGULAR_EXPRESSION.FROM[character] || character)
|
||||
));
|
||||
|
||||
/**
|
||||
* @param {!Object.<string, any|null>} main
|
||||
* @param {!(Object.<string, any|null>|Array.<Object.<string, any|null>>)} items
|
||||
* @param {!boolean} [overwrite = false]
|
||||
* @returns {void}
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
Common.extends = (main, items, overwrite = false) => Common.get_array(items).forEach(item => {
|
||||
item && Object.entries(item).forEach(([key, value]) => {
|
||||
(overwrite || !(key in main)) && (main[key] = value);
|
||||
});
|
||||
});
|
||||
|
||||
return Common;
|
||||
})();
|
||||
@ -347,3 +347,9 @@ export const Body = (attributes = {}, children = []) => ["body", attributes, chi
|
||||
/** @type {element_callback} */
|
||||
export const HTML = (attributes = {}, children = []) => ["html", attributes, children];
|
||||
|
||||
/** @type {element_callback} */
|
||||
export const Pre = (attributes = {}, children = []) => ["pre", attributes, children];
|
||||
|
||||
/** @type {element_callback} */
|
||||
export const Code = (attributes = {}, children = []) => ["code", attributes, children];
|
||||
|
||||
|
||||
101
Public/ecma/Utils/MarkDown.ecma.js
Normal file
101
Public/ecma/Utils/MarkDown.ecma.js
Normal file
@ -0,0 +1,101 @@
|
||||
"use strict";
|
||||
|
||||
export const MarkDown = (function(){
|
||||
|
||||
const MarkDown = function(anp){};
|
||||
|
||||
MarkDown.Item = function(pattern, replace, block = false){
|
||||
this.pattern = pattern;
|
||||
this.replace = replace;
|
||||
this.block = block;
|
||||
this.match = null;
|
||||
this.length = 0;
|
||||
this.more = true;
|
||||
this.index = -1;
|
||||
};
|
||||
|
||||
MarkDown.Header = () => new MarkDown.Item(/^(#{1,6})\s*(.+)$/m, (match, hashes, content) => `<h${hashes.length}>${MarkDown.to_html(content, false)}</h${hashes.length}>`, true);
|
||||
|
||||
MarkDown.Block = () => new MarkDown.Item(/^```([a-z]*)\r?\n([\s\S]*?)\r?\n```/, (match, lang, content) => `<fieldset><legend>${lang}</legend><pre><code class="${lang}">${content}</code></pre></fieldset>`, true);
|
||||
|
||||
MarkDown.BlockQuote = () => new MarkDown.Item(/^>\s?(.+)$/m, (match, content) => `<blockquote>${MarkDown.to_html(content, false)}</blockquote>`, true);
|
||||
|
||||
MarkDown.Paragraph = () => new MarkDown.Item(/^((?:[^\r\n]+(?:\r\n|[\r\n])?)+)/m, (match, content) => `<p>${MarkDown.to_html(content, false)}</p>\n\n`, true);
|
||||
|
||||
MarkDown.Link = () => new MarkDown.Item(/\[([^\]]+)\]\(([^)]+)\)|([a-z]{2,12}:\/{2}[^ "']+)/, (match, text, url, link) => `<a href="${url || link}" target="_blank" title="${text || link}">${text || link}</a>`);
|
||||
|
||||
MarkDown.Bold = () => new MarkDown.Item(/\*{2}((?:(?!(?:\*{2})).)+)\*{2}/, (match, content) => `<b>${MarkDown.to_html(content, false)}</b>`);
|
||||
|
||||
MarkDown.Italic = () => new MarkDown.Item(/\*((?:(?!(?:\*)).|\*{2})+)\*(?!\*)/, (match, content) => `<i>${MarkDown.to_html(content, false)}</i>`);
|
||||
|
||||
MarkDown.to_html = (string, blocks = true) => {
|
||||
|
||||
let html = ``;
|
||||
const matches = [
|
||||
MarkDown.Header(),
|
||||
MarkDown.Block(),
|
||||
MarkDown.BlockQuote(),
|
||||
MarkDown.Paragraph(),
|
||||
MarkDown.Link(),
|
||||
MarkDown.Bold(),
|
||||
MarkDown.Italic()
|
||||
];
|
||||
|
||||
do{
|
||||
|
||||
let index = 1 << 28,
|
||||
i = null;
|
||||
|
||||
[...matches].forEach((item, j) => {
|
||||
if(!item.more)
|
||||
return;
|
||||
|
||||
if(item.block && !blocks){
|
||||
item.more = false;
|
||||
return;
|
||||
};
|
||||
|
||||
if(item.index < 0){
|
||||
|
||||
const submatches = string.match(item.pattern);
|
||||
|
||||
if(!submatches){
|
||||
item.more = false;
|
||||
return;
|
||||
};
|
||||
|
||||
item.match = submatches;
|
||||
item.length = submatches[0].length;
|
||||
item.index = submatches.index;
|
||||
|
||||
};
|
||||
|
||||
if(index > item.index){
|
||||
index = item.index;
|
||||
i = j;
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
if(i === null)
|
||||
break;
|
||||
|
||||
const item = matches[i],
|
||||
total = index + item.length;
|
||||
|
||||
html += string.substring(0, index) + item.replace(...item.match);
|
||||
string = string.substring(total);
|
||||
|
||||
matches.forEach(item => {
|
||||
item.index -= total;
|
||||
});
|
||||
|
||||
}while(matches.length)
|
||||
|
||||
html += string;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
return MarkDown;
|
||||
})();
|
||||
@ -35,13 +35,15 @@
|
||||
|
||||
import {AnP} from "./ecma/Application/AnP.ecma.js";
|
||||
import {SessionsController} from "./ecma/Controllers/SessionsController.ecma.js";
|
||||
import {AIChat} from "./ecma/Controllers/AIChatController.ecma.js";
|
||||
import {AIController} from "./ecma/Controllers/AIController.ecma.js";
|
||||
import {WebSocketsClientsDriver} from "./ecma/Drivers/WebSocketsClientsDriver.ecma.js";
|
||||
|
||||
/** @type {AnP} */
|
||||
const anp = new AnP({
|
||||
models : {
|
||||
"sessions" : SessionsController,
|
||||
"aichat" : AIChat
|
||||
SessionsController : SessionsController,
|
||||
AIController : AIController,
|
||||
WebSocketsClientsDriver : WebSocketsClientsDriver
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -24,5 +24,14 @@
|
||||
["galego", "Galego", "/images/flags/galego.svg"],
|
||||
["nihongo", "日本語", "/images/flags/nihongo.svg"],
|
||||
["russkiy", "Русский", "/images/flags/russkiy.svg"]
|
||||
]
|
||||
],
|
||||
"default_web_sockets_clients" : {
|
||||
"anp" : {
|
||||
"type" : "WebSocketsClientsDriver",
|
||||
"url" : "ws://localhost:18765/"
|
||||
}
|
||||
},
|
||||
"default_controllers" : {
|
||||
"ai" : "AIController"
|
||||
}
|
||||
}
|
||||
@ -14,6 +14,23 @@
|
||||
"gui_mode" : "Modo del GUI",
|
||||
"aichat" : "AIChat",
|
||||
"message" : "Mensaje",
|
||||
"send" : "Enviar"
|
||||
"send" : "Enviar",
|
||||
"bot" : "Bot",
|
||||
"waiting" : "Esperando",
|
||||
"ok" : "OK",
|
||||
"done" : "Hecho",
|
||||
"date_from" : "Fecha de inicio",
|
||||
"date_to" : "Fecha de fin",
|
||||
"time" : "Tiempo",
|
||||
"length" : "Longitud",
|
||||
"response_tokens" : "Tokens de respuesta",
|
||||
"loading" : "Cargando",
|
||||
"error" : "Error",
|
||||
"html" : "HTML",
|
||||
"md" : "Markdown",
|
||||
"status" : "Estado",
|
||||
"raw_html" : "Código HTML",
|
||||
"minimum" : "Mínimo",
|
||||
"maximum" : "Máximo"
|
||||
}
|
||||
}
|
||||
110
Public/scss/AnP.aichat.scss
Normal file
110
Public/scss/AnP.aichat.scss
Normal file
@ -0,0 +1,110 @@
|
||||
@mixin aichar-color($mode){
|
||||
.aichat>legend{border-bottom-color : map-deep-get($color, $mode, "fore")}
|
||||
}
|
||||
.anp{
|
||||
|
||||
&[data-forced-gui-mode=default][data-gui-mode=default]{
|
||||
@include main_color_web(light);
|
||||
}
|
||||
@each $key in (dark, light){
|
||||
&[data-forced-gui-mode=#{$key}],&[data-forced-gui-mode=default][data-gui-mode=#{$key}]{
|
||||
@include main_color_web($key);
|
||||
}
|
||||
}
|
||||
|
||||
.aichat{
|
||||
|
||||
position : absolute;
|
||||
top : 0em;
|
||||
left : 0em;
|
||||
width : 100%;
|
||||
height : 100%;
|
||||
border : none;
|
||||
box-sizing : border-box;
|
||||
|
||||
&>legend{
|
||||
padding-bottom : .1em;
|
||||
border-bottom-width : .1em;
|
||||
border-bottom-style : solid;
|
||||
}
|
||||
|
||||
fieldset{
|
||||
position : relative;
|
||||
border : none;
|
||||
}
|
||||
legend{font-weight : 900;}
|
||||
form{
|
||||
display : flex;
|
||||
flex-direction : row;
|
||||
position : absolute;
|
||||
left : 0em;
|
||||
bottom : 0em;
|
||||
width : 100%;
|
||||
height : 5em;
|
||||
box-sizing : border-box;
|
||||
&>span{flex-grow : 0;}
|
||||
&>button{flex-grow : 0;}
|
||||
}
|
||||
.messages{
|
||||
position : absolute;
|
||||
top : 0em;
|
||||
left : 0em;
|
||||
bottom : 5em;
|
||||
width : 100%;
|
||||
box-sizing : border-box;
|
||||
overflow : auto;
|
||||
}
|
||||
|
||||
.message{
|
||||
clear : both;
|
||||
margin : 2em 0em;
|
||||
legend{
|
||||
float : left;
|
||||
width : 100%;
|
||||
padding : 0em;
|
||||
margin-bottom : .75em;
|
||||
}
|
||||
nav{
|
||||
position : absolute;
|
||||
top : 0em;
|
||||
right : 0em;
|
||||
}
|
||||
pre{
|
||||
width : 100%;
|
||||
white-space : pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.message-box{display : none;}
|
||||
.view.buttons button{
|
||||
opacity : .5;
|
||||
[data-icon]::before{margin : 0em;}
|
||||
[data-i18n]{display : none;}
|
||||
}
|
||||
[data-mode=html] .html-content,
|
||||
[data-mode=raw_html] .raw-html-content,
|
||||
[data-mode=md] .md-content{display : block;}
|
||||
[data-mode=html] button[data-i18n=html],
|
||||
[data-mode=raw_html] button[data-i18n=raw_html],
|
||||
[data-mode=md] button[data-i18n=md]{opacity : 1;}
|
||||
|
||||
.data{
|
||||
position : absolute;
|
||||
right : 0em;
|
||||
bottom : 0em;
|
||||
margin : 0em;
|
||||
padding : 0em;
|
||||
list-style-type : none;
|
||||
opacity : .85;
|
||||
li{
|
||||
display : inline-block;
|
||||
margin : 0em .5em;
|
||||
font-size : .75em;
|
||||
[data-i18n]{display : none;}
|
||||
}
|
||||
[data-name=date_from]{display : none;}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -23,7 +23,10 @@
|
||||
width : 100%;
|
||||
height : 100%;
|
||||
overflow : hidden;
|
||||
&,input,textarea,select,button{font-family : $font-normal;}
|
||||
&,input,textarea,select,button{
|
||||
font-family : $font-normal;
|
||||
box-sizing : border-box;
|
||||
}
|
||||
|
||||
button,input,textarea,select{font-size : 1em;}
|
||||
|
||||
@ -100,6 +103,22 @@
|
||||
min-height : 0%;
|
||||
resize : none;
|
||||
}
|
||||
.information{
|
||||
position : absolute;
|
||||
bottom : 0em;
|
||||
right : 0em;
|
||||
font-size : .8em;
|
||||
color : $color-grey;
|
||||
span>[data-i18n],&>[data-value=null]{display : none;}
|
||||
&>[data-i18n=minimum]::after{
|
||||
content : "-";
|
||||
margin : 0em .2em;
|
||||
}
|
||||
&>[data-i18n=maximum]::before{
|
||||
content : "-";
|
||||
margin : 0em .2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h1{
|
||||
|
||||
@ -792,7 +792,8 @@
|
||||
height: 100%;
|
||||
overflow: hidden; }
|
||||
.anp, .anp input, .anp textarea, .anp select, .anp button {
|
||||
font-family: "Roboto"; }
|
||||
font-family: "Roboto";
|
||||
box-sizing: border-box; }
|
||||
.anp button, .anp input, .anp textarea, .anp select {
|
||||
font-size: 1em; }
|
||||
.anp a[href] {
|
||||
@ -889,6 +890,20 @@
|
||||
min-width: 0%;
|
||||
min-height: 0%;
|
||||
resize: none; }
|
||||
.anp .input .information {
|
||||
position: absolute;
|
||||
bottom: 0em;
|
||||
right: 0em;
|
||||
font-size: .8em;
|
||||
color: #898989; }
|
||||
.anp .input .information span > [data-i18n], .anp .input .information > [data-value=null] {
|
||||
display: none; }
|
||||
.anp .input .information > [data-i18n=minimum]::after {
|
||||
content: "-";
|
||||
margin: 0em .2em; }
|
||||
.anp .input .information > [data-i18n=maximum]::before {
|
||||
content: "-";
|
||||
margin: 0em .2em; }
|
||||
.anp h1 {
|
||||
flex-grow: 0;
|
||||
font-size: 1em;
|
||||
@ -1053,5 +1068,137 @@
|
||||
content: "\f689"; }
|
||||
.anp [data-icon=gui_mode]::before {
|
||||
content: "\f043"; }
|
||||
.anp [data-icon=html]::before {
|
||||
content: "\f13b";
|
||||
font-family: "FA6FB"; }
|
||||
.anp [data-icon=raw_html]::before {
|
||||
content: "\f121"; }
|
||||
.anp [data-icon=md]::before {
|
||||
content: "\f1c9"; }
|
||||
|
||||
.anp[data-forced-gui-mode=default][data-gui-mode=default] {
|
||||
background-color: #EFEFEF;
|
||||
color: #222; }
|
||||
.anp[data-forced-gui-mode=default][data-gui-mode=default] a[href], .anp[data-forced-gui-mode=default][data-gui-mode=default] [data-role=link], .anp[data-forced-gui-mode=default][data-gui-mode=default] button, .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=submit], .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=button], .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=reset], .anp[data-forced-gui-mode=default][data-gui-mode=default] [role=button] {
|
||||
color: #2262b0; }
|
||||
.anp[data-forced-gui-mode=default][data-gui-mode=default] a[href]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [data-role=link]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] button:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=submit]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=reset]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [role=button]:hover {
|
||||
color: #b06222; }
|
||||
.anp[data-forced-gui-mode=default][data-gui-mode=default] button, .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=submit], .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=button], .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=reset], .anp[data-forced-gui-mode=default][data-gui-mode=default] [role=button] {
|
||||
border-color: #2262b0;
|
||||
box-shadow: 0em 0em 0.2em inset #2262b0; }
|
||||
.anp[data-forced-gui-mode=default][data-gui-mode=default] button:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=submit]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=reset]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [role=button]:hover {
|
||||
border-color: #b06222;
|
||||
box-shadow: 0em 0em 0.2em inset #b06222; }
|
||||
.anp[data-forced-gui-mode=dark], .anp[data-forced-gui-mode=default][data-gui-mode=dark] {
|
||||
background-color: #222;
|
||||
color: #EFEFEF; }
|
||||
.anp[data-forced-gui-mode=dark] a[href], .anp[data-forced-gui-mode=dark] [data-role=link], .anp[data-forced-gui-mode=dark] button, .anp[data-forced-gui-mode=dark] [type=submit], .anp[data-forced-gui-mode=dark] [type=button], .anp[data-forced-gui-mode=dark] [type=reset], .anp[data-forced-gui-mode=dark] [role=button], .anp[data-forced-gui-mode=default][data-gui-mode=dark] a[href], .anp[data-forced-gui-mode=default][data-gui-mode=dark] [data-role=link], .anp[data-forced-gui-mode=default][data-gui-mode=dark] button, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=submit], .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=button], .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=reset], .anp[data-forced-gui-mode=default][data-gui-mode=dark] [role=button] {
|
||||
color: #4b8bd9; }
|
||||
.anp[data-forced-gui-mode=dark] a[href]:hover, .anp[data-forced-gui-mode=dark] [data-role=link]:hover, .anp[data-forced-gui-mode=dark] button:hover, .anp[data-forced-gui-mode=dark] [type=submit]:hover, .anp[data-forced-gui-mode=dark] [type=button]:hover, .anp[data-forced-gui-mode=dark] [type=reset]:hover, .anp[data-forced-gui-mode=dark] [role=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] a[href]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [data-role=link]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] button:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=submit]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=reset]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [role=button]:hover {
|
||||
color: #d98b4b; }
|
||||
.anp[data-forced-gui-mode=dark] button, .anp[data-forced-gui-mode=dark] [type=submit], .anp[data-forced-gui-mode=dark] [type=button], .anp[data-forced-gui-mode=dark] [type=reset], .anp[data-forced-gui-mode=dark] [role=button], .anp[data-forced-gui-mode=default][data-gui-mode=dark] button, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=submit], .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=button], .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=reset], .anp[data-forced-gui-mode=default][data-gui-mode=dark] [role=button] {
|
||||
border-color: #4b8bd9;
|
||||
box-shadow: 0em 0em 0.2em inset #4b8bd9; }
|
||||
.anp[data-forced-gui-mode=dark] button:hover, .anp[data-forced-gui-mode=dark] [type=submit]:hover, .anp[data-forced-gui-mode=dark] [type=button]:hover, .anp[data-forced-gui-mode=dark] [type=reset]:hover, .anp[data-forced-gui-mode=dark] [role=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] button:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=submit]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=reset]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [role=button]:hover {
|
||||
border-color: #d98b4b;
|
||||
box-shadow: 0em 0em 0.2em inset #d98b4b; }
|
||||
.anp[data-forced-gui-mode=light], .anp[data-forced-gui-mode=default][data-gui-mode=light] {
|
||||
background-color: #EFEFEF;
|
||||
color: #222; }
|
||||
.anp[data-forced-gui-mode=light] a[href], .anp[data-forced-gui-mode=light] [data-role=link], .anp[data-forced-gui-mode=light] button, .anp[data-forced-gui-mode=light] [type=submit], .anp[data-forced-gui-mode=light] [type=button], .anp[data-forced-gui-mode=light] [type=reset], .anp[data-forced-gui-mode=light] [role=button], .anp[data-forced-gui-mode=default][data-gui-mode=light] a[href], .anp[data-forced-gui-mode=default][data-gui-mode=light] [data-role=link], .anp[data-forced-gui-mode=default][data-gui-mode=light] button, .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=submit], .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=button], .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=reset], .anp[data-forced-gui-mode=default][data-gui-mode=light] [role=button] {
|
||||
color: #2262b0; }
|
||||
.anp[data-forced-gui-mode=light] a[href]:hover, .anp[data-forced-gui-mode=light] [data-role=link]:hover, .anp[data-forced-gui-mode=light] button:hover, .anp[data-forced-gui-mode=light] [type=submit]:hover, .anp[data-forced-gui-mode=light] [type=button]:hover, .anp[data-forced-gui-mode=light] [type=reset]:hover, .anp[data-forced-gui-mode=light] [role=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] a[href]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [data-role=link]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] button:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=submit]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=reset]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [role=button]:hover {
|
||||
color: #b06222; }
|
||||
.anp[data-forced-gui-mode=light] button, .anp[data-forced-gui-mode=light] [type=submit], .anp[data-forced-gui-mode=light] [type=button], .anp[data-forced-gui-mode=light] [type=reset], .anp[data-forced-gui-mode=light] [role=button], .anp[data-forced-gui-mode=default][data-gui-mode=light] button, .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=submit], .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=button], .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=reset], .anp[data-forced-gui-mode=default][data-gui-mode=light] [role=button] {
|
||||
border-color: #2262b0;
|
||||
box-shadow: 0em 0em 0.2em inset #2262b0; }
|
||||
.anp[data-forced-gui-mode=light] button:hover, .anp[data-forced-gui-mode=light] [type=submit]:hover, .anp[data-forced-gui-mode=light] [type=button]:hover, .anp[data-forced-gui-mode=light] [type=reset]:hover, .anp[data-forced-gui-mode=light] [role=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] button:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=submit]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=reset]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [role=button]:hover {
|
||||
border-color: #b06222;
|
||||
box-shadow: 0em 0em 0.2em inset #b06222; }
|
||||
.anp .aichat {
|
||||
position: absolute;
|
||||
top: 0em;
|
||||
left: 0em;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
box-sizing: border-box; }
|
||||
.anp .aichat > legend {
|
||||
padding-bottom: .1em;
|
||||
border-bottom-width: .1em;
|
||||
border-bottom-style: solid; }
|
||||
.anp .aichat fieldset {
|
||||
position: relative;
|
||||
border: none; }
|
||||
.anp .aichat legend {
|
||||
font-weight: 900; }
|
||||
.anp .aichat form {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
position: absolute;
|
||||
left: 0em;
|
||||
bottom: 0em;
|
||||
width: 100%;
|
||||
height: 5em;
|
||||
box-sizing: border-box; }
|
||||
.anp .aichat form > span {
|
||||
flex-grow: 0; }
|
||||
.anp .aichat form > button {
|
||||
flex-grow: 0; }
|
||||
.anp .aichat .messages {
|
||||
position: absolute;
|
||||
top: 0em;
|
||||
left: 0em;
|
||||
bottom: 5em;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow: auto; }
|
||||
.anp .aichat .message {
|
||||
clear: both;
|
||||
margin: 2em 0em; }
|
||||
.anp .aichat .message legend {
|
||||
float: left;
|
||||
width: 100%;
|
||||
padding: 0em;
|
||||
margin-bottom: .75em; }
|
||||
.anp .aichat .message nav {
|
||||
position: absolute;
|
||||
top: 0em;
|
||||
right: 0em; }
|
||||
.anp .aichat .message pre {
|
||||
width: 100%;
|
||||
white-space: pre-wrap; }
|
||||
.anp .aichat .message-box {
|
||||
display: none; }
|
||||
.anp .aichat .view.buttons button {
|
||||
opacity: .5; }
|
||||
.anp .aichat .view.buttons button [data-icon]::before {
|
||||
margin: 0em; }
|
||||
.anp .aichat .view.buttons button [data-i18n] {
|
||||
display: none; }
|
||||
.anp .aichat [data-mode=html] .html-content,
|
||||
.anp .aichat [data-mode=raw_html] .raw-html-content,
|
||||
.anp .aichat [data-mode=md] .md-content {
|
||||
display: block; }
|
||||
.anp .aichat [data-mode=html] button[data-i18n=html],
|
||||
.anp .aichat [data-mode=raw_html] button[data-i18n=raw_html],
|
||||
.anp .aichat [data-mode=md] button[data-i18n=md] {
|
||||
opacity: 1; }
|
||||
.anp .aichat .data {
|
||||
position: absolute;
|
||||
right: 0em;
|
||||
bottom: 0em;
|
||||
margin: 0em;
|
||||
padding: 0em;
|
||||
list-style-type: none;
|
||||
opacity: .85; }
|
||||
.anp .aichat .data li {
|
||||
display: inline-block;
|
||||
margin: 0em .5em;
|
||||
font-size: .75em; }
|
||||
.anp .aichat .data li [data-i18n] {
|
||||
display: none; }
|
||||
.anp .aichat .data [data-name=date_from] {
|
||||
display: none; }
|
||||
|
||||
/*# sourceMappingURL=AnP.css.map */
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1,4 +1,5 @@
|
||||
.anp{
|
||||
|
||||
[data-icon]::before{
|
||||
margin-right : .3em;
|
||||
font-family : $font-icon;
|
||||
@ -13,4 +14,10 @@
|
||||
[data-icon=zoom]::before{content : unicode("f002");}
|
||||
[data-icon=reset_zoom]::before{content : unicode("f689");}
|
||||
[data-icon=gui_mode]::before{content : unicode("f043");}
|
||||
|
||||
// Icons for AI Chat
|
||||
[data-icon=html]::before{content : unicode("f13b"); font-family : "FA6FB";}
|
||||
[data-icon=raw_html]::before{content : unicode("f121");}
|
||||
[data-icon=md]::before{content : unicode("f1c9");}
|
||||
|
||||
}
|
||||
@ -1 +1 @@
|
||||
@import "AnP.fonts.scss", "AnP.settings.scss", "AnP.common.scss", "AnP.base.scss", "AnP.icons.scss";
|
||||
@import "AnP.fonts.scss", "AnP.settings.scss", "AnP.common.scss", "AnP.base.scss", "AnP.icons.scss", "AnP.aichat.scss";
|
||||
@ -30,6 +30,8 @@ class AIInterpretersAbstract(ABC):
|
||||
self.pool:str = self.anp.settings.get(("ai_interpreter_pool", "ai_pool", "pool"), inputs, self.key)
|
||||
self.sessions:dict[int, list[int]] = {}
|
||||
self.sessions_i:int = 0
|
||||
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)
|
||||
|
||||
def start(self:Self) -> None:
|
||||
pass
|
||||
@ -47,7 +49,7 @@ class AIInterpretersAbstract(ABC):
|
||||
return id, self.sessions[id]
|
||||
|
||||
def save_context(self:Self, id:int, context:list[int]) -> None:
|
||||
if id in self.sessions:
|
||||
if self.allow_contexts and id in self.sessions:
|
||||
self.sessions[id] = context
|
||||
|
||||
def close_session(self:Self, id:int) -> bool:
|
||||
@ -56,12 +58,15 @@ class AIInterpretersAbstract(ABC):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_orders(self:Self, orders:str|list[str]) -> str:
|
||||
def get_context(self:Self, id:int) -> list[int]:
|
||||
return self.sessions[id] if id in self.sessions else []
|
||||
|
||||
def get_orders(self:Self, orders:Optional[str|list[str]] = None) -> str:
|
||||
|
||||
results:str = ""
|
||||
i:int = 0
|
||||
|
||||
for block in (orders, self.orders):
|
||||
for block in (self.orders, orders):
|
||||
if block:
|
||||
if Check.is_array(block):
|
||||
|
||||
@ -73,10 +78,13 @@ class AIInterpretersAbstract(ABC):
|
||||
else:
|
||||
results += ("\n\n" if results else "") + str(block).strip()
|
||||
|
||||
return results
|
||||
|
||||
@abstractmethod
|
||||
def request(self:Self,
|
||||
session:int|None,
|
||||
message:str,
|
||||
orders:list[str] = [],
|
||||
callback:Optional[Callable[[int, AIResponseModel], None]] = None
|
||||
callback:Optional[Callable[[int, AIResponseModel], None]] = None,
|
||||
context:list[int] = []
|
||||
) -> tuple[int|None, AIResponseModel]:pass
|
||||
@ -9,8 +9,11 @@ from Utils.Common import Common
|
||||
|
||||
class WebSocketServersAbstract(ABC):
|
||||
|
||||
def __init__(self:Self, anp:AnPInterface, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
|
||||
def __init__(self:Self, anp:AnPInterface, key:str, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
|
||||
self.anp:AnPInterface = anp
|
||||
self.key:str = key
|
||||
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.on_new_client:Event = Event()
|
||||
self.on_message:Event = Event()
|
||||
self.on_close:Event = Event()
|
||||
@ -25,14 +28,15 @@ class WebSocketServersAbstract(ABC):
|
||||
@abstractmethod
|
||||
def close_client(self:Self, id:int) -> None:pass
|
||||
|
||||
def format_data(self:Self, controller:str, method:str, data:Any|None, id:str, code:int = 200) -> str:
|
||||
def format_data(self:Self, controller:str, action:str, data:Any|None, id:str, code:int = 200) -> str:
|
||||
return Common.data_encode({
|
||||
"ok" : code >= 200 and code < 300,
|
||||
"code" : code,
|
||||
"controller": controller,
|
||||
"method": method,
|
||||
"action": action,
|
||||
"data": data,
|
||||
"id": id,
|
||||
"code": code
|
||||
"id": id
|
||||
})
|
||||
|
||||
@abstractmethod
|
||||
def send(self:Self, controller:str, method:str, data:Any|None, ids:Optional[int|Sequence[int]] = 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
|
||||
@ -10,25 +10,51 @@ from Models.PseudoLoRAModel import PseudoLoRAModel
|
||||
class AIController(ControllerAbstract, ModelAbstract):
|
||||
|
||||
def __test_execution(self:Self, end:Callable[[], None], request:RequestModel) -> None:
|
||||
print("PASA")
|
||||
self.anp.ai_interpreters.request(
|
||||
"anp_titles",
|
||||
"anp_responses",
|
||||
None,
|
||||
request.get("message", "Hola, Gemma. ¿Me puedes ayudar a instalar una impresora Canon?"),
|
||||
lambda id, response: print((id, response.response)),
|
||||
[
|
||||
"Seleccionar títulos exactos relacionados con la consulta:" + "".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"
|
||||
]
|
||||
),
|
||||
]
|
||||
# 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:
|
||||
self.anp.queues.add("anp", self.__test_execution, request)
|
||||
request.set_response({
|
||||
"ok" : True,
|
||||
"code" : 200,
|
||||
"message" : "ok"
|
||||
})
|
||||
|
||||
def __message_execution(self:Self, end:Callable[[], None], request:RequestModel) -> None:
|
||||
self.anp.ai_interpreters.request(
|
||||
"anp_responses",
|
||||
None,
|
||||
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()
|
||||
|
||||
def message(self:Self, request:RequestModel) -> None:
|
||||
self.anp.queues.add("anp", self.__message_execution, request)
|
||||
request.set_response({
|
||||
"ok" : True,
|
||||
"code" : 200,
|
||||
|
||||
@ -24,7 +24,8 @@ class OllamaDriver(AIInterpretersAbstract, ModelAbstract):
|
||||
session:int|None,
|
||||
message:str,
|
||||
callback:Optional[Callable[[int, AIResponseModel], None]] = None,
|
||||
orders:str|list[str] = []
|
||||
orders:str|list[str] = [],
|
||||
custom_context:Optional[list[int]] = None
|
||||
) -> tuple[int|None, AIResponseModel]:
|
||||
|
||||
response:Response
|
||||
@ -38,14 +39,18 @@ class OllamaDriver(AIInterpretersAbstract, ModelAbstract):
|
||||
if self.maximum_tokens_per_session is not None:
|
||||
options["num_ctx"] = self.maximum_tokens_per_session
|
||||
|
||||
session, context = self.get_session(session)
|
||||
session, context = custom_context or self.get_session(session)
|
||||
orders = self.get_orders(orders)
|
||||
|
||||
if custom_context:
|
||||
context = context.copy()
|
||||
|
||||
with Post(self.url, json = {
|
||||
"model" : self.model,
|
||||
"prompt": message,
|
||||
**({"system": orders} if len(orders) else {}),
|
||||
"stream": self.stream,
|
||||
"think" : self.think,
|
||||
**(
|
||||
{"format" : self.format} if (
|
||||
self.format == "json" or
|
||||
@ -65,8 +70,9 @@ class OllamaDriver(AIInterpretersAbstract, ModelAbstract):
|
||||
chunk:bytes
|
||||
|
||||
for chunk in response.iter_lines():
|
||||
if not self.anp.working():
|
||||
break
|
||||
if chunk:
|
||||
print(Common.json_decode(chunk))
|
||||
results.update(Common.json_decode(chunk))
|
||||
if results.done:
|
||||
self.save_context(session, results.context)
|
||||
|
||||
@ -12,19 +12,17 @@ from Utils.Checks import Check
|
||||
|
||||
class WebSocketServerDriver(WebSocketServersAbstract, ModelAbstract):
|
||||
|
||||
def __init__(self:Self, anp:AnPInterface, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
|
||||
super().__init__(anp, inputs)
|
||||
def __init__(self:Self, anp:AnPInterface, key:str, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
|
||||
super().__init__(anp, key, inputs)
|
||||
|
||||
self.__server:WebSocketServer
|
||||
self.__clients:dict[str, WebSocketClient] = {}
|
||||
self.__host:str = anp.settings.get(("web_socket_server_host", "host"), inputs, "")
|
||||
self.__port:int = anp.settings.get(("web_socket_server_port", "port"), inputs, 8765)
|
||||
self.__thread:Thread = None
|
||||
|
||||
anp.settings.get(("web_socket_server_autostart", "autostart"), inputs, True) and self.start()
|
||||
|
||||
def __run(self:Self) -> None:
|
||||
self.__server = server_serve(self.__handler, self.__host, self.__port)
|
||||
self.__server = server_serve(self.__handler, self.host, self.port)
|
||||
self.__server.serve_forever()
|
||||
|
||||
def start(self:Self) -> None:
|
||||
@ -40,27 +38,34 @@ class WebSocketServerDriver(WebSocketServersAbstract, ModelAbstract):
|
||||
|
||||
self.__server.shutdown()
|
||||
|
||||
def close_client(self:Self, id:int) -> None:
|
||||
if id in self.__clients:
|
||||
try:
|
||||
self.__clients[id].close()
|
||||
except Exception as exception:
|
||||
self.anp.exception(exception, "web_socket_server_client_close_exception", {
|
||||
"client": id,
|
||||
def close_client(self:Self, ids:Optional[str|list[str]] = None) -> None:
|
||||
|
||||
id:str
|
||||
|
||||
for id in (
|
||||
ids if Check.is_array(ids) else
|
||||
list(self.__clients.keys()) if ids is None else
|
||||
[ids] if Check.is_string(ids) else
|
||||
[]):
|
||||
if id in self.__clients:
|
||||
try:
|
||||
self.__clients[id].close()
|
||||
except Exception as exception:
|
||||
self.anp.exception(exception, "web_socket_server_client_close_exception", {
|
||||
"id": id,
|
||||
"port": self.port,
|
||||
"host": self.host
|
||||
})
|
||||
del self.__clients[id]
|
||||
|
||||
def __handler(self:Self, client:WebSocketClient) -> None:
|
||||
|
||||
id:int = self.anp.unique_keys.create()
|
||||
id:str = self.anp.unique_keys.get()
|
||||
|
||||
self.__clients[id] = client
|
||||
self.on_new_client.execute(id)
|
||||
|
||||
self.anp.print("info", "web_socket_server_client_connected", {
|
||||
"client": id,
|
||||
"id": id,
|
||||
"port": self.port,
|
||||
"host": self.host,
|
||||
"client_host" : client.remote_address[0],
|
||||
@ -76,26 +81,30 @@ class WebSocketServerDriver(WebSocketServersAbstract, ModelAbstract):
|
||||
break
|
||||
self.on_message.execute(id, message)
|
||||
except Exception as exception:
|
||||
self.anp.exception(exception, "web_socket_server_client_exception", {
|
||||
"client": id,
|
||||
self.anp.working() and self.anp.exception(exception, "web_socket_server_client_exception", {
|
||||
"id": id,
|
||||
"port": self.port,
|
||||
"host": self.host
|
||||
})
|
||||
self.on_error.execute(id, exception)
|
||||
finally:
|
||||
self.close_client(id)
|
||||
try:
|
||||
self.__clients[id].close()
|
||||
except Exception as _:
|
||||
pass
|
||||
del self.__clients[id]
|
||||
self.anp.unique_keys.remove(id)
|
||||
self.on_close.execute(id)
|
||||
self.anp.print("info", "web_socket_server_client_disconnected", {
|
||||
"client": id,
|
||||
"id": id,
|
||||
"port": self.port,
|
||||
"host": self.host
|
||||
})
|
||||
|
||||
def send(self:Self, controller:str, method:str, data:str, ids:Optional[int|Sequence[int]] = None, code:int = 200) -> bool:
|
||||
def send(self:Self, controller:str, action:str, data:str, ids:Optional[str|Sequence[str]] = None, code:int = 200) -> bool:
|
||||
|
||||
success:bool = True
|
||||
id:int
|
||||
id:str
|
||||
|
||||
for id in (
|
||||
list(self.__clients.keys()) if ids is None else
|
||||
@ -103,10 +112,10 @@ class WebSocketServerDriver(WebSocketServersAbstract, ModelAbstract):
|
||||
[ids]):
|
||||
if id in self.__clients:
|
||||
try:
|
||||
self.__clients[id].send(self.format_data(controller, method, data, id, code))
|
||||
self.__clients[id].send(self.format_data(controller, action, data, id, code))
|
||||
except Exception as exception:
|
||||
self.anp.exception(exception, "web_socket_server_client_send_exception", {
|
||||
"client": id,
|
||||
"id": id,
|
||||
"port": self.port,
|
||||
"host": self.host
|
||||
})
|
||||
|
||||
@ -49,10 +49,10 @@ class WebSocketServersManager:
|
||||
|
||||
self.__web_sockets[name] = web_socket
|
||||
|
||||
web_socket.on_new_client.add(lambda id:self.on_new_client(web_socket, id, name))
|
||||
web_socket.on_message.add(lambda id, message:self.on_message(web_socket, id, message, name))
|
||||
web_socket.on_close.add(lambda id:self.on_close(web_socket, id, name))
|
||||
web_socket.on_error.add(lambda id, exception:self.on_error(web_socket, id, exception, name))
|
||||
web_socket.on_new_client.add(lambda id:self.on_new_client.execute(web_socket, id, name))
|
||||
web_socket.on_message.add(lambda id, message:self.on_message.execute(web_socket, id, message, name))
|
||||
web_socket.on_close.add(lambda id:self.on_close.execute(web_socket, id, name))
|
||||
web_socket.on_error.add(lambda id, exception:self.on_error.execute(web_socket, id, exception, name))
|
||||
|
||||
def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
|
||||
|
||||
@ -77,7 +77,7 @@ class WebSocketServersManager:
|
||||
|
||||
Class:type[WebSocketServersAbstract] = self.anp.models.get(WebSocketServersAbstract, _type)
|
||||
|
||||
Class and issubclass(Class, WebSocketServersAbstract) and self.__set(key, Class(self.anp, value))
|
||||
Class and issubclass(Class, WebSocketServersAbstract) and self.__set(key, Class(self.anp, key, value))
|
||||
|
||||
def remove(self:Self, names:str|Sequence[str]) -> None:
|
||||
for name in Common.get_keys(names):
|
||||
@ -94,20 +94,14 @@ class WebSocketServersManager:
|
||||
def send(self:Self,
|
||||
name:str,
|
||||
controller:str,
|
||||
method:str,
|
||||
action:str,
|
||||
data:Optional[Any] = None,
|
||||
clients:Optional[int|Sequence[int]] = None,
|
||||
clients:Optional[str|Sequence[str]] = None,
|
||||
code:int = 200
|
||||
) -> None:
|
||||
if name in self.__web_sockets:
|
||||
try:
|
||||
self.__web_sockets[name].send(Common.data_encode({
|
||||
"ok" : code >= 200 and code < 300,
|
||||
"code" : code,
|
||||
"controller" : controller,
|
||||
"method" : method,
|
||||
"data" : data
|
||||
}), clients)
|
||||
self.__web_sockets[name].send(controller, action, data, clients, code)
|
||||
except Exception as exception:
|
||||
self.anp.exception(exception, "web_socket_server_send_exception", {"name": name})
|
||||
|
||||
@ -115,10 +109,12 @@ class WebSocketServersManager:
|
||||
|
||||
data:dict[str, Any|None] = Common.data_decode(raw_data)
|
||||
|
||||
if "controller" in data and "method" in data:
|
||||
if "controller" in data and "action" in data:
|
||||
|
||||
request:RequestModel = RequestModel()
|
||||
|
||||
"data" in data and Check.is_dictionary(data["data"]) and request.set_variables(data["data"])
|
||||
|
||||
request.data = data.get("data", None)
|
||||
request.variables["web_socket"] = web_socket
|
||||
request.variables["client_id"] = client
|
||||
@ -126,6 +122,6 @@ class WebSocketServersManager:
|
||||
|
||||
self.anp.controllers.execute(
|
||||
data["controller"],
|
||||
data["method"],
|
||||
data["action"],
|
||||
request
|
||||
)
|
||||
@ -1,8 +1,9 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Any, Self, Sequence
|
||||
from typing import Any, Self
|
||||
from re import Pattern as REPattern
|
||||
from json import loads as json_decode
|
||||
from Utils.Patterns import RE
|
||||
|
||||
class Check:
|
||||
@ -10,6 +11,10 @@ class Check:
|
||||
@staticmethod
|
||||
def is_string(item:Any|None) -> bool:
|
||||
return isinstance(item, str)
|
||||
|
||||
@staticmethod
|
||||
def is_binary(item:Any|None) -> bool:
|
||||
return isinstance(item, bytes)
|
||||
|
||||
@classmethod
|
||||
def is_key(cls:type[Self], item:Any|None) -> bool:
|
||||
@ -37,4 +42,18 @@ class Check:
|
||||
|
||||
@staticmethod
|
||||
def is_regular_expression(item:Any|None) -> bool:
|
||||
return isinstance(item, REPattern)
|
||||
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
|
||||
if Check.is_string(item) and len(item = item.strip()) >= 2 and item[0] + item[-1] in ("{}", "[]"):
|
||||
if not full:
|
||||
return True
|
||||
try:
|
||||
if json_decode(item) is not None:
|
||||
return True
|
||||
except Exception as _:
|
||||
pass
|
||||
return False
|
||||
@ -105,7 +105,7 @@ class Common:
|
||||
key:str
|
||||
value:Any|None
|
||||
|
||||
for key, value in cls.get_dictionaries(subinputs).items():
|
||||
for key, value in cls.get_dictionary(subinputs).items():
|
||||
if overwrite or key not in dictionary:
|
||||
dictionary[key] = value
|
||||
|
||||
@ -286,7 +286,13 @@ class Common:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def json_decode(data:str) -> dict[str, Any|None]|Sequence[Any|None]|None:
|
||||
def json_decode(data:str|bytes) -> dict[str, Any|None]|Sequence[Any|None]|None:
|
||||
if Check.is_binary(data):
|
||||
for charset in ("utf-8", "latin1"):
|
||||
try:
|
||||
return json_decode(data.decode(charset))
|
||||
except Exception as exception:
|
||||
pass
|
||||
try:
|
||||
return json_decode(data)
|
||||
except Exception as exception:
|
||||
@ -303,10 +309,11 @@ class Common:
|
||||
|
||||
@staticmethod
|
||||
def base64_decode(data:str) -> bytes|None:
|
||||
try:
|
||||
return base64_decode(data.encode("utf-8"))
|
||||
except Exception as exception:
|
||||
pass
|
||||
if Check.is_binary(data) or Check.is_string(data):
|
||||
try:
|
||||
return base64_decode(data)
|
||||
except Exception as exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
@ -319,9 +326,11 @@ class Common:
|
||||
|
||||
@classmethod
|
||||
def data_decode(cls:type[Self], data:str) -> Any|None:
|
||||
if Check.is_string(data):
|
||||
data = cls.base64_decode(data)
|
||||
if Check.is_string(data) or Check.is_binary(data):
|
||||
if Check.is_string(data):
|
||||
data = data.encode("latin1").decode("utf-8")
|
||||
data = cls.base64_decode(data)
|
||||
if Check.is_string(data) or Check.is_binary(data):
|
||||
try:
|
||||
return cls.json_decode(data)
|
||||
except Exception as exception:
|
||||
@ -333,8 +342,8 @@ class Common:
|
||||
if Check.is_function(callback):
|
||||
try:
|
||||
return callback(*arguments)
|
||||
except Exception as _:
|
||||
pass
|
||||
except Exception as exception:
|
||||
print(exception)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
|
||||
Loading…
Reference in New Issue
Block a user