ai-microcore 5.0.0.dev8__tar.gz → 5.1.0__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 (47) hide show
  1. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/PKG-INFO +58 -13
  2. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/README.md +57 -12
  3. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/__init__.py +1 -1
  4. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/configuration.py +5 -12
  5. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/llm/anthropic.py +11 -9
  6. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/llm/google_genai.py +29 -2
  7. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/llm/openai.py +22 -1
  8. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/llm_backends.py +1 -0
  9. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/LICENSE +0 -0
  10. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/_env.py +0 -0
  11. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/_llm_functions.py +0 -0
  12. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/_prepare_llm_args.py +0 -0
  13. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/ai_func/__init__.py +0 -0
  14. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/ai_func/ai-func.json.j2 +0 -0
  15. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/ai_func/ai-func.pythonic.j2 +0 -0
  16. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/ai_func/ai-func.tag.j2 +0 -0
  17. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/ai_modules.py +0 -0
  18. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/embedding_db/__init__.py +0 -0
  19. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/embedding_db/chromadb.py +0 -0
  20. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/embedding_db/qdrant.py +0 -0
  21. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/file_cache.py +0 -0
  22. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/file_storage.py +0 -0
  23. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/images.py +0 -0
  24. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/interactive_setup.py +0 -0
  25. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/json_parsing.py +0 -0
  26. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/llm/__init__.py +0 -0
  27. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/llm/local_llm.py +0 -0
  28. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/llm/local_transformers.py +0 -0
  29. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/llm/shared.py +0 -0
  30. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/lm_client.py +0 -0
  31. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/logging.py +0 -0
  32. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/mcp.py +0 -0
  33. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/message_types.py +0 -0
  34. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/metrics.py +0 -0
  35. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/presets.py +0 -0
  36. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/python.py +0 -0
  37. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/templating/__init__.py +0 -0
  38. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/templating/jinja2.py +0 -0
  39. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/text2speech/elevenlabs.py +0 -0
  40. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/tokenizing.py +0 -0
  41. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/types.py +0 -0
  42. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/ui.py +0 -0
  43. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/utils.py +0 -0
  44. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/wrappers/__init__.py +0 -0
  45. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/wrappers/llm_response_wrapper.py +0 -0
  46. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/microcore/wrappers/prompt_wrapper.py +0 -0
  47. {ai_microcore-5.0.0.dev8 → ai_microcore-5.1.0}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ai-microcore
3
- Version: 5.0.0.dev8
3
+ Version: 5.1.0
4
4
  Summary: # Minimalistic Foundation for AI Applications
5
5
  Keywords: llm,large language models,ai,similarity search,ai search,gpt,openai,framework,adapter,anthropic,google gemini,google vertex ai
6
6
  Author-email: Vitalii Stepanenko <mail@vitaliy.in>
