streamlit-octostar-utils 0.1.7a3__py3-none-any.whl → 0.1.7a4__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.
@@ -23,7 +23,7 @@ from functools import wraps
23
23
  import logging
24
24
 
25
25
  logger = logging.getLogger(__name__)
26
- logging.getLogger('pottery').setLevel(logging.WARNING)
26
+ logging.getLogger("pottery").setLevel(logging.WARNING)
27
27
  from celery.app.defaults import DEFAULTS as CELERY_DEFAULTS
28
28
  import urllib
29
29
 
@@ -630,6 +630,11 @@ class FastAPICeleryTaskRoute(Route):
630
630
  except BaseException as e:
631
631
  exc = e
632
632
  data = {}
633
+ assert (
634
+ (state in ["FAILURE", "RETRY", "REVOKED"] and exc is not None)
635
+ or (state in ["SUCCESS"] and exc is None)
636
+ or (state not in ["SUCCESS", "FAILURE", "RETRY", "REVOKED"])
637
+ )
633
638
  if state in ["FAILURE", "RETRY", "REVOKED"]:
634
639
  error_response = DefaultErrorRoute.format_error(exc, debug=True).body.decode("utf-8")
635
640
  data = {
@@ -682,11 +687,16 @@ class CeleryErrorRoute(DefaultErrorRoute):
682
687
  debug=False,
683
688
  excs_to_status_codes=None,
684
689
  silenced_excs=None,
685
- log_filter=None
686
- ,
690
+ log_filter=None,
687
691
  ):
688
692
  if excs_to_status_codes is None:
689
- excs_to_status_codes = {**DefaultErrorRoute.DEFAULT_STATUS_CODE_MAPPINGS, **CeleryErrorRoute.DEFAULT_STATUS_CODE_MAPPINGS}
693
+ excs_to_status_codes = {
694
+ **DefaultErrorRoute.DEFAULT_STATUS_CODE_MAPPINGS,
695
+ **CeleryErrorRoute.DEFAULT_STATUS_CODE_MAPPINGS,
696
+ }
690
697
  if silenced_excs is None:
691
- silenced_excs = {**DefaultErrorRoute.DEFAULT_SILENCED_EXCEPTIONS, **CeleryErrorRoute.DEFAULT_SILENCED_EXCEPTIONS}
698
+ silenced_excs = {
699
+ **DefaultErrorRoute.DEFAULT_SILENCED_EXCEPTIONS,
700
+ **CeleryErrorRoute.DEFAULT_SILENCED_EXCEPTIONS,
701
+ }
692
702
  DefaultErrorRoute.add_default_exceptions_handler(fs_app, debug, excs_to_status_codes)
@@ -1103,14 +1103,20 @@ class NifiRoute(Route):
1103
1103
  def define_routes(self):
1104
1104
  @Route.route(self, path="/task-state/{task_id}")
1105
1105
  async def get_task_status(task_id: str) -> JSONResponse:
1106
- task_status = await self.tasks_routes.get_task(task_id, pop=False)
1107
- task_status = task_status.model_dump(mode="json")["data"]["task_state"]
1106
+ try:
1107
+ task_status = await self.tasks_routes.get_task(task_id, pop=False)
1108
+ task_status = task_status.model_dump(mode="json")["data"]["task_state"]
1109
+ except BaseException as e:
1110
+ raise ValueError(f"Could not fetch task state for task id {task_id}!\n{e}")
1108
1111
  return JSONResponse(task_status)
1109
1112
 
1110
1113
  @Route.route(self, path="/task-result/{task_id}")
1111
1114
  async def get_task_result(task_id: str) -> JSONResponse:
1112
- return_data = await self.tasks_routes.get_task(task_id, pop=True)
1113
- return_data = return_data.model_dump(mode="json")["data"]["data"]
1115
+ try:
1116
+ return_data = await self.tasks_routes.get_task(task_id, pop=True)
1117
+ return_data = return_data.model_dump(mode="json")["data"]["data"]
1118
+ except BaseException as e:
1119
+ raise ValueError(f"Could not fetch task result for task id {task_id}\n{e}!")
1114
1120
  return JSONResponse(return_data)
1115
1121
 
