promptbuilder 0.4.16__py3-none-any.whl → 0.4.18__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.
@@ -115,7 +115,7 @@ class AiSuiteLLMClient(BaseLLMClient):
115
115
  })
116
116
  aisuite_kwargs["tools"] = aisuite_tools
117
117
 
118
- if result_type is None or result_type == "text":
118
+ if result_type is None or result_type == "json":
119
119
  response = self.client.chat.completions.create(**aisuite_kwargs)
120
120
 
121
121
  parts: list[Part] = []
@@ -164,6 +164,8 @@ class AiSuiteLLMClient(BaseLLMClient):
164
164
  usage_metadata = AiSuiteLLMClient.make_usage_metadata(response.usage) if hasattr(response, "usage") and response.usage is not None else None,
165
165
  parsed=parsed_pydantic,
166
166
  )
167
+ else:
168
+ raise ValueError(f"Unsupported result_type: {result_type}. Supported types are: None, 'json', or a Pydantic model.")
167
169
 
168
170
 
169
171
  class AiSuiteLLMClientAsync(BaseLLMClientAsync):
@@ -272,7 +274,7 @@ class AiSuiteLLMClientAsync(BaseLLMClientAsync):
272
274
  })
273
275
  aisuite_kwargs["tools"] = aisuite_tools
274
276
 
275
- if result_type is None or result_type == "text":
277
+ if result_type is None or result_type == "json":
276
278
  response = await self.client.chat.completions.create(**aisuite_kwargs)
277
279
 
278
280
  parts: list[Part] = []
@@ -321,3 +323,5 @@ class AiSuiteLLMClientAsync(BaseLLMClientAsync):
321
323
  usage_metadata = AiSuiteLLMClient.make_usage_metadata(response.usage) if hasattr(response, "usage") and response.usage is not None else None,
322
324
  parsed=parsed_pydantic,
323
325
  )
326
+ else:
327
+ raise ValueError(f"Unsupported result_type: {result_type}. Supported types are: None, 'json', or a Pydantic model.")
@@ -111,13 +111,14 @@ class AnthropicLLMClient(BaseLLMClient):
111
111
  model: str,
112
112
  api_key: str = os.getenv("ANTHROPIC_API_KEY"),
113
113
  decorator_configs: DecoratorConfigs | None = None,
114
+ default_thinking_config: ThinkingConfig | None = None,
114
115
  default_max_tokens: int | None = None,
115
116
  default_max_tokens_strategy: DefaultMaxTokensStrategy = AnthropicDefaultMaxTokensStrategy(),
116
117
  **kwargs,
117
118
  ):
118
119
  if api_key is None or not isinstance(api_key, str):
119
120
  raise ValueError("To create an anthropic llm client you need to either set the environment variable ANTHROPIC_API_KEY or pass the api_key in string format")
120
- super().__init__(AnthropicLLMClient.PROVIDER, model, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
121
+ super().__init__(AnthropicLLMClient.PROVIDER, model, decorator_configs=decorator_configs, default_thinking_config=default_thinking_config, default_max_tokens=default_max_tokens)
121
122
  self._api_key = api_key
122
123
  self.client = Anthropic(api_key=api_key)
123
124
  self.default_max_tokens_strategy = default_max_tokens_strategy
@@ -166,7 +167,7 @@ class AnthropicLLMClient(BaseLLMClient):
166
167
  messages: list[Content],
167
168
  result_type: ResultType = None,
168
169
  *,
169
- thinking_config: ThinkingConfig = ThinkingConfig(),
170
+ thinking_config: ThinkingConfig | None = None,
170
171
  system_message: str | None = None,
171
172
  max_tokens: int | None = None,
172
173
  tools: list[Tool] | None = None,
@@ -185,15 +186,18 @@ class AnthropicLLMClient(BaseLLMClient):
185
186
  "messages": anthropic_messages,
186
187
  }
187
188
 
188
- if thinking_config.include_thoughts:
189
- anthropic_kwargs["thinking"] = {
190
- "budget_tokens": thinking_config.thinking_budget,
191
- "type": "enabled",
192
- }
193
- else:
194
- anthropic_kwargs["thinking"] = {
195
- "type": "disabled",
196
- }
189
+ if thinking_config is None:
190
+ thinking_config = self.default_thinking_config
191
+ if thinking_config is not None:
192
+ if thinking_config.include_thoughts:
193
+ anthropic_kwargs["thinking"] = {
194
+ "budget_tokens": thinking_config.thinking_budget,
195
+ "type": "enabled",
196
+ }
197
+ else:
198
+ anthropic_kwargs["thinking"] = {
199
+ "type": "disabled",
200
+ }
197
201
 
198
202
  if system_message is not None:
199
203
  anthropic_kwargs["system"] = system_message
@@ -224,7 +228,7 @@ class AnthropicLLMClient(BaseLLMClient):
224
228
  tool_choice_mode = tool_config.function_calling_config.mode
225
229
  anthropic_kwargs["tool_choice"] = {"type": tool_choice_mode.lower()}
226
230
 
227
- if result_type is None or result_type == "text":
231
+ if result_type is None or result_type == "json":
228
232
  response = self.client.messages.create(**anthropic_kwargs)
229
233
 
230
234
  parts: list[Part] = []
@@ -278,6 +282,7 @@ class AnthropicLLMClient(BaseLLMClient):
278
282
  self,
279
283
  messages: list[Content],
280
284
  *,
285
+ thinking_config: ThinkingConfig | None = None,
281
286
  system_message: str | None = None,
282
287
  max_tokens: int | None = None,
283
288
  ) -> Iterator[Response]:
@@ -296,6 +301,19 @@ class AnthropicLLMClient(BaseLLMClient):
296
301
  "stream": True,
297
302
  }
298
303
 
304
+ if thinking_config is None:
305
+ thinking_config = self.default_thinking_config
306
+ if thinking_config is not None:
307
+ if thinking_config.include_thoughts:
308
+ anthropic_kwargs["thinking"] = {
309
+ "budget_tokens": thinking_config.thinking_budget,
310
+ "type": "enabled",
311
+ }
312
+ else:
313
+ anthropic_kwargs["thinking"] = {
314
+ "type": "disabled",
315
+ }
316
+
299
317
  if system_message is not None:
300
318
  anthropic_kwargs["system"] = system_message
301
319
 
@@ -315,6 +333,7 @@ class AnthropicLLMClient(BaseLLMClient):
315
333
  ))
316
334
  return models
317
335
 
336
+
318
337
  class AnthropicStreamIteratorAsync:
319
338
  def __init__(self, anthropic_iterator: AsyncStream[RawMessageStreamEvent]):
320
339
  self._anthropic_iterator = anthropic_iterator
@@ -356,13 +375,14 @@ class AnthropicLLMClientAsync(BaseLLMClientAsync):
356
375
  model: str,
357
376
  api_key: str = os.getenv("ANTHROPIC_API_KEY"),
358
377
  decorator_configs: DecoratorConfigs | None = None,
378
+ default_thinking_config: ThinkingConfig | None = None,
359
379
  default_max_tokens: int | None = None,
360
380
  default_max_tokens_strategy: DefaultMaxTokensStrategy = AnthropicDefaultMaxTokensStrategy(),
361
381
  **kwargs,
362
382
  ):
363
383
  if api_key is None or not isinstance(api_key, str):
364
384
  raise ValueError("To create an anthropic llm client you need to either set the environment variable ANTHROPIC_API_KEY or pass the api_key in string format")
365
- super().__init__(AnthropicLLMClientAsync.PROVIDER, model, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
385
+ super().__init__(AnthropicLLMClientAsync.PROVIDER, model, decorator_configs=decorator_configs, default_thinking_config=default_thinking_config, default_max_tokens=default_max_tokens)
366
386
  self._api_key = api_key
367
387
  self.client = AsyncAnthropic(api_key=api_key)
368
388
  self.default_max_tokens_strategy = default_max_tokens_strategy
@@ -376,7 +396,7 @@ class AnthropicLLMClientAsync(BaseLLMClientAsync):
376
396
  messages: list[Content],
377
397
  result_type: ResultType = None,
378
398
  *,
379
- thinking_config: ThinkingConfig = ThinkingConfig(),
399
+ thinking_config: ThinkingConfig | None = None,
380
400
  system_message: str | None = None,
381
401
  max_tokens: int | None = None,
382
402
  tools: list[Tool] | None = None,
@@ -401,15 +421,18 @@ class AnthropicLLMClientAsync(BaseLLMClientAsync):
401
421
  "messages": anthropic_messages,
402
422
  }
