gllm-core-binary 0.4.4__py3-none-manylinux_2_31_x86_64.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.
- gllm_core/__init__.py +1 -0
- gllm_core/__init__.pyi +0 -0
- gllm_core/adapters/__init__.py +5 -0
- gllm_core/adapters/__init__.pyi +3 -0
- gllm_core/adapters/tool/__init__.py +6 -0
- gllm_core/adapters/tool/__init__.pyi +4 -0
- gllm_core/adapters/tool/google_adk.py +91 -0
- gllm_core/adapters/tool/google_adk.pyi +23 -0
- gllm_core/adapters/tool/langchain.py +130 -0
- gllm_core/adapters/tool/langchain.pyi +31 -0
- gllm_core/constants.py +55 -0
- gllm_core/constants.pyi +36 -0
- gllm_core/event/__init__.py +6 -0
- gllm_core/event/__init__.pyi +4 -0
- gllm_core/event/event_emitter.py +211 -0
- gllm_core/event/event_emitter.pyi +155 -0
- gllm_core/event/handler/__init__.py +7 -0
- gllm_core/event/handler/__init__.pyi +5 -0
- gllm_core/event/handler/console_event_handler.py +48 -0
- gllm_core/event/handler/console_event_handler.pyi +32 -0
- gllm_core/event/handler/event_handler.py +89 -0
- gllm_core/event/handler/event_handler.pyi +51 -0
- gllm_core/event/handler/print_event_handler.py +130 -0
- gllm_core/event/handler/print_event_handler.pyi +33 -0
- gllm_core/event/handler/stream_event_handler.py +85 -0
- gllm_core/event/handler/stream_event_handler.pyi +62 -0
- gllm_core/event/hook/__init__.py +5 -0
- gllm_core/event/hook/__init__.pyi +3 -0
- gllm_core/event/hook/event_hook.py +30 -0
- gllm_core/event/hook/event_hook.pyi +18 -0
- gllm_core/event/hook/json_stringify_event_hook.py +32 -0
- gllm_core/event/hook/json_stringify_event_hook.pyi +16 -0
- gllm_core/event/messenger.py +133 -0
- gllm_core/event/messenger.pyi +66 -0
- gllm_core/schema/__init__.py +8 -0
- gllm_core/schema/__init__.pyi +6 -0
- gllm_core/schema/chunk.py +148 -0
- gllm_core/schema/chunk.pyi +66 -0
- gllm_core/schema/component.py +546 -0
- gllm_core/schema/component.pyi +205 -0
- gllm_core/schema/event.py +50 -0
- gllm_core/schema/event.pyi +33 -0
- gllm_core/schema/schema_generator.py +150 -0
- gllm_core/schema/schema_generator.pyi +35 -0
- gllm_core/schema/tool.py +418 -0
- gllm_core/schema/tool.pyi +198 -0
- gllm_core/utils/__init__.py +32 -0
- gllm_core/utils/__init__.pyi +13 -0
- gllm_core/utils/analyzer.py +256 -0
- gllm_core/utils/analyzer.pyi +123 -0
- gllm_core/utils/binary_handler_factory.py +99 -0
- gllm_core/utils/binary_handler_factory.pyi +62 -0
- gllm_core/utils/chunk_metadata_merger.py +102 -0
- gllm_core/utils/chunk_metadata_merger.pyi +41 -0
- gllm_core/utils/concurrency.py +184 -0
- gllm_core/utils/concurrency.pyi +94 -0
- gllm_core/utils/event_formatter.py +69 -0
- gllm_core/utils/event_formatter.pyi +30 -0
- gllm_core/utils/google_sheets.py +115 -0
- gllm_core/utils/google_sheets.pyi +18 -0
- gllm_core/utils/imports.py +91 -0
- gllm_core/utils/imports.pyi +42 -0
- gllm_core/utils/logger_manager.py +339 -0
- gllm_core/utils/logger_manager.pyi +176 -0
- gllm_core/utils/main_method_resolver.py +185 -0
- gllm_core/utils/main_method_resolver.pyi +54 -0
- gllm_core/utils/merger_method.py +130 -0
- gllm_core/utils/merger_method.pyi +49 -0
- gllm_core/utils/retry.py +258 -0
- gllm_core/utils/retry.pyi +41 -0
- gllm_core/utils/similarity.py +29 -0
- gllm_core/utils/similarity.pyi +10 -0
- gllm_core/utils/validation.py +26 -0
- gllm_core/utils/validation.pyi +12 -0
- gllm_core_binary-0.4.4.dist-info/METADATA +177 -0
- gllm_core_binary-0.4.4.dist-info/RECORD +78 -0
- gllm_core_binary-0.4.4.dist-info/WHEEL +5 -0
- gllm_core_binary-0.4.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""An analyzer for the _run method of a component.
|
|
2
|
+
|
|
3
|
+
Includes the analyzer itself and the data models used to represent the analysis results.
|
|
4
|
+
|
|
5
|
+
Authors:
|
|
6
|
+
Dimitrij Ray (dimitrij.ray@gdplabs.id)
|
|
7
|
+
|
|
8
|
+
References:
|
|
9
|
+
NONE
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import ast
|
|
13
|
+
import inspect
|
|
14
|
+
import textwrap
|
|
15
|
+
from enum import StrEnum
|
|
16
|
+
from typing import Any, Callable
|
|
17
|
+
|
|
18
|
+
from pydantic import BaseModel, Field
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ParameterKind(StrEnum):
|
|
22
|
+
"""Enum representing the different kinds of parameters a method can have."""
|
|
23
|
+
|
|
24
|
+
POSITIONAL_ONLY = "positional_only"
|
|
25
|
+
POSITIONAL_OR_KEYWORD = "positional_or_keyword"
|
|
26
|
+
VAR_POSITIONAL = "var_positional"
|
|
27
|
+
KEYWORD_ONLY = "keyword_only"
|
|
28
|
+
VAR_KEYWORD = "var_keyword"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ParameterInfo(BaseModel):
|
|
32
|
+
"""Model representing information about a method parameter.
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
kind (ParameterKind): The kind of the parameter.
|
|
36
|
+
default (str): The default value of the parameter, if any.
|
|
37
|
+
annotation (str): The type annotation of the parameter, if any.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
kind: ParameterKind
|
|
41
|
+
default: str | None = None
|
|
42
|
+
annotation: str | None = None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class MethodSignature(BaseModel):
|
|
46
|
+
"""Model representing the signature of a method.
|
|
47
|
+
|
|
48
|
+
Attributes:
|
|
49
|
+
parameters (dict[str, ParameterInfo]): A dictionary of parameter names to their information.
|
|
50
|
+
is_async (bool): Whether the method is asynchronous.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
parameters: dict[str, ParameterInfo]
|
|
54
|
+
is_async: bool
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ArgUsages(BaseModel):
|
|
58
|
+
"""Model representing the different types of argument usage in a run.
|
|
59
|
+
|
|
60
|
+
Attributes:
|
|
61
|
+
required (list[str]): A list of argument names that are required.
|
|
62
|
+
optional (list[str]): A list of argument names that are optional.
|
|
63
|
+
unresolvable (list[str]): A list of unresolvable key patterns encountered during analysis.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
required: list[str] = Field(default_factory=list)
|
|
67
|
+
optional: list[str] = Field(default_factory=list)
|
|
68
|
+
unresolvable: list[str] = Field(default_factory=list)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class RunArgumentUsageType(StrEnum):
|
|
72
|
+
"""Enum representing the different types of argument usage in a run."""
|
|
73
|
+
|
|
74
|
+
FULL_PASS = "full_pass"
|
|
75
|
+
REQUIRED = "required"
|
|
76
|
+
OPTIONAL = "optional"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class RunProfile(BaseModel):
|
|
80
|
+
"""Model representing the profile of a run.
|
|
81
|
+
|
|
82
|
+
Attributes:
|
|
83
|
+
arg_usages (ArgUsages): A dictionary mapping argument usage types to lists of
|
|
84
|
+
argument names.
|
|
85
|
+
full_pass_methods (list[str]): A list of method names that fully pass the kwargs.
|
|
86
|
+
method_signatures (dict[str, MethodSignature]): A dictionary mapping method names to their signatures.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
arg_usages: ArgUsages
|
|
90
|
+
full_pass_methods: list[str]
|
|
91
|
+
method_signatures: dict[str, MethodSignature]
|
|
92
|
+
|
|
93
|
+
def __init__(self, **data: Any):
|
|
94
|
+
"""Initialize the RunProfile with the given data.
|
|
95
|
+
|
|
96
|
+
This is to circumvent Pylint false positives due to the usage of Field(default_factory=...).
|
|
97
|
+
"""
|
|
98
|
+
super().__init__(
|
|
99
|
+
arg_usages=data.get("arg_usages", ArgUsages()),
|
|
100
|
+
full_pass_methods=data.get("full_pass_methods", []),
|
|
101
|
+
method_signatures=data.get("method_signatures", {}),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class RunAnalyzer(ast.NodeVisitor):
|
|
106
|
+
"""AST NodeVisitor that analyzes a class to build a RunProfile.
|
|
107
|
+
|
|
108
|
+
The run analyzer visits the AST nodes of a class to analyze the _run method and build a RunProfile.
|
|
109
|
+
It will look for the usage of the **kwargs parameter in method calls and subscript expressions.
|
|
110
|
+
The traversal result is stored as a RunProfile object.
|
|
111
|
+
|
|
112
|
+
Attributes:
|
|
113
|
+
cls (type): The class to analyze.
|
|
114
|
+
profile (RunProfile): The profile of the run being analyzed.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
def __init__(self, cls):
|
|
118
|
+
"""Initialize the RunAnalyzer with a class.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
cls (type): The class to analyze.
|
|
122
|
+
"""
|
|
123
|
+
self.cls = cls
|
|
124
|
+
self.profile = RunProfile()
|
|
125
|
+
|
|
126
|
+
def visit_Call(self, node: ast.Call) -> None:
|
|
127
|
+
"""Visit a Call node in the AST.
|
|
128
|
+
|
|
129
|
+
This node represents a function call in the source code.
|
|
130
|
+
Here, we are looking for calls to methods that fully pass the kwargs.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
node (ast.Call): The Call node to visit.
|
|
134
|
+
"""
|
|
135
|
+
for keyword in node.keywords:
|
|
136
|
+
if keyword.arg is None and isinstance(keyword.value, ast.Name) and keyword.value.id == "kwargs":
|
|
137
|
+
if (
|
|
138
|
+
isinstance(node.func, ast.Attribute)
|
|
139
|
+
and isinstance(node.func.value, ast.Name)
|
|
140
|
+
and node.func.value.id == "self"
|
|
141
|
+
):
|
|
142
|
+
self.profile.full_pass_methods.append(f"{self.cls.__name__}.{node.func.attr}")
|
|
143
|
+
else:
|
|
144
|
+
func_name = node.func.id if isinstance(node.func, ast.Name) else node.func.attr
|
|
145
|
+
self.profile.full_pass_methods.append(func_name)
|
|
146
|
+
|
|
147
|
+
if (
|
|
148
|
+
isinstance(node.func, ast.Attribute)
|
|
149
|
+
and isinstance(node.func.value, ast.Name)
|
|
150
|
+
and node.func.value.id == "kwargs"
|
|
151
|
+
and node.func.attr == "get"
|
|
152
|
+
):
|
|
153
|
+
if node.args:
|
|
154
|
+
try:
|
|
155
|
+
key = self._get_key(node.args[0])
|
|
156
|
+
if key and key != "kwargs":
|
|
157
|
+
self.profile.arg_usages.optional.append(key)
|
|
158
|
+
except ValueError:
|
|
159
|
+
self.profile.arg_usages.unresolvable.append(self._expr_to_text(node.args[0]))
|
|
160
|
+
|
|
161
|
+
self.generic_visit(node)
|
|
162
|
+
|
|
163
|
+
def visit_Subscript(self, node: ast.Subscript) -> None:
|
|
164
|
+
"""Visit a Subscript node in the AST.
|
|
165
|
+
|
|
166
|
+
The Subscript node represents a subscripted value in the source code.
|
|
167
|
+
Example: kwargs["key"]
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
node (ast.Subscript): The Subscript node to visit.
|
|
171
|
+
"""
|
|
172
|
+
if isinstance(node.value, ast.Name) and node.value.id == "kwargs":
|
|
173
|
+
if not isinstance(node.ctx, ast.Load):
|
|
174
|
+
self.generic_visit(node)
|
|
175
|
+
return
|
|
176
|
+
try:
|
|
177
|
+
slice_node = node.slice
|
|
178
|
+
|
|
179
|
+
key = self._get_key(slice_node)
|
|
180
|
+
if key and key != "kwargs":
|
|
181
|
+
self.profile.arg_usages.required.append(key)
|
|
182
|
+
except ValueError:
|
|
183
|
+
self.profile.arg_usages.unresolvable.append(self._expr_to_text(slice_node))
|
|
184
|
+
self.generic_visit(node)
|
|
185
|
+
|
|
186
|
+
def _get_key(self, node_arg: ast.expr | str) -> str:
|
|
187
|
+
"""Get the key from an AST node.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
node_arg (ast.expr | str): The AST node representing the key.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
str: The key as a string.
|
|
194
|
+
|
|
195
|
+
Raises:
|
|
196
|
+
ValueError: If the key cannot be extracted from the node.
|
|
197
|
+
"""
|
|
198
|
+
if isinstance(node_arg, str):
|
|
199
|
+
return node_arg
|
|
200
|
+
|
|
201
|
+
if isinstance(node_arg, ast.Constant):
|
|
202
|
+
if isinstance(node_arg.value, str):
|
|
203
|
+
return node_arg.value
|
|
204
|
+
else:
|
|
205
|
+
raise ValueError(f"Non-string constant: {node_arg.value}")
|
|
206
|
+
|
|
207
|
+
if isinstance(node_arg, ast.Attribute):
|
|
208
|
+
raise ValueError(f"Attribute key not resolvable: {node_arg.attr}")
|
|
209
|
+
|
|
210
|
+
if isinstance(node_arg, ast.Name):
|
|
211
|
+
raise ValueError(f"Variable key not supported: {node_arg.id}")
|
|
212
|
+
|
|
213
|
+
raise ValueError(f"Unsupported AST node type for key extraction: {type(node_arg)}")
|
|
214
|
+
|
|
215
|
+
def _expr_to_text(self, expr: ast.AST) -> str:
|
|
216
|
+
"""Return a readable textual form of an AST expression.
|
|
217
|
+
|
|
218
|
+
On Python 3.11+, ast.unparse is available and sufficient.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
expr (ast.AST): The AST expression node.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
str: Text representation of the expression.
|
|
225
|
+
"""
|
|
226
|
+
try:
|
|
227
|
+
return ast.unparse(expr)
|
|
228
|
+
except Exception:
|
|
229
|
+
if isinstance(expr, ast.Name):
|
|
230
|
+
return expr.id
|
|
231
|
+
if isinstance(expr, ast.Attribute):
|
|
232
|
+
return expr.attr
|
|
233
|
+
if isinstance(expr, ast.Constant):
|
|
234
|
+
return str(expr.value)
|
|
235
|
+
# Fallback for unknown node types
|
|
236
|
+
return expr.__class__.__name__
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def analyze_method(cls: type, method: Callable) -> RunProfile:
|
|
240
|
+
"""Analyze a method using RunAnalyzer.
|
|
241
|
+
|
|
242
|
+
This function encapsulates the common analysis logic used by both
|
|
243
|
+
Component._analyze_run_method() and schema_generator._generate_from_analyzer().
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
cls (type): The class containing the method (for analyzer context).
|
|
247
|
+
method (Callable): The method to analyze.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
RunProfile: The analysis results.
|
|
251
|
+
"""
|
|
252
|
+
analyzer = RunAnalyzer(cls)
|
|
253
|
+
source = inspect.getsource(method)
|
|
254
|
+
tree = ast.parse(textwrap.dedent(source))
|
|
255
|
+
analyzer.visit(tree)
|
|
256
|
+
return analyzer.profile
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
from _typeshed import Incomplete
|
|
3
|
+
from enum import StrEnum
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
from typing import Any, Callable
|
|
6
|
+
|
|
7
|
+
class ParameterKind(StrEnum):
|
|
8
|
+
"""Enum representing the different kinds of parameters a method can have."""
|
|
9
|
+
POSITIONAL_ONLY = 'positional_only'
|
|
10
|
+
POSITIONAL_OR_KEYWORD = 'positional_or_keyword'
|
|
11
|
+
VAR_POSITIONAL = 'var_positional'
|
|
12
|
+
KEYWORD_ONLY = 'keyword_only'
|
|
13
|
+
VAR_KEYWORD = 'var_keyword'
|
|
14
|
+
|
|
15
|
+
class ParameterInfo(BaseModel):
|
|
16
|
+
"""Model representing information about a method parameter.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
kind (ParameterKind): The kind of the parameter.
|
|
20
|
+
default (str): The default value of the parameter, if any.
|
|
21
|
+
annotation (str): The type annotation of the parameter, if any.
|
|
22
|
+
"""
|
|
23
|
+
kind: ParameterKind
|
|
24
|
+
default: str | None
|
|
25
|
+
annotation: str | None
|
|
26
|
+
|
|
27
|
+
class MethodSignature(BaseModel):
|
|
28
|
+
"""Model representing the signature of a method.
|
|
29
|
+
|
|
30
|
+
Attributes:
|
|
31
|
+
parameters (dict[str, ParameterInfo]): A dictionary of parameter names to their information.
|
|
32
|
+
is_async (bool): Whether the method is asynchronous.
|
|
33
|
+
"""
|
|
34
|
+
parameters: dict[str, ParameterInfo]
|
|
35
|
+
is_async: bool
|
|
36
|
+
|
|
37
|
+
class ArgUsages(BaseModel):
|
|
38
|
+
"""Model representing the different types of argument usage in a run.
|
|
39
|
+
|
|
40
|
+
Attributes:
|
|
41
|
+
required (list[str]): A list of argument names that are required.
|
|
42
|
+
optional (list[str]): A list of argument names that are optional.
|
|
43
|
+
unresolvable (list[str]): A list of unresolvable key patterns encountered during analysis.
|
|
44
|
+
"""
|
|
45
|
+
required: list[str]
|
|
46
|
+
optional: list[str]
|
|
47
|
+
unresolvable: list[str]
|
|
48
|
+
|
|
49
|
+
class RunArgumentUsageType(StrEnum):
|
|
50
|
+
"""Enum representing the different types of argument usage in a run."""
|
|
51
|
+
FULL_PASS = 'full_pass'
|
|
52
|
+
REQUIRED = 'required'
|
|
53
|
+
OPTIONAL = 'optional'
|
|
54
|
+
|
|
55
|
+
class RunProfile(BaseModel):
|
|
56
|
+
"""Model representing the profile of a run.
|
|
57
|
+
|
|
58
|
+
Attributes:
|
|
59
|
+
arg_usages (ArgUsages): A dictionary mapping argument usage types to lists of
|
|
60
|
+
argument names.
|
|
61
|
+
full_pass_methods (list[str]): A list of method names that fully pass the kwargs.
|
|
62
|
+
method_signatures (dict[str, MethodSignature]): A dictionary mapping method names to their signatures.
|
|
63
|
+
"""
|
|
64
|
+
arg_usages: ArgUsages
|
|
65
|
+
full_pass_methods: list[str]
|
|
66
|
+
method_signatures: dict[str, MethodSignature]
|
|
67
|
+
def __init__(self, **data: Any) -> None:
|
|
68
|
+
"""Initialize the RunProfile with the given data.
|
|
69
|
+
|
|
70
|
+
This is to circumvent Pylint false positives due to the usage of Field(default_factory=...).
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
class RunAnalyzer(ast.NodeVisitor):
|
|
74
|
+
"""AST NodeVisitor that analyzes a class to build a RunProfile.
|
|
75
|
+
|
|
76
|
+
The run analyzer visits the AST nodes of a class to analyze the _run method and build a RunProfile.
|
|
77
|
+
It will look for the usage of the **kwargs parameter in method calls and subscript expressions.
|
|
78
|
+
The traversal result is stored as a RunProfile object.
|
|
79
|
+
|
|
80
|
+
Attributes:
|
|
81
|
+
cls (type): The class to analyze.
|
|
82
|
+
profile (RunProfile): The profile of the run being analyzed.
|
|
83
|
+
"""
|
|
84
|
+
cls: Incomplete
|
|
85
|
+
profile: Incomplete
|
|
86
|
+
def __init__(self, cls) -> None:
|
|
87
|
+
"""Initialize the RunAnalyzer with a class.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
cls (type): The class to analyze.
|
|
91
|
+
"""
|
|
92
|
+
def visit_Call(self, node: ast.Call) -> None:
|
|
93
|
+
"""Visit a Call node in the AST.
|
|
94
|
+
|
|
95
|
+
This node represents a function call in the source code.
|
|
96
|
+
Here, we are looking for calls to methods that fully pass the kwargs.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
node (ast.Call): The Call node to visit.
|
|
100
|
+
"""
|
|
101
|
+
def visit_Subscript(self, node: ast.Subscript) -> None:
|
|
102
|
+
'''Visit a Subscript node in the AST.
|
|
103
|
+
|
|
104
|
+
The Subscript node represents a subscripted value in the source code.
|
|
105
|
+
Example: kwargs["key"]
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
node (ast.Subscript): The Subscript node to visit.
|
|
109
|
+
'''
|
|
110
|
+
|
|
111
|
+
def analyze_method(cls, method: Callable) -> RunProfile:
|
|
112
|
+
"""Analyze a method using RunAnalyzer.
|
|
113
|
+
|
|
114
|
+
This function encapsulates the common analysis logic used by both
|
|
115
|
+
Component._analyze_run_method() and schema_generator._generate_from_analyzer().
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
cls (type): The class containing the method (for analyzer context).
|
|
119
|
+
method (Callable): The method to analyze.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
RunProfile: The analysis results.
|
|
123
|
+
"""
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""Binary handler factory for handling binary data in various formats.
|
|
2
|
+
|
|
3
|
+
This module provides factory function to create handlers for different types of binary data.
|
|
4
|
+
|
|
5
|
+
Authors:
|
|
6
|
+
Resti Febriana (resti.febriana@gdplabs.id)
|
|
7
|
+
|
|
8
|
+
References:
|
|
9
|
+
NONE
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import base64
|
|
13
|
+
from enum import StrEnum
|
|
14
|
+
from typing import Callable
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BinaryHandlingStrategy(StrEnum):
|
|
18
|
+
"""Enum for binary data handling options.
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
SKIP (str): Skip binary data.
|
|
22
|
+
BASE64 (str): Encode binary data to base64.
|
|
23
|
+
HEX (str): Encode binary data to hex.
|
|
24
|
+
SHOW_SIZE (str): Show the size of binary data.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
SKIP = "skip"
|
|
28
|
+
BASE64 = "base64"
|
|
29
|
+
HEX = "hex"
|
|
30
|
+
SHOW_SIZE = "show_size"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def binary_handler_factory(handling_type: BinaryHandlingStrategy | str) -> Callable[[bytes], str | None]:
|
|
34
|
+
"""Factory function to create appropriate binary data handler.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
handling_type (BinaryHandlingStrategy | str): The type of binary handling to use.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Callable[[bytes], str | None]: A function that handles binary data according to specified type.
|
|
41
|
+
"""
|
|
42
|
+
handling_type = BinaryHandlingStrategy(handling_type)
|
|
43
|
+
|
|
44
|
+
handlers = {
|
|
45
|
+
BinaryHandlingStrategy.SKIP: skip_handler,
|
|
46
|
+
BinaryHandlingStrategy.BASE64: base64_handler,
|
|
47
|
+
BinaryHandlingStrategy.HEX: hex_handler,
|
|
48
|
+
BinaryHandlingStrategy.SHOW_SIZE: show_size_handler,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return handlers[handling_type]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def skip_handler(v: bytes) -> None:
|
|
55
|
+
"""Handler for skipping binary data.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
v (bytes): The binary data to skip.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
None: The handler returns None.
|
|
62
|
+
"""
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def base64_handler(v: bytes) -> str:
|
|
67
|
+
"""Handler for encoding binary data to base64.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
v (bytes): The binary data to encode.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
str: The raw base64 encoded binary data.
|
|
74
|
+
"""
|
|
75
|
+
return base64.b64encode(v).decode()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def hex_handler(v: bytes) -> str:
|
|
79
|
+
"""Handler for encoding binary data to hex.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
v (bytes): The binary data to encode.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
str: The raw hex encoded binary data.
|
|
86
|
+
"""
|
|
87
|
+
return v.hex()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def show_size_handler(v: bytes) -> str:
|
|
91
|
+
"""Handler for showing the size of binary data.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
v (bytes): The binary data to show the size.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
str: The size of the binary data.
|
|
98
|
+
"""
|
|
99
|
+
return f"<Binary Data> (Size: {len(v):,} bytes)"
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
from typing import Callable
|
|
3
|
+
|
|
4
|
+
class BinaryHandlingStrategy(StrEnum):
|
|
5
|
+
"""Enum for binary data handling options.
|
|
6
|
+
|
|
7
|
+
Attributes:
|
|
8
|
+
SKIP (str): Skip binary data.
|
|
9
|
+
BASE64 (str): Encode binary data to base64.
|
|
10
|
+
HEX (str): Encode binary data to hex.
|
|
11
|
+
SHOW_SIZE (str): Show the size of binary data.
|
|
12
|
+
"""
|
|
13
|
+
SKIP = 'skip'
|
|
14
|
+
BASE64 = 'base64'
|
|
15
|
+
HEX = 'hex'
|
|
16
|
+
SHOW_SIZE = 'show_size'
|
|
17
|
+
|
|
18
|
+
def binary_handler_factory(handling_type: BinaryHandlingStrategy | str) -> Callable[[bytes], str | None]:
|
|
19
|
+
"""Factory function to create appropriate binary data handler.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
handling_type (BinaryHandlingStrategy | str): The type of binary handling to use.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Callable[[bytes], str | None]: A function that handles binary data according to specified type.
|
|
26
|
+
"""
|
|
27
|
+
def skip_handler(v: bytes) -> None:
|
|
28
|
+
"""Handler for skipping binary data.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
v (bytes): The binary data to skip.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
None: The handler returns None.
|
|
35
|
+
"""
|
|
36
|
+
def base64_handler(v: bytes) -> str:
|
|
37
|
+
"""Handler for encoding binary data to base64.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
v (bytes): The binary data to encode.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
str: The raw base64 encoded binary data.
|
|
44
|
+
"""
|
|
45
|
+
def hex_handler(v: bytes) -> str:
|
|
46
|
+
"""Handler for encoding binary data to hex.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
v (bytes): The binary data to encode.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
str: The raw hex encoded binary data.
|
|
53
|
+
"""
|
|
54
|
+
def show_size_handler(v: bytes) -> str:
|
|
55
|
+
"""Handler for showing the size of binary data.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
v (bytes): The binary data to show the size.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
str: The size of the binary data.
|
|
62
|
+
"""
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Defines a helper class to merge metadata from multiple chunks.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Henry Wicaksono (henry.wicaksono@gdplabs.id)
|
|
5
|
+
|
|
6
|
+
References:
|
|
7
|
+
NONE
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Any, Callable
|
|
11
|
+
|
|
12
|
+
from gllm_core.constants import DefaultChunkMetadata
|
|
13
|
+
from gllm_core.utils.merger_method import MergerMethod
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ChunkMetadataMerger:
|
|
17
|
+
"""A helper class to merge metadata from multiple chunks.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
merger_func_map (dict[str, Callable[[list[Any]], Any]]): A mapping of metadata keys to merger functions.
|
|
21
|
+
default_merger_func (Callable[[list[Any]], Any]): The default merger function for metadata keys that are not
|
|
22
|
+
present in the merger_func_map.
|
|
23
|
+
retained_keys (set[str] | None): The keys that should be retained in the merged metadata.
|
|
24
|
+
If None, all intersection keys are retained.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
merger_func_map: dict[str, Callable[[list[Any]], Any]] | None = None,
|
|
30
|
+
default_merger_func: Callable[[list[Any]], Any] | None = None,
|
|
31
|
+
retained_keys: set[str] | None = None,
|
|
32
|
+
):
|
|
33
|
+
"""Initializes a new instance of the ChunkMetadataMerger class.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
merger_func_map (dict[str, Callable[[list[Any]], Any]] | None, optional): A mapping of metadata keys to
|
|
37
|
+
merger functions. Defaults to None, in which case a default merger map is used. The default merger map:
|
|
38
|
+
1. Picks the first value of the PREV_CHUNK_ID key.
|
|
39
|
+
2. Picks the last value of the NEXT_CHUNK_ID key.
|
|
40
|
+
default_merger_func (Callable[[list[Any]], Any] | None, optional): The default merger for metadata keys that
|
|
41
|
+
are not present in the merger_func_map. Defaults to None, in which case a default merger that picks the
|
|
42
|
+
first value is used.
|
|
43
|
+
retained_keys (set[str] | None, optional): The keys that should be retained in the merged metadata. Defaults
|
|
44
|
+
to None, in which case all intersection keys are retained.
|
|
45
|
+
"""
|
|
46
|
+
self.merger_map = merger_func_map or {
|
|
47
|
+
DefaultChunkMetadata.PREV_CHUNK_ID: MergerMethod.pick_first,
|
|
48
|
+
DefaultChunkMetadata.NEXT_CHUNK_ID: MergerMethod.pick_last,
|
|
49
|
+
}
|
|
50
|
+
self.default_merger = default_merger_func or MergerMethod.pick_first
|
|
51
|
+
self.retained_keys = retained_keys
|
|
52
|
+
|
|
53
|
+
def merge(self, metadatas: list[dict[str, Any]]) -> dict[str, Any]:
|
|
54
|
+
"""Merges metadata from multiple chunks.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
metadatas (list[dict[str, Any]]): The metadata to merge.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
dict[str, Any]: The merged metadata.
|
|
61
|
+
"""
|
|
62
|
+
if not metadatas:
|
|
63
|
+
return {}
|
|
64
|
+
|
|
65
|
+
metadata_keys = self._get_retained_keys(metadatas)
|
|
66
|
+
|
|
67
|
+
merged_metadata = {}
|
|
68
|
+
for key in metadata_keys:
|
|
69
|
+
metadata_values = [metadata[key] for metadata in metadatas]
|
|
70
|
+
|
|
71
|
+
if key in self.merger_map:
|
|
72
|
+
merged_value = self.merger_map[key](metadata_values)
|
|
73
|
+
else:
|
|
74
|
+
merged_value = self.default_merger(metadata_values)
|
|
75
|
+
|
|
76
|
+
merged_metadata[key] = merged_value
|
|
77
|
+
|
|
78
|
+
return merged_metadata
|
|
79
|
+
|
|
80
|
+
def _get_retained_keys(self, metadatas: list[dict[str, Any]]) -> set[str]:
|
|
81
|
+
"""Gets the set of retained keys.
|
|
82
|
+
|
|
83
|
+
This method ensures that the retained keys are present in all metadata dictionaries.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
metadatas (list[dict[str, Any]]): The metadata to parse.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
set[str]: The set of retained keys.
|
|
90
|
+
|
|
91
|
+
Raises:
|
|
92
|
+
ValueError: If any of the retained keys are missing from any of the metadata dictionaries.
|
|
93
|
+
"""
|
|
94
|
+
common_keys = set.intersection(*(set(metadata.keys()) for metadata in metadatas))
|
|
95
|
+
|
|
96
|
+
retained_keys = self.retained_keys or common_keys
|
|
97
|
+
|
|
98
|
+
missing_keys = retained_keys - common_keys
|
|
99
|
+
if missing_keys:
|
|
100
|
+
raise ValueError(f"The following keys are missing in the metadata: {missing_keys}")
|
|
101
|
+
|
|
102
|
+
return retained_keys
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from _typeshed import Incomplete
|
|
2
|
+
from gllm_core.constants import DefaultChunkMetadata as DefaultChunkMetadata
|
|
3
|
+
from gllm_core.utils.merger_method import MergerMethod as MergerMethod
|
|
4
|
+
from typing import Any, Callable
|
|
5
|
+
|
|
6
|
+
class ChunkMetadataMerger:
|
|
7
|
+
"""A helper class to merge metadata from multiple chunks.
|
|
8
|
+
|
|
9
|
+
Attributes:
|
|
10
|
+
merger_func_map (dict[str, Callable[[list[Any]], Any]]): A mapping of metadata keys to merger functions.
|
|
11
|
+
default_merger_func (Callable[[list[Any]], Any]): The default merger function for metadata keys that are not
|
|
12
|
+
present in the merger_func_map.
|
|
13
|
+
retained_keys (set[str] | None): The keys that should be retained in the merged metadata.
|
|
14
|
+
If None, all intersection keys are retained.
|
|
15
|
+
"""
|
|
16
|
+
merger_map: Incomplete
|
|
17
|
+
default_merger: Incomplete
|
|
18
|
+
retained_keys: Incomplete
|
|
19
|
+
def __init__(self, merger_func_map: dict[str, Callable[[list[Any]], Any]] | None = None, default_merger_func: Callable[[list[Any]], Any] | None = None, retained_keys: set[str] | None = None) -> None:
|
|
20
|
+
"""Initializes a new instance of the ChunkMetadataMerger class.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
merger_func_map (dict[str, Callable[[list[Any]], Any]] | None, optional): A mapping of metadata keys to
|
|
24
|
+
merger functions. Defaults to None, in which case a default merger map is used. The default merger map:
|
|
25
|
+
1. Picks the first value of the PREV_CHUNK_ID key.
|
|
26
|
+
2. Picks the last value of the NEXT_CHUNK_ID key.
|
|
27
|
+
default_merger_func (Callable[[list[Any]], Any] | None, optional): The default merger for metadata keys that
|
|
28
|
+
are not present in the merger_func_map. Defaults to None, in which case a default merger that picks the
|
|
29
|
+
first value is used.
|
|
30
|
+
retained_keys (set[str] | None, optional): The keys that should be retained in the merged metadata. Defaults
|
|
31
|
+
to None, in which case all intersection keys are retained.
|
|
32
|
+
"""
|
|
33
|
+
def merge(self, metadatas: list[dict[str, Any]]) -> dict[str, Any]:
|
|
34
|
+
"""Merges metadata from multiple chunks.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
metadatas (list[dict[str, Any]]): The metadata to merge.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
dict[str, Any]: The merged metadata.
|
|
41
|
+
"""
|