1116
1122
  @Route.route(self, path="/{op}", methods=["POST"])
@@ -12,4 +12,4 @@ def detect_language(text, min_confidence=None):
12
12
  return None
13
13
  detected_lang = re.sub("[^A-Za-z]", "", detected_lang).lower()
14
14
  detected_lang = languages.to_name(detected_lang).lower()
15
- return detected_lang
15
+ return detected_lang, confidence
@@ -1,7 +1,5 @@
1
1
  import re
2
2
  import streamlit as st
3
- import py3langid as langid
4
- import iso639 as languages
5
3
  from spacy_download import load_spacy
6
4
  from flair.data import Sentence
7
5
  from flair.models import SequenceTagger
@@ -17,8 +15,6 @@ import math
17
15
  import nltk
18
16
  from typing import Optional, List
19
17
 
20
- nltk.download("punkt")
21
-
22
18
  SPACY_NER_MODELS = {
23
19
  "english": lambda: load_spacy(
24
20
  "en_core_web_sm",
@@ -26,9 +22,17 @@ SPACY_NER_MODELS = {
26
22
  )
27
23
  }
28
24
  FLAIR_NER_MODELS = {"english": lambda: SequenceTagger.load("flair/ner-english")}
25
+ REGEX_NER_MODELS = {
26
+ "IP_ADDRESS": [
27
+ r"(?:(?<=:=)|(?<=\s)|(?<=\b))(?:\d{1,3}\.){3}\d{1,3}(?::\d{1,5})?(?:(?=\s)|(?=\b))",
28
+ r"(?:(?<=:=)|(?<=\s)|(?<=\b))(?:[A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}(?::\d{1,5})?(?:(?=\s)|(?=\b))"
29
+ ],
30
+ "PHONE": r"(?:(?<=:=)|(?<=\s)|(?<=\b))[+]?[(]?[0-9]{1,4}[)]?[-\s\/0-9]*(?:(?=\s)|(?=\b))",
31
+ "EMAIL": r"(?:(?<=:=)|(?<=\s)|(?<=\b))[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}(?:(?=\s)|(?=\b))",
32
+ }
29
33
 
30
34
  BASE_TO_ONTONOTES_LABELMAP = {"PER": "PERSON"}
31
- BASE_ALLOWED_LABELS = ["PERSON", "ORG", "LOC", "NORP", "GPE", "PRODUCT", "DATE", "TIME"]
35
+ BASE_ALLOWED_LABELS = ["PERSON", "ORG", "LOC", "NORP", "GPE", "PRODUCT", "DATE", "TIME", "PHONE"]
32
36
 
33
37
 
34
38
  def _sumy__get_best_sentences(sentences, rating, *args, **kwargs):
@@ -39,9 +43,7 @@ def _sumy__get_best_sentences(sentences, rating, *args, **kwargs):
39
43
  if isinstance(rating, dict):
40
44
  assert not args and not kwargs
41
45
  rate = lambda s: rating[s]
42
- infos = (
43
- SentenceInfo(s, o, rate(s, *args, **kwargs)) for o, s in enumerate(sentences)
44
- )
46
+ infos = (SentenceInfo(s, o, rate(s, *args, **kwargs)) for o, s in enumerate(sentences))
45
47
  infos = sorted(infos, key=attrgetter("rating"), reverse=True)
46
48
  return tuple((i.sentence, i.rating, i.order) for i in infos)
47
49
 
@@ -62,9 +64,15 @@ def _sumy__lsa_call(summarizer, document):
62
64
 
63
65
  def _sumy__luhn_call(summarizer, document):
64
66
  words = summarizer._get_significant_words(document.words)
65
- return _sumy__get_best_sentences(
66
- document.sentences, summarizer.rate_sentence, words
67
- )
67
+ return _sumy__get_best_sentences(document.sentences, summarizer.rate_sentence, words)
68
+
69
+
70
+ def get_nltk_tokenizer(language: str) -> Tokenizer:
71
+ try:
72
+ nltk.data.find("tokenizers/punkt")
73
+ except LookupError:
74
+ nltk.download("punkt")
75
+ return Tokenizer(language)
68
76
 
69
77
 
70
78
  class NERObject(object):
@@ -77,22 +85,25 @@ class NERObject(object):
77
85
  self.comentions: Optional[List[str]] = comentions
78
86
  self.sources: Optional[List[str]] = list()
79
87
 
88
+ def to_dict(self):
89
+ data = {
90
+ "name": self.name,
91
+ "label": self.label,
92
+ "score": self.score,
93
+ "context": self.context,
94
+ "count": self.count,
95
+ "comentions": self.comentions or [],
96
+ }
97
+ if self.sources:
98
+ data["sources"] = self.sources
99
+
100
+ def __repr__(self):
101
+ return f"NERObject(label={self.label},name={self.name})"
80
102
 
81
- def detect_language(text, min_confidence=None):
82
- detector = langid.langid.LanguageIdentifier.from_pickled_model(
83
- langid.langid.MODEL_FILE, norm_probs=True
84
- )
85
- detected_lang, confidence = detector.classify(text)
86
- if min_confidence and confidence < min_confidence:
87
- return None
88
- detected_lang = re.sub("[^A-Za-z]", "", detected_lang).lower()
89
- detected_lang = languages.to_name(detected_lang).lower()
90
- return detected_lang
91
103
 
92
-
93
- def postprocess_ner(entities, allowed_labels, max_entities=100):
94
- if allowed_labels != "all":
95
- entities = [e for e in entities if e.label in allowed_labels]
104
+ def postprocess_ner(entities, whitelisted_labels=None, max_entities=None):
105
+ if whitelisted_labels is not None:
106
+ entities = [e for e in entities if e.label in whitelisted_labels]
96
107
  entities = sorted(entities, key=lambda x: x.name)
97
108
  final_entities = []
98
109
  for _, group in itertools.groupby(entities, key=lambda x: x.name):
@@ -108,34 +119,24 @@ def postprocess_ner(entities, allowed_labels, max_entities=100):
108
119
  )