403
423
 
404
- if thinking_config.include_thoughts:
405
- anthropic_kwargs["thinking"] = {
406
- "budget_tokens": thinking_config.thinking_budget,
407
- "type": "enabled",
408
- }
409
- else:
410
- anthropic_kwargs["thinking"] = {
411
- "type": "disabled",
412
- }
424
+ if thinking_config is None:
425
+ thinking_config = self.default_thinking_config
426
+ if thinking_config is not None:
427
+ if thinking_config.include_thoughts:
428
+ anthropic_kwargs["thinking"] = {
429
+ "budget_tokens": thinking_config.thinking_budget,
430
+ "type": "enabled",
431
+ }
432
+ else:
433
+ anthropic_kwargs["thinking"] = {
434
+ "type": "disabled",
435
+ }
413
436
 
414
437
  if system_message is not None:
415
438
  anthropic_kwargs["system"] = system_message
@@ -440,7 +463,7 @@ class AnthropicLLMClientAsync(BaseLLMClientAsync):
440
463
  tool_choice_mode = tool_config.function_calling_config.mode
441
464
  anthropic_kwargs["tool_choice"] = {"type": tool_choice_mode.lower()}
442
465
 
443
- if result_type is None or result_type == "text":
466
+ if result_type is None or result_type == "json":
444
467
  response = await self.client.messages.create(**anthropic_kwargs)
445
468
 
446
469
  parts: list[Part] = []
@@ -487,11 +510,14 @@ class AnthropicLLMClientAsync(BaseLLMClientAsync):
487
510
  ),
488
511
  parsed=parsed_pydantic,
489
512
  )
513
+ else:
514
+ raise ValueError(f"Unsupported result_type: {result_type}. Supported types are: None, 'json', or a Pydantic model.")
490
515
 
491
516
  async def create_stream(
492
517
  self,
493
518
  messages: list[Content],
494
519
  *,
520
+ thinking_config: ThinkingConfig | None = None,
495
521
  system_message: str | None = None,
496
522
  max_tokens: int | None = None,
497
523
  ) -> AsyncIterator[Response]:
@@ -515,6 +541,19 @@ class AnthropicLLMClientAsync(BaseLLMClientAsync):
515
541
  "stream": True,
516
542
  }
517
543
 
544
+ if thinking_config is None:
545
+ thinking_config = self.default_thinking_config
546
+ if thinking_config is not None:
547
+ if thinking_config.include_thoughts:
548
+ anthropic_kwargs["thinking"] = {
549
+ "budget_tokens": thinking_config.thinking_budget,
550
+ "type": "enabled",
551
+ }
552
+ else:
553
+ anthropic_kwargs["thinking"] = {
554
+ "type": "disabled",
555
+ }
556
+
518
557
  if system_message is not None:
519
558
  anthropic_kwargs["system"] = system_message
520
559
 
@@ -6,7 +6,7 @@ import logging
6
6
  from abc import ABC, abstractmethod
7
7
  from typing import Iterator, AsyncIterator, Literal, overload
8
8
 
9
- from promptbuilder.llm_client.types import Response, Content, Part, Tool, ToolConfig, FunctionCall, FunctionCallingConfig, Json, ThinkingConfig, ApiKey, PydanticStructure, FinishReason
9
+ from promptbuilder.llm_client.types import Response, Content, Part, Tool, ToolConfig, FunctionCall, FunctionCallingConfig, Json, ThinkingConfig, ApiKey, PydanticStructure, ResultType, FinishReason
10
10
  import promptbuilder.llm_client.utils as utils
11
11
  import promptbuilder.llm_client.logfire_decorators as logfire_decorators
12
12
  from promptbuilder.llm_client.config import GLOBAL_CONFIG
@@ -14,13 +14,18 @@ from promptbuilder.llm_client.config import GLOBAL_CONFIG
14
14
 
15
15
  logger = logging.getLogger(__name__)
16
16
 
17
- type ResultType = Literal["json"] | type[PydanticStructure] | None
18
-
19
-
20
17
  class BaseLLMClient(ABC, utils.InheritDecoratorsMixin):
21
18
  provider: str
22
19
 
23
- def __init__(self, provider: str, model: str, decorator_configs: utils.DecoratorConfigs | None = None, default_max_tokens: int | None = None, **kwargs):
20
+ def __init__(
21
+ self,
22
+ provider: str,
23
+ model: str,
24
+ decorator_configs: utils.DecoratorConfigs | None = None,
25
+ default_thinking_config: ThinkingConfig | None = None,
26
+ default_max_tokens: int | None = None,
27
+ **kwargs,
28
+ ):
24
29
  self.provider = provider
25
30
  self.model = model
26
31
 
@@ -31,6 +36,11 @@ class BaseLLMClient(ABC, utils.InheritDecoratorsMixin):
31
36
  decorator_configs = utils.DecoratorConfigs()
32
37
  self._decorator_configs = decorator_configs
33
38
 
39
+ if default_thinking_config is None:
40
+ if self.full_model_name in GLOBAL_CONFIG.default_thinking_configs:
41
+ default_thinking_config = GLOBAL_CONFIG.default_thinking_configs[self.full_model_name]
42
+ self.default_thinking_config = default_thinking_config
43
+
34
44
  if default_max_tokens is None:
35
45
  if self.full_model_name in GLOBAL_CONFIG.default_max_tokens:
36
46
  default_max_tokens = GLOBAL_CONFIG.default_max_tokens[self.full_model_name]
@@ -72,7 +82,7 @@ class BaseLLMClient(ABC, utils.InheritDecoratorsMixin):
72
82
  messages: list[Content],
73
83
  result_type: ResultType = None,
74
84
  *,
75
- thinking_config: ThinkingConfig = ThinkingConfig(),
85
+ thinking_config: ThinkingConfig | None = None,
76
86
  system_message: str | None = None,
77
87
  max_tokens: int | None = None,
78
88
  tools: list[Tool] | None = None,
@@ -86,7 +96,7 @@ class BaseLLMClient(ABC, utils.InheritDecoratorsMixin):
86
96
  messages: list[Content],
87
97
  result_type: None = None,
88
98
  *,
89
- thinking_config: ThinkingConfig = ThinkingConfig(),
99
+ thinking_config: ThinkingConfig | None = None,
90
100
  system_message: str | None = None,
91
101
  max_tokens: int | None = None,
92
102
  tools: None = None,
@@ -98,7 +108,7 @@ class BaseLLMClient(ABC, utils.InheritDecoratorsMixin):
98
108
  messages: list[Content],
99
109
  result_type: Literal["json"],
100
110
  *,
101
- thinking_config: ThinkingConfig = ThinkingConfig(),
111
+ thinking_config: ThinkingConfig | None = None,
102
112
  system_message: str | None = None,
103
113
  max_tokens: int | None = None,
104
114
  tools: None = None,
@@ -110,7 +120,7 @@ class BaseLLMClient(ABC, utils.InheritDecoratorsMixin):
110
120
  messages: list[Content],
111
121
  result_type: type[PydanticStructure],
112
122
  *,
113
- thinking_config: ThinkingConfig = ThinkingConfig(),
123
+ thinking_config: ThinkingConfig | None = None,
114
124
  system_message: str | None = None,
115
125
  max_tokens: int | None = None,
116
126
  tools: None = None,
@@ -122,7 +132,7 @@ class BaseLLMClient(ABC, utils.InheritDecoratorsMixin):
122
132
  messages: list[Content],
123
133
  result_type: Literal["tools"],
124
134
  *,
125
- thinking_config: ThinkingConfig = ThinkingConfig(),
135
+ thinking_config: ThinkingConfig | None = None,
126
136
  system_message: str | None = None,
127
137
  max_tokens: int | None = None,
128
138
  tools: list[Tool],
@@ -134,12 +144,12 @@ class BaseLLMClient(ABC, utils.InheritDecoratorsMixin):
134
144
  messages: list[Content],
135
145
  result_type: ResultType | Literal["tools"] = None,
136
146
  *,
137
- thinking_config: ThinkingConfig = ThinkingConfig(),
147
+ thinking_config: ThinkingConfig | None = None,
138
148
  system_message: str | None = None,
139
149
  max_tokens: int | None = None,
140
150
  tools: list[Tool] | None = None,
141
151
  tool_choice_mode: Literal["ANY", "NONE"] = "NONE",
142
- autocomplete: bool = False
152
+ autocomplete: bool = False,
143
153
  ):
144
154
  if result_type == "tools":
145
155
  response = self.create(
@@ -223,7 +233,14 @@ class BaseLLMClient(ABC, utils.InheritDecoratorsMixin):
223
233
  @logfire_decorators.create_stream
224
234
  @utils.retry_cls
225
235
  @utils.rpm_limit_cls
226
- def create_stream(self, messages: list[Content], *, system_message: str | None = None, max_tokens: int | None = None) -> Iterator[Response]:
236
+ def create_stream(
237
+ self,
238
+ messages: list[Content],
239
+ *,
240
+ thinking_config: ThinkingConfig | None = None,
241
+ system_message: str | None = None,
242
+ max_tokens: int | None = None,
243
+ ) -> Iterator[Response]:
227
244
  raise NotImplementedError
228
245
 
229
246
  @overload
@@ -232,7 +249,7 @@ class BaseLLMClient(ABC, utils.InheritDecoratorsMixin):
232
249
  prompt: str,
233
250
  result_type: None = None,
234
251
  *,
235
- thinking_config: ThinkingConfig = ThinkingConfig(),
252
+ thinking_config: ThinkingConfig | None = None,
236
253
  system_message: str | None = None,
237
254
  max_tokens: int | None = None,
238
255
  tools: None = None,
@@ -244,7 +261,7 @@ class BaseLLMClient(ABC, utils.InheritDecoratorsMixin):
244
261
  prompt: str,
245
262
  result_type: Literal["json"],
246
263
  *,
247
- thinking_config: ThinkingConfig = ThinkingConfig(),
264
+ thinking_config: ThinkingConfig | None = None,
248
265
  system_message: str | None = None,
249
266
  max_tokens: int | None = None,
250
267
  tools: None = None,
@@ -256,7 +273,7 @@ class BaseLLMClient(ABC, utils.InheritDecoratorsMixin):
256
273
  prompt: str,
257
274
  result_type: type[PydanticStructure],
258
275
  *,
259
- thinking_config: ThinkingConfig = ThinkingConfig(),
276
+ thinking_config: ThinkingConfig | None = None,
260
277
  system_message: str | None = None,
261
278
  max_tokens: int | None = None,
262
279
  tools: None = None,
@@ -268,7 +285,7 @@ class BaseLLMClient(ABC, utils.InheritDecoratorsMixin):
268
285
  prompt: str,
269
286
  result_type: Literal["tools"],
270
287
  *,
271
- thinking_config: ThinkingConfig = ThinkingConfig(),
288
+ thinking_config: ThinkingConfig | None = None,
272
289
  system_message: str | None = None,
273
290
  max_tokens: int | None = None,
274
291
  tools: list[Tool],
@@ -280,7 +297,7 @@ class BaseLLMClient(ABC, utils.InheritDecoratorsMixin):
280
297
  prompt: str,
281
298
  result_type: ResultType | Literal["tools"] = None,
282
299
  *,
283
- thinking_config: ThinkingConfig = ThinkingConfig(),
300
+ thinking_config: ThinkingConfig | None = None,
284
301
  system_message: str | None = None,
285
302
  max_tokens: int | None = None,
286
303
  tools: list[Tool] | None = None,
@@ -300,7 +317,15 @@ class BaseLLMClient(ABC, utils.InheritDecoratorsMixin):
300
317
  class BaseLLMClientAsync(ABC, utils.InheritDecoratorsMixin):
301
318
  provider: str
302
319
 
303
- def __init__(self, provider: str, model: str, decorator_configs: utils.DecoratorConfigs | None = None, default_max_tokens: int | None = None, **kwargs):
320
+ def __init__(
321
+ self,
322
+ provider: str,
323
+ model: str,
324
+ decorator_configs: utils.DecoratorConfigs | None = None,
325
+ default_thinking_config: ThinkingConfig | None = None,
326
+ default_max_tokens: int | None = None,
327
+ **kwargs,
328
+ ):
304
329
  self.provider = provider
305
330
  self.model = model
306
331
 
@@ -311,6 +336,11 @@ class BaseLLMClientAsync(ABC, utils.InheritDecoratorsMixin):
311
336
  decorator_configs = utils.DecoratorConfigs()
312
337
  self._decorator_configs = decorator_configs
313
338
 
339
+ if default_thinking_config is None:
340
+ if self.full_model_name in GLOBAL_CONFIG.default_thinking_configs:
341
+ default_thinking_config = GLOBAL_CONFIG.default_thinking_configs[self.full_model_name]
342
+ self.default_thinking_config = default_thinking_config
343
+
314
344
  if default_max_tokens is None:
315
345
  if self.full_model_name in GLOBAL_CONFIG.default_max_tokens:
316
346
  default_max_tokens = GLOBAL_CONFIG.default_max_tokens[self.full_model_name]
@@ -335,7 +365,7 @@ class BaseLLMClientAsync(ABC, utils.InheritDecoratorsMixin):
335
365
  messages: list[Content],
336
366
  result_type: ResultType = None,
337
367
  *,
338
- thinking_config: ThinkingConfig = ThinkingConfig(),
368
+ thinking_config: ThinkingConfig | None = None,
339
369
  system_message: str | None = None,
340
370
  max_tokens: int | None = None,
341
371
  tools: list[Tool] | None = None,
@@ -349,7 +379,7 @@ class BaseLLMClientAsync(ABC, utils.InheritDecoratorsMixin):
349
379
  messages: list[Content],
350
380
  result_type: None = None,
351
381
  *,
352
- thinking_config: ThinkingConfig = ThinkingConfig(),
382
+ thinking_config: ThinkingConfig | None = None,
353
383
  system_message: str | None = None,
354
384
  max_tokens: int | None = None,
355
385
  tools: None = None,
@@ -361,7 +391,7 @@ class BaseLLMClientAsync(ABC, utils.InheritDecoratorsMixin):
361
391
  messages: list[Content],
362
392
  result_type: Literal["json"],
363
393
  *,
364
- thinking_config: ThinkingConfig = ThinkingConfig(),
394
+ thinking_config: ThinkingConfig | None = None,
365
395
  system_message: str | None = None,
366
396
  max_tokens: int | None = None,
367
397
  tools: None = None,
@@ -373,7 +403,7 @@ class BaseLLMClientAsync(ABC, utils.InheritDecoratorsMixin):
373
403
  messages: list[Content],
374
404
  result_type: type[PydanticStructure],
375
405
  *,
376
- thinking_config: ThinkingConfig = ThinkingConfig(),
406
+ thinking_config: ThinkingConfig | None = None,
377
407
  system_message: str | None = None,
378
408
  max_tokens: int | None = None,
379
409
  tools: None = None,
@@ -385,7 +415,7 @@ class BaseLLMClientAsync(ABC, utils.InheritDecoratorsMixin):
385
415
  messages: list[Content],
386
416
  result_type: Literal["tools"],
387
417
  *,
388
- thinking_config: ThinkingConfig = ThinkingConfig(),
418
+ thinking_config: ThinkingConfig | None = None,
389
419
  system_message: str | None = None,
390
420
  max_tokens: int | None = None,
391
421
  tools: list[Tool],
@@ -397,12 +427,12 @@ class BaseLLMClientAsync(ABC, utils.InheritDecoratorsMixin):
397
427
  messages: list[Content],
398
428
  result_type: ResultType | Literal["tools"] = None,
399
429
  *,
400
- thinking_config: ThinkingConfig = ThinkingConfig(),
430
+ thinking_config: ThinkingConfig | None = None,
401
431
  system_message: str | None = None,
402
432
  max_tokens: int | None = None,
403
433
  tools: list[Tool] | None = None,
404
434
  tool_choice_mode: Literal["ANY", "NONE"] = "NONE",
405
- autocomplete: bool = False
435
+ autocomplete: bool = False,
406
436
  ):
