openaivec 0.10.0__py3-none-any.whl → 1.0.10__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 (45) hide show
  1. openaivec/__init__.py +13 -4
  2. openaivec/_cache/__init__.py +12 -0
  3. openaivec/_cache/optimize.py +109 -0
  4. openaivec/_cache/proxy.py +806 -0
  5. openaivec/_di.py +326 -0
  6. openaivec/_embeddings.py +203 -0
  7. openaivec/{log.py → _log.py} +2 -2
  8. openaivec/_model.py +113 -0
  9. openaivec/{prompt.py → _prompt.py} +95 -28
  10. openaivec/_provider.py +207 -0
  11. openaivec/_responses.py +511 -0
  12. openaivec/_schema/__init__.py +9 -0
  13. openaivec/_schema/infer.py +340 -0
  14. openaivec/_schema/spec.py +350 -0
  15. openaivec/_serialize.py +234 -0
  16. openaivec/{util.py → _util.py} +25 -85
  17. openaivec/pandas_ext.py +1635 -425
  18. openaivec/spark.py +604 -335
  19. openaivec/task/__init__.py +27 -29
  20. openaivec/task/customer_support/__init__.py +9 -15
  21. openaivec/task/customer_support/customer_sentiment.py +51 -41
  22. openaivec/task/customer_support/inquiry_classification.py +86 -61
  23. openaivec/task/customer_support/inquiry_summary.py +44 -45
  24. openaivec/task/customer_support/intent_analysis.py +56 -41
  25. openaivec/task/customer_support/response_suggestion.py +49 -43
  26. openaivec/task/customer_support/urgency_analysis.py +76 -71
  27. openaivec/task/nlp/__init__.py +4 -4
  28. openaivec/task/nlp/dependency_parsing.py +19 -20
  29. openaivec/task/nlp/keyword_extraction.py +22 -24
  30. openaivec/task/nlp/morphological_analysis.py +25 -25
  31. openaivec/task/nlp/named_entity_recognition.py +26 -28
  32. openaivec/task/nlp/sentiment_analysis.py +29 -21
  33. openaivec/task/nlp/translation.py +24 -30
  34. openaivec/task/table/__init__.py +3 -0
  35. openaivec/task/table/fillna.py +183 -0
  36. openaivec-1.0.10.dist-info/METADATA +399 -0
  37. openaivec-1.0.10.dist-info/RECORD +39 -0
  38. {openaivec-0.10.0.dist-info → openaivec-1.0.10.dist-info}/WHEEL +1 -1
  39. openaivec/embeddings.py +0 -172
  40. openaivec/responses.py +0 -392
  41. openaivec/serialize.py +0 -225
  42. openaivec/task/model.py +0 -84
  43. openaivec-0.10.0.dist-info/METADATA +0 -546
  44. openaivec-0.10.0.dist-info/RECORD +0 -29
  45. {openaivec-0.10.0.dist-info → openaivec-1.0.10.dist-info}/licenses/LICENSE +0 -0
@@ -6,7 +6,7 @@ construction of a prompt in a structured way, including setting the
6
6
  purpose, adding cautions, and providing examples.
7
7
 