109
120
  best_entity.sources = list(set(itertools.chain(*[e.sources for e in group])))
110
121
  final_entities.append(best_entity)
111
- final_entities = sorted(
112
- final_entities, key=lambda x: x.score * x.count, reverse=True
113
- )
114
- if len(final_entities) > max_entities:
122
+ final_entities = sorted(final_entities, key=lambda x: x.score * x.count, reverse=True)
123
+ if max_entities and len(final_entities) > max_entities:
115
124
  final_entities = final_entities[:max_entities]
116
125
  return final_entities
117
126
 
118
127
 
119
- def compute_ner(language, sentences, fast=True, context_width=150):
128
+ def compute_ner(language, sentences, spacy_model, flair_model=None, context_width=150):
120
129
  sentence_starts = [0] + [len(s[0]) + 1 for s in sentences]
121
130
  del sentence_starts[-1]
122
131
  sentence_starts = list(np.cumsum(sentence_starts))
123
132
  text = "\n".join([s[0] for s in sentences])
124
- if fast:
125
- model = SPACY_NER_MODELS.get(language, SPACY_NER_MODELS["english"])()
126
- entities = [
127
- (
128
- entity.text,
129
- BASE_TO_ONTONOTES_LABELMAP.get(entity.label_, entity.label_),
130
- 0,
131
- entity.start_char,
132
- )
133
- for entity in model(text).ents
134
- ]
135
- else:
136
- model = FLAIR_NER_MODELS.get(language, FLAIR_NER_MODELS["english"])()
133
+ min_score = 1.0
134
+ entities = []
135
+
136
+ # FLAIR model (if not fast)
137
+ if flair_model:
137
138
  input = [Sentence(sentence[0]) for sentence in sentences]
138
- model.predict(input)
139
+ flair_model.predict(input)
139
140
  output = [e for sentence in input for e in sentence.get_spans("ner")]
140
141
  flair_entities = [
141
142
  (
@@ -145,35 +146,45 @@ def compute_ner(language, sentences, fast=True, context_width=150):
145
146
  entity.annotation_layers["ner"][0].value,
146
147
  ),
147
148
  entity.score,
148
- sentence_starts[input.index(entity[0].sentence)]
149
- + entity[0].start_position,
149
+ sentence_starts[input.index(entity[0].sentence)] + entity[0].start_position,
150
150
  )
