mbudisic commited on
Commit
70159ab
·
1 Parent(s): 9063e00

The full chain is now a single LCEL chain

Browse files
Files changed (2) hide show
  1. app.py +7 -3
  2. notebooks/transcript_rag.ipynb +178 -51
app.py CHANGED
@@ -18,6 +18,7 @@ import pstuts_rag.datastore
18
  class ApplicationParameters:
19
  filename = "data/test.json"
20
  embedding_model = "text-embedding-3-small"
 
21
 
22
 
23
  class ApplicationState:
@@ -25,7 +26,6 @@ class ApplicationState:
25
  docs: List[Document] = []
26
  qdrantclient: QdrantClient = None
27
  vectorstore: QdrantVectorStore = None
28
- n_context_docs = 2
29
  retriever = None
30
 
31
 
@@ -55,15 +55,19 @@ async def on_chat_start():
55
 
56
  _ = state.vectorstore.add_documents(documents=state.docs)
57
  state.retriever = state.vectorstore.as_retriever(
58
- search_kwargs={"k": state.n_context_docs}
59
  )
60
 
 
 
61
 
62
  @cl.on_message
63
  async def main(message: cl.Message):
64
  # Send a response back to the user
65
 
66
- await cl.Message(content=f"Hello! You said: {message.content}").send()
 
 
67
 
68
 
69
  if __name__ == "__main__":
 
18
  class ApplicationParameters:
19
  filename = "data/test.json"
20
  embedding_model = "text-embedding-3-small"
21
+ n_context_docs = 2
22
 
23
 
24
  class ApplicationState:
 
26
  docs: List[Document] = []
27
  qdrantclient: QdrantClient = None
28
  vectorstore: QdrantVectorStore = None
 
29
  retriever = None
30
 
31
 
 
55
 
56
  _ = state.vectorstore.add_documents(documents=state.docs)
57
  state.retriever = state.vectorstore.as_retriever(
58
+ search_kwargs={"k": params.n_context_docs}
59
  )
60
 
61
+ await cl.Message(content=f"Populated vector database.").send()
62
+
63
 
64
  @cl.on_message
65
  async def main(message: cl.Message):
66
  # Send a response back to the user
67
 
68
+ v = await state.retriever.ainvoke(message.content)
69
+
70
+ await cl.Message(content=f"Hello! {len(v)}").send()
71
 
72
 
73
  if __name__ == "__main__":
notebooks/transcript_rag.ipynb CHANGED
@@ -32,6 +32,22 @@
32
  "%autoreload 2\n"
33
  ]
34
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  {
36
  "cell_type": "code",
37
  "execution_count": 4,
@@ -62,7 +78,7 @@
62
  },