407
437
  if result_type == "tools":
408
438
  response = await self.create(
@@ -457,7 +487,14 @@ class BaseLLMClientAsync(ABC, utils.InheritDecoratorsMixin):
457
487
  @logfire_decorators.create_stream_async
458
488
  @utils.retry_cls_async
459
489
  @utils.rpm_limit_cls_async
460
- async def create_stream(self, messages: list[Content], *, system_message: str | None = None, max_tokens: int | None = None) -> AsyncIterator[Response]:
490
+ async def create_stream(
491
+ self,
492
+ messages: list[Content],
493
+ *,
494
+ thinking_config: ThinkingConfig | None = None,
495
+ system_message: str | None = None,
496
+ max_tokens: int | None = None,
497
+ ) -> AsyncIterator[Response]:
461
498
  raise NotImplementedError
462
499
 
463
500
  @overload
@@ -466,7 +503,7 @@ class BaseLLMClientAsync(ABC, utils.InheritDecoratorsMixin):
466
503
  prompt: str,
467
504
  result_type: None = None,
468
505
  *,
469
- thinking_config: ThinkingConfig = ThinkingConfig(),
506
+ thinking_config: ThinkingConfig | None = None,
470
507
  system_message: str | None = None,
471
508
  max_tokens: int | None = None,
472
509
  tools: None = None,
@@ -478,7 +515,7 @@ class BaseLLMClientAsync(ABC, utils.InheritDecoratorsMixin):
478
515
  prompt: str,
479
516
  result_type: Literal["json"],
480
517
  *,
481
- thinking_config: ThinkingConfig = ThinkingConfig(),
518
+ thinking_config: ThinkingConfig | None = None,
482
519
  system_message: str | None = None,
483
520
  max_tokens: int | None = None,
484
521
  tools: None = None,
@@ -490,7 +527,7 @@ class BaseLLMClientAsync(ABC, utils.InheritDecoratorsMixin):
490
527
  prompt: str,
491
528
  result_type: type[PydanticStructure],
492
529
  *,
493
- thinking_config: ThinkingConfig = ThinkingConfig(),
530
+ thinking_config: ThinkingConfig | None = None,
494
531
  system_message: str | None = None,
495
532
  max_tokens: int | None = None,
496
533
  tools: None = None,
@@ -502,7 +539,7 @@ class BaseLLMClientAsync(ABC, utils.InheritDecoratorsMixin):
502
539
  prompt: str,
503
540
  result_type: Literal["tools"],
504
541
  *,
505
- thinking_config: ThinkingConfig = ThinkingConfig(),
542
+ thinking_config: ThinkingConfig | None = None,
506
543
  system_message: str | None = None,
507
544
  max_tokens: int | None = None,
508
545
  tools: list[Tool],
@@ -514,7 +551,7 @@ class BaseLLMClientAsync(ABC, utils.InheritDecoratorsMixin):
514
551
  prompt: str,
515
552
  result_type: ResultType | Literal["tools"] = None,
516
553
  *,
517
- thinking_config: ThinkingConfig = ThinkingConfig(),
554
+ thinking_config: ThinkingConfig | None = None,
518
555
  system_message: str | None = None,
519
556
  max_tokens: int | None = None,
520
557
  tools: list[Tool] | None = None,
@@ -537,7 +574,13 @@ class CachedLLMClient(BaseLLMClient):
537
574
  return self.llm_client.api_key
538
575
 
539
576
  def __init__(self, llm_client: BaseLLMClient, cache_dir: str = "data/llm_cache"):
540
- super().__init__(provider=llm_client.provider, model=llm_client.model, decorator_configs=llm_client._decorator_configs, default_max_tokens=llm_client.default_max_tokens)
577
+ super().__init__(
578
+ provider=llm_client.provider,
579
+ model=llm_client.model,
580
+ decorator_configs=llm_client._decorator_configs,
581
+ default_thinking_config=llm_client.default_thinking_config,
582
+ default_max_tokens=llm_client.default_max_tokens,
583
+ )
541
584
  self.provider = llm_client.provider
542
585
  self.llm_client = llm_client
543
586
  self.cache_dir = cache_dir
@@ -53,6 +53,7 @@ class BedrockLLMClient(BaseLLMClient):
53
53
  model: str,
54
54
  api_key: BedrockApiKey = BedrockApiKey(),
55
55
  decorator_configs: DecoratorConfigs | None = None,
56
+ default_thinking_config: ThinkingConfig | None = None,
56
57
  default_max_tokens: int | None = None,
57
58
  **kwargs,
58
59
  ):
@@ -61,7 +62,7 @@ class BedrockLLMClient(BaseLLMClient):
61
62
  "To create a bedrock llm client you need to either set the environment variables "
62
63
  "AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and optional AWS_DEFAULT_REGION or pass the api_key as BedrockApiKey instance"
63
64
  )
64
- super().__init__(BedrockLLMClient.PROVIDER, model, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
65
+ super().__init__(BedrockLLMClient.PROVIDER, model, decorator_configs=decorator_configs, default_thinking_config=default_thinking_config, default_max_tokens=default_max_tokens)
65
66
  self._api_key = api_key
66
67
 
67
68
  @property
@@ -73,7 +74,7 @@ class BedrockLLMClient(BaseLLMClient):
73
74
  messages: list[Content],
74
75
  result_type: ResultType = None,
75
76
  *,
76
- thinking_config: ThinkingConfig = ThinkingConfig(),
77
+ thinking_config: ThinkingConfig | None = None,
77
78
  system_message: str | None = None,
78
79
  max_tokens: int | None = None,
79
80
  tools: list[Tool] | None = None,
@@ -230,6 +231,7 @@ class BedrockLLMClient(BaseLLMClient):
230
231
  self,
231
232
  messages: list[Content],
232
233
  *,
234
+ thinking_config: ThinkingConfig | None = None,
233
235
  system_message: str | None = None,
234
236
  max_tokens: int | None = None,
235
237
  ) -> Iterator[Response]:
@@ -313,6 +315,7 @@ class BedrockLLMClientAsync(BaseLLMClientAsync):
313
315
  model: str,
314
316
  api_key: BedrockApiKey = BedrockApiKey(),
315
317
  decorator_configs: DecoratorConfigs | None = None,
318
+ default_thinking_config: ThinkingConfig | None = None,
316
319
  default_max_tokens: int | None = None,
317
320
  **kwargs,
318
321
  ):
@@ -321,7 +324,7 @@ class BedrockLLMClientAsync(BaseLLMClientAsync):
321
324
  "To create a bedrock llm client you need to either set the environment variables "
322
325
  "AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and optional AWS_DEFAULT_REGION or pass the api_key as BedrockApiKey instance"
323
326
  )
324
- super().__init__(BedrockLLMClient.PROVIDER, model, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
327
+ super().__init__(BedrockLLMClient.PROVIDER, model, decorator_configs=decorator_configs, default_thinking_config=default_thinking_config, default_max_tokens=default_max_tokens)
325
328
  self._api_key = api_key
326
329
  self._aioboto_session = aioboto3.Session(
327
330
  aws_access_key_id=api_key.aws_access_key_id,
@@ -338,7 +341,7 @@ class BedrockLLMClientAsync(BaseLLMClientAsync):
338
341
  messages: list[Content],
339
342
  result_type: ResultType = None,
340
343
  *,
341
- thinking_config: ThinkingConfig = ThinkingConfig(),
344
+ thinking_config: ThinkingConfig | None = None,
342
345
  system_message: str | None = None,
343
346
  max_tokens: int | None = None,
344
347
  tools: list[Tool] | None = None,
@@ -489,6 +492,7 @@ class BedrockLLMClientAsync(BaseLLMClientAsync):
489
492
  self,
490
493
  messages: list[Content],
491
494
  *,
495
+ thinking_config: ThinkingConfig | None = None,
492
496
  system_message: str | None = None,
493
497
  max_tokens: int | None = None,
494
498
  ) -> AsyncIterator[Response]:
@@ -1,6 +1,7 @@
1
1
  import dataclasses
2
2
 
3
3
  from promptbuilder.llm_client.utils import DecoratorConfigs
4
+ from promptbuilder.llm_client.types import ThinkingConfig
4
5
 
5
6
 
6
7
  @dataclasses.dataclass
@@ -9,6 +10,9 @@ class LlmClientConfigs:
9
10
  default_decorator_configs: dict[str, DecoratorConfigs] = dataclasses.field(default_factory=dict)
10
11
  """Dictionary mapping a client name to the default decorator configs to be used for that model."""
11
12
 
13
+ default_thinking_configs: dict[str, ThinkingConfig] = dataclasses.field(default_factory=dict)
14
+ """Dictionary mapping a client name to the default thinking config to be used for that model."""
15
+
12
16
  default_max_tokens: dict[str, int] = dataclasses.field(default_factory=dict)
13
17
  """Dictionary mapping a client name to the default max_tokens value to be used for that model."""
14
18
 
@@ -17,12 +17,13 @@ class GoogleLLMClient(BaseLLMClient):
17
17
  model: str,
18
18
  api_key: str = os.getenv("GOOGLE_API_KEY"),
19
19
  decorator_configs: DecoratorConfigs | None = None,
20
+ default_thinking_config: ThinkingConfig | None = None,
20
21
  default_max_tokens: int | None = None,
21
22
  **kwargs,
22
23
  ):
