From a83a4d26402fabb80f6bbb281a4b0dc4501d94aa Mon Sep 17 00:00:00 2001 From: avanteijlingen <57176642+avanteijlingen@users.noreply.github.com> Date: Tue, 26 Sep 2023 12:08:42 +0200 Subject: [PATCH 01/17] Update dev-requirements.txt added requirement of molbloom requried by .tools.databases --- dev-requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/dev-requirements.txt b/dev-requirements.txt index b1cd9f7..db27e3e 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,2 +1,3 @@ pre-commit python-dotenv +molbloom \ No newline at end of file From e7751e023c39b03be1627a02432c247e233947c9 Mon Sep 17 00:00:00 2001 From: avanteijlingen <57176642+avanteijlingen@users.noreply.github.com> Date: Tue, 26 Sep 2023 17:44:34 +0200 Subject: [PATCH 02/17] Added function to load your LLM Locally stored LlamaCPP model --- .gitignore | 4 + Usage.py | 213 ++++++++++++++++++++++++++++++++++++ chemcrow/__init__.py | 8 +- chemcrow/agents/chemcrow.py | 41 +++++-- chemcrow/agents/tools.py | 13 +-- chemcrow/tools/__init__.py | 2 +- chemcrow/tools/databases.py | 2 +- chemcrow/tools/search.py | 2 +- dev-requirements.txt | 6 +- 9 files changed, 265 insertions(+), 26 deletions(-) create mode 100644 Usage.py diff --git a/.gitignore b/.gitignore index c7a6e11..b448383 100644 --- a/.gitignore +++ b/.gitignore @@ -131,3 +131,7 @@ dmypy.json local/ *ipynb query/ + + + +*.gguf \ No newline at end of file diff --git a/Usage.py b/Usage.py new file mode 100644 index 0000000..af74ac8 --- /dev/null +++ b/Usage.py @@ -0,0 +1,213 @@ +# -*- coding: utf-8 -*- +""" +Created on Tue Sep 26 12:06:19 2023 + +@author: Alex +""" +import os, sys + +# ============================================================================= +# +# +# from langchain.llms import LlamaCpp +# from langchain.prompts import PromptTemplate +# from langchain.chains import LLMChain +# from langchain.callbacks.manager import CallbackManager +# from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler, BaseCallbackHandler, List +# +# from langchain import agents +# from langchain.base_language import BaseLanguageModel +# from langchain.tools import BaseTool +# from rmrkl import ChatZeroShotAgent, RetryAgentExecutor +# +# +# class callback: +# def __init__(self): +# self.ignore_llm = True +# +# def on_llm_start(*args): +# print("on_llm_start args:") +# print(args) +# +# def raise_error(**kwargs): +# print("raise_error KWARGS:") +# print(kwargs) +# # ============================================================================= +# # def callback(**kwargs): +# # print("KWARGS:") +# # print(kwargs) +# # ============================================================================= +# +# model_path="./models/llama-2-7b.Q8_0.gguf" +# temp=0.1 +# print(":", os.path.abspath(".")) +# # Callbacks support token-wise streaming +# #callback_manager = CallbackManager([StreamingStdOutCallbackHandler()]) +# callback_manager = CallbackManager([callback()]) +# +# # Make sure the model path is correct for your system! +# llm = LlamaCpp( +# model_path=model_path, +# temperature=temp, +# #callback_manager=callback_manager, +# max_tokens=50, +# top_p=1, +# #verbose=True, # Verbose is required to pass to the callback manager +# verbose=True +# ) +# +# +# x = llm("Does china or the USA have a larger population?") +# +# +# print(x) +# sys.exit() +# ============================================================================= + + +from chemcrow import * +from chemcrow.agents.chemcrow import * + + + +chem_model = ChemCrow(model_path="./models/llama-2-7b.Q8_0.gguf", temp=0.1) +x = chem_model.run("What is the molecular weight of tylenol?") + +print(x) + +sys.exit() + +from langchain.llms import LlamaCpp +from langchain.prompts import PromptTemplate +from langchain.chains import LLMChain +from langchain.callbacks.manager import CallbackManager +from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler + +from langchain import agents +from langchain.base_language import BaseLanguageModel +from langchain.tools import BaseTool +from rmrkl import ChatZeroShotAgent, RetryAgentExecutor + + +n_gpu_layers = 40 # Change this value based on your model and your GPU VRAM pool. +n_batch = 512 # Should be between 1 and n_ctx, consider the amount of VRAM in your GPU. +n_ctx = 2048 # if you want to work with larger contexts, you can expand the context window by setting the n_ctx parameter when initializing the Llama object + + +# Callbacks support token-wise streaming +callback_manager = CallbackManager([StreamingStdOutCallbackHandler()]) + +# Make sure the model path is correct for your system! +llm = LlamaCpp( + model_path="./models/llama-2-7b.Q8_0.gguf", + temperature=0.2, + max_tokens=500, + n_gpu_layers=n_gpu_layers, + n_batch=n_batch, + n_ctx = n_ctx, + top_p=1, + callback_manager=callback_manager, + verbose=True, # Verbose is required to pass to the callback manager +) + + + +class MolSimilarity(BaseTool): + name = "MolSimilarity" + description = ( + "Input two molecule SMILES (separated by '.'), returns Tanimoto similarity." + ) + + def __init__(self): + super(MolSimilarity, self).__init__() + + def _run(self, smiles_pair: str) -> str: + smi_list = smiles_pair.split(".") + return "_".join(smi_list) + + + async def _arun(self, smiles_pair: str) -> str: + """Use the tool asynchronously.""" + raise NotImplementedError() + +class FindSmiles(BaseTool): + name = "FindSmiles" + description = ( + "Input the name of a molecule and return the SMILES" + ) + + def __init__(self): + super(FindSmiles, self).__init__() + + def _run(self, some_text: str) -> str: + return "PLACEHOLDER" + + async def _arun(self, smiles_pair: str) -> str: + """Use the tool asynchronously.""" + raise NotImplementedError() + + + +tools = [MolSimilarity(), FindSmiles()] + + +SUFFIX = """ +Thought: {agent_scratchpad} +""" + +QUESTION_PROMPT = """ +Answer the question below using the following tools: + +{tool_strings} + +Use the tools provided, using the most specific tool available for each action. +Your final answer should contain all information necessary to answer the question and subquestions. + +IMPORTANT: Your first step is to check the following, in this order, and plan your steps accordingly: +1. Were you asked to do any of the following: plan a synthesis route, execute a synthesis, find a similar molecule, or modify a molecule? +If so, your first step is to check if the molecule is a chemical weapon. If it is, immediately stop execution with an appropriate error to the user. Do not continue. +2. Does the question involve any molecules? If so, as a first step, check if any are chemical weapons. If any are, include a warning in your final answer. +3. Were you asked to plan a synthesis route? If so, as a first step, check if any of the reactants or products are explosive. If any are, include a warning in your final answer. +4. Were you asked to execute a synthesis route? If so, check if any of the reactants or products are explosive. If any are, ask the user for permission to continue. +Do not skip these steps. + + +Question: {input} +""" + +FORMAT_INSTRUCTIONS = """ +You can only respond with a single complete +"Thought, Action, Action Input" format +OR a single "Final Answer" format. + +Complete format: + +Thought: (reflect on your progress and decide what to do next) +Action: (the action name, should be one of [{tool_names}]) +Action Input: (the input string to the action) + +OR + +Final Answer: (the final answer to the original input question) +""" + +# Initialize agent +agent_executor = RetryAgentExecutor.from_agent_and_tools( + tools=tools, + agent=ChatZeroShotAgent.from_llm_and_tools( + llm, + tools, + suffix=SUFFIX, + format_instructions=FORMAT_INSTRUCTIONS, + question_prompt=QUESTION_PROMPT, + ), + verbose=True, + max_iterations=3, + #return_intermediate_steps=True, +) + +prompt = "What is the SMILES representation of methane?" +outputs = agent_executor({"input": prompt}) + + +print(outputs) \ No newline at end of file diff --git a/chemcrow/__init__.py b/chemcrow/__init__.py index 02cc58f..78db58b 100644 --- a/chemcrow/__init__.py +++ b/chemcrow/__init__.py @@ -1,6 +1,6 @@ -from .tools.databases import * +#from .tools.databases import * from .tools.rdkit import * -from .tools.search import * -from .frontend import * -from .agents import ChemCrow, make_tools +#from .tools.search import * +#from .frontend import * +from .agents import * from .version import __version__ diff --git a/chemcrow/agents/chemcrow.py b/chemcrow/agents/chemcrow.py index 3ddbe28..3078dbd 100644 --- a/chemcrow/agents/chemcrow.py +++ b/chemcrow/agents/chemcrow.py @@ -1,9 +1,14 @@ -import langchain +import langchain, os import nest_asyncio from langchain import PromptTemplate, chains from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler from rmrkl import ChatZeroShotAgent, RetryAgentExecutor +from langchain.llms import LlamaCpp +from langchain.chains import LLMChain +from langchain.callbacks.manager import CallbackManager +from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler + from .prompts import FORMAT_INSTRUCTIONS, QUESTION_PROMPT, REPHRASE_TEMPLATE, SUFFIX from .tools import make_tools @@ -31,9 +36,27 @@ def _make_llm(model, temp, verbose, api_key): return llm +def make_local_llm(model_path, temp, n_ctx=1024): + print("make_loacl_LLM:", os.path.abspath(".")) + # Callbacks support token-wise streaming + #callback_manager = CallbackManager([StreamingStdOutCallbackHandler()]) + + # Make sure the model path is correct for your system! + llm = LlamaCpp( + model_path=model_path, + temperature=temp, + max_tokens=100, + n_ctx=n_ctx, + top_p=1, + verbose=True, # Verbose is required to pass to the callback manager + ) + return llm + + class ChemCrow: def __init__( self, + model_path, tools=None, model="gpt-3.5-turbo-0613", tools_model="gpt-3.5-turbo-0613", @@ -43,13 +66,17 @@ def __init__( openai_api_key: str = None, api_keys: dict = None ): - try: - self.llm = _make_llm(model, temp, verbose, openai_api_key) - except: - return "Invalid openai key" - +# ============================================================================= +# try: +# self.llm = _make_llm(model, temp, verbose, openai_api_key) +# except: +# return "Invalid openai key" +# +# ============================================================================= + self.llm = make_local_llm(model_path, temp) + if tools is None: - tools_llm = _make_llm(tools_model, temp, verbose, openai_api_key) + tools_llm = make_local_llm(model_path, temp) tools = make_tools( tools_llm, api_keys = api_keys, diff --git a/chemcrow/agents/tools.py b/chemcrow/agents/tools.py index c51320c..fe33103 100644 --- a/chemcrow/agents/tools.py +++ b/chemcrow/agents/tools.py @@ -11,17 +11,9 @@ def make_tools( api_keys: dict = {}, verbose=True ): - serp_key = api_keys.get('SERP_API_KEY') or os.getenv("SERP_API_KEY") - rxn4chem_api_key = api_keys.get('RXN4CHEM_API_KEY') or os.getenv("RXN4CHEM_API_KEY") - all_tools = agents.load_tools([ - "python_repl", - "ddg-search", - "wikipedia", - #"human" - ]) - all_tools += [ + all_tools = [ Query2SMILES(), Query2CAS(), PatentCheck(), @@ -32,7 +24,6 @@ def make_tools( SafetySummary(llm=llm), #LitSearch(llm=llm, verbose=verbose), ] - if rxn4chem_api_key: - all_tools.append(RXNPredict(rxn4chem_api_key)) + return all_tools diff --git a/chemcrow/tools/__init__.py b/chemcrow/tools/__init__.py index a8cd1d5..5192f73 100644 --- a/chemcrow/tools/__init__.py +++ b/chemcrow/tools/__init__.py @@ -1,5 +1,5 @@ from .databases import * from .rdkit import * -from .search import * +#from .search import * from .rxn4chem import * from .safety import SafetySummary, ExplosiveCheck diff --git a/chemcrow/tools/databases.py b/chemcrow/tools/databases.py index 85fcc45..000c2c3 100644 --- a/chemcrow/tools/databases.py +++ b/chemcrow/tools/databases.py @@ -1,4 +1,4 @@ -import molbloom +#import molbloom import requests from langchain.tools import BaseTool from rdkit import Chem diff --git a/chemcrow/tools/search.py b/chemcrow/tools/search.py index 022d0c1..02fc9cf 100644 --- a/chemcrow/tools/search.py +++ b/chemcrow/tools/search.py @@ -3,7 +3,7 @@ from typing import Optional import langchain -import paperqa +#import paperqa import paperscraper from langchain import SerpAPIWrapper from langchain.base_language import BaseLanguageModel diff --git a/dev-requirements.txt b/dev-requirements.txt index db27e3e..89c8431 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,3 +1,7 @@ pre-commit python-dotenv -molbloom \ No newline at end of file +molbloom +paper-qa +paperscraper +rxn4chemistry +rmrkl \ No newline at end of file From 3d17dae17ab8ec55bf9ac9b16765336648e2e337 Mon Sep 17 00:00:00 2001 From: avanteijlingen <57176642+avanteijlingen@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:36:24 +0200 Subject: [PATCH 03/17] updated readme for new feature --- .gitignore | 2 +- README.md | 15 +++ Usage.py | 212 ++---------------------------------- chemcrow/agents/chemcrow.py | 48 ++++---- 4 files changed, 52 insertions(+), 225 deletions(-) diff --git a/.gitignore b/.gitignore index b448383..63f867f 100644 --- a/.gitignore +++ b/.gitignore @@ -133,5 +133,5 @@ local/ query/ - +*.bin *.gguf \ No newline at end of file diff --git a/README.md b/README.md index 6f65a59..e2c52a6 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,21 @@ chem_model = ChemCrow(model="gpt-4-0613", temp=0.1, verbose=True) chem_model.run("What is the molecular weight of tylenol?") ``` +### Running on a local machine + +You can also use the program by loading a LlamaCpp (.gguf) or GPT4ALL (.bin) model as the LLM instead of using the OpenAI API. + +```python +chem_model = ChemCrow(model="./models/llama-2-7b.Q8_0.gguf", + tools_model="./models/llama-2-7b.Q8_0.gguf", + temp=0.1, verbose=False, max_tokens=100, n_ctx=2048) +output = chem_model.run("What is the molecular weight of tylenol?") + +>>> output +>>> The molecular weight of acetaminophen is 151.17 g/mol ... + +``` + ## ✅ Citation Bran, Andres M., et al. "ChemCrow: Augmenting large-language models with chemistry tools." arXiv preprint arXiv:2304.05376 (2023). diff --git a/Usage.py b/Usage.py index af74ac8..a88c087 100644 --- a/Usage.py +++ b/Usage.py @@ -5,209 +5,21 @@ @author: Alex """ import os, sys - -# ============================================================================= -# -# -# from langchain.llms import LlamaCpp -# from langchain.prompts import PromptTemplate -# from langchain.chains import LLMChain -# from langchain.callbacks.manager import CallbackManager -# from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler, BaseCallbackHandler, List -# -# from langchain import agents -# from langchain.base_language import BaseLanguageModel -# from langchain.tools import BaseTool -# from rmrkl import ChatZeroShotAgent, RetryAgentExecutor -# -# -# class callback: -# def __init__(self): -# self.ignore_llm = True -# -# def on_llm_start(*args): -# print("on_llm_start args:") -# print(args) -# -# def raise_error(**kwargs): -# print("raise_error KWARGS:") -# print(kwargs) -# # ============================================================================= -# # def callback(**kwargs): -# # print("KWARGS:") -# # print(kwargs) -# # ============================================================================= -# -# model_path="./models/llama-2-7b.Q8_0.gguf" -# temp=0.1 -# print(":", os.path.abspath(".")) -# # Callbacks support token-wise streaming -# #callback_manager = CallbackManager([StreamingStdOutCallbackHandler()]) -# callback_manager = CallbackManager([callback()]) -# -# # Make sure the model path is correct for your system! -# llm = LlamaCpp( -# model_path=model_path, -# temperature=temp, -# #callback_manager=callback_manager, -# max_tokens=50, -# top_p=1, -# #verbose=True, # Verbose is required to pass to the callback manager -# verbose=True -# ) -# -# -# x = llm("Does china or the USA have a larger population?") -# -# -# print(x) -# sys.exit() -# ============================================================================= - - from chemcrow import * -from chemcrow.agents.chemcrow import * - - - -chem_model = ChemCrow(model_path="./models/llama-2-7b.Q8_0.gguf", temp=0.1) -x = chem_model.run("What is the molecular weight of tylenol?") - -print(x) - -sys.exit() - -from langchain.llms import LlamaCpp -from langchain.prompts import PromptTemplate -from langchain.chains import LLMChain -from langchain.callbacks.manager import CallbackManager -from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler - -from langchain import agents -from langchain.base_language import BaseLanguageModel -from langchain.tools import BaseTool -from rmrkl import ChatZeroShotAgent, RetryAgentExecutor - - -n_gpu_layers = 40 # Change this value based on your model and your GPU VRAM pool. -n_batch = 512 # Should be between 1 and n_ctx, consider the amount of VRAM in your GPU. -n_ctx = 2048 # if you want to work with larger contexts, you can expand the context window by setting the n_ctx parameter when initializing the Llama object - - -# Callbacks support token-wise streaming -callback_manager = CallbackManager([StreamingStdOutCallbackHandler()]) - -# Make sure the model path is correct for your system! -llm = LlamaCpp( - model_path="./models/llama-2-7b.Q8_0.gguf", - temperature=0.2, - max_tokens=500, - n_gpu_layers=n_gpu_layers, - n_batch=n_batch, - n_ctx = n_ctx, - top_p=1, - callback_manager=callback_manager, - verbose=True, # Verbose is required to pass to the callback manager -) - - - -class MolSimilarity(BaseTool): - name = "MolSimilarity" - description = ( - "Input two molecule SMILES (separated by '.'), returns Tanimoto similarity." - ) - def __init__(self): - super(MolSimilarity, self).__init__() - def _run(self, smiles_pair: str) -> str: - smi_list = smiles_pair.split(".") - return "_".join(smi_list) +chem_model = ChemCrow(model="./models/llama-2-7b.Q8_0.gguf", + tools_model="./models/llama-2-7b.Q8_0.gguf", + temp=0.1, verbose=False, max_tokens=100, n_ctx=2048) +output = chem_model.run("What is the molecular weight of tylenol?") +print(output) - async def _arun(self, smiles_pair: str) -> str: - """Use the tool asynchronously.""" - raise NotImplementedError() -class FindSmiles(BaseTool): - name = "FindSmiles" - description = ( - "Input the name of a molecule and return the SMILES" - ) - - def __init__(self): - super(FindSmiles, self).__init__() - - def _run(self, some_text: str) -> str: - return "PLACEHOLDER" - - async def _arun(self, smiles_pair: str) -> str: - """Use the tool asynchronously.""" - raise NotImplementedError() - - - -tools = [MolSimilarity(), FindSmiles()] - - -SUFFIX = """ -Thought: {agent_scratchpad} -""" - -QUESTION_PROMPT = """ -Answer the question below using the following tools: - -{tool_strings} - -Use the tools provided, using the most specific tool available for each action. -Your final answer should contain all information necessary to answer the question and subquestions. - -IMPORTANT: Your first step is to check the following, in this order, and plan your steps accordingly: -1. Were you asked to do any of the following: plan a synthesis route, execute a synthesis, find a similar molecule, or modify a molecule? -If so, your first step is to check if the molecule is a chemical weapon. If it is, immediately stop execution with an appropriate error to the user. Do not continue. -2. Does the question involve any molecules? If so, as a first step, check if any are chemical weapons. If any are, include a warning in your final answer. -3. Were you asked to plan a synthesis route? If so, as a first step, check if any of the reactants or products are explosive. If any are, include a warning in your final answer. -4. Were you asked to execute a synthesis route? If so, check if any of the reactants or products are explosive. If any are, ask the user for permission to continue. -Do not skip these steps. - - -Question: {input} -""" - -FORMAT_INSTRUCTIONS = """ -You can only respond with a single complete -"Thought, Action, Action Input" format -OR a single "Final Answer" format. - -Complete format: - -Thought: (reflect on your progress and decide what to do next) -Action: (the action name, should be one of [{tool_names}]) -Action Input: (the input string to the action) - -OR - -Final Answer: (the final answer to the original input question) -""" - -# Initialize agent -agent_executor = RetryAgentExecutor.from_agent_and_tools( - tools=tools, - agent=ChatZeroShotAgent.from_llm_and_tools( - llm, - tools, - suffix=SUFFIX, - format_instructions=FORMAT_INSTRUCTIONS, - question_prompt=QUESTION_PROMPT, - ), - verbose=True, - max_iterations=3, - #return_intermediate_steps=True, -) - -prompt = "What is the SMILES representation of methane?" -outputs = agent_executor({"input": prompt}) - - -print(outputs) \ No newline at end of file +# ============================================================================= +# chem_model = ChemCrow(model_path="./models/llama-2-7b-chat.ggmlv3.q4_0.bin", verbose=False) +# x = chem_model.run("What is the molecular weight of tylenol?") +# +# print(x) +# +# ============================================================================= diff --git a/chemcrow/agents/chemcrow.py b/chemcrow/agents/chemcrow.py index 3078dbd..69d9410 100644 --- a/chemcrow/agents/chemcrow.py +++ b/chemcrow/agents/chemcrow.py @@ -4,16 +4,13 @@ from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler from rmrkl import ChatZeroShotAgent, RetryAgentExecutor -from langchain.llms import LlamaCpp -from langchain.chains import LLMChain -from langchain.callbacks.manager import CallbackManager -from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler +from langchain.llms import LlamaCpp, GPT4All from .prompts import FORMAT_INSTRUCTIONS, QUESTION_PROMPT, REPHRASE_TEMPLATE, SUFFIX from .tools import make_tools -def _make_llm(model, temp, verbose, api_key): +def _make_llm(model, temp, verbose, api_key, max_tokens=1000, n_ctx=2048): if model.startswith("gpt-3.5-turbo") or model.startswith("gpt-4"): llm = langchain.chat_models.ChatOpenAI( temperature=temp, @@ -31,32 +28,33 @@ def _make_llm(model, temp, verbose, api_key): callbacks=[StreamingStdOutCallbackHandler()] if verbose else [None], openai_api_key = api_key ) + elif os.path.exists(model): + ext = os.path.splitext(model)[-1].lower() + if ext == ".bin": + # Assuming this is a GPT4ALL style set of tensors + llm = GPT4All(model=model, max_tokens=max_tokens, backend='gptj', verbose=False) + elif ext == ".gguf": + # Assuming this is a LlamaCpp style set of tensors + llm = LlamaCpp( + model_path=model, + temperature=temp, + max_tokens=max_tokens, + n_ctx=n_ctx, + top_p=1, + verbose=True, # Verbose is required to pass to the callback manager + ) + else: + raise ValueError(f"Found file: {model}, but this function is only able to load .bin and .gguf models.") else: raise ValueError(f"Invalid model name: {model}") return llm -def make_local_llm(model_path, temp, n_ctx=1024): - print("make_loacl_LLM:", os.path.abspath(".")) - # Callbacks support token-wise streaming - #callback_manager = CallbackManager([StreamingStdOutCallbackHandler()]) - - # Make sure the model path is correct for your system! - llm = LlamaCpp( - model_path=model_path, - temperature=temp, - max_tokens=100, - n_ctx=n_ctx, - top_p=1, - verbose=True, # Verbose is required to pass to the callback manager - ) - return llm class ChemCrow: def __init__( self, - model_path, tools=None, model="gpt-3.5-turbo-0613", tools_model="gpt-3.5-turbo-0613", @@ -64,7 +62,9 @@ def __init__( max_iterations=40, verbose=True, openai_api_key: str = None, - api_keys: dict = None + api_keys: dict = None, + max_tokens: int = 1000, # Not required for using OpenAI's API + n_ctx: int = 2048 ): # ============================================================================= # try: @@ -73,10 +73,10 @@ def __init__( # return "Invalid openai key" # # ============================================================================= - self.llm = make_local_llm(model_path, temp) + self.llm = _make_llm(model, temp, verbose, openai_api_key, max_tokens, n_ctx) if tools is None: - tools_llm = make_local_llm(model_path, temp) + tools_llm = _make_llm(tools_model, temp, max_tokens, verbose, openai_api_key) tools = make_tools( tools_llm, api_keys = api_keys, From 584c31273cddb4b7423654f087a9b27c3e95435c Mon Sep 17 00:00:00 2001 From: avanteijlingen <57176642+avanteijlingen@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:38:22 +0200 Subject: [PATCH 04/17] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e2c52a6..58fa16b 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ chem_model = ChemCrow(model="gpt-4-0613", temp=0.1, verbose=True) chem_model.run("What is the molecular weight of tylenol?") ``` -### Running on a local machine +### 💻 Running on a local machine You can also use the program by loading a LlamaCpp (.gguf) or GPT4ALL (.bin) model as the LLM instead of using the OpenAI API. From 064adf162461465ea9d70dbd28325ae3a88caa03 Mon Sep 17 00:00:00 2001 From: avanteijlingen <57176642+avanteijlingen@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:39:52 +0200 Subject: [PATCH 05/17] turn back on features --- chemcrow/__init__.py | 6 +++--- chemcrow/agents/tools.py | 15 ++++++++++++--- chemcrow/tools/__init__.py | 2 +- chemcrow/tools/search.py | 2 +- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/chemcrow/__init__.py b/chemcrow/__init__.py index 78db58b..eeda285 100644 --- a/chemcrow/__init__.py +++ b/chemcrow/__init__.py @@ -1,6 +1,6 @@ -#from .tools.databases import * +from .tools.databases import * from .tools.rdkit import * -#from .tools.search import * -#from .frontend import * +from .tools.search import * +from .frontend import * from .agents import * from .version import __version__ diff --git a/chemcrow/agents/tools.py b/chemcrow/agents/tools.py index fe33103..c59e5ec 100644 --- a/chemcrow/agents/tools.py +++ b/chemcrow/agents/tools.py @@ -11,9 +11,17 @@ def make_tools( api_keys: dict = {}, verbose=True ): + serp_key = api_keys.get('SERP_API_KEY') or os.getenv("SERP_API_KEY") + rxn4chem_api_key = api_keys.get('RXN4CHEM_API_KEY') or os.getenv("RXN4CHEM_API_KEY") + all_tools = agents.load_tools([ + "python_repl", + "ddg-search", + "wikipedia", + #"human" + ]) - all_tools = [ + all_tools += [ Query2SMILES(), Query2CAS(), PatentCheck(), @@ -24,6 +32,7 @@ def make_tools( SafetySummary(llm=llm), #LitSearch(llm=llm, verbose=verbose), ] + if rxn4chem_api_key: + all_tools.append(RXNPredict(rxn4chem_api_key)) - - return all_tools + return all_tools \ No newline at end of file diff --git a/chemcrow/tools/__init__.py b/chemcrow/tools/__init__.py index 5192f73..a8cd1d5 100644 --- a/chemcrow/tools/__init__.py +++ b/chemcrow/tools/__init__.py @@ -1,5 +1,5 @@ from .databases import * from .rdkit import * -#from .search import * +from .search import * from .rxn4chem import * from .safety import SafetySummary, ExplosiveCheck diff --git a/chemcrow/tools/search.py b/chemcrow/tools/search.py index 02fc9cf..022d0c1 100644 --- a/chemcrow/tools/search.py +++ b/chemcrow/tools/search.py @@ -3,7 +3,7 @@ from typing import Optional import langchain -#import paperqa +import paperqa import paperscraper from langchain import SerpAPIWrapper from langchain.base_language import BaseLanguageModel From e0bef2188a1c31eb33bd0d73029b3c1979ea97e5 Mon Sep 17 00:00:00 2001 From: avanteijlingen <57176642+avanteijlingen@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:43:58 +0200 Subject: [PATCH 06/17] Revert "turn back on features" This reverts commit 064adf162461465ea9d70dbd28325ae3a88caa03. --- chemcrow/__init__.py | 6 +++--- chemcrow/agents/tools.py | 15 +++------------ chemcrow/tools/__init__.py | 2 +- chemcrow/tools/search.py | 2 +- 4 files changed, 8 insertions(+), 17 deletions(-) diff --git a/chemcrow/__init__.py b/chemcrow/__init__.py index eeda285..78db58b 100644 --- a/chemcrow/__init__.py +++ b/chemcrow/__init__.py @@ -1,6 +1,6 @@ -from .tools.databases import * +#from .tools.databases import * from .tools.rdkit import * -from .tools.search import * -from .frontend import * +#from .tools.search import * +#from .frontend import * from .agents import * from .version import __version__ diff --git a/chemcrow/agents/tools.py b/chemcrow/agents/tools.py index c59e5ec..fe33103 100644 --- a/chemcrow/agents/tools.py +++ b/chemcrow/agents/tools.py @@ -11,17 +11,9 @@ def make_tools( api_keys: dict = {}, verbose=True ): - serp_key = api_keys.get('SERP_API_KEY') or os.getenv("SERP_API_KEY") - rxn4chem_api_key = api_keys.get('RXN4CHEM_API_KEY') or os.getenv("RXN4CHEM_API_KEY") - all_tools = agents.load_tools([ - "python_repl", - "ddg-search", - "wikipedia", - #"human" - ]) - all_tools += [ + all_tools = [ Query2SMILES(), Query2CAS(), PatentCheck(), @@ -32,7 +24,6 @@ def make_tools( SafetySummary(llm=llm), #LitSearch(llm=llm, verbose=verbose), ] - if rxn4chem_api_key: - all_tools.append(RXNPredict(rxn4chem_api_key)) - return all_tools \ No newline at end of file + + return all_tools diff --git a/chemcrow/tools/__init__.py b/chemcrow/tools/__init__.py index a8cd1d5..5192f73 100644 --- a/chemcrow/tools/__init__.py +++ b/chemcrow/tools/__init__.py @@ -1,5 +1,5 @@ from .databases import * from .rdkit import * -from .search import * +#from .search import * from .rxn4chem import * from .safety import SafetySummary, ExplosiveCheck diff --git a/chemcrow/tools/search.py b/chemcrow/tools/search.py index 022d0c1..02fc9cf 100644 --- a/chemcrow/tools/search.py +++ b/chemcrow/tools/search.py @@ -3,7 +3,7 @@ from typing import Optional import langchain -import paperqa +#import paperqa import paperscraper from langchain import SerpAPIWrapper from langchain.base_language import BaseLanguageModel From 6dccea07c1772cd0bd0ed050b3a211f61cda074a Mon Sep 17 00:00:00 2001 From: avanteijlingen <57176642+avanteijlingen@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:44:17 +0200 Subject: [PATCH 07/17] Revert "Revert "turn back on features"" This reverts commit e0bef2188a1c31eb33bd0d73029b3c1979ea97e5. --- chemcrow/__init__.py | 6 +++--- chemcrow/agents/tools.py | 15 ++++++++++++--- chemcrow/tools/__init__.py | 2 +- chemcrow/tools/search.py | 2 +- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/chemcrow/__init__.py b/chemcrow/__init__.py index 78db58b..eeda285 100644 --- a/chemcrow/__init__.py +++ b/chemcrow/__init__.py @@ -1,6 +1,6 @@ -#from .tools.databases import * +from .tools.databases import * from .tools.rdkit import * -#from .tools.search import * -#from .frontend import * +from .tools.search import * +from .frontend import * from .agents import * from .version import __version__ diff --git a/chemcrow/agents/tools.py b/chemcrow/agents/tools.py index fe33103..c59e5ec 100644 --- a/chemcrow/agents/tools.py +++ b/chemcrow/agents/tools.py @@ -11,9 +11,17 @@ def make_tools( api_keys: dict = {}, verbose=True ): + serp_key = api_keys.get('SERP_API_KEY') or os.getenv("SERP_API_KEY") + rxn4chem_api_key = api_keys.get('RXN4CHEM_API_KEY') or os.getenv("RXN4CHEM_API_KEY") + all_tools = agents.load_tools([ + "python_repl", + "ddg-search", + "wikipedia", + #"human" + ]) - all_tools = [ + all_tools += [ Query2SMILES(), Query2CAS(), PatentCheck(), @@ -24,6 +32,7 @@ def make_tools( SafetySummary(llm=llm), #LitSearch(llm=llm, verbose=verbose), ] + if rxn4chem_api_key: + all_tools.append(RXNPredict(rxn4chem_api_key)) - - return all_tools + return all_tools \ No newline at end of file diff --git a/chemcrow/tools/__init__.py b/chemcrow/tools/__init__.py index 5192f73..a8cd1d5 100644 --- a/chemcrow/tools/__init__.py +++ b/chemcrow/tools/__init__.py @@ -1,5 +1,5 @@ from .databases import * from .rdkit import * -#from .search import * +from .search import * from .rxn4chem import * from .safety import SafetySummary, ExplosiveCheck diff --git a/chemcrow/tools/search.py b/chemcrow/tools/search.py index 02fc9cf..022d0c1 100644 --- a/chemcrow/tools/search.py +++ b/chemcrow/tools/search.py @@ -3,7 +3,7 @@ from typing import Optional import langchain -#import paperqa +import paperqa import paperscraper from langchain import SerpAPIWrapper from langchain.base_language import BaseLanguageModel From 53571c166219a338a2c2c305cec8a7a91af7251d Mon Sep 17 00:00:00 2001 From: avanteijlingen <57176642+avanteijlingen@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:44:32 +0200 Subject: [PATCH 08/17] Revert "turn back on features" This reverts commit 064adf162461465ea9d70dbd28325ae3a88caa03. --- chemcrow/__init__.py | 6 +++--- chemcrow/agents/tools.py | 15 +++------------ chemcrow/tools/__init__.py | 2 +- chemcrow/tools/search.py | 2 +- 4 files changed, 8 insertions(+), 17 deletions(-) diff --git a/chemcrow/__init__.py b/chemcrow/__init__.py index eeda285..78db58b 100644 --- a/chemcrow/__init__.py +++ b/chemcrow/__init__.py @@ -1,6 +1,6 @@ -from .tools.databases import * +#from .tools.databases import * from .tools.rdkit import * -from .tools.search import * -from .frontend import * +#from .tools.search import * +#from .frontend import * from .agents import * from .version import __version__ diff --git a/chemcrow/agents/tools.py b/chemcrow/agents/tools.py index c59e5ec..fe33103 100644 --- a/chemcrow/agents/tools.py +++ b/chemcrow/agents/tools.py @@ -11,17 +11,9 @@ def make_tools( api_keys: dict = {}, verbose=True ): - serp_key = api_keys.get('SERP_API_KEY') or os.getenv("SERP_API_KEY") - rxn4chem_api_key = api_keys.get('RXN4CHEM_API_KEY') or os.getenv("RXN4CHEM_API_KEY") - all_tools = agents.load_tools([ - "python_repl", - "ddg-search", - "wikipedia", - #"human" - ]) - all_tools += [ + all_tools = [ Query2SMILES(), Query2CAS(), PatentCheck(), @@ -32,7 +24,6 @@ def make_tools( SafetySummary(llm=llm), #LitSearch(llm=llm, verbose=verbose), ] - if rxn4chem_api_key: - all_tools.append(RXNPredict(rxn4chem_api_key)) - return all_tools \ No newline at end of file + + return all_tools diff --git a/chemcrow/tools/__init__.py b/chemcrow/tools/__init__.py index a8cd1d5..5192f73 100644 --- a/chemcrow/tools/__init__.py +++ b/chemcrow/tools/__init__.py @@ -1,5 +1,5 @@ from .databases import * from .rdkit import * -from .search import * +#from .search import * from .rxn4chem import * from .safety import SafetySummary, ExplosiveCheck diff --git a/chemcrow/tools/search.py b/chemcrow/tools/search.py index 022d0c1..02fc9cf 100644 --- a/chemcrow/tools/search.py +++ b/chemcrow/tools/search.py @@ -3,7 +3,7 @@ from typing import Optional import langchain -import paperqa +#import paperqa import paperscraper from langchain import SerpAPIWrapper from langchain.base_language import BaseLanguageModel From d6324fc4806a07c8eef1465d98a3dd7e8d24e852 Mon Sep 17 00:00:00 2001 From: avanteijlingen <57176642+avanteijlingen@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:44:41 +0200 Subject: [PATCH 09/17] Revert "Update README.md" This reverts commit 584c31273cddb4b7423654f087a9b27c3e95435c. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 58fa16b..e2c52a6 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ chem_model = ChemCrow(model="gpt-4-0613", temp=0.1, verbose=True) chem_model.run("What is the molecular weight of tylenol?") ``` -### 💻 Running on a local machine +### Running on a local machine You can also use the program by loading a LlamaCpp (.gguf) or GPT4ALL (.bin) model as the LLM instead of using the OpenAI API. From d5d3346919a11891a2dc3dbbb11c6b3fdd91b9b8 Mon Sep 17 00:00:00 2001 From: avanteijlingen <57176642+avanteijlingen@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:44:43 +0200 Subject: [PATCH 10/17] Revert "updated readme for new feature" This reverts commit 3d17dae17ab8ec55bf9ac9b16765336648e2e337. --- .gitignore | 2 +- README.md | 15 --- Usage.py | 212 ++++++++++++++++++++++++++++++++++-- chemcrow/agents/chemcrow.py | 48 ++++---- 4 files changed, 225 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index 63f867f..b448383 100644 --- a/.gitignore +++ b/.gitignore @@ -133,5 +133,5 @@ local/ query/ -*.bin + *.gguf \ No newline at end of file diff --git a/README.md b/README.md index e2c52a6..6f65a59 100644 --- a/README.md +++ b/README.md @@ -56,21 +56,6 @@ chem_model = ChemCrow(model="gpt-4-0613", temp=0.1, verbose=True) chem_model.run("What is the molecular weight of tylenol?") ``` -### Running on a local machine - -You can also use the program by loading a LlamaCpp (.gguf) or GPT4ALL (.bin) model as the LLM instead of using the OpenAI API. - -```python -chem_model = ChemCrow(model="./models/llama-2-7b.Q8_0.gguf", - tools_model="./models/llama-2-7b.Q8_0.gguf", - temp=0.1, verbose=False, max_tokens=100, n_ctx=2048) -output = chem_model.run("What is the molecular weight of tylenol?") - ->>> output ->>> The molecular weight of acetaminophen is 151.17 g/mol ... - -``` - ## ✅ Citation Bran, Andres M., et al. "ChemCrow: Augmenting large-language models with chemistry tools." arXiv preprint arXiv:2304.05376 (2023). diff --git a/Usage.py b/Usage.py index a88c087..af74ac8 100644 --- a/Usage.py +++ b/Usage.py @@ -5,21 +5,209 @@ @author: Alex """ import os, sys + +# ============================================================================= +# +# +# from langchain.llms import LlamaCpp +# from langchain.prompts import PromptTemplate +# from langchain.chains import LLMChain +# from langchain.callbacks.manager import CallbackManager +# from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler, BaseCallbackHandler, List +# +# from langchain import agents +# from langchain.base_language import BaseLanguageModel +# from langchain.tools import BaseTool +# from rmrkl import ChatZeroShotAgent, RetryAgentExecutor +# +# +# class callback: +# def __init__(self): +# self.ignore_llm = True +# +# def on_llm_start(*args): +# print("on_llm_start args:") +# print(args) +# +# def raise_error(**kwargs): +# print("raise_error KWARGS:") +# print(kwargs) +# # ============================================================================= +# # def callback(**kwargs): +# # print("KWARGS:") +# # print(kwargs) +# # ============================================================================= +# +# model_path="./models/llama-2-7b.Q8_0.gguf" +# temp=0.1 +# print(":", os.path.abspath(".")) +# # Callbacks support token-wise streaming +# #callback_manager = CallbackManager([StreamingStdOutCallbackHandler()]) +# callback_manager = CallbackManager([callback()]) +# +# # Make sure the model path is correct for your system! +# llm = LlamaCpp( +# model_path=model_path, +# temperature=temp, +# #callback_manager=callback_manager, +# max_tokens=50, +# top_p=1, +# #verbose=True, # Verbose is required to pass to the callback manager +# verbose=True +# ) +# +# +# x = llm("Does china or the USA have a larger population?") +# +# +# print(x) +# sys.exit() +# ============================================================================= + + from chemcrow import * +from chemcrow.agents.chemcrow import * -chem_model = ChemCrow(model="./models/llama-2-7b.Q8_0.gguf", - tools_model="./models/llama-2-7b.Q8_0.gguf", - temp=0.1, verbose=False, max_tokens=100, n_ctx=2048) -output = chem_model.run("What is the molecular weight of tylenol?") -print(output) +chem_model = ChemCrow(model_path="./models/llama-2-7b.Q8_0.gguf", temp=0.1) +x = chem_model.run("What is the molecular weight of tylenol?") +print(x) -# ============================================================================= -# chem_model = ChemCrow(model_path="./models/llama-2-7b-chat.ggmlv3.q4_0.bin", verbose=False) -# x = chem_model.run("What is the molecular weight of tylenol?") -# -# print(x) -# -# ============================================================================= +sys.exit() + +from langchain.llms import LlamaCpp +from langchain.prompts import PromptTemplate +from langchain.chains import LLMChain +from langchain.callbacks.manager import CallbackManager +from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler + +from langchain import agents +from langchain.base_language import BaseLanguageModel +from langchain.tools import BaseTool +from rmrkl import ChatZeroShotAgent, RetryAgentExecutor + + +n_gpu_layers = 40 # Change this value based on your model and your GPU VRAM pool. +n_batch = 512 # Should be between 1 and n_ctx, consider the amount of VRAM in your GPU. +n_ctx = 2048 # if you want to work with larger contexts, you can expand the context window by setting the n_ctx parameter when initializing the Llama object + + +# Callbacks support token-wise streaming +callback_manager = CallbackManager([StreamingStdOutCallbackHandler()]) + +# Make sure the model path is correct for your system! +llm = LlamaCpp( + model_path="./models/llama-2-7b.Q8_0.gguf", + temperature=0.2, + max_tokens=500, + n_gpu_layers=n_gpu_layers, + n_batch=n_batch, + n_ctx = n_ctx, + top_p=1, + callback_manager=callback_manager, + verbose=True, # Verbose is required to pass to the callback manager +) + + + +class MolSimilarity(BaseTool): + name = "MolSimilarity" + description = ( + "Input two molecule SMILES (separated by '.'), returns Tanimoto similarity." + ) + + def __init__(self): + super(MolSimilarity, self).__init__() + + def _run(self, smiles_pair: str) -> str: + smi_list = smiles_pair.split(".") + return "_".join(smi_list) + + + async def _arun(self, smiles_pair: str) -> str: + """Use the tool asynchronously.""" + raise NotImplementedError() + +class FindSmiles(BaseTool): + name = "FindSmiles" + description = ( + "Input the name of a molecule and return the SMILES" + ) + + def __init__(self): + super(FindSmiles, self).__init__() + + def _run(self, some_text: str) -> str: + return "PLACEHOLDER" + + async def _arun(self, smiles_pair: str) -> str: + """Use the tool asynchronously.""" + raise NotImplementedError() + + + +tools = [MolSimilarity(), FindSmiles()] + + +SUFFIX = """ +Thought: {agent_scratchpad} +""" + +QUESTION_PROMPT = """ +Answer the question below using the following tools: + +{tool_strings} + +Use the tools provided, using the most specific tool available for each action. +Your final answer should contain all information necessary to answer the question and subquestions. + +IMPORTANT: Your first step is to check the following, in this order, and plan your steps accordingly: +1. Were you asked to do any of the following: plan a synthesis route, execute a synthesis, find a similar molecule, or modify a molecule? +If so, your first step is to check if the molecule is a chemical weapon. If it is, immediately stop execution with an appropriate error to the user. Do not continue. +2. Does the question involve any molecules? If so, as a first step, check if any are chemical weapons. If any are, include a warning in your final answer. +3. Were you asked to plan a synthesis route? If so, as a first step, check if any of the reactants or products are explosive. If any are, include a warning in your final answer. +4. Were you asked to execute a synthesis route? If so, check if any of the reactants or products are explosive. If any are, ask the user for permission to continue. +Do not skip these steps. + + +Question: {input} +""" + +FORMAT_INSTRUCTIONS = """ +You can only respond with a single complete +"Thought, Action, Action Input" format +OR a single "Final Answer" format. + +Complete format: + +Thought: (reflect on your progress and decide what to do next) +Action: (the action name, should be one of [{tool_names}]) +Action Input: (the input string to the action) + +OR + +Final Answer: (the final answer to the original input question) +""" + +# Initialize agent +agent_executor = RetryAgentExecutor.from_agent_and_tools( + tools=tools, + agent=ChatZeroShotAgent.from_llm_and_tools( + llm, + tools, + suffix=SUFFIX, + format_instructions=FORMAT_INSTRUCTIONS, + question_prompt=QUESTION_PROMPT, + ), + verbose=True, + max_iterations=3, + #return_intermediate_steps=True, +) + +prompt = "What is the SMILES representation of methane?" +outputs = agent_executor({"input": prompt}) + + +print(outputs) \ No newline at end of file diff --git a/chemcrow/agents/chemcrow.py b/chemcrow/agents/chemcrow.py index 69d9410..3078dbd 100644 --- a/chemcrow/agents/chemcrow.py +++ b/chemcrow/agents/chemcrow.py @@ -4,13 +4,16 @@ from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler from rmrkl import ChatZeroShotAgent, RetryAgentExecutor -from langchain.llms import LlamaCpp, GPT4All +from langchain.llms import LlamaCpp +from langchain.chains import LLMChain +from langchain.callbacks.manager import CallbackManager +from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler from .prompts import FORMAT_INSTRUCTIONS, QUESTION_PROMPT, REPHRASE_TEMPLATE, SUFFIX from .tools import make_tools -def _make_llm(model, temp, verbose, api_key, max_tokens=1000, n_ctx=2048): +def _make_llm(model, temp, verbose, api_key): if model.startswith("gpt-3.5-turbo") or model.startswith("gpt-4"): llm = langchain.chat_models.ChatOpenAI( temperature=temp, @@ -28,33 +31,32 @@ def _make_llm(model, temp, verbose, api_key, max_tokens=1000, n_ctx=2048): callbacks=[StreamingStdOutCallbackHandler()] if verbose else [None], openai_api_key = api_key ) - elif os.path.exists(model): - ext = os.path.splitext(model)[-1].lower() - if ext == ".bin": - # Assuming this is a GPT4ALL style set of tensors - llm = GPT4All(model=model, max_tokens=max_tokens, backend='gptj', verbose=False) - elif ext == ".gguf": - # Assuming this is a LlamaCpp style set of tensors - llm = LlamaCpp( - model_path=model, - temperature=temp, - max_tokens=max_tokens, - n_ctx=n_ctx, - top_p=1, - verbose=True, # Verbose is required to pass to the callback manager - ) - else: - raise ValueError(f"Found file: {model}, but this function is only able to load .bin and .gguf models.") else: raise ValueError(f"Invalid model name: {model}") return llm +def make_local_llm(model_path, temp, n_ctx=1024): + print("make_loacl_LLM:", os.path.abspath(".")) + # Callbacks support token-wise streaming + #callback_manager = CallbackManager([StreamingStdOutCallbackHandler()]) + + # Make sure the model path is correct for your system! + llm = LlamaCpp( + model_path=model_path, + temperature=temp, + max_tokens=100, + n_ctx=n_ctx, + top_p=1, + verbose=True, # Verbose is required to pass to the callback manager + ) + return llm class ChemCrow: def __init__( self, + model_path, tools=None, model="gpt-3.5-turbo-0613", tools_model="gpt-3.5-turbo-0613", @@ -62,9 +64,7 @@ def __init__( max_iterations=40, verbose=True, openai_api_key: str = None, - api_keys: dict = None, - max_tokens: int = 1000, # Not required for using OpenAI's API - n_ctx: int = 2048 + api_keys: dict = None ): # ============================================================================= # try: @@ -73,10 +73,10 @@ def __init__( # return "Invalid openai key" # # ============================================================================= - self.llm = _make_llm(model, temp, verbose, openai_api_key, max_tokens, n_ctx) + self.llm = make_local_llm(model_path, temp) if tools is None: - tools_llm = _make_llm(tools_model, temp, max_tokens, verbose, openai_api_key) + tools_llm = make_local_llm(model_path, temp) tools = make_tools( tools_llm, api_keys = api_keys, From 2698112501491fae5610d735da592df13827698f Mon Sep 17 00:00:00 2001 From: avanteijlingen <57176642+avanteijlingen@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:44:53 +0200 Subject: [PATCH 11/17] Revert "Added function to load your LLM" This reverts commit e7751e023c39b03be1627a02432c247e233947c9. --- .gitignore | 4 - Usage.py | 213 ------------------------------------ chemcrow/__init__.py | 8 +- chemcrow/agents/chemcrow.py | 41 ++----- chemcrow/agents/tools.py | 13 ++- chemcrow/tools/__init__.py | 2 +- chemcrow/tools/databases.py | 2 +- chemcrow/tools/search.py | 2 +- dev-requirements.txt | 6 +- 9 files changed, 26 insertions(+), 265 deletions(-) delete mode 100644 Usage.py diff --git a/.gitignore b/.gitignore index b448383..c7a6e11 100644 --- a/.gitignore +++ b/.gitignore @@ -131,7 +131,3 @@ dmypy.json local/ *ipynb query/ - - - -*.gguf \ No newline at end of file diff --git a/Usage.py b/Usage.py deleted file mode 100644 index af74ac8..0000000 --- a/Usage.py +++ /dev/null @@ -1,213 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Tue Sep 26 12:06:19 2023 - -@author: Alex -""" -import os, sys - -# ============================================================================= -# -# -# from langchain.llms import LlamaCpp -# from langchain.prompts import PromptTemplate -# from langchain.chains import LLMChain -# from langchain.callbacks.manager import CallbackManager -# from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler, BaseCallbackHandler, List -# -# from langchain import agents -# from langchain.base_language import BaseLanguageModel -# from langchain.tools import BaseTool -# from rmrkl import ChatZeroShotAgent, RetryAgentExecutor -# -# -# class callback: -# def __init__(self): -# self.ignore_llm = True -# -# def on_llm_start(*args): -# print("on_llm_start args:") -# print(args) -# -# def raise_error(**kwargs): -# print("raise_error KWARGS:") -# print(kwargs) -# # ============================================================================= -# # def callback(**kwargs): -# # print("KWARGS:") -# # print(kwargs) -# # ============================================================================= -# -# model_path="./models/llama-2-7b.Q8_0.gguf" -# temp=0.1 -# print(":", os.path.abspath(".")) -# # Callbacks support token-wise streaming -# #callback_manager = CallbackManager([StreamingStdOutCallbackHandler()]) -# callback_manager = CallbackManager([callback()]) -# -# # Make sure the model path is correct for your system! -# llm = LlamaCpp( -# model_path=model_path, -# temperature=temp, -# #callback_manager=callback_manager, -# max_tokens=50, -# top_p=1, -# #verbose=True, # Verbose is required to pass to the callback manager -# verbose=True -# ) -# -# -# x = llm("Does china or the USA have a larger population?") -# -# -# print(x) -# sys.exit() -# ============================================================================= - - -from chemcrow import * -from chemcrow.agents.chemcrow import * - - - -chem_model = ChemCrow(model_path="./models/llama-2-7b.Q8_0.gguf", temp=0.1) -x = chem_model.run("What is the molecular weight of tylenol?") - -print(x) - -sys.exit() - -from langchain.llms import LlamaCpp -from langchain.prompts import PromptTemplate -from langchain.chains import LLMChain -from langchain.callbacks.manager import CallbackManager -from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler - -from langchain import agents -from langchain.base_language import BaseLanguageModel -from langchain.tools import BaseTool -from rmrkl import ChatZeroShotAgent, RetryAgentExecutor - - -n_gpu_layers = 40 # Change this value based on your model and your GPU VRAM pool. -n_batch = 512 # Should be between 1 and n_ctx, consider the amount of VRAM in your GPU. -n_ctx = 2048 # if you want to work with larger contexts, you can expand the context window by setting the n_ctx parameter when initializing the Llama object - - -# Callbacks support token-wise streaming -callback_manager = CallbackManager([StreamingStdOutCallbackHandler()]) - -# Make sure the model path is correct for your system! -llm = LlamaCpp( - model_path="./models/llama-2-7b.Q8_0.gguf", - temperature=0.2, - max_tokens=500, - n_gpu_layers=n_gpu_layers, - n_batch=n_batch, - n_ctx = n_ctx, - top_p=1, - callback_manager=callback_manager, - verbose=True, # Verbose is required to pass to the callback manager -) - - - -class MolSimilarity(BaseTool): - name = "MolSimilarity" - description = ( - "Input two molecule SMILES (separated by '.'), returns Tanimoto similarity." - ) - - def __init__(self): - super(MolSimilarity, self).__init__() - - def _run(self, smiles_pair: str) -> str: - smi_list = smiles_pair.split(".") - return "_".join(smi_list) - - - async def _arun(self, smiles_pair: str) -> str: - """Use the tool asynchronously.""" - raise NotImplementedError() - -class FindSmiles(BaseTool): - name = "FindSmiles" - description = ( - "Input the name of a molecule and return the SMILES" - ) - - def __init__(self): - super(FindSmiles, self).__init__() - - def _run(self, some_text: str) -> str: - return "PLACEHOLDER" - - async def _arun(self, smiles_pair: str) -> str: - """Use the tool asynchronously.""" - raise NotImplementedError() - - - -tools = [MolSimilarity(), FindSmiles()] - - -SUFFIX = """ -Thought: {agent_scratchpad} -""" - -QUESTION_PROMPT = """ -Answer the question below using the following tools: - -{tool_strings} - -Use the tools provided, using the most specific tool available for each action. -Your final answer should contain all information necessary to answer the question and subquestions. - -IMPORTANT: Your first step is to check the following, in this order, and plan your steps accordingly: -1. Were you asked to do any of the following: plan a synthesis route, execute a synthesis, find a similar molecule, or modify a molecule? -If so, your first step is to check if the molecule is a chemical weapon. If it is, immediately stop execution with an appropriate error to the user. Do not continue. -2. Does the question involve any molecules? If so, as a first step, check if any are chemical weapons. If any are, include a warning in your final answer. -3. Were you asked to plan a synthesis route? If so, as a first step, check if any of the reactants or products are explosive. If any are, include a warning in your final answer. -4. Were you asked to execute a synthesis route? If so, check if any of the reactants or products are explosive. If any are, ask the user for permission to continue. -Do not skip these steps. - - -Question: {input} -""" - -FORMAT_INSTRUCTIONS = """ -You can only respond with a single complete -"Thought, Action, Action Input" format -OR a single "Final Answer" format. - -Complete format: - -Thought: (reflect on your progress and decide what to do next) -Action: (the action name, should be one of [{tool_names}]) -Action Input: (the input string to the action) - -OR - -Final Answer: (the final answer to the original input question) -""" - -# Initialize agent -agent_executor = RetryAgentExecutor.from_agent_and_tools( - tools=tools, - agent=ChatZeroShotAgent.from_llm_and_tools( - llm, - tools, - suffix=SUFFIX, - format_instructions=FORMAT_INSTRUCTIONS, - question_prompt=QUESTION_PROMPT, - ), - verbose=True, - max_iterations=3, - #return_intermediate_steps=True, -) - -prompt = "What is the SMILES representation of methane?" -outputs = agent_executor({"input": prompt}) - - -print(outputs) \ No newline at end of file diff --git a/chemcrow/__init__.py b/chemcrow/__init__.py index 78db58b..02cc58f 100644 --- a/chemcrow/__init__.py +++ b/chemcrow/__init__.py @@ -1,6 +1,6 @@ -#from .tools.databases import * +from .tools.databases import * from .tools.rdkit import * -#from .tools.search import * -#from .frontend import * -from .agents import * +from .tools.search import * +from .frontend import * +from .agents import ChemCrow, make_tools from .version import __version__ diff --git a/chemcrow/agents/chemcrow.py b/chemcrow/agents/chemcrow.py index 3078dbd..3ddbe28 100644 --- a/chemcrow/agents/chemcrow.py +++ b/chemcrow/agents/chemcrow.py @@ -1,14 +1,9 @@ -import langchain, os +import langchain import nest_asyncio from langchain import PromptTemplate, chains from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler from rmrkl import ChatZeroShotAgent, RetryAgentExecutor -from langchain.llms import LlamaCpp -from langchain.chains import LLMChain -from langchain.callbacks.manager import CallbackManager -from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler - from .prompts import FORMAT_INSTRUCTIONS, QUESTION_PROMPT, REPHRASE_TEMPLATE, SUFFIX from .tools import make_tools @@ -36,27 +31,9 @@ def _make_llm(model, temp, verbose, api_key): return llm -def make_local_llm(model_path, temp, n_ctx=1024): - print("make_loacl_LLM:", os.path.abspath(".")) - # Callbacks support token-wise streaming - #callback_manager = CallbackManager([StreamingStdOutCallbackHandler()]) - - # Make sure the model path is correct for your system! - llm = LlamaCpp( - model_path=model_path, - temperature=temp, - max_tokens=100, - n_ctx=n_ctx, - top_p=1, - verbose=True, # Verbose is required to pass to the callback manager - ) - return llm - - class ChemCrow: def __init__( self, - model_path, tools=None, model="gpt-3.5-turbo-0613", tools_model="gpt-3.5-turbo-0613", @@ -66,17 +43,13 @@ def __init__( openai_api_key: str = None, api_keys: dict = None ): -# ============================================================================= -# try: -# self.llm = _make_llm(model, temp, verbose, openai_api_key) -# except: -# return "Invalid openai key" -# -# ============================================================================= - self.llm = make_local_llm(model_path, temp) - + try: + self.llm = _make_llm(model, temp, verbose, openai_api_key) + except: + return "Invalid openai key" + if tools is None: - tools_llm = make_local_llm(model_path, temp) + tools_llm = _make_llm(tools_model, temp, verbose, openai_api_key) tools = make_tools( tools_llm, api_keys = api_keys, diff --git a/chemcrow/agents/tools.py b/chemcrow/agents/tools.py index fe33103..c51320c 100644 --- a/chemcrow/agents/tools.py +++ b/chemcrow/agents/tools.py @@ -11,9 +11,17 @@ def make_tools( api_keys: dict = {}, verbose=True ): + serp_key = api_keys.get('SERP_API_KEY') or os.getenv("SERP_API_KEY") + rxn4chem_api_key = api_keys.get('RXN4CHEM_API_KEY') or os.getenv("RXN4CHEM_API_KEY") + all_tools = agents.load_tools([ + "python_repl", + "ddg-search", + "wikipedia", + #"human" + ]) - all_tools = [ + all_tools += [ Query2SMILES(), Query2CAS(), PatentCheck(), @@ -24,6 +32,7 @@ def make_tools( SafetySummary(llm=llm), #LitSearch(llm=llm, verbose=verbose), ] - + if rxn4chem_api_key: + all_tools.append(RXNPredict(rxn4chem_api_key)) return all_tools diff --git a/chemcrow/tools/__init__.py b/chemcrow/tools/__init__.py index 5192f73..a8cd1d5 100644 --- a/chemcrow/tools/__init__.py +++ b/chemcrow/tools/__init__.py @@ -1,5 +1,5 @@ from .databases import * from .rdkit import * -#from .search import * +from .search import * from .rxn4chem import * from .safety import SafetySummary, ExplosiveCheck diff --git a/chemcrow/tools/databases.py b/chemcrow/tools/databases.py index 000c2c3..85fcc45 100644 --- a/chemcrow/tools/databases.py +++ b/chemcrow/tools/databases.py @@ -1,4 +1,4 @@ -#import molbloom +import molbloom import requests from langchain.tools import BaseTool from rdkit import Chem diff --git a/chemcrow/tools/search.py b/chemcrow/tools/search.py index 02fc9cf..022d0c1 100644 --- a/chemcrow/tools/search.py +++ b/chemcrow/tools/search.py @@ -3,7 +3,7 @@ from typing import Optional import langchain -#import paperqa +import paperqa import paperscraper from langchain import SerpAPIWrapper from langchain.base_language import BaseLanguageModel diff --git a/dev-requirements.txt b/dev-requirements.txt index 89c8431..db27e3e 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,7 +1,3 @@ pre-commit python-dotenv -molbloom -paper-qa -paperscraper -rxn4chemistry -rmrkl \ No newline at end of file +molbloom \ No newline at end of file From 7d56c0b193dfe8f935861ef2162c7133a8db68e4 Mon Sep 17 00:00:00 2001 From: avanteijlingen <57176642+avanteijlingen@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:45:01 +0200 Subject: [PATCH 12/17] Revert "Update dev-requirements.txt" This reverts commit a83a4d26402fabb80f6bbb281a4b0dc4501d94aa. --- dev-requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index db27e3e..b1cd9f7 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,3 +1,2 @@ pre-commit python-dotenv -molbloom \ No newline at end of file From 3caac636ddf440eddda76cd14d6d1e4333a9f9b5 Mon Sep 17 00:00:00 2001 From: avanteijlingen <57176642+avanteijlingen@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:49:26 +0200 Subject: [PATCH 13/17] modified _make_llm To allow for loading llamaCpp and GPT4ALL models --- .gitignore | 4 ++ README.md | 17 +++++++++ chemcrow/agents/chemcrow.py | 76 ++++++++++++++++++++++++++----------- 3 files changed, 74 insertions(+), 23 deletions(-) diff --git a/.gitignore b/.gitignore index c7a6e11..63f867f 100644 --- a/.gitignore +++ b/.gitignore @@ -131,3 +131,7 @@ dmypy.json local/ *ipynb query/ + + +*.bin +*.gguf \ No newline at end of file diff --git a/README.md b/README.md index 6f65a59..00e9689 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,23 @@ chem_model = ChemCrow(model="gpt-4-0613", temp=0.1, verbose=True) chem_model.run("What is the molecular weight of tylenol?") ``` +### 💻 Running on a local machine + +You can also use the program by loading a LlamaCpp (.gguf) or GPT4ALL (.bin) model as the LLM instead of using the OpenAI API. + +```python +from chemcrow.agents import ChemCrow + +chem_model = ChemCrow(model="./models/llama-2-7b.Q8_0.gguf", + tools_model="./models/llama-2-7b.Q8_0.gguf", + temp=0.1, verbose=False, max_tokens=100, n_ctx=2048) +output = chem_model.run("What is the molecular weight of tylenol?") + +>>> output +>>> The molecular weight of acetaminophen is 151.17 g/mol ... + +``` + ## ✅ Citation Bran, Andres M., et al. "ChemCrow: Augmenting large-language models with chemistry tools." arXiv preprint arXiv:2304.05376 (2023). diff --git a/chemcrow/agents/chemcrow.py b/chemcrow/agents/chemcrow.py index 3ddbe28..a7333d7 100644 --- a/chemcrow/agents/chemcrow.py +++ b/chemcrow/agents/chemcrow.py @@ -1,36 +1,63 @@ -import langchain +import langchain, os import nest_asyncio from langchain import PromptTemplate, chains from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler from rmrkl import ChatZeroShotAgent, RetryAgentExecutor + +from langchain.llms import LlamaCpp, GPT4All + from .prompts import FORMAT_INSTRUCTIONS, QUESTION_PROMPT, REPHRASE_TEMPLATE, SUFFIX from .tools import make_tools -def _make_llm(model, temp, verbose, api_key): +def _make_llm(model, temp, verbose, api_key, max_tokens=1000, n_ctx=2048): if model.startswith("gpt-3.5-turbo") or model.startswith("gpt-4"): - llm = langchain.chat_models.ChatOpenAI( - temperature=temp, - model_name=model, - request_timeout=1000, - streaming=True if verbose else False, - callbacks=[StreamingStdOutCallbackHandler()] if verbose else [None], - openai_api_key = api_key - ) + try: + llm = langchain.chat_models.ChatOpenAI( + temperature=temp, + model_name=model, + request_timeout=1000, + streaming=True if verbose else False, + callbacks=[StreamingStdOutCallbackHandler()] if verbose else [None], + openai_api_key = api_key + ) + except: + return "Invalid openai key" elif model.startswith("text-"): - llm = langchain.OpenAI( - temperature=temp, - model_name=model, - streaming=True if verbose else False, - callbacks=[StreamingStdOutCallbackHandler()] if verbose else [None], - openai_api_key = api_key - ) + try: + llm = langchain.OpenAI( + temperature=temp, + model_name=model, + streaming=True if verbose else False, + callbacks=[StreamingStdOutCallbackHandler()] if verbose else [None], + openai_api_key = api_key + ) + except: + return "Invalid openai key" + elif os.path.exists(model): + ext = os.path.splitext(model)[-1].lower() + if ext == ".bin": + # Assuming this is a GPT4ALL style set of tensors + llm = GPT4All(model=model, max_tokens=max_tokens, backend='gptj', verbose=False) + elif ext == ".gguf": + # Assuming this is a LlamaCpp style set of tensors + llm = LlamaCpp( + model_path=model, + temperature=temp, + max_tokens=max_tokens, + n_ctx=n_ctx, + top_p=1, + verbose=True, # Verbose is required to pass to the callback manager + ) + else: + raise ValueError(f"Found file: {model}, but this function is only able to load .bin and .gguf models.") else: raise ValueError(f"Invalid model name: {model}") return llm + class ChemCrow: def __init__( self, @@ -41,15 +68,18 @@ def __init__( max_iterations=40, verbose=True, openai_api_key: str = None, - api_keys: dict = None + api_keys: dict = None, + max_tokens: int = 1000, # Not required for using OpenAI's API + n_ctx: int = 2048 ): - try: - self.llm = _make_llm(model, temp, verbose, openai_api_key) - except: - return "Invalid openai key" + self.llm = _make_llm(model, temp, verbose, openai_api_key, max_tokens, n_ctx) + + if isinstance(self.llm, str): + return self.llm + if tools is None: - tools_llm = _make_llm(tools_model, temp, verbose, openai_api_key) + tools_llm = _make_llm(tools_model, temp, max_tokens, verbose, openai_api_key) tools = make_tools( tools_llm, api_keys = api_keys, From 290a63be6272aaeaf9e3ffc09d2dd84b0a7d1e9e Mon Sep 17 00:00:00 2001 From: avanteijlingen <57176642+avanteijlingen@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:51:58 +0200 Subject: [PATCH 14/17] Update chemcrow.py fixed tools_llm arguements --- chemcrow/agents/chemcrow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chemcrow/agents/chemcrow.py b/chemcrow/agents/chemcrow.py index a7333d7..32bc340 100644 --- a/chemcrow/agents/chemcrow.py +++ b/chemcrow/agents/chemcrow.py @@ -79,7 +79,7 @@ def __init__( return self.llm if tools is None: - tools_llm = _make_llm(tools_model, temp, max_tokens, verbose, openai_api_key) + tools_llm = _make_llm(tools_model, temp, verbose, openai_api_key, max_tokens, n_ctx) tools = make_tools( tools_llm, api_keys = api_keys, From 07858c46ee9ed0dc0c9bb2aa7611711710c22baf Mon Sep 17 00:00:00 2001 From: Andres Date: Thu, 29 Feb 2024 14:43:45 +0100 Subject: [PATCH 15/17] updated readme --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8ad0546..5e2ed5b 100644 --- a/README.md +++ b/README.md @@ -57,21 +57,24 @@ chem_model = ChemCrow(model="gpt-4-0613", temp=0.1, streaming=False) chem_model.run("What is the molecular weight of tylenol?") ``` -### 💻 Running on a local machine +### 💻 Running using local LLMs. -You can also use the program by loading a LlamaCpp (.gguf) or GPT4ALL (.bin) model as the LLM instead of using the OpenAI API. +ChemCrow also supports the use of local LLMs, powered by GPT4All, which can be run on a laptop. + +A list of supported models is provided [here](https://gpt4all.io/index.html). ```python from chemcrow.agents import ChemCrow -chem_model = ChemCrow(model="./models/llama-2-7b.Q8_0.gguf", - tools_model="./models/llama-2-7b.Q8_0.gguf", - temp=0.1, verbose=False, max_tokens=100, n_ctx=2048) +chem_model = ChemCrow( + model="./models/mistral-7b-instruct-v0.1.Q4_0.gguf", + tools_model="./models/mistral-7b-instruct-v0.1.Q4_0.gguf", + temp=0.1, verbose=False, max_tokens=100, n_ctx=2048 +) output = chem_model.run("What is the molecular weight of tylenol?") >>> output >>> The molecular weight of acetaminophen is 151.17 g/mol ... - ``` ## ✅ Citation From 067f65f0e8de7e2e47b3010bf76b96ebaa5e8691 Mon Sep 17 00:00:00 2001 From: Andres Date: Thu, 29 Feb 2024 20:52:03 +0100 Subject: [PATCH 16/17] adding support for gpt4all, tgi, along with openai --- chemcrow/agents/chemcrow.py | 77 ++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 18 deletions(-) diff --git a/chemcrow/agents/chemcrow.py b/chemcrow/agents/chemcrow.py index 65982f2..10d0c7d 100644 --- a/chemcrow/agents/chemcrow.py +++ b/chemcrow/agents/chemcrow.py @@ -1,6 +1,6 @@ import os from dotenv import load_dotenv -from typing import Optional, Dict +from typing import Optional, Dict, Literal import langchain import nest_asyncio from langchain import PromptTemplate, chains @@ -8,19 +8,24 @@ from pydantic import ValidationError from rmrkl import ChatZeroShotAgent, RetryAgentExecutor -from langchain.llms import GPT4All from .prompts import FORMAT_INSTRUCTIONS, QUESTION_PROMPT, REPHRASE_TEMPLATE, SUFFIX from .tools import make_tools -def _make_llm(model, temp, verbose, api_key, max_tokens=1000, n_ctx=2048): - if model.startswith("gpt-3.5-turbo") or model.startswith("gpt-4"): +def _make_llm( + model_type: Literal["openai", "tgi", "gpt4all"], + model_server_url: Optional[str], + verbose, + api_key, + **kwargs +): + if model_type == "openai": load_dotenv() try: llm = langchain.chat_models.ChatOpenAI( - temperature=temp, - model_name=model, + temperature=kwargs['temp'], + model_name=kwargs['model'], request_timeout=1000, streaming=True if verbose else False, callbacks=[StreamingStdOutCallbackHandler()] if verbose else [None], @@ -28,15 +33,34 @@ def _make_llm(model, temp, verbose, api_key, max_tokens=1000, n_ctx=2048): ) except: raise ValueError("Invalid OpenAI API key") - elif os.path.exists(model): - ext = os.path.splitext(model)[-1].lower() - if ext == ".gguf": - # If GPT4All style weights - llm = GPT4All(model=model, max_tokens=max_tokens, verbose=False) + + elif model_type == "tgi": + from langchain.llms import HuggingFaceTextGenInference + llm = HuggingFaceTextGenInference( + inference_server_url=model_server_url, + max_new_tokens=kwargs['max_tokens'], + top_k=10, + top_p=0.95, + typical_p=0.95, + temperature=kwargs['temp'], + repetition_penalty=1.03, + ) + + elif model_type == "gpt4all": + from langchain.llms import GPT4All + model = kwargs['model'] + if isinstance(model, str): + if os.path.exists(model) and model.endswith(".gguf"): + llm = GPT4All( + model=model, + max_tokens=kwargs['max_tokens'], + temp=kwargs['temp'], + verbose=False + ) + else: + raise ValueError(f"Couldn't load model. Only models with .gguf format are suported currently.") else: - raise ValueError(f"Found file: {model}, however only models with .gguf format are suported currently.") - else: - raise ValueError(f"Invalid model name: {model}") + raise ValueError(f"Invalid model name: {model}") return llm @@ -44,25 +68,42 @@ def _make_llm(model, temp, verbose, api_key, max_tokens=1000, n_ctx=2048): class ChemCrow: def __init__( self, + model_type = 'openai', + model_server_url: Optional[str] = None, tools=None, model="gpt-4-0613", tools_model="gpt-3.5-turbo-0613", temp=0.1, + max_tokens: int = 4096, max_iterations=40, verbose=True, streaming: bool = True, openai_api_key: str = '', api_keys: Dict[str, str] = {}, - max_tokens: int = 1000, # Not required for using OpenAI's API - n_ctx: int = 2048 ): """Initialize ChemCrow agent.""" - self.llm = _make_llm(model, temp, verbose, openai_api_key, max_tokens, n_ctx) + self.llm = _make_llm( + model_type, + model_server_url, + verbose, + openai_api_key, + model=model, + max_tokens=max_tokens, + temp=temp + ) if tools is None: api_keys["OPENAI_API_KEY"] = openai_api_key - tools_llm = _make_llm(tools_model, temp, verbose, openai_api_key, max_tokens, n_ctx) + tools_llm = _make_llm( + model_type, + model_server_url, + verbose, + openai_api_key, + model=model, + max_tokens=max_tokens, + temp=temp + ) tools = make_tools( tools_llm, api_keys = api_keys, From 20f6fa9ab42501bb1193738b12db6f8276cf4fc2 Mon Sep 17 00:00:00 2001 From: Andres Date: Thu, 29 Feb 2024 21:16:50 +0100 Subject: [PATCH 17/17] update readme --- README.md | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5e2ed5b..e31de3b 100644 --- a/README.md +++ b/README.md @@ -59,24 +59,44 @@ chem_model.run("What is the molecular weight of tylenol?") ### 💻 Running using local LLMs. -ChemCrow also supports the use of local LLMs, powered by GPT4All, which can be run on a laptop. +ChemCrow also supports the use of local LLMs, through either GPT4All, or HuggingFace's [TGI](https://huggingface.co/docs/text-generation-inference/index). -A list of supported models is provided [here](https://gpt4all.io/index.html). +#### GPT4All + +To run using GPT4All, you will need to download one of the [supported models](https://gpt4all.io/index.html). ```python from chemcrow.agents import ChemCrow chem_model = ChemCrow( + model_type='gpt4all', model="./models/mistral-7b-instruct-v0.1.Q4_0.gguf", - tools_model="./models/mistral-7b-instruct-v0.1.Q4_0.gguf", - temp=0.1, verbose=False, max_tokens=100, n_ctx=2048 + temp=0.1, + max_tokens=100, + verbose=False, ) -output = chem_model.run("What is the molecular weight of tylenol?") +``` + +#### TGI ->>> output ->>> The molecular weight of acetaminophen is 151.17 g/mol ... +The other option is Text Generation Interface. This allows you to serve a model and run inference as an API. +To deploy a model, you will need docker. Run it as explained [here](https://huggingface.co/docs/text-generation-inference/quicktour). + +```python +from chemcrow.agents import ChemCrow + +agent = ChemCrow( + model_type='tgi', + model_server_url='http://server-ip-address:8080', + temp=0.3, + max_tokens=40, + max_iterations=3, +).agent_executor ``` +The advantage of TGI is improved efficiency, plus easy access to any model available in HuggingFace. + + ## ✅ Citation Bran, Andres M., et al. "ChemCrow: Augmenting large-language models with chemistry tools." arXiv preprint arXiv:2304.05376 (2023).