unisi 0.3.18__py3-none-any.whl → 0.3.20__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.
Files changed (48) hide show
  1. unisi/common.py +5 -5
  2. unisi/llmrag.py +70 -6
  3. unisi/server.py +5 -5
  4. unisi/units.py +6 -5
  5. unisi/users.py +3 -1
  6. unisi/web/css/565.f35d8840.css +1 -0
  7. unisi/web/css/72.9393078c.css +1 -0
  8. unisi/web/css/{vendor.f7e3cefe.css → app.36cc37f3.css} +3 -3
  9. unisi/web/css/app.36cc37f3.css.gz +0 -0
  10. unisi/web/fonts/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuYjalmUiAw.2cafd699.woff +0 -0
  11. unisi/web/fonts/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuZtalmUiAw.7f471697.woff +0 -0
  12. unisi/web/fonts/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuaabVmUiAw.b69d1cce.woff +0 -0
  13. unisi/web/fonts/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWub2bVmUiAw.2115c3bd.woff +0 -0
  14. unisi/web/fonts/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbFmUiAw.bb8ee9ee.woff +0 -0
  15. unisi/web/fonts/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVmUiAw.eb9d7304.woff +0 -0
  16. unisi/web/index.html +1 -1
  17. unisi/web/js/260.1d50abd6.js +1 -0
  18. unisi/web/js/565.437dc595.js +3 -0
  19. unisi/web/js/565.437dc595.js.gz +0 -0
  20. unisi/web/js/609.a5c4177e.js +1 -0
  21. unisi/web/js/72.f1673470.js +1 -0
  22. unisi/web/js/935.a10cb1a0.js +1 -0
  23. unisi/web/js/app.aa62bd3c.js +1 -0
  24. unisi/web/js/echart.8f773fb7.js +1 -0
  25. unisi/web/js/echart.8f773fb7.js.gz +0 -0
  26. unisi/web/js/sigma.ce21336a.js +1 -0
  27. unisi/web/js/sigma.ce21336a.js.gz +0 -0
  28. unisi/web/js/vendor.6a64dcc5.js +57 -0
  29. unisi/web/js/vendor.6a64dcc5.js.gz +0 -0
  30. {unisi-0.3.18.dist-info → unisi-0.3.20.dist-info}/METADATA +1 -1
  31. unisi-0.3.20.dist-info/RECORD +58 -0
  32. unisi/web/css/131.703d8f36.css +0 -1
  33. unisi/web/css/app.31d6cfe0.css +0 -0
  34. unisi/web/fonts/KFOkCnqEu92Fr1MmgVxIIzQ.68bb21d0.woff +0 -0
  35. unisi/web/fonts/KFOlCnqEu92Fr1MmEU9fBBc-.48af7707.woff +0 -0
  36. unisi/web/fonts/KFOlCnqEu92Fr1MmSU5fBBc-.c2f7ab22.woff +0 -0
  37. unisi/web/fonts/KFOlCnqEu92Fr1MmWUlfBBc-.77ecb942.woff +0 -0
  38. unisi/web/fonts/KFOlCnqEu92Fr1MmYUtfBBc-.f5677eb2.woff +0 -0
  39. unisi/web/fonts/KFOmCnqEu92Fr1Mu4mxM.f1e2a767.woff +0 -0
  40. unisi/web/js/131.76c61515.js +0 -3
  41. unisi/web/js/609.35dc13d3.js +0 -1
  42. unisi/web/js/935.cc0c012c.js +0 -1
  43. unisi/web/js/app.6f20f926.js +0 -1
  44. unisi/web/js/vendor.1bb14e9d.js +0 -45
  45. unisi-0.3.18.dist-info/RECORD +0 -49
  46. {unisi-0.3.18.dist-info → unisi-0.3.20.dist-info}/WHEEL +0 -0
  47. {unisi-0.3.18.dist-info → unisi-0.3.20.dist-info}/entry_points.txt +0 -0
  48. {unisi-0.3.18.dist-info → unisi-0.3.20.dist-info}/licenses/LICENSE +0 -0