23
24
  if api_key is None or not isinstance(api_key, str):
24
25
  raise ValueError("To create a google llm client you need to either set the environment variable GOOGLE_API_KEY or pass the api_key in string format")
25
- super().__init__(GoogleLLMClient.PROVIDER, model, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
26
+ super().__init__(GoogleLLMClient.PROVIDER, model, decorator_configs=decorator_configs, default_thinking_config=default_thinking_config, default_max_tokens=default_max_tokens)
26
27
  self._api_key = api_key
27
28
  self.client = Client(api_key=api_key)
28
29
 
@@ -58,7 +59,7 @@ class GoogleLLMClient(BaseLLMClient):
58
59
  messages: list[Content],
59
60
  result_type: ResultType = None,
60
61
  *,
61
- thinking_config: ThinkingConfig = ThinkingConfig(),
62
+ thinking_config: ThinkingConfig | None = None,
62
63
  system_message: str | None = None,
63
64
  max_tokens: int | None = None,
64
65
  tools: list[Tool] | None = None,
@@ -74,10 +75,9 @@ class GoogleLLMClient(BaseLLMClient):
74
75
  tool_config=tool_config,
75
76
  )
76
77
 
77
- if not thinking_config.include_thoughts:
78
- thinking_config = ThinkingConfig(include_thoughts=False, thinking_budget=0)
79
- if thinking_config.include_thoughts or "gemini-2.5-pro-preview-05-06" in self.model:
80
- config.thinking_config = thinking_config
78
+ if thinking_config is None:
79
+ thinking_config = self.default_thinking_config
80
+ config.thinking_config = thinking_config
81
81
 
82
82
  if result_type is None:
83
83
  return self.client.models.generate_content(
@@ -108,6 +108,7 @@ class GoogleLLMClient(BaseLLMClient):
108
108
  self,
109
109
  messages: list[Content],
110
110
  *,
111
+ thinking_config: ThinkingConfig | None = None,
111
112
  system_message: str | None = None,
112
113
  max_tokens: int | None = None,
113
114
  ) -> Iterator[Response]:
@@ -116,8 +117,12 @@ class GoogleLLMClient(BaseLLMClient):
116
117
  config = types.GenerateContentConfig(
117
118
  system_instruction=system_message,
118
119
  max_output_tokens=max_tokens,
119
- thinking_config=ThinkingConfig(include_thoughts=False, thinking_budget=0),
120
120
  )
121
+
122
+ if thinking_config is None:
123
+ thinking_config = self.default_thinking_config
124
+ config.thinking_config = thinking_config
125
+
121
126
  response = self.client.models.generate_content_stream(
122
127
  model=self.model,
123
128
  contents=messages,
@@ -162,12 +167,13 @@ class GoogleLLMClientAsync(BaseLLMClientAsync):
162
167
  model: str,
163
168
  api_key: str = os.getenv("GOOGLE_API_KEY"),
164
169
  decorator_configs: DecoratorConfigs | None = None,
170
+ default_thinking_config: ThinkingConfig | None = None,
165
171
  default_max_tokens: int | None = None,
166
172
  **kwargs,
167
173
  ):
168
174
  if api_key is None or not isinstance(api_key, str):
169
175
  raise ValueError("To create a google llm client you need to either set the environment variable GOOGLE_API_KEY or pass the api_key in string format")
170
- super().__init__(GoogleLLMClientAsync.PROVIDER, model, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
176
+ super().__init__(GoogleLLMClientAsync.PROVIDER, model, decorator_configs=decorator_configs, default_thinking_config=default_thinking_config, default_max_tokens=default_max_tokens)
171
177
  self._api_key = api_key
172
178
  self.client = Client(api_key=api_key)
173
179
 
@@ -180,7 +186,7 @@ class GoogleLLMClientAsync(BaseLLMClientAsync):
180
186
  messages: list[Content],
181
187
  result_type: ResultType = None,
182
188
  *,
183
- thinking_config: ThinkingConfig = ThinkingConfig(),
189
+ thinking_config: ThinkingConfig | None = None,
184
190
  system_message: str | None = None,
185
191
  max_tokens: int | None = None,
186
192
  tools: list[Tool] | None = None,
@@ -196,10 +202,9 @@ class GoogleLLMClientAsync(BaseLLMClientAsync):
196
202
  tool_config=tool_config,
197
203
  )
198
204
 
199
- if not thinking_config.include_thoughts:
200
- thinking_config = ThinkingConfig(include_thoughts=False, thinking_budget=0)
201
- if thinking_config.include_thoughts or "gemini-2.5" in self.model:
202
- config.thinking_config = thinking_config
205
+ if thinking_config is None:
206
+ thinking_config = self.default_thinking_config
207
+ config.thinking_config = thinking_config
203
208
 
204
209
  if result_type is None or result_type == "json":
205
210
  return await self.client.aio.models.generate_content(
@@ -222,6 +227,7 @@ class GoogleLLMClientAsync(BaseLLMClientAsync):
222
227
  self,
223
228
  messages: list[Content],
224
229
  *,
230
+ thinking_config: ThinkingConfig | None = None,
225
231
  system_message: str | None = None,
226
232
  max_tokens: int | None = None,
227
233
  ) -> AsyncIterator[Response]:
@@ -230,8 +236,12 @@ class GoogleLLMClientAsync(BaseLLMClientAsync):
230
236
  config = types.GenerateContentConfig(
231
237
  system_instruction=system_message,
232
238
  max_output_tokens=max_tokens,
233
- thinking_config=ThinkingConfig(include_thoughts=False, thinking_budget=0),
234
239
  )