151
151
  for entity in output
152
152
  ]
153
- min_score = min([0] + [e[2] for e in flair_entities])
154
- model = SPACY_NER_MODELS.get(language, SPACY_NER_MODELS["english"])()
155
- spacy_entities = [
156
- (
157
- entity.text,
158
- BASE_TO_ONTONOTES_LABELMAP.get(entity.label_, entity.label_),
159
- min_score - 1,
160
- entity.start_char,
161
- )
162
- for entity in model(text).ents
163
- ]
164
- entities = flair_entities + spacy_entities
153
+ min_score = min(min_score, *[e[2] for e in flair_entities])
154
+ entities += flair_entities
155
+ del flair_entities
156
+
157
+ # REGEX model
158
+ for label, regexes in REGEX_NER_MODELS.items():
159
+ if not isinstance(regexes, list):
160
+ regexes = [regexes]
161
+ for regex in regexes:
162
+ print(regex)
163
+ regex_entities = [
164
+ (match.group(), label, min_score - 0.5, match.start()) for match in re.finditer(regex, text)
165
+ ]
166
+ print(regex_entities)
167
+ entities += regex_entities
168
+
169
+ # SPACY model
170
+ spacy_entities = [
171
+ (
172
+ entity.text,
173
+ BASE_TO_ONTONOTES_LABELMAP.get(entity.label_, entity.label_),
174
+ min_score - 1,
175
+ entity.start_char,
176
+ )
177
+ for entity in spacy_model(text).ents
178
+ ]
179
+ entities += spacy_entities
180
+ del spacy_entities
181
+
182
+ # Reformatting for consistency
165
183
  if entities:
166
184
  min_entity_score = min([e[2] for e in entities])
167
185
  max_entity_score = max([min_entity_score] + [e[2] for e in entities])
168
- entity_score_range = (
169
- 1
170
- if min_entity_score == max_entity_score
171
- else (max_entity_score - min_entity_score)
172
- )
173
- entities = [
174
- (e[0], e[1], (e[2] - min_entity_score) / entity_score_range, e[3])
175
- for e in entities
176
- ]
186
+ entity_score_range = 1 if min_entity_score == max_entity_score else (max_entity_score - min_entity_score)
187
+ entities = [(e[0], e[1], (e[2] - min_entity_score) / entity_score_range, e[3]) for e in entities]
177
188
  scores = list(np.searchsorted(sentence_starts, [e[3] + 1 for e in entities]))
178
189
  scores = [sentences[i - 1][1] for i in scores]
179
190
  scores = [scores[i] + int(10 * entities[i][2]) for i in range(len(entities))]
@@ -185,8 +196,7 @@ def compute_ner(language, sentences, fast=True, context_width=150):
185
196
  comentions = [
186
197
  entities[j][0]
187
198
  for j in range(len(entities))
188
- if j != i
189
- and abs(entities[j][3] - entity[3]) < math.ceil(context_width / 2)
199
+ if j != i and abs(entities[j][3] - entity[3]) < math.ceil(context_width / 2)
190
200
  ]
