glchat-plugin 0.4.3__py3-none-any.whl → 0.4.5__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.
@@ -7,11 +7,13 @@ Authors:
7
7
  Hermes Vincentius Gani (hermes.v.gani@gdplabs.id)
8
8
  """
9
9
 
10
+ import inspect
10
11
  import traceback
11
12
  from typing import Any, Type
12
13
 
13
14
  from bosa_core import Plugin
14
15
  from bosa_core.plugin.handler import PluginHandler
16
+ from gllm_core.schema import Tool
15
17
  from gllm_core.utils import LoggerManager
16
18
  from gllm_inference.catalog import LMRequestProcessorCatalog, PromptBuilderCatalog
17
19
  from gllm_pipeline.pipeline.pipeline import Pipeline
@@ -19,6 +21,7 @@ from pydantic import BaseModel, ConfigDict
19
21
 
20
22
  from glchat_plugin.config.app_config import AppConfig
21
23
  from glchat_plugin.config.constant import DEFAULT_ORGANIZATION_ID
24
+ from glchat_plugin.pipeline.tool_processor import ToolProcessor
22
25
  from glchat_plugin.storage.base_chat_history_storage import BaseChatHistoryStorage
23
26
 
24
27
 
@@ -64,6 +67,73 @@ class ChatbotPresetMapping(BaseModel):
64
67
  chatbot_preset_map: dict[str, PipelinePresetConfig]
65
68
 
66
69
 
70
+ class PipelineBundle:
71
+ """Pipeline bundle.
72
+
73
+ Attributes:
74
+ pipeline (Pipeline): The pipeline.
75
+ tools (list[ToolProcessor]): The tools.
76
+ """
77
+
78
+ pipeline: Pipeline
79
+ tools: dict[str, ToolProcessor]
80
+
81
+ def __init__(self, pipeline: Pipeline, tools: list[ToolProcessor]):
82
+ """Initialize the pipeline bundle.
83
+
84
+ Args:
85
+ pipeline (Pipeline): The pipeline.
86
+ tools (list[ToolProcessor]): The tools.
87
+ """
88
+ self.pipeline = pipeline
89
+ self.tools = {tool.name: tool for tool in tools}
90
+
91
+ async def invoke(self, **kwargs: Any) -> dict[str, Any]:
92
+ """Invoke the pipeline bundle.
93
+
94
+ Args:
95
+ kwargs (Any): The keyword arguments to pass to the pipeline.
96
+
97
+ Returns:
98
+ dict[str, Any]: The result of the pipeline invocation.
99
+ """
100
+ return await self.pipeline.invoke(**kwargs)
101
+
102
+ async def invoke_as_tool(
103
+ self,
104
+ tool_name: str,
105
+ pipeline_config: dict[str, Any],
106
+ inputs: dict[str, Any],
107
+ config: dict[str, Any],
108
+ **kwargs: Any,
109
+ ) -> Any:
110
+ """Invoke the pipeline bundle as a tool.
111
+
112
+ Args:
113
+ tool_name (str): The name of the tool.
114
+ pipeline_config (dict[str, Any]): The pipeline configuration.
115
+ inputs (dict[str, Any]): The inputs to the tool.
116
+ config (dict[str, Any]): The configuration of the tool.
117
+ kwargs (Any): The keyword arguments to pass to the tool.
118
+
119
+ Returns:
120
+ Any: The result of the tool invocation.
121
+
122
+ Raises:
123
+ ValueError: If the tool is not found in the pipeline bundle.
124
+ """
125
+ tool = self.tools.get(tool_name)
126
+ if not tool:
127
+ raise ValueError(f"Tool `{tool_name}` not found in pipeline bundle")
128
+
129
+ return await tool.process(
130
+ pipeline_config=pipeline_config,
131
+ inputs=inputs,
132
+ config=config,
133
+ **kwargs,
134
+ )
135
+
136
+
67
137
  logger = LoggerManager().get_logger(__name__)
68
138
 
69
139
 
@@ -98,7 +168,7 @@ class PipelineHandler(PluginHandler):
98
168
  _chatbot_configs: dict[tuple[str, str], ChatbotConfig] = {}
99
169
  _builders: dict[tuple[str, str], Plugin] = {}
100
170
  _plugins: dict[tuple[str, str], Plugin] = {}
101
- _pipeline_cache: dict[tuple[str, str, str], Pipeline] = {}
171
+ _pipeline_cache: dict[tuple[str, str, str], PipelineBundle] = {}
102
172
  _chatbot_pipeline_keys: dict[tuple[str, str], set[tuple[str, str, str]]] = {}
103
173
  _pipeline_build_errors: dict[tuple[str, str, str], str] = {}
104
174
 
@@ -203,8 +273,13 @@ class PipelineHandler(PluginHandler):
203
273
  organization_id (str): The organization ID.
204
274
  """
