ag2 0.9.5__py3-none-any.whl → 0.9.7__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 ag2 might be problematic. Click here for more details.

autogen/oai/ollama.py CHANGED
@@ -22,6 +22,7 @@ Resources:
22
22
 
23
23
  from __future__ import annotations
24
24
 
25
+ import ast
25
26
  import copy
26
27
  import json
27
28
  import random
@@ -59,6 +60,7 @@ class OllamaLLMConfigEntry(LLMConfigEntry):
59
60
  top_k: int = Field(default=40)
60
61
  top_p: float = Field(default=0.9)
61
62
  hide_tools: Literal["if_all_run", "if_any_run", "never"] = "never"
63
+ native_tool_calls: bool = False
62
64
 
63
65
  def create_client(self):
64
66
  raise NotImplementedError("OllamaLLMConfigEntry.create_client is not implemented.")
@@ -612,7 +614,7 @@ def _object_to_tool_call(data_object: Any) -> list[dict[str, Any]]:
612
614
  is_invalid = False
613
615
  for i, item in enumerate(data_copy):
614
616
  try:
615
- new_item = eval(item)
617
+ new_item = ast.literal_eval(item)
616
618
  if isinstance(new_item, dict):
617
619
  if is_valid_tool_call_item(new_item):
618
620
  data_object[i] = new_item
@@ -467,59 +467,70 @@ def filter_config(
467
467
  filter_dict: Optional[dict[str, Union[list[Union[str, None]], set[Union[str, None]]]]],
468
468
  exclude: bool = False,
469
469
  ) -> list[dict[str, Any]]:
470
- """This function filters `config_list` by checking each configuration dictionary against the criteria specified in
471
- `filter_dict`. A configuration dictionary is retained if for every key in `filter_dict`, see example below.
470
+ """Filter configuration dictionaries based on specified criteria.
471
+
472
+ This function filters a list of configuration dictionaries by applying ALL criteria specified in `filter_dict`.
473
+ A configuration is included in the result if it satisfies every key-value constraint in the filter dictionary.
474
+ For each filter key, the configuration's corresponding field value must match at least one of the acceptable
475
+ values (OR logic within each criteria, AND logic between different criteria).
472
476
 
473
477
  Args:
474
478
  config_list (list of dict): A list of configuration dictionaries to be filtered.
475
- filter_dict (dict): A dictionary representing the filter criteria, where each key is a
476
- field name to check within the configuration dictionaries, and the
477
- corresponding value is a list of acceptable values for that field.
478
- If the configuration's field's value is not a list, then a match occurs
479
- when it is found in the list of acceptable values. If the configuration's
480
- field's value is a list, then a match occurs if there is a non-empty
481
- intersection with the acceptable values.
482
- exclude (bool): If False (the default value), configs that match the filter will be included in the returned
483
- list. If True, configs that match the filter will be excluded in the returned list.
479
+
480
+ filter_dict (dict, optional): A dictionary specifying filter criteria where:
481
+ - Keys are field names to check in each configuration dictionary
482
+ - Values are lists/sets of acceptable values for that field
483
+ - A configuration matches if ALL filter keys are satisfied AND for each key,
484
+ the config's field value matches at least one acceptable value
485
+ - If a filter value includes None, configurations missing that field will match
486
+ - If None, no filtering is applied
487
+
488
+ exclude (bool, optional): If False (default), return configurations that match the filter.
489
+ If True, return configurations that do NOT match the filter.
484
490
 
485
491
  Returns:
486
- list of dict: A list of configuration dictionaries that meet all the criteria specified
487
- in `filter_dict`.
492
+ list of dict: Filtered list of configuration dictionaries.
488
493
 
