lionagi 0.0.306__py3-none-any.whl → 0.0.308__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.
Files changed (78) hide show
  1. lionagi/__init__.py +2 -5
  2. lionagi/core/__init__.py +7 -5
  3. lionagi/core/agent/__init__.py +3 -0
  4. lionagi/core/agent/base_agent.py +10 -12
  5. lionagi/core/branch/__init__.py +4 -0
  6. lionagi/core/branch/base_branch.py +81 -81
  7. lionagi/core/branch/branch.py +16 -28
  8. lionagi/core/branch/branch_flow_mixin.py +3 -7
  9. lionagi/core/branch/executable_branch.py +86 -56
  10. lionagi/core/branch/util.py +77 -162
  11. lionagi/core/{flow/direct → direct}/__init__.py +1 -1
  12. lionagi/core/{flow/direct/predict.py → direct/parallel_predict.py} +39 -17
  13. lionagi/core/direct/parallel_react.py +0 -0
  14. lionagi/core/direct/parallel_score.py +0 -0
  15. lionagi/core/direct/parallel_select.py +0 -0
  16. lionagi/core/direct/parallel_sentiment.py +0 -0
  17. lionagi/core/direct/predict.py +174 -0
  18. lionagi/core/{flow/direct → direct}/react.py +2 -2
  19. lionagi/core/{flow/direct → direct}/score.py +28 -23
  20. lionagi/core/{flow/direct → direct}/select.py +48 -45
  21. lionagi/core/direct/utils.py +83 -0
  22. lionagi/core/flow/monoflow/ReAct.py +6 -5
  23. lionagi/core/flow/monoflow/__init__.py +9 -0
  24. lionagi/core/flow/monoflow/chat.py +10 -10
  25. lionagi/core/flow/monoflow/chat_mixin.py +11 -10
  26. lionagi/core/flow/monoflow/followup.py +6 -5
  27. lionagi/core/flow/polyflow/__init__.py +1 -0
  28. lionagi/core/flow/polyflow/chat.py +15 -3
  29. lionagi/core/mail/mail_manager.py +18 -19
  30. lionagi/core/mail/schema.py +5 -4
  31. lionagi/core/messages/schema.py +18 -20
  32. lionagi/core/prompt/__init__.py +0 -0
  33. lionagi/core/prompt/prompt_template.py +0 -0
  34. lionagi/core/schema/__init__.py +2 -2
  35. lionagi/core/schema/action_node.py +11 -3
  36. lionagi/core/schema/base_mixin.py +56 -59
  37. lionagi/core/schema/base_node.py +34 -37
  38. lionagi/core/schema/condition.py +24 -0
  39. lionagi/core/schema/data_logger.py +96 -99
  40. lionagi/core/schema/data_node.py +19 -19
  41. lionagi/core/schema/prompt_template.py +0 -0
  42. lionagi/core/schema/structure.py +171 -169
  43. lionagi/core/session/__init__.py +1 -3
  44. lionagi/core/session/session.py +196 -214
  45. lionagi/core/tool/tool_manager.py +95 -103
  46. lionagi/integrations/__init__.py +1 -3
  47. lionagi/integrations/bridge/langchain_/documents.py +17 -18
  48. lionagi/integrations/bridge/langchain_/langchain_bridge.py +14 -14
  49. lionagi/integrations/bridge/llamaindex_/llama_index_bridge.py +22 -22
  50. lionagi/integrations/bridge/llamaindex_/node_parser.py +12 -12
  51. lionagi/integrations/bridge/llamaindex_/reader.py +11 -11
  52. lionagi/integrations/bridge/llamaindex_/textnode.py +7 -7
  53. lionagi/integrations/config/openrouter_configs.py +0 -1
  54. lionagi/integrations/provider/oai.py +26 -26
  55. lionagi/integrations/provider/services.py +38 -38
  56. lionagi/libs/__init__.py +34 -1
  57. lionagi/libs/ln_api.py +211 -221
  58. lionagi/libs/ln_async.py +53 -60
  59. lionagi/libs/ln_convert.py +118 -120
  60. lionagi/libs/ln_dataframe.py +32 -33
  61. lionagi/libs/ln_func_call.py +334 -342
  62. lionagi/libs/ln_nested.py +99 -107
  63. lionagi/libs/ln_parse.py +161 -165
  64. lionagi/libs/sys_util.py +52 -52
  65. lionagi/tests/test_core/test_session.py +254 -266
  66. lionagi/tests/test_core/test_session_base_util.py +299 -300
  67. lionagi/tests/test_core/test_tool_manager.py +70 -74
  68. lionagi/tests/test_libs/test_nested.py +2 -7
  69. lionagi/tests/test_libs/test_parse.py +2 -2
  70. lionagi/version.py +1 -1
  71. {lionagi-0.0.306.dist-info → lionagi-0.0.308.dist-info}/METADATA +4 -2
  72. lionagi-0.0.308.dist-info/RECORD +115 -0
  73. lionagi/core/flow/direct/utils.py +0 -43
  74. lionagi-0.0.306.dist-info/RECORD +0 -106
  75. /lionagi/core/{flow/direct → direct}/sentiment.py +0 -0
  76. {lionagi-0.0.306.dist-info → lionagi-0.0.308.dist-info}/LICENSE +0 -0
  77. {lionagi-0.0.306.dist-info → lionagi-0.0.308.dist-info}/WHEEL +0 -0
  78. {lionagi-0.0.306.dist-info → lionagi-0.0.308.dist-info}/top_level.txt +0 -0
lionagi/libs/ln_parse.py CHANGED
@@ -1,11 +1,11 @@
1
1
  import re
2
2
  import inspect
3
+ import itertools
3
4
  from collections.abc import Callable
4
5
  from typing import Any
5
6
  import numpy as np
6
7
  import lionagi.libs.ln_convert as convert
7
8
 
8
-
9
9
  md_json_char_map = {"\n": "\\n", "\r": "\\r", "\t": "\\t", '"': '\\"'}
10
10
 
11
11
 
@@ -20,35 +20,35 @@ class ParseUtil:
20
20
  the string by appending necessary closing characters before retrying.
21
21
 
22
22
  Args:
23
- s (str): The JSON string to parse.
24
- strict (bool, optional): If True, enforces strict JSON syntax. Defaults to False.
23
+ s (str): The JSON string to parse.
24
+ strict (bool, optional): If True, enforces strict JSON syntax. Defaults to False.
25
25
 
26
26
  Returns:
27
- The parsed JSON object, typically a dictionary or list.
27
+ The parsed JSON object, typically a dictionary or list.
28
28
 
29
29
  Raises:
30
- ValueError: If parsing fails even after attempting to correct the string.
30
+ ValueError: If parsing fails even after attempting to correct the string.
31
31
 
32
32
  Example:
33
- >>> fuzzy_parse_json('{"name": "John", "age": 30, "city": "New York"')
34
- {'name': 'John', 'age': 30, 'city': 'New York'}
33
+ >>> fuzzy_parse_json('{"name": "John", "age": 30, "city": "New York"')
34
+ {'name': 'John', 'age': 30, 'city': 'New York'}
35
35
  """
36
36
  try:
37
37
  return convert.to_dict(str_to_parse, strict=strict)
38
- except:
38
+ except Exception:
39
39
  fixed_s = ParseUtil.fix_json_string(str_to_parse)
40
40
  try:
41
41
  return convert.to_dict(fixed_s, strict=strict)
42
-
43
- except:
42
+
43
+ except Exception:
44
44
  try:
45
- fixed_s = fixed_s.replace('\'', '\"')
45
+ fixed_s = fixed_s.replace("'", '"')
46
46
  return convert.to_dict(fixed_s, strict=strict)
47
-
47
+
48
48
  except Exception as e:
49
49
  raise ValueError(
50
50
  f"Failed to parse JSON even after fixing attempts: {e}"
51
- )
51
+ ) from e
52
52
 
53
53
  @staticmethod
54
54
  def fix_json_string(str_to_parse: str) -> str:
@@ -76,17 +76,17 @@ class ParseUtil:
76
76
  a default mapping is used.
77
77
 
78
78
  Args:
79
- value: The string to be escaped.
80
- char_map: An optional dictionary mapping characters to their escaped versions.
81
- If not provided, a default mapping that escapes newlines, carriage returns,
82
- tabs, and double quotes is used.
79
+ value: The string to be escaped.
80
+ char_map: An optional dictionary mapping characters to their escaped versions.
81
+ If not provided, a default mapping that escapes newlines, carriage returns,
82
+ tabs, and double quotes is used.
83
83
 
84
84
  Returns:
85
- The escaped JSON string.
85
+ The escaped JSON string.
86
86
 
87
87
  Examples:
88
- >>> escape_chars_in_json('Line 1\nLine 2')
89
- 'Line 1\\nLine 2'
88
+ >>> escape_chars_in_json('Line 1\nLine 2')
89
+ 'Line 1\\nLine 2'
90
90
  """
