glchat-plugin 0.2.0__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.
@@ -0,0 +1,602 @@
1
+ """Pipeline handler for pipeline builder plugins.
2
+
3
+ This handler manages pipeline builder plugins and provides necessary services.
4
+
5
+ Authors:
6
+ Samuel Lusandi (samuel.lusandi@gdplabs.id)
7
+ Hermes Vincentius Gani (hermes.v.gani@gdplabs.id)
8
+ """
9
+
10
+ import traceback
11
+ from typing import Any, Type
12
+
13
+ from bosa_core import Plugin
14
+ from bosa_core.plugin.handler import PluginHandler
15
+ from gllm_core.utils import LoggerManager
16
+ from gllm_inference.catalog import LMRequestProcessorCatalog, PromptBuilderCatalog
17
+ from gllm_pipeline.pipeline.pipeline import Pipeline
18
+ from pydantic import BaseModel, ConfigDict
19
+
20
+ from glchat_plugin.config.app_config import AppConfig
21
+ from glchat_plugin.storage.base_chat_history_storage import BaseChatHistoryStorage
22
+
23
+
24
+ class ChatbotConfig(BaseModel):
25
+ """Chatbot configuration class containing pipeline configs and metadata.
26
+
27
+ Attributes:
28
+ pipeline_type (str): Type of pipeline to use.
29
+ pipeline_config (dict[str, Any]): Pipeline configuration dictionary.
30
+ prompt_builder_catalogs (dict[str, PromptBuilderCatalog] | None): Mapping of prompt builder catalogs.
31
+ lmrp_catalogs (dict[str, LMRequestProcessorCatalog] | None): Mapping of LM request processor catalogs.
32
+ """
33
+
34
+ pipeline_type: str
35
+ pipeline_config: dict[str, Any]
36
+ prompt_builder_catalogs: dict[str, PromptBuilderCatalog] | None
37
+ lmrp_catalogs: dict[str, LMRequestProcessorCatalog] | None
38
+ model_config = ConfigDict(arbitrary_types_allowed=True)
39
+
40
+
41
+ class PipelinePresetConfig(BaseModel):
42
+ """Pipeline preset configuration class.
43
+
44
+ Attributes:
45
+ preset_id (str): Unique identifier for the pipeline preset.
46
+ supported_models (list[dict[str, Any]]): List of models (including config) supported by this preset.
47
+ """
48
+
49
+ preset_id: str
50
+ supported_models: list[dict[str, Any]]
51
+
52
+
53
+ class ChatbotPresetMapping(BaseModel):
54
+ """Chatbot preset mapping.
55
+
56
+ Attributes:
57
+ pipeline_type (str): Type of pipeline.
58
+ chatbot_preset_map (dict[str, PipelinePresetConfig]):
59
+ Mapping of chatbot IDs to their pipeline preset configurations.
60
+ """
61
+
62
+ pipeline_type: str
63
+ chatbot_preset_map: dict[str, PipelinePresetConfig]
64
+
65
+
66
+ logger = LoggerManager().get_logger(__name__)
67
+
68
+
69
+ class PipelineHandler(PluginHandler):
70
+ """Handler for pipeline builder plugins.
71
+
72
+ This handler manages pipeline builder plugins and provides caching for built pipelines.
73
+
74
+ Attributes:
75
+ app_config (AppConfig): Application configuration.
76
+ _activated_configs (dict[str, ChatbotPresetMapping]): Collection of chatbot preset mapping by pipeline types.
77
+ _chatbot_configs (dict[str, ChatbotConfig]): Mapping of chatbot IDs to their configurations.
78
+ _builders (dict[str, Plugin]): Mapping of chatbot IDs to their pipeline builder plugins.
79
+ _plugins (dict[str, Plugin]): Mapping of pipeline types to their plugins.
80
+ _pipeline_cache (dict[tuple[str, str], Pipeline]):
81
+ Cache mapping (chatbot_id, model_id) to Pipeline instances.
82
+ _chatbot_pipeline_keys (dict[str, set[tuple[str, str]]]): Mapping of chatbot IDs to their pipeline keys.
83
+ """
84
+
85
+ app_config: AppConfig
86
+ _activated_configs: dict[str, ChatbotPresetMapping] = {}
87
+ _chatbot_configs: dict[str, ChatbotConfig] = {}
88
+ _builders: dict[str, Plugin] = {}
89
+ _plugins: dict[str, Plugin] = {}
90
+ _pipeline_cache: dict[tuple[str, str], Pipeline] = {}
91
+ _chatbot_pipeline_keys: dict[str, set[tuple[str, str]]] = {}
92
+
93
+ def __init__(self, app_config: AppConfig, chat_history_storage: BaseChatHistoryStorage):
94
+ """Initialize the pipeline handler.
95
+
96
+ Args:
97
+ app_config (AppConfig): Application configuration.
98
+ chat_history_storage (BaseChatHistoryStorage): Chat history storage.
99
+ """
100
+ self.app_config = app_config
101
+ self.chat_history_storage = chat_history_storage
102
+ self._prepare_pipelines()
103
+
104
+ @classmethod
105
+ def create_injections(cls, instance: "PipelineHandler") -> dict[Type[Any], Any]:
106
+ """Create injection mappings for pipeline builder plugins.
107
+
108
+ Args:
109
+ instance (PipelineHandler): The handler instance providing injections.
110
+
111
+ Returns:
112
+ dict[Type[Any], Any]: Dictionary mapping service types to their instances.
113
+ """
114
+ return {
115
+ AppConfig: instance.app_config,
116
+ BaseChatHistoryStorage: instance.chat_history_storage,
117
+ }
118
+
119
+ @classmethod
120
+ def initialize_plugin(cls, instance: "PipelineHandler", plugin: Plugin) -> None:
121
+ """Initialize plugin-specific resources.
122
+
123
+ This method is called after plugin creation and service injection.
124
+ For each pipeline builder plugin, we build pipelines for all supported models and cache them.
125
+
126
+ Args:
127
+ instance (PipelineHandler): The handler instance.
128
+ plugin (Plugin): The pipeline builder plugin instance.
129
+ """
130
+ pass
131
+
132
+ @classmethod
133
+ async def ainitialize_plugin(cls, instance: "PipelineHandler", plugin: Plugin) -> None:
134
+ """Initialize plugin-specific resources.
135
+
136
+ This method is called after plugin creation and service injection.
137
+ For each pipeline builder plugin, we build pipelines for all supported models and cache them.
138
+
139
+ Args:
140
+ instance (PipelineHandler): The handler instance.
141
+ plugin (Plugin): The pipeline builder plugin instance.
142
+ """
143
+ pipeline_type = plugin.name
144
+ instance._plugins[pipeline_type] = plugin
145
+
146
+ if pipeline_type not in instance._activated_configs:
147
+ return
148
+
149
+ active_config = instance._activated_configs[pipeline_type]
150
+ for chatbot_id, preset in active_config.chatbot_preset_map.items():
151
+ try:
152
+ if pipeline_type != instance._chatbot_configs[chatbot_id].pipeline_type:
153
+ continue
154
+
155
+ await cls._build_plugin(instance, chatbot_id, preset.supported_models, plugin)
156
+ except Exception as e:
157
+ logger.warning(f"Failed when ainit pliugin {traceback.format_exc()}")
158
+ logger.warning(f"Error initializing plugin for chatbot `{chatbot_id}`: {e}")
159
+
160
+ @classmethod
161
+ async def acleanup_plugins(cls, instance: "PipelineHandler") -> None:
162
+ """Cleanup all plugins.
163
+
164
+ Args:
165
+ instance (PipelineHandler): The handler instance.
166
+ """
167
+ for plugin in instance._plugins.values():
168
+ try:
169
+ await plugin.cleanup()
170
+ except Exception as e:
171
+ logger.warning(f"Error cleaning up plugin `{plugin.name}`: {e}")
172
+
173
+ @classmethod
174
+ async def _build_plugin(
175
+ cls, instance: "PipelineHandler", chatbot_id: str, supported_models: list[dict[str, Any]], plugin: Plugin
176
+ ) -> None:
177
+ """Build a plugin for the given chatbot.
178
+
179
+ Args:
180
+ instance (PipelineHandler): The handler instance.
181
+ chatbot_id (str): The chatbot ID.
182
+ supported_models (list[dict[str, Any]]): List of models (including config).
183
+ plugin (Plugin): The pipeline builder plugin instance.
184
+ """
185
+ plugin.prompt_builder_catalogs = instance._chatbot_configs[chatbot_id].prompt_builder_catalogs
186
+ plugin.lmrp_catalogs = instance._chatbot_configs[chatbot_id].lmrp_catalogs
187
+ instance._builders[chatbot_id] = plugin
188
+
189
+ for model in supported_models:
190
+ try:
191
+ model_id = model.get("model_id", model.get("name"))
192
+ if not model_id:
193
+ continue
194
+
195
+ pipeline_config = instance._chatbot_configs[chatbot_id].pipeline_config.copy()
196
+ # use original model name
197
+ pipeline_config["model_name"] = model.get("name", model_id)
198
+ pipeline_config["model_kwargs"] = model.get("model_kwargs", {})
199
+ pipeline_config["model_env_kwargs"] = model.get("model_env_kwargs", {})
200
+ credentials = pipeline_config["model_env_kwargs"].get("credentials")
201
+ if credentials:
202
+ pipeline_config["api_key"] = credentials
203
+
204
+ pipeline = await plugin.build(pipeline_config)
205
+ pipeline_key = (chatbot_id, str(model_id))
206
+ instance._chatbot_pipeline_keys.setdefault(chatbot_id, set()).add(pipeline_key)
207
+ instance._pipeline_cache[pipeline_key] = pipeline
208
+ except Exception as e:
209
+ logger.warning(f"Failed when ainit pliugin {traceback.format_exc()}")
210
+ logger.warning(f"Error building pipeline for chatbot `{chatbot_id}` model `{model_id}`: {e}")
211
+
212
+ def get_pipeline_builder(self, chatbot_id: str) -> Plugin:
213
+ """Get a pipeline builder instance for the given chatbot.
214
+
215
+ Args:
216
+ chatbot_id (str): The chatbot ID.
217
+
218
+ Returns:
219
+ Plugin: The pipeline builder instance.
220
+
221
+ Raises:
222
+ ValueError: If the chatbot ID is invalid or the model name is not supported.
223
+ """
224
+ if chatbot_id not in self._builders:
225
+ logger.warning(f"Pipeline builder not found for chatbot `{chatbot_id}`, attempting to rebuild...")
226
+ # Try to rebuild the plugin if it's not found
227
+ self._try_rebuild_plugin(chatbot_id)
228
+
229
+ if chatbot_id not in self._builders:
230
+ raise ValueError(f"Pipeline builder for chatbot `{chatbot_id}` not found and could not be rebuilt")
231
+
232
+ return self._builders[chatbot_id]
233
+
234
+ def _try_rebuild_plugin(self, chatbot_id: str) -> None:
235
+ """Try to rebuild a plugin for the given chatbot.
236
+
237
+ Args:
238
+ chatbot_id (str): The chatbot ID.
239
+ """
240
+ try:
241
+ # Check if we have the chatbot configuration
242
+ if chatbot_id not in self._chatbot_configs:
243
+ logger.warning(f"Chatbot configuration not found for `{chatbot_id}`")
244
+ return
245
+
246
+ chatbot_config = self._chatbot_configs[chatbot_id]
247
+ pipeline_type = chatbot_config.pipeline_type
248
+
249
+ # Check if we have the plugin for this pipeline type
250
+ if pipeline_type not in self._plugins:
251
+ logger.warning(f"Plugin not found for pipeline type `{pipeline_type}`")
252
+ return
253
+
254
+ plugin = self._plugins[pipeline_type]
255
+
256
+ # Get supported models from the configuration
257
+ supported_models = list(chatbot_config.pipeline_config.get("supported_models", {}).values())
258
+
259
+ if not supported_models:
260
+ logger.warning(f"No supported models found for chatbot `{chatbot_id}`")
261
+ return
262
+
263
+ # Rebuild the plugin synchronously (this is a simplified version)
264
+ # Set the catalogs
265
+ plugin.prompt_builder_catalogs = chatbot_config.prompt_builder_catalogs
266
+ plugin.lmrp_catalogs = chatbot_config.lmrp_catalogs
267
+ self._builders[chatbot_id] = plugin
268
+
269
+ logger.info(f"Successfully rebuilt pipeline builder for chatbot `{chatbot_id}`")
270
+
271
+ except Exception as e:
272
+ logger.warning(f"Error rebuilding plugin for chatbot `{chatbot_id}`: {e}")
273
+
274
+ async def _async_rebuild_pipeline(self, chatbot_id: str, model_id: str) -> None:
275
+ """Asynchronously rebuild a pipeline for the given chatbot and model.
276
+
277
+ Args:
278
+ chatbot_id (str): The chatbot ID.
279
+ model_id (str): The model ID.
280
+ """
281
+ try:
282
+ # First, ensure we have the pipeline builder
283
+ if chatbot_id not in self._builders:
284
+ await self._async_rebuild_plugin(chatbot_id)
285
+
286
+ if chatbot_id not in self._builders:
287
+ logger.warning(f"Could not rebuild pipeline builder for chatbot `{chatbot_id}`")
288
+ return
289
+
290
+ # Check if we have the chatbot configuration
291
+ if chatbot_id not in self._chatbot_configs:
292
+ logger.warning(f"Chatbot configuration not found for `{chatbot_id}`")
293
+ return
294
+
295
+ chatbot_config = self._chatbot_configs[chatbot_id]
296
+ plugin = self._builders[chatbot_id]
297
+
298
+ # Find the model configuration
299
+ supported_models = list(chatbot_config.pipeline_config.get("supported_models", {}).values())
300
+ model_config = None
301
+
302
+ for model in supported_models:
303
+ if model.get("model_id", model.get("name")) == model_id:
304
+ model_config = model
305
+ break
306
+ if not model_config:
307
+ logger.warning(
308
+ f"Model `{model_id}` not found in supported models "
309
+ f"for chatbot `{chatbot_id}` async rebuild pipeline"
310
+ )
311
+ return
312
+
313
+ # Use the existing _build_plugin method to rebuild the pipeline
314
+ await __class__._build_plugin(self, chatbot_id, [model_config], plugin)
315
+
316
+ logger.info(f"Successfully rebuilt pipeline for chatbot `{chatbot_id}` model `{model_id}`")
317
+
318
+ except Exception as e:
319
+ logger.warning(f"Error rebuilding pipeline for chatbot `{chatbot_id}` model `{model_id}`: {e}")
320
+
321
+ async def aget_pipeline(self, chatbot_id: str, model_id: str) -> Pipeline:
322
+ """Get a pipeline instance for the given chatbot and model ID (async version).
323
+
324
+ Args:
325
+ chatbot_id (str): The chatbot ID.
326
+ model_id (str): The model ID to use for inference.
327
+
328
+ Returns:
329
+ Pipeline: The pipeline instance.
330
+
331
+ Raises:
332
+ ValueError: If the chatbot ID is invalid.
333
+ """
334
+ pipeline_key = (chatbot_id, str(model_id))
335
+
336
+ if pipeline_key not in self._pipeline_cache:
337
+ logger.warning(
338
+ f"Pipeline not found for chatbot `{chatbot_id}` model `{model_id}`, attempting to rebuild..."
339
+ )
340
+ # Try to rebuild the pipeline if it's not found
341
+ await self._async_rebuild_pipeline(chatbot_id, str(model_id))
342
+
343
+ if pipeline_key not in self._pipeline_cache:
344
+ raise ValueError(
345
+ f"Pipeline for chatbot `{chatbot_id}` model `{model_id}` not found and could not be rebuilt"
346
+ )
347
+
348
+ return self._pipeline_cache[pipeline_key]
349
+
350
+ def get_pipeline_config(self, chatbot_id: str) -> dict[str, Any]:
351
+ """Get the pipeline configuration by chatbot ID.
352
+
353
+ Args:
354
+ chatbot_id (str): The chatbot ID.
355
+
356
+ Returns:
357
+ dict[str, Any]: The pipeline configuration.
358
+
359
+ Raises:
360
+ ValueError: If the chatbot or pipeline is not found.
361
+ """
362
+ self._validate_pipeline(chatbot_id)
363
+ return self._chatbot_configs[chatbot_id].pipeline_config
364
+
365
+ def get_pipeline_type(self, chatbot_id: str) -> str:
366
+ """Get the pipeline type for the given chatbot.
367
+
368
+ Args:
369
+ chatbot_id (str): The chatbot ID.
370
+ """
371
+ return self._chatbot_configs[chatbot_id].pipeline_type
372
+
373
+ def get_use_docproc(self, chatbot_id: str) -> bool:
374
+ """Get whether DocProc should be used for this chatbot.
375
+
376
+ Args:
377
+ chatbot_id (str): The chatbot ID.
378
+
379
+ Returns:
380
+ bool: Whether DocProc should be used.
381
+
382
+ Raises:
383
+ ValueError: If the chatbot or pipeline is not found.
384
+ """
385
+ self._validate_pipeline(chatbot_id)
386
+ config = self._chatbot_configs[chatbot_id].pipeline_config
387
+ return config["use_docproc"]
388
+
389
+ def get_max_file_size(self, chatbot_id: str) -> int | None:
390
+ """Get maximum file size for the given chatbot.
391
+
392
+ Args:
393
+ chatbot_id (str): The chatbot ID.
394
+
395
+ Returns:
396
+ int | None: The maximum file size if provided.
397
+
398
+ Raises:
399
+ ValueError: If the chatbot or pipeline is not found.
400
+ """
401
+ self._validate_pipeline(chatbot_id)
402
+ config = self._chatbot_configs[chatbot_id].pipeline_config
403
+ return config.get("max_file_size")
404
+
405
+ async def create_chatbot(self, app_config: AppConfig, chatbot_id: str) -> None:
406
+ """Create a new chatbot.
407
+
408
+ Args:
409
+ app_config (AppConfig): The application configuration.
410
+ chatbot_id (str): The ID of the chatbot.
411
+ """
412
+ chatbot_info = app_config.chatbots.get(chatbot_id)
413
+
414
+ if not chatbot_info or not chatbot_info.pipeline:
415
+ logger.warning(f"Pipeline config not found for chatbot `{chatbot_id}`")
416
+ return
417
+
418
+ pipeline_info = chatbot_info.pipeline
419
+ pipeline_type = pipeline_info["type"]
420
+ plugin = self._plugins.get(pipeline_type)
421
+ if not plugin:
422
+ logger.warning(f"Pipeline plugin not found for chatbot `{chatbot_id}`")
423
+ return
424
+
425
+ logger.info(f"Storing pipeline config for chatbot `{chatbot_id}`")
426
+ self._chatbot_configs[chatbot_id] = ChatbotConfig(
427
+ pipeline_type=pipeline_type,
428
+ pipeline_config=pipeline_info["config"],
429
+ prompt_builder_catalogs=pipeline_info["prompt_builder_catalogs"],
430
+ lmrp_catalogs=pipeline_info["lmrp_catalogs"],
431
+ )
432
+
433
+ supported_models = list(pipeline_info["config"].get("supported_models", {}).values())
434
+ await __class__._build_plugin(self, chatbot_id, supported_models, plugin)
435
+
436
+ async def delete_chatbot(self, chatbot_id: str) -> None:
437
+ """Delete a chatbot.
438
+
439
+ Args:
440
+ chatbot_id (str): The ID of the chatbot.
441
+ """
442
+ for pipeline_key in self._chatbot_pipeline_keys.get(chatbot_id, set()):
443
+ self._pipeline_cache.pop(pipeline_key, None)
444
+
445
+ self._chatbot_pipeline_keys.pop(chatbot_id, None)
446
+ self._chatbot_configs.pop(chatbot_id, None)
447
+ self._builders.pop(chatbot_id, None)
448
+
449
+ async def update_chatbots(self, app_config: AppConfig, chatbot_ids: list[str]) -> None:
450
+ """Update the chatbots.
451
+
452
+ Args:
453
+ app_config (AppConfig): The application configuration.
454
+ chatbot_ids (list[str]): The updated chatbot IDs.
455
+ """
456
+ for chatbot_id in chatbot_ids:
457
+ try:
458
+ chatbot_info = app_config.chatbots.get(chatbot_id)
459
+ if not chatbot_info or not chatbot_info.pipeline:
460
+ logger.warning(f"Pipeline config not found for chatbot `{chatbot_id}`")
461
+ continue
462
+
463
+ pipeline_info = chatbot_info.pipeline
464
+ pipeline_type = pipeline_info["type"]
465
+
466
+ supported_models = list(pipeline_info["config"].get("supported_models", {}).values())
467
+
468
+ logger.info(f"Storing pipeline config for chatbot `{chatbot_id}`")
469
+ self._chatbot_configs.pop(chatbot_id, None)
470
+ self._chatbot_configs[chatbot_id] = ChatbotConfig(
471
+ pipeline_type=pipeline_type,
472
+ pipeline_config=pipeline_info["config"],
473
+ prompt_builder_catalogs=pipeline_info["prompt_builder_catalogs"],
474
+ lmrp_catalogs=pipeline_info["lmrp_catalogs"],
475
+ )
476
+
477
+ new_pipeline_keys = set()
478
+ for model in supported_models:
479
+ model_id = model.get("model_id", model.get("name"))
480
+ new_pipeline_key = (chatbot_id, str(model_id))
481
+ new_pipeline_keys.add(new_pipeline_key)
482
+
483
+ for pipeline_key in self._chatbot_pipeline_keys.get(chatbot_id, set()):
484
+ if pipeline_key not in new_pipeline_keys:
485
+ self._pipeline_cache.pop(pipeline_key, None)
486
+
487
+ self._chatbot_pipeline_keys[chatbot_id] = set()
488
+
489
+ plugin = self._plugins.get(pipeline_type)
490
+ if not plugin:
491
+ logger.warning(f"Pipeline plugin not found for chatbot `{chatbot_id}`")
492
+ continue
493
+
494
+ self._builders.pop(chatbot_id, None)
495
+ self._builders[chatbot_id] = plugin
496
+
497
+ await __class__._build_plugin(self, chatbot_id, supported_models, plugin)
498
+ except Exception as e:
499
+ logger.warning(f"Error updating chatbot `{chatbot_id}`: {e}")
500
+
501
+ def _prepare_pipelines(self) -> None:
502
+ """Build pipeline configurations from the chatbots configuration."""
503
+ pipeline_types: set[str] = set()
504
+ chatbot_preset_map: dict[str, PipelinePresetConfig] = {}
505
+ for chatbot_id, chatbot_info in self.app_config.chatbots.items():
506
+ if not chatbot_info.pipeline:
507
+ logger.warning(f"Pipeline config not found for chatbot `{chatbot_id}`")
508
+ continue
509
+
510
+ pipeline_info = chatbot_info.pipeline
511
+ pipeline_type = pipeline_info["type"]
512
+
513
+ chatbot_preset_map[chatbot_id] = PipelinePresetConfig(
514
+ preset_id=pipeline_info["config"]["pipeline_preset_id"],
515
+ supported_models=list(pipeline_info["config"].get("supported_models", {}).values()),
516
+ )
517
+
518
+ logger.info(f"Storing pipeline config for chatbot `{chatbot_id}`")
519
+ self._chatbot_configs[chatbot_id] = ChatbotConfig(
520
+ pipeline_type=pipeline_type,
521
+ pipeline_config=pipeline_info["config"],
522
+ prompt_builder_catalogs=pipeline_info["prompt_builder_catalogs"],
523
+ lmrp_catalogs=pipeline_info["lmrp_catalogs"],
524
+ )
525
+ pipeline_types.add(pipeline_type)
526
+
527
+ for pipeline_type in pipeline_types:
528
+ self._activated_configs[pipeline_type] = ChatbotPresetMapping(
529
+ pipeline_type=pipeline_type,
530
+ chatbot_preset_map=chatbot_preset_map,
531
+ )
532
+
533
+ def _validate_pipeline(self, chatbot_id: str) -> None:
534
+ """Validate the pipeline configuration exists.
535
+
536
+ Args:
537
+ chatbot_id (str): The chatbot ID.
538
+
539
+ Raises:
540
+ ValueError: If the chatbot or pipeline configuration is not found.
541
+ """
542
+ if chatbot_id not in self._chatbot_configs:
543
+ raise ValueError(f"Pipeline configuration for chatbot `{chatbot_id}` not found")
544
+
545
+ async def aget_pipeline_builder(self, chatbot_id: str) -> Plugin:
546
+ """Get a pipeline builder instance for the given chatbot (async version).
547
+
548
+ Args:
549
+ chatbot_id (str): The chatbot ID.
550
+
551
+ Returns:
552
+ Plugin: The pipeline builder instance.
553
+
554
+ Raises:
555
+ ValueError: If the chatbot ID is invalid or the model name is not supported.
556
+ """
557
+ if chatbot_id not in self._builders:
558
+ logger.warning(f"Pipeline builder not found for chatbot `{chatbot_id}`, attempting to rebuild...")
559
+ # Try to rebuild the plugin if it's not found
560
+ await self._async_rebuild_plugin(chatbot_id)
561
+
562
+ if chatbot_id not in self._builders:
563
+ raise ValueError(f"Pipeline builder for chatbot `{chatbot_id}` not found and could not be rebuilt")
564
+
565
+ return self._builders[chatbot_id]
566
+
567
+ async def _async_rebuild_plugin(self, chatbot_id: str) -> None:
568
+ """Asynchronously rebuild a plugin for the given chatbot.
569
+
570
+ Args:
571
+ chatbot_id (str): The chatbot ID.
572
+ """
573
+ try:
574
+ # Check if we have the chatbot configuration
575
+ if chatbot_id not in self._chatbot_configs:
576
+ logger.warning(f"Chatbot configuration not found for `{chatbot_id}`")
577
+ return
578
+
579
+ chatbot_config = self._chatbot_configs[chatbot_id]
580
+ pipeline_type = chatbot_config.pipeline_type
581
+
582
+ # Check if we have the plugin for this pipeline type
583
+ if pipeline_type not in self._plugins:
584
+ logger.warning(f"Plugin not found for pipeline type `{pipeline_type}`")
585
+ return
586
+
587
+ plugin = self._plugins[pipeline_type]
588
+
589
+ # Get supported models from the configuration
590
+ supported_models = list(chatbot_config.pipeline_config.get("supported_models", {}).values())
591
+
592
+ if not supported_models:
593
+ logger.warning(f"No supported models found for chatbot `{chatbot_id}`")
594
+ return
595
+
596
+ # Use the existing _build_plugin method to rebuild the plugin
597
+ await __class__._build_plugin(self, chatbot_id, supported_models, plugin)
598
+
599
+ logger.info(f"Successfully rebuilt pipeline builder for chatbot `{chatbot_id}`")
600
+
601
+ except Exception as e:
602
+ logger.warning(f"Error rebuilding plugin for chatbot `{chatbot_id}`: {e}")