489
- Example:
494
+ Matching Logic:
495
+ - **Between different filter keys**: AND logic (all criteria must be satisfied)
496
+ - **Within each filter key's values**: OR logic (any acceptable value can match)
497
+ - **For list-type config values**: Match if there's any intersection with acceptable values
498
+ - **For scalar config values**: Match if the value is in the list of acceptable values
499
+ - **Missing fields**: Only match if None is included in the acceptable values for that field
500
+
501
+ Examples:
490
502
  ```python
491
- # Example configuration list with various models and API types
492
503
  configs = [
493
- {"model": "gpt-3.5-turbo"},
494
- {"model": "gpt-4"},
495
- {"model": "gpt-3.5-turbo", "api_type": "azure"},
496
- {"model": "gpt-3.5-turbo", "tags": ["gpt35_turbo", "gpt-35-turbo"]},
504
+ {"model": "gpt-3.5-turbo", "api_type": "openai"},
505
+ {"model": "gpt-4", "api_type": "openai"},
506
+ {"model": "gpt-3.5-turbo", "api_type": "azure", "api_version": "2024-02-01"},
507
+ {"model": "gpt-4", "tags": ["premium", "latest"]},
497
508
  ]
498
- # Define filter criteria to select configurations for the 'gpt-3.5-turbo' model
499
- # that are also using the 'azure' API type
500
- filter_criteria = {
501
- "model": ["gpt-3.5-turbo"], # Only accept configurations for 'gpt-3.5-turbo'
502
- "api_type": ["azure"], # Only accept configurations for 'azure' API type
503
- }
504
- # Apply the filter to the configuration list
505
- filtered_configs = filter_config(configs, filter_criteria)
506
- # The resulting `filtered_configs` will be:
507
- # [{'model': 'gpt-3.5-turbo', 'api_type': 'azure', ...}]
508
- # Define a filter to select a given tag
509
- filter_criteria = {
510
- "tags": ["gpt35_turbo"],
511
- }
512
- # Apply the filter to the configuration list
513
- filtered_configs = filter_config(configs, filter_criteria)
514
- # The resulting `filtered_configs` will be:
515
- # [{'model': 'gpt-3.5-turbo', 'tags': ['gpt35_turbo', 'gpt-35-turbo']}]
509
+
510
+ # Example 1: Single criterion - matches any model in the list
511
+ filter_dict = {"model": ["gpt-4", "gpt-4o"]}
512
+ result = filter_config(configs, filter_dict)
513
+ # Returns: [{"model": "gpt-4", "api_type": "openai"}, {"model": "gpt-4", "tags": ["premium", "latest"]}]
514
+
515
+ # Example 2: Multiple criteria - must satisfy ALL conditions
516
+ filter_dict = {"model": ["gpt-3.5-turbo"], "api_type": ["azure"]}
517
+ result = filter_config(configs, filter_dict)
518
+ # Returns: [{"model": "gpt-3.5-turbo", "api_type": "azure", "api_version": "2024-02-01"}]
519
+
520
+ # Example 3: Tag filtering with list intersection
521
+ filter_dict = {"tags": ["premium"]}
522
+ result = filter_config(configs, filter_dict)
523
+ # Returns: [{"model": "gpt-4", "tags": ["premium", "latest"]}]
524
+
525
+ # Example 4: Exclude matching configurations
526
+ filter_dict = {"api_type": ["openai"]}
527
+ result = filter_config(configs, filter_dict, exclude=True)
528
+ # Returns configs that do NOT have api_type="openai"
516
529
  ```
517
530
  Note:
518
531
  - If `filter_dict` is empty or None, no filtering is applied and `config_list` is returned as is.
519
532
  - If a configuration dictionary in `config_list` does not contain a key specified in `filter_dict`,
520
533
  it is considered a non-match and is excluded from the result.
521
- - If the list of acceptable values for a key in `filter_dict` includes None, then configuration
522
- dictionaries that do not have that key will also be considered a match.
523
534
 
