glchat-plugin 0.4.5__py3-none-any.whl → 0.4.7__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.
@@ -9,11 +9,12 @@ Authors:
9
9
 
10
10
  import inspect
11
11
  import traceback
12
+ from collections import OrderedDict
13
+ from threading import Lock
12
14
  from typing import Any, Type
13
15
 
14
16
  from bosa_core import Plugin
15
17
  from bosa_core.plugin.handler import PluginHandler
16
- from gllm_core.schema import Tool
17
18
  from gllm_core.utils import LoggerManager
18
19
  from gllm_inference.catalog import LMRequestProcessorCatalog, PromptBuilderCatalog
19
20
  from gllm_pipeline.pipeline.pipeline import Pipeline
@@ -138,6 +139,7 @@ logger = LoggerManager().get_logger(__name__)
138
139
 
139
140
 
140
141
  INTERNAL_PIPELINES = ["preprocessing", "postprocessing"]
142
+ DEFAULT_MAX_PIPELINE_CACHE_SIZE = 300
141
143
 
142
144
 
143
145
  class PipelineHandler(PluginHandler):
@@ -155,8 +157,10 @@ class PipelineHandler(PluginHandler):
155
157
  Mapping of chatbot IDs to their pipeline builder plugins.
156
158
  _plugins (dict[tuple[str, str], Plugin]):
157
159
  Mapping of pipeline types to their plugins.
158
- _pipeline_cache (dict[tuple[str, str, str], Pipeline]):
159
- Cache mapping (chatbot_id, model_id, organization_id) to Pipeline instances.
160
+ _pipeline_cache (OrderedDict[tuple[str, str, str], PipelineBundle]):
161
+ Cache mapping (chatbot_id, model_id, organization_id) to PipelineBundle instances for non-internal pipelines.
162
+ _internal_pipeline_cache (dict[tuple[str, str, str], PipelineBundle]):
163
+ Cache mapping (chatbot_id, model_id, organization_id) to PipelineBundle instances for internal pipelines.
160
164
  _chatbot_pipeline_keys (dict[tuple[str, str], set[tuple[str, str, str]]]):
161
165
  Mapping of chatbot IDs to their pipeline keys.
162
166
  _pipeline_build_errors (dict[tuple[str, str, str], str]):
@@ -168,11 +172,19 @@ class PipelineHandler(PluginHandler):
168
172
  _chatbot_configs: dict[tuple[str, str], ChatbotConfig] = {}
169
173
  _builders: dict[tuple[str, str], Plugin] = {}
170
174
  _plugins: dict[tuple[str, str], Plugin] = {}
171
- _pipeline_cache: dict[tuple[str, str, str], PipelineBundle] = {}
175
+ # LRU cache for non-internal pipelines
176
+ _pipeline_cache: OrderedDict[tuple[str, str, str], PipelineBundle] = OrderedDict()
177
+ # Simple cache for internal pipelines (e.g., __preprocessing__, __postprocessing__)
178
+ _internal_pipeline_cache: dict[tuple[str, str, str], PipelineBundle] = {}
172
179
  _chatbot_pipeline_keys: dict[tuple[str, str], set[tuple[str, str, str]]] = {}
173
180
  _pipeline_build_errors: dict[tuple[str, str, str], str] = {}
174
181
 
175
- def __init__(self, app_config: dict[str, AppConfig], chat_history_storage: BaseChatHistoryStorage):
182
+ def __init__(
183
+ self,
184
+ app_config: dict[str, AppConfig],
185
+ chat_history_storage: BaseChatHistoryStorage,
186
+ max_pipeline_cache_size: int | None = None,
187
+ ):
176
188
  """Initialize the pipeline handler.
177
189
 
178
190
  Args:
@@ -181,6 +193,14 @@ class PipelineHandler(PluginHandler):
181
193
  """
182
194
  self.app_config = app_config
183
195
  self.chat_history_storage = chat_history_storage
196
+ # Effective max size for the non-internal LRU cache. If not provided, use the default.
197
+ self._max_pipeline_cache_size = (
198
+ DEFAULT_MAX_PIPELINE_CACHE_SIZE
199
+ if max_pipeline_cache_size is None
200
+ else max_pipeline_cache_size
201
+ )
202
+ # Lock to guard cache and metadata updates across concurrent async calls.
203
+ self._cache_lock: Lock = Lock()
184
204
  self._prepare_pipelines()
185
205
 
186
206
  @classmethod