63
  {
64
  "cell_type": "code",
65
- "execution_count": 6,
66
  "metadata": {},
67
  "outputs": [],
68
  "source": [
@@ -74,7 +90,7 @@
74
  },
75
  {
76
  "cell_type": "code",
77
- "execution_count": 8,
78
  "metadata": {},
79
  "outputs": [],
80
  "source": [
@@ -102,7 +118,7 @@
102
  },
103
  {
104
  "cell_type": "code",
105
- "execution_count": 45,
106
  "metadata": {},
107
  "outputs": [],
108
  "source": [
@@ -110,19 +126,17 @@
110
  "from qdrant_client import QdrantClient\n",
111
  "from qdrant_client.http.models import Distance, VectorParams\n",
112
  "\n",
113
- "client = QdrantClient(\":memory:\")\n",
114
  "\n",
115
- "collection_name = f\"{filename}_qdrant\"\n",
116
- "\n",
117
- "client.create_collection(\n",
118
- " collection_name=collection_name,\n",
119
- " vectors_config=VectorParams(size=1536, distance=Distance.COSINE),\n",
120
  ")\n",
121
  "\n",
122
- "vector_store = QdrantVectorStore(\n",
123
- " client=client,\n",
124
- " collection_name=collection_name,\n",
125
- " embedding=embeddings,\n",
126
  ")"
127
  ]
128
  },
@@ -131,18 +145,14 @@
131
  "execution_count": 46,
132
  "metadata": {},
133
  "outputs": [],
134
- "source": [
135
- "_ = vector_store.add_documents(documents=docs_chunks_semantic)"
136
- ]
137
  },
138
  {
139
  "cell_type": "code",
140
- "execution_count": 47,
141
  "metadata": {},
142
  "outputs": [],
143
  "source": [
144
- "retriever = vector_store.as_retriever(search_kwargs={\"k\":2})\n",
145
- "\n",
146
  "def retrieve(state):\n",
147
  " retrieved_docs = retriever.invoke(state[\"question\"])\n",
148
  " return {\"context\":retrieved_docs}\n"
@@ -150,9 +160,66 @@
150
  },
151
  {
152
  "cell_type": "code",
153
- "execution_count": null,
154
  "metadata": {},
155
  "outputs": [],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  "source": [
157
  "a = retrieve({\"question\":\"What is a layer?\"})\n",
158
  "[ pp(d.page_content) for d in a[\"context\"] ]"
@@ -169,13 +236,14 @@
169
  },
170
  {
171
  "cell_type": "code",
172
- "execution_count": 49,
173
  "metadata": {},
174
  "outputs": [],
175
  "source": [
176
  "from langchain.prompts import ChatPromptTemplate\n",
177
  "\n",
178
- "SYSTEM_PROMPT = \"\"\"\\\n",
 
179
  "You are a helpful an expert on Photoshop and your goal is to help users\n",
180
  "gain knowledge from a database of training videos. \n",
181
  "You answer questions based on provided context. \n",
@@ -191,12 +259,9 @@
191
  "\"What is ...\"\n",
192
  "\n",
193
  "When appropriate, provide your answers in a step-by-step form.\n",
194
- "ALWAYS list the URL and the title of the reference video.\n",
195
  "NEVER invent the explanation. ALWAYS use ONLY the context information.\n",
196
- "\n",
197
- "\"\"\"\n",
198
- "\n",
199
- "RAG_PROMPT=\"\"\"\\\n",
200
  "\n",
201
  "### Question\n",
202
  "{question}\n",
@@ -206,14 +271,22 @@
206
  "### Context\n",
207
  "{context}\n",
208
  "\n",
209
- "\n",
210
- "\"\"\"\n",
211
- "\n",
212
- "rag_prompt = ChatPromptTemplate(\n",
213
- " [(\"system\",SYSTEM_PROMPT), \n",
214
- " (\"human\",RAG_PROMPT)\n",
215
- " ]\n",
216
- " )"
 
 
 
 
 
 
 
 
217
  ]
218
  },
219
  {
@@ -227,7 +300,7 @@
227
  },
228
  {
229
  "cell_type": "code",
230
- "execution_count": 50,
231
  "metadata": {},
232
  "outputs": [],
233
  "source": [
@@ -238,26 +311,80 @@
238
  },
239
  {
240
  "cell_type": "code",
241
- "execution_count": 51,
242
  "metadata": {},
243
- "outputs": [],
 
 
 
 
 
 
 
 
244
  "source": [
245
- "def generate(state):\n",
246
- " docs_content = \"\\n\\n\".join(doc.page_content for doc in state[\"context\"])\n",
 
 
 
 
 
 
 
 
247
  "\n",
248
- " references = [ \n",
249
- " {k: doc.metadata[k] for k in (\"title\",\"source\",\"start\",\"stop\")} \n",
250
- " for doc in state[\"context\"] \n",
251
- " ] \n",
 
 
 
 
252
  "\n",
253
  "\n",
254
- " messages = rag_prompt.format_messages(question=state[\"question\"], \n",
255
- " context=docs_content)\n",
256
- " response = llm.invoke(messages)\n",
257
- " retval = {\"response\":f\"{response.content}\\n\\n**References**:\\n{json.dumps(references,indent=2)}\",\n",
258
- " \"context\":state[\"context\"]}\n",
259
- " \n",
260
- " return retval\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  ]
262
  },
263
  {
 
32
  "%autoreload 2\n"
33
  ]
34
  },
35
+ {
36
+ "cell_type": "code",
37
+ "execution_count": 12,
38
+ "metadata": {},
39
+ "outputs": [],
40
+ "source": [
41
+ "from dataclasses import dataclass\n",
42
+ "@dataclass\n",
43
+ "class ApplicationParameters:\n",
44
+ " filename = \"data/test.json\"\n",
45
+ " embedding_model = \"text-embedding-3-small\"\n",
46
+ " n_context_docs = 2\n",
47
+ "\n",
48
+ "params = ApplicationParameters()"
49
+ ]
50
+ },
51
  {
52
  "cell_type": "code",
53
  "execution_count": 4,
 
78
  },
79
  {
80
  "cell_type": "code",
81
+ "execution_count": 5,
82
  "metadata": {},
83
  "outputs": [],
84
  "source": [
 
90
  },
91
  {
92
  "cell_type": "code",
93
+ "execution_count": 6,
94
  "metadata": {},
95
  "outputs": [],
96
  "source": [
 
118
  },
119
  {
120
  "cell_type": "code",
121
+ "execution_count": 15,
122
  "metadata": {},
123
  "outputs": [],
124
  "source": [
 
126
  "from qdrant_client import QdrantClient\n",
127
  "from qdrant_client.http.models import Distance, VectorParams\n",
128
  "\n",
129
+ "qdrantclient = QdrantClient(\":memory:\")\n",
130
  "\n",
131
+ "vectorstore = pstuts_rag.datastore.initialize_vectorstore(\n",
132
+ " client=qdrantclient,\n",
133
+ " collection_name=f\"{params.filename}_qdrant\",\n",
134
+ " embeddings=embeddings,\n",
 
135
  ")\n",
136
  "\n",
137
+ "_ = vectorstore.add_documents(documents=docs_chunks_semantic)\n",
138
+ "retriever =vectorstore.as_retriever(\n",
139
+ " search_kwargs={\"k\": params.n_context_docs}\n",
 
140
  ")"
141
  ]
142
  },
 
145
  "execution_count": 46,
146
  "metadata": {},
147
  "outputs": [],
148
+ "source": []
 
 
149
  },
150
  {
151
  "cell_type": "code",
152
+ "execution_count": 17,
153
  "metadata": {},
154
  "outputs": [],
155
  "source": [
 
 
156
  "def retrieve(state):\n",
157
  " retrieved_docs = retriever.invoke(state[\"question\"])\n",
158
  " return {\"context\":retrieved_docs}\n"
 
160
  },
161
  {
162
  "cell_type": "code",
163
+ "execution_count": 21,
164
  "metadata": {},
165
  "outputs": [],
166
+ "source": [
167
+ "from pprint import pp"
168
+ ]
169
+ },
170
+ {
171
+ "cell_type": "code",
172
+ "execution_count": 22,
173
+ "metadata": {},
174
+ "outputs": [
175
+ {
176
+ "name": "stdout",
177
+ "output_type": "stream",
178
+ "text": [
179
+ "(\"Layers are the building blocks of any image in Photoshop CC. So, it's \"\n",
180
+ " \"important to understand, what layers are and why to use them - which we'll \"\n",
181
+ " \"cover in this video. If you're following along, open this layered image from \"\n",
182
+ " 'the downloadable practice files for this tutorial. You might think of layers '\n",
183
+ " 'like separate flat pints of glass, stacked one on top of the other. Each '\n",
184
+ " 'layer contains separate pieces of content. To get a sense of how layers are '\n",
185
+ " \"constructed, let's take a look at this Layers panel. I've closed my other \"\n",
186
+ " 'panels, so that we can focus on the Layers panel. But you can skip that. By '\n",
187
+ " \"the way: If your Layers panel isn't showing, go up to the Window menu and \"\n",
188
+ " 'choose Layers from there. The Layers panel is where you go to select and '\n",
189
+ " 'work with layers. In this image there are 4 layers, each with separate '\n",
190
+ " 'content. If you click the Eye icon to the left of a layer, you can toggle '\n",
191
+ " \"the visibility of that layer off and on. So, I'm going to turn off the \"\n",
192
+ " 'visibility of the tailor layer. And keep your eye on the image, so you can '\n",
193
+ " \"see what's on that layer.\")\n",
194
+ "(\"Now let's take a look at just one layer, the tailor layer. A quick way to \"\n",
195
+ " 'turn off all the layers except the tailor layer, is to hold down the Option '\n",
196
+ " 'key on the Mac, or the ALT key on the PC, and click on the Eye icon to the '\n",
197
+ " 'left of the tailor layer. In the Document window, you can see that this '\n",
198
+ " 'layer contains just the one small photo surrounded by a gray and white '\n",
199
+ " 'checkerboard pattern. That pattern represents transparent pixels, which '\n",
200
+ " 'allow us to see down through the corresponding part of this layer to the '\n",
201
+ " \"content of the layers below. So, let's turn that content back on by going \"\n",
202
+ " 'back to the Layers panel, again holding the Option key on the Mac or the ALT '\n",
203
+ " 'key on the PC and clicking on the Eye icon to the left of the tailor layer. '\n",
204
+ " 'And all the other layers and their Eye icons come back into view. So again: '\n",
205
+ " 'You might think of layers like a stack of pints of glass, each with its own '\n",
206
+ " 'artwork and in some cases transparent areas that let you see down through to '\n",
207
+ " 'the layers below. The biggest benefit of having items on separate layers '\n",
208
+ " \"like this, is that you'll be able to edit pieces of an image independently \"\n",
209
+ " 'without affecting the rest of the image.')\n"
210
+ ]
211
+ },
212
+ {
213
+ "data": {
214
+ "text/plain": [
215
+ "[None, None]"
216
+ ]
217
+ },
218
+ "execution_count": 22,
219
+ "metadata": {},
220
+ "output_type": "execute_result"
221
+ }
222
+ ],
223
  "source": [
224
  "a = retrieve({\"question\":\"What is a layer?\"})\n",
225
  "[ pp(d.page_content) for d in a[\"context\"] ]"
 
236
  },
237
  {
238
  "cell_type": "code",
239
+ "execution_count": 85,
240
  "metadata": {},
241
  "outputs": [],
242
  "source": [
243
  "from langchain.prompts import ChatPromptTemplate\n",
244
  "\n",
245
+ "prompt_template = ChatPromptTemplate.from_messages([\n",
246
+ " (\"system\", \"\"\"\\\n",
247
  "You are a helpful an expert on Photoshop and your goal is to help users\n",
248
  "gain knowledge from a database of training videos. \n",
249
  "You answer questions based on provided context. \n",
 
259
  "\"What is ...\"\n",
260
  "\n",
261
  "When appropriate, provide your answers in a step-by-step form.\n",
 
262
  "NEVER invent the explanation. ALWAYS use ONLY the context information.\n",
263
+ "\"\"\"),\n",
264
+ " (\"user\",\"\"\"\\\n",
 
 
265
  "\n",
266
  "### Question\n",
267
  "{question}\n",
 
271
  "### Context\n",
272
  "{context}\n",
273
  "\n",
274
+ "\"\"\")])\n"
275
+ ]
276
+ },
277
+ {
278
+ "cell_type": "code",
279
+ "execution_count": 86,
280
+ "metadata": {},
281
+ "outputs": [],
282
+ "source": [
283
+ "def compile_references(context):\n",
284
+ " references = [ \n",
285
+ " {k: doc.metadata[k] for k in (\"title\",\"source\",\"start\",\"stop\")} \n",
286
+ " for doc in context\n",
287
+ " ] \n",
288
+ " print(type(references))\n",
289
+ " return json.dumps(references,indent=2)\n"
290
  ]
291
  },
292
  {
 
300
  },
301
  {
302
  "cell_type": "code",
303
+ "execution_count": 87,
304
  "metadata": {},
305
  "outputs": [],
306
  "source": [
 
311
  },
312
  {
313
  "cell_type": "code",
314
+ "execution_count": 96,
315
  "metadata": {},
316
+ "outputs": [
317
+ {
318
+ "name": "stdout",
319
+ "output_type": "stream",
320
+ "text": [
321
+ "<class 'list'>\n"
322
+ ]
323
+ }
324
+ ],
325
  "source": [
326
+ "from operator import itemgetter\n",
327
+ "from langchain.schema.output_parser import StrOutputParser\n",
328
+ "from langchain_core.runnables import RunnableLambda\n",
329
+ "\n",
330
+ "form_context = {\n",
331
+ " \"context\": itemgetter(\"question\") | retriever, \n",
332
+ " \"question\": itemgetter(\"question\") \n",
333
+ " } | RunnablePassthrough()\n",
334
+ "\n",
335
+ "answer_chain = prompt_template | llm | StrOutputParser()\n",
336
  "\n",
337
+ "get_videos = form_context | \\\n",
338
+ " {\"input\":RunnablePassthrough(),\"answer\": answer_chain} |\\\n",
339
+ " RunnableLambda( lambda d: \n",
340
+ " {**d[\"input\"], \"answer\": d[\"answer\"] + \n",
341
+ " \"\\nReferences:\\n\" +\n",
342
+ " compile_references(d[\"input\"][\"context\"]) \n",
343
+ " } )\n",
344
+ " \n",
345
  "\n",
346
  "\n",
347
+ "val = get_videos.invoke({\"question\":\"What are layers\"})"
348
+ ]
349
+ },
350
+ {
351
+ "cell_type": "code",
352
+ "execution_count": 94,
353
+ "metadata": {},
354
+ "outputs": [
355
+ {
356
+ "data": {
357
+ "text/plain": [
358
+ "dict_keys(['context', 'question', 'answer'])"
359
+ ]
360
+ },
361
+ "execution_count": 94,
362
+ "metadata": {},
363
+ "output_type": "execute_result"
364
+ }
365
+ ],
366
+ "source": [
367
+ "val.keys()"
368
+ ]
369
+ },
370
+ {
371
+ "cell_type": "code",
372
+ "execution_count": 42,
373
+ "metadata": {},
374
+ "outputs": [
375
+ {
376
+ "data": {
377
+ "text/plain": [
378
+ "AIMessage(content='Layers are the building blocks of any image in Photoshop CC. 🖼️ They can be thought of as separate flat pints of glass, stacked one on top of the other. Each layer contains separate pieces of content, and some layers may have transparent areas that let you see through to the layers below. The Layers panel is where you select and work with layers, and you can toggle their visibility by clicking the Eye icon. The main benefit of layers is that they allow you to edit parts of an image independently without affecting the rest. \\n\\n📺 Watch the full explanation in the video titled \"Understand layers\" here: [https://images-tv.adobe.com/avp/vr/b758b4c4-2a74-41f4-8e67-e2f2eab83c6a/f810fc5b-2b04-4e23-8fa4-5c532e7de6f8/e268fe4d-e5c7-415c-9f5c-d34d024b14d8_20170727011753.1280x720at2400_h264.mp4](https://images-tv.adobe.com/avp/vr/b758b4c4-2a74-41f4-8e67-e2f2eab83c6a/f810fc5b-2b04-4e23-8fa4-5c532e7de6f8/e268fe4d-e5c7-415c-9f5c-d34d024b14d8_20170727011753.1280x720at2400_h264.mp4)', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 349, 'prompt_tokens': 1630, 'total_tokens': 1979, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 1536}}, 'model_name': 'gpt-4.1-nano-2025-04-14', 'system_fingerprint': 'fp_8fd43718b3', 'finish_reason': 'stop', 'logprobs': None}, id='run--3f91ffdc-d962-40ca-9e06-71040894c9a9-0', usage_metadata={'input_tokens': 1630, 'output_tokens': 349, 'total_tokens': 1979, 'input_token_details': {'audio': 0, 'cache_read': 1536}, 'output_token_details': {'audio': 0, 'reasoning': 0}})"
379
+ ]
380
+ },
381
+ "execution_count": 42,
382
+ "metadata": {},
383
+ "output_type": "execute_result"
384
+ }
385
+ ],
386
+ "source": [
387
+ "value"
388
  ]
389
  },
390
  {