ai-microcore 4.0.0.dev4__tar.gz → 4.0.0.dev6__tar.gz

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 (41) hide show
  1. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/PKG-INFO +1 -1
  2. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/__init__.py +1 -1
  3. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/_env.py +3 -2
  4. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/_llm_functions.py +67 -6
  5. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/text2speech/elevenlabs.py +31 -1
  6. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/types.py +3 -2
  7. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/wrappers/llm_response_wrapper.py +20 -2
  8. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/LICENSE +0 -0
  9. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/README.md +0 -0
  10. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/_prepare_llm_args.py +0 -0
  11. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/ai_func/__init__.py +0 -0
  12. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/ai_func/ai-func.json.j2 +0 -0
  13. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/ai_func/ai-func.pythonic.j2 +0 -0
  14. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/ai_modules.py +0 -0
  15. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/configuration.py +0 -0
  16. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/embedding_db/__init__.py +0 -0
  17. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/embedding_db/chromadb.py +0 -0
  18. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/file_storage.py +0 -0
  19. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/json_parsing.py +0 -0
  20. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/llm/__init__.py +0 -0
  21. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/llm/_openai_llm_v0.py +0 -0
  22. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/llm/_openai_llm_v1.py +0 -0
  23. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/llm/anthropic.py +0 -0
  24. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/llm/google_genai.py +0 -0
  25. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/llm/google_vertex_ai.py +0 -0
  26. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/llm/local_llm.py +0 -0
  27. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/llm/local_transformers.py +0 -0
  28. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/llm/openai_llm.py +0 -0
  29. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/llm/shared.py +0 -0
  30. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/logging.py +0 -0
  31. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/message_types.py +0 -0
  32. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/metrics.py +0 -0
  33. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/python.py +0 -0
  34. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/templating/__init__.py +0 -0
  35. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/templating/jinja2.py +0 -0
  36. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/tokenizing.py +0 -0
  37. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/ui.py +0 -0
  38. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/utils.py +0 -0
  39. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/wrappers/__init__.py +0 -0
  40. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/microcore/wrappers/prompt_wrapper.py +0 -0
  41. {ai_microcore-4.0.0.dev4 → ai_microcore-4.0.0.dev6}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ai-microcore
3
- Version: 4.0.0.dev4
3
+ Version: 4.0.0.dev6
4
4
  Summary: # Minimalistic Foundation for AI Applications
5
5
  Keywords: llm,large language models,ai,similarity search,ai search,gpt,openai
6
6
  Author-email: Vitalii Stepanenko <mail@vitalii.in>
@@ -161,4 +161,4 @@ __all__ = [
161
161
  # "wrappers",
162
162
  ]
163
163
 
164
- __version__ = "4.0.0-dev4"
164
+ __version__ = "4.0.0-dev6"
@@ -14,6 +14,7 @@ from .llm.local_llm import make_llm_functions as make_local_llm_functions
14
14
 
15
15
  if TYPE_CHECKING:
16
16
  from .wrappers.llm_response_wrapper import LLMResponse # noqa: F401
17
+ from transformers import PreTrainedModel, PreTrainedTokenizer # noqa: F401
17
18
 
18
19
 
19
20
  @dataclass
@@ -26,10 +27,10 @@ class Env:
26
27
  llm_before_handlers: list[callable] = field(default_factory=list)
27
28
  llm_after_handlers: list[callable] = field(default_factory=list)
28
29
  texts: AbstractEmbeddingDB = None