205
275
  chatbot_organization_id = (chatbot_id, organization_id)
206
- plugin.prompt_builder_catalogs = instance._chatbot_configs[chatbot_organization_id].prompt_builder_catalogs
207
- plugin.lmrp_catalogs = instance._chatbot_configs[chatbot_organization_id].lmrp_catalogs
276
+
277
+ prompt_builder_catalogs = instance._chatbot_configs[chatbot_organization_id].prompt_builder_catalogs
278
+ plugin.prompt_builder_catalogs = prompt_builder_catalogs
279
+
280
+ lmrp_catalogs = instance._chatbot_configs[chatbot_organization_id].lmrp_catalogs
281
+ plugin.lmrp_catalogs = lmrp_catalogs
282
+
208
283
  instance._builders[chatbot_organization_id] = plugin
209
284
 
210
285
  for pipeline_type in INTERNAL_PIPELINES:
@@ -212,15 +287,19 @@ class PipelineHandler(PluginHandler):
212
287
  internal_plugin = instance._plugins.get(pipeline_type_organization_id)
213
288
  if internal_plugin:
214
289
  try:
215
- internal_plugin.prompt_builder_catalogs = instance._chatbot_configs[
216
- chatbot_organization_id
217
- ].prompt_builder_catalogs
218
- internal_plugin.lmrp_catalogs = instance._chatbot_configs[chatbot_organization_id].lmrp_catalogs
290
+ internal_plugin.prompt_builder_catalogs = prompt_builder_catalogs
291
+ internal_plugin.lmrp_catalogs = lmrp_catalogs
219
292
  pipeline_config = instance._chatbot_configs[chatbot_organization_id].pipeline_config.copy()
220
- pipeline = await internal_plugin.build(pipeline_config)
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)
221
300
  pipeline_key = (chatbot_id, f"__{pipeline_type}__", organization_id)
222
301
  instance._chatbot_pipeline_keys.setdefault(chatbot_organization_id, set()).add(pipeline_key)
223
- instance._pipeline_cache[pipeline_key] = pipeline
302
+ instance._pipeline_cache[pipeline_key] = PipelineBundle(pipeline, pipeline_tools)
224
303
 
225
304
  # Clear any previous error for this internal pipeline if build succeeded
226
305
  instance._pipeline_build_errors.pop(pipeline_key, None)
@@ -251,10 +330,16 @@ class PipelineHandler(PluginHandler):
251
330
  if credentials:
252
331
  pipeline_config["api_key"] = credentials
253
332
 
254
- pipeline = await plugin.build(pipeline_config)
333
+ pipeline = await cls._call_build(
334
+ plugin,
335
+ pipeline_config,
336
+ prompt_builder_catalogs,
337
+ lmrp_catalogs,
338
+ )
339
+ pipeline_tools = await plugin.build_tools(pipeline_config)
255
340
  pipeline_key = (chatbot_id, str(model_id), organization_id)
256
341
  instance._chatbot_pipeline_keys.setdefault(chatbot_organization_id, set()).add(pipeline_key)
257
- instance._pipeline_cache[pipeline_key] = pipeline
342
+ instance._pipeline_cache[pipeline_key] = PipelineBundle(pipeline, pipeline_tools)
258
343
 
259
344
  # Clear any previous error for this pipeline if build succeeded
260
345
  instance._pipeline_build_errors.pop(pipeline_key, None)
@@ -266,6 +351,41 @@ class PipelineHandler(PluginHandler):
266
351
  # Store the error message for later retrieval
267
352
  instance._store_pipeline_build_error(chatbot_id, str(model_id), organization_id, error_message)
268
353
 
354
+ @classmethod
355
+ async def _call_build(
356
+ cls,
357
+ plugin: Plugin,
358
+ pipeline_config: dict[str, Any],
359
+ prompt_builder_catalogs: dict[str, PromptBuilderCatalog] | None,
360
+ lmrp_catalogs: dict[str, LMRequestProcessorCatalog] | None,
361
+ ) -> Pipeline:
362
+ """Call plugin.build in a backward-compatible way.
363
+
364
+ If the plugin.build signature declares prompt_builder_catalogs or lmrp_catalogs,
365
+ they will be passed as keyword arguments. Otherwise, only pipeline_config is passed.
366
+ """
367
+ try:
368
+ sig = inspect.signature(plugin.build)
369
+ except (TypeError, ValueError):
370
+ # Fallback: call with just pipeline_config
371
+ return await plugin.build(pipeline_config)
372
+
373
+ params = list(sig.parameters.values())
374
+ # Skip 'self' if present
375
+ if params and params[0].name == "self":
376
+ params = params[1:]
377
+
378
+ param_names = {p.name for p in params}
379
+
380
+ if "prompt_builder_catalogs" in param_names or "lmrp_catalogs" in param_names:
381
+ return await plugin.build(
382
+ pipeline_config,
383
+ prompt_builder_catalogs=prompt_builder_catalogs,
384
+ lmrp_catalogs=lmrp_catalogs,
385
+ )
386
+
387
+ return await plugin.build(pipeline_config)
388
+
269
389
  def get_pipeline_builder(self, chatbot_id: str, organization_id: str = DEFAULT_ORGANIZATION_ID) -> Plugin:
