from helper import extract_html_content from IPython.display import display, HTML from llama_index.utils.workflow import draw_all_possible_flows from llama_index.core.tools import FunctionTool from llama_index.core.agent import FunctionCallingAgent from llama_index.core import Settings from llama_parse import LlamaParse from llama_index.llms.groq import Groq from llama_index.embeddings.huggingface import HuggingFaceEmbedding from llama_index.core import ( VectorStoreIndex, StorageContext, load_index_from_storage ) import nest_asyncio from llama_index.core.workflow import InputRequiredEvent, HumanResponseEvent from llama_index.core.workflow import ( StartEvent, StopEvent, Workflow, step, Event, Context ) from pathlib import Path from dotenv import load_dotenv import os, json import asyncio storage_dir = "./storage" application_file = "./data/fake_application_form.pdf" nest_asyncio.apply() load_dotenv() llama_cloud_api_key = os.getenv("LLAMA_CLOUD_API_KEY") GROQ_API_KEY = os.getenv("GROQ_API_KEY") LLAMA_CLOUD_BASE_URL = os.getenv("LLAMA_CLOUD_BASE_URL") global_llm = Groq(api_key=GROQ_API_KEY, model="llama3-70b-8192") global_embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5") Settings.embed_model = global_embed_model class ParseFormEvent(Event): application_form: str class QueryEvent(Event): query: str field: str class ResponseEvent(Event): response: str # new! class FeedbackEvent(Event): feedback: str class GenerateQuestionsEvent(Event): pass class RAGWorkflow(Workflow): storage_dir = "./storage" llm: Groq query_engine: VectorStoreIndex @step async def set_up(self, ctx: Context, ev: StartEvent) -> ParseFormEvent: self.llm = global_llm self.storage_dir = storage_dir if not ev.resume_file: raise ValueError("No resume file provided") if not ev.application_form: raise ValueError("No application form provided") # ingest the data and set up the query engine if os.path.exists(self.storage_dir): # you've already ingested the resume document storage_context = StorageContext.from_defaults(persist_dir=self.storage_dir) index = load_index_from_storage(storage_context) else: # parse and load the resume document documents = LlamaParse( result_type="markdown", content_guideline_instruction="This is a resume, gather related facts together and format it as " "bullet points with headers" ).load_data(ev.resume_file) # embed and index the documents index = VectorStoreIndex.from_documents( documents, embed_model=global_embed_model ) index.storage_context.persist(persist_dir=self.storage_dir) # create a query engine self.query_engine = index.as_query_engine(llm=self.llm, similarity_top_k=5) # you no longer need a query to be passed in, # you'll be generating the queries instead # let's pass the application form to a new step to parse it return ParseFormEvent(application_form=ev.application_form) # new - separated the form parsing from the question generation @step async def parse_form(self, ctx: Context, ev: ParseFormEvent) -> GenerateQuestionsEvent: parser = LlamaParse( result_type="markdown", content_guideline_instruction="This is a job application form. Create a list of all the fields " "that need to be filled in.", formatting_instruction="Return a bulleted list of the fields ONLY." ) # get the LLM to convert the parsed form into JSON result = parser.load_data(ev.application_form)[0] raw_json = self.llm.complete( f""" This is a parsed form. Convert it into a JSON object containing only the list of fields to be filled in, in the form {{ fields: [...] }}.
. Return JSON ONLY, no markdown. """) fields = json.loads(raw_json.text)["fields"] await ctx.set("fields_to_fill", fields) print("\n DEBUG: all fields written to Context >>>>>>>>>>>>>>>>>>>>>>>>>>\n") return GenerateQuestionsEvent() # new - this step can get triggered either by GenerateQuestionsEvent or a FeedbackEvent @step async def generate_questions(self, ctx: Context, ev: GenerateQuestionsEvent | FeedbackEvent) -> QueryEvent: # get the list of fields to fill in fields = await ctx.get("fields_to_fill") print("\n DEBUG:all fields Read from Context >>>>>>>>>>>>>>>>>>>>>>>>>>\n") # generate one query for each of the fields, and fire them off for field in fields: question = f"How would you answer this question about the candidate?