@@ -236,7 +256,7 @@ class PipelineHandler(PluginHandler):
236
256
  if pipeline_type != instance._chatbot_configs[chatbot_organization_id].pipeline_type:
237
257
  continue
238
258
 
239
- await cls._build_plugin(instance, chatbot_id, preset.supported_models, plugin, organization_id)
259
+ await cls._build_plugin(instance, chatbot_id, [], plugin, organization_id)
240
260
  except Exception as e:
241
261
  logger.warning(f"Failed when ainit pliugin {traceback.format_exc()}")
242
262
  logger.warning(f"Error initializing plugin for chatbot `{chatbot_id}`: {e}")
@@ -262,6 +282,7 @@ class PipelineHandler(PluginHandler):
262
282
  supported_models: list[dict[str, Any]],
263
283
  plugin: Plugin,
264
284
  organization_id: str = DEFAULT_ORGANIZATION_ID,
285
+ is_build_internal: bool = True,
265
286
  ) -> None:
266
287
  """Build a plugin for the given chatbot.
267
288
 
@@ -271,6 +292,7 @@ class PipelineHandler(PluginHandler):
271
292
  supported_models (list[dict[str, Any]]): List of models (including config).
272
293
  plugin (Plugin): The pipeline builder plugin instance.
273
294
  organization_id (str): The organization ID.
295
+ is_build_internal (bool): Whether to build internal pipelines.
274
296
  """
275
297
  chatbot_organization_id = (chatbot_id, organization_id)
276
298
 
@@ -282,38 +304,43 @@ class PipelineHandler(PluginHandler):
282
304
 
283
305
  instance._builders[chatbot_organization_id] = plugin
284
306
 
285
- for pipeline_type in INTERNAL_PIPELINES:
286
- pipeline_type_organization_id = (pipeline_type, organization_id)
287
- internal_plugin = instance._plugins.get(pipeline_type_organization_id)
288
- if internal_plugin:
289
- try:
290
- internal_plugin.prompt_builder_catalogs = prompt_builder_catalogs
291
- internal_plugin.lmrp_catalogs = lmrp_catalogs
292
- pipeline_config = instance._chatbot_configs[chatbot_organization_id].pipeline_config.copy()
293
- pipeline = await cls._call_build(
294
- internal_plugin,
295
- pipeline_config,
296
- prompt_builder_catalogs,
297
- lmrp_catalogs,
298
- )
299
- pipeline_tools = await internal_plugin.build_tools(pipeline_config)
300
- pipeline_key = (chatbot_id, f"__{pipeline_type}__", organization_id)
301
- instance._chatbot_pipeline_keys.setdefault(chatbot_organization_id, set()).add(pipeline_key)
302
- instance._pipeline_cache[pipeline_key] = PipelineBundle(pipeline, pipeline_tools)
303
-
304
- # Clear any previous error for this internal pipeline if build succeeded
305
- instance._pipeline_build_errors.pop(pipeline_key, None)
306
- except Exception as e:
307
- error_message = (
308
- f"Error building internal pipeline `{pipeline_type}` for chatbot `{chatbot_id}`: {e}"
309
- )
310
- logger.warning(f"Failed when ainit plugin {traceback.format_exc()}")
311
- logger.warning(error_message)
312
-
313
- # Store the error message for later retrieval
314
- instance._store_pipeline_build_error(
315
- chatbot_id, f"__{pipeline_type}__", organization_id, error_message
316
- )
307
+ if is_build_internal:
308
+ for pipeline_type in INTERNAL_PIPELINES:
309
+ pipeline_type_organization_id = (pipeline_type, organization_id)
310
+ internal_plugin = instance._plugins.get(pipeline_type_organization_id)
311
+ if internal_plugin:
312
+ try:
313
+ internal_plugin.prompt_builder_catalogs = prompt_builder_catalogs
314
+ internal_plugin.lmrp_catalogs = lmrp_catalogs
315
+ pipeline_config = instance._chatbot_configs[chatbot_organization_id].pipeline_config.copy()
316
+ pipeline = await cls._call_build(
317
+ internal_plugin,
318
+ pipeline_config,
319
+ prompt_builder_catalogs,
320
+ lmrp_catalogs,
321
+ )
322
+ pipeline_tools = await internal_plugin.build_tools(
323
+ pipeline_config, prompt_builder_catalogs, lmrp_catalogs
324
+ )
325
+ pipeline_key = (chatbot_id, f"__{pipeline_type}__", organization_id)
326
+ with instance._cache_lock:
327
+ instance._chatbot_pipeline_keys.setdefault(chatbot_organization_id, set()).add(pipeline_key)
328
+ # Store internal pipelines in a dedicated cache without LRU eviction.
329
+ instance._internal_pipeline_cache[pipeline_key] = PipelineBundle(pipeline, pipeline_tools)
330
+
331
+ # Clear any previous error for this internal pipeline if build succeeded
332
+ instance._pipeline_build_errors.pop(pipeline_key, None)
333
+ except Exception as e:
334
+ error_message = (
335
+ f"Error building internal pipeline `{pipeline_type}` for chatbot `{chatbot_id}`: {e}"
336
+ )
337
+ logger.warning(f"Failed when ainit plugin {traceback.format_exc()}")
338
+ logger.warning(error_message)
339
+
340
+ # Store the error message for later retrieval
341
+ instance._store_pipeline_build_error(
342
+ chatbot_id, f"__{pipeline_type}__", organization_id, error_message
343
+ )
317
344
 
318
345
  for model in supported_models:
319
346
  try:
@@ -336,10 +363,11 @@ class PipelineHandler(PluginHandler):
336
363
  prompt_builder_catalogs,
337
364
  lmrp_catalogs,
338
365
  )
339
- pipeline_tools = await plugin.build_tools(pipeline_config)
366
+ pipeline_tools = await plugin.build_tools(pipeline_config, prompt_builder_catalogs, lmrp_catalogs)
340
367
  pipeline_key = (chatbot_id, str(model_id), organization_id)
341
- instance._chatbot_pipeline_keys.setdefault(chatbot_organization_id, set()).add(pipeline_key)
342
- instance._pipeline_cache[pipeline_key] = PipelineBundle(pipeline, pipeline_tools)
368
+ with instance._cache_lock:
369
+ instance._chatbot_pipeline_keys.setdefault(chatbot_organization_id, set()).add(pipeline_key)
370
+ instance._set_pipeline_cache_unlocked(pipeline_key, PipelineBundle(pipeline, pipeline_tools))
343
371
 
344
372
  # Clear any previous error for this pipeline if build succeeded
345
373
  instance._pipeline_build_errors.pop(pipeline_key, None)
@@ -501,7 +529,7 @@ class PipelineHandler(PluginHandler):
501
529
  return
502
530
 
503
531
  # Use the existing _build_plugin method to rebuild the pipeline
504
- await __class__._build_plugin(self, chatbot_id, [model_config], plugin, organization_id)
532
+ await __class__._build_plugin(self, chatbot_id, [model_config], plugin, organization_id, False)
505
533
 
506
534
  logger.info(f"Successfully rebuilt pipeline for chatbot `{chatbot_organization_id}` model `{model_id}`")
507
535
 
@@ -530,14 +558,14 @@ class PipelineHandler(PluginHandler):
530
558
  """
531
559
  pipeline_key = (chatbot_id, str(model_id), organization_id)
532
560
 
533
- if pipeline_key not in self._pipeline_cache:
534
- logger.warning(
561
+ if not self._has_pipeline_in_cache(pipeline_key):
562
+ logger.info(
535
563
  f"Pipeline not found for chatbot `{chatbot_id}` model `{model_id}`, attempting to rebuild..."
536
564
  )
537
565
  # Try to rebuild the pipeline if it's not found
538
566
  await self._async_rebuild_pipeline(chatbot_id, str(model_id), organization_id)
539
567
 
540
- if pipeline_key not in self._pipeline_cache:
568
+ if not self._has_pipeline_in_cache(pipeline_key):
541
569
  # Check if there's a stored error message for this pipeline
542
570
  stored_error = self.get_pipeline_build_error(chatbot_id, str(model_id), organization_id)
543
571
  if stored_error:
@@ -550,7 +578,26 @@ class PipelineHandler(PluginHandler):
550
578
  f"Pipeline for chatbot `{chatbot_id}` model `{model_id}` not found and could not be rebuilt"
551
579
  )
552
580
 
553
- return self._pipeline_cache[pipeline_key]
581
+ # Final read and LRU update need to be atomic with respect to evictions.
582
+ with self._cache_lock:
583
+ # Internal pipelines are served from the dedicated cache without LRU behaviour.
584
+ bundle = self._internal_pipeline_cache.get(pipeline_key)
585
+ if bundle:
586
+ return bundle
587
+
588
+ # For non-internal pipelines, update LRU order on access.
589
+ bundle = self._pipeline_cache.get(pipeline_key)
590
+ self._pipeline_cache.move_to_end(pipeline_key)
591
+ return bundle
592
+
593
+
594
+ def _has_pipeline_in_cache(self, pipeline_key: tuple[str, str, str]) -> bool:
595
+ """Return True if the given pipeline key exists in either cache.
596
+
597
+ Args:
598
+ pipeline_key (tuple[str, str, str]): The key for the pipeline bundle.
599
+ """
600
+ return pipeline_key in self._pipeline_cache or pipeline_key in self._internal_pipeline_cache
554
601
 
555
602
  def get_pipeline_config(self, chatbot_id: str, organization_id: str = DEFAULT_ORGANIZATION_ID) -> dict[str, Any]:
556
603
  """Get the pipeline configuration by chatbot ID.
