ag2 0.9.1.post0__py3-none-any.whl → 0.9.3__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.

Files changed (37) hide show
  1. {ag2-0.9.1.post0.dist-info → ag2-0.9.3.dist-info}/METADATA +22 -12
  2. {ag2-0.9.1.post0.dist-info → ag2-0.9.3.dist-info}/RECORD +37 -23
  3. autogen/agentchat/contrib/capabilities/transforms.py +22 -9
  4. autogen/agentchat/conversable_agent.py +37 -34
  5. autogen/agentchat/group/group_utils.py +65 -20
  6. autogen/agentchat/group/handoffs.py +81 -5
  7. autogen/agentchat/group/on_context_condition.py +2 -2
  8. autogen/agentchat/group/patterns/pattern.py +7 -1
  9. autogen/agentchat/groupchat.py +2 -2
  10. autogen/agentchat/realtime/experimental/realtime_swarm.py +12 -4
  11. autogen/agents/experimental/document_agent/document_agent.py +232 -40
  12. autogen/events/agent_events.py +7 -4
  13. autogen/interop/litellm/litellm_config_factory.py +68 -2
  14. autogen/llm_config.py +4 -1
  15. autogen/mcp/__main__.py +78 -0
  16. autogen/mcp/mcp_proxy/__init__.py +19 -0
  17. autogen/mcp/mcp_proxy/fastapi_code_generator_helpers.py +63 -0
  18. autogen/mcp/mcp_proxy/mcp_proxy.py +581 -0
  19. autogen/mcp/mcp_proxy/operation_grouping.py +158 -0
  20. autogen/mcp/mcp_proxy/operation_renaming.py +114 -0
  21. autogen/mcp/mcp_proxy/patch_fastapi_code_generator.py +98 -0
  22. autogen/mcp/mcp_proxy/security.py +400 -0
  23. autogen/mcp/mcp_proxy/security_schema_visitor.py +37 -0
  24. autogen/oai/client.py +11 -2
  25. autogen/oai/gemini.py +20 -3
  26. autogen/oai/gemini_types.py +27 -0
  27. autogen/oai/oai_models/chat_completion.py +1 -1
  28. autogen/tools/experimental/__init__.py +5 -0
  29. autogen/tools/experimental/reliable/__init__.py +10 -0
  30. autogen/tools/experimental/reliable/reliable.py +1316 -0
  31. autogen/version.py +1 -1
  32. templates/client_template/main.jinja2 +69 -0
  33. templates/config_template/config.jinja2 +7 -0
  34. templates/main.jinja2 +61 -0
  35. {ag2-0.9.1.post0.dist-info → ag2-0.9.3.dist-info}/WHEEL +0 -0
  36. {ag2-0.9.1.post0.dist-info → ag2-0.9.3.dist-info}/licenses/LICENSE +0 -0
  37. {ag2-0.9.1.post0.dist-info → ag2-0.9.3.dist-info}/licenses/NOTICE.md +0 -0
@@ -0,0 +1,400 @@
1
+ # Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ import base64
5
+ import json
6
+ import logging
7
+ from typing import Any, ClassVar, Literal, Optional
8
+
9
+ import requests
10
+ from pydantic import BaseModel, model_validator
11
+ from typing_extensions import TypeAlias
12
+
13
+ # Get the logger
14
+ logger = logging.getLogger(__name__)
15
+ logger.setLevel(logging.DEBUG)
16
+
17
+ BaseSecurityType: TypeAlias = type["BaseSecurity"]
18
+
19
+
20
+ class BaseSecurity(BaseModel):
21
+ """Base class for security classes."""
22
+
23
+ type: ClassVar[Literal["apiKey", "http", "mutualTLS", "oauth2", "openIdConnect", "unsupported"]]
24
+ in_value: ClassVar[Literal["header", "query", "cookie", "bearer", "basic", "tls", "unsupported"]]
25
+ name: str
26
+
27
+ @model_validator(mode="after") # type: ignore[misc]
28
+ def __post_init__(
29
+ self,
30
+ ) -> "BaseSecurity": # dataclasses uses __post_init__ instead of model_validator
31
+ """Validate the in_value based on the type."""
32
+ valid_in_values = {
33
+ "apiKey": ["header", "query", "cookie"],
34
+ "http": ["bearer", "basic"],
35
+ "oauth2": ["bearer"],
36
+ "openIdConnect": ["bearer"],
37
+ "mutualTLS": ["tls"],
38
+ "unsupported": ["unsupported"],
39
+ }
40
+ if self.in_value not in valid_in_values[self.type]:
41
+ raise ValueError(f"Invalid in_value '{self.in_value}' for type '{self.type}'")
42
+ return self
43
+
44
+ def accept(self, security_params: "BaseSecurityParameters") -> bool:
45
+ return isinstance(self, security_params.get_security_class())
46
+
47
+ @classmethod
48
+ def is_supported(cls, type: str, schema_parameters: dict[str, Any]) -> bool:
49
+ return cls.type == type and cls.in_value == schema_parameters.get("in")
50
+
51
+ @classmethod
52
+ def get_security_class(cls, type: str, schema_parameters: dict[str, Any]) -> BaseSecurityType:
53
+ sub_classes = cls.__subclasses__()
54
+
55
+ for sub_class in sub_classes:
56
+ if sub_class.is_supported(type, schema_parameters):
57
+ return sub_class
58
+
59
+ logger.error(f"Unsupported type '{type}' and schema_parameters '{schema_parameters}' combination")
60
+ return UnsuportedSecurityStub
61
+
62
+ @classmethod
63
+ def get_security_parameters(cls, schema_parameters: dict[str, Any]) -> str:
64
+ return f'{cls.__name__}(name="{schema_parameters.get("name")}")'
65
+
66
+ @classmethod
67
+ def parse_security_parameters(cls, unparsed_params: dict[str, Any]) -> "BaseSecurityParameters":
68
+ type = unparsed_params.pop("type")
69
+ schema_parameters = unparsed_params.pop("schema_parameters")
70
+ security_class = cls.get_security_class(type, schema_parameters)
71
+ return security_class.Parameters.model_validate(unparsed_params)
72
+
73
+ @classmethod
74
+ def parse_security_parameters_from_env(cls, env: dict[str, str]) -> "BaseSecurityParameters":
75
+ """Parse security parameters from environment variables."""
76
+ security_str = env.get("SECURITY")
77
+ if not security_str:
78
+ logger.warning("No security parameters found in environment variables.")
79
+
80
+ return cls.parse_security_parameters(json.loads(security_str))
81
+
82
+
83
+ class BaseSecurityParameters(BaseModel):
84
+ """Base class for security parameters."""
85
+
86
+ def apply(
87
+ self,
88
+ q_params: dict[str, Any],
89
+ body_dict: dict[str, Any],
90
+ security: BaseSecurity,
91
+ ) -> None: ...
92
+
93
+ def get_security_class(self) -> type[BaseSecurity]: ...
94
+
95
+ def dump(self) -> dict[str, Any]:
96
+ raise NotImplementedError("Subclasses must implement the dump method")
97
+
98
+ def to_env(self) -> dict[str, Any]:
99
+ """Convert the security parameters to a dictionary."""
100
+ return {
101
+ "SECURITY": json.dumps(self.dump()),
102
+ }
103
+
104
+
105
+ class UnsuportedSecurityStub(BaseSecurity):
106
+ """Unsupported security stub class."""
107
+
108
+ type: ClassVar[Literal["unsupported"]] = "unsupported"
109
+ in_value: ClassVar[Literal["unsupported"]] = "unsupported"
110
+
111
+ @classmethod
112
+ def is_supported(cls, type: str, schema_parameters: dict[str, Any]) -> bool:
113
+ return False
114
+
115
+ def accept(self, security_params: "BaseSecurityParameters") -> bool:
116
+ if isinstance(self, security_params.get_security_class()):
117
+ raise RuntimeError("Trying to set UnsuportedSecurityStub params")
118
+ return False
119
+
120
+ class Parameters(BaseSecurityParameters): # BaseSecurityParameters
121
+ """API Key Header security parameters class."""
122
+
123
+ def apply(
124
+ self,
125
+ q_params: dict[str, Any],
126
+ body_dict: dict[str, Any],
127
+ security: BaseSecurity,
128
+ ) -> None:
129
+ pass
130
+
131
+ def get_security_class(self) -> type[BaseSecurity]:
132
+ return UnsuportedSecurityStub
133
+
134
+ def dump(self) -> dict[str, Any]:
135
+ return {
136
+ "type": "unsupported",
137
+ }
138
+
139
+
140
+ class APIKeyHeader(BaseSecurity):
141
+ """API Key Header security class."""
142
+
143
+ type: ClassVar[Literal["apiKey"]] = "apiKey"
144
+ in_value: ClassVar[Literal["header"]] = "header"
145
+
146
+ class Parameters(BaseSecurityParameters): # BaseSecurityParameters
147
+ """API Key Header security parameters class."""
148
+
149
+ value: str = "API_KEY"
150
+
151
+ def apply(
152
+ self,
153
+ q_params: dict[str, Any],
154
+ body_dict: dict[str, Any],
155
+ security: BaseSecurity,
156
+ ) -> None:
157
+ api_key_header: APIKeyHeader = security # type: ignore[assignment]
158
+
159
+ if "headers" not in body_dict:
160
+ body_dict["headers"] = {}
161
+
162
+ body_dict["headers"][api_key_header.name] = self.value
163
+
164
+ def get_security_class(self) -> type[BaseSecurity]:
165
+ return APIKeyHeader
166
+
167
+ def dump(self) -> dict[str, Any]:
168
+ return {
169
+ "type": "apiKey",
170
+ "schema_parameters": {"in": "header"},
171
+ **self.model_dump(),
172
+ }
173
+
174
+
175
+ class APIKeyQuery(BaseSecurity):
176
+ """API Key Query security class."""
177
+
178
+ type: ClassVar[Literal["apiKey"]] = "apiKey"
179
+ in_value: ClassVar[Literal["query"]] = "query"
180
+
181
+ @classmethod
182
+ def is_supported(cls, type: str, schema_parameters: dict[str, Any]) -> bool:
183
+ return super().is_supported(type, schema_parameters)
184
+
185
+ class Parameters(BaseSecurityParameters): # BaseSecurityParameters
186
+ """API Key Query security parameters class."""
187
+
188
+ value: str = "API_KEY"
189
+
190
+ def apply(
191
+ self,
192
+ q_params: dict[str, Any],
193
+ body_dict: dict[str, Any],
194
+ security: BaseSecurity,
195
+ ) -> None:
196
+ api_key_query: APIKeyQuery = security # type: ignore[assignment]
197
+
198
+ q_params[api_key_query.name] = self.value
199
+
200
+ def get_security_class(self) -> type[BaseSecurity]:
201
+ return APIKeyQuery
202
+
203
+ def dump(self) -> dict[str, Any]:
204
+ return {
205
+ "type": "apiKey",
206
+ "schema_parameters": {"in": "query"},
207
+ **self.model_dump(),
208
+ }
209
+
210
+
211
+ class APIKeyCookie(BaseSecurity):
212
+ """API Key Cookie security class."""
213
+
214
+ type: ClassVar[Literal["apiKey"]] = "apiKey"
215
+ in_value: ClassVar[Literal["cookie"]] = "cookie"
216
+
217
+ class Parameters(BaseSecurityParameters): # BaseSecurityParameters
218
+ """API Key Cookie security parameters class."""
219
+
220
+ value: str = "API_KEY"
221
+
222
+ def apply(
223
+ self,
224
+ q_params: dict[str, Any],
225
+ body_dict: dict[str, Any],
226
+ security: BaseSecurity,
227
+ ) -> None:
228
+ api_key_cookie: APIKeyCookie = security # type: ignore[assignment]
229
+
230
+ if "cookies" not in body_dict:
231
+ body_dict["cookies"] = {}
232
+
233
+ body_dict["cookies"][api_key_cookie.name] = self.value
234
+
235
+ def get_security_class(self) -> type[BaseSecurity]:
236
+ return APIKeyCookie
237
+
238
+ def dump(self) -> dict[str, Any]:
239
+ return {
240
+ "type": "apiKey",
241
+ "schema_parameters": {"in": "cookie"},
242
+ **self.model_dump(),
243
+ }
244
+
245
+
246
+ class HTTPBearer(BaseSecurity):
247
+ """HTTP Bearer security class."""
248
+
249
+ type: ClassVar[Literal["http"]] = "http"
250
+ in_value: ClassVar[Literal["bearer"]] = "bearer"
251
+
252
+ @classmethod
253
+ def is_supported(cls, type: str, schema_parameters: dict[str, Any]) -> bool:
254
+ return cls.type == type and cls.in_value == schema_parameters.get("scheme")
255
+
256
+ class Parameters(BaseSecurityParameters): # BaseSecurityParameters
257
+ """HTTP Bearer security parameters class."""
258
+
259
+ value: str = "BEARER_TOKEN"
260
+
261
+ def apply(
262
+ self,
263
+ q_params: dict[str, Any],
264
+ body_dict: dict[str, Any],
265
+ security: BaseSecurity,
266
+ ) -> None:
267
+ if "headers" not in body_dict:
268
+ body_dict["headers"] = {}
269
+
270
+ body_dict["headers"]["Authorization"] = f"Bearer {self.value}"
271
+
272
+ def get_security_class(self) -> type[BaseSecurity]:
273
+ return HTTPBearer
274
+
275
+ def dump(self) -> dict[str, Any]:
276
+ return {
277
+ "type": "http",
278
+ "schema_parameters": {"scheme": "bearer"},
279
+ **self.model_dump(),
280
+ }
281
+
282
+
283
+ class HTTPBasic(BaseSecurity):
284
+ """HTTP Bearer security class."""
285
+
286
+ type: ClassVar[Literal["http"]] = "http"
287
+ in_value: ClassVar[Literal["basic"]] = "basic"
288
+
289
+ @classmethod
290
+ def is_supported(cls, type: str, schema_parameters: dict[str, Any]) -> bool:
291
+ return cls.type == type and cls.in_value == schema_parameters.get("scheme")
292
+
293
+ class Parameters(BaseSecurityParameters): # BaseSecurityParameters
294
+ """HTTP Basic security parameters class."""
295
+
296
+ username: str = "USERNAME"
297
+ password: str = "PASSWORD"
298
+
299
+ def apply(
300
+ self,
301
+ q_params: dict[str, Any],
302
+ body_dict: dict[str, Any],
303
+ security: BaseSecurity,
304
+ ) -> None:
305
+ if "headers" not in body_dict:
306
+ body_dict["headers"] = {}
307
+
308
+ credentials = f"{self.username}:{self.password}"
309
+ encoded_credentials = base64.b64encode(credentials.encode("utf-8")).decode("utf-8")
310
+
311
+ body_dict["headers"]["Authorization"] = f"Basic {encoded_credentials}"
312
+
313
+ def get_security_class(self) -> type[BaseSecurity]:
314
+ return HTTPBasic
315
+
316
+ def dump(self) -> dict[str, Any]:
317
+ return {
318
+ "type": "http",
319
+ "schema_parameters": {"scheme": "basic"},
320
+ **self.model_dump(),
321
+ }
322
+
323
+
324
+ class OAuth2PasswordBearer(BaseSecurity):
325
+ """OAuth2 Password Bearer security class."""
326
+
327
+ type: ClassVar[Literal["oauth2"]] = "oauth2"
328
+ in_value: ClassVar[Literal["bearer"]] = "bearer"
329
+ token_url: str
330
+
331
+ @classmethod
332
+ def is_supported(cls, type: str, schema_parameters: dict[str, Any]) -> bool:
333
+ return type == cls.type and "password" in schema_parameters.get("flows", {})
334
+
335
+ @classmethod
336
+ def get_security_parameters(cls, schema_parameters: dict[str, Any]) -> str:
337
+ name = schema_parameters.get("name")
338
+ token_url = f"{schema_parameters.get('server_url')}/{schema_parameters['flows']['password']['tokenUrl']}"
339
+ return f'{cls.__name__}(name="{name}", token_url="{token_url}")'
340
+
341
+ class Parameters(BaseSecurityParameters): # BaseSecurityParameters
342
+ """OAuth2 Password Bearer security class."""
343
+
344
+ username: str = "USERNAME"
345
+ password: str = "PASSWORD"
346
+ bearer_token: Optional[str] = None
347
+ token_url: str = "TOKEN_URL"
348
+
349
+ # @model_validator(mode="before")
350
+ # def check_credentials(cls, values: dict[str, Any]) -> Any: # noqa
351
+ # username = values.get("username")
352
+ # password = values.get("password")
353
+ # bearer_token = values.get("bearer_token")
354
+
355
+ # if not bearer_token and (not username or not password):
356
+ # # If bearer_token is not provided, both username and password must be defined
357
+ # raise ValueError("Both username and password are required if bearer_token is not provided.")
358
+
359
+ # return values
360
+
361
+ def get_token(self, token_url: str) -> str:
362
+ # Get the token
363
+ request = requests.post(
364
+ token_url,
365
+ data={
366
+ "username": self.username,
367
+ "password": self.password,
368
+ },
369
+ timeout=5,
370
+ )
371
+ request.raise_for_status()
372
+ return request.json()["access_token"] # type: ignore
373
+
374
+ def apply(
375
+ self,
376
+ q_params: dict[str, Any],
377
+ body_dict: dict[str, Any],
378
+ security: BaseSecurity,
379
+ ) -> None:
380
+ if not self.bearer_token:
381
+ if security.token_url is None: # type: ignore
382
+ raise ValueError("Token URL is not defined")
383
+ self.bearer_token = self.get_token(security.token_url) # type: ignore
384
+
385
+ if "headers" not in body_dict:
386
+ body_dict["headers"] = {}
387
+
388
+ body_dict["headers"]["Authorization"] = f"Bearer {self.bearer_token}"
389
+
390
+ def get_security_class(self) -> type[BaseSecurity]:
391
+ return OAuth2PasswordBearer
392
+
393
+ def dump(self) -> dict[str, Any]:
394
+ return {
395
+ "type": "oauth2",
396
+ "schema_parameters": {"flows": {"password": {"tokenUrl": self.token_url or ""}}},
397
+ "username": self.username,
398
+ "password": self.password,
399
+ "bearer_token": self.bearer_token,
400
+ }
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ from pathlib import Path
5
+
6
+ from autogen.import_utils import optional_import_block
7
+ from autogen.mcp.mcp_proxy.security import BaseSecurity
8
+
9
+ with optional_import_block() as result:
10
+ from fastapi_code_generator.parser import OpenAPIParser
11
+ from fastapi_code_generator.visitor import Visitor
12
+
13
+
14
+ def custom_visitor(parser: "OpenAPIParser", model_path: Path) -> dict[str, object]:
15
+ if "components" not in parser.raw_obj or "securitySchemes" not in parser.raw_obj["components"]:
16
+ return {}
17
+ security_schemes = parser.raw_obj["components"]["securitySchemes"]
18
+ server_url = parser.raw_obj["servers"][0]["url"]
19
+
20
+ security_classes = []
21
+ security_parameters = {}
22
+ for k, v in security_schemes.items():
23
+ v["server_url"] = server_url
24
+ security_class = BaseSecurity.get_security_class(type=v["type"], schema_parameters=v)
25
+
26
+ security_classes.append(security_class.__name__)
27
+
28
+ security_parameters[k] = security_class.get_security_parameters(schema_parameters=v)
29
+
30
+ return {
31
+ "security_schemes": security_schemes,
32
+ "security_classes": security_classes,
33
+ "security_parameters": security_parameters,
34
+ }
35
+
36
+
37
+ visit: "Visitor" = custom_visitor
autogen/oai/client.py CHANGED
@@ -832,7 +832,16 @@ class OpenAIWrapper:
832
832
  def _configure_azure_openai(self, config: dict[str, Any], openai_config: dict[str, Any]) -> None:
