glchat-plugin 0.0.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,126 @@
1
+ """Base Pipeline Builder Plugin.
2
+
3
+ This module defines the base class for pipeline builder plugins.
4
+
5
+ Authors:
6
+ Samuel Lusandi (samuel.lusandi@gdplabs.id)
7
+ Hermes Vincentius Gani (hermes.v.gani@gdplabs.id)
8
+ """
9
+
10
+ from abc import ABC, abstractmethod
11
+ from typing import Any, Generic, Type, TypeVar
12
+
13
+ from bosa_core.plugin.plugin import Plugin
14
+ from gllm_inference.catalog.catalog import BaseCatalog
15
+ from gllm_pipeline.pipeline.pipeline import Pipeline
16
+
17
+ from glchat_plugin.pipeline.pipeline_handler import PipelineHandler
18
+
19
+ PipelineState = TypeVar("PipelineState")
20
+ PipelinePresetConfig = TypeVar("PipelinePresetConfig", bound="BasePipelinePresetConfig")
21
+ PipelineRuntimeConfig = TypeVar("PipelineRuntimeConfig", bound="BaseModel")
22
+
23
+
24
+ @Plugin.for_handler(PipelineHandler)
25
+ class PipelineBuilderPlugin(Plugin, Generic[PipelineState, PipelinePresetConfig], ABC):
26
+ """Base class for pipeline builder plugins.
27
+
28
+ This class combines the Plugin architecture with the Pipeline Builder functionality.
29
+
30
+ Attributes:
31
+ name (str): The name of the plugin.
32
+ description (str): The description of the plugin.
33
+ version (str): The version of the plugin.
34
+ catalog (BaseCatalog): The catalog instance.
35
+ additional_config_class (Type[PipelineRuntimeConfig] | None): The additional runtime configuration class.
36
+ preset_config_class (Type[PipelinePresetConfig] | None): The preset configuration class.
37
+ """
38
+
39
+ name: str
40
+ description: str = "Pipeline builder plugin"
41
+ version: str = "0.0.0"
42
+
43
+ catalog: BaseCatalog[Any]
44
+
45
+ additional_config_class: Type[PipelineRuntimeConfig] | None = None
46
+ preset_config_class: Type[PipelinePresetConfig] | None = None
47
+
48
+ @classmethod
49
+ def get_preset_config_class(cls) -> Type[PipelinePresetConfig]:
50
+ """Get the preset_config_class.
51
+
52
+ Returns:
53
+ Type[PipelinePresetConfig]: The pipeline preset config class.
54
+
55
+ Raises:
56
+ NotImplementedError: If the preset_config_class is not defined.
57
+ """
58
+ if cls.preset_config_class is None:
59
+ raise NotImplementedError(f"{cls.__name__} must define a `preset_config_class` attribute.")
60
+ return cls.preset_config_class
61
+
62
+ @abstractmethod
63
+ def build_initial_state(
64
+ self,
65
+ request_config: dict[str, Any],
66
+ pipeline_config: dict[str, Any],
67
+ **kwargs: Any,
68
+ ) -> PipelineState:
69
+ """Build the initial pipeline state.
70
+
71
+ Args:
72
+ request_config (dict[str, Any]): Request configuration.
73
+ pipeline_config (dict[str, Any]): Pipeline configuration.
74
+ kwargs (Any): Additional state arguments.
75
+
76
+ Returns:
77
+ PipelineState: Initial pipeline state.
78
+ """
79
+ pass
80
+
81
+ @abstractmethod
82
+ async def build(
83
+ self,
84
+ pipeline_config: dict[str, Any],
85
+ ) -> Pipeline:
86
+ """Build a pipeline instance.
87
+
88
+ Args:
89
+ pipeline_config (dict[str, Any]): Pipeline configuration including model name and other settings.
90
+
91
+ Returns:
92
+ Pipeline: Built pipeline instance.
93
+ """
94
+ pass
95
+
96
+ async def cleanup(self):
97
+ """Cleanup the pipeline resources, if needed."""
98
+ pass
99
+
100
+ def build_additional_runtime_config(
101
+ self,
102
+ pipeline_config: dict[str, Any],
103
+ ) -> dict[str, Any]:
104
+ """Build additional runtime configuration.
105
+
106
+ Args:
107
+ pipeline_config (dict[str, Any]): Pipeline configuration.
108
+
109
+ Returns:
110
+ dict[str, Any]: Additional runtime configuration.
111
+ """
112
+ if not self.additional_config_class:
113
+ return {}
114
+
115
+ config = self.additional_config_class(**pipeline_config)
116
+ return config.model_dump(exclude_none=True)
117
+
118
+ def get_config(self) -> dict[str, Any]:
119
+ """Get the pipeline configuration.
120
+
121
+ Returns:
122
+ dict[str, Any]: Pipeline configuration.
123
+ """
124
+ if self.preset_config_class:
125
+ return self.preset_config_class().model_dump()
126
+ return {}
@@ -0,0 +1 @@
1
+ """Module for glchat_plugin storage interface."""
@@ -0,0 +1,58 @@
1
+ """Interface for Anonymizer Storage.
2
+
3
+ Authors:
4
+ Hermes Vincentius Gani (hermes.v.gani@gdplabs.id)
5
+
6
+ References:
7
+ None
8
+ """
9
+
10
+ from abc import ABC, abstractmethod
11
+ from typing import TypeVar
12
+
13
+ AnonymizerMapping = TypeVar("AnonymizerMapping")
14
+ MappingDataType = TypeVar("MappingDataType")
15
+
16
+
17
+ class BaseAnonymizerStorage(ABC):
18
+ """Interface for anonymizer storage that defines methods for managing anonymizer mappings."""
19
+
20
+ @abstractmethod
21
+ def get_mappings_by_conversation_id(self, conversation_id: str) -> list[AnonymizerMapping]:
22
+ """Retrieve anonymizer mappings by conversation ID.
23
+
24
+ Args:
25
+ conversation_id (str): The ID of the conversation.
26
+
27
+ Returns:
28
+ list[AnonymizerMapping]: A list of anonymizer mappings associated with the conversation ID.
29
+ """
30
+ pass
31
+
32
+ @abstractmethod
33
+ def create_mapping(
34
+ self, conversation_id: str, pii_type: str, anonymized_value: str, pii_value: str
35
+ ) -> AnonymizerMapping:
36
+ """Create a new anonymizer mapping.
37
+
38
+ Args:
39
+ conversation_id (str): The ID of the conversation.
40
+ pii_type (str): The type of personally identifiable information (PII).
41
+ anonymized_value (str): The anonymized value of the PII.
42
+ pii_value (str): The original PII value.
43
+
44
+ Returns:
45
+ AnonymizerMapping: The created anonymizer mapping.
46
+ """
47
+ pass
48
+
49
+ @abstractmethod
50
+ def update_mapping(self, conversation_id: str, is_anonymized: bool, mapping_data_type: MappingDataType) -> None:
51
+ """Update the mappings for a specific conversation with new anonymizer mappings.
52
+
53
+ Args:
54
+ conversation_id (str): The unique identifier for the conversation.
55
+ is_anonymized (bool): The flag to determine if the message is anonymized.
56
+ mapping_data_type (MappingDataType): A dictionary of new anonymizer mappings to update for deanonymization.
57
+ """
58
+ pass
@@ -0,0 +1,356 @@
1
+ """Interface for Chat History Storage.
2
+
3
+ Authors:
4
+ Hermes Vincentius Gani (hermes.v.gani@gdplabs.id)
5
+
6
+ References:
7
+ None
8
+ """
9
+
10
+ from abc import ABC, abstractmethod
11
+ from datetime import datetime
12
+ from enum import StrEnum
13
+ from typing import Any
14
+
15
+ from typing_extensions import TypeVar
16
+
17
+ from glchat_plugin.storage.base_anonymizer_storage import AnonymizerMapping
18
+
19
+ Conversation = TypeVar("Conversation")
20
+ Message = TypeVar("Message")
21
+ ConversationDocument = TypeVar("ConversationDocument")
22
+
23
+
24
+ class MessageRole(StrEnum):
25
+ """Enum for Message Type."""
26
+
27
+ USER = "USER"
28
+ AI = "AI"
29
+
30
+
31
+ class DocumentStatus(StrEnum):
32
+ """Enum for ConversationDocument Status."""
33
+
34
+ PROCESSING = "processing"
35
+ DONE = "done"
36
+ FAILED = "failed"
37
+
38
+
39
+ class BaseChatHistoryStorage(ABC):
40
+ """Interface for chat history storage that defines methods for managing chat conversations and messages."""
41
+
42
+ @abstractmethod
43
+ def create_conversation(
44
+ self, user_id: str, conversation_title: str, chatbot_id: str, **kwargs: Any
45
+ ) -> Conversation:
46
+ """Create a new conversation.
47
+
48
+ Args:
49
+ user_id (str): The ID of the user.
50
+ conversation_title (str): The title of the conversation.
51
+ chatbot_id (str): The ID of the chatbot.
52
+ kwargs (Any): Additional arguments.
53
+
54
+ Returns:
55
+ Conversation: The created conversation.
56
+ """
57
+ pass
58
+
59
+ @abstractmethod
60
+ def get_conversation(self, user_id: str, conversation_id: str, **kwargs: Any) -> Conversation | None:
61
+ """Retrieve a specific conversation by its ID.
62
+
63
+ Args:
64
+ user_id (str): The ID of the user.
65
+ conversation_id (str): The ID of the conversation.
66
+ kwargs (Any): Additional arguments.
67
+
68
+ Returns:
69
+ Conversation | None: The conversation if found, otherwise None.
70
+ """
71
+ pass
72
+
73
+ @abstractmethod
74
+ def get_conversations(
75
+ self,
76
+ user_id: str,
77
+ query: str = "",
78
+ chatbot_ids: list[str] | None = None,
79
+ cursor: str | None = None,
80
+ limit: int | None = None,
81
+ **kwargs: Any,
82
+ ) -> list[Conversation]:
83
+ """Retrieve a list of conversations for a user.
84
+
85
+ Args:
86
+ user_id (str): The ID of the user.
87
+ query (str, optional): A search query to filter conversations. Defaults to "".
88
+ chatbot_ids (list[str] | None, optional): A list of chatbot IDs to filter conversations. Defaults to None.
89
+ cursor (str | None, optional): A cursor for pagination. Defaults to None.
90
+ limit (int | None, optional): The maximum number of conversations to retrieve. Defaults to None.
91
+ kwargs (Any): Additional arguments.
92
+
93
+ Returns:
94
+ list[Conversation]: A list of conversations.
95
+ """
96
+ pass
97
+
98
+ @abstractmethod
99
+ def rename_conversation(self, user_id: str, conversation_id: str, new_title: str, **kwargs: Any) -> Conversation:
100
+ """Rename an existing conversation.
101
+
102
+ Args:
103
+ user_id (str): The ID of the user.
104
+ conversation_id (str): The ID of the conversation.
105
+ new_title (str): The new title of the conversation.
106
+ kwargs (Any): Additional arguments.
107
+
108
+ Returns:
109
+ Conversation: The updated conversation.
110
+ """
111
+ pass
112
+
113
+ @abstractmethod
114
+ def delete_conversation(self, user_id: str, conversation_id: str, **kwargs: Any) -> None:
115
+ """Delete a conversation by its ID.
116
+
117
+ Args:
118
+ user_id (str): The ID of the user.
119
+ conversation_id (str): The ID of the conversation.
120
+ kwargs (Any): Additional arguments.
121
+
122
+ Returns:
123
+ None
124
+ """
125
+ pass
126
+
127
+ @abstractmethod
128
+ def delete_conversations(self, user_id: str, chatbot_id: str, **kwargs: Any) -> None:
129
+ """Delete all conversations associated with a chatbot.
130
+
131
+ Args:
132
+ user_id (str): The ID of the user.
133
+ chatbot_id (str): The ID of the chatbot.
134
+ kwargs (Any): Additional arguments.
135
+
136
+ Returns:
137
+ None
138
+ """
139
+ pass
140
+
141
+ @abstractmethod
142
+ def save_message(
143
+ self,
144
+ user_id: str,
145
+ conversation_id: str,
146
+ message_list: list[Any],
147
+ attachments: dict[str, Any] | None,
148
+ **kwargs: Any,
149
+ ) -> list[Message]:
150
+ """Save a list of messages to a conversation.
151
+
152
+ Args:
153
+ user_id (str): The ID of the user.
154
+ conversation_id (str): The ID of the conversation.
155
+ message_list (list[Any]): A list of messages to save.
156
+ attachments (dict[str, Any] | None): Attachments associated with the messages.
157
+ kwargs (Any): Additional arguments.
158
+
159
+ Returns:
160
+ list[Message]: The saved messages.
161
+ """
162
+ pass
163
+
164
+ @abstractmethod
165
+ def get_message_by_id(self, message_id: str, **kwargs: Any) -> Message:
166
+ """Retrieve a specific message by its ID.
167
+
168
+ Args:
169
+ message_id (str): The ID of the message.
170
+ kwargs (Any): Additional arguments.
171
+
172
+ Returns:
173
+ Message: The message.
174
+ """
175
+ pass
176
+
177
+ @abstractmethod
178
+ def get_messages_by_ids(self, message_ids: list[str], **kwargs: Any) -> list[Message]:
179
+ """Retrieve messages by their IDs.
180
+
181
+ Args:
182
+ message_ids (list[str]): A list of message IDs.
183
+ kwargs (Any): Additional arguments.
184
+
185
+ Returns:
186
+ list[Message]: A list of messages.
187
+ """
188
+ pass
189
+
190
+ @abstractmethod
191
+ def get_messages(
192
+ self,
193
+ user_id: str,
194
+ conversation_id: str,
195
+ limit: int | None = None,
196
+ max_timestamp: datetime | None = None,
197
+ **kwargs: Any,
198
+ ) -> list[Message]:
199
+ """Retrieve messages from a conversation.
200
+
201
+ Args:
202
+ user_id (str): The ID of the user.
203
+ conversation_id (str): The ID of the conversation.
204
+ limit (int | None, optional): The maximum number of messages to retrieve. Defaults to None.
205
+ max_timestamp (datetime | None, optional): The maximum timestamp for the messages. Defaults to None.
206
+ kwargs (Any): Additional arguments.
207
+
208
+ Returns:
209
+ list[Message]: A list of messages.
210
+ """
211
+ pass
212
+
213
+ @abstractmethod
214
+ def update_message_metadata(self, message_id: str, metadata_: dict[str, Any], **kwargs: Any) -> Message:
215
+ """Update the metadata of a message.
216
+
217
+ Args:
218
+ message_id (str): The ID of the message.
219
+ metadata_ (dict[str, Any]): The metadata to update.
220
+ kwargs (Any): Additional arguments.
221
+
222
+ Returns:
223
+ Message: The updated message.
224
+ """
225
+ pass
226
+
227
+ @abstractmethod
228
+ def delete_messages(self, user_id: str, message_ids: list[str], chatbot_ids: list[str], **kwargs: Any) -> None:
229
+ """Delete messages by their IDs.
230
+
231
+ Args:
232
+ user_id (str): The ID of the user.
233
+ message_ids (list[str]): A list of message IDs to delete.
234
+ chatbot_ids (list[str]): A list of chatbot IDs associated with the messages.
235
+ kwargs (Any): Additional arguments.
236
+
237
+ Returns:
238
+ None
239
+ """
240
+ pass
241
+
242
+ @abstractmethod
243
+ def create_conversation_document(
244
+ self, conversation_id: str, status: str = DocumentStatus.PROCESSING.value, file_hash: str = "", **kwargs: Any
245
+ ) -> ConversationDocument:
246
+ """Create a new conversation document.
247
+
248
+ Args:
249
+ conversation_id (str): The ID of the conversation.
250
+ status (str, optional): The status of the document. Defaults to DocumentStatus.PROCESSING.value.
251
+ file_hash (str, optional): The hash of the file. Defaults to "".
252
+ kwargs (Any): Additional arguments.
253
+
254
+ Returns:
255
+ ConversationDocument: The created conversation document.
256
+ """
257
+ pass
258
+
259
+ @abstractmethod
260
+ def get_conversation_document(self, document_id: str, **kwargs: Any) -> ConversationDocument:
261
+ """Retrieve a conversation document by its ID.
262
+
263
+ Args:
264
+ document_id (str): The ID of the document.
265
+ kwargs (Any): Additional arguments.
266
+
267
+ Returns:
268
+ ConversationDocument: The conversation document.
269
+ """
270
+ pass
271
+
272
+ @abstractmethod
273
+ def update_conversation_document(
274
+ self,
275
+ document_id: str,
276
+ status: str,
277
+ number_of_chunks: int,
278
+ message: str | None,
279
+ object_key: str | None,
280
+ **kwargs: Any,
281
+ ) -> ConversationDocument:
282
+ """Update a conversation document.
283
+
284
+ Args:
285
+ document_id (str): The ID of the document.
286
+ status (str): The status of the document.
287
+ number_of_chunks (int): The number of chunks in the document.
288
+ message (str | None, optional): A message associated with the document. Defaults to None.
289
+ object_key (str | None, optional): The object key of the document. Defaults to None.
290
+ kwargs (Any): Additional arguments.
291
+
292
+ Returns:
293
+ ConversationDocument: The updated conversation document.
294
+ """
295
+ pass
296
+
297
+ @abstractmethod
298
+ def save_feedback(self, user_id: str, message_id: str, feedback: str, **kwargs: Any) -> None:
299
+ """Save feedback for a message.
300
+
301
+ Args:
302
+ user_id (str): The ID of the user.
303
+ message_id (str): The ID of the message.
304
+ feedback (str): The feedback content.
305
+ kwargs (Any): Additional arguments.
306
+
307
+ Returns:
308
+ None
309
+ """
310
+ pass
311
+
312
+ @abstractmethod
313
+ def get_deanonymized_message(
314
+ self,
315
+ user_id: str,
316
+ conversation_id: str,
317
+ message_id: str,
318
+ is_anonymized: bool,
319
+ mappings: list[AnonymizerMapping],
320
+ **kwargs: Any,
321
+ ) -> Message:
322
+ """Retrieve a deanonymized message.
323
+
324
+ Args:
325
+ user_id (str): The ID of the user.
326
+ conversation_id (str): The ID of the conversation.
327
+ message_id (str): The ID of the message.
328
+ is_anonymized (bool): Whether the message is anonymized.
329
+ mappings (list[AnonymizerMapping]): A list of anonymizer mappings.
330
+ kwargs (Any): Additional arguments.
331
+
332
+ Returns:
333
+ Message: The deanonymized message.
334
+ """
335
+ pass
336
+
337
+ @abstractmethod
338
+ def get_deanonymized_messages(
339
+ self,
340
+ messages: list[Message],
341
+ is_anonymized: bool,
342
+ mappings: list[AnonymizerMapping] | None = None,
343
+ **kwargs: Any,
344
+ ) -> list[Message]:
345
+ """Retrieve a list of deanonymized messages.
346
+
347
+ Args:
348
+ messages (list[Message]): A list of messages.
349
+ is_anonymized (bool): Whether the messages are anonymized.
350
+ mappings (list[AnonymizerMapping] | None, optional): A list of anonymizer mappings. Defaults to None.
351
+ kwargs (Any): Additional arguments.
352
+
353
+ Returns:
354
+ list[Message]: A list of deanonymized messages.
355
+ """
356
+ pass
@@ -0,0 +1,21 @@
1
+ """Tool plugin system for GLLM Agents.
2
+
3
+ This module provides a plugin system for tools, allowing dynamic marking and preparation of tools
4
+ similar to VSCode's extension model. Tools can be decorated with metadata while remaining independent
5
+ from the core registration mechanism.
6
+
7
+ Authors:
8
+ Raymond Christopher (raymond.christopher@gdplabs.id)
9
+ """
10
+
11
+ from glchat_plugin.tools.decorators import (
12
+ get_plugin_metadata,
13
+ is_tool_plugin,
14
+ tool_plugin,
15
+ )
16
+
17
+ __all__ = [
18
+ "tool_plugin",
19
+ "is_tool_plugin",
20
+ "get_plugin_metadata",
21
+ ]
@@ -0,0 +1,115 @@
1
+ """Tool Plugin Decorators for External Use.
2
+
3
+ This module provides utilities for marking and preparing tool plugins using a decorator-based approach.
4
+ The decorators and utilities can be used independently in external repositories, as they only add
5
+ metadata to tool classes without creating any connections to a plugin registration system.
6
+
7
+ Authors:
8
+ Raymond Christopher (raymond.christopher@gdplabs.id)
9
+ Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
10
+ """
11
+
12
+ import inspect
13
+ from typing import Any, Callable, Type
14
+
15
+ from gllm_core.utils import LoggerManager
16
+ from langchain_core.tools import BaseTool
17
+
18
+ logger = LoggerManager().get_logger()
19
+
20
+
21
+ def tool_plugin(
22
+ version: str = "1.0.0",
23
+ ) -> Callable[[Type[BaseTool]], Type[BaseTool]]:
24
+ """Decorator to mark a BaseTool class as a tool plugin.
25
+
26
+ This decorator adds metadata to the tool class that will be used by the
27
+ plugin system when the tool is loaded. It doesn't directly register
28
+ the tool with any system, allowing for use in external repositories.
29
+ The actual tool name and description are intended to be retrieved
30
+ from the tool instance at runtime.
31
+
32
+ Args:
33
+ version (str): Version of the plugin. Defaults to "1.0.0".
34
+
35
+ Returns:
36
+ Callable[[Type[BaseTool]], Type[BaseTool]]: A decorator function that wraps the tool class.
37
+
38
+ Example:
39
+ ```python
40
+ @tool_plugin(version="1.0.0")
41
+ class MyAwesomeTool(BaseTool):
42
+ name = "my_awesome_tool"
43
+ description = "Does something awesome"
44
+
45
+ def _run(self, **kwargs):
46
+ return "Awesome result!"
47
+ ```
48
+ """
49
+
50
+ def decorator(tool_class: Type[BaseTool]) -> Type[BaseTool]:
51
+ """Marks a BaseTool class as a plugin by adding metadata for discovery.
52
+
53
+ This decorator adds plugin metadata to the tool class and marks it for later discovery.
54
+ It validates that the decorated class is a subclass of BaseTool.
55
+
56
+ Args:
57
+ tool_class (Type[BaseTool]): The BaseTool class to be decorated with plugin metadata.
58
+
59
+ Returns:
60
+ Type[BaseTool]: The decorated BaseTool class with added plugin metadata.
61
+ """
62
+ if not issubclass(tool_class, BaseTool):
63
+ raise TypeError(f"{tool_class.__name__} is not a subclass of BaseTool")
64
+
65
+ # Store basic plugin metadata as class attributes for later discovery
66
+ tool_class._plugin_metadata = {
67
+ "version": version,
68
+ "tool_class": tool_class.__name__,
69
+ "module": tool_class.__module__,
70
+ }
71
+
72
+ # Mark the class as a decorated tool plugin for easy discovery
73
+ tool_class._is_tool_plugin = True
74
+
75
+ # Log the preparation (but don't require any specific logger)
76
+ try:
77
+ # Simplified logging message
78
+ logger.info(f"Marked tool class {tool_class.__name__} as plugin with version {version}")
79
+ except Exception:
80
+ # Ignore logging errors in standalone mode
81
+ pass
82
+
83
+ return tool_class
84
+
85
+ return decorator
86
+
87
+
88
+ def is_tool_plugin(obj: Any) -> bool:
89
+ """Check if an object is a tool plugin.
90
+
91
+ Args:
92
+ obj (Any): The object to check.
93
+
94
+ Returns:
95
+ bool: True if the object is a decorated tool plugin, False otherwise.
96
+ """
97
+ return inspect.isclass(obj) and getattr(obj, "_is_tool_plugin", False) is True
98
+
99
+
100
+ def get_plugin_metadata(tool_class: Type[BaseTool]) -> dict[str, Any]:
101
+ """Get the plugin metadata from a decorated tool class.
102
+
103
+ Args:
104
+ tool_class (Type[BaseTool]): The tool class to get metadata from.
105
+
106
+ Returns:
107
+ dict[str, Any]: A dictionary of plugin metadata.
108
+
109
+ Raises:
110
+ ValueError: If the tool class is not a decorated tool plugin.
111
+ """
112
+ if not is_tool_plugin(tool_class):
113
+ raise ValueError(f"{tool_class.__name__} is not a decorated tool plugin")
114
+
115
+ return getattr(tool_class, "_plugin_metadata", {})