RAG: Ask a folder of menus with LangChain and Gradio
RAG demo using LangChain and LlamaIndex.
Objective
The menus are the source of truth. The chatbot should answer from those files, so it’s domain-specific retrieval.
Demo
One line from Mira’s menu:
Cilantro rice. Served Tuesday and Friday only. 3.50
Run python src/app.py and open the local URL Gradio prints. The example questions are already there.

Wednesday is no. Sources come back as mira-counter.md and priya-spice.md. Hummus is not on any menu, so the bot says it does not know, since we have these instructions in the system prompt.
The code
We load the menu files, split them, and store each chunk as a vector. Similarity search returns nearby text. We paste that into {context} and tell the model to answer from the menus only.
store = InMemoryVectorStore.from_documents(
chunks, OpenAIEmbeddings(model="text-embedding-3-small")
)
hits = store.similarity_search(query, k=3)
context = "\n\n".join(doc.page_content for doc in hits)
embed_store.py · retrieve_stuff.py
With Gradio, each message runs that same retrieve-and-stuff loop. Sources are the filenames of the hits.
def chat(message, history):
hits = store.similarity_search(message, k=3)
context = "\n\n".join(doc.page_content for doc in hits)
answer = (prompt | llm).invoke(
{"context": context, "history": history_text, "question": message}
).content
names = ", ".join(
sorted({Path(doc.metadata["source"]).name for doc in hits})
)
return f"{answer}\n\nSources: {names}"
Same menus, same question. Only the library changes (LangChain then LlamaIndex). You should see Wednesday is still no. Wording can differ but the answer should not. The script also fetches a fourth menu from a gist.
Outcome
The retrieve-and-stuff steps are in place, served with a Gradio chat.
This article is a practical implementation of the concepts in Build RAG Applications: Get Started.
Repository: asaleh-lab/rag-menus-chat