833
833
  openai_config["azure_deployment"] = openai_config.get("azure_deployment", config.get("model"))
834
834
  if openai_config["azure_deployment"] is not None:
835
- openai_config["azure_deployment"] = openai_config["azure_deployment"].replace(".", "")
835
+ # Preserve dots for specific model versions that require them
836
+ deployment_name = openai_config["azure_deployment"]
837
+ if deployment_name in [
838
+ "gpt-4.1"
839
+ ]: # Add more as needed, Whitelist approach so as to not break existing deployments
840
+ # Keep the deployment name as-is for these specific models
841
+ pass
842
+ else:
843
+ # Remove dots for all other models (maintain existing behavior)
844
+ openai_config["azure_deployment"] = deployment_name.replace(".", "")
836
845
  openai_config["azure_endpoint"] = openai_config.get("azure_endpoint", openai_config.pop("base_url", None))
837
846
 
838
847
  # Create a default Azure token provider if requested
@@ -975,7 +984,7 @@ class OpenAIWrapper:
975
984
  existing_client_class = True
976
985
 
977
986
  if existing_client_class:
978
- logger.warn(
987
+ logger.warning(
979
988
  f"Model client {model_client_cls.__name__} is already registered. Add more entries in the config_list to use multiple model clients."
980
989
  )
981
990
  else:
autogen/oai/gemini.py CHANGED
@@ -574,7 +574,16 @@ class GeminiClient:
574
574
  if self.use_vertexai
575
575
  else rst.append(Content(parts=parts, role=role))
576
576
  )