@@ -116,21 +116,14 @@ For the full list of available configuration options, you may also check [`micro
116
116
 
117
117
  ### Installing vendor-specific packages
118
118
  For models working not via OpenAI API, you may need to install additional packages:
119
- #### Anthropic Claude 3
119
+ #### Anthropic Claude
120
120
  ```bash
121
121
  pip install anthropic
122
122
  ```
123
- #### Google Gemini via AI Studio
123
+ #### Google Gemini via AI Studio or Vertex AI
124
124
  ```bash
125
- pip install google-generativeai
125
+ pip install google-genai
126
126
  ```
127
- #### Google Gemini via Vertex AI
128
- ```bash
129
- pip install vertexai
130
- ```
131
- 📌Additionally for working through [Vertex AI](https://cloud.google.com/vertex-ai) you need to
132
- [install the Google Cloud CLI](https://cloud.google.com/sdk/docs/install)
133
- and [configure the authorization](https://cloud.google.com/sdk/docs/authorizing).
134
127
 
135
128
  #### Local language models via Hugging Face Transformers
136
129
 
@@ -207,7 +200,7 @@ ai_response = llm('What is your model name?')
207
200
  # - For chat completion models elements are treated as separate messages
208
201
  # - For completion LLMs elements are treated as text lines
209
202
  llm(['1+2', '='])
210
- llm('1+2=', model='gpt-4')
203
+ llm('1+2=', model='gpt-5.2')
211
204
 
212
205
  # To specify a message role, you can use dictionary or classes
213
206
  llm(dict(role='system', content='1+2='))
@@ -313,9 +306,61 @@ Text generation using HF/Transformers model locally (example with Qwen 3 0.6B).
313
306
 
314
307
  #### [Other examples](https://github.com/llm-microcore/microcore/tree/main/examples)
315
308
 
309
+ ## 📚 Guides & Reference<a id="-guides--reference"></a>
310
+
311
+ For more detailed information, check out these articles:
312
+
313
+ - [Custom HTTP Headers](https://github.com/Nayjest/ai-microcore/blob/main/doc/features/http_headers.md)
314
+ - [File Cache](https://github.com/Nayjest/ai-microcore/blob/main/doc/features/file_cache.md)
315
+
316
316
  ## Python functions as AI tools
317
+ *Usage Example*:
318
+ ```python
319
+ from microcore.ai_func import ai_func
320
+
321
+ @ai_func
322
+ def search_products(
323
+ query: str,
324
+ category: str = "all",
325
+ max_results: int = 10,
326
+ in_stock_only: bool = False
327
+ ):
328
+ """
329
+ Search for products in the catalog.
330
+
331
+ Args:
332
+ query: Search terms to find matching products
333
+ category: Product category to filter by (e.g., "electronics", "clothing")
334
+ max_results: Maximum number of results to return
335
+ in_stock_only: If True, only return products currently in stock
336
+
337
+ Returns:
338
+ List of matching products with name, price, and availability
339
+ """
340
+ # Implementation would go here
341
+ pass
342
+ ```
343
+ *Output*:
344
+ ```
345
+ # Search for products in the catalog.
346
+
347
+ Args:
348
+ query: Search terms to find matching products
349
+ category: Product category to filter by (e.g., "electronics", "clothing")
350
+ max_results: Maximum number of results to return
351
+ in_stock_only: If True, only return products currently in stock
352
+
353
+ Returns:
354
+ List of matching products with name, price, and availability
355
+ {
356
+ "call": "search_products",
357
+ "query": <str>,
358
+ "category": <str> (default = "all"),
359
+ "max_results": <int> (default = 10),
360
+ "in_stock_only": <bool> (default = False)
361
+ }
317
362
 
318
- @TODO
363
+ ```
319
364
 
320
365
  ## 🤖 AI Modules
321
366
  **This is an experimental feature.**
@@ -81,21 +81,14 @@ For the full list of available configuration options, you may also check [`micro
81
81
 
82
82
  ### Installing vendor-specific packages
83
83
  For models working not via OpenAI API, you may need to install additional packages:
84
- #### Anthropic Claude 3
84
+ #### Anthropic Claude
85
85
  ```bash
86
86
  pip install anthropic
87
87
  ```
88
- #### Google Gemini via AI Studio
88
+ #### Google Gemini via AI Studio or Vertex AI
89
89
  ```bash
90
- pip install google-generativeai
90
+ pip install google-genai
91
91
  ```
92
- #### Google Gemini via Vertex AI
93
- ```bash
94
- pip install vertexai
95
- ```
96
- 📌Additionally for working through [Vertex AI](https://cloud.google.com/vertex-ai) you need to
97
- [install the Google Cloud CLI](https://cloud.google.com/sdk/docs/install)
98
- and [configure the authorization](https://cloud.google.com/sdk/docs/authorizing).
99
92
 
100
93
  #### Local language models via Hugging Face Transformers
101
94
 
@@ -172,7 +165,7 @@ ai_response = llm('What is your model name?')
172
165
  # - For chat completion models elements are treated as separate messages
173
166
  # - For completion LLMs elements are treated as text lines
174
167
  llm(['1+2', '='])
175
- llm('1+2=', model='gpt-4')
168
+ llm('1+2=', model='gpt-5.2')
176
169
 
177
170
  # To specify a message role, you can use dictionary or classes
178
171
  llm(dict(role='system', content='1+2='))
@@ -278,9 +271,61 @@ Text generation using HF/Transformers model locally (example with Qwen 3 0.6B).
278
271
 
279
272
  #### [Other examples](https://github.com/llm-microcore/microcore/tree/main/examples)
280
273
 
274
+ ## 📚 Guides & Reference<a id="-guides--reference"></a>
275
+
276
+ For more detailed information, check out these articles:
277
+
278
+ - [Custom HTTP Headers](https://github.com/Nayjest/ai-microcore/blob/main/doc/features/http_headers.md)
279
+ - [File Cache](https://github.com/Nayjest/ai-microcore/blob/main/doc/features/file_cache.md)
280
+
281
281
  ## Python functions as AI tools
282
+ *Usage Example*:
283
+ ```python
284
+ from microcore.ai_func import ai_func
285
+
286
+ @ai_func
287
+ def search_products(
288
+ query: str,
289
+ category: str = "all",
290
+ max_results: int = 10,
291
+ in_stock_only: bool = False
292
+ ):
293
+ """
294
+ Search for products in the catalog.
295
+
296
+ Args:
297
+ query: Search terms to find matching products
298
+ category: Product category to filter by (e.g., "electronics", "clothing")
299
+ max_results: Maximum number of results to return
300
+ in_stock_only: If True, only return products currently in stock
301
+
302
+ Returns:
303
+ List of matching products with name, price, and availability
304
+ """
305
+ # Implementation would go here
306
+ pass
307
+ ```
308
+ *Output*:
309
+ ```
310
+ # Search for products in the catalog.
311
+
312
+ Args:
313
+ query: Search terms to find matching products
314
+ category: Product category to filter by (e.g., "electronics", "clothing")
315
+ max_results: Maximum number of results to return
316
+ in_stock_only: If True, only return products currently in stock
317
+
318
+ Returns:
319
+ List of matching products with name, price, and availability
320
+ {
321
+ "call": "search_products",
322
+ "query": <str>,
323
+ "category": <str> (default = "all"),
324
+ "max_results": <int> (default = 10),
325
+ "in_stock_only": <bool> (default = False)
326
+ }
282
327
 
283
- @TODO
328
+ ```
284
329
 
285
330
  ## 🤖 AI Modules
286
331
  **This is an experimental feature.**
@@ -231,4 +231,4 @@ __all__ = [
231
231
  # "wrappers",
232
232
  ]
233
233
 
234
- __version__ = "5.0.0dev8"
234
+ __version__ = "5.1.0"
@@ -150,18 +150,6 @@ class _AnthropicEnvVars:
150
150
  ANTHROPIC_API_KEY: str = from_env()
151
151
 
152
152
 
153
- @dataclass
154
- class _GoogleVertexAiEnvVars:
155
- """@deprecated, use _GoogleGenAiEnvVars instead"""
156
- GOOGLE_VERTEX_ACCESS_TOKEN: str = from_env()
157
- GOOGLE_VERTEX_PROJECT_ID: str = from_env()
158
- GOOGLE_VERTEX_LOCATION: str = from_env()
159
- GOOGLE_VERTEX_GCLOUD_AUTH: bool = from_env(dtype=bool)
160
-
161
- GOOGLE_VERTEX_RESPONSE_VALIDATION: bool = from_env(dtype=bool, default=False)
162
- GOOGLE_GEMINI_SAFETY_SETTINGS: dict = from_env(dtype=dict)
163
-
164
-
165
153
  @dataclass
166
154
  class _GoogleGenAiEnvVars:
167
155
  # see https://docs.cloud.google.com/docs/authentication/application-default-credentials
@@ -216,6 +204,9 @@ class LLMConfig(
216
204
  i. e. temperature, max_tokens, etc.
217
205
  """
218
206
 
207
+ HTTP_HEADERS: dict = from_env(dtype=dict)
208
+ """Additional HTTP headers to add to LLM API requests"""
209
+
219
210
  AZURE_DEPLOYMENT_ID: str = from_env()
220
211
 
221
212
  INFERENCE_FUNC: Union[Callable, str] = from_env()
@@ -344,6 +335,8 @@ class LLMConfig(
344
335
  self._resolve_model()
345
336
 
346
337
  def _validate_local_llm(self):
338
+ if self.HTTP_HEADERS:
339
+ logging.warning("HTTP_HEADERS will be ignored for local models")
347
340
  if self.CHAT_MODE is None:
348
341
  logging.warning(
349
342
  "When using local models, "
@@ -96,16 +96,18 @@ def _extract_sys_msg(prepared_messages: list[dict]) -> tuple[str, list[dict]]:
96
96
 
97
97
 
98
98
  def make_llm_functions(config: Config) -> tuple[LLMFunctionType, LLMAsyncFunctionType]:
99
- sync_client = anthropic.Anthropic(
100
- api_key=config.LLM_API_KEY,
101
- base_url=config.LLM_API_BASE,
99
+ client_params = {
100
+ "api_key": config.LLM_API_KEY,
101
+ "base_url": config.LLM_API_BASE,
102
102
  **config.INIT_PARAMS,
103
- )
104
- async_client = anthropic.AsyncAnthropic(
105
- api_key=config.LLM_API_KEY,
106
- base_url=config.LLM_API_BASE,
107
- **config.INIT_PARAMS,
108
- )
103
+ }
104
+ if config.HTTP_HEADERS:
105
+ if "default_headers" not in client_params:
106
+ client_params["default_headers"] = {}
107
+ client_params["default_headers"].update(config.HTTP_HEADERS)
108
+
109
+ sync_client = anthropic.Anthropic(**client_params)
110
+ async_client = anthropic.AsyncAnthropic(**client_params)
109
111
 
110
112
  async def allm(prompt, **kwargs):
111
113
  args, options = _prepare_llm_arguments(config, kwargs)
@@ -1,10 +1,14 @@
1
+ """
2
+ Client for Google GenAI SDK to interact with Google Gemini models.
3
+ """
1
4
  import asyncio
2
5
  import json
3
- from typing import Any, Optional
6
+ from typing import Any, Optional, Mapping
4
7
  from dataclasses import dataclass
5
8
 
6
9
  from google import genai
7
10
  from google.genai import types
11
+ from google.genai.types import HttpOptions
8
12
  from google.oauth2.service_account import Credentials
9
13
 
10
14
  from ..configuration import Config, LLMCredentialError
@@ -59,6 +63,22 @@ def _load_service_account_info(config: Config) -> Optional[dict]:
59
63
  )
60
64
  return None
61
65
 
66
+ def inject_headers(headers: Mapping[str,str], params: dict) -> None:
67
+ """
68
+ Inject extra HTTP headers into the params dictionary for Google GenAI client.
69
+ """
70
+ if "http_options" not in params:
71
+ params["http_options"] = {}
72
+ http_options = params["http_options"]
73
+ if isinstance(http_options, dict):
74
+ if "headers" not in http_options:
75
+ http_options["headers"] = {}
76
+ http_options["headers"].update(headers)
77
+ elif isinstance(http_options, HttpOptions):
78
+ if http_options.headers is None:
79
+ http_options.headers = {}
80
+ http_options.headers.update(headers)
81
+
62
82
 
63
83
  class GoogleClient(BaseAIChatClient):
64
84
  """
@@ -71,6 +91,9 @@ class GoogleClient(BaseAIChatClient):
71
91
  super().__init__(config)
72
92
  client_params = {**config.INIT_PARAMS}
73
93
 
94
+ if config.HTTP_HEADERS:
95
+ inject_headers(config.HTTP_HEADERS, client_params)
96
+
74
97
  if config.GOOGLE_GENAI_USE_VERTEXAI is not None:
75
98
  client_params["vertexai"] = config.GOOGLE_GENAI_USE_VERTEXAI
76
99
 
@@ -222,6 +245,10 @@ class _GenerationContext:
222
245
  model_name = kwargs.pop("model", client.config.MODEL)
223
246
  callbacks = prepare_callbacks(client.config, kwargs, set_stream=False)
224
247
  is_image = is_image_model(model_name)
248
+ stream = kwargs.pop("stream", False) or (callbacks and not is_image)
249
+ if "extra_headers" in kwargs: # OpenAI compatible extra headers injection
250
+ extra_headers = kwargs.pop("extra_headers")
251
+ inject_headers(extra_headers, kwargs)
225
252
  return _GenerationContext(
226
253
  model_name=model_name,
227
254
  save=kwargs.pop("save", True),
@@ -231,7 +258,7 @@ class _GenerationContext:
231
258
  genai_client=client.genai_client,
232
259
  config=client.config,
233
260
  is_image_model=is_image,
234
- stream=callbacks and not is_image
261
+ stream=stream,
235
262
  )
236
263
 
237
264
 
@@ -1,11 +1,15 @@
1
+ """
2
+ OpenAI LLM client implementation.
3
+ """
1
4
  import asyncio
2
5
  import base64
6
+ from typing import Any
3
7
 
4
8
  import openai
5
9
  from openai.types import CompletionChoice, ImagesResponse
6
10
 
7
11
  from ..lm_client import BaseAIChatClient, BaseAsyncAIClient
8
- from ..message_types import TMsgContentPart
12
+ from ..message_types import TMsgContentPart, TMsgContent
9
13
  from ..configuration import Config
10
14
  from ..llm_backends import ApiPlatform
11
15
  from .._prepare_llm_args import prepare_prompt
@@ -111,6 +115,11 @@ class OpenAIClient(BaseAIChatClient):
111
115
  "base_url": config.LLM_API_BASE,
112
116
  **config.INIT_PARAMS,
113
117
  }
118
+ if config.HTTP_HEADERS:
119
+ if "default_headers" not in client_params: # maybe set in INIT_PARAMS
120
+ client_params["default_headers"] = {}
121
+ client_params["default_headers"].update(config.HTTP_HEADERS)
122
+
114
123
  self.oai_client = client_type(**client_params)
115
124
  self.aio = AsyncOpenAIClient(
116
125
  oai_connection=async_client_type(**client_params),
@@ -132,6 +141,18 @@ class OpenAIClient(BaseAIChatClient):
132
141
  return image_to_oai(img)
133
142
  return content_part
134
143
 
144
+ def _convert_message_content(self, message_content: TMsgContent) -> Any:
145
+ """
146
+ Convert the message content into a format suitable for the LLM inference chat API.
147
+ """
148
+ if isinstance(message_content, str):
149
+ # Prevent conversion of string content into dict(type=text, text=...)
150
+ # because Azure OpenAI fails with Error 400
151
+ # when passing "azure_search" data source like following:
152
+ # llm(..., extra_body={"data_sources"=[{"type": "azure_search",...}]})
153
+ return message_content
154
+ return super()._convert_message_content(message_content)
155
+
135
156
  def load_models(self, **kwargs) -> dict:
136
157
  models_iter = self.oai_client.models.list(**kwargs)
137
158
  return {model.id: model for model in models_iter}
@@ -166,6 +166,7 @@ class ApiPlatform(SafeStrEnum):
166
166
  return LLM_API_BASE_URLS.get(self.api_type(), {}).get(self)
167
167
 
168
168
 
169
+ # Human-readable labels for API platforms not following val.replace('_', ' ').title() pattern
169
170
  _API_PLATFORM_CUSTOM_LABELS: dict[ApiPlatform, str] = {
170
171
  ApiPlatform.OPENAI: "OpenAI",
171
172
  ApiPlatform.GOOGLE_AI_STUDIO: "Google AI Studio",
File without changes