8
8
  ```python
9
- from openaivec.prompt import FewShotPromptBuilder
9
+ from openaivec import FewShotPromptBuilder
10
10
 
11
11
  prompt_str: str = (
12
12
  FewShotPromptBuilder()
@@ -44,13 +44,15 @@ this will produce an XML string that looks like this:
44
44
 
45
45
  import difflib
46
46
  import logging
47
- from typing import Any, List
48
47
  from xml.etree import ElementTree
49
48
 
50
49
  from openai import OpenAI
51
50
  from openai.types.responses import ParsedResponse
52
51
  from pydantic import BaseModel
53
52
 
53
+ from openaivec._model import ResponsesModelName
54
+ from openaivec._provider import CONTAINER
55
+
54
56
  __all__ = [
55
57
  "FewShotPrompt",
56
58
  "FewShotPromptBuilder",
@@ -87,8 +89,8 @@ class FewShotPrompt(BaseModel):
87
89
  """
88
90
 
89
91
  purpose: str
90
- cautions: List[str]
91
- examples: List[Example]
92
+ cautions: list[str]
93
+ examples: list[Example]
92
94
 
93
95
 
94
96
  class Step(BaseModel):
@@ -113,7 +115,7 @@ class Request(BaseModel):
113
115
 
114
116
 
115
117
  class Response(BaseModel):
116
- iterations: List[Step]
118
+ iterations: list[Step]
117
119
 
118
120
 
119
121
  _PROMPT: str = """
@@ -123,6 +125,7 @@ _PROMPT: str = """
123
125
  Receive the prompt in JSON format with fields "purpose",
124
126
  "cautions", and "examples". Ensure the entire prompt is free
125
127
  from logical contradictions, redundancies, and ambiguities.
128
+ IMPORTANT: The "examples" array must always contain at least one example throughout all iterations.
126
129
  </Instruction>
127
130
  <Instruction id="2">
128
131
  - Modify only one element per iteration among “purpose”, “examples”, or
@@ -152,8 +155,10 @@ _PROMPT: str = """
152
155
  </Instruction>
153
156
  <Instruction id="6">
154
157
  In the "examples" field, enhance the examples to cover a wide range of scenarios.
158
+ CRITICAL: The examples array must NEVER be empty - always maintain at least one example.
155
159
  Add as many non-redundant examples as possible,
156
160
  since having more examples leads to better coverage and understanding.
161
+ You may modify existing examples or add new ones, but never remove all examples.
157
162
  </Instruction>
158
163
  <Instruction id="7">
159
164
  Verify that the improved prompt adheres to the Request and
@@ -163,6 +168,7 @@ _PROMPT: str = """
163
168
  Generate the final refined FewShotPrompt as an iteration in
164
169
  the Response, ensuring the final output is consistent,
165
170
  unambiguous, and free from any redundancies or contradictions.
171
+ MANDATORY: Verify that the examples array contains at least one example before completing.
166
172
  </Instruction>
167
173
  </Instructions>
168
174
  <Example>
@@ -203,7 +209,9 @@ _PROMPT: str = """
203
209
  "iterations": [
204
210
  {
205
211
  "id": 1,
206
- "analysis": "The original purpose was vague and did not explicitly state the main objective. This ambiguity could lead to confusion about the task. In this iteration, we refined the purpose to clearly specify that the goal is to determine the correct category for a given word based on its context.",
212
+ "analysis": "The original purpose was vague and did not explicitly state the main objective.
213
+ This ambiguity could lead to confusion about the task. In this iteration, we refined the purpose to
214
+ clearly specify that the goal is to determine the correct category for a given word based on its context.",
207
215
  "prompt": {
208
216
  "purpose": "Determine the correct category for a given word by analyzing its context for clear meaning.",
209
217
  "cautions": [
@@ -225,7 +233,10 @@ _PROMPT: str = """
225
233
  },
226
234
  {
227
235
  "id": 2,
228
- "analysis": "Next, we focused solely on the cautions section. The original cautions were generic and did not mention potential pitfalls like homonyms or polysemy. Failing to address these could result in misclassification. Therefore, we added a specific caution regarding homonyms while keeping the purpose and examples unchanged.",
236
+ "analysis": "Next, we focused solely on the cautions section. The original cautions were generic and
237
+ did not mention potential pitfalls like homonyms or polysemy. Failing to address these could result in
238
+ misclassification. Therefore, we added a specific caution regarding homonyms while keeping the purpose
239
+ and examples unchanged.",
229
240
  "prompt": {
230
241
  "purpose": "Determine the correct category for a given word by analyzing its context for clear meaning.",
231
242
  "cautions": [
@@ -248,7 +259,10 @@ _PROMPT: str = """
248
259
  },
249
260
  {
250
261
  "id": 3,
251
- "analysis": "In this step, we improved the examples section to cover a broader range of scenarios and address potential ambiguities. By adding examples that include words with multiple interpretations (such as 'Mercury' for both a planet and an element), we enhance clarity and ensure better coverage. This iteration only modifies the examples section, leaving purpose and cautions intact.",
262
+ "analysis": "In this step, we improved the examples section to cover a broader range of scenarios and
263
+ address potential ambiguities. By adding examples that include words with multiple interpretations
264
+ (such as 'Mercury' for both a planet and an element), we enhance clarity and ensure better coverage.
265
+ This iteration only modifies the examples section, leaving purpose and cautions intact.",
252
266
  "prompt": {
253
267
  "purpose": "Determine the correct category for a given word by analyzing its context for clear meaning.",
254
268
  "cautions": [
@@ -328,11 +342,29 @@ def _render_prompt(prompt: FewShotPrompt) -> str:
328
342
 
329
343
 
330
344
  class FewShotPromptBuilder:
345
+ """Builder for creating few-shot prompts with validation.
346
+
347
+ Usage:
348
+ builder = (FewShotPromptBuilder()
349
+ .purpose("Your task description")
350
+ .example("input1", "output1") # At least one required
351
+ .example("input2", "output2")
352
+ .build())
353
+
354
+ Note:
355
+ Both .purpose() and at least one .example() call are required before
356
+ calling .build(), .improve(), or .get_object().
357
+ """
358
+
331
359
  _prompt: FewShotPrompt
332
- _steps: List[Step]
360
+ _steps: list[Step]
333
361
 
334
362
  def __init__(self):
335
- """Initialize an empty FewShotPromptBuilder."""
363
+ """Initialize an empty FewShotPromptBuilder.
364
+
365
+ Note:
366
+ You must call .purpose() and at least one .example() before building.
367
+ """
336
368
  self._prompt = FewShotPrompt(purpose="", cautions=[], examples=[])
337
369
 
338
370
  @classmethod
@@ -391,6 +423,8 @@ class FewShotPromptBuilder:
391
423
  ) -> "FewShotPromptBuilder":
392
424
  """Add a single input/output example.
393
425
 
426
+ At least one example is required before calling .build(), .improve(), or .get_object().
427
+
394
428
  Args:
395
429
  input_value (str | BaseModel): Example input; if a Pydantic model is
396
430
  provided it is serialised to JSON.
@@ -409,45 +443,67 @@ class FewShotPromptBuilder:
409
443
 
410
444
  def improve(
411
445
  self,
412
- client: OpenAI,
413
- model_name: str,
414
- temperature: float = 0.0,
415
- top_p: float = 1.0,
446
+ client: OpenAI | None = None,
447
+ model_name: str | None = None,
448
+ **api_kwargs,
416
449
  ) -> "FewShotPromptBuilder":
417
450
  """Iteratively refine the prompt using an LLM.
418
451
 
419
452
  The method calls a single LLM request that returns multiple
420
453
  editing steps and stores each step for inspection.
421
454
 
455
+ When client is None, automatically creates a client using environment variables:
456
+ - For OpenAI: ``OPENAI_API_KEY``
457
+ - For Azure OpenAI: ``AZURE_OPENAI_API_KEY``, ``AZURE_OPENAI_BASE_URL``, ``AZURE_OPENAI_API_VERSION``
458
+
422
459
  Args:
423
- client (openai.OpenAI): Configured OpenAI client.
424
- model_name (str): Model identifier (e.g. ``gpt-4o-mini``).
425
- temperature (float, optional): Sampling temperature. Defaults to 0.0.
426
- top_p (float, optional): Nucleus sampling parameter. Defaults to 1.0.
460
+ client (OpenAI | None): Configured OpenAI client. If None, uses DI container with environment variables.
461
+ model_name (str | None): Model identifier. If None, uses default ``gpt-4.1-mini``.
462
+ **api_kwargs: Additional OpenAI API parameters (temperature, top_p, etc.).
427
463
 
428
464
  Returns:
429
465
  FewShotPromptBuilder: The current builder instance containing the refined prompt and iteration history.
466
+
467
+ Raises:
468
+ ValueError: If the prompt is not valid (missing purpose or examples).
430
469
  """
470
+ # Validate before making API call to provide early feedback
471
+ self._validate()
431
472
 
432
- response: ParsedResponse[Response] = client.responses.parse(
433
- model=model_name,
473
+ _client = client or CONTAINER.resolve(OpenAI)
474
+ _model_name = model_name or CONTAINER.resolve(ResponsesModelName).value
475
+
476
+ response: ParsedResponse[Response] = _client.responses.parse(
477
+ model=_model_name,
434
478
  instructions=_PROMPT,
435
479
  input=Request(prompt=self._prompt).model_dump_json(),
436
- temperature=temperature,
437
- top_p=top_p,
438
480
  text_format=Response,
481
+ **api_kwargs,
439
482
  )
440
483
 
441
484
  # keep the original prompt
442
485
  self._steps = [Step(id=0, analysis="Original Prompt", prompt=self._prompt)]
443
486
 
444
487
  # add the histories
445
- for step in response.output_parsed.iterations:
446
- self._steps.append(step)
488
+ if response.output_parsed:
489
+ for step in response.output_parsed.iterations:
490
+ self._steps.append(step)
447
491
 
448
492
  # set the final prompt
449
493
  self._prompt = self._steps[-1].prompt
450
494
 
495
+ # Validate the improved prompt to ensure examples weren't removed by LLM
496
+ try:
497
+ self._validate()
498
+ except ValueError as e:
499
+ _logger.warning(f"LLM produced invalid prompt during improve(): {e}")
500
+ # Restore original prompt if LLM produced invalid result
501
+ self._prompt = self._steps[0].prompt
502
+ raise ValueError(
503
+ f"LLM improvement failed to maintain required fields: {e}. "
504
+ "This may indicate an issue with the improvement instructions or model behavior."
505
+ )
506
+
451
507
  return self
452
508
 
453
509
  def explain(self) -> "FewShotPromptBuilder":
@@ -456,6 +512,10 @@ class FewShotPromptBuilder:
456
512
  Returns:
457
513
  FewShotPromptBuilder: The current builder instance.
458
514
  """
515
+ if not hasattr(self, "_steps") or not self._steps:
516
+ print("No improvement steps available. Call improve() first.")
517
+ return self
518
+
459
519
  for previous, current in zip(self._steps, self._steps[1:]):
460
520
  print(f"=== Iteration {current.id} ===\n")
461
521
  print(f"Instruction: {current.analysis}")
@@ -479,9 +539,14 @@ class FewShotPromptBuilder:
479
539
  """
480
540
  # Validate that 'purpose' and 'examples' are not empty.
481
541
  if not self._prompt.purpose:
482
- raise ValueError("Purpose is required.")
542
+ raise ValueError(
543
+ "Purpose is required. Please call .purpose('your purpose description') before building the prompt."
544
+ )
483
545
  if not self._prompt.examples or len(self._prompt.examples) == 0:
484
- raise ValueError("At least one example is required.")
546
+ raise ValueError(
547
+ "At least one example is required. Please add examples using "
548
+ ".example('input', 'output') before building the prompt."
549
+ )
485
550
 
486
551
  def get_object(self) -> FewShotPrompt:
487
552
  """Return the underlying FewShotPrompt object.
@@ -501,11 +566,13 @@ class FewShotPromptBuilder:
501
566
  self._validate()
502
567
  return self.build_xml()
503
568
 
504
- def build_json(self, **kwargs: Any) -> str:
569
+ def build_json(self, **kwargs) -> str:
505
570
  """Build and return the prompt as a JSON string.
506
571
 
507
572
  Args:
508
- **kwargs: Keyword arguments forwarded to ``model_dump_json``.
573
+ **kwargs: Keyword arguments forwarded to Pydantic's ``model_dump_json``.
574
+ Common options include ``indent``, ``include``, ``exclude``,
575
+ ``by_alias``, ``exclude_unset``, ``exclude_defaults``, ``exclude_none``.
509
576
 
510
577
  Returns:
511
578
  str: JSON representation of the prompt.
openaivec/_provider.py ADDED
@@ -0,0 +1,207 @@
1
+ import os
2
+ import warnings
3
+
4
+ import tiktoken
5
+ from openai import AsyncAzureOpenAI, AsyncOpenAI, AzureOpenAI, OpenAI
6
+
7
+ from openaivec import _di as di
8
+ from openaivec._model import (
9
+ AzureOpenAIAPIKey,
10
+ AzureOpenAIAPIVersion,
11
+ AzureOpenAIBaseURL,
12
+ EmbeddingsModelName,
13
+ OpenAIAPIKey,
14
+ ResponsesModelName,
15
+ )
16
+ from openaivec._schema import SchemaInferer
17
+ from openaivec._util import TextChunker
18
+
19
+ __all__ = []
20
+
21
+ CONTAINER = di.Container()
22
+
23
+
24
+ def _build_missing_credentials_error(
25
+ openai_api_key: str | None,
26
+ azure_api_key: str | None,
27
+ azure_base_url: str | None,
28
+ azure_api_version: str | None,
29
+ ) -> str:
30
+ """Build a detailed error message for missing credentials.
31
+
32
+ Args:
33
+ openai_api_key (str | None): The OpenAI API key value.
34
+ azure_api_key (str | None): The Azure OpenAI API key value.
35
+ azure_base_url (str | None): The Azure OpenAI base URL value.
36
+ azure_api_version (str | None): The Azure OpenAI API version value.
37
+
38
+ Returns:
39
+ str: A detailed error message with missing variables and setup instructions.
40
+ """
41
+ lines = ["No valid OpenAI or Azure OpenAI credentials found.", ""]
42
+
43
+ # Check OpenAI
44
+ lines.append("Option 1: Set OPENAI_API_KEY for OpenAI")
45
+ if openai_api_key:
46
+ lines.append(" ✓ OPENAI_API_KEY is set")
47
+ else:
48
+ lines.append(" ✗ OPENAI_API_KEY is not set")
49
+ lines.append(' Example: export OPENAI_API_KEY="sk-..."')
50
+ lines.append("")
51
+
52
+ # Check Azure OpenAI
53
+ lines.append("Option 2: Set all Azure OpenAI variables")
54
+ azure_vars = [
55
+ ("AZURE_OPENAI_API_KEY", azure_api_key, '"your-azure-api-key"'),
56
+ ("AZURE_OPENAI_BASE_URL", azure_base_url, '"https://YOUR-RESOURCE-NAME.services.ai.azure.com/openai/v1/"'),
57
+ ("AZURE_OPENAI_API_VERSION", azure_api_version, '"2024-12-01-preview"'),
58
+ ]
59
+ for var_name, var_value, example in azure_vars:
60
+ if var_value:
61
+ lines.append(f" ✓ {var_name} is set")
62
+ else:
63
+ lines.append(f" ✗ {var_name} is not set")
64
+ lines.append(f" Example: export {var_name}={example}")
65
+
66
+ return "\n".join(lines)
67
+
68
+
69
+ def _check_azure_v1_api_url(base_url: str) -> None:
70
+ """Check if Azure OpenAI base URL uses the recommended v1 API format.
71
+
72
+ Issues a warning if the URL doesn't end with '/openai/v1/' to encourage
73
+ migration to the v1 API format as recommended by Microsoft.
74
+
75
+ Reference: https://learn.microsoft.com/en-us/azure/ai-foundry/openai/api-version-lifecycle
76
+
77
+ Args:
78
+ base_url (str): The Azure OpenAI base URL to check.
79
+ """
80
+ if base_url and not base_url.rstrip("/").endswith("/openai/v1"):
81
+ warnings.warn(
82
+ "⚠️ Azure OpenAI v1 API is recommended. Your base URL should end with '/openai/v1/'. "
83
+ f"Current URL: '{base_url}'. "
84
+ "Consider updating to: 'https://YOUR-RESOURCE-NAME.services.ai.azure.com/openai/v1/' "
85
+ "for better performance and future compatibility. "
86
+ "See: https://learn.microsoft.com/en-us/azure/ai-foundry/openai/api-version-lifecycle",
87
+ UserWarning,
88
+ stacklevel=3,
89
+ )
90
+
91
+
92
+ def provide_openai_client() -> OpenAI:
93
+ """Provide OpenAI client based on environment variables.
94
+
95
+ Automatically detects and prioritizes OpenAI over Azure OpenAI configuration.
96
+ Checks the following environment variables in order:
97
+ 1. OPENAI_API_KEY - if set, creates standard OpenAI client
98
+ 2. Azure OpenAI variables (AZURE_OPENAI_API_KEY, AZURE_OPENAI_BASE_URL,
99
+ AZURE_OPENAI_API_VERSION) - if all set, creates Azure OpenAI client
100
+
101
+ Returns:
102
+ OpenAI: Configured OpenAI or AzureOpenAI client instance.
103
+
104
+ Raises:
105
+ ValueError: If no valid environment variables are found for either service.
106
+ """
107
+ openai_api_key = CONTAINER.resolve(OpenAIAPIKey)
108
+ if openai_api_key.value:
109
+ return OpenAI()
110
+
111
+ azure_api_key = CONTAINER.resolve(AzureOpenAIAPIKey)
112
+ azure_base_url = CONTAINER.resolve(AzureOpenAIBaseURL)
113
+ azure_api_version = CONTAINER.resolve(AzureOpenAIAPIVersion)
114
+
115
+ if all(param.value for param in [azure_api_key, azure_base_url, azure_api_version]):
116
+ # Type checker support: values are guaranteed non-None by the all() check above
117
+ assert azure_api_key.value is not None
118
+ assert azure_base_url.value is not None
119
+ assert azure_api_version.value is not None
120
+
121
+ _check_azure_v1_api_url(azure_base_url.value)
122
+ return AzureOpenAI(
123
+ api_key=azure_api_key.value,
124
+ base_url=azure_base_url.value,
125
+ api_version=azure_api_version.value,
126
+ )
127
+
128
+ raise ValueError(
129
+ _build_missing_credentials_error(
130
+ openai_api_key=openai_api_key.value,
131
+ azure_api_key=azure_api_key.value,
132
+ azure_base_url=azure_base_url.value,
133
+ azure_api_version=azure_api_version.value,
134
+ )
135
+ )
136
+
137
+
138
+ def provide_async_openai_client() -> AsyncOpenAI:
139
+ """Provide asynchronous OpenAI client based on environment variables.
140
+
141
+ Automatically detects and prioritizes OpenAI over Azure OpenAI configuration.
142
+ Checks the following environment variables in order:
143
+ 1. OPENAI_API_KEY - if set, creates standard AsyncOpenAI client
144
+ 2. Azure OpenAI variables (AZURE_OPENAI_API_KEY, AZURE_OPENAI_BASE_URL,
145
+ AZURE_OPENAI_API_VERSION) - if all set, creates AsyncAzureOpenAI client
146
+
147
+ Returns:
148
+ AsyncOpenAI: Configured AsyncOpenAI or AsyncAzureOpenAI client instance.
149
+
150
+ Raises:
151
+ ValueError: If no valid environment variables are found for either service.
152
+ """
153
+ openai_api_key = CONTAINER.resolve(OpenAIAPIKey)
154
+ if openai_api_key.value:
155
+ return AsyncOpenAI()
156
+
157
+ azure_api_key = CONTAINER.resolve(AzureOpenAIAPIKey)
158
+ azure_base_url = CONTAINER.resolve(AzureOpenAIBaseURL)
159
+ azure_api_version = CONTAINER.resolve(AzureOpenAIAPIVersion)
160
+
161
+ if all(param.value for param in [azure_api_key, azure_base_url, azure_api_version]):
162
+ # Type checker support: values are guaranteed non-None by the all() check above
163
+ assert azure_api_key.value is not None
164
+ assert azure_base_url.value is not None
165
+ assert azure_api_version.value is not None
166
+
167
+ _check_azure_v1_api_url(azure_base_url.value)
168
+ return AsyncAzureOpenAI(
169
+ api_key=azure_api_key.value,
170
+ base_url=azure_base_url.value,
171
+ api_version=azure_api_version.value,
172
+ )
173
+
174
+ raise ValueError(
175
+ _build_missing_credentials_error(
176
+ openai_api_key=openai_api_key.value,
177
+ azure_api_key=azure_api_key.value,
178
+ azure_base_url=azure_base_url.value,
179
+ azure_api_version=azure_api_version.value,
180
+ )
181
+ )
182
+
183
+
184
+ def set_default_registrations():
185
+ CONTAINER.register(ResponsesModelName, lambda: ResponsesModelName("gpt-4.1-mini"))
186
+ CONTAINER.register(EmbeddingsModelName, lambda: EmbeddingsModelName("text-embedding-3-small"))
187
+ CONTAINER.register(OpenAIAPIKey, lambda: OpenAIAPIKey(os.getenv("OPENAI_API_KEY")))
188
+ CONTAINER.register(AzureOpenAIAPIKey, lambda: AzureOpenAIAPIKey(os.getenv("AZURE_OPENAI_API_KEY")))
189
+ CONTAINER.register(AzureOpenAIBaseURL, lambda: AzureOpenAIBaseURL(os.getenv("AZURE_OPENAI_BASE_URL")))
190
+ CONTAINER.register(
191
+ cls=AzureOpenAIAPIVersion,
192
+ provider=lambda: AzureOpenAIAPIVersion(os.getenv("AZURE_OPENAI_API_VERSION", "preview")),
193
+ )
194
+ CONTAINER.register(OpenAI, provide_openai_client)
195
+ CONTAINER.register(AsyncOpenAI, provide_async_openai_client)
196
+ CONTAINER.register(tiktoken.Encoding, lambda: tiktoken.get_encoding("o200k_base"))
197
+ CONTAINER.register(TextChunker, lambda: TextChunker(CONTAINER.resolve(tiktoken.Encoding)))
198
+ CONTAINER.register(
199
+ SchemaInferer,
200
+ lambda: SchemaInferer(
201
+ client=CONTAINER.resolve(OpenAI),
202
+ model_name=CONTAINER.resolve(ResponsesModelName).value,
203
+ ),
204
+ )
205
+
206
+
207
+ set_default_registrations()