@@ -643,8 +690,7 @@ class PipelineHandler(PluginHandler):
643
690
  lmrp_catalogs=pipeline_info["lmrp_catalogs"],
644
691
  )
645
692
 
646
- supported_models = list(pipeline_info["config"].get("supported_models", {}).values())
647
- await __class__._build_plugin(self, chatbot_id, supported_models, plugin, organization_id)
693
+ await __class__._build_plugin(self, chatbot_id, [], plugin, organization_id)
648
694
 
649
695
  async def delete_chatbot(self, chatbot_id: str, organization_id: str = DEFAULT_ORGANIZATION_ID) -> None:
650
696
  """Delete a chatbot.
@@ -654,8 +700,9 @@ class PipelineHandler(PluginHandler):
654
700
  organization_id (str): The organization ID.
655
701
  """
656
702
  chatbot_organization_id = (chatbot_id, organization_id)
657
- for pipeline_key in self._chatbot_pipeline_keys.get(chatbot_organization_id, set()):
658
- self._pipeline_cache.pop(pipeline_key, None)
703
+ # Iterate over a copy to avoid mutating the set during iteration.
704
+ for pipeline_key in list(self._chatbot_pipeline_keys.get(chatbot_organization_id, set())):
705
+ self._remove_pipeline_cache(pipeline_key)
659
706
 
660
707
  # Clear stored error messages for this chatbot
661
708
  error_keys_to_remove = [key for key in self._pipeline_build_errors.keys() if key[0] == chatbot_organization_id]
@@ -704,9 +751,10 @@ class PipelineHandler(PluginHandler):
704
751
  new_pipeline_key = (chatbot_id, str(model_id), organization_id)
705
752
  new_pipeline_keys.add(new_pipeline_key)
706
753
 
707
- for pipeline_key in self._chatbot_pipeline_keys.get(chatbot_organization_id, set()):
754
+ # Iterate over a copy to avoid mutating the set during iteration.
755
+ for pipeline_key in list(self._chatbot_pipeline_keys.get(chatbot_organization_id, set())):
708
756
  if pipeline_key not in new_pipeline_keys:
709
- self._pipeline_cache.pop(pipeline_key, None)
757
+ self._remove_pipeline_cache(pipeline_key)
710
758
 
711
759
  self._chatbot_pipeline_keys[chatbot_organization_id] = set()
712
760
 
@@ -719,7 +767,7 @@ class PipelineHandler(PluginHandler):
719
767
  self._builders.pop(chatbot_organization_id, None)
720
768
  self._builders[chatbot_organization_id] = plugin
721
769
 
722
- await __class__._build_plugin(self, chatbot_id, supported_models, plugin, organization_id)
770
+ await __class__._build_plugin(self, chatbot_id, [], plugin, organization_id)
723
771
  except Exception as e:
724
772
  logger.warning(f"Error updating chatbot `{chatbot_id}`: {e}")
725
773
 
@@ -757,6 +805,73 @@ class PipelineHandler(PluginHandler):
757
805
  chatbot_preset_map=chatbot_preset_map[org_id],
758
806
  )
759
807
 