unisi/common.py CHANGED
@@ -120,17 +120,17 @@ class Message:
120
120
 
121
121
  def fill_paths4(self, user):
122
122
  if hasattr(self, 'updates'):
123
- invalid = []
123
+ invisible = []
124
124
  for update in self.updates:
125
125
  data = update["data"]
126
126
  path = user.find_path(data)
127
127
  if path:
128
128
  update['path'] = path
129
129
  else:
130
- invalid.append(update)
131
- user.log(f'Invalid element update {data.name}, type {data.type}.\n\
132
- Such element not on the screen!')
133
- for inv in invalid:
130
+ invisible.append(update)
131
+ user.log(f'Invisible element update {data.name}, type {data.type}.\n\
132
+ Such element not on the screen!', type = 'warning')
133
+ for inv in invisible:
134
134
  self.updates.remove(inv)
135
135
 
136
136
  def contains(self, unit):
unisi/llmrag.py CHANGED
@@ -9,9 +9,50 @@ from langchain_google_genai import (
9
9
  HarmCategory,
10
10
  )
11
11
  from datetime import datetime
12
- import collections, inspect, re, json
12
+ import os, inspect, re, json
13
13
  from typing import get_origin, get_args
14
14
 
15
+ class QueryCache:
16
+ ITEM_SEPARATOR = "§¶†‡◊•→±"
17
+ ENTRY_SEPARATOR = "€£¥¢≠≈∆√"
18
+
19
+ def __init__(self, file_path):
20
+ self.file_path = file_path
21
+ self.cache = {}
22
+ self._load_cache()
23
+
24
+ def _load_cache(self):
25
+ if os.path.exists(self.file_path):
26
+ with open(self.file_path, 'r', encoding='utf-8') as f:
27
+ content = f.read()
28
+ entries = content.split(self.ENTRY_SEPARATOR)
29
+ for entry in entries:
30
+ if not entry.strip():
31
+ continue
32
+ parts = entry.split(self.ITEM_SEPARATOR)
33
+ if len(parts) == 2:
34
+ query, result = parts
35
+ self.cache[query] = result
36
+
37
+ def get(self, query):
38
+ return self.cache.get(query)
39
+
40
+ def set(self, query, result):
41
+ entry_data = f"{query}{self.ITEM_SEPARATOR}{result}"
42
+ prepend_separator = bool(self.cache)
43
+
44
+ try:
45
+ with open(self.file_path, 'a', encoding='utf-8') as f:
46
+ if prepend_separator:
47
+ f.write(self.ENTRY_SEPARATOR)
48
+ f.write(entry_data)
49
+
50
+ self.cache[query] = result
51
+ return True
52
+ except Exception as e:
53
+ print(f"Error caching result: {e}")
54
+ return False
55
+
15
56
  def jstype(type_value):
16
57
  if isinstance(type_value, type):
17
58
  if type_value == int:
@@ -80,6 +121,13 @@ def is_type(variable, expected_type):
80
121
 
81
122
  return False
82
123
 
124
+ def remove_comments(json_str):
125
+ # Regular expression to remove single-line comments (// ...)
126
+ json_str = re.sub(r'//.*', '', json_str)
127
+ # Regular expression to remove multi-line comments (/* ... */)
128
+ json_str = re.sub(r'/\*.*?\*/', '', json_str, flags=re.DOTALL)
129
+ return json_str
130
+
83
131
  def Q(str_prompt, type_value = str, blank = True, **format_model):
84
132
  """returns LLM async call for a question"""
85
133
  llm = Unishare.llm_model
@@ -90,13 +138,26 @@ def Q(str_prompt, type_value = str, blank = True, **format_model):
90
138
  if not re.search(r'json', str_prompt, re.IGNORECASE):
91
139
  jtype = jstype(type_value)
92
140
  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}. Do not output any commentary." + str_prompt
141
+ str_prompt = f"System: You are an intelligent and extremely smart assistant. Output STRONGLY {format}. DO NOT OUTPUT ANY COMMENTARY." + str_prompt
94
142
  async def f():
95
- io = await llm.ainvoke(str_prompt)
96
- js = io.content.strip().strip('`').replace('json', '')
143
+ if Unishare.llm_cache:
144
+ if content := Unishare.llm_cache.get(str_prompt):
145
+ pass
146
+ else:
147
+ io = await llm.ainvoke(str_prompt)
148
+ content = io.content
149
+ Unishare.llm_cache.set(str_prompt, content)
150
+ else:
151
+ io = await llm.ainvoke(str_prompt)
152
+ content = io.content
153
+ js = content.strip().strip('`').replace('json', '')
97
154
  if type_value == str or type_value == 'date':
98
155
  return js
99
- parsed = json.loads(js)
156
+ try:
157
+ clean_js = remove_comments(js)
158
+ parsed = json.loads(clean_js)
159
+ except json.JSONDecodeError as e:
160
+ raise ValueError(f'Invalid JSON: {js}, \n Query: {str_prompt}')
100
161
  if isinstance(type_value, dict):
101
162
  for k, v in type_value.items():
102
163
  if k not in parsed:
@@ -114,7 +175,7 @@ def Q(str_prompt, type_value = str, blank = True, **format_model):
114
175
  raise TypeError(f'Invalid type for {k}: {type(parsed[k])} != {v}')
115
176
  else:
116
177
  if not is_type(parsed, type_value):
117
- raise TypeError(f'Invalid type: {type(parsed)} != {type_value}')
178
+ raise TypeError(f'Invalid type: {type(parsed)} != {type_value}')
118
179
  return parsed
119
180
  return f()
120
181
 
@@ -169,6 +230,9 @@ def setup_llmrag():
169
230
  max_retries=2,
170
231
  # other params...
171
232
  )
