autogen/test/agentchat/contrib/test_web_surfer.py
Gunnar Kudrjavets f68c09b035
Validate the OpenAI API key format (#1635)
* Validate the OpenAI API key format

Increase the amount of internal validation for OpenAI API keys. The intent is
to shorten the debugging loop in case of typos. The changes do *not* add
validation for Azure OpenAI API keys.

* Add the validation in `__init__` of `OpenAIClient`.

* Introduce the `MOCK_OPEN_AI_API_KEY` constant for testing.

*  Add unit test coverage for the `is_valid_api_key` function.

* Validate the OpenAI API key format

Increase the amount of internal validation for OpenAI API keys. The intent is
to shorten the debugging loop in case of typos. The changes do *not* add
validation for Azure OpenAI API keys.

* Add the validation in `__init__` of `OpenAIClient`.

* Introduce the `MOCK_OPEN_AI_API_KEY` constant for testing.

*Add unit test coverage for the `is_valid_api_key` function.

* Log a warning when register a default client fails.

* Validate the OpenAI API key format

Increase the amount of internal validation for OpenAI API keys. The intent is
to shorten the debugging loop in case of typos. The changes do *not* add
validation for Azure OpenAI API keys.

* Add the validation in `__init__` of `OpenAIClient`. We'll log a
  warning when the OpenAI API key isn't valid.

* Introduce the `MOCK_OPEN_AI_API_KEY` constant for testing.

* Add unit test coverage for the `is_valid_api_key` function.

* Check for OpenAI base_url before API key validation

---------

Co-authored-by: Chi Wang <wang.chi@microsoft.com>
2024-02-14 18:51:38 +00:00

188 lines
6.6 KiB
Python

import os
import sys
import re
import pytest
from autogen import UserProxyAgent, config_list_from_json
from autogen.oai.openai_utils import filter_config
from autogen.cache import Cache
sys.path.append(os.path.join(os.path.dirname(__file__), "../.."))
from conftest import MOCK_OPEN_AI_API_KEY, skip_openai # noqa: E402
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
from test_assistant_agent import KEY_LOC, OAI_CONFIG_LIST # noqa: E402
BLOG_POST_URL = "https://microsoft.github.io/autogen/blog/2023/04/21/LLM-tuning-math"
BLOG_POST_TITLE = "Does Model and Inference Parameter Matter in LLM Applications? - A Case Study for MATH | AutoGen"
BING_QUERY = "Microsoft"
try:
from autogen.agentchat.contrib.web_surfer import WebSurferAgent
except ImportError:
skip_all = True
else:
skip_all = False
try:
from openai import OpenAI
except ImportError:
skip_oai = True
else:
skip_oai = False or skip_openai
try:
BING_API_KEY = os.environ["BING_API_KEY"]
except KeyError:
skip_bing = True
else:
skip_bing = False
if not skip_oai:
config_list = config_list_from_json(env_or_file=OAI_CONFIG_LIST, file_location=KEY_LOC)
@pytest.mark.skipif(
skip_all,
reason="do not run if dependency is not installed",
)
def test_web_surfer() -> None:
with pytest.MonkeyPatch.context() as mp:
# we mock the API key so we can register functions (llm_config must be present for this to work)
mp.setenv("OPENAI_API_KEY", MOCK_OPEN_AI_API_KEY)
page_size = 4096
web_surfer = WebSurferAgent(
"web_surfer", llm_config={"config_list": []}, browser_config={"viewport_size": page_size}
)
# Sneak a peak at the function map, allowing us to call the functions for testing here
function_map = web_surfer._user_proxy._function_map
# Test some basic navigations
response = function_map["visit_page"](BLOG_POST_URL)
assert f"Address: {BLOG_POST_URL}".strip() in response
assert f"Title: {BLOG_POST_TITLE}".strip() in response
# Test scrolling
m = re.search(r"\bViewport position: Showing page 1 of (\d+).", response)
total_pages = int(m.group(1)) # type: ignore[union-attr]
response = function_map["page_down"]()
assert (
f"Viewport position: Showing page 2 of {total_pages}." in response
) # Assumes the content is longer than one screen
response = function_map["page_up"]()
assert f"Viewport position: Showing page 1 of {total_pages}." in response
# Try to scroll too far back up
response = function_map["page_up"]()
assert f"Viewport position: Showing page 1 of {total_pages}." in response
# Try to scroll too far down
for i in range(0, total_pages + 1):
response = function_map["page_down"]()
assert f"Viewport position: Showing page {total_pages} of {total_pages}." in response
# Test web search -- we don't have a key in this case, so we expect it to raise an error (but it means the code path is correct)
with pytest.raises(ValueError, match="Missing Bing API key."):
response = function_map["informational_web_search"](BING_QUERY)
with pytest.raises(ValueError, match="Missing Bing API key."):
response = function_map["navigational_web_search"](BING_QUERY)
# Test Q&A and summarization -- we don't have a key so we expect it to fail (but it means the code path is correct)
with pytest.raises(IndexError):
response = function_map["answer_from_page"]("When was it founded?")
with pytest.raises(IndexError):
response = function_map["summarize_page"]()
@pytest.mark.skipif(
skip_oai,
reason="do not run if oai is not installed",
)
def test_web_surfer_oai() -> None:
llm_config = {"config_list": config_list, "timeout": 180, "cache_seed": 42}
# adding Azure name variations to the model list
model = ["gpt-3.5-turbo-1106", "gpt-3.5-turbo-16k-0613", "gpt-3.5-turbo-16k"]
model += [m.replace(".", "") for m in model]
summarizer_llm_config = {
"config_list": filter_config(config_list, dict(model=model)), # type: ignore[no-untyped-call]
"timeout": 180,
}
assert len(llm_config["config_list"]) > 0 # type: ignore[arg-type]
assert len(summarizer_llm_config["config_list"]) > 0
page_size = 4096
web_surfer = WebSurferAgent(
"web_surfer",
llm_config=llm_config,
summarizer_llm_config=summarizer_llm_config,
browser_config={"viewport_size": page_size},
)
user_proxy = UserProxyAgent(
"user_proxy",
human_input_mode="NEVER",
code_execution_config=False,
default_auto_reply="",
is_termination_msg=lambda x: True,
)
with Cache.disk():
# Make some requests that should test function calling
user_proxy.initiate_chat(web_surfer, message="Please visit the page 'https://en.wikipedia.org/wiki/Microsoft'")
user_proxy.initiate_chat(web_surfer, message="Please scroll down.")
user_proxy.initiate_chat(web_surfer, message="Please scroll up.")
user_proxy.initiate_chat(web_surfer, message="When was it founded?")
user_proxy.initiate_chat(web_surfer, message="What's this page about?")
@pytest.mark.skipif(
skip_bing,
reason="do not run if bing api key is not available",
)
def test_web_surfer_bing() -> None:
page_size = 4096
web_surfer = WebSurferAgent(
"web_surfer",
llm_config={
"config_list": [
{
"model": "gpt-3.5-turbo-16k",
"api_key": "sk-PLACEHOLDER_KEY",
}
]
},
browser_config={"viewport_size": page_size, "bing_api_key": BING_API_KEY},
)
# Sneak a peak at the function map, allowing us to call the functions for testing here
function_map = web_surfer._user_proxy._function_map
# Test informational queries
response = function_map["informational_web_search"](BING_QUERY)
assert f"Address: bing: {BING_QUERY}" in response
assert f"Title: {BING_QUERY} - Search" in response
assert "Viewport position: Showing page 1 of 1." in response
assert f"A Bing search for '{BING_QUERY}' found " in response
# Test informational queries
response = function_map["navigational_web_search"](BING_QUERY + " Wikipedia")
assert "Address: https://en.wikipedia.org/wiki/" in response
if __name__ == "__main__":
"""Runs this file's tests from the command line."""
test_web_surfer()
test_web_surfer_oai()
test_web_surfer_bing()