CoACT initialize (#292)

This commit is contained in:
Linxin Song
2025-07-30 19:35:20 -07:00
committed by GitHub
parent 862d704b8c
commit b968155757
228 changed files with 42386 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
# Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
#
# SPDX-License-Identifier: Apache-2.0
from .google_search import GoogleSearchTool
from .youtube_search import YoutubeSearchTool
__all__ = ["GoogleSearchTool", "YoutubeSearchTool"]

View File

@@ -0,0 +1,93 @@
# Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
#
# SPDX-License-Identifier: Apache-2.0
import logging
from typing import Annotated, Any, Optional
from ....doc_utils import export_module
from ....import_utils import optional_import_block, require_optional_import
from ... import Depends, Tool
from ...dependency_injection import on
with optional_import_block():
from googleapiclient.discovery import build
@require_optional_import(
[
"googleapiclient",
],
"google-search",
)
def _execute_query(query: str, search_api_key: str, search_engine_id: str, num_results: int) -> Any:
service = build("customsearch", "v1", developerKey=search_api_key)
return service.cse().list(q=query, cx=search_engine_id, num=num_results).execute()
def _google_search(
query: str,
search_api_key: str,
search_engine_id: str,
num_results: int,
) -> list[dict[str, Any]]:
res = _execute_query(
query=query, search_api_key=search_api_key, search_engine_id=search_engine_id, num_results=num_results
)
return [
{"title": item.get("title", ""), "link": item.get("link", ""), "snippet": item.get("snippet", "")}
for item in res.get("items", [])
]
@export_module("autogen.tools.experimental")
class GoogleSearchTool(Tool):
"""GoogleSearchTool is a tool that uses the Google Search API to perform a search."""
def __init__(
self,
*,
search_api_key: Optional[str] = None,
search_engine_id: Optional[str] = None,
use_internal_llm_tool_if_available: bool = True,
):
"""GoogleSearchTool is a tool that uses the Google Search API to perform a search.
Args:
search_api_key: The API key for the Google Search API.
search_engine_id: The search engine ID for the Google Search API.
use_internal_llm_tool_if_available: Whether to use the predefined (e.g. Gemini GenaAI) search tool. Currently, this can only be used for agents with the Gemini (GenAI) configuration.
"""
self.search_api_key = search_api_key
self.search_engine_id = search_engine_id
self.use_internal_llm_tool_if_available = use_internal_llm_tool_if_available
if not use_internal_llm_tool_if_available and (search_api_key is None or search_engine_id is None):
raise ValueError(
"search_api_key and search_engine_id must be provided if use_internal_llm_tool_if_available is False"
)
if use_internal_llm_tool_if_available and (search_api_key is not None or search_engine_id is not None):
logging.warning("search_api_key and search_engine_id will be ignored if internal LLM tool is available")
def google_search(
query: Annotated[str, "The search query."],
search_api_key: Annotated[Optional[str], Depends(on(search_api_key))],
search_engine_id: Annotated[Optional[str], Depends(on(search_engine_id))],
num_results: Annotated[int, "The number of results to return."] = 10,
) -> list[dict[str, Any]]:
if search_api_key is None or search_engine_id is None:
raise ValueError(
"Your LLM is not configured to use prebuilt google-search tool.\n"
"Please provide search_api_key and search_engine_id.\n"
)
return _google_search(query, search_api_key, search_engine_id, num_results)
super().__init__(
# GeminiClient will look for a tool with the name "prebuilt_google_search"
name="prebuilt_google_search" if use_internal_llm_tool_if_available else "google_search",
description="Use the Google Search API to perform a search.",
func_or_tool=google_search,
)

View File

@@ -0,0 +1,181 @@
# Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
#
# SPDX-License-Identifier: Apache-2.0
import logging
from typing import Annotated, Any, Dict, List, Optional
from ....doc_utils import export_module
from ....import_utils import optional_import_block, require_optional_import
from ... import Depends, Tool
from ...dependency_injection import on
with optional_import_block():
import googleapiclient.errors
from googleapiclient.discovery import build
@require_optional_import(
["googleapiclient"],
"google-search",
)
def _execute_search_query(query: str, youtube_api_key: str, max_results: int) -> Any:
"""Execute a YouTube search query using the YouTube Data API.
Args:
query: The search query string.
youtube_api_key: The API key for the YouTube Data API.
max_results: The maximum number of results to return.
Returns:
The search response from the YouTube Data API.
"""
youtube = build("youtube", "v3", developerKey=youtube_api_key)
try:
search_response = (
youtube.search().list(q=query, part="id,snippet", maxResults=max_results, type="video").execute()
)
return search_response
except googleapiclient.errors.HttpError as e:
logging.error(f"An HTTP error occurred: {e}")
raise
@require_optional_import(
["googleapiclient"],
"google-search",
)
def _get_video_details(video_ids: List[str], youtube_api_key: str) -> Any:
"""Get detailed information about specific YouTube videos.
Args:
video_ids: List of YouTube video IDs.
youtube_api_key: The API key for the YouTube Data API.
Returns:
The video details response from the YouTube Data API.
"""
if not video_ids:
return {"items": []}
youtube = build("youtube", "v3", developerKey=youtube_api_key)
try:
videos_response = (
youtube.videos().list(id=",".join(video_ids), part="snippet,contentDetails,statistics").execute()
)
return videos_response
except googleapiclient.errors.HttpError as e:
logging.error(f"An HTTP error occurred: {e}")
raise
def _youtube_search(
query: str,
youtube_api_key: str,
max_results: int,
include_video_details: bool = True,
) -> List[Dict[str, Any]]:
"""Search YouTube videos based on a query.
Args:
query: The search query string.
youtube_api_key: The API key for the YouTube Data API.
max_results: The maximum number of results to return.
include_video_details: Whether to include detailed video information.
Returns:
A list of dictionaries containing information about the videos.
"""
search_response = _execute_search_query(query=query, youtube_api_key=youtube_api_key, max_results=max_results)
results = []
video_ids = []
# Extract basic info from search results
for item in search_response.get("items", []):
if item["id"]["kind"] == "youtube#video":
video_ids.append(item["id"]["videoId"])
video_info = {
"title": item["snippet"]["title"],
"description": item["snippet"]["description"],
"publishedAt": item["snippet"]["publishedAt"],
"channelTitle": item["snippet"]["channelTitle"],
"videoId": item["id"]["videoId"],
"url": f"https://www.youtube.com/watch?v={item['id']['videoId']}",
}
results.append(video_info)
# If detailed info requested, get it
if include_video_details and video_ids:
video_details = _get_video_details(video_ids, youtube_api_key)
# Create a mapping of videoId to details
details_map = {item["id"]: item for item in video_details.get("items", [])}
# Update results with additional details
for result in results:
video_id = result["videoId"]
if video_id in details_map:
details = details_map[video_id]
result.update({
"viewCount": details["statistics"].get("viewCount"),
"likeCount": details["statistics"].get("likeCount"),
"commentCount": details["statistics"].get("commentCount"),
"duration": details["contentDetails"].get("duration"),
"definition": details["contentDetails"].get("definition"),
})
return results
@export_module("autogen.tools.experimental")
class YoutubeSearchTool(Tool):
"""YoutubeSearchTool is a tool that uses the YouTube Data API to search for videos."""
def __init__(
self,
*,
youtube_api_key: Optional[str] = None,
):
"""Initialize a YouTube search tool.
Args:
youtube_api_key: The API key for the YouTube Data API.
"""
self.youtube_api_key = youtube_api_key
if youtube_api_key is None:
raise ValueError("youtube_api_key must be provided")
def youtube_search(
query: Annotated[str, "The search query for YouTube videos."],
youtube_api_key: Annotated[str, Depends(on(youtube_api_key))],
max_results: Annotated[int, "The maximum number of results to return."] = 5,
include_video_details: Annotated[bool, "Whether to include detailed video information."] = True,
) -> List[Dict[str, Any]]:
"""Search for YouTube videos based on a query.
Args:
query: The search query string.
youtube_api_key: The API key for the YouTube Data API.
max_results: The maximum number of results to return.
include_video_details: Whether to include detailed video information.
Returns:
A list of dictionaries containing information about the videos.
"""
if youtube_api_key is None:
raise ValueError("YouTube API key is required")
return _youtube_search(query, youtube_api_key, max_results, include_video_details)
super().__init__(
name="youtube_search",
description="Search for YouTube videos based on a query, optionally including detailed information.",
func_or_tool=youtube_search,
)