91
91
 
92
92
  def replacement(match):
@@ -114,22 +114,22 @@ class ParseUtil:
114
114
  filtered by language. If a code block is found, it is parsed using the provided parser function.
115
115
 
116
116
  Args:
117
- str_to_parse: The Markdown content to search.
118
- language: An optional language specifier for the code block. If provided,
119
- only code blocks of this language are considered.
120
- regex_pattern: An optional regular expression pattern to use for finding the code block.
121
- If provided, it overrides the language parameter.
122
- parser: A function to parse the extracted code block string.
117
+ str_to_parse: The Markdown content to search.
118
+ language: An optional language specifier for the code block. If provided,
119
+ only code blocks of this language are considered.
120
+ regex_pattern: An optional regular expression pattern to use for finding the code block.
121
+ If provided, it overrides the language parameter.
122
+ parser: A function to parse the extracted code block string.
123
123
 
124
124
  Returns:
125
- The result of parsing the code block with the provided parser function.
125
+ The result of parsing the code block with the provided parser function.
126
126
 
127
127
  Raises:
128
- ValueError: If no code block is found in the Markdown content.
128
+ ValueError: If no code block is found in the Markdown content.
129
129
 
130
130
  Examples:
131
- >>> extract_code_block('```python\\nprint("Hello, world!")\\n```', language='python', parser=lambda x: x)
132
- 'print("Hello, world!")'
131
+ >>> extract_code_block('```python\\nprint("Hello, world!")\\n```', language='python', parser=lambda x: x)
132
+ 'print("Hello, world!")'
133
133
  """
134
134
 
135
135
  if language:
@@ -140,7 +140,7 @@ class ParseUtil:
140
140
  match = re.search(regex_pattern, str_to_parse, re.DOTALL)
141
141
  code_str = ""
142
142
  if match:
143
- code_str = match.group(1).strip()
143
+ code_str = match[1].strip()
144
144
  else:
145
145
  raise ValueError(
146
146
  f"No {language or 'specified'} code block found in the Markdown content."
@@ -162,29 +162,28 @@ class ParseUtil:
162
162
  Markdown string. It then optionally verifies that the parsed JSON object contains all expected keys.
163
163
 
164
164
  Args:
165
- str_to_parse: The Markdown content to parse.
166
- expected_keys: An optional list of keys expected to be present in the parsed JSON object.
167
- parser: An optional function to parse the extracted code block. If not provided,
168
- `fuzzy_parse_json` is used with default settings.
165
+ str_to_parse: The Markdown content to parse.
166
+ expected_keys: An optional list of keys expected to be present in the parsed JSON object.
167
+ parser: An optional function to parse the extracted code block. If not provided,
168
+ `fuzzy_parse_json` is used with default settings.
169
169
 
170
170
  Returns:
171
- The parsed JSON object from the Markdown content.
171
+ The parsed JSON object from the Markdown content.
172
172
 
173
173
  Raises:
174
- ValueError: If the JSON code block is missing, or if any of the expected keys are missing
175
- from the parsed JSON object.
174
+ ValueError: If the JSON code block is missing, or if any of the expected keys are missing
175
+ from the parsed JSON object.
176
176
 
177
177
  Examples:
178
- >>> md_to_json('```json\\n{"key": "value"}\\n```', expected_keys=['key'])
179
- {'key': 'value'}
178
+ >>> md_to_json('```json\\n{"key": "value"}\\n```', expected_keys=['key'])
179
+ {'key': 'value'}
180
180
  """