191
201
  entities[i] = (
192
202
  entity[0],
@@ -201,11 +211,7 @@ def compute_ner(language, sentences, fast=True, context_width=150):
201
211
  if entity[3] >= 0 and entity[3] < len(text):
202
212
  left = max(0, entity[3] - math.floor(context_width / 2))
203
213
  right = min(len(text), entity[3] + math.ceil(context_width / 2))
204
- context = (
205
- ("[..]" if left > 0 else "")
206
- + text[left:right]
207
- + ("[..]" if right < len(text) else "")
208
- )
214
+ context = ("[..]" if left > 0 else "") + text[left:right] + ("[..]" if right < len(text) else "")
209
215
  entities[i] = (
210
216
  entity[0],
211
217
  entity[1],
@@ -214,22 +220,12 @@ def compute_ner(language, sentences, fast=True, context_width=150):
214
220
  entity[4],
215
221
  entity[5],
216
222
  )
217
- entities = [
218
- NERObject(
219
- entities[i][0],
220
- entities[i][1],
221
- entities[i][2],
222
- entities[i][3],
223
- entities[i][4],
224
- entities[i][5],
225
- )
226
- for i in range(len(entities))
227
- ]
223
+ entities = [NERObject(*entities[i]) for i in range(len(entities))]
228
224
  return entities
229
225
 
230
226
 
231
227
  def get_extractive_summary(text, language, max_chars, fast=False, with_scores=False):
232
- tokenizer = Tokenizer(language)
228
+ tokenizer = get_nltk_tokenizer(language)
233
229
  stemmer = Stemmer(language)
234
230
  parser = PlaintextParser.from_string(text, tokenizer)
235
231
  if fast:
@@ -268,24 +264,33 @@ def get_extractive_summary(text, language, max_chars, fast=False, with_scores=Fa
268
264
  return summary
269
265
 
270
266
 
271
- def ner_pipe(text, language, fast=False, compression_ratio="auto"):
267
+ def ner_pipe(text, language, spacy_model, flair_model=None, fast=False, compression_ratio="auto"):
272
268
  if compression_ratio == "auto":
273
269
  compression_ratio = max(1.0, len(text) / 15000) if fast else 1.0
274
- sentences = get_extractive_summary(
275
- text, language, int(len(text) / compression_ratio), fast=fast, with_scores=True
276
- )
277
- ner = compute_ner(language, sentences, fast=fast)
270
+ sentences = get_extractive_summary(text, language, int(len(text) / compression_ratio), fast=fast, with_scores=True)
271
+ ner = compute_ner(language, sentences, spacy_model, flair_model)
278
272
  return ner
279
273
 
280
274
 
281
275
  def get_ner_handler(language, fast=False, compression_ratio="auto"):
282
276
  try:
283
- Tokenizer(language) # raises a LookupError if the language is not valid
277
+ get_nltk_tokenizer(language) # raises a LookupError if the language is not valid
284
278
  except LookupError:
285
279
  language = "english"
286
- return lambda text: ner_pipe(text, language, fast, compression_ratio)
280
+ spacy_model = SPACY_NER_MODELS.get(language, SPACY_NER_MODELS['english'])()
281
+ flair_model = None if fast else FLAIR_NER_MODELS.get(language, FLAIR_NER_MODELS['english'])()
282
+ return lambda text: ner_pipe(text, language, spacy_model, flair_model, fast, compression_ratio)
287
283
 
288
284
 
289
285
  @st.cache_resource
290
286
  def get_cached_ner_handler(language, fast):
291
287
  return get_ner_handler(language, fast)
288
+
289
+
290
+ def test():
291
+ text = """My name is Valerio Simoni, and I live in NYC. I love the Colosseum, and my phone is +123 456 789.
292
+ my email is aaa@ggg.com, but my ip address is secret! 123.123.123.0:1111"""
293
+ entities = get_ner_handler("english", True)(text)
294
+ print(entities)
295
+
296
+ test()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: streamlit-octostar-utils
3
- Version: 0.1.7a3
3
+ Version: 0.1.7a4
4
4
  Summary:
5
5
  License: MIT
6
6
  Author: Octostar
@@ -1,8 +1,8 @@
1
1
  streamlit_octostar_utils/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
2
2
  streamlit_octostar_utils/api_crafter/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
3
- streamlit_octostar_utils/api_crafter/celery.py,sha256=d6jRllPHQ_GpCR_DDofp1CZlDVgjKwQFXQqZyydf3Zk,29427
3
+ streamlit_octostar_utils/api_crafter/celery.py,sha256=RRkL2W2L17ML3vsSgg5pbngWLAhubnMvvumkP21szkA,29771
4
4
  streamlit_octostar_utils/api_crafter/fastapi.py,sha256=s4WayFitfS2fX_FGtv1s56RmLB9rx7cfwuFTZjzrKoY,14133
5
- streamlit_octostar_utils/api_crafter/nifi.py,sha256=fImkuVTI_eA7U3KGQUJZ_KZkYZ1f1I0VfShSM7yHVuc,44062
5
+ streamlit_octostar_utils/api_crafter/nifi.py,sha256=fQ5k9eZl2oSQZ2BexZYwKUiO05-FryUi7wnCd_56P9Y,44375
6
6
  streamlit_octostar_utils/api_crafter/parser/__init__.py,sha256=YeYWF6sdQiCFV_RKNW2t9Vs6KJExE2pbXxWTe_DOayY,107
7
7
  streamlit_octostar_utils/api_crafter/parser/combine_fields.py,sha256=ddc44xkajw8MU0peAX_263DL7rPXbTKbHUjpOhRgvyU,8790
8
8
  streamlit_octostar_utils/api_crafter/parser/entities_parser.py,sha256=7mUxwcOcaIhDx3GnJ42p5L0PUfAiEvE_fkopNIujlL8,30067
@@ -21,8 +21,8 @@ streamlit_octostar_utils/core/threading/key_queue.py,sha256=7CJpj0gvZMQd8eC5wKQi
21
21
  streamlit_octostar_utils/core/timestamp.py,sha256=a3s4xfm1nctLzYsHOJxqoWIDTdbNY_yN1OByl8ahLc8,383
22
22
  streamlit_octostar_utils/hello.py,sha256=JVeug8fnyYYf_qw6eeMDBrdqsSkiwnSHdPvb9puEGdA,69
23
23
  streamlit_octostar_utils/nlp/__init__.py,sha256=BtlYDZK_xaEbc7Ju_7MznXbCVPZcdLn26xwR9qf_UhM,336
24
- streamlit_octostar_utils/nlp/language.py,sha256=gQnti-h56SIqfwit6noH9UVHEG_A9YkS9gnhUhDyrew,537
25
- streamlit_octostar_utils/nlp/ner.py,sha256=k1Fq0ohsCH4eBgXZtXqy9V3kNIZdQOjRrC-M9CZtytI,10806
24
+ streamlit_octostar_utils/nlp/language.py,sha256=13f6kAALYjC3EclBzcyKPd4GlIeIR1mWPu3S6d-z814,549
25
+ streamlit_octostar_utils/nlp/ner.py,sha256=mMCbUydVDkjl2seB5dlp5NXSqi3-hemurk1OexNJZzE,11611
26
26
  streamlit_octostar_utils/octostar/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
27
27
  streamlit_octostar_utils/octostar/client.py,sha256=29tA1LY9ndLzAMR_KlL7Jb071YNAanubsppzy2BXb1E,1779
28
28
  streamlit_octostar_utils/octostar/context.py,sha256=TpucK48EbeVy4vDqKd9UULEtr1JOY-_4nBs-rXZzESw,212
@@ -37,7 +37,7 @@ streamlit_octostar_utils/threading/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzp
37
37
  streamlit_octostar_utils/threading/async_task_manager.py,sha256=q7N6YZwUvIYMzkSHmsJNheNVCv93c03H6Hyg9uH8pvk,4747
38
38
  streamlit_octostar_utils/threading/session_callback_manager.py,sha256=LvZVP4g6tvKtYmI13f2j1sX_7hm61Groqp5xJine9_k,3973
39
39
  streamlit_octostar_utils/threading/session_state_hot_swapper.py,sha256=6eeCQI6A42hp4DmW2NQw2rbeR-k9N8DhfBKQdN_fbLU,811
40
- streamlit_octostar_utils-0.1.7a3.dist-info/LICENSE,sha256=dkwVPyV03fPHHtERnF6RnvRXcll__tud9gWca1RcgnQ,1073
41
- streamlit_octostar_utils-0.1.7a3.dist-info/METADATA,sha256=IzeI1n1njM88-VnU1n3Wjwglz0-NsdKpqZGkBR9C_B8,2267
42
- streamlit_octostar_utils-0.1.7a3.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
43
- streamlit_octostar_utils-0.1.7a3.dist-info/RECORD,,
40
+ streamlit_octostar_utils-0.1.7a4.dist-info/LICENSE,sha256=dkwVPyV03fPHHtERnF6RnvRXcll__tud9gWca1RcgnQ,1073
41
+ streamlit_octostar_utils-0.1.7a4.dist-info/METADATA,sha256=qyNzLU0FuRUB0VI7RY6F3xXPH5in1Iv_pNr9kZobyho,2267
42
+ streamlit_octostar_utils-0.1.7a4.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
43
+ streamlit_octostar_utils-0.1.7a4.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.1
2
+ Generator: poetry-core 2.1.2
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any