unisi 0.3.14__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/llmrag.py +126 -83
- unisi/server.py +6 -2
- unisi/users.py +5 -5
- {unisi-0.3.14.dist-info → unisi-0.3.15.dist-info}/METADATA +2 -1
- {unisi-0.3.14.dist-info → unisi-0.3.15.dist-info}/RECORD +8 -8
- {unisi-0.3.14.dist-info → unisi-0.3.15.dist-info}/WHEEL +0 -0
- {unisi-0.3.14.dist-info → unisi-0.3.15.dist-info}/entry_points.txt +0 -0
- {unisi-0.3.14.dist-info → unisi-0.3.15.dist-info}/licenses/LICENSE +0 -0
unisi/llmrag.py
CHANGED
@@ -2,62 +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
|
)
|
10
|
-
from
|
11
|
-
|
12
|
-
import
|
11
|
+
from datetime import datetime
|
12
|
+
import collections, inspect, re, json
|
13
|
+
from typing import get_origin, get_args
|
13
14
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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.'
|
25
36
|
else:
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
29
59
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
37
72
|
|
38
|
-
|
39
|
-
return
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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())
|
44
80
|
|
45
|
-
|
46
|
-
"""returns LLM answer for a question"""
|
47
|
-
q = Question.get(question, type_value, **format_model)
|
48
|
-
llm = Unishare.llm_model
|
49
|
-
str_prompt = q.question
|
50
|
-
if '{' in str_prompt:
|
51
|
-
caller_frame = inspect.currentframe().f_back
|
52
|
-
str_prompt = str_prompt.format(**caller_frame.f_locals)
|
53
|
-
io = await llm.ainvoke(str_prompt)
|
54
|
-
js = io.content.strip('`')
|
55
|
-
js = js.replace('json', '').replace('\n', '')
|
56
|
-
return q.format.parse_raw(js).root
|
81
|
+
return False
|
57
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()
|
58
121
|
|
59
122
|
def setup_llmrag():
|
60
|
-
import config #the module is loaded before config
|
123
|
+
import config #the module is loaded before config analysis
|
61
124
|
temperature = getattr(config, 'temperature', 0.0)
|
62
125
|
if config.llm:
|
63
126
|
match config.llm:
|
@@ -79,7 +142,7 @@ def setup_llmrag():
|
|
79
142
|
openai_api_base = address
|
80
143
|
)
|
81
144
|
case 'openai':
|
82
|
-
Unishare.llm_model = ChatOpenAI(temperature=
|
145
|
+
Unishare.llm_model = ChatOpenAI(temperature = temperature)
|
83
146
|
|
84
147
|
case 'groq':
|
85
148
|
Unishare.llm_model = ChatGroq(
|
@@ -100,43 +163,23 @@ def setup_llmrag():
|
|
100
163
|
HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE
|
101
164
|
}
|
102
165
|
)
|
166
|
+
case 'mistral':
|
167
|
+
Unishare.llm_model = ChatMistralAI(
|
168
|
+
model = model,
|
169
|
+
temperature=0,
|
170
|
+
max_retries=2,
|
171
|
+
# other params...
|
172
|
+
)
|
103
173
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
if
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
f"""You are an intelligent and extremely smart assistant."""
|
117
|
-
),
|
118
|
-
("human", f"""{context} . Reason and infer {name}, which {limits}.
|
119
|
-
Do not include any additional text or commentary in your answer, just exact the property value.""")
|
120
|
-
]
|
121
|
-
ai_msg = await Unishare.llm_model.ainvoke(messages)
|
122
|
-
value = ai_msg.content
|
123
|
-
log_error = ''
|
124
|
-
if type in numeric_types:
|
125
|
-
try:
|
126
|
-
value = float(value)
|
127
|
-
except:
|
128
|
-
log_error = f'Invalid value {value} from llm-rag for {messages[1][1]}'
|
129
|
-
return value
|
130
|
-
else:
|
131
|
-
value = value.strip('""')
|
132
|
-
|
133
|
-
if not log_error and options and value not in options:
|
134
|
-
attempts -= 1
|
135
|
-
if attempts > 0:
|
136
|
-
value = get_property(name, context, type, options, attempts, messages)
|
137
|
-
else:
|
138
|
-
log_error = f'Invalid value {value} from llm-rag for {messages[1][1]}'
|
139
|
-
|
140
|
-
if log_error:
|
141
|
-
Unishare.message_logger(log_error)
|
142
|
-
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
|
|
@@ -1,7 +1,7 @@
|
|
1
|
-
unisi-0.3.
|
2
|
-
unisi-0.3.
|
3
|
-
unisi-0.3.
|
4
|
-
unisi-0.3.
|
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
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
|
@@ -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
|