181
181
  json_obj = ParseUtil.extract_code_block(
182
182
  str_to_parse, language="json", parser=parser or ParseUtil.fuzzy_parse_json
183
183
  )
184
184
 
185
185
  if expected_keys:
186
- missing_keys = [key for key in expected_keys if key not in json_obj]
187
- if missing_keys:
186
+ if missing_keys := [key for key in expected_keys if key not in json_obj]:
188
187
  raise ValueError(
189
188
  f"Missing expected keys in JSON object: {', '.join(missing_keys)}"
190
189
  )
@@ -198,26 +197,26 @@ class ParseUtil:
198
197
  docstring following the Google style format.
199
198
 
200
199
  Args:
201
- func (Callable): The function from which to extract docstring details.
200
+ func (Callable): The function from which to extract docstring details.
202
201
 
203
202
  Returns:
204
- Tuple[str, Dict[str, str]]: A tuple containing the function description
205
- and a dictionary with parameter names as keys and their descriptions as values.
203
+ Tuple[str, Dict[str, str]]: A tuple containing the function description
204
+ and a dictionary with parameter names as keys and their descriptions as values.
206
205
 
207
206
  Examples:
208
- >>> def example_function(param1: int, param2: str):
209
- ... '''Example function.
210
- ...
211
- ... Args:
212
- ... param1 (int): The first parameter.
213
- ... param2 (str): The second parameter.
214
- ... '''
215
- ... pass
216
- >>> description, params = _extract_docstring_details_google(example_function)
217
- >>> description
218
- 'Example function.'
219
- >>> params == {'param1': 'The first parameter.', 'param2': 'The second parameter.'}
220
- True
207
+ >>> def example_function(param1: int, param2: str):
208
+ ... '''Example function.
209
+ ...
210
+ ... Args:
211
+ ... param1 (int): The first parameter.
212
+ ... param2 (str): The second parameter.
213
+ ... '''
214
+ ... pass
215
+ >>> description, params = _extract_docstring_details_google(example_function)
216
+ >>> description
217
+ 'Example function.'
218
+ >>> params == {'param1': 'The first parameter.', 'param2': 'The second parameter.'}
219
+ True
221
220
  """
222
221
  docstring = inspect.getdoc(func)
223
222
  if not docstring:
@@ -225,19 +224,21 @@ class ParseUtil:
225
224
  lines = docstring.split("\n")
226
225
  func_description = lines[0].strip()
227
226
 
228
- param_start_pos = 0
229
227
  lines_len = len(lines)
230
228
 
231
229
  params_description = {}
232
- for i in range(1, lines_len):
233
- if (
234
- lines[i].startswith("Args")
235
- or lines[i].startswith("Arguments")
236
- or lines[i].startswith("Parameters")
237
- ):
238
- param_start_pos = i + 1
239
- break
240
-
230
+ param_start_pos = next(
231
+ (
232
+ i + 1
233
+ for i in range(1, lines_len)
234
+ if (
235
+ lines[i].startswith("Args")
236
+ or lines[i].startswith("Arguments")
237
+ or lines[i].startswith("Parameters")
238
+ )
239
+ ),
240
+ 0,
241
+ )
241
242
  current_param = None
242
243
  for i in range(param_start_pos, lines_len):
243
244
  if lines[i] == "":
@@ -245,7 +246,7 @@ class ParseUtil:
245
246
  elif lines[i].startswith(" "):
246
247
  param_desc = lines[i].split(":", 1)
247
248
  if len(param_desc) == 1:
248
- params_description[current_param] += " " + param_desc[0].strip()
249
+ params_description[current_param] += f" {param_desc[0].strip()}"
249
250
  continue
250
251
  param, desc = param_desc
251
252
  param = param.split("(")[0].strip()
@@ -262,27 +263,27 @@ class ParseUtil:
262
263
  docstring following the reStructuredText (reST) style format.
263
264
 
264
265
  Args:
265
- func (Callable): The function from which to extract docstring details.
266
+ func (Callable): The function from which to extract docstring details.
266
267
 
267
268
  Returns:
268
- Tuple[str, Dict[str, str]]: A tuple containing the function description
269
- and a dictionary with parameter names as keys and their descriptions as values.
269
+ Tuple[str, Dict[str, str]]: A tuple containing the function description
270
+ and a dictionary with parameter names as keys and their descriptions as values.
270
271
 
271
272
  Examples:
272
- >>> def example_function(param1: int, param2: str):
273
- ... '''Example function.
274
- ...
275
- ... :param param1: The first parameter.
276
- ... :type param1: int
277
- ... :param param2: The second parameter.
278
- ... :type param2: str
279
- ... '''
280
- ... pass
281
- >>> description, params = _extract_docstring_details_rest(example_function)
282
- >>> description
283
- 'Example function.'
284
- >>> params == {'param1': 'The first parameter.', 'param2': 'The second parameter.'}
285
- True
273
+ >>> def example_function(param1: int, param2: str):
274
+ ... '''Example function.
275
+ ...
276
+ ... :param param1: The first parameter.
277
+ ... :type param1: int
278
+ ... :param param2: The second parameter.
279
+ ... :type param2: str
280
+ ... '''
281
+ ... pass
282
+ >>> description, params = _extract_docstring_details_rest(example_function)
283
+ >>> description
284
+ 'Example function.'
285
+ >>> params == {'param1': 'The first parameter.', 'param2': 'The second parameter.'}
286
+ True
286
287
  """
287
288
  docstring = inspect.getdoc(func)
288
289
  if not docstring:
@@ -301,7 +302,7 @@ class ParseUtil:
301
302
  params_description[param] = desc.strip()
302
303
  current_param = param
303
304
  elif line.startswith(" "):
304
- params_description[current_param] += " " + line
305
+ params_description[current_param] += f" {line}"
305
306
 
306
307
  return func_description, params_description
307
308
 
@@ -313,30 +314,30 @@ class ParseUtil:
313
314
  (reST) style format.
314
315
 
315
316
  Args:
316
- func (Callable): The function from which to extract docstring details.
317
- style (str): The style of docstring to parse ('google' or 'reST').
317
+ func (Callable): The function from which to extract docstring details.
318
+ style (str): The style of docstring to parse ('google' or 'reST').
318
319
 
319
320
  Returns:
320
- Tuple[str, Dict[str, str]]: A tuple containing the function description
321
- and a dictionary with parameter names as keys and their descriptions as values.
321
+ Tuple[str, Dict[str, str]]: A tuple containing the function description
322
+ and a dictionary with parameter names as keys and their descriptions as values.
322
323
 
323
324
  Raises:
324
- ValueError: If an unsupported style is provided.
325
+ ValueError: If an unsupported style is provided.
325
326
 
326
327
  Examples:
327
- >>> def example_function(param1: int, param2: str):
328
- ... '''Example function.
329
- ...
330
- ... Args:
331
- ... param1 (int): The first parameter.
332
- ... param2 (str): The second parameter.
333
- ... '''
334
- ... pass
335
- >>> description, params = _extract_docstring_details(example_function, style='google')
336
- >>> description
337
- 'Example function.'
338
- >>> params == {'param1': 'The first parameter.', 'param2': 'The second parameter.'}
339
- True
328
+ >>> def example_function(param1: int, param2: str):
329
+ ... '''Example function.
330
+ ...
331
+ ... Args:
332
+ ... param1 (int): The first parameter.
333
+ ... param2 (str): The second parameter.
334
+ ... '''
335
+ ... pass
336
+ >>> description, params = _extract_docstring_details(example_function, style='google')
337
+ >>> description
338
+ 'Example function.'
339
+ >>> params == {'param1': 'The first parameter.', 'param2': 'The second parameter.'}
340
+ True
340
341
  """
341
342
  if style == "google":
342
343
  func_description, params_description = (
@@ -358,16 +359,16 @@ class ParseUtil:
358
359
  Converts a Python type to its JSON type equivalent.
359
360
 
360
361
  Args:
361
- py_type (str): The name of the Python type.
362
+ py_type (str): The name of the Python type.
362
363
 
363
364
  Returns:
364
- str: The corresponding JSON type.
365
+ str: The corresponding JSON type.
365
366
 
366
367
  Examples:
367
- >>> _python_to_json_type('str')
368
- 'string'
369
- >>> _python_to_json_type('int')
370
- 'number'
368
+ >>> _python_to_json_type('str')
369
+ 'string'
370
+ >>> _python_to_json_type('int')
371
+ 'number'
371
372
  """
