unisi 0.3.13__py3-none-any.whl → 0.3.15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- unisi/__init__.py +1 -0
- unisi/llmrag.py +132 -41
- unisi/server.py +6 -2
- unisi/users.py +5 -5
- {unisi-0.3.13.dist-info → unisi-0.3.15.dist-info}/METADATA +6 -6
- {unisi-0.3.13.dist-info → unisi-0.3.15.dist-info}/RECORD +9 -9
- {unisi-0.3.13.dist-info → unisi-0.3.15.dist-info}/WHEEL +0 -0
- {unisi-0.3.13.dist-info → unisi-0.3.15.dist-info}/entry_points.txt +0 -0
- {unisi-0.3.13.dist-info → unisi-0.3.15.dist-info}/licenses/LICENSE +0 -0
unisi/__init__.py
CHANGED
unisi/llmrag.py
CHANGED
@@ -2,14 +2,125 @@
|
|
2
2
|
from .common import Unishare
|
3
3
|
from langchain_groq import ChatGroq
|
4
4
|
from langchain_openai import ChatOpenAI
|
5
|
+
from langchain_mistralai import ChatMistralAI
|
5
6
|
from langchain_google_genai import (
|
6
7
|
ChatGoogleGenerativeAI,
|
7
8
|
HarmBlockThreshold,
|
8
9
|
HarmCategory,
|
9
10
|
)
|
11
|
+
from datetime import datetime
|
12
|
+
import collections, inspect, re, json
|
13
|
+
from typing import get_origin, get_args
|
14
|
+
|
15
|
+
def jstype(type_value):
|
16
|
+
if isinstance(type_value, type):
|
17
|
+
if type_value == int:
|
18
|
+
return 'integer'
|
19
|
+
elif type_value == float:
|
20
|
+
return 'number'
|
21
|
+
elif type_value == bool:
|
22
|
+
return 'boolean'
|
23
|
+
elif type_value == str:
|
24
|
+
return 'string'
|
25
|
+
elif type_value == dict:
|
26
|
+
return 'object'
|
27
|
+
elif type_value == list:
|
28
|
+
return 'array'
|
29
|
+
else:
|
30
|
+
origin = get_origin(type_value)
|
31
|
+
args = get_args(type_value)
|
32
|
+
if origin == list:
|
33
|
+
return f'array of {jstype(args[0])} '
|
34
|
+
elif origin == dict:
|
35
|
+
return f'object of {jstype(args[0])} to {jstype(args[1])} structure.'
|
36
|
+
else:
|
37
|
+
return 'string'
|
38
|
+
else:
|
39
|
+
match type_value:
|
40
|
+
case str():
|
41
|
+
jtype = 'string'
|
42
|
+
case int():
|
43
|
+
jtype = 'integer'
|
44
|
+
case float():
|
45
|
+
jtype = 'number'
|
46
|
+
case bool():
|
47
|
+
jtype = 'boolean'
|
48
|
+
case dict():
|
49
|
+
if type_value:
|
50
|
+
ptypes = ','.join(f'"{k}": "[Type: {jstype(v)}]"' for k, v in type_value.items())
|
51
|
+
jtype = f'object with {{{ptypes}}} structure'
|
52
|
+
else:
|
53
|
+
jtype = 'object'
|
54
|
+
case list():
|
55
|
+
jtype = 'array'
|
56
|
+
case _:
|
57
|
+
jtype = 'string'
|
58
|
+
return jtype
|
59
|
+
|
60
|
+
def is_type(variable, expected_type):
|
61
|
+
"""
|
62
|
+
Check if the variable matches the expected type hint.
|
63
|
+
"""
|
64
|
+
origin = get_origin(expected_type)
|
65
|
+
if origin is None:
|
66
|
+
return isinstance(variable, expected_type)
|
67
|
+
args = get_args(expected_type)
|
68
|
+
|
69
|
+
# Check if the type matches the generic type
|
70
|
+
if not isinstance(variable, origin):
|
71
|
+
return False
|
72
|
+
|
73
|
+
if not args:
|
74
|
+
return True
|
75
|
+
|
76
|
+
if origin is list:
|
77
|
+
return all(isinstance(item, args[0]) for item in variable)
|
78
|
+
elif origin is dict:
|
79
|
+
return all(isinstance(k, args[0]) and isinstance(v, args[1]) for k, v in variable.items())
|
80
|
+
|
81
|
+
return False
|
82
|
+
|
83
|
+
def Q(str_prompt, type_value = str, blank = True, **format_model):
|
84
|
+
"""returns LLM async call for a question"""
|
85
|
+
llm = Unishare.llm_model
|
86
|
+
if '{' in str_prompt:
|
87
|
+
caller_frame = inspect.currentframe().f_back
|
88
|
+
format_model = caller_frame.f_locals | format_model if format_model else caller_frame.f_locals
|
89
|
+
str_prompt = str_prompt.format(**format_model)
|
90
|
+
if not re.search(r'json', str_prompt, re.IGNORECASE):
|
91
|
+
jtype = jstype(type_value)
|
92
|
+
format = " dd/mm/yyyy string" if type_value == 'date' else f'a JSON {jtype}' if jtype != 'string' else jtype
|
93
|
+
str_prompt = f"System: You are an intelligent and extremely smart assistant. Output STRONGLY {format}." + str_prompt
|
94
|
+
async def f():
|
95
|
+
io = await llm.ainvoke(str_prompt)
|
96
|
+
js = io.content.strip().strip('`').replace('json', '')
|
97
|
+
if type_value == str or type_value == 'date':
|
98
|
+
return js
|
99
|
+
parsed = json.loads(js)
|
100
|
+
if isinstance(type_value, dict):
|
101
|
+
for k, v in type_value.items():
|
102
|
+
if k not in parsed:
|
103
|
+
for k2, v2 in parsed.items():
|
104
|
+
if re.fullmatch(k, k2, re.IGNORECASE) is not None:
|
105
|
+
parsed[k] = parsed.pop(k2)
|
106
|
+
break
|
107
|
+
else:
|
108
|
+
if blank:
|
109
|
+
parsed[k] = None
|
110
|
+
continue
|
111
|
+
else:
|
112
|
+
raise KeyError(f'Key {k} not found in {parsed}')
|
113
|
+
|
114
|
+
if not is_type(parsed[k], v):
|
115
|
+
raise TypeError(f'Invalid type for {k}: {type(parsed[k])} != {v}')
|
116
|
+
else:
|
117
|
+
if not is_type(parsed, type_value):
|
118
|
+
raise TypeError(f'Invalid type: {type(parsed)} != {type_value}')
|
119
|
+
return parsed
|
120
|
+
return f()
|
10
121
|
|
11
122
|
def setup_llmrag():
|
12
|
-
import config #the module is loaded before config
|
123
|
+
import config #the module is loaded before config analysis
|
13
124
|
temperature = getattr(config, 'temperature', 0.0)
|
14
125
|
if config.llm:
|
15
126
|
match config.llm:
|
@@ -31,7 +142,7 @@ def setup_llmrag():
|
|
31
142
|
openai_api_base = address
|
32
143
|
)
|
33
144
|
case 'openai':
|
34
|
-
Unishare.llm_model = ChatOpenAI(temperature=
|
145
|
+
Unishare.llm_model = ChatOpenAI(temperature = temperature)
|
35
146
|
|
36
147
|
case 'groq':
|
37
148
|
Unishare.llm_model = ChatGroq(
|
@@ -52,43 +163,23 @@ def setup_llmrag():
|
|
52
163
|
HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE
|
53
164
|
}
|
54
165
|
)
|
166
|
+
case 'mistral':
|
167
|
+
Unishare.llm_model = ChatMistralAI(
|
168
|
+
model = model,
|
169
|
+
temperature=0,
|
170
|
+
max_retries=2,
|
171
|
+
# other params...
|
172
|
+
)
|
55
173
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
if
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
f"""You are an intelligent and extremely smart assistant."""
|
69
|
-
),
|
70
|
-
("human", f"""{context} . Reason and infer {name}, which {limits}.
|
71
|
-
Do not include any additional text or commentary in your answer, just exact the property value.""")
|
72
|
-
]
|
73
|
-
ai_msg = await Unishare.llm_model.ainvoke(messages)
|
74
|
-
value = ai_msg.content
|
75
|
-
log_error = ''
|
76
|
-
if type in numeric_types:
|
77
|
-
try:
|
78
|
-
value = float(value)
|
79
|
-
except:
|
80
|
-
log_error = f'Invalid value {value} from llm-rag for {messages[1][1]}'
|
81
|
-
return value
|
82
|
-
else:
|
83
|
-
value = value.strip('""')
|
84
|
-
|
85
|
-
if not log_error and options and value not in options:
|
86
|
-
attempts -= 1
|
87
|
-
if attempts > 0:
|
88
|
-
value = get_property(name, context, type, options, attempts, messages)
|
89
|
-
else:
|
90
|
-
log_error = f'Invalid value {value} from llm-rag for {messages[1][1]}'
|
91
|
-
|
92
|
-
if log_error:
|
93
|
-
Unishare.message_logger(log_error)
|
94
|
-
return value
|
174
|
+
async def get_property(name, context = '', type = str, options = None):
|
175
|
+
if type == str and re.search(r'date', name, re.IGNORECASE):
|
176
|
+
type = 'date'
|
177
|
+
limits = f', which possible options are {",".join(opt for opt in options)},' if options else ''
|
178
|
+
prompt = """Context: {context} . Output ONLY "{name}" explicit value{limits} based on the context. """
|
179
|
+
try:
|
180
|
+
value = await Q(prompt, type)
|
181
|
+
except Exception as e:
|
182
|
+
Unishare.message_logger(e)
|
183
|
+
return None
|
184
|
+
return value
|
185
|
+
|
unisi/server.py
CHANGED
@@ -18,9 +18,13 @@ def context_screen():
|
|
18
18
|
user = context_user()
|
19
19
|
return user.screen if user else None
|
20
20
|
|
21
|
-
def message_logger(
|
21
|
+
def message_logger(message, type = 'error'):
|
22
22
|
user = context_user()
|
23
|
-
user
|
23
|
+
if user:
|
24
|
+
user.log(message, type)
|
25
|
+
else:
|
26
|
+
with logging_lock:
|
27
|
+
logging.error(message)
|
24
28
|
|
25
29
|
Unishare.context_user = context_user
|
26
30
|
Unishare.message_logger = message_logger
|
unisi/users.py
CHANGED
@@ -312,18 +312,18 @@ class User:
|
|
312
312
|
def sync_send(self, obj):
|
313
313
|
asyncio.run(self.send(obj))
|
314
314
|
|
315
|
-
def log(self,
|
315
|
+
def log(self, message, type = 'error'):
|
316
316
|
scr = self.screen.name if self.screens else 'void'
|
317
|
-
|
317
|
+
message = f"session: {self.session}, screen: {scr}, message: {self.last_message}\n {message}"
|
318
318
|
with logging_lock:
|
319
319
|
if type == 'error':
|
320
|
-
logging.error(
|
320
|
+
logging.error(message)
|
321
321
|
elif type == 'warning':
|
322
|
-
logging.warning(
|
322
|
+
logging.warning(message)
|
323
323
|
else:
|
324
324
|
func = logging.getLogger().setLevel
|
325
325
|
func(level = logging.INFO)
|
326
|
-
logging.info(
|
326
|
+
logging.info(message)
|
327
327
|
func(level = logging.WARNING)
|
328
328
|
|
329
329
|
def init_user():
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: unisi
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.15
|
4
4
|
Summary: Unified System Interface, GUI and Remote API
|
5
5
|
Author-Email: UNISI Tech <g.dernovoy@gmail.com>
|
6
6
|
License: Apache-2.0
|
@@ -20,6 +20,7 @@ Requires-Dist: langchain-groq
|
|
20
20
|
Requires-Dist: langchain-community
|
21
21
|
Requires-Dist: langchain-openai
|
22
22
|
Requires-Dist: langchain-google-genai
|
23
|
+
Requires-Dist: langchain_mistralai
|
23
24
|
Requires-Dist: word2number
|
24
25
|
Description-Content-Type: text/markdown
|
25
26
|
|
@@ -51,9 +52,8 @@ pip install unisi
|
|
51
52
|
```
|
52
53
|
|
53
54
|
### Programming ###
|
54
|
-
|
55
|
-
|
56
|
-
|
55
|
+
Automatic functionality means that only configuration has to be defined and for all paramaters UNISI has defaults that can be redefined in config.py file.
|
56
|
+
UNISI is a universal data protocol and compact yet highly efficient framework designed for serving and proccessing data in UNISI format. The library includes the web version of Unisi and a comprehensive set of tools and resources for web application development. Supports Python 3.10+.
|
57
57
|
|
58
58
|
### High level - Screen ###
|
59
59
|
The program directory has to contain a screens folder which contains all screens which Unisi has to show.
|
@@ -100,7 +100,7 @@ Connect a browser to localhast:8000 which are by default and will see:
|
|
100
100
|
|
101
101
|

|
102
102
|
|
103
|
-
### 'The fastest way to create Web applications in Python.' is a free crash course video how to use UNISI ###
|
103
|
+
### 'The fastest way to create Web applications in Python.' is a free crash course 1-hour video how to use UNISI ###
|
104
104
|
https://www.unisi.tech/learn
|
105
105
|
|
106
106
|
### Handling events ###
|
@@ -177,7 +177,7 @@ concept_block = Block('Concept block',
|
|
177
177
|
Edit('Working folder','run_folder')
|
178
178
|
], result_table)
|
179
179
|
```
|
180
|
-
If some elements are enumerated inside an array,
|
180
|
+
If some elements are enumerated inside an array, UNISI will display them on a line one after another, otherwise everyone will be displayed on a new own line(s).
|
181
181
|
|
182
182
|
Using a shared block in some screen:
|
183
183
|
```
|
@@ -1,8 +1,8 @@
|
|
1
|
-
unisi-0.3.
|
2
|
-
unisi-0.3.
|
3
|
-
unisi-0.3.
|
4
|
-
unisi-0.3.
|
5
|
-
unisi/__init__.py,sha256=
|
1
|
+
unisi-0.3.15.dist-info/METADATA,sha256=r-Ax_zHww1_W5413mkStd6mb-9dBiG-_Pnzf9fcxkNI,27266
|
2
|
+
unisi-0.3.15.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
|
3
|
+
unisi-0.3.15.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
4
|
+
unisi-0.3.15.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
5
|
+
unisi/__init__.py,sha256=prG4FwJzpNJRX1trto0x_4Bne3kkpEX1dUxcRnIxWVw,301
|
6
6
|
unisi/autotest.py,sha256=qYKwSPEPUEio6koUSu1tc71pDkX-doCQJlyRppaXCtY,8709
|
7
7
|
unisi/common.py,sha256=bMPZo7V9nlJW5HC0yJLRDbrh0DZ4oqmEtBuOvGyN6fw,5759
|
8
8
|
unisi/containers.py,sha256=va4kyqYJ8g7un1hiKx21xXixMXOomXupFPxvS9vga9A,7280
|
@@ -14,14 +14,14 @@ unisi/jsoncomparison/config.py,sha256=LbdLJE1KIebFq_tX7zcERhPvopKhnzcTqMCnS3jN12
|
|
14
14
|
unisi/jsoncomparison/errors.py,sha256=wqphE1Xn7K6n16uvUhDC45m2BxbsMUhIF2olPbhqf4o,1192
|
15
15
|
unisi/jsoncomparison/ignore.py,sha256=xfF0a_BBEyGdZBoq-ovpCpawgcX8SRwwp7IrGnu1c2w,2634
|
16
16
|
unisi/kdb.py,sha256=K-Lqc3e9hLTwO0i1ilTC6qrwZp90tXjLm7HFb_lM1Os,13621
|
17
|
-
unisi/llmrag.py,sha256=
|
17
|
+
unisi/llmrag.py,sha256=M5BvP8MbVjJhnhrnoyP-PfN0OnFYXBzgSGtd52W56tM,7057
|
18
18
|
unisi/multimon.py,sha256=YKwCuvMsMfdgOGkJoqiqh_9wywXMeo9bUhHmbAIUeSE,4060
|
19
19
|
unisi/proxy.py,sha256=QMHSSFJtmVZIexIMAsuFNlF5JpnYNG90rkTM3PYJhY4,7750
|
20
20
|
unisi/reloader.py,sha256=qml-ufoUME7mrWrPMwMo3T8Jsh4e26CBj564cHCB6I0,6749
|
21
|
-
unisi/server.py,sha256=
|
21
|
+
unisi/server.py,sha256=xoUSn4lNv0o3Jn68wE4hL4UcfEBo_jVBvKxUfu1bIGU,6185
|
22
22
|
unisi/tables.py,sha256=tszF62VToSchILzPhJgA4U02MFjv44LopXgD5mYg7fg,13822
|
23
23
|
unisi/units.py,sha256=SCUZAOV0nu9khg6JE0lWwsKjiCVz29hiUCRXyZJffeA,11111
|
24
|
-
unisi/users.py,sha256=
|
24
|
+
unisi/users.py,sha256=JeIori4XsW1blkasLwqZeK8XloX7UjDV_0aHE7WNWjo,16169
|
25
25
|
unisi/utils.py,sha256=yNhDKCTjHL1H2Suk9DRQkXAZKYy6nqub-dNSdwPwl9I,2625
|
26
26
|
unisi/voicecom.py,sha256=QzS1gIrBeGLO5dEwiu7KIEdJIIVbPBZFGb5nY632Ws8,16707
|
27
27
|
unisi/web/css/885.703d8f36.css,sha256=9O3mFR661UJ_WySZjYt69TbPXhKwz9yEPE7seHR_3aY,3264
|
@@ -46,4 +46,4 @@ unisi/web/js/885.d3e9dd2b.js,sha256=7A39S4SDApVc4iHHABjOd5julybSa4UwaH4kj8vSn0E,
|
|
46
46
|
unisi/web/js/935.cc0c012c.js,sha256=FzVIRBr4vyQgW38ROCoh929gtzuXqM73Cf77vejfDWk,6561
|
47
47
|
unisi/web/js/app.3d5227f7.js,sha256=lJkD2OPQOYlxivZmNY8FYKI1JMQ_bh1Pm4zC7y8Ayt0,6150
|
48
48
|
unisi/web/js/vendor.1bb14e9d.js,sha256=7q80jaZcms7UhWSqHAk2pXSx67cYQJGlsp-6DBXBZuU,1253597
|
49
|
-
unisi-0.3.
|
49
|
+
unisi-0.3.15.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|