Added support for Ollama running locally or publicly hosted api
This commit is contained in:
parent
6f3b3252ba
commit
d964f599be
5 changed files with 34 additions and 17 deletions
|
|
@ -152,6 +152,12 @@ This file contains sensitive information. Never share or commit this file to ver
|
||||||
- Replace with your OpenAI API key for GPT integration
|
- Replace with your OpenAI API key for GPT integration
|
||||||
- To obtain an API key, follow the tutorial at: https://medium.com/@lorenzozar/how-to-get-your-own-openai-api-key-f4d44e60c327
|
- To obtain an API key, follow the tutorial at: https://medium.com/@lorenzozar/how-to-get-your-own-openai-api-key-f4d44e60c327
|
||||||
- Note: You need to add credit to your OpenAI account to use the API. You can add credit by visiting the [OpenAI billing dashboard](https://platform.openai.com/account/billing).
|
- Note: You need to add credit to your OpenAI account to use the API. You can add credit by visiting the [OpenAI billing dashboard](https://platform.openai.com/account/billing).
|
||||||
|
- `openai_api_free_hosted_url`:
|
||||||
|
- Optional paramter, if you want to use freely hosted GPT model, set `openai_api_key: "freehosted"` and `openai_api_free_hosted_url` with the URL of the endpoint
|
||||||
|
- Ollama local support
|
||||||
|
- If you want to use Ollama which is deployed locally, leave `openai_api_key` blank.
|
||||||
|
- To setup Ollama to run locally follow the instructions here: [Ollama installation](https://github.com/ollama/ollama).
|
||||||
|
- Download mistral model by pulling mistral:v0.3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
email: myemaillinkedin@gmail.com
|
email: myemaillinkedin@gmail.com
|
||||||
password: ImpossiblePassowrd10
|
password: ImpossiblePassowrd10
|
||||||
openai_api_key: sk-11KRr4uuTwpRGfeRTfj1T9BlbkFJjP8QTrswHU1yGruru2FR
|
openai_api_key: sk-11KRr4uuTwpRGfeRTfj1T9BlbkFJjP8QTrswHU1yGruru2FR
|
||||||
|
openai_api_free_hosted_url: https://api.pawan.krd/cosmosrp/v1
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
email: myemaillinkedin@gmail.com
|
email: myemaillinkedin@gmail.com
|
||||||
password: ImpossiblePassowrd10
|
password: ImpossiblePassowrd10
|
||||||
openai_api_key: sk-11KRr4uuTwpRGfeRTfj1T9BlbkFJjP8QTrswHU1yGruru2FR
|
openai_api_key: sk-11KRr4uuTwpRGfeRTfj1T9BlbkFJjP8QTrswHU1yGruru2FR
|
||||||
|
openai_api_free_hosted_url: https://api.pawan.krd/cosmosrp/v1
|
||||||
12
main.py
12
main.py
|
|
@ -101,7 +101,7 @@ class ConfigValidator:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_secrets(secrets_yaml_path: Path) -> tuple:
|
def validate_secrets(secrets_yaml_path: Path) -> tuple:
|
||||||
secrets = ConfigValidator.validate_yaml_file(secrets_yaml_path)
|
secrets = ConfigValidator.validate_yaml_file(secrets_yaml_path)
|
||||||
mandatory_secrets = ['email', 'password', 'openai_api_key']
|
mandatory_secrets = ['email', 'password']
|
||||||
|
|
||||||
for secret in mandatory_secrets:
|
for secret in mandatory_secrets:
|
||||||
if secret not in secrets:
|
if secret not in secrets:
|
||||||
|
|
@ -114,7 +114,7 @@ class ConfigValidator:
|
||||||
if not secrets['openai_api_key']:
|
if not secrets['openai_api_key']:
|
||||||
raise ConfigError(f"OpenAI API key cannot be empty in secrets file {secrets_yaml_path}.")
|
raise ConfigError(f"OpenAI API key cannot be empty in secrets file {secrets_yaml_path}.")
|
||||||
|
|
||||||
return secrets['email'], str(secrets['password']), secrets['openai_api_key']
|
return secrets['email'], str(secrets['password']), secrets['openai_api_key'], secrets['openai_api_free_hosted_url']
|
||||||
|
|
||||||
class FileManager:
|
class FileManager:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -158,7 +158,7 @@ def init_browser() -> webdriver.Chrome:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise RuntimeError(f"Failed to initialize browser: {str(e)}")
|
raise RuntimeError(f"Failed to initialize browser: {str(e)}")
|
||||||
|
|
||||||
def create_and_run_bot(email: str, password: str, parameters: dict, openai_api_key: str):
|
def create_and_run_bot(email, password, parameters, openai_api_key, openai_api_free_hosted_url):
|
||||||
try:
|
try:
|
||||||
style_manager = StyleManager()
|
style_manager = StyleManager()
|
||||||
resume_generator = ResumeGenerator()
|
resume_generator = ResumeGenerator()
|
||||||
|
|
@ -175,7 +175,7 @@ def create_and_run_bot(email: str, password: str, parameters: dict, openai_api_k
|
||||||
browser = init_browser()
|
browser = init_browser()
|
||||||
login_component = LinkedInAuthenticator(browser)
|
login_component = LinkedInAuthenticator(browser)
|
||||||
apply_component = LinkedInJobManager(browser)
|
apply_component = LinkedInJobManager(browser)
|
||||||
gpt_answerer_component = GPTAnswerer(openai_api_key)
|
gpt_answerer_component = GPTAnswerer(openai_api_key, openai_api_free_hosted_url)
|
||||||
bot = LinkedInBotFacade(login_component, apply_component)
|
bot = LinkedInBotFacade(login_component, apply_component)
|
||||||
bot.set_secrets(email, password)
|
bot.set_secrets(email, password)
|
||||||
bot.set_job_application_profile_and_resume(job_application_profile_object, resume_object)
|
bot.set_job_application_profile_and_resume(job_application_profile_object, resume_object)
|
||||||
|
|
@ -197,12 +197,12 @@ def main(resume: Path = None):
|
||||||
secrets_file, config_file, plain_text_resume_file, output_folder = FileManager.validate_data_folder(data_folder)
|
secrets_file, config_file, plain_text_resume_file, output_folder = FileManager.validate_data_folder(data_folder)
|
||||||
|
|
||||||
parameters = ConfigValidator.validate_config(config_file)
|
parameters = ConfigValidator.validate_config(config_file)
|
||||||
email, password, openai_api_key = ConfigValidator.validate_secrets(secrets_file)
|
email, password, openai_api_key, openai_api_free_hosted_url = ConfigValidator.validate_secrets(secrets_file)
|
||||||
|
|
||||||
parameters['uploads'] = FileManager.file_paths_to_dict(resume, plain_text_resume_file)
|
parameters['uploads'] = FileManager.file_paths_to_dict(resume, plain_text_resume_file)
|
||||||
parameters['outputFileDirectory'] = output_folder
|
parameters['outputFileDirectory'] = output_folder
|
||||||
|
|
||||||
create_and_run_bot(email, password, parameters, openai_api_key)
|
create_and_run_bot(email, password, parameters, openai_api_key, openai_api_free_hosted_url)
|
||||||
except ConfigError as ce:
|
except ConfigError as ce:
|
||||||
print(f"Configuration error: {str(ce)}")
|
print(f"Configuration error: {str(ce)}")
|
||||||
print("Refer to the configuration guide for troubleshooting: https://github.com/feder-cr/LinkedIn_AIHawk_automatic_job_application/blob/main/readme.md#configuration")
|
print("Refer to the configuration guide for troubleshooting: https://github.com/feder-cr/LinkedIn_AIHawk_automatic_job_application/blob/main/readme.md#configuration")
|
||||||
|
|
|
||||||
25
src/gpt.py
25
src/gpt.py
|
|
@ -3,7 +3,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import textwrap
|
import textwrap
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Dict, List
|
from typing import Dict, List, Union
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from langchain_core.messages.ai import AIMessage
|
from langchain_core.messages.ai import AIMessage
|
||||||
|
|
@ -11,6 +11,7 @@ from langchain_core.output_parsers import StrOutputParser
|
||||||
from langchain_core.prompt_values import StringPromptValue
|
from langchain_core.prompt_values import StringPromptValue
|
||||||
from langchain_core.prompts import ChatPromptTemplate
|
from langchain_core.prompts import ChatPromptTemplate
|
||||||
from langchain_openai import ChatOpenAI
|
from langchain_openai import ChatOpenAI
|
||||||
|
from langchain_ollama import ChatOllama
|
||||||
from Levenshtein import distance
|
from Levenshtein import distance
|
||||||
|
|
||||||
import src.strings as strings
|
import src.strings as strings
|
||||||
|
|
@ -20,7 +21,7 @@ load_dotenv()
|
||||||
|
|
||||||
class LLMLogger:
|
class LLMLogger:
|
||||||
|
|
||||||
def __init__(self, llm: ChatOpenAI):
|
def __init__(self, llm: Union[ChatOpenAI, ChatOllama]):
|
||||||
self.llm = llm
|
self.llm = llm
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -78,12 +79,12 @@ class LLMLogger:
|
||||||
|
|
||||||
class LoggerChatModel:
|
class LoggerChatModel:
|
||||||
|
|
||||||
def __init__(self, llm: ChatOpenAI):
|
def __init__(self, llm: Union[ChatOpenAI, ChatOllama]):
|
||||||
self.llm = llm
|
self.llm = llm
|
||||||
|
|
||||||
def __call__(self, messages: List[Dict[str, str]]) -> str:
|
def __call__(self, messages: List[Dict[str, str]]) -> str:
|
||||||
# Call the LLM with the provided messages and log the response.
|
# Call the LLM with the provided messages and log the response.
|
||||||
reply = self.llm(messages)
|
reply = self.llm.invoke(messages)
|
||||||
parsed_reply = self.parse_llmresult(reply)
|
parsed_reply = self.parse_llmresult(reply)
|
||||||
LLMLogger.log_request(prompts=messages, parsed_reply=parsed_reply)
|
LLMLogger.log_request(prompts=messages, parsed_reply=parsed_reply)
|
||||||
return reply
|
return reply
|
||||||
|
|
@ -113,10 +114,18 @@ class LoggerChatModel:
|
||||||
|
|
||||||
|
|
||||||
class GPTAnswerer:
|
class GPTAnswerer:
|
||||||
def __init__(self, openai_api_key):
|
def __init__(self, openai_api_key, openai_api_free_hosted_url):
|
||||||
self.llm_cheap = LoggerChatModel(
|
if openai_api_key == "":
|
||||||
ChatOpenAI(model_name="gpt-4o-mini", openai_api_key=openai_api_key, temperature=0.4)
|
print('Using locally hosted mistral:v0.3')
|
||||||
)
|
self.llm_model = ChatOllama(model = "mistral:v0.3", temperature = 0.4, num_predict = 256)
|
||||||
|
elif openai_api_key == "freehosted":
|
||||||
|
print('Using free hosted gpt-4o-mini')
|
||||||
|
self.llm_model = ChatOpenAI(model_name="gpt-4o-mini", openai_api_key="anything", temperature=0.4,
|
||||||
|
base_url=openai_api_free_hosted_url)
|
||||||
|
else:
|
||||||
|
print("Using gpt-4o-mini")
|
||||||
|
self.llm_model = ChatOpenAI(model_name="gpt-4o-mini", openai_api_key=openai_api_key, temperature=0.4)
|
||||||
|
self.llm_cheap = LoggerChatModel(self.llm_model)
|
||||||
@property
|
@property
|
||||||
def job_description(self):
|
def job_description(self):
|
||||||
return self.job.description
|
return self.job.description
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue