lionagi 0.0.306__py3-none-any.whl → 0.0.307__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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 +35 -38
  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.307.dist-info}/METADATA +4 -2
  72. lionagi-0.0.307.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.307.dist-info}/LICENSE +0 -0
  77. {lionagi-0.0.306.dist-info → lionagi-0.0.307.dist-info}/WHEEL +0 -0
  78. {lionagi-0.0.306.dist-info → lionagi-0.0.307.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]