From c76c67a69c897be822335bedce59eb47f0f77e1d Mon Sep 17 00:00:00 2001 From: Richard Beales Date: Wed, 14 Jun 2023 10:03:11 +0100 Subject: [PATCH 01/18] Introduce method to ignore unexpected command params (#3570) Co-authored-by: Nicholas Tindle Co-authored-by: Reinier van der Leer Co-authored-by: Luke K <2609441+lc0rp@users.noreply.github.com> --- autogpt/commands/command.py | 30 +++++++++++++++++++++++++++++ autogpt/commands/file_operations.py | 4 +++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/autogpt/commands/command.py b/autogpt/commands/command.py index 742cc8df..ed93589f 100644 --- a/autogpt/commands/command.py +++ b/autogpt/commands/command.py @@ -1,6 +1,7 @@ import functools import importlib import inspect +from inspect import Parameter from typing import Any, Callable, Optional from autogpt.config import Config @@ -175,3 +176,32 @@ def command( return wrapper return decorator + + +def ignore_unexpected_kwargs(func: Callable[..., Any]) -> Callable[..., Any]: + def filter_kwargs(kwargs: dict) -> dict: + sig = inspect.signature(func) + # Parameter.VAR_KEYWORD - a dict of keyword arguments that aren't bound to any other + if any(map(lambda p: p.kind == Parameter.VAR_KEYWORD, sig.parameters.values())): + # if **kwargs exist, return directly + return kwargs + + _params = list( + filter( + lambda p: p.kind + in {Parameter.KEYWORD_ONLY, Parameter.POSITIONAL_OR_KEYWORD}, + sig.parameters.values(), + ) + ) + + res_kwargs = { + param.name: kwargs[param.name] for param in _params if param.name in kwargs + } + return res_kwargs + + @functools.wraps(func) + def wrapper(*args, **kwargs) -> Any: + kwargs = filter_kwargs(kwargs) + return func(*args, **kwargs) + + return wrapper diff --git a/autogpt/commands/file_operations.py b/autogpt/commands/file_operations.py index a0a61539..b851d662 100644 --- a/autogpt/commands/file_operations.py +++ b/autogpt/commands/file_operations.py @@ -13,8 +13,9 @@ from confection import Config from requests.adapters import HTTPAdapter, Retry from autogpt.agent.agent import Agent -from autogpt.commands.command import command +from autogpt.commands.command import command, ignore_unexpected_kwargs from autogpt.commands.file_operations_utils import read_textual_file +from autogpt.config import Config from autogpt.logs import logger from autogpt.memory.vector import MemoryItem, VectorMemory from autogpt.spinner import Spinner @@ -308,6 +309,7 @@ def delete_file(filename: str, agent: Agent) -> str: @command("list_files", "List Files in Directory", '"directory": ""') +@ignore_unexpected_kwargs def list_files(directory: str, agent: Agent) -> list[str]: """lists files in a directory recursively From c17d825bbac86365488b586b2c05660e74a9276e Mon Sep 17 00:00:00 2001 From: gravelBridge Date: Wed, 14 Jun 2023 03:43:06 -0700 Subject: [PATCH 02/18] Remove urls tts macos (#4260) Co-authored-by: k-boikov <64261260+k-boikov@users.noreply.github.com> Co-authored-by: Nicholas Tindle Co-authored-by: Luke K (pr-0f3t) <2609441+lc0rp@users.noreply.github.com> --- autogpt/speech/base.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/autogpt/speech/base.py b/autogpt/speech/base.py index a7570d94..07c8d9fe 100644 --- a/autogpt/speech/base.py +++ b/autogpt/speech/base.py @@ -1,5 +1,6 @@ """Base class for all voice classes.""" import abc +import re from threading import Lock from autogpt.singleton import AbstractSingleton @@ -29,6 +30,11 @@ class VoiceBase(AbstractSingleton): text (str): The text to say. voice_index (int): The index of the voice to use. """ + text = re.sub( + r"\b(?:https?://[-\w_.]+/?\w[-\w_.]*\.(?:[-\w_.]+/?\w[-\w_.]*\.)?[a-z]+(?:/[-\w_.%]+)*\b(?!\.))", + "", + text, + ) with self._mutex: return self._speech(text, voice_index) From bbaa5b89c2c0e81b8636029fcf43f85b5b1c7240 Mon Sep 17 00:00:00 2001 From: Reinier van der Leer Date: Thu, 15 Jun 2023 13:16:25 +0200 Subject: [PATCH 03/18] Add CI trigger for release-* branches --- .github/workflows/ci.yml | 16 ++++++++-------- .github/workflows/docker-ci.yml | 2 +- .github/workflows/pr-label.yml | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0718c46f..3e21d1d7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,18 +7,18 @@ on: - 'tests/Auto-GPT-test-cassettes' - 'tests/challenges/current_score.json' pull_request: - branches: [ stable, master ] + branches: [ stable, master, release-* ] pull_request_target: - branches: [ master, ci-test* ] + branches: [ master, release-*, ci-test* ] concurrency: - group: ${{ format('ci-{0}', github.head_ref && format('pr-{0}', github.event.pull_request.number) || github.sha) }} - cancel-in-progress: ${{ startsWith(github.event_name, 'pull_request') && github.event.pull_request.head.repo.fork == (github.event_name == 'pull_request_target') }} + group: ${{ format('ci-{0}', github.head_ref && format('{0}-{1}', github.event_name, github.event.pull_request.number) || github.sha) }} + cancel-in-progress: ${{ startsWith(github.event_name, 'pull_request') }} jobs: lint: - # eliminate duplicate runs on master - if: github.event_name == 'push' || github.base_ref != 'master' || (github.event.pull_request.head.repo.fork == (github.event_name == 'pull_request_target')) + # eliminate duplicate runs + if: github.event_name == 'push' || (github.event.pull_request.head.repo.fork == (github.event_name == 'pull_request_target')) runs-on: ubuntu-latest env: @@ -73,8 +73,8 @@ jobs: $cmd --check || (echo "You have unused imports or pass statements, please run '${cmd} --in-place'" && exit 1) test: - # eliminate duplicate runs on master - if: github.event_name == 'push' || github.base_ref != 'master' || (github.event.pull_request.head.repo.fork == (github.event_name == 'pull_request_target')) + # eliminate duplicate runs + if: github.event_name == 'push' || (github.event.pull_request.head.repo.fork == (github.event_name == 'pull_request_target')) permissions: # Gives the action the necessary permissions for publishing new diff --git a/.github/workflows/docker-ci.yml b/.github/workflows/docker-ci.yml index cacb58c5..3da88891 100644 --- a/.github/workflows/docker-ci.yml +++ b/.github/workflows/docker-ci.yml @@ -7,7 +7,7 @@ on: - 'tests/Auto-GPT-test-cassettes' - 'tests/challenges/current_score.json' pull_request: - branches: [ master, stable ] + branches: [ master, release-*, stable ] concurrency: group: ${{ format('docker-ci-{0}', github.head_ref && format('pr-{0}', github.event.pull_request.number) || github.sha) }} diff --git a/.github/workflows/pr-label.yml b/.github/workflows/pr-label.yml index e024f34b..ebeb7305 100644 --- a/.github/workflows/pr-label.yml +++ b/.github/workflows/pr-label.yml @@ -3,7 +3,7 @@ name: "Pull Request auto-label" on: # So that PRs touching the same files as the push are updated push: - branches: [ master ] + branches: [ master, release-* ] paths-ignore: - 'tests/Auto-GPT-test-cassettes' - 'tests/challenges/current_score.json' From a1e5be707763313b3841889ec6fafebfe09f3407 Mon Sep 17 00:00:00 2001 From: Reinier van der Leer Date: Thu, 15 Jun 2023 17:14:24 +0200 Subject: [PATCH 04/18] Update OpenAI model info and remove duplicate modelsinfo.py (#4700) * Update OpenAI model info and remove duplicate modelsinfo.py * Fix max_tokens for gpt-4-0613 --- autogpt/llm/api_manager.py | 13 ++++---- autogpt/llm/base.py | 15 ++++++--- autogpt/llm/modelsinfo.py | 11 ------- autogpt/llm/providers/openai.py | 44 +++++++++++++++---------- tests/unit/test_api_manager.py | 57 +++++++++++++++++++++------------ 5 files changed, 81 insertions(+), 59 deletions(-) delete mode 100644 autogpt/llm/modelsinfo.py diff --git a/autogpt/llm/api_manager.py b/autogpt/llm/api_manager.py index 7442579d..7a384562 100644 --- a/autogpt/llm/api_manager.py +++ b/autogpt/llm/api_manager.py @@ -6,8 +6,8 @@ import openai from openai import Model from autogpt.config import Config -from autogpt.llm.base import MessageDict -from autogpt.llm.modelsinfo import COSTS +from autogpt.llm.base import CompletionModelInfo, MessageDict +from autogpt.llm.providers.openai import OPEN_AI_MODELS from autogpt.logs import logger from autogpt.singleton import Singleton @@ -83,13 +83,14 @@ class ApiManager(metaclass=Singleton): """ # the .model property in API responses can contain version suffixes like -v2 model = model[:-3] if model.endswith("-v2") else model + model_info = OPEN_AI_MODELS[model] self.total_prompt_tokens += prompt_tokens self.total_completion_tokens += completion_tokens - self.total_cost += ( - prompt_tokens * COSTS[model]["prompt"] - + completion_tokens * COSTS[model]["completion"] - ) / 1000 + self.total_cost += prompt_tokens * model_info.prompt_token_cost / 1000 + if issubclass(type(model_info), CompletionModelInfo): + self.total_cost += completion_tokens * model_info.completion_token_cost / 1000 + logger.debug(f"Total running cost: ${self.total_cost:.3f}") def set_total_budget(self, total_budget): diff --git a/autogpt/llm/base.py b/autogpt/llm/base.py index 76bd3db1..43cc0ad9 100644 --- a/autogpt/llm/base.py +++ b/autogpt/llm/base.py @@ -31,22 +31,27 @@ class ModelInfo: Would be lovely to eventually get this directly from APIs, but needs to be scraped from websites for now. - """ name: str - prompt_token_cost: float - completion_token_cost: float max_tokens: int + prompt_token_cost: float @dataclass -class ChatModelInfo(ModelInfo): +class CompletionModelInfo(ModelInfo): + """Struct for generic completion model information.""" + + completion_token_cost: float + + +@dataclass +class ChatModelInfo(CompletionModelInfo): """Struct for chat model information.""" @dataclass -class TextModelInfo(ModelInfo): +class TextModelInfo(CompletionModelInfo): """Struct for text completion model information.""" diff --git a/autogpt/llm/modelsinfo.py b/autogpt/llm/modelsinfo.py deleted file mode 100644 index 425472de..00000000 --- a/autogpt/llm/modelsinfo.py +++ /dev/null @@ -1,11 +0,0 @@ -COSTS = { - "gpt-3.5-turbo": {"prompt": 0.002, "completion": 0.002}, - "gpt-3.5-turbo-0301": {"prompt": 0.002, "completion": 0.002}, - "gpt-4-0314": {"prompt": 0.03, "completion": 0.06}, - "gpt-4": {"prompt": 0.03, "completion": 0.06}, - "gpt-4-0314": {"prompt": 0.03, "completion": 0.06}, - "gpt-4-32k": {"prompt": 0.06, "completion": 0.12}, - "gpt-4-32k-0314": {"prompt": 0.06, "completion": 0.12}, - "text-embedding-ada-002": {"prompt": 0.0004, "completion": 0.0}, - "text-davinci-003": {"prompt": 0.02, "completion": 0.02}, -} diff --git a/autogpt/llm/providers/openai.py b/autogpt/llm/providers/openai.py index acaf0671..b4254cd1 100644 --- a/autogpt/llm/providers/openai.py +++ b/autogpt/llm/providers/openai.py @@ -3,23 +3,23 @@ from autogpt.llm.base import ChatModelInfo, EmbeddingModelInfo, TextModelInfo OPEN_AI_CHAT_MODELS = { info.name: info for info in [ - ChatModelInfo( - name="gpt-3.5-turbo", - prompt_token_cost=0.002, - completion_token_cost=0.002, - max_tokens=4096, - ), ChatModelInfo( name="gpt-3.5-turbo-0301", - prompt_token_cost=0.002, + prompt_token_cost=0.0015, completion_token_cost=0.002, max_tokens=4096, ), ChatModelInfo( - name="gpt-4", - prompt_token_cost=0.03, - completion_token_cost=0.06, - max_tokens=8192, + name="gpt-3.5-turbo-0613", + prompt_token_cost=0.0015, + completion_token_cost=0.002, + max_tokens=4096, + ), + ChatModelInfo( + name="gpt-3.5-turbo-16k-0613", + prompt_token_cost=0.003, + completion_token_cost=0.004, + max_tokens=16384, ), ChatModelInfo( name="gpt-4-0314", @@ -28,10 +28,10 @@ OPEN_AI_CHAT_MODELS = { max_tokens=8192, ), ChatModelInfo( - name="gpt-4-32k", - prompt_token_cost=0.06, - completion_token_cost=0.12, - max_tokens=32768, + name="gpt-4-0613", + prompt_token_cost=0.03, + completion_token_cost=0.06, + max_tokens=8192, ), ChatModelInfo( name="gpt-4-32k-0314", @@ -39,8 +39,19 @@ OPEN_AI_CHAT_MODELS = { completion_token_cost=0.12, max_tokens=32768, ), + ChatModelInfo( + name="gpt-4-32k-0613", + prompt_token_cost=0.06, + completion_token_cost=0.12, + max_tokens=32768, + ), ] } +# Set aliases for rolling model IDs +OPEN_AI_CHAT_MODELS["gpt-3.5-turbo"] = OPEN_AI_CHAT_MODELS["gpt-3.5-turbo-0301"] +OPEN_AI_CHAT_MODELS["gpt-3.5-turbo-16k"] = OPEN_AI_CHAT_MODELS["gpt-3.5-turbo-16k-0613"] +OPEN_AI_CHAT_MODELS["gpt-4"] = OPEN_AI_CHAT_MODELS["gpt-4-0314"] +OPEN_AI_CHAT_MODELS["gpt-4-32k"] = OPEN_AI_CHAT_MODELS["gpt-4-32k-0314"] OPEN_AI_TEXT_MODELS = { info.name: info @@ -59,8 +70,7 @@ OPEN_AI_EMBEDDING_MODELS = { for info in [ EmbeddingModelInfo( name="text-embedding-ada-002", - prompt_token_cost=0.0004, - completion_token_cost=0.0, + prompt_token_cost=0.0001, max_tokens=8191, embedding_dimensions=1536, ), diff --git a/tests/unit/test_api_manager.py b/tests/unit/test_api_manager.py index 9585fba7..e259f56a 100644 --- a/tests/unit/test_api_manager.py +++ b/tests/unit/test_api_manager.py @@ -1,8 +1,9 @@ from unittest.mock import MagicMock, patch import pytest +from pytest_mock import MockerFixture -from autogpt.llm.api_manager import COSTS, ApiManager +from autogpt.llm.api_manager import OPEN_AI_MODELS, ApiManager api_manager = ApiManager() @@ -14,16 +15,17 @@ def reset_api_manager(): @pytest.fixture(autouse=True) -def mock_costs(): - with patch.dict( - COSTS, - { - "gpt-3.5-turbo": {"prompt": 0.002, "completion": 0.002}, - "text-embedding-ada-002": {"prompt": 0.0004, "completion": 0}, - }, - clear=True, - ): - yield +def mock_costs(mocker: MockerFixture): + mocker.patch.multiple( + OPEN_AI_MODELS["gpt-3.5-turbo"], + prompt_token_cost=0.0013, + completion_token_cost=0.0025, + ) + mocker.patch.multiple( + OPEN_AI_MODELS["text-embedding-ada-002"], + prompt_token_cost=0.0004, + ) + yield class TestApiManager: @@ -87,15 +89,15 @@ class TestApiManager: assert api_manager.get_total_prompt_tokens() == 10 assert api_manager.get_total_completion_tokens() == 20 - assert api_manager.get_total_cost() == (10 * 0.002 + 20 * 0.002) / 1000 + assert api_manager.get_total_cost() == (10 * 0.0013 + 20 * 0.0025) / 1000 def test_getter_methods(self): """Test the getter methods for total tokens, cost, and budget.""" - api_manager.update_cost(60, 120, "gpt-3.5-turbo") + api_manager.update_cost(600, 1200, "gpt-3.5-turbo") api_manager.set_total_budget(10.0) - assert api_manager.get_total_prompt_tokens() == 60 - assert api_manager.get_total_completion_tokens() == 120 - assert api_manager.get_total_cost() == (60 * 0.002 + 120 * 0.002) / 1000 + assert api_manager.get_total_prompt_tokens() == 600 + assert api_manager.get_total_completion_tokens() == 1200 + assert api_manager.get_total_cost() == (600 * 0.0013 + 1200 * 0.0025) / 1000 assert api_manager.get_total_budget() == 10.0 @staticmethod @@ -107,7 +109,7 @@ class TestApiManager: assert api_manager.get_total_budget() == total_budget @staticmethod - def test_update_cost(): + def test_update_cost_completion_model(): """Test if updating the cost works correctly.""" prompt_tokens = 50 completion_tokens = 100 @@ -115,9 +117,24 @@ class TestApiManager: api_manager.update_cost(prompt_tokens, completion_tokens, model) - assert api_manager.get_total_prompt_tokens() == 50 - assert api_manager.get_total_completion_tokens() == 100 - assert api_manager.get_total_cost() == (50 * 0.002 + 100 * 0.002) / 1000 + assert api_manager.get_total_prompt_tokens() == prompt_tokens + assert api_manager.get_total_completion_tokens() == completion_tokens + assert ( + api_manager.get_total_cost() + == (prompt_tokens * 0.0013 + completion_tokens * 0.0025) / 1000 + ) + + @staticmethod + def test_update_cost_embedding_model(): + """Test if updating the cost works correctly.""" + prompt_tokens = 1337 + model = "text-embedding-ada-002" + + api_manager.update_cost(prompt_tokens, 0, model) + + assert api_manager.get_total_prompt_tokens() == prompt_tokens + assert api_manager.get_total_completion_tokens() == 0 + assert api_manager.get_total_cost() == (prompt_tokens * 0.0004) / 1000 @staticmethod def test_get_models(): From 3525a4b6dbed72408f72dc2c595570713492ddb4 Mon Sep 17 00:00:00 2001 From: merwanehamadi Date: Thu, 15 Jun 2023 09:09:59 -0700 Subject: [PATCH 05/18] Count tokens with tiktoken (#4704) * Update OpenAI model info and remove duplicate modelsinfo.py (#4700) * Update OpenAI model info and remove duplicate modelsinfo.py * Fix max_tokens for gpt-4-0613 Signed-off-by: Merwane Hamadi Co-authored-by: Merwane Hamadi * Update count_message_tokens to support new OpenAI models Signed-off-by: Merwane Hamadi Co-authored-by: Merwane Hamadi * Fix error message in count_message_tokens --------- Signed-off-by: Merwane Hamadi Co-authored-by: Erik Peterson Co-authored-by: Reinier van der Leer --- .gitignore | 1 + autogpt/llm/api_manager.py | 4 +++- autogpt/llm/utils/token_counter.py | 26 +++++++++++--------------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 307a6723..29a0285a 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ build/ develop-eggs/ dist/ plugins/ +plugins_config.yaml downloads/ eggs/ .eggs/ diff --git a/autogpt/llm/api_manager.py b/autogpt/llm/api_manager.py index 7a384562..454b4f22 100644 --- a/autogpt/llm/api_manager.py +++ b/autogpt/llm/api_manager.py @@ -89,7 +89,9 @@ class ApiManager(metaclass=Singleton): self.total_completion_tokens += completion_tokens self.total_cost += prompt_tokens * model_info.prompt_token_cost / 1000 if issubclass(type(model_info), CompletionModelInfo): - self.total_cost += completion_tokens * model_info.completion_token_cost / 1000 + self.total_cost += ( + completion_tokens * model_info.completion_token_cost / 1000 + ) logger.debug(f"Total running cost: ${self.total_cost:.3f}") diff --git a/autogpt/llm/utils/token_counter.py b/autogpt/llm/utils/token_counter.py index bd1dcf1b..e34dbd1c 100644 --- a/autogpt/llm/utils/token_counter.py +++ b/autogpt/llm/utils/token_counter.py @@ -24,32 +24,28 @@ def count_message_tokens( Returns: int: The number of tokens used by the list of messages. """ - try: - encoding = tiktoken.encoding_for_model(model) - except KeyError: - logger.warn("Warning: model not found. Using cl100k_base encoding.") - encoding = tiktoken.get_encoding("cl100k_base") - if model == "gpt-3.5-turbo": - # !Note: gpt-3.5-turbo may change over time. - # Returning num tokens assuming gpt-3.5-turbo-0301.") - return count_message_tokens(messages, model="gpt-3.5-turbo-0301") - elif model == "gpt-4": - # !Note: gpt-4 may change over time. Returning num tokens assuming gpt-4-0314.") - return count_message_tokens(messages, model="gpt-4-0314") - elif model == "gpt-3.5-turbo-0301": + if model.startswith("gpt-3.5-turbo"): tokens_per_message = ( 4 # every message follows <|start|>{role/name}\n{content}<|end|>\n ) tokens_per_name = -1 # if there's a name, the role is omitted - elif model == "gpt-4-0314": + encoding_model = "gpt-3.5-turbo" + elif model.startswith("gpt-4"): tokens_per_message = 3 tokens_per_name = 1 + encoding_model = "gpt-4" else: raise NotImplementedError( - f"num_tokens_from_messages() is not implemented for model {model}.\n" + f"count_message_tokens() is not implemented for model {model}.\n" " See https://github.com/openai/openai-python/blob/main/chatml.md for" " information on how messages are converted to tokens." ) + try: + encoding = tiktoken.encoding_for_model(encoding_model) + except KeyError: + logger.warn("Warning: model not found. Using cl100k_base encoding.") + encoding = tiktoken.get_encoding("cl100k_base") + num_tokens = 0 for message in messages: num_tokens += tokens_per_message From 426cfef882e61fb47e5390867083bd585ea7956a Mon Sep 17 00:00:00 2001 From: Reinier van der Leer Date: Thu, 15 Jun 2023 18:19:03 +0200 Subject: [PATCH 06/18] Unpin OpenAI model versions in model info mapping --- autogpt/llm/providers/openai.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/autogpt/llm/providers/openai.py b/autogpt/llm/providers/openai.py index b4254cd1..eede1a7d 100644 --- a/autogpt/llm/providers/openai.py +++ b/autogpt/llm/providers/openai.py @@ -48,10 +48,16 @@ OPEN_AI_CHAT_MODELS = { ] } # Set aliases for rolling model IDs -OPEN_AI_CHAT_MODELS["gpt-3.5-turbo"] = OPEN_AI_CHAT_MODELS["gpt-3.5-turbo-0301"] -OPEN_AI_CHAT_MODELS["gpt-3.5-turbo-16k"] = OPEN_AI_CHAT_MODELS["gpt-3.5-turbo-16k-0613"] -OPEN_AI_CHAT_MODELS["gpt-4"] = OPEN_AI_CHAT_MODELS["gpt-4-0314"] -OPEN_AI_CHAT_MODELS["gpt-4-32k"] = OPEN_AI_CHAT_MODELS["gpt-4-32k-0314"] +chat_model_mapping = { + "gpt-3.5-turbo": "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-16k": "gpt-3.5-turbo-16k-0613", + "gpt-4": "gpt-4-0314", + "gpt-4-32k": "gpt-4-32k-0314", +} +for alias, target in chat_model_mapping.items(): + alias_info = ChatModelInfo(**OPEN_AI_CHAT_MODELS[target].__dict__) + alias_info.name = alias + OPEN_AI_CHAT_MODELS[alias] = alias_info OPEN_AI_TEXT_MODELS = { info.name: info From 12588b6483b181d9389caa1419d408e7bb756ad9 Mon Sep 17 00:00:00 2001 From: Reinier van der Leer Date: Thu, 15 Jun 2023 18:50:26 +0200 Subject: [PATCH 07/18] Fix openai.py linting errors --- autogpt/llm/providers/openai.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/autogpt/llm/providers/openai.py b/autogpt/llm/providers/openai.py index eede1a7d..0f24b56e 100644 --- a/autogpt/llm/providers/openai.py +++ b/autogpt/llm/providers/openai.py @@ -49,10 +49,10 @@ OPEN_AI_CHAT_MODELS = { } # Set aliases for rolling model IDs chat_model_mapping = { - "gpt-3.5-turbo": "gpt-3.5-turbo-0301", + "gpt-3.5-turbo": "gpt-3.5-turbo-0301", "gpt-3.5-turbo-16k": "gpt-3.5-turbo-16k-0613", - "gpt-4": "gpt-4-0314", - "gpt-4-32k": "gpt-4-32k-0314", + "gpt-4": "gpt-4-0314", + "gpt-4-32k": "gpt-4-32k-0314", } for alias, target in chat_model_mapping.items(): alias_info = ChatModelInfo(**OPEN_AI_CHAT_MODELS[target].__dict__) From 244393e4ef4ee47d84b18958c2467a149c0201f5 Mon Sep 17 00:00:00 2001 From: Reinier van der Leer Date: Thu, 15 Jun 2023 23:11:52 +0200 Subject: [PATCH 08/18] Add fallback token limit in llm.utils.create_chat_completion --- autogpt/llm/utils/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/autogpt/llm/utils/__init__.py b/autogpt/llm/utils/__init__.py index 756c4bd5..fd7ba49f 100644 --- a/autogpt/llm/utils/__init__.py +++ b/autogpt/llm/utils/__init__.py @@ -17,6 +17,7 @@ from autogpt.logs import logger from ..api_manager import ApiManager from ..base import ChatSequence, Message +from ..providers.openai import OPEN_AI_CHAT_MODELS from .token_counter import * @@ -205,6 +206,8 @@ def create_chat_completion( model = prompt.model.name if temperature is None: temperature = cfg.temperature + if max_tokens is None: + max_tokens = OPEN_AI_CHAT_MODELS[model].max_tokens - prompt.token_length logger.debug( f"{Fore.GREEN}Creating chat completion with model {model}, temperature {temperature}, max_tokens {max_tokens}{Fore.RESET}" From 9943c58fbab0cace10cd58e8851cfbd9c26e9b9f Mon Sep 17 00:00:00 2001 From: Reinier van der Leer Date: Thu, 15 Jun 2023 23:59:15 +0200 Subject: [PATCH 09/18] Fix test_make_agent --- autogpt/llm/api_manager.py | 2 +- autogpt/llm/utils/__init__.py | 2 +- tests/unit/test_make_agent.py | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/autogpt/llm/api_manager.py b/autogpt/llm/api_manager.py index 454b4f22..acc38c44 100644 --- a/autogpt/llm/api_manager.py +++ b/autogpt/llm/api_manager.py @@ -34,7 +34,7 @@ class ApiManager(metaclass=Singleton): temperature: float = None, max_tokens: int | None = None, deployment_id=None, - ) -> str: + ): """ Create a chat completion and update the cost. Args: diff --git a/autogpt/llm/utils/__init__.py b/autogpt/llm/utils/__init__.py index fd7ba49f..736745cf 100644 --- a/autogpt/llm/utils/__init__.py +++ b/autogpt/llm/utils/__init__.py @@ -242,7 +242,7 @@ def create_chat_completion( max_tokens=max_tokens, ) - resp = response.choices[0].message["content"] + resp = response.choices[0].message.content for plugin in cfg.plugins: if not plugin.can_handle_on_response(): continue diff --git a/tests/unit/test_make_agent.py b/tests/unit/test_make_agent.py index 23eea027..61a7a6f5 100644 --- a/tests/unit/test_make_agent.py +++ b/tests/unit/test_make_agent.py @@ -11,10 +11,11 @@ def test_make_agent(agent: Agent, mocker: MockerFixture) -> None: mock = mocker.patch("openai.ChatCompletion.create") response = MagicMock() - # del response.error - response.choices[0].messages[0].content = "Test message" + response.choices[0].message.content = "Test message" response.usage.prompt_tokens = 1 response.usage.completion_tokens = 1 + del response.error + mock.return_value = response start_agent("Test Agent", "chat", "Hello, how are you?", agent, "gpt-3.5-turbo") agents = list_agents(agent) From e02105ee890567870e28f235e34eaf64169eb8d5 Mon Sep 17 00:00:00 2001 From: "Luke K (pr-0f3t)" <2609441+lc0rp@users.noreply.github.com> Date: Fri, 16 Jun 2023 08:28:58 -0300 Subject: [PATCH 10/18] Improve plugin backward compatibility (#4716) --- autogpt/plugins/__init__.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/autogpt/plugins/__init__.py b/autogpt/plugins/__init__.py index 60022352..4d84c9b5 100644 --- a/autogpt/plugins/__init__.py +++ b/autogpt/plugins/__init__.py @@ -254,11 +254,6 @@ def scan_plugins(cfg: Config, debug: bool = False) -> List[AutoGPTPluginTemplate logger.debug(f"Plugin: {plugin} Module: {module}") zipped_package = zipimporter(str(plugin)) zipped_module = zipped_package.load_module(str(module.parent)) - plugin_module_name = zipped_module.__name__.split(os.path.sep)[-1] - - if not plugins_config.is_enabled(plugin_module_name): - logger.warn(f"Plugin {plugin_module_name} found but not configured") - continue for key in dir(zipped_module): if key.startswith("__"): @@ -269,7 +264,26 @@ def scan_plugins(cfg: Config, debug: bool = False) -> List[AutoGPTPluginTemplate "_abc_impl" in a_keys and a_module.__name__ != "AutoGPTPluginTemplate" ): - loaded_plugins.append(a_module()) + plugin_name = a_module.__name__ + plugin_configured = plugins_config.get(plugin_name) is not None + plugin_enabled = plugins_config.is_enabled(plugin_name) + + if plugin_configured and plugin_enabled: + logger.debug( + f"Loading plugin {plugin_name} as it was enabled in config." + ) + loaded_plugins.append(a_module()) + elif plugin_configured and not plugin_enabled: + logger.debug( + f"Not loading plugin {plugin_name} as it was disabled in config." + ) + elif not plugin_configured: + logger.warn( + f"Not loading plugin {plugin_name} as it was not found in config. " + f"Please check your config. Starting with 0.4.1, plugins will not be loaded unless " + f"they are enabled in plugins_config.yaml. Zipped plugins should use the class " + f"name ({plugin_name}) as the key." + ) # OpenAI plugins if cfg.plugins_openai: From 4a307ad4eb2871f2946f8e2cfd2475ca9669e1aa Mon Sep 17 00:00:00 2001 From: Luke <2609441+lc0rp@users.noreply.github.com> Date: Fri, 16 Jun 2023 11:53:25 +0000 Subject: [PATCH 11/18] Fixed plugin test --- tests/unit/test_plugins.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_plugins.py b/tests/unit/test_plugins.py index 3a6f6d70..80aa1b9d 100644 --- a/tests/unit/test_plugins.py +++ b/tests/unit/test_plugins.py @@ -30,8 +30,8 @@ def test_scan_plugins_generic(config: Config): plugins_config.plugins["auto_gpt_guanaco"] = PluginConfig( name="auto_gpt_guanaco", enabled=True ) - plugins_config.plugins["auto_gpt_vicuna"] = PluginConfig( - name="auto_gptp_vicuna", enabled=True + plugins_config.plugins["AutoGPTPVicuna"] = PluginConfig( + name="AutoGPTPVicuna", enabled=True ) result = scan_plugins(config, debug=True) plugin_class_names = [plugin.__class__.__name__ for plugin in result] From 7f6f18f642459d30f1a17ba2947133eab95eecc7 Mon Sep 17 00:00:00 2001 From: "Luke K (pr-0f3t)" <2609441+lc0rp@users.noreply.github.com> Date: Sat, 17 Jun 2023 05:03:21 -0300 Subject: [PATCH 12/18] Bulletin.md update for 0.4.1 release (#4721) --- BULLETIN.md | 52 ++++++++++++++++---------------------------------- pyproject.toml | 2 +- 2 files changed, 17 insertions(+), 37 deletions(-) diff --git a/BULLETIN.md b/BULLETIN.md index 4c858b73..ba1de5a1 100644 --- a/BULLETIN.md +++ b/BULLETIN.md @@ -3,45 +3,25 @@ Check out *https://agpt.co*, the official news & updates site for Auto-GPT! The documentation also has a place here, at *https://docs.agpt.co* # For contributors 👷🏼 -Since releasing v0.3.0, we are working on re-architecting the Auto-GPT core to make -it more extensible and to make room for structural performance-oriented R&D. -In the meantime, we have less time to process incoming pull requests and issues, -so we focus on high-value contributions: - * significant bugfixes - * *major* improvements to existing functionality and/or docs (so no single-typo fixes) - * contributions that help us with re-architecture and other roadmapped items -We have to be somewhat selective in order to keep making progress, but this does not -mean you can't contribute. Check out the contribution guide on our wiki: +Since releasing v0.3.0, whave been working on re-architecting the Auto-GPT core to make it more extensible and make room for structural performance-oriented R&D. + +Check out the contribution guide on our wiki: https://github.com/Significant-Gravitas/Auto-GPT/wiki/Contributing -# 🚀 v0.4.0 Release 🚀 -Two weeks and 76 pull requests have passed since v0.3.1, and we are happy to announce -the release of v0.4.0! +# 🚀 v0.4.1 Release 🚀 +Two weeks and 50+ pull requests have passed since v0.4.0, and we are happy to announce the release of v0.4.1! -Highlights and notable changes since v0.3.0: - -## ⚠️ Command `send_tweet` is REMOVED -Twitter functionality (and more) is now covered by plugins. - -## ⚠️ Memory backend deprecation 💾 -The Milvus, Pinecone and Weaviate memory backends were rendered incompatible -by work on the memory system, and have been removed in `master`. The Redis -memory store was also temporarily removed; we will merge a new implementation ASAP. -Whether built-in support for the others will be added back in the future is subject to -discussion, feel free to pitch in: https://github.com/Significant-Gravitas/Auto-GPT/discussions/4280 - -## Document support in `read_file` 📄 -Auto-GPT can now read text from document files, with support added for PDF, DOCX, CSV, -HTML, TeX and more! - -## Managing Auto-GPT's access to commands ❌🔧 -You can now disable set of built-in commands through the *DISABLED_COMMAND_CATEGORIES* -variable in .env. Specific shell commands can also be disabled using *DENY_COMMANDS*, -or selectively enabled using *ALLOW_COMMANDS*. +Highlights and notable changes since v0.4.0: +- The .env.template is more readable and better explains the purpose of each environment variable. +- More dependable search + - The CUSTOM_SEARCH_ENGINE_ID variable has been replaced to GOOGLE_CUSTOM_SEARCH_ENGINE_ID, make sure you update it. +- Better read_file +- More reliable python code execution +- Lots of JSON error fixes +- Directory-based plugins ## Further fixes and changes 🛠️ -Other highlights include improvements to self-feedback mode and continuous mode, -documentation, docker and devcontainer setups, and much more. Most of the improvements -that were made are not yet visible to users, but will pay off in the long term. -Take a look at the Release Notes on Github for the full changelog! +Under the hood, we've done a bunch of work improving architectures and streamlining code. Most of that won't be user-visible + +## Take a look at the Release Notes on Github for the full changelog! https://github.com/Significant-Gravitas/Auto-GPT/releases diff --git a/pyproject.toml b/pyproject.toml index d695ac08..d795f53e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "agpt" -version = "0.4.0" +version = "0.4.1" authors = [ { name="Torantulino", email="support@agpt.co" }, ] From 7bac56b57d30770df0f25a4fbcd3226a63b1e4e4 Mon Sep 17 00:00:00 2001 From: Erik Peterson Date: Sat, 17 Jun 2023 14:45:34 -0700 Subject: [PATCH 13/18] Fix execute_command coming from plugins (#4730) --- autogpt/agent/agent.py | 5 ++--- autogpt/app.py | 8 ++++---- tests/unit/test_execute_command.py | 24 ++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 tests/unit/test_execute_command.py diff --git a/autogpt/agent/agent.py b/autogpt/agent/agent.py index 5a236f67..1f31be16 100644 --- a/autogpt/agent/agent.py +++ b/autogpt/agent/agent.py @@ -291,9 +291,8 @@ class Agent: command_name, arguments ) command_result = execute_command( - self.command_registry, - command_name, - arguments, + command_name=command_name, + arguments=arguments, agent=self, ) result = f"Command {command_name} returned: " f"{command_result}" diff --git a/autogpt/app.py b/autogpt/app.py index eb25fa7d..780b74a0 100644 --- a/autogpt/app.py +++ b/autogpt/app.py @@ -4,7 +4,7 @@ from typing import Dict, List, Union from autogpt.agent.agent import Agent from autogpt.agent.agent_manager import AgentManager -from autogpt.commands.command import CommandRegistry, command +from autogpt.commands.command import command from autogpt.commands.web_requests import scrape_links, scrape_text from autogpt.processing.text import summarize_text from autogpt.speech import say_text @@ -84,7 +84,6 @@ def map_command_synonyms(command_name: str): def execute_command( - command_registry: CommandRegistry, command_name: str, arguments: dict[str, str], agent: Agent, @@ -94,12 +93,13 @@ def execute_command( Args: command_name (str): The name of the command to execute arguments (dict): The arguments for the command + agent (Agent): The agent that is executing the command Returns: str: The result of the command """ try: - cmd = command_registry.commands.get(command_name) + cmd = agent.command_registry.commands.get(command_name) # If the command is found, call it with the provided arguments if cmd: @@ -111,7 +111,7 @@ def execute_command( # TODO: Change these to take in a file rather than pasted code, if # non-file is given, return instructions "Input should be a python # filepath, write your code to file and try again - for command in agent.prompt.commands: + for command in agent.ai_config.prompt_generator.commands: if ( command_name == command["label"].lower() or command_name == command["name"].lower() diff --git a/tests/unit/test_execute_command.py b/tests/unit/test_execute_command.py new file mode 100644 index 00000000..fb3f043a --- /dev/null +++ b/tests/unit/test_execute_command.py @@ -0,0 +1,24 @@ +from autogpt.agent import Agent +from autogpt.app import execute_command + + +def check_plan(): + return "hi" + + +def test_execute_command_plugin(agent: Agent): + """Test that executing a command that came from a plugin works as expected""" + agent.ai_config.prompt_generator.add_command( + "check_plan", + "Read the plan.md with the next goals to achieve", + {}, + check_plan, + ) + command_name = "check_plan" + arguments = {} + command_result = execute_command( + command_name=command_name, + arguments=arguments, + agent=agent, + ) + assert command_result == "hi" From 71ca4ea9904ecd07a1287fa5b146490be6bd2945 Mon Sep 17 00:00:00 2001 From: Luke <2609441+lc0rp@users.noreply.github.com> Date: Mon, 19 Jun 2023 19:36:46 +0000 Subject: [PATCH 14/18] Updates to sync 0.4.1 to master --- autogpt/app.py | 6 ---- tests/unit/test_api_manager.py | 64 +++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/autogpt/app.py b/autogpt/app.py index 8586653c..78e3a4dd 100644 --- a/autogpt/app.py +++ b/autogpt/app.py @@ -3,12 +3,6 @@ import json from typing import Dict from autogpt.agent.agent import Agent -from autogpt.agent.agent_manager import AgentManager -from autogpt.commands.command import command -from autogpt.commands.web_requests import scrape_links, scrape_text -from autogpt.processing.text import summarize_text -from autogpt.speech import say_text -from autogpt.url_utils.validators import validate_url def is_valid_int(value: str) -> bool: diff --git a/tests/unit/test_api_manager.py b/tests/unit/test_api_manager.py index 4a21a891..e259f56a 100644 --- a/tests/unit/test_api_manager.py +++ b/tests/unit/test_api_manager.py @@ -1,4 +1,4 @@ -from unittest.mock import patch +from unittest.mock import MagicMock, patch import pytest from pytest_mock import MockerFixture @@ -29,6 +29,68 @@ def mock_costs(mocker: MockerFixture): class TestApiManager: + @staticmethod + def test_create_chat_completion_debug_mode(caplog): + """Test if debug mode logs response.""" + api_manager_debug = ApiManager(debug=True) + messages = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Who won the world series in 2020?"}, + ] + model = "gpt-3.5-turbo" + + with patch("openai.ChatCompletion.create") as mock_create: + mock_response = MagicMock() + del mock_response.error + mock_response.usage.prompt_tokens = 10 + mock_response.usage.completion_tokens = 20 + mock_create.return_value = mock_response + + api_manager_debug.create_chat_completion(messages, model=model) + + assert "Response" in caplog.text + + @staticmethod + def test_create_chat_completion_empty_messages(): + """Test if empty messages result in zero tokens and cost.""" + messages = [] + model = "gpt-3.5-turbo" + + with patch("openai.ChatCompletion.create") as mock_create: + mock_response = MagicMock() + del mock_response.error + mock_response.usage.prompt_tokens = 0 + mock_response.usage.completion_tokens = 0 + mock_create.return_value = mock_response + + api_manager.create_chat_completion(messages, model=model) + + assert api_manager.get_total_prompt_tokens() == 0 + assert api_manager.get_total_completion_tokens() == 0 + assert api_manager.get_total_cost() == 0 + + @staticmethod + def test_create_chat_completion_valid_inputs(): + """Test if valid inputs result in correct tokens and cost.""" + messages = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Who won the world series in 2020?"}, + ] + model = "gpt-3.5-turbo" + + with patch("openai.ChatCompletion.create") as mock_create: + mock_response = MagicMock() + del mock_response.error + mock_response.usage.prompt_tokens = 10 + mock_response.usage.completion_tokens = 20 + mock_create.return_value = mock_response + + api_manager.create_chat_completion(messages, model=model) + + assert api_manager.get_total_prompt_tokens() == 10 + assert api_manager.get_total_completion_tokens() == 20 + assert api_manager.get_total_cost() == (10 * 0.0013 + 20 * 0.0025) / 1000 + def test_getter_methods(self): """Test the getter methods for total tokens, cost, and budget.""" api_manager.update_cost(600, 1200, "gpt-3.5-turbo") From 2a8f4ce0b74e2ca01f098b9f8bdc479a87190e41 Mon Sep 17 00:00:00 2001 From: Luke <2609441+lc0rp@users.noreply.github.com> Date: Mon, 19 Jun 2023 19:46:46 +0000 Subject: [PATCH 15/18] Fixing syntax error from mis-merge --- autogpt/llm/api_manager.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/autogpt/llm/api_manager.py b/autogpt/llm/api_manager.py index aaf93421..3a9d3eac 100644 --- a/autogpt/llm/api_manager.py +++ b/autogpt/llm/api_manager.py @@ -4,14 +4,8 @@ from typing import List, Optional import openai from openai import Model - -<<<<<<< HEAD -from autogpt.config import Config -from autogpt.llm.base import CompletionModelInfo, MessageDict +from autogpt.llm.base import CompletionModelInfo from autogpt.llm.providers.openai import OPEN_AI_MODELS -======= -from autogpt.llm.modelsinfo import COSTS ->>>>>>> upstream/master from autogpt.logs import logger from autogpt.singleton import Singleton @@ -30,8 +24,8 @@ class ApiManager(metaclass=Singleton): self.total_cost = 0 self.total_budget = 0.0 self.models = None - - def update_cost(self, prompt_tokens, completion_tokens, model) + + def update_cost(self, prompt_tokens, completion_tokens, model): """ Update the total cost, prompt tokens, and completion tokens. From 2568164cb3546856d3c8edcb00399d4ba2c8d204 Mon Sep 17 00:00:00 2001 From: Luke <2609441+lc0rp@users.noreply.github.com> Date: Mon, 19 Jun 2023 20:54:52 +0000 Subject: [PATCH 16/18] Fixing circular imports --- autogpt/llm/api_manager.py | 2 +- autogpt/llm/providers/openai.py | 2 +- tests/integration/test_provider_openai.py | 15 +------------- tests/unit/test_api_manager.py | 3 ++- tests/unit/test_make_agent.py | 25 ----------------------- 5 files changed, 5 insertions(+), 42 deletions(-) delete mode 100644 tests/unit/test_make_agent.py diff --git a/autogpt/llm/api_manager.py b/autogpt/llm/api_manager.py index 3a9d3eac..afab6e4a 100644 --- a/autogpt/llm/api_manager.py +++ b/autogpt/llm/api_manager.py @@ -5,7 +5,6 @@ from typing import List, Optional import openai from openai import Model from autogpt.llm.base import CompletionModelInfo -from autogpt.llm.providers.openai import OPEN_AI_MODELS from autogpt.logs import logger from autogpt.singleton import Singleton @@ -35,6 +34,7 @@ class ApiManager(metaclass=Singleton): model (str): The model used for the API call. """ # the .model property in API responses can contain version suffixes like -v2 + from autogpt.llm.providers.openai import OPEN_AI_MODELS model = model[:-3] if model.endswith("-v2") else model model_info = OPEN_AI_MODELS[model] diff --git a/autogpt/llm/providers/openai.py b/autogpt/llm/providers/openai.py index 08375aa8..707a7db8 100644 --- a/autogpt/llm/providers/openai.py +++ b/autogpt/llm/providers/openai.py @@ -9,7 +9,6 @@ from colorama import Fore, Style from openai.error import APIError, RateLimitError, Timeout from openai.openai_object import OpenAIObject -from autogpt.llm.api_manager import ApiManager from autogpt.llm.base import ( ChatModelInfo, EmbeddingModelInfo, @@ -111,6 +110,7 @@ OPEN_AI_MODELS: dict[str, ChatModelInfo | EmbeddingModelInfo | TextModelInfo] = def meter_api(func): """Adds ApiManager metering to functions which make OpenAI API calls""" + from autogpt.llm.api_manager import ApiManager api_manager = ApiManager() openai_obj_processor = openai.util.convert_to_openai_object diff --git a/tests/integration/test_provider_openai.py b/tests/integration/test_provider_openai.py index f5ae65cf..f51ad9ac 100644 --- a/tests/integration/test_provider_openai.py +++ b/tests/integration/test_provider_openai.py @@ -2,7 +2,7 @@ from unittest.mock import MagicMock, patch import pytest -from autogpt.llm.api_manager import COSTS, ApiManager +from autogpt.llm.api_manager import ApiManager from autogpt.llm.providers import openai api_manager = ApiManager() @@ -14,19 +14,6 @@ def reset_api_manager(): yield -@pytest.fixture(autouse=True) -def mock_costs(): - with patch.dict( - COSTS, - { - "gpt-3.5-turbo": {"prompt": 0.002, "completion": 0.002}, - "text-embedding-ada-002": {"prompt": 0.0004, "completion": 0}, - }, - clear=True, - ): - yield - - class TestProviderOpenAI: @staticmethod def test_create_chat_completion_debug_mode(caplog): diff --git a/tests/unit/test_api_manager.py b/tests/unit/test_api_manager.py index e259f56a..c29aadf5 100644 --- a/tests/unit/test_api_manager.py +++ b/tests/unit/test_api_manager.py @@ -3,7 +3,8 @@ from unittest.mock import MagicMock, patch import pytest from pytest_mock import MockerFixture -from autogpt.llm.api_manager import OPEN_AI_MODELS, ApiManager +from autogpt.llm.api_manager import ApiManager +from autogpt.llm.providers.openai import OPEN_AI_MODELS api_manager = ApiManager() diff --git a/tests/unit/test_make_agent.py b/tests/unit/test_make_agent.py deleted file mode 100644 index 61a7a6f5..00000000 --- a/tests/unit/test_make_agent.py +++ /dev/null @@ -1,25 +0,0 @@ -from unittest.mock import MagicMock - -from pytest_mock import MockerFixture - -from autogpt.agent.agent import Agent -from autogpt.app import list_agents, start_agent - - -def test_make_agent(agent: Agent, mocker: MockerFixture) -> None: - """Test that an agent can be created""" - mock = mocker.patch("openai.ChatCompletion.create") - - response = MagicMock() - response.choices[0].message.content = "Test message" - response.usage.prompt_tokens = 1 - response.usage.completion_tokens = 1 - del response.error - - mock.return_value = response - start_agent("Test Agent", "chat", "Hello, how are you?", agent, "gpt-3.5-turbo") - agents = list_agents(agent) - assert "List of agents:\n0: chat" == agents - start_agent("Test Agent 2", "write", "Hello, how are you?", agent, "gpt-3.5-turbo") - agents = list_agents(agent.config) - assert "List of agents:\n0: chat\n1: write" == agents From 162d77707b0dcb6e6f2487a399363097d66ad26e Mon Sep 17 00:00:00 2001 From: Merwane Hamadi Date: Mon, 19 Jun 2023 16:13:30 -0700 Subject: [PATCH 17/18] Fix test API manager --- tests/unit/test_api_manager.py | 68 ++-------------------------------- 1 file changed, 3 insertions(+), 65 deletions(-) diff --git a/tests/unit/test_api_manager.py b/tests/unit/test_api_manager.py index c29aadf5..04242d57 100644 --- a/tests/unit/test_api_manager.py +++ b/tests/unit/test_api_manager.py @@ -4,7 +4,7 @@ import pytest from pytest_mock import MockerFixture from autogpt.llm.api_manager import ApiManager -from autogpt.llm.providers.openai import OPEN_AI_MODELS +from autogpt.llm.providers.openai import OPEN_AI_CHAT_MODELS, OPEN_AI_EMBEDDING_MODELS api_manager = ApiManager() @@ -18,80 +18,18 @@ def reset_api_manager(): @pytest.fixture(autouse=True) def mock_costs(mocker: MockerFixture): mocker.patch.multiple( - OPEN_AI_MODELS["gpt-3.5-turbo"], + OPEN_AI_CHAT_MODELS["gpt-3.5-turbo"], prompt_token_cost=0.0013, completion_token_cost=0.0025, ) mocker.patch.multiple( - OPEN_AI_MODELS["text-embedding-ada-002"], + OPEN_AI_EMBEDDING_MODELS["text-embedding-ada-002"], prompt_token_cost=0.0004, ) yield class TestApiManager: - @staticmethod - def test_create_chat_completion_debug_mode(caplog): - """Test if debug mode logs response.""" - api_manager_debug = ApiManager(debug=True) - messages = [ - {"role": "system", "content": "You are a helpful assistant."}, - {"role": "user", "content": "Who won the world series in 2020?"}, - ] - model = "gpt-3.5-turbo" - - with patch("openai.ChatCompletion.create") as mock_create: - mock_response = MagicMock() - del mock_response.error - mock_response.usage.prompt_tokens = 10 - mock_response.usage.completion_tokens = 20 - mock_create.return_value = mock_response - - api_manager_debug.create_chat_completion(messages, model=model) - - assert "Response" in caplog.text - - @staticmethod - def test_create_chat_completion_empty_messages(): - """Test if empty messages result in zero tokens and cost.""" - messages = [] - model = "gpt-3.5-turbo" - - with patch("openai.ChatCompletion.create") as mock_create: - mock_response = MagicMock() - del mock_response.error - mock_response.usage.prompt_tokens = 0 - mock_response.usage.completion_tokens = 0 - mock_create.return_value = mock_response - - api_manager.create_chat_completion(messages, model=model) - - assert api_manager.get_total_prompt_tokens() == 0 - assert api_manager.get_total_completion_tokens() == 0 - assert api_manager.get_total_cost() == 0 - - @staticmethod - def test_create_chat_completion_valid_inputs(): - """Test if valid inputs result in correct tokens and cost.""" - messages = [ - {"role": "system", "content": "You are a helpful assistant."}, - {"role": "user", "content": "Who won the world series in 2020?"}, - ] - model = "gpt-3.5-turbo" - - with patch("openai.ChatCompletion.create") as mock_create: - mock_response = MagicMock() - del mock_response.error - mock_response.usage.prompt_tokens = 10 - mock_response.usage.completion_tokens = 20 - mock_create.return_value = mock_response - - api_manager.create_chat_completion(messages, model=model) - - assert api_manager.get_total_prompt_tokens() == 10 - assert api_manager.get_total_completion_tokens() == 20 - assert api_manager.get_total_cost() == (10 * 0.0013 + 20 * 0.0025) / 1000 - def test_getter_methods(self): """Test the getter methods for total tokens, cost, and budget.""" api_manager.update_cost(600, 1200, "gpt-3.5-turbo") From f4c000a547e5351048eae8e6f919d268ffafd7e4 Mon Sep 17 00:00:00 2001 From: Merwane Hamadi Date: Mon, 19 Jun 2023 16:21:40 -0700 Subject: [PATCH 18/18] Fixes LLM thinking command descriptions are orders Signed-off-by: Merwane Hamadi --- autogpt/commands/execute_code.py | 8 ++++---- autogpt/commands/file_operations.py | 8 ++++---- autogpt/commands/git_operations.py | 2 +- autogpt/commands/image_gen.py | 2 +- autogpt/commands/web_search.py | 2 +- autogpt/commands/web_selenium.py | 2 +- autogpt/llm/api_manager.py | 2 ++ autogpt/llm/providers/openai.py | 1 + tests/unit/test_api_manager.py | 2 +- 9 files changed, 16 insertions(+), 13 deletions(-) diff --git a/autogpt/commands/execute_code.py b/autogpt/commands/execute_code.py index 0b0f731c..2db78ccc 100644 --- a/autogpt/commands/execute_code.py +++ b/autogpt/commands/execute_code.py @@ -18,7 +18,7 @@ DENYLIST_CONTROL = "denylist" @command( "execute_python_code", - "Create a Python file and execute it", + "Creates a Python file and executes it", { "code": { "type": "string", @@ -63,7 +63,7 @@ def execute_python_code(code: str, name: str, agent: Agent) -> str: @command( "execute_python_file", - "Execute an existing Python file", + "Executes an existing Python file", { "filename": { "type": "string", @@ -191,7 +191,7 @@ def validate_command(command: str, config: Config) -> bool: @command( "execute_shell", - "Execute Shell Command, non-interactive commands only", + "Executes a Shell Command, non-interactive commands only", { "command_line": { "type": "string", @@ -237,7 +237,7 @@ def execute_shell(command_line: str, agent: Agent) -> str: @command( "execute_shell_popen", - "Execute Shell Command, non-interactive commands only", + "Executes a Shell Command, non-interactive commands only", { "query": { "type": "string", diff --git a/autogpt/commands/file_operations.py b/autogpt/commands/file_operations.py index d059493f..ca248743 100644 --- a/autogpt/commands/file_operations.py +++ b/autogpt/commands/file_operations.py @@ -176,7 +176,7 @@ def ingest_file( @command( "write_to_file", - "Write to file", + "Writes to a file", { "filename": { "type": "string", @@ -216,7 +216,7 @@ def write_to_file(filename: str, text: str, agent: Agent) -> str: @command( "append_to_file", - "Append to file", + "Appends to a file", { "filename": { "type": "string", @@ -261,7 +261,7 @@ def append_to_file( @command( "delete_file", - "Delete file", + "Deletes a file", { "filename": { "type": "string", @@ -291,7 +291,7 @@ def delete_file(filename: str, agent: Agent) -> str: @command( "list_files", - "List Files in Directory", + "Lists Files in a Directory", { "directory": { "type": "string", diff --git a/autogpt/commands/git_operations.py b/autogpt/commands/git_operations.py index 3832ca88..fc967e40 100644 --- a/autogpt/commands/git_operations.py +++ b/autogpt/commands/git_operations.py @@ -9,7 +9,7 @@ from autogpt.url_utils.validators import validate_url @command( "clone_repository", - "Clone Repository", + "Clones a Repository", { "url": { "type": "string", diff --git a/autogpt/commands/image_gen.py b/autogpt/commands/image_gen.py index d6bb73d8..c295392c 100644 --- a/autogpt/commands/image_gen.py +++ b/autogpt/commands/image_gen.py @@ -16,7 +16,7 @@ from autogpt.logs import logger @command( "generate_image", - "Generate Image", + "Generates an Image", { "prompt": { "type": "string", diff --git a/autogpt/commands/web_search.py b/autogpt/commands/web_search.py index 50b06e48..5af81058 100644 --- a/autogpt/commands/web_search.py +++ b/autogpt/commands/web_search.py @@ -15,7 +15,7 @@ DUCKDUCKGO_MAX_ATTEMPTS = 3 @command( "web_search", - "Search the web", + "Searches the web", { "query": { "type": "string", diff --git a/autogpt/commands/web_selenium.py b/autogpt/commands/web_selenium.py index 471e203b..821957f3 100644 --- a/autogpt/commands/web_selenium.py +++ b/autogpt/commands/web_selenium.py @@ -41,7 +41,7 @@ FILE_DIR = Path(__file__).parent.parent @command( "browse_website", - "Browse Website", + "Browses a Website", { "url": {"type": "string", "description": "The URL to visit", "required": True}, "question": { diff --git a/autogpt/llm/api_manager.py b/autogpt/llm/api_manager.py index afab6e4a..4e2aba9d 100644 --- a/autogpt/llm/api_manager.py +++ b/autogpt/llm/api_manager.py @@ -4,6 +4,7 @@ from typing import List, Optional import openai from openai import Model + from autogpt.llm.base import CompletionModelInfo from autogpt.logs import logger from autogpt.singleton import Singleton @@ -35,6 +36,7 @@ class ApiManager(metaclass=Singleton): """ # the .model property in API responses can contain version suffixes like -v2 from autogpt.llm.providers.openai import OPEN_AI_MODELS + model = model[:-3] if model.endswith("-v2") else model model_info = OPEN_AI_MODELS[model] diff --git a/autogpt/llm/providers/openai.py b/autogpt/llm/providers/openai.py index 707a7db8..add9954c 100644 --- a/autogpt/llm/providers/openai.py +++ b/autogpt/llm/providers/openai.py @@ -111,6 +111,7 @@ OPEN_AI_MODELS: dict[str, ChatModelInfo | EmbeddingModelInfo | TextModelInfo] = def meter_api(func): """Adds ApiManager metering to functions which make OpenAI API calls""" from autogpt.llm.api_manager import ApiManager + api_manager = ApiManager() openai_obj_processor = openai.util.convert_to_openai_object diff --git a/tests/unit/test_api_manager.py b/tests/unit/test_api_manager.py index 04242d57..615204d1 100644 --- a/tests/unit/test_api_manager.py +++ b/tests/unit/test_api_manager.py @@ -1,4 +1,4 @@ -from unittest.mock import MagicMock, patch +from unittest.mock import patch import pytest from pytest_mock import MockerFixture