Package: agent-dingo
Agent DingoA microframework for buildling simple conversational AI agents.Dingoallows you to easily integrate any function into ChatGPT by adding a single line of code. WithDingo, you no longer have to worry about manually integrating the functions or dealing with intermediate function calls. The framework is crafted to automate these tasks, allowing you to focus on writing the core functionality.Quick Start ⚡️Step 1:Installagent-dingopipinstallagent-dingoStep 2:Configure your OpenAI API keyexportOPENAI_API_KEY=<YOUR_KEY>Step 3:Instantiate the agentfromagent_dingoimportAgentDingoagent=AgentDingo()Step 4:Addagent.functiondecorator to the function you wish to integrate@agent.functiondefget_current_weather(city:str):...Step 5:Run the conversationagent.chat("What is the current weather in Linz?")Optional:Run an OpenAI compatible serverfromagent_dingo.wrapperimportDingoWrapperDingoWrapper(agent).serve()The server can be accessed using theopenaipython package:importopenaiopenai.api_base="http://localhost:8080"r=openai.ChatCompletion.create(model="gpt-3.5-turbo",messages=[{"role":"user","content":"What is the current weather in Linz?"}],temperature=0.0,)Support us 🤝You can support the project in the following ways:⭐ StarDingoon GitHub (click the star button in the top right corner)💡 Provide your feedback or propose ideas in the issues section or Discord📰 Post aboutDingoon LinkedIn or other platforms🔗 Check out our other projects (cards below are clickable):Documentation 📚OpenAI API KeyDingois built around function calling feature of newer generation OpenAI chat models that were explicitly fine-tuned for these tasks.
Hence, an OpenAI key is required.You can either set theOPENAI_API_KEYenv variable or register the key using theopenaipython package.exportOPENAI_API_KEY=<YOUR_KEY>importopenaiopenai.api_key="<YOUR_KEY>"AgentAgentDingois a central part of the framework which allows you to register the functions to use with ChatGPT. The intermediate function calling is also handled by the agent directly.fromagent_dingoimportAgentDingoagent=AgentDingo()Registering the functionsOption 1(Recommended): Registering the function with a docstringBy default, the agent uses the information from the docstring to generate a function descriptor that is passed to the model. It is advised to always use the functions with docstrings as this way you can describe the purpose of the function (and its arguments) more accurately. The library was explicitly tested withgoogleandnumpydocstring styles.Example:@agent.functiondefget_temperature(city:str)->str:"""Retrieves the current temperature in a city.Parameters----------city : strThe city to get the temperature for.Returns-------strString representation of the json response from the weather api."""base_url="https://api.openweathermap.org/data/2.5/weather"params={"q":city,"appid":"<openweathermap_api_key>","units":"metric"}response=requests.get(base_url,params=params)data=response.json()returnstr(data)Option 2: Registering the function without a docstringIf the function does not have a docstring, it can still be registered. In that case, the docstring will be generated automatically by ChatGPT.
However, there are several drawbacks of this approach:The source code of your function is passed to the model;The generated docstring might be inaccurate (especially for complex functions);The generated docstrings are not persistant (i.e. they are not saved to disk) and will have to be re-generated every time the function is registered.If you wish to disable the automatic docstring generation, you can set theallow_codegenparameter toFalsewhen instantiating the agent.Example:agent=AgentDingo(allow_codegen=False)By default theallow_codegenparameter is set to"env"which means that the value is read from theDINGO_ALLOW_CODEGENenvironment variable. If the variable is not set, it is assumed to beTrue.It is also possible to change the model used for the code generation by setting theDINGO_CODEGEN_MODELenvironment variable. By default, thegpt-3.5-turbo-0613model is used.exportDINGO_CODEGEN_MODEL="gpt-4-0613"Registering of external functionsIf you wish to register a function that is not defined in the current file, you can use theregister_functionmethod of the agent.frommy_moduleimportget_temperatureagent.register_function(get_temperature)Alternatively, you can define a function descriptor manually and register it using theregister_descriptormethod. In this case, ajson_representationcompatible withOpenAI function calling APIshould be provided.fromagent_dingo.descriptorimportFunctionDescriptord=FunctionDescriptor(name="<function_name>",json_representation={name:"<function_name>",description:"<function_description>",parameters:...}func=function_callablerequires_context=TrueorFalse)agent.register_descriptor(d)Running the conversationOnce the functions are registered, you can run the conversation using thechatmethod of the agent.agent.chat("What is the current temperature in Linz?")The chat method accepts the following parameters:messages- the message to start the conversation with; it can either be a string or a list of messages (conversation history);chat_context- the global context of the conversation (more details are provided in the next section); by default, it is set toNone;model- the model to use for the conversation; by default, thegpt-3.5-turbo-0613model is used;temperature- the randomness parameter of the model; by default, it is set to1.0;max_function_calls- the maximum number of function calls allowed during the conversation; by default, it is set to10;before_function_call- an interceptor that is called before the function is called (mode details are provided in the next section); by default, it is set toNone.All of the parameters exceptmessagesare optional.The method returns a tuple which contains the last message of the conversation (as string) and the full conversation history (including function calls).Chat contextIn some cases the function might require to access the global context of the conversation. For example, the function might need to access some user-specific information (e.g. user id). In this case, thechat_contextparameter can be used. It is a special dictionary that is passed to the function and can contain any information that is required. Unlike other arguments, the content of the chat_context is not generated by the model and is passed directly to the function.fromagent_dingo.contextimportChatContext@agent.functiondefget_user_name(greeting_msg:str,chat_context:ChatContext)->str:"""Returns a greeting message with the user's name.Parameters----------greeting_msg : strMessage to greet the user with.chat_context : ChatContextThe chat context.Returns-------strThe greeting message with the user's name."""user_name=chat_context["user_name"]returnf"{greeting_msg},{user_name}!"r=agent.chat("Say hi.",chat_context=ChatContext(user_name="John"),temperature=0.0)# > Hi, John! How can I assist you today?Note: thechat_contextparameter is not passed to the model and is not used for the generation of the function descriptor.Before function call interceptorIn some cases, it might be required to perform some actions before the function is called. For example, you might want to log the function call or perform some checks. This is especially handy since the function arguments generated by the model are not guaranteed to be correct/valid, hence, it is advised to add some additional validators. Thebefore_function_callparameter can be used to register an interceptor that is called before the function. The interceptor receives the following parameters:function_name,function_callable,function_kwargsand should return a tuple with the updatedfunction_callableandfunction_kwargs.Example: intercepting the function call and logging the function name and arguments.defbefore_function_call(function_name:str,function_callable:Callable,function_kwargs:dict):print(f"Calling function{function_name}with arguments{function_kwargs}")returnfunction_callable,function_kwargsagent.chat("What is the current temperature in Linz?",before_function_call=before_function_call,)DingoWrapper + Web ServerIn addition to using the agent directly, it is possible to wrap it into aDingoWrapper, which provides an OpenAI-like API.fromagent_dingo.wrapperimportDingoWrapperwrapped_agent=DingoWrapper(agent,before_function_call=None,max_function_calls=10)Once the agent is wrapped, it can be used to create the chat completions using thechat_completionmethod.r=wrapped_agent.chat_completion(messages=[{"role":"user","content":"What is the current weather in Linz?"}],model="gpt-3.5-turbo",temperature=0.0,#optionalchat_context=None,#optional)In principle, this method can be used as a drop-in replacement for theopenai.ChatCompletion.createmethod. However, there are several differences:DingoWrapper does not support most of the optional hyperparameters of theopenai.ChatCompletion.createmethod (excepttemperature);DingoWrapper has an additional (optional)chat_contextparameter that can be used to pass the global context of the conversation;Example:# openai.ChatCompletionr=openai.ChatCompletion.create(messages=[{"role":"user","content":"What is the current weather in Linz?"}],model="gpt-3.5-turbo",temperature=0.0)# DingoWrapperr=wrapped_agent.chat_completion(messages=[{"role":"user","content":"What is the current weather in Linz?"}],model="gpt-3.5-turbo",temperature=0.0)TheDingoWrappercan also be used to run a web server (also compatible with the OpenAI API). The server can be started using theservemethod.The serve method requires additional dependencies:pipinstallagent_dingo[server]wrapped_agent.serve(port=8080,host="0.0.0.0",threads=4)Once the server has started, it can be accessed using e.g. theopenaipython package.# client.pyimportopenaiopenai.api_base="http://localhost:8080"r=openai.ChatCompletion.create(model="gpt-3.5-turbo",messages=[{"role":"user","content":"What is the temperature in Linz ?"}],temperature=0.0,)print(r)Response:{"choices":[{"finish_reason":"stop","index":0,"message":{"content":"The current temperature in Linz is 25\u00b0C and it is sunny.","role":"assistant"}}],"created":1692537919,"id":"chatcmpl-d6a9d6cc-7a26-41d5-a4a6-2c737b652f4b","model":"dingo-gpt-3.5-turbo-0613","object":"chat.completion","usage":{"completion_tokens":32,"prompt_tokens":318,"total_tokens":350}}Note: the "usage" metric accumulates the number of tokens used for all intermediate function calls during the conversation.LangChain Tools 🦜️🔗It is possible to convertLangchain Toolsinto function descriptors in order to register them with Dingo. The converter can be used as follows:Install langchain:pipinstallagent_dingo[langchain]Define the tool, we will use the Wikipedia tool as an example:fromlangchain.tools.wikipedia.toolimportWikipediaQueryRunfromlangchain.utilities.wikipediaimportWikipediaAPIWrappertool=WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())Please refer to theLangChain documentationfor more details on how to define the tools.Convert the tool into a function descriptor and register:fromagent_dingo.langchainimportconvert_langchain_tooldescriptor=convert_langchain_tool(tool)agent.register_descriptor(descriptor)Run the conversation:# The agent will query Wikipedia to obtain the answer.agent.chat("What is LangChain according to Wikipedia? Explain in one sentence.")# > According to Wikipedia, LangChain is a framework designed to simplify the creation of applications using large language models (LLMs), with use-cases including document analysis and summarization, chatbots, and code analysis.In comparison, when we try to query ChatGPT directly with the same question, we get the following hallucinated response (since it does not have access to the relevant up-to-date information):# > LangChain is a blockchain-based platform that aims to provide language learning services and connect language learners with native speakers for real-time practice and feedback.Note: some of the tools might be incompatible with (or simply unsuitable for) Dingo. We do not guarantee that all of the tools will work out of the box.