#wip: Web Service communication.
This commit is contained in:
parent
178f678b69
commit
f1371e7389
@ -71,7 +71,7 @@
|
|||||||
"default_web_sockets_servers2" : {
|
"default_web_sockets_servers2" : {
|
||||||
"anp" : {
|
"anp" : {
|
||||||
"type" : "WebSocketServerDriver",
|
"type" : "WebSocketServerDriver",
|
||||||
"host" : "localhost",
|
"host" : "",
|
||||||
"port" : 18765
|
"port" : 18765
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import {SessionsManager} from "../Managers/SessionsManager.ecma.js";
|
|||||||
import {ModelsManager} from "../Managers/ModelsManager.ecma.js";
|
import {ModelsManager} from "../Managers/ModelsManager.ecma.js";
|
||||||
import {ViewsManager} from "../Managers/ViewsManager.ecma.js";
|
import {ViewsManager} from "../Managers/ViewsManager.ecma.js";
|
||||||
import {RoutesManager} from "../Managers/RoutesManager.ecma.js";
|
import {RoutesManager} from "../Managers/RoutesManager.ecma.js";
|
||||||
|
import {WebSocketsClientsManager} from "../Managers/WebSocketsClientsManager.ecma.js";
|
||||||
import {Components} from "./Components.ecma.js";
|
import {Components} from "./Components.ecma.js";
|
||||||
import {Common} from "../Utils/Common.ecma.js";
|
import {Common} from "../Utils/Common.ecma.js";
|
||||||
import {Check} from "../Utils/Checks.ecma.js";
|
import {Check} from "../Utils/Checks.ecma.js";
|
||||||
@ -73,6 +74,8 @@ export const AnP = (function(){
|
|||||||
this.views = new ViewsManager(self);
|
this.views = new ViewsManager(self);
|
||||||
/** @type {RoutesManager} */
|
/** @type {RoutesManager} */
|
||||||
this.routes = new RoutesManager(self);
|
this.routes = new RoutesManager(self);
|
||||||
|
/** @type {WebSocketsClientsManager} */
|
||||||
|
this.web_sockets_clients = new WebSocketsClientsManager(self);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
|
|||||||
@ -28,6 +28,8 @@ export const AIChatComponent = (function(){
|
|||||||
|
|
||||||
/** @type {AIChatComponent} */
|
/** @type {AIChatComponent} */
|
||||||
const self = this;
|
const self = this;
|
||||||
|
/** @type {string|null} */
|
||||||
|
let web_socket_id = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
@ -39,6 +41,8 @@ export const AIChatComponent = (function(){
|
|||||||
|
|
||||||
const name = Common.get_value("name", inputs, "aichat");
|
const name = Common.get_value("name", inputs, "aichat");
|
||||||
|
|
||||||
|
web_socket_id = anp.web_sockets_clients.add("AAA", "https://localhost:18000/");
|
||||||
|
|
||||||
return Fieldset({class : "aichat"}, [
|
return Fieldset({class : "aichat"}, [
|
||||||
anp.components.i18n(name, "legend"),
|
anp.components.i18n(name, "legend"),
|
||||||
Section({class : "messages"}),
|
Section({class : "messages"}),
|
||||||
@ -66,7 +70,8 @@ export const AIChatComponent = (function(){
|
|||||||
if(text){
|
if(text){
|
||||||
Common.HTML(".aichat .messages", Fieldset({
|
Common.HTML(".aichat .messages", Fieldset({
|
||||||
class : "message",
|
class : "message",
|
||||||
data_type : "user"
|
data_type : "user",
|
||||||
|
data_id : anp.unique_keys.create()
|
||||||
}, [
|
}, [
|
||||||
anp.components.i18n("user", "legend"),
|
anp.components.i18n("user", "legend"),
|
||||||
Div({class : "content"}, text)
|
Div({class : "content"}, text)
|
||||||
@ -81,6 +86,27 @@ export const AIChatComponent = (function(){
|
|||||||
event.key == "Enter" && !event.shiftKey && send(item, event);
|
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;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
constructor();
|
constructor();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
65
Public/ecma/Managers/WebSocketsClientsManager.ecma.js
Normal file
65
Public/ecma/Managers/WebSocketsClientsManager.ecma.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
import {WebSocketClientModel} from "../Models/WebSocketClientModel.ecma.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import("../Application/AnP.ecma.js").AnP} AnP
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class WebSocketsClientsManager
|
||||||
|
* @constructor
|
||||||
|
* @param {!AnP} anp
|
||||||
|
* @param {!string} key
|
||||||
|
* @param {!(Object.<string, any|null>|Array.<any|null>)} inputs
|
||||||
|
* @returns {void}
|
||||||
|
* @access private
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
export const WebSocketsClientsManager = (function(){
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @constructs WebSocketsClientsManager
|
||||||
|
* @param {!AnP} anp
|
||||||
|
* @param {!string} key
|
||||||
|
* @param {!(Object.<string, any|null>|Array.<any|null>)} inputs
|
||||||
|
* @returns {void}
|
||||||
|
* @access private
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
const WebSocketsClientsManager = function(anp, key, inputs){
|
||||||
|
|
||||||
|
/** @type {WebSocketsClientsManager} */
|
||||||
|
const self = this,
|
||||||
|
/** @type {Object.<string, WebSocketClientModel} */
|
||||||
|
clients = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {void}
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
const constructor = () => {};
|
||||||
|
|
||||||
|
this.add = (key, url) => {
|
||||||
|
|
||||||
|
clients[key] = new WebSocketClientModel(anp, url);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.remove = key => {
|
||||||
|
if(key in clients){
|
||||||
|
clients[key].close();
|
||||||
|
delete clients[key];
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.send = (key, data) => {};
|
||||||
|
|
||||||
|
constructor();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
return WebSocketsClientsManager;
|
||||||
|
})();
|
||||||
92
Public/ecma/Models/WebSocketClientModel.ecma.js
Normal file
92
Public/ecma/Models/WebSocketClientModel.ecma.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
"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;
|
||||||
|
})();
|
||||||
@ -18,7 +18,7 @@ class AIInterpretersAbstract(ABC):
|
|||||||
self.key:str = key
|
self.key:str = key
|
||||||
self.url:str = self.anp.settings.get(("ai_interpreter_url", "ai_url", "url"), inputs, "")
|
self.url:str = self.anp.settings.get(("ai_interpreter_url", "ai_url", "url"), inputs, "")
|
||||||
self.stream:bool = self.anp.settings.get(("ai_interpreter_stream", "ai_stream", "stream"), inputs, False)
|
self.stream:bool = self.anp.settings.get(("ai_interpreter_stream", "ai_stream", "stream"), inputs, False)
|
||||||
self.orders:list[str] = self.anp.settings.get(("ai_interpreter_orders", "ai_orders", "orders"), inputs, [])
|
self.orders:str|list[str] = self.anp.settings.get(("ai_interpreter_orders", "ai_orders", "orders"), inputs, [])
|
||||||
self.maximum_tokens_per_session:int = self.anp.settings.get((
|
self.maximum_tokens_per_session:int = self.anp.settings.get((
|
||||||
"ai_interpreter_maximum_tokens_per_session", "ai_maximum_tokens_per_session", "maximum_tokens_per_session"
|
"ai_interpreter_maximum_tokens_per_session", "ai_maximum_tokens_per_session", "maximum_tokens_per_session"
|
||||||
), inputs, None)
|
), inputs, None)
|
||||||
@ -56,6 +56,23 @@ class AIInterpretersAbstract(ABC):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_orders(self:Self, orders:str|list[str]) -> str:
|
||||||
|
|
||||||
|
results:str = ""
|
||||||
|
i:int = 0
|
||||||
|
|
||||||
|
for block in (orders, self.orders):
|
||||||
|
if block:
|
||||||
|
if Check.is_array(block):
|
||||||
|
|
||||||
|
block_string:str = "".join(str(i + 1 + j) + ". " + str(order) + "\n" for j, order in enumerate(block))
|
||||||
|
|
||||||
|
i += len(block)
|
||||||
|
results += ("\n\n" if results else "") + block_string.strip()
|
||||||
|
|
||||||
|
else:
|
||||||
|
results += ("\n\n" if results else "") + str(block).strip()
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def request(self:Self,
|
def request(self:Self,
|
||||||
session:int|None,
|
session:int|None,
|
||||||
|
|||||||
@ -5,6 +5,7 @@ from typing import Any, Self, Optional, Sequence
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from Interfaces.Application.AnPInterface import AnPInterface
|
from Interfaces.Application.AnPInterface import AnPInterface
|
||||||
from Application.Event import Event
|
from Application.Event import Event
|
||||||
|
from Utils.Common import Common
|
||||||
|
|
||||||
class WebSocketServersAbstract(ABC):
|
class WebSocketServersAbstract(ABC):
|
||||||
|
|
||||||
@ -24,5 +25,14 @@ class WebSocketServersAbstract(ABC):
|
|||||||
@abstractmethod
|
@abstractmethod
|
||||||
def close_client(self:Self, id:int) -> None:pass
|
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:
|
||||||
|
return Common.data_encode({
|
||||||
|
"controller": controller,
|
||||||
|
"method": method,
|
||||||
|
"data": data,
|
||||||
|
"id": id,
|
||||||
|
"code": code
|
||||||
|
})
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def send(self:Self, data:Any|None, ids:Optional[int|Sequence[int]] = None) -> None:pass
|
def send(self:Self, controller:str, method:str, data:Any|None, ids:Optional[int|Sequence[int]] = None, code:int = 200) -> None:pass
|
||||||
@ -24,7 +24,7 @@ class OllamaDriver(AIInterpretersAbstract, ModelAbstract):
|
|||||||
session:int|None,
|
session:int|None,
|
||||||
message:str,
|
message:str,
|
||||||
callback:Optional[Callable[[int, AIResponseModel], None]] = None,
|
callback:Optional[Callable[[int, AIResponseModel], None]] = None,
|
||||||
orders:list[str] = []
|
orders:str|list[str] = []
|
||||||
) -> tuple[int|None, AIResponseModel]:
|
) -> tuple[int|None, AIResponseModel]:
|
||||||
|
|
||||||
response:Response
|
response:Response
|
||||||
@ -38,15 +38,13 @@ class OllamaDriver(AIInterpretersAbstract, ModelAbstract):
|
|||||||
if self.maximum_tokens_per_session is not None:
|
if self.maximum_tokens_per_session is not None:
|
||||||
options["num_ctx"] = self.maximum_tokens_per_session
|
options["num_ctx"] = self.maximum_tokens_per_session
|
||||||
|
|
||||||
orders += self.orders
|
|
||||||
session, context = self.get_session(session)
|
session, context = self.get_session(session)
|
||||||
|
orders = self.get_orders(orders)
|
||||||
|
|
||||||
with Post(self.url, json = {
|
with Post(self.url, json = {
|
||||||
"model" : self.model,
|
"model" : self.model,
|
||||||
"prompt": message,
|
"prompt": message,
|
||||||
**({"system": "\\n".join(
|
**({"system": orders} if len(orders) else {}),
|
||||||
str(i + 1) + ". " + order for i, order in enumerate(orders, 1)
|
|
||||||
)} if len(orders) else {}),
|
|
||||||
"stream": self.stream,
|
"stream": self.stream,
|
||||||
**(
|
**(
|
||||||
{"format" : self.format} if (
|
{"format" : self.format} if (
|
||||||
|
|||||||
@ -16,10 +16,9 @@ class WebSocketServerDriver(WebSocketServersAbstract, ModelAbstract):
|
|||||||
super().__init__(anp, inputs)
|
super().__init__(anp, inputs)
|
||||||
|
|
||||||
self.__server:WebSocketServer
|
self.__server:WebSocketServer
|
||||||
self.__clients:dict[int, WebSocketClient] = {}
|
self.__clients:dict[str, WebSocketClient] = {}
|
||||||
self.__host:str = anp.settings.get(("web_socket_server_host", "host"), inputs, "")
|
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.__port:int = anp.settings.get(("web_socket_server_port", "port"), inputs, 8765)
|
||||||
self.__client_i:int = 0
|
|
||||||
self.__thread:Thread = None
|
self.__thread:Thread = None
|
||||||
|
|
||||||
anp.settings.get(("web_socket_server_autostart", "autostart"), inputs, True) and self.start()
|
anp.settings.get(("web_socket_server_autostart", "autostart"), inputs, True) and self.start()
|
||||||
@ -55,9 +54,7 @@ class WebSocketServerDriver(WebSocketServersAbstract, ModelAbstract):
|
|||||||
|
|
||||||
def __handler(self:Self, client:WebSocketClient) -> None:
|
def __handler(self:Self, client:WebSocketClient) -> None:
|
||||||
|
|
||||||
id:int = self.__client_i
|
id:int = self.anp.unique_keys.create()
|
||||||
|
|
||||||
self.__client_i += 1
|
|
||||||
|
|
||||||
self.__clients[id] = client
|
self.__clients[id] = client
|
||||||
self.on_new_client.execute(id)
|
self.on_new_client.execute(id)
|
||||||
@ -70,6 +67,8 @@ class WebSocketServerDriver(WebSocketServersAbstract, ModelAbstract):
|
|||||||
"client_port" : client.remote_address[1]
|
"client_port" : client.remote_address[1]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
self.send("web_socket_client", "set_id", id, id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while self.anp.working():
|
while self.anp.working():
|
||||||
message:str = client.recv()
|
message:str = client.recv()
|
||||||
@ -85,6 +84,7 @@ class WebSocketServerDriver(WebSocketServersAbstract, ModelAbstract):
|
|||||||
self.on_error.execute(id, exception)
|
self.on_error.execute(id, exception)
|
||||||
finally:
|
finally:
|
||||||
self.close_client(id)
|
self.close_client(id)
|
||||||
|
self.anp.unique_keys.remove(id)
|
||||||
self.on_close.execute(id)
|
self.on_close.execute(id)
|
||||||
self.anp.print("info", "web_socket_server_client_disconnected", {
|
self.anp.print("info", "web_socket_server_client_disconnected", {
|
||||||
"client": id,
|
"client": id,
|
||||||
@ -92,15 +92,18 @@ class WebSocketServerDriver(WebSocketServersAbstract, ModelAbstract):
|
|||||||
"host": self.host
|
"host": self.host
|
||||||
})
|
})
|
||||||
|
|
||||||
def send(self:Self, data:str, ids:int|Sequence[int]) -> bool:
|
def send(self:Self, controller:str, method:str, data:str, ids:Optional[int|Sequence[int]] = None, code:int = 200) -> bool:
|
||||||
|
|
||||||
success:bool = True
|
success:bool = True
|
||||||
id:int
|
id:int
|
||||||
|
|
||||||
for id in ids if Check.is_array(ids) else [ids]:
|
for id in (
|
||||||
|
list(self.__clients.keys()) if ids is None else
|
||||||
|
ids if Check.is_array(ids) else
|
||||||
|
[ids]):
|
||||||
if id in self.__clients:
|
if id in self.__clients:
|
||||||
try:
|
try:
|
||||||
self.__clients[id].send(data)
|
self.__clients[id].send(self.format_data(controller, method, data, id, code))
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
self.anp.exception(exception, "web_socket_server_client_send_exception", {
|
self.anp.exception(exception, "web_socket_server_client_send_exception", {
|
||||||
"client": id,
|
"client": id,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user