240
+
241
+ if thinking_config is None:
242
+ thinking_config = self.default_thinking_config
243
+ config.thinking_config = thinking_config
244
+
235
245
  response = await self.client.aio.models.generate_content_stream(
236
246
  model=self.model,
237
247
  contents=messages,
@@ -1,7 +1,7 @@
1
1
  import warnings
2
2
  from itertools import chain
3
3
 
4
- from promptbuilder.llm_client.types import ApiKey, Model
4
+ from promptbuilder.llm_client.types import ApiKey, Model, ThinkingConfig
5
5
  from promptbuilder.llm_client.base_client import BaseLLMClient, BaseLLMClientAsync
6
6
  from promptbuilder.llm_client.config import GLOBAL_CONFIG
7
7
  from promptbuilder.llm_client.utils import DecoratorConfigs
@@ -17,41 +17,45 @@ _memory: dict[tuple[str, ApiKey], BaseLLMClient] = {}
17
17
  _memory_async: dict[tuple[str, ApiKey], BaseLLMClientAsync] = {}
18
18
 
19
19
 
20
- def get_client(full_model_name: str, api_key: ApiKey | None = None, decorator_configs: DecoratorConfigs | None = None, default_max_tokens: int | None = None) -> BaseLLMClient:
20
+ def get_client(
21
+ full_model_name: str,
22
+ api_key: ApiKey | None = None,
23
+ decorator_configs: DecoratorConfigs | None = None,
24
+ default_thinking_config: ThinkingConfig | None = None,
25
+ default_max_tokens: int | None = None,
26
+ ) -> BaseLLMClient:
21
27
  global _memory
22
28
 
29
+ kwargs = {
30
+ "decorator_configs": decorator_configs,
31
+ "default_thinking_config": default_thinking_config,
32
+ "default_max_tokens": default_max_tokens,
33
+ }
34
+ provider_to_client_class: dict[str, type[BaseLLMClient]] = {
35
+ "google": GoogleLLMClient,
36
+ "anthropic": AnthropicLLMClient,
37
+ "openai": OpenaiLLMClient,
38
+ "bedrock": BedrockLLMClient,
39
+ }
23
40
  provider, model = full_model_name.split(":", 1)
24
- match provider:
25
- case "google":
26
- if api_key is None:
27
- client = GoogleLLMClient(model, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
28
- else:
29
- client = GoogleLLMClient(model, api_key, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
30
- case "anthropic":
31
- if api_key is None:
32
- client = AnthropicLLMClient(model, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
33
- else:
34
- client = AnthropicLLMClient(model, api_key, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
35
- case "openai":
36
- if api_key is None:
37
- client = OpenaiLLMClient(model, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
38
- else:
39
- client = OpenaiLLMClient(model, api_key, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
40
- case "bedrock":
41
- if api_key is None:
42
- client = BedrockLLMClient(model, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
43
- else:
44
- client = BedrockLLMClient(model, api_key, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
45
- case _:
46
- if api_key is None:
47
- raise ValueError(f"You should directly provide api_key for this provider: {provider}")
48
- else:
49
- client = AiSuiteLLMClient(full_model_name, api_key, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
41
+ if provider in provider_to_client_class:
42
+ client_class = provider_to_client_class[provider]
43
+ if api_key is None:
44
+ client = client_class(model, **kwargs)
45
+ else:
46
+ client = client_class(model, api_key, **kwargs)
47
+ else:
48
+ if api_key is None:
49
+ raise ValueError(f"You should directly provide api_key for this provider: {provider}")
50
+ else:
51
+ client = AiSuiteLLMClient(full_model_name, api_key, **kwargs)
50
52
 
51
53
  if (full_model_name, client.api_key) in _memory:
52
54
  client = _memory[(full_model_name, client.api_key)]
53
55
  if decorator_configs is not None:
54
56
  client._decorator_configs = decorator_configs
57
+ if default_thinking_config is not None:
58
+ client.default_thinking_config = default_thinking_config
55
59
  if default_max_tokens is not None:
56
60
  client.default_max_tokens = default_max_tokens
57
61
  return client
@@ -60,42 +64,45 @@ def get_client(full_model_name: str, api_key: ApiKey | None = None, decorator_co
60
64
  return client
61
65
 
62
66
 
63
- def get_async_client(full_model_name: str, api_key: ApiKey | None = None, decorator_configs: DecoratorConfigs | None = None, default_max_tokens: int | None = None) -> BaseLLMClientAsync:
67
+ def get_async_client(
68
+ full_model_name: str,
69
+ api_key: ApiKey | None = None,
70
+ decorator_configs: DecoratorConfigs | None = None,
71
+ default_thinking_config: ThinkingConfig | None = None,
72
+ default_max_tokens: int | None = None,
73
+ ) -> BaseLLMClientAsync:
64
74
  global _memory_async
65
75
 
66
-
76
+ kwargs = {
77
+ "decorator_configs": decorator_configs,
78
+ "default_thinking_config": default_thinking_config,
79
+ "default_max_tokens": default_max_tokens,
80
+ }
81
+ provider_to_client_class: dict[str, type[BaseLLMClientAsync]] = {
82
+ "google": GoogleLLMClientAsync,
83
+ "anthropic": AnthropicLLMClientAsync,
84
+ "openai": OpenaiLLMClientAsync,
85
+ "bedrock": BedrockLLMClientAsync,
86
+ }
67
87
  provider, model = full_model_name.split(":", 1)
68
- match provider:
69
- case "google":
70
- if api_key is None:
71
- client = GoogleLLMClientAsync(model, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
72
- else:
73
- client = GoogleLLMClientAsync(model, api_key, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
74
- case "anthropic":
75
- if api_key is None:
76
- client = AnthropicLLMClientAsync(model, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
77
- else:
78
- client = AnthropicLLMClientAsync(model, api_key, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
79
- case "openai":
80
- if api_key is None:
81
- client = OpenaiLLMClientAsync(model, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
82
- else:
83
- client = OpenaiLLMClientAsync(model, api_key, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
84
- case "bedrock":
85
- if api_key is None:
86
- client = BedrockLLMClientAsync(model, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
87
- else:
88
- client = BedrockLLMClientAsync(model, api_key, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
89
- case _:
90
- if api_key is None:
91
- raise ValueError(f"You should directly provide api_key for this provider: {provider}")
92
- else:
93
- client = AiSuiteLLMClientAsync(full_model_name, api_key, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
88
+ if provider in provider_to_client_class:
89
+ client_class = provider_to_client_class[provider]
90
+ if api_key is None:
91
+ client = client_class(model, **kwargs)
92
+ else:
93
+ client = client_class(model, api_key, **kwargs)
94
+ else:
95
+ if api_key is None:
96
+ raise ValueError(f"You should directly provide api_key for this provider: {provider}")
97
+ else:
98
+ client = AiSuiteLLMClientAsync(full_model_name, api_key, **kwargs)
94
99
 
95
100
  if (full_model_name, client.api_key) in _memory_async:
96
101
  client = _memory_async[(full_model_name, client.api_key)]
97
102
  if decorator_configs is not None:
98
103
  client._decorator_configs = decorator_configs
104
+ if default_thinking_config is not None:
105
+ client.default_thinking_config = default_thinking_config
99
106
  if default_max_tokens is not None:
100
107
  client.default_max_tokens = default_max_tokens
101
108
  return client
@@ -130,6 +137,8 @@ def configure(
130
137
  *,
131
138
  decorator_configs: dict[str, DecoratorConfigs] | None = None,
132
139
  update_decorator_configs: dict[str, DecoratorConfigs] | None = None,
140
+ thinking_configs: dict[str, ThinkingConfig] | None = None,
141
+ update_thinking_configs: dict[str, ThinkingConfig] | None = None,
133
142
  max_tokens: dict[str, int] | None = None,
134
143
  update_max_tokens: dict[str, int] | None = None,
135
144
  use_logfire: bool | None = None,
@@ -138,6 +147,10 @@ def configure(
138
147
  warnings.warn("Both 'decorator_configs' and 'update_decorator_configs' were provided. "
139
148
  "'update_decorator_configs' will be ignored.", UserWarning)
140
149
  update_decorator_configs = None
150
+ if thinking_configs is not None and update_thinking_configs is not None:
151
+ warnings.warn("Both 'thinking_configs' and 'update_thinking_configs' were provided. "
152
+ "'update_thinking_configs' will be ignored.", UserWarning)
153
+ update_thinking_configs = None
141
154
  if max_tokens is not None and update_max_tokens is not None:
142
155
  warnings.warn("Both 'max_tokens' and 'update_max_tokens' were provided. "
143
156
  "'update_max_tokens' will be ignored.", UserWarning)
@@ -148,6 +161,11 @@ def configure(
148
161
  if update_decorator_configs is not None:
149
162
  GLOBAL_CONFIG.default_decorator_configs.update(update_decorator_configs)
150
163
 
164
+ if thinking_configs is not None:
165
+ GLOBAL_CONFIG.default_thinking_configs = thinking_configs
166
+ if update_thinking_configs is not None:
167
+ GLOBAL_CONFIG.default_thinking_configs.update(update_thinking_configs)
168
+
151
169
  if max_tokens is not None:
152
170
  GLOBAL_CONFIG.default_max_tokens = max_tokens
153
171
  if update_max_tokens is not None:
@@ -163,6 +181,11 @@ def sync_existing_clients_with_global_config():
163
181
  else:
164
182
  llm_client._decorator_configs = DecoratorConfigs()
165
183
 
184
+ if full_model_name in GLOBAL_CONFIG.default_thinking_configs:
185
+ llm_client.default_thinking_config = GLOBAL_CONFIG.default_thinking_configs[full_model_name]
186
+ else:
187
+ llm_client.default_thinking_config = None
188
+
166
189
  if full_model_name in GLOBAL_CONFIG.default_max_tokens:
167
190
  llm_client.default_max_tokens = GLOBAL_CONFIG.default_max_tokens[full_model_name]
168
191
  else:
@@ -45,12 +45,13 @@ class OpenaiLLMClient(BaseLLMClient):
45
45
  model: str,
46
46
  api_key: str = os.getenv("OPENAI_API_KEY"),
47
47
  decorator_configs: DecoratorConfigs | None = None,
48
+ default_thinking_config: ThinkingConfig | None = None,
48
49
  default_max_tokens: int | None = None,
49
50
  **kwargs,
50
51
  ):
51
52
  if api_key is None or not isinstance(api_key, str):
52
53
  raise ValueError("To create an openai llm client you need to either set the environment variable OPENAI_API_KEY or pass the api_key in string format")
53
- super().__init__(OpenaiLLMClient.PROVIDER, model, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
54
+ super().__init__(OpenaiLLMClient.PROVIDER, model, decorator_configs=decorator_configs, default_thinking_config=default_thinking_config, default_max_tokens=default_max_tokens)
54
55
  self._api_key = api_key
55
56
  self.client = OpenAI(api_key=api_key)
56
57
 
@@ -92,7 +93,10 @@ class OpenaiLLMClient(BaseLLMClient):
92
93
  return openai_messages
93
94
 
94
95
  @staticmethod
95
- def _process_thinking_config(thinking_config: ThinkingConfig) -> dict[str, str]:
96
+ def _process_thinking_config(thinking_config: ThinkingConfig | None) -> dict[str, str]:
97
+ if thinking_config is None:
98
+ return {}
99
+
96
100
  openai_thinking_config = {}
97
101
  if thinking_config.include_thoughts:
98
102
  # openai_thinking_config["summary"] = "auto"
@@ -114,7 +118,7 @@ class OpenaiLLMClient(BaseLLMClient):
114
118
  messages: list[Content],
115
119
  result_type: ResultType = None,
116
120
  *,
117
- thinking_config: ThinkingConfig = ThinkingConfig(),
121
+ thinking_config: ThinkingConfig | None = None,
118
122
  system_message: str | None = None,
119
123
  max_tokens: int | None = None,
120
124
  tools: list[Tool] | None = None,
@@ -131,6 +135,8 @@ class OpenaiLLMClient(BaseLLMClient):
131
135
  "input": openai_messages,
132
136
  }
133
137
 
138
+ if thinking_config is None:
139
+ thinking_config = self.default_thinking_config
134
140
  openai_kwargs.update(OpenaiLLMClient._process_thinking_config(thinking_config))
135
141
 
136
142
  if tools is not None:
@@ -222,6 +228,7 @@ class OpenaiLLMClient(BaseLLMClient):
222
228
  self,
223
229
  messages: list[Content],
224
230
  *,
231
+ thinking_config: ThinkingConfig | None = None,
225
232
  system_message: str | None = None,
226
233
  max_tokens: int | None = None,
227
234
  ) -> Iterator[Response]:
@@ -235,6 +242,11 @@ class OpenaiLLMClient(BaseLLMClient):
235
242
  "max_output_tokens": max_tokens,
236
243
  "input": openai_messages,
237
244
  }
245
+
246
+ if thinking_config is None:
247
+ thinking_config = self.default_thinking_config
248
+ openai_kwargs.update(OpenaiLLMClient._process_thinking_config(thinking_config))
249
+
238
250
  response = self.client.responses.create(**openai_kwargs, stream=True)
239
251
  return OpenaiStreamIterator(response)
240
252
 
@@ -298,12 +310,13 @@ class OpenaiLLMClientAsync(BaseLLMClientAsync):
298
310
  model: str,
299
311
  api_key: str = os.getenv("OPENAI_API_KEY"),
300
312
  decorator_configs: DecoratorConfigs | None = None,
313
+ default_thinking_config: ThinkingConfig | None = None,
301
314
  default_max_tokens: int | None = None,
302
315
  **kwargs,
303
316
  ):
304
317
  if api_key is None or not isinstance(api_key, str):
305
318
  raise ValueError("To create an openai llm client you need to either set the environment variable OPENAI_API_KEY or pass the api_key in string format")
306
- super().__init__(OpenaiLLMClientAsync.PROVIDER, model, decorator_configs=decorator_configs, default_max_tokens=default_max_tokens)
319
+ super().__init__(OpenaiLLMClientAsync.PROVIDER, model, decorator_configs=decorator_configs, default_thinking_config=default_thinking_config, default_max_tokens=default_max_tokens)
307
320
  self._api_key = api_key
308
321
  self.client = AsyncOpenAI(api_key=api_key)
309
322
 
@@ -315,7 +328,8 @@ class OpenaiLLMClientAsync(BaseLLMClientAsync):
315
328
  self,
316
329
  messages: list[Content],
317
330
  result_type: ResultType = None,
318
- thinking_config: ThinkingConfig = ThinkingConfig(),
331
+ *,
332
+ thinking_config: ThinkingConfig | None = None,
319
333
  system_message: str | None = None,
320
334
  max_tokens: int | None = None,
321
335
  tools: list[Tool] | None = None,
@@ -339,6 +353,8 @@ class OpenaiLLMClientAsync(BaseLLMClientAsync):
339
353
  "input": openai_messages,
340
354
  }
341
355
 
356
+ if thinking_config is None:
357
+ thinking_config = self.default_thinking_config
342
358
  openai_kwargs.update(OpenaiLLMClient._process_thinking_config(thinking_config))
343
359
 
344
360
  if tools is not None:
@@ -377,7 +393,7 @@ class OpenaiLLMClientAsync(BaseLLMClientAsync):
377
393
  elif tool_choice_mode == "ANY":
378
394
  openai_kwargs["tool_choice"] = "required"
379
395
 
380
- if result_type is None or result_type == "text":
396
+ if result_type is None or result_type == "json":
381
397
  response = await self.client.responses.create(**openai_kwargs)
382
398
 
383
399
  parts: list[Part] = []
@@ -423,11 +439,14 @@ class OpenaiLLMClientAsync(BaseLLMClientAsync):
423
439
  ),
424
440
  parsed=parsed,
425
441
  )
442
+ else:
443
+ raise ValueError(f"Unsupported result_type: {result_type}. Supported types are: None, 'json', or a Pydantic model.")
426
444
 
427
445
  async def create_stream(
428
446
  self,
429
447
  messages: list[Content],
430
448
  *,
449
+ thinking_config: ThinkingConfig | None = None,
431
450
  system_message: str | None = None,
432
451
  max_tokens: int | None = None,
433
452
  ) -> AsyncIterator[Response]:
@@ -448,6 +467,11 @@ class OpenaiLLMClientAsync(BaseLLMClientAsync):
448
467
  "max_output_tokens": max_tokens,
449
468
  "input": openai_messages,
450
469
  }
470
+
471
+ if thinking_config is None:
472
+ thinking_config = self.default_thinking_config
473
+ openai_kwargs.update(OpenaiLLMClient._process_thinking_config(thinking_config))
474
+
451
475
  response = await self.client.responses.create(**openai_kwargs, stream=True)
452
476
  return OpenaiStreamIteratorAsync(response)
453
477
 
@@ -14,6 +14,8 @@ type Json = list | dict
14
14
  type JsonType = Literal["string", "number", "integer", "boolean", "array", "object"]
15
15
  PydanticStructure = TypeVar("PydanticStructure", bound=BaseModel)
16
16
 
17
+ type ResultType = Literal["json"] | type[PydanticStructure] | None
18
+
17
19
 
18
20
  class CustomApiKey(ABC):
19
21
  @abstractmethod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: promptbuilder
3
- Version: 0.4.16
3
+ Version: 0.4.18
4
4
  Summary: Library for building prompts for LLMs
5
5
  Home-page: https://github.com/kapulkin/promptbuilder
6
6
  Author: Kapulkin Stanislav
@@ -14,7 +14,7 @@ License-File: LICENSE
14
14
  Requires-Dist: pydantic
15
15
  Requires-Dist: pytest
16
16
  Requires-Dist: aisuite-async
17
- Requires-Dist: google-genai
17
+ Requires-Dist: google-genai<=1.21.0,>=1.4.0
18
18
  Requires-Dist: anthropic
19
19
  Requires-Dist: openai
20
20
  Requires-Dist: aioboto3
@@ -0,0 +1,24 @@
1
+ promptbuilder/__init__.py,sha256=o_NdXl7NppM399-fy5VGfYkSN8iYDAaFAwJNhdkW3bI,56
2
+ promptbuilder/prompt_builder.py,sha256=kK6WHr2umYmsanYb2fQVxqEajs_dzGPXRulTo40g36E,12428
3
+ promptbuilder/agent/__init__.py,sha256=qG4Jq4wbmCH5NKLOX6ZMtZ7lFURhJXf464BntR-u5rU,56
4
+ promptbuilder/agent/agent.py,sha256=dVu251C1r9w5LS2P_shsIRH9tFz1Jq93MDv3Uu41_4E,9274
5
+ promptbuilder/agent/context.py,sha256=CVw715vFrhfvddQmRNy4A1U87GsZyIKj9Xu4SCidbc0,1120
6
+ promptbuilder/agent/tool.py,sha256=VDbIHK3_Q62Ei7hwLF7nIgHq-PTMKnv1NSjHpDYkUZE,2651
7
+ promptbuilder/agent/utils.py,sha256=vTkphKw04v_QDIJtoB2JKK0RGY6iI1t_0LbmuStunzI,356
8
+ promptbuilder/llm_client/__init__.py,sha256=2tPVYqwNdwTRdIg4Pde6Nc259FJvy70gjEj1N2oqNrc,458
9
+ promptbuilder/llm_client/aisuite_client.py,sha256=aMqg05zefzck9Lz7pm7jZoKFdzr_ymFYhrAjZtzdHlQ,15561
10
+ promptbuilder/llm_client/anthropic_client.py,sha256=JeTVC26ahuJJT4G_3Bsoc4TqLzVDPuJpJiCRxTALnqA,26146
11
+ promptbuilder/llm_client/base_client.py,sha256=GS-Qb20WtZnljmEUD2ibhTHDet7exoyhQ0_mGNAEKlg,24219
12
+ promptbuilder/llm_client/bedrock_client.py,sha256=W4wFW7Vbv-nsT2ReyhJ4YIPSTXxE_4S83352vJDSmDk,25772
13
+ promptbuilder/llm_client/config.py,sha256=exQEm35wp7lK5SfXNpN5H9VZEb2LVa4pyZ-cxGt1U-U,1124
14
+ promptbuilder/llm_client/google_client.py,sha256=heyeACt_0bVP3p4pCQeWR92MhCsyNk844kWJ_0MVTfg,9830
15
+ promptbuilder/llm_client/logfire_decorators.py,sha256=un_QnIekypOEcqTZ5v1y9pwijGnF95xwnwKO5rFSHVY,9667
16
+ promptbuilder/llm_client/main.py,sha256=k4JTyKq2atNyFtI1bjjqXEnGSEugj4xk0AJEvHJiMig,8310
17
+ promptbuilder/llm_client/openai_client.py,sha256=5yvjp-Zzp4JsBC9_ffSb1A9-iMG4Lu2B2et2CdtK9R0,22864
18
+ promptbuilder/llm_client/types.py,sha256=2E-aPRb5uAkLFJocmjF1Lh2aQRq9r8a5JRIw-duHfjA,7460
19
+ promptbuilder/llm_client/utils.py,sha256=79lvSppjrrItHB5MIozbp_5Oq7TsOK4Qzt9Ae3XMLFw,7624
20
+ promptbuilder-0.4.18.dist-info/licenses/LICENSE,sha256=fqXmInzgsvEOIaKSBgcrwKyYCGYF0MKErJ0YivtODcc,1096
21
+ promptbuilder-0.4.18.dist-info/METADATA,sha256=bbynjS91gKgHZKKCzw1VgD2FgI54Orn5OLRUqZJsQmA,3738
22
+ promptbuilder-0.4.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
+ promptbuilder-0.4.18.dist-info/top_level.txt,sha256=UBVcYn4UgrPy3O3fmmnPEU_kieuplBMgheetIMei4EI,14
24
+ promptbuilder-0.4.18.dist-info/RECORD,,
@@ -1,24 +0,0 @@
1
- promptbuilder/__init__.py,sha256=o_NdXl7NppM399-fy5VGfYkSN8iYDAaFAwJNhdkW3bI,56
2
- promptbuilder/prompt_builder.py,sha256=kK6WHr2umYmsanYb2fQVxqEajs_dzGPXRulTo40g36E,12428
3
- promptbuilder/agent/__init__.py,sha256=qG4Jq4wbmCH5NKLOX6ZMtZ7lFURhJXf464BntR-u5rU,56
4
- promptbuilder/agent/agent.py,sha256=dVu251C1r9w5LS2P_shsIRH9tFz1Jq93MDv3Uu41_4E,9274
5
- promptbuilder/agent/context.py,sha256=CVw715vFrhfvddQmRNy4A1U87GsZyIKj9Xu4SCidbc0,1120
6
- promptbuilder/agent/tool.py,sha256=VDbIHK3_Q62Ei7hwLF7nIgHq-PTMKnv1NSjHpDYkUZE,2651
7
- promptbuilder/agent/utils.py,sha256=vTkphKw04v_QDIJtoB2JKK0RGY6iI1t_0LbmuStunzI,356
8
- promptbuilder/llm_client/__init__.py,sha256=2tPVYqwNdwTRdIg4Pde6Nc259FJvy70gjEj1N2oqNrc,458
9
- promptbuilder/llm_client/aisuite_client.py,sha256=Yvg2qnW182ksDoKKDeIpfx_oJTaZMSpwMBJ3KGh9Eek,15271
10
- promptbuilder/llm_client/anthropic_client.py,sha256=nt5kxqNXfsIkzaeBDm1eWDRC07mrLALALRAMbVmpP1Y,24309
11
- promptbuilder/llm_client/base_client.py,sha256=wmRSm5fhaoGCFIktkOTVJqDl78g5LVJI6JmzhSWk5JY,23174
12
- promptbuilder/llm_client/bedrock_client.py,sha256=nbyHQxLKl1nONNSnQtF9euj2kwBJb147fQetjU17Z38,25444
13
- promptbuilder/llm_client/config.py,sha256=Qgk9XeBGJdElY6cx91yz_e7eZJ_Ced6vAI9ByV_6lIA,858
14
- promptbuilder/llm_client/google_client.py,sha256=3w7g38r2NAP780nLZxegc3qQkIWEVUTRuQH6VvtKAEg,9606
15
- promptbuilder/llm_client/logfire_decorators.py,sha256=un_QnIekypOEcqTZ5v1y9pwijGnF95xwnwKO5rFSHVY,9667
16
- promptbuilder/llm_client/main.py,sha256=-naNScxlU-guvoPYJpThYNupGkl-ZQoViCGExDIR1gA,8566
17
- promptbuilder/llm_client/openai_client.py,sha256=y71LmgX0YzMJfj2yfao0rnbcbInjfOOrOj-twfAQk88,21696
18
- promptbuilder/llm_client/types.py,sha256=bb83O4YakOC8-JPk0W1HFUxEk_GJCpV_XPDJIAe890A,7390
19
- promptbuilder/llm_client/utils.py,sha256=79lvSppjrrItHB5MIozbp_5Oq7TsOK4Qzt9Ae3XMLFw,7624
20
- promptbuilder-0.4.16.dist-info/licenses/LICENSE,sha256=fqXmInzgsvEOIaKSBgcrwKyYCGYF0MKErJ0YivtODcc,1096
21
- promptbuilder-0.4.16.dist-info/METADATA,sha256=dT1ONqbFaFJLKuRH5c0eAom4TyL2IT2u56dj4ES1OAw,3722
22
- promptbuilder-0.4.16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- promptbuilder-0.4.16.dist-info/top_level.txt,sha256=UBVcYn4UgrPy3O3fmmnPEU_kieuplBMgheetIMei4EI,14
24
- promptbuilder-0.4.16.dist-info/RECORD,,