29
- model: "transformers.PreTrainedModel" = field(
30
+ model: "PreTrainedModel" = field(
30
31
  default=None, init=False, repr=False
31
32
  ) # noqa
32
- tokenizer: "transformers.PreTrainedTokenizer" = field( # noqa
33
+ tokenizer: "PreTrainedTokenizer" = field( # noqa
33
34
  default=None, init=False, repr=False
34
35
  )
35
36
 
@@ -1,17 +1,27 @@
1
+ import logging
1
2
  from datetime import datetime
2
3
 
3
4
  from .utils import run_parallel
4
- from .wrappers.llm_response_wrapper import LLMResponse
5
+ from .wrappers.llm_response_wrapper import LLMResponse, DictFromLLMResponse
5
6
  from .types import TPrompt
6
7
  from ._env import env
7
8
 
8
9
 
9
- def llm(prompt: TPrompt, **kwargs) -> str | LLMResponse:
10
+ def llm(
11
+ prompt: TPrompt,
12
+ retries: int = 0,
13
+ parse_json: bool | dict = False,
14
+ **kwargs
15
+ ) -> str | LLMResponse:
10
16
  """
11
17
  Request Large Language Model synchronously
12
18
 
13
19
  Args:
14
20
  prompt (str | Msg | dict | list[str | Msg | dict]): Text to send to LLM
21
+ retries (int): Number of retries in case of error
22
+ parse_json (bool|dict):
23
+ If True, parses response as JSON,
24
+ alternatively non-empty dict can be used as parse_json arguments
15
25
  **kwargs (dict): Parameters supported by the LLM API
16
26
 
17
27
  See parameters supported by the OpenAI:
@@ -40,7 +50,18 @@ def llm(prompt: TPrompt, **kwargs) -> str | LLMResponse:
40
50
  """
41
51
  [h(prompt, **kwargs) for h in env().llm_before_handlers]
42
52
  start = datetime.now()
43
- response = env().llm_function(prompt, **kwargs)
53
+ tries = retries + 1
54
+ while tries > 0:
55
+ try:
56
+ tries -= 1
57
+ response = env().llm_function(prompt, **kwargs)
58
+ break
59
+ except Exception as e: # pylint: disable=W0718
60
+ if tries == 0:
61
+ raise e
62
+ logging.error(f"LLM error: {e}")
63
+ logging.info(f"Retrying... {tries} retries left")
64
+ continue
44
65
  try:
45
66
  response.gen_duration = (datetime.now() - start).total_seconds()
46
67
  if not env().config.SAVE_MEMORY:
@@ -48,15 +69,35 @@ def llm(prompt: TPrompt, **kwargs) -> str | LLMResponse:
48
69
  except AttributeError:
49
70
  ...
50
71
  [h(response) for h in env().llm_after_handlers]
72
+ if tries > 0:
73
+ retry_params = dict(**kwargs)
74
+ retry_params["retries"] = tries - 1
75
+ setattr(
76
+ response,
77
+ "_retry_callback",
78
+ lambda: llm(prompt, **retry_params)
79
+ )
80
+ if parse_json:
81
+ parsing_params = parse_json if isinstance(parse_json, dict) else {}
82
+ return response.parse_json(**parsing_params)
51
83
  return response
52
84
 
53
85
 
54
- async def allm(prompt: TPrompt, **kwargs) -> str | LLMResponse:
86
+ async def allm(
87
+ prompt: TPrompt,
88
+ retries: int = 0,
89
+ parse_json: bool | dict = False,
90
+ **kwargs
91
+ ) -> str | LLMResponse | DictFromLLMResponse:
55
92
  """
56
93
  Request Large Language Model asynchronously
57
94
 
58
95
  Args:
59
96
  prompt (str | Msg | dict | list[str | Msg | dict]): Text to send to LLM
97
+ retries (int): Number of retries in case of error
98
+ parse_json (bool|dict):
99
+ If True, parses response as JSON,
100
+ alternatively non-empty dict can be used as parse_json arguments
60
101
  **kwargs (dict): Parameters supported by the LLM API
61
102
 
62
103
  See parameters supported by the OpenAI:
@@ -87,7 +128,18 @@ async def allm(prompt: TPrompt, **kwargs) -> str | LLMResponse:
87
128
  """
88
129
  [h(prompt, **kwargs) for h in env().llm_before_handlers]
89
130
  start = datetime.now()
90
- response = await env().llm_async_function(prompt, **kwargs)
131
+ tries = retries + 1
132
+ while tries > 0:
133
+ try:
134
+ tries -= 1
135
+ response = await env().llm_async_function(prompt, **kwargs)
136
+ break
137
+ except Exception as e: # pylint: disable=W0718
138
+ if tries == 0:
139
+ raise e
140
+ logging.error(f"LLM error: {e}")
141
+ logging.info(f"Retrying... {tries} retries left")
142
+ continue
91
143
  try:
92
144
  response.gen_duration = (datetime.now() - start).total_seconds()
93
145
  if not env().config.SAVE_MEMORY:
@@ -95,6 +147,15 @@ async def allm(prompt: TPrompt, **kwargs) -> str | LLMResponse:
95
147
  except AttributeError:
96
148
  ...
97
149
  [h(response) for h in env().llm_after_handlers]
150
+ if parse_json:
151
+ try:
152
+ parsing_params = parse_json if isinstance(parse_json, dict) else {}
153
+ return response.parse_json(**parsing_params)
154
+ except Exception as e: # pylint: disable=W0718
155
+ if tries > 0:
156
+ logging.error(f"LLM error: {e}")
157
+ logging.info(f"Retrying... {tries} retries left")
158
+ return await allm(prompt, retries=tries - 1, parse_json=parse_json, **kwargs)
98
159
  return response
99
160
 
100
161
 
@@ -109,7 +170,7 @@ async def llm_parallel(
109
170
  tasks = [allm(prompt, **kwargs) for prompt in prompts]
110
171
 
111
172
  if max_concurrent_tasks is None:
112
- max_concurrent_tasks = int(env().config.MAX_CONCURRENT_TASKS)
173
+ max_concurrent_tasks = int(env().config.MAX_CONCURRENT_TASKS or 0)
113
174
  if not max_concurrent_tasks:
114
175
  max_concurrent_tasks = len(tasks)
115
176
 
@@ -1,10 +1,29 @@
1
1
  import os
2
+ from dataclasses import dataclass, asdict
2
3
  from datetime import datetime
3
4
  import aiohttp
4
5
  from .._env import env
5
6
 
6
7
 
7
- async def text_to_speech(
8
+ @dataclass
9
+ class TTSArgs:
10
+ text: str
11
+ out_file: str = None
12
+ voice: str = "D38z5RcWu1voky8WS1ja"
13
+ stability: float = 0.29
14
+ similarity_boost: float = 0.5
15
+ style: float = 0.0
16
+ chunk_size: int = 1024
17
+ speed: float = 1.0
18
+ use_speaker_boost: bool = False
19
+ previous_text: str = None
20
+ next_text: str = None
21
+
22
+ def to_dict(self) -> dict:
23
+ return asdict(self)
24
+
25
+
26
+ async def text_to_speech( # pylint: disable=R0914
8
27
  text: str,
9
28
  out_file: str = None,
10
29
  voice: str = "D38z5RcWu1voky8WS1ja",
@@ -12,6 +31,10 @@ async def text_to_speech(
12
31
  similarity_boost=0.5,
13
32
  style=0.0,
14
33
  chunk_size=1024,
34
+ speed=1.0,
35
+ use_speaker_boost: bool = False,
36
+ previous_text: str = None,
37
+ next_text: str = None,
15
38
  ) -> str:
16
39
  url = f"https://api.elevenlabs.io/v1/text-to-speech/{voice}"
17
40
  if not out_file:
@@ -25,8 +48,15 @@ async def text_to_speech(
25
48
  "stability": stability,
26
49
  "similarity_boost": similarity_boost,
27
50
  "style": style,
51
+ "speed": speed,
28
52
  },
29
53
  }
54
+ if use_speaker_boost:
55
+ data["voice_settings"]["use_speaker_boost"] = use_speaker_boost
56
+ if previous_text:
57
+ data["previous_text"] = previous_text
58
+ if next_text:
59
+ data["next_text"] = next_text
30
60
  headers = {
31
61
  "Accept": "audio/mpeg",
32
62
  "Content-Type": "application/json",
@@ -5,6 +5,7 @@ from .message_types import Msg
5
5
 
6
6
  if TYPE_CHECKING:
7
7
  from .wrappers.prompt_wrapper import PromptWrapper # noqa: F401
8
+ from .wrappers.llm_response_wrapper import LLMResponse # noqa: F401
8
9
 
9
10
  TPrompt = Union[
10
11
  dict, Msg, str, "PromptWrapper", List[Union[dict, Msg, str, "PromptWrapper"]]
@@ -12,9 +13,9 @@ TPrompt = Union[
12
13
  """Type for prompt argument in LLM requests"""
13
14
  TplFunctionType = Callable[[Union[PathLike[str], str], Any], str]
14
15
  """Function type for rendering prompt templates"""
15
- LLMFunctionType = Callable[[TPrompt, Any], str]
16
+ LLMFunctionType = Callable[[TPrompt, Any], "LLMResponse"]
16
17
  """Function type for requesting LLM synchronously"""
17
- LLMAsyncFunctionType = Callable[[TPrompt, Any], Awaitable[str]]
18
+ LLMAsyncFunctionType = Callable[[TPrompt, Any], Awaitable["LLMResponse"]]
18
19
  """Function type for requesting LLM asynchronously"""
19
20
 
20
21
 
@@ -46,9 +46,27 @@ class LLMResponse(ExtendedString, ConvertableToMessage):
46
46
  return obj
47
47
 
48
48
  def parse_json(
49
- self, raise_errors: bool = True, required_fields: list[str] = None
49
+ self,
50
+ raise_errors: bool = True,
51
+ required_fields: list[str] = None,
52
+ validator: callable = None,
50
53
  ) -> list | dict | float | int | str | DictFromLLMResponse:
51
- res = parse_json(self.content, raise_errors, required_fields)
54
+ try:
55
+ res = parse_json(self.content, True, required_fields)
56
+ if validator:
57
+ try:
58
+ validator(res)
59
+ except Exception as e:
60
+ raise BadAIAnswer(f"Language model response validation failed: {e}") from None
61
+ except Exception as e: # pylint: disable=W0718
62
+ if hasattr(self, "_retry_callback"):
63
+ res = self._retry_callback()
64
+ if isinstance(res, DictFromLLMResponse):
65
+ return res
66
+ return res.parse_json(raise_errors, required_fields, validator)
67
+ if raise_errors:
68
+ raise e
69
+ res = False
52
70
  if isinstance(res, dict):
53
71
  res = DictFromLLMResponse(res)
54
72
  res.llm_response = self