577
- elif part_type == "tool" or part_type == "tool_call":
577
+ elif part_type == "tool":
578
+ # Function responses should be assigned "model" role to keep them separate from function calls
579
+ role = "function" if version.parse(genai.__version__) < version.parse("1.4.0") else "model"
580
+ rst.append(
581
+ VertexAIContent(parts=parts, role=role)
582
+ if self.use_vertexai
583
+ else rst.append(Content(parts=parts, role=role))
584
+ )
585
+ elif part_type == "tool_call":
586
+ # Function calls should be assigned "user" role
578
587
  role = "function" if version.parse(genai.__version__) < version.parse("1.4.0") else "user"
579
588
  rst.append(
580
589
  VertexAIContent(parts=parts, role=role)
@@ -898,7 +907,11 @@ def calculate_gemini_cost(use_vertexai: bool, input_tokens: int, output_tokens:
898
907
  # Vertex AI pricing - based on Text input
899
908
  # https://cloud.google.com/vertex-ai/generative-ai/pricing#vertex-ai-pricing
900
909
 
901
- if "gemini-2.5-pro-preview-03-25" in model_name or "gemini-2.5-pro-exp-03-25" in model_name:
910
+ if (
911
+ "gemini-2.5-pro-preview-03-25" in model_name
912
+ or "gemini-2.5-pro-exp-03-25" in model_name
913
+ or "gemini-2.5-pro-preview-05-06" in model_name
914
+ ):
902
915
  if up_to_200k:
903
916
  return total_cost_mil(1.25, 10)
904
917
  else:
@@ -938,7 +951,11 @@ def calculate_gemini_cost(use_vertexai: bool, input_tokens: int, output_tokens:
938
951
  else:
939
952
  # Non-Vertex AI pricing
940
953
 
941
- if "gemini-2.5-pro-preview-03-25" in model_name or "gemini-2.5-pro-exp-03-25" in model_name:
954
+ if (
955
+ "gemini-2.5-pro-preview-03-25" in model_name
956
+ or "gemini-2.5-pro-exp-03-25" in model_name
957
+ or "gemini-2.5-pro-preview-05-06" in model_name
958
+ ):
942
959
  # https://ai.google.dev/gemini-api/docs/pricing#gemini-2.5-pro-preview
943
960
  if up_to_200k:
944
961
  return total_cost_mil(1.25, 10)
@@ -108,6 +108,25 @@ class FunctionCallingConfigMode(CaseInSensitiveEnum):
108
108
  NONE = "NONE"
109
109
 
110
110
 
111
+ class LatLng(CommonBaseModel):
112
+ """An object that represents a latitude/longitude pair.
113
+
114
+ This is expressed as a pair of doubles to represent degrees latitude and
115
+ degrees longitude. Unless specified otherwise, this object must conform to the
116
+ <a href="https://en.wikipedia.org/wiki/World_Geodetic_System#1984_version">
117
+ WGS84 standard</a>. Values must be within normalized ranges.
118
+ """
119
+
120
+ latitude: Optional[float] = Field(
121
+ default=None,
122
+ description="""The latitude in degrees. It must be in the range [-90.0, +90.0].""",
123
+ )
124
+ longitude: Optional[float] = Field(
125
+ default=None,
126
+ description="""The longitude in degrees. It must be in the range [-180.0, +180.0]""",
127
+ )
128
+
129
+
111
130
  class FunctionCallingConfig(CommonBaseModel):
112
131
  """Function calling config."""
113
132
 
@@ -118,6 +137,13 @@ class FunctionCallingConfig(CommonBaseModel):
118
137
  )
119
138
 
120
139
 
140
+ class RetrievalConfig(CommonBaseModel):
141
+ """Retrieval config."""
142
+
143
+ lat_lng: Optional[LatLng] = Field(default=None, description="""Optional. The location of the user.""")
144
+ language_code: Optional[str] = Field(default=None, description="""The language code of the user.""")
145
+
146
+
121
147
  class ToolConfig(CommonBaseModel):
122
148
  """Tool config.
123
149
 
@@ -127,3 +153,4 @@ class ToolConfig(CommonBaseModel):
127
153
  function_calling_config: Optional[FunctionCallingConfig] = Field(
128
154
  default=None, description="""Optional. Function calling config."""
129
155
  )
156
+ retrieval_config: Optional[RetrievalConfig] = Field(default=None, description="""Optional. Retrieval config.""")
@@ -66,7 +66,7 @@ class ChatCompletion(BaseModel):
66
66
  object: Literal["chat.completion"]
67
67
  """The object type, which is always `chat.completion`."""
68
68
 
69
- service_tier: Optional[Literal["auto", "default", "flex"]] = None
69
+ service_tier: Optional[Literal["auto", "default", "flex", "scale"]] = None
70
70
  """The service tier used for processing the request."""
71
71
 
72
72
  system_fingerprint: Optional[str] = None
@@ -17,6 +17,7 @@ from .messageplatform import (
17
17
  TelegramSendTool,
18
18
  )
19
19
  from .perplexity import PerplexitySearchTool
20
+ from .reliable import ReliableTool, ReliableToolError, SuccessfulExecutionParameters, ToolExecutionDetails
20
21
  from .tavily import TavilySearchTool
21
22
  from .web_search_preview import WebSearchPreviewTool
22
23
  from .wikipedia import WikipediaPageLoadTool, WikipediaQueryRunTool
@@ -30,12 +31,16 @@ __all__ = [
30
31
  "DuckDuckGoSearchTool",
31
32
  "GoogleSearchTool",
32
33
  "PerplexitySearchTool",
34
+ "ReliableTool",
35
+ "ReliableToolError",
33
36
  "SlackRetrieveRepliesTool",
34
37
  "SlackRetrieveTool",
35
38
  "SlackSendTool",
39
+ "SuccessfulExecutionParameters",
36
40
  "TavilySearchTool",
37
41
  "TelegramRetrieveTool",
38
42
  "TelegramSendTool",
43
+ "ToolExecutionDetails",
39
44
  "WebSearchPreviewTool",
40
45
  "WikipediaPageLoadTool",
41
46
  "WikipediaQueryRunTool",
@@ -0,0 +1,10 @@
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
+ # Portions derived from https://github.com/microsoft/autogen are under the MIT License.
6
+ # SPDX-License-Identifier: MIT
7
+
8
+ from .reliable import ReliableTool, ReliableToolError, SuccessfulExecutionParameters, ToolExecutionDetails
9
+
10
+ __all__ = ["ReliableTool", "ReliableToolError", "SuccessfulExecutionParameters", "ToolExecutionDetails"]