808
+ def _set_pipeline_cache(self, pipeline_key: tuple[str, str, str], bundle: PipelineBundle) -> None:
809
+ """Public entrypoint to update the LRU cache with locking.
810
+
811
+ Args:
812
+ pipeline_key (tuple[str, str, str]): The key for the pipeline bundle.
813
+ bundle (PipelineBundle): The pipeline bundle to store.
814
+ """
815
+ with self._cache_lock:
816
+ self._set_pipeline_cache_unlocked(pipeline_key, bundle)
817
+
818
+ def _set_pipeline_cache_unlocked(self, pipeline_key: tuple[str, str, str], bundle: PipelineBundle) -> None:
819
+ """Insert or update a pipeline bundle in the LRU cache and evict oldest entries when over capacity.
820
+
821
+ This method assumes the caller already holds ``self._cache_lock``.
822
+
823
+ Args:
824
+ pipeline_key (tuple[str, str, str]): The key for the pipeline bundle.
825
+ bundle (PipelineBundle): The pipeline bundle to store.
826
+ """
827
+ try:
828
+ if pipeline_key in self._pipeline_cache:
829
+ self._pipeline_cache.move_to_end(pipeline_key)
830
+ self._pipeline_cache[pipeline_key] = bundle
831
+ while len(self._pipeline_cache) > self._max_pipeline_cache_size:
832
+ # The first key in the OrderedDict is the least recently used.
833
+ oldest_key = next(iter(self._pipeline_cache))
834
+ self._remove_pipeline_cache_unlocked(oldest_key)
835
+ except Exception:
836
+ logger.warning("Error while updating pipeline LRU cache: %s", traceback.format_exc())
837
+ def _remove_pipeline_cache(self, pipeline_key: tuple[str, str, str]) -> None:
838
+ """Public entrypoint to remove a pipeline from caches and metadata with locking.
839
+
840
+ Args:
841
+ pipeline_key (tuple[str, str, str]): The key for the pipeline bundle.
842
+ """
843
+ with self._cache_lock:
844
+ self._remove_pipeline_cache_unlocked(pipeline_key)
845
+
846
+ def _remove_pipeline_cache_unlocked(self, pipeline_key: tuple[str, str, str]) -> None:
847
+ """Remove a pipeline bundle from cache and associated tracking structures.
848
+
849
+ This deletes the entry from `_pipeline_cache`, `_internal_pipeline_cache`,
850
+ `_pipeline_build_errors`, and the corresponding set in `_chatbot_pipeline_keys`
851
+ (if present).
852
+
853
+ This method assumes the caller already holds ``self._cache_lock``.
854
+
855
+ Args:
856
+ pipeline_key (tuple[str, str, str]): The key for the pipeline bundle.
857
+ """
858
+ try:
859
+ chatbot_id, _model_id, organization_id = pipeline_key
860
+
861
+ # Remove from caches
862
+ self._pipeline_cache.pop(pipeline_key, None)
863
+ self._internal_pipeline_cache.pop(pipeline_key, None)
864
+
865
+ # Remove any stored build error for this specific pipeline key
866
+ self._pipeline_build_errors.pop(pipeline_key, None)
867
+
868
+ # Remove from chatbot-to-pipeline-keys mapping
869
+ chatbot_organization_id = (chatbot_id, organization_id)
870
+ if chatbot_organization_id in self._chatbot_pipeline_keys:
871
+ self._chatbot_pipeline_keys[chatbot_organization_id].discard(pipeline_key)
872
+ except Exception:
873
+ logger.warning("Error while removing pipeline from cache: %s", traceback.format_exc())
874
+
760
875
  def _validate_pipeline(self, chatbot_id: str, organization_id: str) -> None:
761
876
  """Validate the pipeline configuration exists.
762
877
 
@@ -814,7 +929,7 @@ class PipelineHandler(PluginHandler):
814
929
  """
815
930
  chatbot_organization_id = (chatbot_id, organization_id)
816
931
  if chatbot_organization_id not in self._builders:
817
- logger.warning(
932
+ logger.info(
818
933
  f"Pipeline builder not found for chatbot `{chatbot_organization_id}`, attempting to rebuild..."
819
934
  )
820
935
  # Try to rebuild the plugin if it's not found
@@ -871,7 +986,7 @@ class PipelineHandler(PluginHandler):
871
986
  return
872
987
 
873
988
  # Use the existing _build_plugin method to rebuild the plugin
874
- await __class__._build_plugin(self, chatbot_id, supported_models, plugin, organization_id)
989
+ await __class__._build_plugin(self, chatbot_id, [], plugin, organization_id)
875
990
 
876
991
  logger.info(f"Successfully rebuilt pipeline builder for chatbot `{chatbot_id}`")
877
992
 
@@ -137,6 +137,8 @@ class PipelineBuilderPlugin(Plugin, Generic[PipelineState, PipelinePresetConfig]
137
137
  async def build_tools(
138
138
  self,
139
139
  pipeline_config: dict[str, Any],
140
+ prompt_builder_catalogs: dict[str, BaseCatalog[Any]] | None = None,
141
+ lmrp_catalogs: dict[str, BaseCatalog[Any]] | None = None,
140
142
  ) -> list[ToolProcessor]:
141
143
  """Build a pipeline instance.
142
144
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: glchat-plugin
3
- Version: 0.4.5
3
+ Version: 0.4.7
4
4
  Author-email: GenAI SDK Team <gat-sdk@gdplabs.id>
5
5
  Requires-Python: <3.13,>=3.11
6
6
  Description-Content-Type: text/markdown
@@ -8,8 +8,8 @@ glchat_plugin/handler/__init__.py,sha256=H5DJaAfwwtRsvMcOaEzHfGMQk25H7la0E7uPfks
8
8
  glchat_plugin/handler/base_post_login_handler.py,sha256=48xSbe_LwTCjRY-lCuzWXqbnEr1ql8bAhQih1Xeh8f8,2835
9
9
  glchat_plugin/pipeline/__init__.py,sha256=Sk-NfIGyA9VKIg0Bt5OHatNUYyWVPh9i5xhE5DFAfbo,41
10
10
  glchat_plugin/pipeline/base_pipeline_preset_config.py,sha256=lbMH8y_HU3LrqtMYXLzQ2906ZkMXARKY5vBuIGvRVdA,2969
11
- glchat_plugin/pipeline/pipeline_handler.py,sha256=sBD9Ohkd6GIIiTxgRAm81HuQJIzcNmV7KjE3hHQiftw,38108
12
- glchat_plugin/pipeline/pipeline_plugin.py,sha256=fDQWJkEMgbbY6bK81gHjzp3fLZZN0a0Pv6_DS4wSKL0,5040
11
+ glchat_plugin/pipeline/pipeline_handler.py,sha256=lCIbLjuSXR8dKo5tdrqb3Zey5ZIlm9dmP4BHuKOKENs,43886
12
+ glchat_plugin/pipeline/pipeline_plugin.py,sha256=xUOVnMu2Skc1QseVpCAm45YBFnQIOEuSAqpHoBVsyJc,5182
13
13
  glchat_plugin/pipeline/tool_processor.py,sha256=pc-uHoBqOSXjLM8R_Wgws_z5ehPtaSeb9Bhk9aeLDus,2731
14
14
  glchat_plugin/service/__init__.py,sha256=9T4qzyYL052qLqva5el1F575OTRNaaf9tb9UvW-leTc,47
15
15
  glchat_plugin/service/base_rate_limiter_service.py,sha256=tgKwdr4EqnGo5iDRVJPnlg8W9q0hiUzfeewAtdW4IjU,1232
@@ -20,7 +20,7 @@ glchat_plugin/storage/base_anonymizer_storage.py,sha256=oFwovWrsjM7v1YjeN-4p-M3O
20
20
  glchat_plugin/storage/base_chat_history_storage.py,sha256=YIGM8zv7s5BQ8O7W1LfaLyQW4SF9Bt3aolowsoDaQw8,11257
21
21
  glchat_plugin/tools/__init__.py,sha256=OFotHbgQ8mZEbdlvlv5aVMdxfubPvkVWAcTwhIPdIqQ,542
22
22
  glchat_plugin/tools/decorators.py,sha256=AvQBV18wzXWdC483RSSmpfh92zsqTyp8SzDLIkreIGU,3925
23
- glchat_plugin-0.4.5.dist-info/METADATA,sha256=8lU2TR_qmgA8oM25F_Kq7g0v5YAKiGJuLUsFvKO2P1k,2063
24
- glchat_plugin-0.4.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
25
- glchat_plugin-0.4.5.dist-info/top_level.txt,sha256=fzKSXmct5dY4CAKku4-mkdHX-QPAyQVvo8vpQj8qizY,14
26
- glchat_plugin-0.4.5.dist-info/RECORD,,
23
+ glchat_plugin-0.4.7.dist-info/METADATA,sha256=LZHDE9aYJLhVudqVLZgVbmR3WjeqOUHrKZlIR60PEkE,2063
24
+ glchat_plugin-0.4.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
25
+ glchat_plugin-0.4.7.dist-info/top_level.txt,sha256=fzKSXmct5dY4CAKku4-mkdHX-QPAyQVvo8vpQj8qizY,14
26
+ glchat_plugin-0.4.7.dist-info/RECORD,,