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.
- {ag2-0.9.1.post0.dist-info → ag2-0.9.3.dist-info}/METADATA +22 -12
- {ag2-0.9.1.post0.dist-info → ag2-0.9.3.dist-info}/RECORD +37 -23
- autogen/agentchat/contrib/capabilities/transforms.py +22 -9
- autogen/agentchat/conversable_agent.py +37 -34
- autogen/agentchat/group/group_utils.py +65 -20
- autogen/agentchat/group/handoffs.py +81 -5
- autogen/agentchat/group/on_context_condition.py +2 -2
- autogen/agentchat/group/patterns/pattern.py +7 -1
- autogen/agentchat/groupchat.py +2 -2
- autogen/agentchat/realtime/experimental/realtime_swarm.py +12 -4
- autogen/agents/experimental/document_agent/document_agent.py +232 -40
- autogen/events/agent_events.py +7 -4
- autogen/interop/litellm/litellm_config_factory.py +68 -2
- autogen/llm_config.py +4 -1
- autogen/mcp/__main__.py +78 -0
- autogen/mcp/mcp_proxy/__init__.py +19 -0
- autogen/mcp/mcp_proxy/fastapi_code_generator_helpers.py +63 -0
- autogen/mcp/mcp_proxy/mcp_proxy.py +581 -0
- autogen/mcp/mcp_proxy/operation_grouping.py +158 -0
- autogen/mcp/mcp_proxy/operation_renaming.py +114 -0
- autogen/mcp/mcp_proxy/patch_fastapi_code_generator.py +98 -0
- autogen/mcp/mcp_proxy/security.py +400 -0
- autogen/mcp/mcp_proxy/security_schema_visitor.py +37 -0
- autogen/oai/client.py +11 -2
- autogen/oai/gemini.py +20 -3
- autogen/oai/gemini_types.py +27 -0
- autogen/oai/oai_models/chat_completion.py +1 -1
- autogen/tools/experimental/__init__.py +5 -0
- autogen/tools/experimental/reliable/__init__.py +10 -0
- autogen/tools/experimental/reliable/reliable.py +1316 -0
- autogen/version.py +1 -1
- templates/client_template/main.jinja2 +69 -0
- templates/config_template/config.jinja2 +7 -0
- templates/main.jinja2 +61 -0
- {ag2-0.9.1.post0.dist-info → ag2-0.9.3.dist-info}/WHEEL +0 -0
- {ag2-0.9.1.post0.dist-info → ag2-0.9.3.dist-info}/licenses/LICENSE +0 -0
- {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
|
-
|
|
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.
|
|
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"
|
|
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
|
|
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
|
|
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)
|
autogen/oai/gemini_types.py
CHANGED
|
@@ -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"]
|