270
390
  """Get a pipeline builder instance for the given chatbot.
271
391
 
@@ -394,7 +514,7 @@ class PipelineHandler(PluginHandler):
394
514
 
395
515
  async def aget_pipeline(
396
516
  self, chatbot_id: str, model_id: str, organization_id: str = DEFAULT_ORGANIZATION_ID
397
- ) -> Pipeline:
517
+ ) -> PipelineBundle:
398
518
  """Get a pipeline instance for the given chatbot and model ID (async version).
399
519
 
400
520
  Args:
@@ -403,7 +523,7 @@ class PipelineHandler(PluginHandler):
403
523
  organization_id (str): The organization ID.
404
524
 
405
525
  Returns:
406
- Pipeline: The pipeline instance.
526
+ PipelineBundle: The pipeline bundle instance.
407
527
 
408
528
  Raises:
409
529
  ValueError: If the chatbot ID is invalid.
@@ -11,11 +11,13 @@ from abc import ABC, abstractmethod
11
11
  from typing import Any, Generic, Type, TypeVar
12
12
 
13
13
  from bosa_core.plugin.plugin import Plugin
14
+ from gllm_core.schema import Tool
14
15
  from gllm_inference.catalog.catalog import BaseCatalog
15
16
  from gllm_pipeline.pipeline.pipeline import Pipeline
16
17
 
17
18
  from glchat_plugin.config.constant import DEFAULT_ORGANIZATION_ID
18
19
  from glchat_plugin.pipeline.pipeline_handler import PipelineHandler
20
+ from glchat_plugin.pipeline.tool_processor import ToolProcessor
19
21
 
20
22
  PipelineState = TypeVar("PipelineState")
21
23
  PipelinePresetConfig = TypeVar("PipelinePresetConfig", bound="BasePipelinePresetConfig")
