waldiez 0.5.9__py3-none-any.whl → 0.6.0__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.

Potentially problematic release.


This version of waldiez might be problematic. Click here for more details.

Files changed (109) hide show
  1. waldiez/_version.py +1 -1
  2. waldiez/cli.py +113 -24
  3. waldiez/exporting/agent/exporter.py +9 -6
  4. waldiez/exporting/agent/extras/captain_agent_extras.py +44 -7
  5. waldiez/exporting/agent/extras/group_manager_agent_extas.py +6 -1
  6. waldiez/exporting/agent/extras/handoffs/after_work.py +1 -0
  7. waldiez/exporting/agent/extras/handoffs/available.py +1 -0
  8. waldiez/exporting/agent/extras/handoffs/condition.py +3 -1
  9. waldiez/exporting/agent/extras/handoffs/handoff.py +1 -0
  10. waldiez/exporting/agent/extras/handoffs/target.py +1 -0
  11. waldiez/exporting/agent/termination.py +1 -0
  12. waldiez/exporting/chats/utils/common.py +25 -23
  13. waldiez/exporting/core/__init__.py +0 -2
  14. waldiez/exporting/core/constants.py +3 -1
  15. waldiez/exporting/core/context.py +13 -13
  16. waldiez/exporting/core/extras/serializer.py +12 -10
  17. waldiez/exporting/core/protocols.py +0 -141
  18. waldiez/exporting/core/result.py +5 -5
  19. waldiez/exporting/core/types.py +1 -0
  20. waldiez/exporting/core/utils/llm_config.py +2 -2
  21. waldiez/exporting/flow/execution_generator.py +1 -0
  22. waldiez/exporting/flow/merger.py +2 -2
  23. waldiez/exporting/flow/orchestrator.py +1 -0
  24. waldiez/exporting/flow/utils/common.py +3 -3
  25. waldiez/exporting/flow/utils/importing.py +1 -0
  26. waldiez/exporting/flow/utils/logging.py +7 -80
  27. waldiez/exporting/tools/exporter.py +5 -0
  28. waldiez/exporting/tools/factory.py +4 -0
  29. waldiez/exporting/tools/processor.py +5 -1
  30. waldiez/io/__init__.py +3 -1
  31. waldiez/io/_ws.py +15 -5
  32. waldiez/io/models/content/image.py +1 -0
  33. waldiez/io/models/user_input.py +4 -4
  34. waldiez/io/models/user_response.py +1 -0
  35. waldiez/io/mqtt.py +1 -1
  36. waldiez/io/structured.py +98 -45
  37. waldiez/io/utils.py +17 -11
  38. waldiez/io/ws.py +10 -12
  39. waldiez/logger.py +180 -63
  40. waldiez/models/agents/agent/agent.py +2 -1
  41. waldiez/models/agents/agent/update_system_message.py +0 -2
  42. waldiez/models/agents/doc_agent/doc_agent.py +8 -1
  43. waldiez/models/chat/chat.py +1 -0
  44. waldiez/models/chat/chat_data.py +0 -2
  45. waldiez/models/common/base.py +2 -0
  46. waldiez/models/common/dict_utils.py +169 -40
  47. waldiez/models/common/handoff.py +2 -0
  48. waldiez/models/common/method_utils.py +2 -0
  49. waldiez/models/flow/flow.py +6 -6
  50. waldiez/models/flow/info.py +5 -1
  51. waldiez/models/model/_llm.py +31 -14
  52. waldiez/models/model/model.py +4 -1
  53. waldiez/models/model/model_data.py +18 -5
  54. waldiez/models/tool/predefined/_config.py +5 -1
  55. waldiez/models/tool/predefined/_duckduckgo.py +4 -0
  56. waldiez/models/tool/predefined/_email.py +477 -0
  57. waldiez/models/tool/predefined/_google.py +4 -1
  58. waldiez/models/tool/predefined/_perplexity.py +4 -1
  59. waldiez/models/tool/predefined/_searxng.py +4 -1
  60. waldiez/models/tool/predefined/_tavily.py +4 -1
  61. waldiez/models/tool/predefined/_wikipedia.py +5 -2
  62. waldiez/models/tool/predefined/_youtube.py +4 -1
  63. waldiez/models/tool/predefined/protocol.py +3 -0
  64. waldiez/models/tool/tool.py +22 -4
  65. waldiez/models/waldiez.py +12 -0
  66. waldiez/runner.py +37 -54
  67. waldiez/running/__init__.py +6 -0
  68. waldiez/running/base_runner.py +381 -363
  69. waldiez/running/environment.py +1 -0
  70. waldiez/running/exceptions.py +9 -0
  71. waldiez/running/post_run.py +10 -4
  72. waldiez/running/pre_run.py +199 -66
  73. waldiez/running/protocol.py +21 -101
  74. waldiez/running/run_results.py +1 -1
  75. waldiez/running/standard_runner.py +83 -276
  76. waldiez/running/step_by_step/__init__.py +46 -0
  77. waldiez/running/step_by_step/breakpoints_mixin.py +512 -0
  78. waldiez/running/step_by_step/command_handler.py +151 -0
  79. waldiez/running/step_by_step/events_processor.py +199 -0
  80. waldiez/running/step_by_step/step_by_step_models.py +541 -0
  81. waldiez/running/step_by_step/step_by_step_runner.py +750 -0
  82. waldiez/running/subprocess_runner/__base__.py +279 -0
  83. waldiez/running/subprocess_runner/__init__.py +16 -0
  84. waldiez/running/subprocess_runner/_async_runner.py +362 -0
  85. waldiez/running/subprocess_runner/_sync_runner.py +456 -0
  86. waldiez/running/subprocess_runner/runner.py +570 -0
  87. waldiez/running/timeline_processor.py +1 -1
  88. waldiez/running/utils.py +492 -3
  89. waldiez/utils/version.py +2 -6
  90. waldiez/ws/__init__.py +71 -0
  91. waldiez/ws/__main__.py +15 -0
  92. waldiez/ws/_file_handler.py +199 -0
  93. waldiez/ws/_mock.py +74 -0
  94. waldiez/ws/cli.py +235 -0
  95. waldiez/ws/client_manager.py +851 -0
  96. waldiez/ws/errors.py +416 -0
  97. waldiez/ws/models.py +988 -0
  98. waldiez/ws/reloader.py +363 -0
  99. waldiez/ws/server.py +508 -0
  100. waldiez/ws/session_manager.py +393 -0
  101. waldiez/ws/session_stats.py +83 -0
  102. waldiez/ws/utils.py +410 -0
  103. {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/METADATA +105 -96
  104. {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/RECORD +108 -83
  105. waldiez/running/patch_io_stream.py +0 -210
  106. {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/WHEEL +0 -0
  107. {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/entry_points.txt +0 -0
  108. {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/licenses/LICENSE +0 -0
  109. {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/licenses/NOTICE.md +0 -0
@@ -2,63 +2,192 @@
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
  """Dictionary related utilities."""
4
4
 
5
+ import ast
6
+ import json
5
7
  import re
6
- from typing import Any
8
+ from typing import Any, Union
7
9
 
8
10
  BOOL_VALUES = {"true", "false"}
9
11
  NULL_VALUES = {"none", "null", "nil", "undefined"}
10
12
 
11
13
 
14
+ def _strip_outer_quotes(value: str) -> str:
15
+ """Remove outer quotes from a string if present."""
16
+ value_stripped = value.strip()
17
+ if (value_stripped.startswith('"') and value_stripped.endswith('"')) or (
18
+ value_stripped.startswith("'") and value_stripped.endswith("'")
19
+ ):
20
+ return value_stripped[1:-1]
21
+ return value_stripped
22
+
23
+
24
+ def _detect_null_or_boolean(value: str) -> Union[None, bool, str]:
25
+ """
26
+ Detect null values or booleans.
27
+
28
+ Parameters
29
+ ----------
30
+ value : str
31
+ The string value to check.
32
+
33
+ Returns
34
+ -------
35
+ Union[None, bool, str]
36
+ None for null values, bool for booleans, or original string if neither.
37
+ """
38
+ value_lower = value.lower()
39
+
40
+ if value_lower in NULL_VALUES:
41
+ return None
42
+ if value_lower in BOOL_VALUES:
43
+ return value_lower == "true"
44
+
45
+ return value
46
+
47
+
48
+ def _detect_numeric_type(value: str) -> Union[int, float, str]:
49
+ """
50
+ Detect if string represents an integer or float.
51
+
52
+ Parameters
53
+ ----------
54
+ value : str
55
+ The string value to check.
56
+
57
+ Returns
58
+ -------
59
+ Union[int, float, str]
60
+ int for integers, float for floats, or original string if neither.
61
+ """
62
+ # Check for integer first (more specific)
63
+ if re.fullmatch(r"[-+]?\d+", value):
64
+ return int(value)
65
+
66
+ # Try float conversion
67
+ try:
68
+ return float(value)
69
+ except ValueError:
70
+ return value
71
+
72
+
73
+ def _detect_container_type(
74
+ value: str,
75
+ ) -> Union[dict[str, Any], list[Any], tuple[Any], set[Any], str]:
76
+ """
77
+ Detect if string represents a container type (dict, list, tuple, set).
78
+
79
+ Parameters
80
+ ----------
81
+ value : str
82
+ The string value to check.
83
+
84
+ Returns
85
+ -------
86
+ Union[dict[str, Any], list[Any], tuple[Any], set[Any], str]
87
+ Parsed container or original string if not a container.
88
+ """
89
+ if not (value[0] in "{[(" and value[-1] in "}])"):
90
+ return value
91
+
92
+ # Handle empty containers
93
+ if value in ("()", "[]", "{}"):
94
+ return ast.literal_eval(value)
95
+
96
+ # Try JSON first (expects double quotes)
97
+ try:
98
+ parsed = json.loads(value)
99
+ if isinstance(parsed, (dict, list)):
100
+ return parsed # pyright: ignore
101
+ except (json.JSONDecodeError, TypeError):
102
+ pass
103
+
104
+ # Fallback: Python literal (handles single quotes, tuples, sets)
105
+ try:
106
+ parsed = ast.literal_eval(value)
107
+ if isinstance(parsed, (dict, list, tuple, set)):
108
+ return parsed # pyright: ignore
109
+ except (ValueError, SyntaxError):
110
+ pass
111
+
112
+ return value
113
+
114
+
115
+ def _convert_string_value(value: str) -> Any:
116
+ """
117
+ Convert a string value to its detected type.
118
+
119
+ Parameters
120
+ ----------
121
+ value : str
122
+ The string value to convert.
123
+
124
+ Returns
125
+ -------
126
+ Any
127
+ The converted value or original string if no conversion possible.
128
+ """
129
+ # Strip outer quotes if present
130
+ cleaned_value = _strip_outer_quotes(value)
131
+
132
+ # Skip conversion for empty strings
133
+ if not cleaned_value:
134
+ return value
135
+
136
+ # Try conversions in order of specificity
137
+
138
+ # 1. Container types (most specific structure)
139
+ container_result = _detect_container_type(cleaned_value)
140
+ if container_result != cleaned_value:
141
+ return container_result
142
+
143
+ # 2. Null and boolean values
144
+ null_bool_result = _detect_null_or_boolean(cleaned_value)
145
+ if null_bool_result != cleaned_value:
146
+ return null_bool_result
147
+
148
+ # 3. Numeric types
149
+ numeric_result = _detect_numeric_type(cleaned_value)
150
+ if numeric_result != cleaned_value:
151
+ return numeric_result
152
+
153
+ # 4. Keep as string if no conversion succeeded
154
+ return cleaned_value
155
+
156
+
12
157
  def update_dict(original: dict[str, Any]) -> dict[str, Any]:
13
158
  """
14
- Try to determine the type of the dictionary values.
159
+ Convert string values in a dictionary to their detected types.
160
+
161
+ Automatically detects and converts strings that represent:
162
+ - Boolean values (true/false)
163
+ - Null values (none/null/nil/undefined)
164
+ - Integers and floats
165
+ - Container types (lists, dicts, tuples, sets)
15
166
 
16
167
  Parameters
17
168
  ----------
18
169
  original : dict[str, Any]
19
- The original dictionary.
170
+ The original dictionary with potentially string-encoded values.
20
171
 
21
172
  Returns
22
173
  -------
23
174
  dict[str, Any]
24
- The updated dictionary with values converted to the detected types.
175
+ A new dictionary with string values converted to their detected types.
176
+ Non-string values are preserved unchanged.
177
+
178
+ Examples
179
+ --------
180
+ >>> data = {"count": "42", "active": "true", "tags": "['a', 'b']"}
181
+ >>> update_dict(data)
182
+ {"count": 42, "active": True, "tags": ['a', 'b']}
25
183
  """
26
- new_dict: dict[str, Any] = {}
184
+ converted_dict: dict[str, Any] = {}
27
185
 
28
186
  for key, value in original.items():
29
- # Skip conversion if already the desired type
30
- if not isinstance(value, str):
31
- new_dict[key] = value
32
- continue
33
-
34
- value_stripped = value.strip()
35
- if (
36
- value_stripped.startswith('"') and value_stripped.endswith('"')
37
- ) or (value_stripped.startswith("'") and value_stripped.endswith("'")):
38
- value_stripped = value_stripped[1:-1]
39
- if not value_stripped: # Empty or whitespace-only
40
- new_dict[key] = value
41
- continue
42
-
43
- value_lower = value_stripped.lower()
44
-
45
- # Check for None/null
46
- if value_lower in NULL_VALUES:
47
- new_dict[key] = None
48
- # Check for boolean
49
- elif value_lower in BOOL_VALUES:
50
- new_dict[key] = value_lower == "true"
51
- # Check for integer
52
- elif re.fullmatch(r"[-+]?\d+", value_stripped):
53
- new_dict[key] = int(value_stripped)
54
- # Check for float
187
+ # Only process string values
188
+ if isinstance(value, str):
189
+ converted_dict[key] = _convert_string_value(value)
55
190
  else:
56
- try:
57
- # This handles floats, scientific notation, etc.
58
- float_val = float(value_stripped)
59
- new_dict[key] = float_val
60
- except ValueError:
61
- # Keep as string if conversion fails
62
- new_dict[key] = value_stripped
63
-
64
- return new_dict
191
+ converted_dict[key] = value
192
+
193
+ return converted_dict
@@ -343,6 +343,7 @@ class WaldiezTransitionAvailability(WaldiezBase):
343
343
  value: str = ""
344
344
 
345
345
 
346
+ # noinspection PyTypeHints
346
347
  class WaldiezLLMBasedTransition(WaldiezBase):
347
348
  """Condition wrapper for LLM conditions."""
348
349
 
@@ -351,6 +352,7 @@ class WaldiezLLMBasedTransition(WaldiezBase):
351
352
  available: WaldiezTransitionAvailability
352
353
 
353
354
 
355
+ # noinspection PyTypeHints
354
356
  class WaldiezContextBasedTransition(WaldiezBase):
355
357
  """Condition wrapper for context conditions."""
356
358
 
@@ -123,11 +123,13 @@ def _extract_imports_from_ast(code_string: str) -> tuple[list[str], list[str]]:
123
123
 
124
124
  for node in ast.walk(tree):
125
125
  if isinstance(node, (ast.Import, ast.ImportFrom)):
126
+ # noinspection PyTypeChecker
126
127
  full_import_statement = ast.get_source_segment(code_string, node)
127
128
  if not full_import_statement: # pragma: no cover
128
129
  continue
129
130
  full_import_statement = full_import_statement.strip()
130
131
 
132
+ # noinspection PyTypeChecker
131
133
  module_name = _extract_module_name(node)
132
134
  if not module_name: # pragma: no cover
133
135
  continue
@@ -397,7 +397,7 @@ class WaldiezFlow(WaldiezBase):
397
397
  If no group manager is found.
398
398
  """
399
399
  for agent in self.data.agents.groupManagerAgents:
400
- if agent.data.parent_id is None:
400
+ if agent.data.parent_id is None: # pragma: no branch
401
401
  return agent
402
402
  raise ValueError("No group manager found.")
403
403
 
@@ -431,16 +431,16 @@ class WaldiezFlow(WaldiezBase):
431
431
  to_root_manager: WaldiezChat | None = None
432
432
  root_manager: WaldiezGroupManager = self.get_root_group_manager()
433
433
  for chat in self.data.chats:
434
- if chat.target == root_manager.id:
434
+ if chat.target == root_manager.id: # pragma: no branch
435
435
  # check if the source is a user agent
436
436
  source = self.get_agent_by_id(chat.source)
437
- if source.is_user:
437
+ if source.is_user: # pragma: no branch
438
438
  user_agent = source
439
439
  to_root_manager = chat
440
440
  break
441
- if not to_root_manager:
441
+ if not to_root_manager: # pragma: no cover
442
442
  return []
443
- if not user_agent:
443
+ if not user_agent: # pragma: no cover
444
444
  return []
445
445
  return [
446
446
  {
@@ -572,7 +572,7 @@ class WaldiezFlow(WaldiezBase):
572
572
  - If the model IDs are not unique.
573
573
  - If the tool IDs are not unique.
574
574
  """
575
- if member.is_group_manager:
575
+ if member.is_group_manager: # pragma: no cover
576
576
  raise ValueError(
577
577
  "In single agent mode, the agent must not be a group manager."
578
578
  )
@@ -18,6 +18,8 @@ class WaldiezAgentInfo(WaldiezBase):
18
18
 
19
19
  Attributes
20
20
  ----------
21
+ id : str
22
+ The ID of the agent.
21
23
  name : str
22
24
  The name of the agent.
23
25
  human_input_mode : WaldiezAgentHumanInputMode
@@ -27,6 +29,7 @@ class WaldiezAgentInfo(WaldiezBase):
27
29
  The type of the agent (e.g., "user", "assistant").
28
30
  """
29
31
 
32
+ id: Annotated[str, Field(description="ID of the agent")]
30
33
  name: Annotated[str, Field(description="Name of the agent")]
31
34
  human_input_mode: Annotated[
32
35
  WaldiezAgentHumanInputMode,
@@ -53,7 +56,7 @@ class WaldiezFlowInfo(WaldiezBase):
53
56
  description="List of chat participants with their info",
54
57
  default_factory=list[WaldiezAgentInfo],
55
58
  ),
56
- ]
59
+ ] = []
57
60
 
58
61
  @classmethod
59
62
  def create(
@@ -77,6 +80,7 @@ class WaldiezFlowInfo(WaldiezBase):
77
80
  for agent in agents:
78
81
  participants.append(
79
82
  WaldiezAgentInfo(
83
+ id=agent.id,
80
84
  name=agent_names.get(agent.id, agent.name),
81
85
  human_input_mode=agent.data.human_input_mode,
82
86
  agent_type=agent.agent_type,
@@ -31,9 +31,10 @@ if TYPE_CHECKING:
31
31
  from .model import WaldiezModel
32
32
 
33
33
 
34
+ # noinspection PyUnusedLocal
34
35
  def get_llm_requirements(
35
36
  model: "WaldiezModel",
36
- ag2_version: str,
37
+ ag2_version: str, # pylint: disable=unused-argument
37
38
  ) -> set[str]:
38
39
  """Get the LLM requirements for the model.
39
40
 
@@ -50,9 +51,16 @@ def get_llm_requirements(
50
51
  The set of LLM requirements for the model.
51
52
  """
52
53
  requirements: set[str] = {
54
+ # f"ag2[rag]=={ag2_version}",
55
+ "chromadb>=0.5,<2",
56
+ "docling>=2.15.1,<3",
57
+ "selenium>=4.28.1,<5",
58
+ "webdriver-manager==4.0.2",
53
59
  "llama-index",
54
60
  "llama-index-core",
55
- f"ag2[rag]=={ag2_version}",
61
+ "llama-index-embeddings-huggingface",
62
+ "llama-index-llms-langchain",
63
+ "llama-index-vector-stores-chroma",
56
64
  }
57
65
  match model.data.api_type:
58
66
  case "openai":
@@ -87,7 +95,7 @@ def get_llm_requirements(
87
95
  )
88
96
  case "together":
89
97
  requirements.add("llama-index-llms-together")
90
- case "other":
98
+ case "other": # pragma: no cover
91
99
  # openai compatible LLMs
92
100
  requirements.add("llama-index-llms-openai-like")
93
101
 
@@ -147,6 +155,7 @@ def get_llm_imports(model: "WaldiezModel") -> set[str]:
147
155
  case "other":
148
156
  return {"from llama_index.llms.openai_like import OpenAILike"}
149
157
  case _: # pragma: no cover
158
+ # noinspection PyUnreachableCode
150
159
  raise ValueError(f"Unsupported API type: {model.data.api_type}")
151
160
 
152
161
 
@@ -194,6 +203,7 @@ def get_llm_arg(model: "WaldiezModel") -> tuple[str, str]:
194
203
  case "other":
195
204
  return do_other_llm(model)
196
205
  case _: # pragma: no cover
206
+ # noinspection PyUnreachableCode
197
207
  raise ValueError(f"Unsupported API type: {model.data.api_type}")
198
208
 
199
209
 
@@ -279,9 +289,9 @@ def do_azure_llm(model: "WaldiezModel") -> tuple[str, str]:
279
289
  f' model="{model.name}",\n'
280
290
  f" temperature={temperature},\n"
281
291
  )
282
- if model.data.base_url:
292
+ if model.data.base_url: # pragma: no branch
283
293
  arg += f' azure_endpoint="{model.data.base_url}",\n'
284
- if model.data.api_version:
294
+ if model.data.api_version: # pragma: no branch
285
295
  arg += f' api_version="{model.data.api_version}",\n'
286
296
  arg += ")"
287
297
  before = ""
@@ -322,11 +332,11 @@ def do_bedrock_llm(model: "WaldiezModel") -> tuple[str, str]:
322
332
  aws_access_key_id = model.data.aws.access_key or ""
323
333
  aws_region = model.data.aws.region or ""
324
334
  arg = f'BedrockConverse(\n model="{model.name}",\n'
325
- if profile_name:
335
+ if profile_name: # pragma: no branch
326
336
  arg += f' profile_name="{profile_name}",\n'
327
- if aws_access_key_id:
337
+ if aws_access_key_id: # pragma: no branch
328
338
  arg += f' aws_access_key_id="{aws_access_key_id}",\n'
329
- if aws_region:
339
+ if aws_region: # pragma: no branch
330
340
  arg += f' region_name="{aws_region}",\n'
331
341
  arg += ")"
332
342
  before = ""
@@ -347,11 +357,11 @@ def do_cohere_llm(model: "WaldiezModel") -> tuple[str, str]:
347
357
  A tuple containing the LLM argument string and any content before it.
348
358
  """
349
359
  arg = f'Cohere(\n model="{model.name}",\n'
350
- if model.data.api_key:
360
+ if model.data.api_key: # pragma: no branch
351
361
  arg += f' api_key="{model.data.api_key}",\n'
352
- if model.data.base_url:
362
+ if model.data.base_url: # pragma: no branch
353
363
  arg += f' base_url="{model.data.base_url}",\n'
354
- if model.data.temperature is not None:
364
+ if model.data.temperature is not None: # pragma: no branch
355
365
  arg += f" temperature={model.data.temperature},\n"
356
366
  arg += ")"
357
367
  before = ""
@@ -507,11 +517,18 @@ def do_other_llm(model: "WaldiezModel") -> tuple[str, str]:
507
517
  f' model="{model.name}",\n'
508
518
  f' api_base="{model.data.base_url}",\n'
509
519
  )
510
- if not model.data.api_key:
520
+ if not model.data.api_key: # pragma: no cover
511
521
  arg += ' api_key="na",\n'
512
- if model.data.extras:
522
+ else:
523
+ arg += f' api_key="{model.data.api_key}",\n'
524
+ if model.data.temperature is not None: # pragma: no branch
525
+ arg += f" temperature={model.data.temperature},\n"
526
+ if model.data.extras: # pragma: no branch
513
527
  for key, value in model.data.extras.items():
514
- arg += f' {key}="{value}",\n'
528
+ if isinstance(value, str):
529
+ arg += f' {key}="{value}",\n'
530
+ else:
531
+ arg += f" {key}={value},\n"
515
532
  arg += ")"
516
533
  # if model.data.price:
517
534
  before = ""
@@ -213,7 +213,7 @@ class WaldiezModel(WaldiezBase):
213
213
  optionals: list[tuple[str, type]] = [
214
214
  ("base_url", str),
215
215
  ("max_tokens", int),
216
- # ("temperature", float),
216
+ ("temperature", float),
217
217
  ("top_p", float),
218
218
  ("api_version", str),
219
219
  ("default_headers", dict),
@@ -229,6 +229,9 @@ class WaldiezModel(WaldiezBase):
229
229
  value = getattr(self, attr)
230
230
  if value:
231
231
  _llm_config[attr] = value
232
+ if "top_p" in _llm_config and "temperature" in _llm_config:
233
+ # only keep one
234
+ _llm_config.pop("top_p", None)
232
235
  if self.data.api_type == "bedrock":
233
236
  _llm_config.pop("base_url", None)
234
237
  return set_bedrock_aws_config(_llm_config, self.data.aws)
@@ -3,12 +3,12 @@
3
3
  # flake8: noqa: E501
4
4
  """Waldiez Model Data."""
5
5
 
6
- from typing import Optional
6
+ from typing import Any, Optional
7
7
 
8
- from pydantic import Field
9
- from typing_extensions import Annotated, Literal
8
+ from pydantic import Field, model_validator
9
+ from typing_extensions import Annotated, Literal, Self
10
10
 
11
- from ..common import WaldiezBase
11
+ from ..common import WaldiezBase, update_dict
12
12
  from ._aws import WaldiezModelAWS
13
13
  from ._price import WaldiezModelPrice
14
14
 
@@ -131,7 +131,7 @@ class WaldiezModelData(WaldiezBase):
131
131
  ),
132
132
  ] = None
133
133
  extras: Annotated[
134
- dict[str, str],
134
+ dict[str, Any],
135
135
  Field(
136
136
  alias="extras",
137
137
  default_factory=dict,
@@ -154,3 +154,16 @@ class WaldiezModelData(WaldiezBase):
154
154
  default=None, title="Price", description="The price of the model"
155
155
  ),
156
156
  ] = None
157
+
158
+ @model_validator(mode="after")
159
+ def validate_model_data(self) -> Self:
160
+ """Validate model data.
161
+
162
+ Returns
163
+ -------
164
+ WaldiezModelData
165
+ The validated model data.
166
+ """
167
+ if self.extras:
168
+ self.extras = update_dict(self.extras)
169
+ return self
@@ -3,6 +3,7 @@
3
3
  """Predefined tool configuration for Waldiez."""
4
4
 
5
5
  from dataclasses import dataclass
6
+ from typing import Any
6
7
 
7
8
  from .protocol import PredefinedTool
8
9
 
@@ -52,6 +53,7 @@ class PredefinedToolConfig:
52
53
  def get_content(
53
54
  self,
54
55
  secrets: dict[str, str],
56
+ runtime_kwargs: dict[str, Any] | None = None,
55
57
  ) -> str:
56
58
  """Get the content of the tool.
57
59
 
@@ -59,10 +61,12 @@ class PredefinedToolConfig:
59
61
  ----------
60
62
  secrets : dict[str, str]
61
63
  Dictionary of secrets/environment variables.
64
+ runtime_kwargs : dict[str, Any] | None, optional
65
+ Runtime keyword arguments to customize the content generation.
62
66
 
63
67
  Returns
64
68
  -------
65
69
  str
66
70
  Content of the tool.
67
71
  """
68
- return self.implementation.get_content(secrets)
72
+ return self.implementation.get_content(secrets, runtime_kwargs)
@@ -81,6 +81,7 @@ class DuckDuckGoSearchToolImpl(PredefinedTool):
81
81
  def get_content(
82
82
  self,
83
83
  secrets: dict[str, str],
84
+ runtime_kwargs: dict[str, Any] | None = None,
84
85
  ) -> str:
85
86
  """Get content for the tool.
86
87
 
@@ -89,6 +90,9 @@ class DuckDuckGoSearchToolImpl(PredefinedTool):
89
90
  secrets : dict[str, str]
90
91
  Dictionary of secrets/environment variables.
91
92
 
93
+ runtime_kwargs : dict[str, Any] | None, optional
94
+ Runtime keyword arguments to customize the content generation.
95
+
92
96
  Returns
93
97
  -------
94
98
  str