524
535
  """
525
536
  if inspect.stack()[1].function != "where":
@@ -538,18 +549,73 @@ def filter_config(
538
549
  return config_list
539
550
 
540
551
 
541
- def _satisfies_criteria(value: Any, criteria_values: Any) -> bool:
542
- if value is None:
552
+ def _satisfies_criteria(config_value: Any, criteria_values: Any) -> bool:
553
+ """Check if a configuration field value satisfies the filter criteria.
554
+
555
+ This helper function implements the matching logic between a single configuration
556
+ field value and the acceptable values specified in the filter criteria. It handles
557
+ both scalar and list-type configuration values with appropriate matching strategies.
558
+
559
+ Args:
560
+ config_value (Any): The value from a configuration dictionary field.
561
+ Can be None, a scalar value, or a list of values.
562
+ criteria_values (Any): The acceptable values from the filter dictionary.
563
+ Can be a single value or a list/set of acceptable values.
564
+
565
+ Returns:
566
+ bool: True if the config_value satisfies the criteria, False otherwise.
567
+
568
+ Matching Logic:
569
+ - **None config values**: Always return False (missing fields don't match)
570
+ - **List config values**:
571
+ - If criteria is a list: Match if there's any intersection (set overlap)
572
+ - If criteria is scalar: Match if the scalar is contained in the config list
573
+ - **Scalar config values**:
574
+ - If criteria is a list: Match if the config value is in the criteria list
575
+ - If criteria is scalar: Match if the values are exactly equal
576
+
577
+ Examples:
578
+ ```python
579
+ # List config value with list criteria (intersection matching)
580
+ _satisfies_criteria(["gpt-4", "gpt-3.5"], ["gpt-4", "claude"]) # True (gpt-4 intersects)
581
+ _satisfies_criteria(["tag1", "tag2"], ["tag3", "tag4"]) # False (no intersection)
582
+
583
+ # List config value with scalar criteria (containment matching)
584
+ _satisfies_criteria(["premium", "latest"], "premium") # True (premium is in list)
585
+ _satisfies_criteria(["tag1", "tag2"], "tag3") # False (tag3 not in list)
586
+
587
+ # Scalar config value with list criteria (membership matching)
588
+ _satisfies_criteria("gpt-4", ["gpt-4", "gpt-3.5"]) # True (gpt-4 in criteria)
589
+ _satisfies_criteria("claude", ["gpt-4", "gpt-3.5"]) # False (claude not in criteria)
590
+
591
+ # Scalar config value with scalar criteria (equality matching)
592
+ _satisfies_criteria("openai", "openai") # True (exact match)
593
+ _satisfies_criteria("openai", "azure") # False (different values)
594
+
595
+ # None config values (missing fields)
596
+ _satisfies_criteria(None, ["gpt-4"]) # False (missing field)
597
+ _satisfies_criteria(None, "gpt-4") # False (missing field)
598
+ ```
599
+
600
+ Note:
601
+ This is an internal helper function used by `filter_config()`. The function
602
+ assumes that both parameters can be of various types and handles type
603
+ checking internally to determine the appropriate matching strategy.
604
+ """
605
+ if config_value is None:
543
606
  return False
544
607
 
545
- if isinstance(value, list):
546
- return bool(set(value) & set(criteria_values)) # Non-empty intersection
608
+ if isinstance(config_value, list):
609
+ if isinstance(criteria_values, list):
610
+ return bool(set(config_value) & set(criteria_values)) # Non-empty intersection
611
+ else:
612
+ return criteria_values in config_value
547
613
  else:
548
614
  # In filter_dict, filter could be either a list of values or a single value.
549
615
  # For example, filter_dict = {"model": ["gpt-3.5-turbo"]} or {"model": "gpt-3.5-turbo"}
550
616
  if isinstance(criteria_values, list):
551
- return value in criteria_values
552
- return bool(value == criteria_values)
617
+ return config_value in criteria_values
618
+ return bool(config_value == criteria_values)
553
619
 
554
620
 
555
621
  @export_module("autogen")
@@ -620,8 +686,6 @@ def config_list_from_json(
620
686
  with open(config_list_path) as json_file:
621
687
  config_list = json.load(json_file)
622
688
 
623
- config_list = filter_config(config_list, filter_dict)
624
-
625
689
  return filter_config(config_list, filter_dict)
626
690
 
627
691
 
@@ -3,6 +3,7 @@
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
5
  from .browser_use import BrowserUseTool
6
+ from .code_execution import PythonCodeExecutionTool
6
7
  from .crawl4ai import Crawl4AITool
7
8
  from .deep_research import DeepResearchTool
8
9
  from .duckduckgo import DuckDuckGoSearchTool
@@ -34,6 +35,7 @@ __all__ = [
34
35
  "FirecrawlTool",
35
36
  "GoogleSearchTool",
36
37
  "PerplexitySearchTool",
38
+ "PythonCodeExecutionTool",
37
39
  "ReliableTool",
38
40
  "ReliableToolError",
39
41
  "SearxngSearchTool",
@@ -0,0 +1,7 @@
1
+ # Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ from .python_code_execution import PythonCodeExecutionTool
6
+
7
+ __all__ = ["PythonCodeExecutionTool"]
@@ -0,0 +1,88 @@
1
+ # Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ import os
6
+ import tempfile
7
+ from typing import Annotated, Any, Optional
8
+
9
+ from pydantic import BaseModel, Field
10
+
11
+ from ....doc_utils import export_module
12
+ from ....environments import WorkingDirectory
13
+ from ....environments.python_environment import PythonEnvironment
14
+ from ... import Tool
15
+
16
+ __all__ = ["PythonCodeExecutionTool"]
17
+
18
+
19
+ @export_module("autogen.tools.experimental")
20
+ class PythonCodeExecutionTool(Tool):
21
+ """Executes Python code in a given environment and returns the result."""
22
+
23
+ def __init__(
24
+ self,
25
+ *,
26
+ timeout: int = 30,
27
+ working_directory: Optional[WorkingDirectory] = None,
28
+ python_environment: Optional[PythonEnvironment] = None,
29
+ ) -> None:
30
+ """
31
+ Initialize the PythonCodeExecutionTool.
32
+
33
+ **CAUTION**: If provided a local environment, this tool will execute code in your local environment, which can be dangerous if the code is untrusted.
34
+
35
+ Args:
36
+ timeout: Maximum execution time allowed in seconds, will raise a TimeoutError exception if exceeded.
37
+ working_directory: Optional WorkingDirectory context manager to use.
38
+ python_environment: Optional PythonEnvironment to use. If None, will auto-detect or create based on other parameters.
39
+ """
40
+ # Store configuration parameters
41
+ self.timeout = timeout
42
+ self.working_directory = WorkingDirectory.get_current_working_directory(working_directory)
43
+ tool_python_environment = PythonEnvironment.get_current_python_environment(python_environment)
44
+
45
+ assert self.working_directory, "No Working directory found"
46
+ assert tool_python_environment, "No Python environment found"
47
+
48
+ self.python_environment = tool_python_environment
49
+
50
+ # Pydantic model to contain the code and list of libraries to execute
51
+ class CodeExecutionRequest(BaseModel):
52
+ code: Annotated[str, Field(description="Python code to execute")]
53
+ libraries: Annotated[list[str], Field(description="List of libraries to install before execution")]
54
+
55
+ # The tool function, this is what goes to the LLM
56
+ async def execute_python_code(
57
+ code_execution_request: Annotated[CodeExecutionRequest, "Python code and the libraries required"],
58
+ ) -> dict[str, Any]:
59
+ """
60
+ Executes Python code in the attached environment and returns the result.
61
+
62
+ Args:
63
+ code_execution_request (CodeExecutionRequest): The Python code and libraries to execute
64
+ """
65
+ code = code_execution_request.code
66
+
67
+ # NOTE: Libraries are not installed (something to consider for future versions)
68
+
69
+ # Prepare a script file path
70
+ script_dir = self._get_script_directory()
71
+ script_path = os.path.join(script_dir, "script.py")
72
+
73
+ # Execute the code
74
+ return await self.python_environment.execute_code(code=code, script_path=script_path, timeout=self.timeout)
75
+
76
+ super().__init__(
77
+ name="python_execute_code",
78
+ description="Executes Python code and returns the result.",
79
+ func_or_tool=execute_python_code,
80
+ )
81
+
82
+ def _get_script_directory(self) -> str:
83
+ """Get the directory to use for scripts."""
84
+ if self.working_directory and hasattr(self.working_directory, "path") and self.working_directory.path:
85
+ path = self.working_directory.path
86
+ os.makedirs(path, exist_ok=True)
87
+ return path
88
+ return tempfile.mkdtemp(prefix="ag2_script_dir_")
autogen/tools/tool.py CHANGED
@@ -96,8 +96,7 @@ class Tool:
96
96
  """
97
97
  if self._func_schema:
98
98
  agent.update_tool_signature(self._func_schema, is_remove=False)
99
- else:
100
- agent.register_for_llm()(self)
99
+ agent.register_for_llm()(self)
101
100
 
102
101
  def register_for_execution(self, agent: "ConversableAgent") -> None:
103
102
  """Registers the tool for direct execution by a ConversableAgent.
autogen/version.py CHANGED
@@ -4,4 +4,4 @@
4
4
 
5
5
  __all__ = ["__version__"]
6
6
 
7
- __version__ = "0.9.5"
7
+ __version__ = "0.9.7"
File without changes