@@ -131,3 +133,17 @@ class PipelineBuilderPlugin(Plugin, Generic[PipelineState, PipelinePresetConfig]
131
133
  if self.preset_config_class:
132
134
  return self.preset_config_class().model_dump()
133
135
  return {}
136
+
137
+ async def build_tools(
138
+ self,
139
+ pipeline_config: dict[str, Any],
140
+ ) -> list[ToolProcessor]:
141
+ """Build a pipeline instance.
142
+
143
+ Args:
144
+ pipeline_config (dict[str, Any]): Pipeline configuration including model name and other settings.
145
+
146
+ Returns:
147
+ list[ToolProcessor]: Built tool processors.
148
+ """
149
+ return []
@@ -0,0 +1,105 @@
1
+ """Tool processor.
2
+
3
+ This module provides a base class for tool processors.
4
+
5
+ Authors:
6
+ Surya Mahadi (surya.mahadi@gdplabs.id)
7
+
8
+ References:
9
+ None
10
+ """
11
+
12
+ from abc import ABC, abstractmethod
13
+ from typing import Any
14
+
15
+ from gllm_core.schema import Tool
16
+
17
+
18
+ class ToolProcessor(ABC):
19
+ """Tool processor.
20
+
21
+ This class is responsible for processing tools.
22
+
23
+ Attributes:
24
+ tool (Tool): The tool.
25
+ """
26
+
27
+ def __init__(self, tool: Tool):
28
+ """Initialize the tool processor.
29
+
30
+ Args:
31
+ tool (Tool): The tool.
32
+ """
33
+ self.tool = tool
34
+
35
+ @property
36
+ def name(self) -> str:
37
+ """Get the name of the tool.
38
+
39
+ Returns:
40
+ str: The name of the tool.
41
+ """
42
+ return self.tool.name
43
+
44
+ @abstractmethod
45
+ async def preprocess(
46
+ self,
47
+ pipeline_config: dict[str, Any],
48
+ inputs: dict[str, Any],
49
+ config: dict[str, Any],
50
+ **kwargs: Any,
51
+ ) -> tuple[dict[str, Any], dict[str, Any]]:
52
+ """Process a tool.
53
+
54
+ Args:
55
+ pipeline_config (dict[str, Any]): The pipeline configuration.
56
+ inputs (dict[str, Any]): The inputs to the tool.
57
+ config (dict[str, Any]): The configuration of the tool.
58
+ kwargs (Any): The keyword arguments to pass to the tool.
59
+
60
+ Returns:
61
+ tuple[dict[str, Any], dict[str, Any]]: The inputs and configuration for the tool invocation.
62
+ """
63
+ raise NotImplementedError
64
+
65
+ @abstractmethod
66
+ async def postprocess(self, result: Any) -> dict[str, Any]:
67
+ """Postprocess a tool for json friendly output.
68
+
69
+ Args:
70
+ result (Any): The result of the tool invocation.
71
+
72
+ Returns:
73
+ dict[str, Any]: The result of the tool invocation.
74
+ """
75
+ raise NotImplementedError
76
+
77
+ async def process(
78
+ self,
79
+ pipeline_config: dict[str, Any],
80
+ inputs: dict[str, Any],
81
+ config: dict[str, Any],
82
+ **kwargs: Any,
83
+ ) -> Any:
84
+ """Process a tool.
85
+
86
+ Args:
87
+ pipeline_config (dict[str, Any]): The pipeline configuration.
88
+ inputs (dict[str, Any]): The inputs to the tool.
89
+ config (dict[str, Any]): The configuration of the tool.
90
+ kwargs (Any): The keyword arguments to pass to the tool.
91
+
92
+ Returns:
93
+ Any: The result of the tool invocation.
94
+ """
95
+ tool_input, config = await self.preprocess(
96
+ pipeline_config=pipeline_config,
97
+ inputs=inputs,
98
+ config=config,
99
+ **kwargs,
100
+ )
101
+ result = await self.tool.invoke(
102
+ input=tool_input,
103
+ context=config,
104
+ )
105
+ return await self.postprocess(result)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: glchat-plugin
3
- Version: 0.4.3
3
+ Version: 0.4.5
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,9 @@ 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=Kp2LBqtVHzJ2twLaUvYXnL-1QGkqL_8h21IAR7FxBTI,34203
12
- glchat_plugin/pipeline/pipeline_plugin.py,sha256=eBrg4THS0P49Mz4glEjrwqe1pxnvOn_UitJ9gOBVcac,4562
11
+ glchat_plugin/pipeline/pipeline_handler.py,sha256=sBD9Ohkd6GIIiTxgRAm81HuQJIzcNmV7KjE3hHQiftw,38108
12
+ glchat_plugin/pipeline/pipeline_plugin.py,sha256=fDQWJkEMgbbY6bK81gHjzp3fLZZN0a0Pv6_DS4wSKL0,5040
13
+ glchat_plugin/pipeline/tool_processor.py,sha256=pc-uHoBqOSXjLM8R_Wgws_z5ehPtaSeb9Bhk9aeLDus,2731
13
14
  glchat_plugin/service/__init__.py,sha256=9T4qzyYL052qLqva5el1F575OTRNaaf9tb9UvW-leTc,47
14
15
  glchat_plugin/service/base_rate_limiter_service.py,sha256=tgKwdr4EqnGo5iDRVJPnlg8W9q0hiUzfeewAtdW4IjU,1232
15
16
  glchat_plugin/service/base_tenant_service.py,sha256=CB-jJWXi87nTcjO1XhPoSgNMf2YA_LfXoHr30ye0VtI,734
@@ -19,7 +20,7 @@ glchat_plugin/storage/base_anonymizer_storage.py,sha256=oFwovWrsjM7v1YjeN-4p-M3O
19
20
  glchat_plugin/storage/base_chat_history_storage.py,sha256=YIGM8zv7s5BQ8O7W1LfaLyQW4SF9Bt3aolowsoDaQw8,11257
20
21
  glchat_plugin/tools/__init__.py,sha256=OFotHbgQ8mZEbdlvlv5aVMdxfubPvkVWAcTwhIPdIqQ,542
21
22
  glchat_plugin/tools/decorators.py,sha256=AvQBV18wzXWdC483RSSmpfh92zsqTyp8SzDLIkreIGU,3925
22
- glchat_plugin-0.4.3.dist-info/METADATA,sha256=Annr1O57pSyLVG6niQiTXTZT7A2s9R1QWlo5nqzgur4,2063
23
- glchat_plugin-0.4.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
- glchat_plugin-0.4.3.dist-info/top_level.txt,sha256=fzKSXmct5dY4CAKku4-mkdHX-QPAyQVvo8vpQj8qizY,14
25
- glchat_plugin-0.4.3.dist-info/RECORD,,
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,,