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 CHANGED
@@ -1,4 +1,5 @@
1
1
  from .utils import *
2
+ from .llmrag import Q
2
3
  from .units import *
3
4
  from .users import User
4
5
  from .server import start, handle, context_user, context_screen
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.py
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=0.0)
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
- numeric_types = ['number', 'int', 'float', 'double']
57
-
58
- async def get_property(name, context = '', type = 'string', options = None, attempts = 1, messages = None):
59
- if messages is None:
60
- limits = f'type is {type}'
61
- if type == 'date':
62
- limits = f'{limits}, use format "dd/mm/yyyy"'
63
- if options:
64
- limits = f'{limits}, and its possible options are {",".join(opt for opt in options)}'
65
- messages = [
66
- (
67
- "system",
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(str, type = 'error'):
21
+ def message_logger(message, type = 'error'):
22
22
  user = context_user()
23
- user.log(str, type)
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, str, type = 'error'):
315
+ def log(self, message, type = 'error'):
316
316
  scr = self.screen.name if self.screens else 'void'
317
- str = f"session: {self.session}, screen: {scr}, message: {self.last_message}\n {str}"
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(str)
320
+ logging.error(message)
321
321
  elif type == 'warning':
322
- logging.warning(str)
322
+ logging.warning(message)
323
323
  else:
324
324
  func = logging.getLogger().setLevel
325
325
  func(level = logging.INFO)
326
- logging.info(str)
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.13
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
- UNISI tech provides a unified system interface and advanced program functionality, eliminating the need for front-end and most back-end programming. It automates common tasks by inner services, as well as unique ones, significantly reducing the necessity for manual programming and effort.
55
- This document serves as a comprehensive guide on utilizing Unisi with Python, along with a compact yet highly efficient framework specifically designed for this purpose. Additionally, the library includes the web version of Unisi, providing developers with a comprehensive set of tools and resources for web application development. Supports Python 3.10+.
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
  ![image](https://github.com/unisi-tech/unisi/assets/1247062/dafebd1f-ae48-4790-9282-dea83d986749)
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, Unisi will display them on a line one after another, otherwise everyone will be displayed on a new own line(s).
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.13.dist-info/METADATA,sha256=xrIMtE75CW1YksJIGWA_zFyhwBJrJiEtgk9QPYIJ_Z8,27430
2
- unisi-0.3.13.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
- unisi-0.3.13.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
- unisi-0.3.13.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
5
- unisi/__init__.py,sha256=JVioDSebhtmoYTldT6ChEayuRTHOgYsAflcxcBYWBTY,279
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=Wh9pQ8kBMlersKxbEDlZ3XeY2grH0_Rfg8I3E2W87hI,3481
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=V0I3OAWcebttN1KXHd_-5Vx9tOZ_RzPfSg-3ZJVxWY0,6084
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=h4kjPAo8LkUG9mKSDthLoDC-XVFLlPxjUvXcJdXT47g,16145
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.13.dist-info/RECORD,,
49
+ unisi-0.3.15.dist-info/RECORD,,
File without changes