372
373
  type_mapping = {
373
374
  "str": "string",
@@ -387,24 +388,24 @@ class ParseUtil:
387
388
  docstrings. The schema includes the function's name, description, and parameters.
388
389
 
389
390
  Args:
390
- func (Callable): The function to generate a schema for.
391
- style (str): The docstring format ('google' or 'reST').
391
+ func (Callable): The function to generate a schema for.
392
+ style (str): The docstring format ('google' or 'reST').
392
393
 
393
394
  Returns:
394
- Dict[str, Any]: A schema describing the function.
395
+ Dict[str, Any]: A schema describing the function.
395
396
 
396
397
  Examples:
397
- >>> def example_function(param1: int, param2: str) -> bool:
398
- ... '''Example function.
399
- ...
400
- ... Args:
401
- ... param1 (int): The first parameter.
402
- ... param2 (str): The second parameter.
403
- ... '''
404
- ... return True
405
- >>> schema = _func_to_schema(example_function)
406
- >>> schema['function']['name']
407
- 'example_function'
398
+ >>> def example_function(param1: int, param2: str) -> bool:
399
+ ... '''Example function.
400
+ ...
401
+ ... Args:
402
+ ... param1 (int): The first parameter.
403
+ ... param2 (str): The second parameter.
404
+ ... '''
405
+ ... return True
406
+ >>> schema = _func_to_schema(example_function)
407
+ >>> schema['function']['name']
408
+ 'example_function'
408
409
  """
409
410
  # Extracting function name and docstring details
410
411
  func_name = func.__name__
@@ -438,8 +439,7 @@ class ParseUtil:
438
439
  "description": param_description,
439
440
  }
440
441
 
441
- # Constructing the schema
442
- schema = {
442
+ return {
443
443
  "type": "function",
444
444
  "function": {
445
445
  "name": func_name,
@@ -448,8 +448,6 @@ class ParseUtil:
448
448
  },
449
449
  }
450
450
 
451
- return schema
452
-
453
451
 
454
452
  class StringMatch:
455
453
 
@@ -463,16 +461,16 @@ class StringMatch:
463
461
  and 1 is an exact match.
464
462
 
465
463
  Args:
466
- s: The first string to compare.
467
- t: The second string to compare.
464
+ s: The first string to compare.
465
+ t: The second string to compare.
468
466
 
469
467
  Returns:
470
- A float representing the Jaro distance between the two strings, ranging from 0 to 1,
471
- where 1 means the strings are identical.
468
+ A float representing the Jaro distance between the two strings, ranging from 0 to 1,
469
+ where 1 means the strings are identical.
472
470
 
473
471
  Examples:
474
- >>> jaro_distance("martha", "marhta")
475
- 0.9444444444444445
472
+ >>> jaro_distance("martha", "marhta")
473
+ 0.9444444444444445
476
474
  """
477
475
  s_len = len(s)
478
476
  t_len = len(t)
@@ -527,18 +525,18 @@ class StringMatch:
527
525
  person names, and is designed to improve the scoring of strings that have a common prefix.
528
526
 
529
527
  Args:
530
- s: The first string to compare.
531
- t: The second string to compare.
532
- scaling: The scaling factor for how much the score is adjusted upwards for having common prefixes.
533
- The scaling factor should be less than 1, and a typical value is 0.1.
528
+ s: The first string to compare.
529
+ t: The second string to compare.
530
+ scaling: The scaling factor for how much the score is adjusted upwards for having common prefixes.
531
+ The scaling factor should be less than 1, and a typical value is 0.1.
534
532
 
535
533
  Returns:
536
- A float representing the Jaro-Winkler similarity between the two strings, ranging from 0 to 1,
537
- where 1 means the strings are identical.
534
+ A float representing the Jaro-Winkler similarity between the two strings, ranging from 0 to 1,
535
+ where 1 means the strings are identical.
538
536
 
539
537
  Examples:
540
- >>> jaro_winkler_similarity("dixon", "dicksonx")
541
- 0.8133333333333332
538
+ >>> jaro_winkler_similarity("dixon", "dicksonx")
539
+ 0.8133333333333332
542
540
  """
543
541
  jaro_sim = StringMatch.jaro_distance(s, t)
544
542
  prefix_len = 0
@@ -561,15 +559,15 @@ class StringMatch:
561
559
  required to change one word into the other. Each operation has an equal cost.
562
560
 
563
561
  Args:
564
- a: The first string to compare.
565
- b: The second string to compare.
562
+ a: The first string to compare.
563
+ b: The second string to compare.
566
564
 
567
565
  Returns:
568
- An integer representing the Levenshtein distance between the two strings.
566
+ An integer representing the Levenshtein distance between the two strings.
569
567
 
570
568
  Examples:
571
- >>> levenshtein_distance("kitten", "sitting")
572
- 3
569
+ >>> levenshtein_distance("kitten", "sitting")
570
+ 3
573
571
  """
574
572
  m, n = len(a), len(b)
575
573
  # Initialize 2D array (m+1) x (n+1)
@@ -582,17 +580,13 @@ class StringMatch:
582
580
  d[0][j] = j
583
581
 
584
582
  # Compute the distance
585
- for i in range(1, m + 1):
586
- for j in range(1, n + 1):
587
- if a[i - 1] == b[j - 1]:
588
- cost = 0
589
- else:
590
- cost = 1
591
- d[i][j] = min(
592
- d[i - 1][j] + 1, # deletion
593
- d[i][j - 1] + 1, # insertion
594
- d[i - 1][j - 1] + cost,
595
- ) # substitution
583
+ for i, j in itertools.product(range(1, m + 1), range(1, n + 1)):
584
+ cost = 0 if a[i - 1] == b[j - 1] else 1
585
+ d[i][j] = min(
586
+ d[i - 1][j] + 1, # deletion
587
+ d[i][j - 1] + 1, # insertion
588
+ d[i - 1][j - 1] + cost,
589
+ ) # substitution
596
590
  return d[m][n]
597
591
 
598
592
  @staticmethod
@@ -632,12 +626,14 @@ class StringMatch:
632
626
 
633
627
  if score_func is None:
634
628
  score_func = StringMatch.jaro_winkler_similarity
635
-
629
+
636
630
  # Calculate Jaro-Winkler similarity scores for each potential match
637
- scores = np.array([score_func(convert.to_str(word), correct_word) for correct_word in correct_words_list])
631
+ scores = np.array(
632
+ [
633
+ score_func(convert.to_str(word), correct_word)
634
+ for correct_word in correct_words_list
635
+ ]
636
+ )
638
637
  # Find the index of the highest score
639
638
  max_score_index = np.argmax(scores)
640
- # Select the best match based on the highest score
641
- best_match = correct_words_list[max_score_index]
642
-
643
- return best_match
639
+ return correct_words_list[max_score_index]