Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DRAFT : PromptStrategy can use Individual ChatModelProviders & set own configuration (llm model, temperature, top_k, top_p...) #6898

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
226 changes: 226 additions & 0 deletions autogpts/autogpt/autogpt/adapters/openai/chatmodel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
import os
from typing import Any, Callable, Dict, ParamSpec, Tuple, TypeVar, Optional

import tiktoken
from openai.resources import AsyncCompletions

from autogpt.adapters.openai.configuration import (
OPEN_AI_CHAT_MODELS,
OPEN_AI_DEFAULT_CHAT_CONFIGS,
OPEN_AI_MODELS,
OpenAIModelName,
OpenAIPromptConfiguration,
OpenAISettings,
)

from langchain_core.messages import AIMessage , ChatMessage
from autogpt.core.configuration import Configurable
from autogpt.interfaces.adapters.chatmodel.chatmodel import (
AbstractChatModelProvider,
AbstractChatModelResponse,
AssistantChatMessage,
CompletionModelFunction,
)
from autogpt.interfaces.adapters.language_model import ModelTokenizer, LanguageModelResponse
import logging

LOG = logging.getLogger(__name__)

_T = TypeVar("_T")
_P = ParamSpec("_P")

# Example : LangChain Client
from langchain_openai import ChatOpenAI
from langchain_community.callbacks import get_openai_callback, OpenAICallbackHandler

# Example : OAClient :
# from openai import AsyncOpenAI

class ChatOpenAIAdapter(Configurable[OpenAISettings], AbstractChatModelProvider):

# Example : LangChain Client
callback : Optional[OpenAICallbackHandler] = None
llm_api_client = ChatOpenAI()

# Example : OAClient :
# llm_model = = AsyncOpenAI(api_key=os.environ["OPENAI_API_KEY"])

llmmodel_default : str = "gpt-3.5-turbo"
llmmodel_fine_tuned : str = "gpt-3.5-turbo"
llmmodel_cheap : str = "gpt-3.5-turbo"
llmmodel_code_expert_model : str = "gpt-3.5-turbo"
llmmodel_long_context_model : str = "gpt-3.5-turbo"



def __init__(
self,
settings: OpenAISettings = OpenAISettings(),
):
super().__init__(settings)
self._credentials = settings.credentials
self._budget = settings.budget

def get_token_limit(self, model_name: str) -> int:
return OPEN_AI_MODELS[model_name].max_tokens

def get_remaining_budget(self) -> float:
return self._budget.remaining_budget

@classmethod
def get_tokenizer(cls, model_name: OpenAIModelName) -> ModelTokenizer:
return tiktoken.encoding_for_model(model_name)

@classmethod
def count_tokens(cls, text: str, model_name: OpenAIModelName) -> int:
encoding = cls.get_tokenizer(model_name)
return len(encoding.encode(text))

@classmethod
def count_message_tokens(
cls,
messages: ChatMessage | list[ChatMessage],
model_name: OpenAIModelName,
) -> int:

if model_name.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
encoding_model = "gpt-3.5-turbo"
elif model_name.startswith("gpt-4"):
tokens_per_message = 3
tokens_per_name = 1
encoding_model = "gpt-4"
else:
raise NotImplementedError(
f"count_message_tokens() is not implemented for model {model_name}.\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:
LOG.warning(
f"Model {model_name} not found. Defaulting to cl100k_base encoding."
)
encoding = tiktoken.get_encoding("cl100k_base")

num_tokens = 0
for message in messages:
num_tokens += tokens_per_message
for key, value in message.dict().items():
num_tokens += len(encoding.encode(value))
if key == "name":
num_tokens += tokens_per_name
num_tokens += 3 # every reply is primed with <|start|>assistant<|message|>
return num_tokens

def extract_response_details(
self, response: AsyncCompletions, model_name: str
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
if (isinstance(response, AsyncCompletions)) :
response_args = LanguageModelResponse(
llm_model_info=OPEN_AI_CHAT_MODELS[model_name],
prompt_tokens=response.usage.prompt_tokens,
completion_tokens=response.usage.completion_tokens,
)
#response_message = response.choices[0].message.model_dump()
elif (isinstance(response, AIMessage)) :
# AGPT retro compatibility
response_args = LanguageModelResponse(
llm_model_info=OPEN_AI_CHAT_MODELS[model_name],
prompt_tokens= self.callback.prompt_tokens,
completion_tokens= self.callback.completion_tokens,
)
response.base_response = response_args
return response

def should_retry_function_call(
self, tools: list[CompletionModelFunction], response_message: Dict[str, Any]
) -> bool:
if not tools :
return False

if not isinstance(response_message, AIMessage):
# AGPT retro compatibility
if "tool_calls" not in response_message:
return True
else:
if "tool_calls" not in response_message.additional_kwargs:
return True

return False

def formulate_final_response(
self,
response_message: Dict[str, Any],
completion_parser: Callable[[AssistantChatMessage], _T],
**kwargs
) -> AbstractChatModelResponse[_T]:

response_info = response_message.base_response.model_dump()

response_message_dict = response_message.dict()
parsed_result = completion_parser(response_message)

response = AbstractChatModelResponse(
response=response_message_dict,
parsed_result=parsed_result,
**response_info,
)
self._budget.update_usage_and_cost(model_response=response)
return response

def __repr__(self):
return "OpenAIProvider()"

def get_default_config(self) -> OpenAIPromptConfiguration:
LOG.warning(f"Using {__class__.__name__} default config, we recommend setting individual model configs")
return OPEN_AI_DEFAULT_CHAT_CONFIGS.SMART_MODEL_32K

def make_tools_arg(self, tools : list[CompletionModelFunction]) -> dict:
return { "tools" : [self.make_tool(f) for f in tools] }

def make_tool(self, f : CompletionModelFunction) -> dict:
return {"type": "function", "function": f.schema(schema_builder=self.tool_builder)}

@staticmethod
def tool_builder(func: CompletionModelFunction) -> dict[str, str | dict | list]:
return {
"name": func.name,
"description": func.description,
"parameters": {
"type": "object",
"properties": {
name: param.to_dict() for name, param in func.parameters.items()
},
"required": [
name for name, param in func.parameters.items() if param.required
],
},
}

def make_tool_choice_arg(self , name : str) -> dict:
return {
"tool_choice" : {
"type": "function",
"function": {"name": name},
}
}

def make_model_arg(self, model_name : str) -> dict:
return { "model" : model_name }


async def chat(
self, messages: list[ChatMessage], *_, **llm_kwargs
) -> AsyncCompletions:

with get_openai_callback() as callback:
self.callback : OpenAICallbackHandler = callback
return await self.llm_api_client.ainvoke(input = messages , **llm_kwargs)

# Example : OAClient :
# return await self.llm_api_client.chat.completions.create( messages=messages, **llm_kwargs )
153 changes: 153 additions & 0 deletions autogpts/autogpt/autogpt/adapters/openai/configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import enum
import math
import os
from typing import Callable, ClassVar, ParamSpec, TypeVar

from openai import AsyncOpenAI

from autogpt.core.configuration import Field
from autogpt.interfaces.adapters.chatmodel import (
ChatModelInfo,
)
from autogpt.interfaces.adapters.chatmodel.chatmessage import AbstractChatMessage, AbstractRoleLabels
from autogpt.interfaces.adapters.language_model import (
AbstractPromptConfiguration,
LanguageModelProviderBudget,
LanguageModelProviderConfiguration,
BaseModelProviderCredentials,
LanguageModelProviderSettings,
BaseModelProviderUsage,
ModelProviderName,
ModelProviderService,
)
import logging

LOG = logging.getLogger(__name__)

_T = TypeVar("_T")
_P = ParamSpec("_P")


class OpenAIRoleLabel(AbstractRoleLabels):
USER : str = "user"
SYSTEM : str = "system"
ASSISTANT : str = "assistant"

FUNCTION : str = "function"
"""May be used for the return value of function calls"""


class OpenAIChatMessage(AbstractChatMessage):
_role_labels: ClassVar[OpenAIRoleLabel] = OpenAIRoleLabel()


class OpenAIModelName(str, enum.Enum):
# TODO : Remove
ADA = "text-embedding-ada-002"
GPT3 = "gpt-3.5-turbo"
GPT3_16k = "gpt-3.5-turbo"
GPT3_FINE_TUNED = "gpt-3.5-turbo" + ""
GPT4 = "gpt-3.5-turbo"
GPT4_32k = "gpt-3.5-turbo"


OPEN_AI_CHAT_MODELS = {
#TODO : USEFULL FOR AGPT BUDGET MANAGEMENT
info.name: info
for info in [
ChatModelInfo(
name=OpenAIModelName.GPT3,
service=ModelProviderService.CHAT,
provider_name=ModelProviderName.OPENAI,
prompt_token_cost=0.0015 / 1000,
completion_token_cost=0.002 / 1000,
max_tokens=4096,
),
ChatModelInfo(
name=OpenAIModelName.GPT3_16k,
service=ModelProviderService.CHAT,
provider_name=ModelProviderName.OPENAI,
prompt_token_cost=0.003 / 1000,
completion_token_cost=0.004 / 1000,
max_tokens=16384,
),
ChatModelInfo(
name=OpenAIModelName.GPT3_FINE_TUNED,
service=ModelProviderService.CHAT,
provider_name=ModelProviderName.OPENAI,
prompt_token_cost=0.0120 / 1000,
completion_token_cost=0.0160 / 1000,
max_tokens=4096,
),
ChatModelInfo(
name=OpenAIModelName.GPT4,
service=ModelProviderService.CHAT,
provider_name=ModelProviderName.OPENAI,
prompt_token_cost=0.03 / 1000,
completion_token_cost=0.06 / 1000,
max_tokens=8191,
),
ChatModelInfo(
name=OpenAIModelName.GPT4_32k,
service=ModelProviderService.CHAT,
provider_name=ModelProviderName.OPENAI,
prompt_token_cost=0.06 / 1000,
completion_token_cost=0.12 / 1000,
max_tokens=32768,
),
]
}


OPEN_AI_MODELS = {
**OPEN_AI_CHAT_MODELS,
}


class OpenAIProviderConfiguration(LanguageModelProviderConfiguration):
...

class OpenAIModelProviderBudget(LanguageModelProviderBudget):
graceful_shutdown_threshold: float = Field(default=0.005)
warning_threshold: float = Field(default=0.01)

total_budget: float = math.inf
total_cost: float = 0.0
remaining_budget: float = math.inf
usage: BaseModelProviderUsage = BaseModelProviderUsage()


class OpenAISettings(LanguageModelProviderSettings):
configuration: OpenAIProviderConfiguration = OpenAIProviderConfiguration()
credentials: BaseModelProviderCredentials = BaseModelProviderCredentials()
budget: OpenAIModelProviderBudget = OpenAIModelProviderBudget()
name : str = "chat_model_provider"
description : str = "Provides access to OpenAI's API."


class OpenAIPromptConfiguration(AbstractPromptConfiguration):
...

class OPEN_AI_DEFAULT_CHAT_CONFIGS:
# TODO : Can be removed
FAST_MODEL_4K = OpenAIPromptConfiguration(
llm_model_name=OpenAIModelName.GPT3,
temperature=0.7,
)
FAST_MODEL_16K = OpenAIPromptConfiguration(
llm_model_name=OpenAIModelName.GPT3_16k,
temperature=0.7,
)
FAST_MODEL_FINE_TUNED_4K = OpenAIPromptConfiguration(
llm_model_name=OpenAIModelName.GPT3_FINE_TUNED,
temperature=0.7,
)
MART_MODEL_8K = OpenAIPromptConfiguration(
llm_model_name=OpenAIModelName.GPT4,
temperature=0.7,
)
SMART_MODEL_32K = OpenAIPromptConfiguration(
llm_model_name=OpenAIModelName.GPT4_32k,
temperature=0.7,
)

Loading
Loading