233
+
234
+ if hasattr(config, 'llm_cache'):
235
+ Unishare.llm_cache = QueryCache(config.llm_cache)
172
236
 
173
237
  async def get_property(name, context = '', type = str, options = None):
174
238
  if type == str and re.search(r'date', name, re.IGNORECASE):
unisi/server.py CHANGED
@@ -173,12 +173,12 @@ def start(user_type = User, http_handlers = []):
173
173
 
174
174
  User.type = user_type
175
175
  run_tests(User.init_user())
176
-
177
- http_handlers.insert(0, web.get('/ws', websocket_handler))
178
- http_handlers += [web.static(f'/{config.upload_dir}', upload_dir),
179
- web.get('/{tail:.*}', static_serve), web.post('/', post_handler)]
176
+ #http_handlers has to be the first argument
177
+ server_handlers = http_handlers + [web.get('/ws', websocket_handler),
178
+ web.static(f'/{config.upload_dir}', upload_dir),
179
+ web.get('/{tail:.*}', static_serve), web.post('/', post_handler)]
180
180
 
181
181
  app = web.Application()
182
- app.add_routes(http_handlers)
182
+ app.add_routes(server_handlers)
183
183
  web.run_app(app, port = port)
184
184
 
unisi/units.py CHANGED
@@ -111,11 +111,12 @@ class Unit:
111
111
  super().__setattr__(name, value)
112
112
 
113
113
  def mutate(self, obj):
114
- self.__dict__.clear()
115
- for key, value in obj.__dict__.items():
116
- setattr(self, key, value)
117
- if self._mark_changed:
118
- self._mark_changed()
114
+ if self is not obj:
115
+ self.__dict__.clear()
116
+ for key, value in obj.__dict__.items():
117
+ setattr(self, key, value)
118
+ if self._mark_changed:
119
+ self._mark_changed()
119
120
 
120
121
  def accept(self, value):
121
122
  if hasattr(self, 'changed'):
unisi/users.py CHANGED
@@ -229,7 +229,9 @@ class User:
229
229
  match raw:
230
230
  case None:
231
231
  if self.changed_units:
232
- raw = Message(*self.changed_units, user = self)
232
+ raw = Message(*self.changed_units, user = self)
233
+ if not raw.updates:
234
+ raw = None
233
235
  case Message():
234
236
  if self.changed_units:
235
237
  message_units = [x['data'] for x in raw.updates]
@@ -0,0 +1 @@
1
+ thead tr:first-child th{-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);position:sticky;top:0;z-index:1000}:root{--scrollbar-width-height:10px;--scrollbar-thumb-hover:#2176d2;--scrollbar-thumb-dark:#2176d2;--scrollbar-thumb-hover-dark:#2176d2}::-webkit-scrollbar{height:var(--scrollbar-width-height);width:var(--scrollbar-width-height)}::-webkit-scrollbar-track{box-shadow:inset 0 0 4px var(--scrollbar-track-dark)}::-webkit-scrollbar-corner{background:var(--scrollbar-track-dark)}::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb-dark);border-radius:5px}::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-hover-dark)}body[data-v-6398f67f]{display:flex;justify-content:center}.custom-caption[data-v-6398f67f]{padding:5px!important}.web-camera-container[data-v-6398f67f]{align-items:center;border:1px solid #ccc;border-radius:4px;display:flex;flex-direction:column;justify-content:center;margin-bottom:2rem;margin-top:2rem;padding:2rem;width:500px}.web-camera-container .camera-button[data-v-6398f67f]{margin-bottom:2rem}.web-camera-container .camera-box .camera-shutter[data-v-6398f67f]{background-color:#fff;height:337.5px;opacity:0;position:absolute;width:450px}.web-camera-container .camera-box .camera-shutter.flash[data-v-6398f67f]{opacity:1}.web-camera-container .camera-shoot[data-v-6398f67f]{margin:1rem 0}.web-camera-container .camera-shoot button[data-v-6398f67f]{align-items:center;border-radius:100%;display:flex;height:60px;justify-content:center;width:60px}.web-camera-container .camera-shoot button img[data-v-6398f67f]{height:35px;object-fit:cover}.web-camera-container .camera-loading[data-v-6398f67f]{height:100%;margin:3rem 0 0 -1.2rem;min-height:150px;overflow:hidden;position:absolute;width:100%}.web-camera-container .camera-loading ul[data-v-6398f67f]{height:100%;margin:0;position:absolute;width:100%;z-index:999999}.web-camera-container .camera-loading .loader-circle[data-v-6398f67f]{display:block;height:14px;left:100%;margin:0 auto;padding:0;position:absolute;top:50%;transform:translateY(-50%);transform:translateX(-50%);width:100%}.web-camera-container .camera-loading .loader-circle li[data-v-6398f67f]{animation:preload-6398f67f 1s infinite;background:#999;border-radius:100%;display:block;float:left;height:10px;line-height:10px;margin:0 0 0 4px;padding:0;position:relative;top:-50%;width:10px}.web-camera-container .camera-loading .loader-circle li[data-v-6398f67f]:nth-child(2){animation-delay:.2s}.web-camera-container .camera-loading .loader-circle li[data-v-6398f67f]:nth-child(3){animation-delay:.4s}@keyframes preload-6398f67f{0%{opacity:1}50%{opacity:.4}to{opacity:1}}.container[data-v-6398f67f]{position:relative}.frame[data-v-6398f67f]{border:2px solid #573497;border-radius:8px;height:100%;left:0;pointer-events:none;position:absolute;top:0;width:100%;z-index:10}.container{position:relative}.frame{border:2px solid #573497;border-radius:8px;height:100%;left:0;pointer-events:none;position:absolute;top:0;width:100%;z-index:10}.q-tab__label{font-size:16px;font-weight:700}
@@ -0,0 +1 @@
1
+ #header[data-v-a6b7e7b0]{font-size:16px;font-weight:700;left:10px;pointer-events:none;position:absolute;top:10px;z-index:2}#graph[data-v-a6b7e7b0]{border:1px solid #ccc;height:600px;position:relative;width:100%}