Spaces:
Runtime error
Runtime error
wip: langchain text to SQL agent
Browse files
notebooks/langchain_text2SQL.ipynb
ADDED
@@ -0,0 +1,330 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"cells": [
|
3 |
+
{
|
4 |
+
"cell_type": "code",
|
5 |
+
"execution_count": 1,
|
6 |
+
"metadata": {},
|
7 |
+
"outputs": [],
|
8 |
+
"source": [
|
9 |
+
"import getpass\n",
|
10 |
+
"import os\n",
|
11 |
+
"\n",
|
12 |
+
"if not os.environ.get(\"OPENAI_API_KEY\"):\n",
|
13 |
+
" os.environ[\"OPENAI_API_KEY\"] = getpass.getpass()\n",
|
14 |
+
"\n",
|
15 |
+
"# Comment out the below to opt-out of using LangSmith in this notebook. Not required.\n",
|
16 |
+
"# if not os.environ.get(\"LANGCHAIN_API_KEY\"):\n",
|
17 |
+
"# os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass()\n",
|
18 |
+
"# os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\""
|
19 |
+
]
|
20 |
+
},
|
21 |
+
{
|
22 |
+
"cell_type": "code",
|
23 |
+
"execution_count": 2,
|
24 |
+
"metadata": {},
|
25 |
+
"outputs": [
|
26 |
+
{
|
27 |
+
"name": "stdout",
|
28 |
+
"output_type": "stream",
|
29 |
+
"text": [
|
30 |
+
"sqlite\n",
|
31 |
+
"['Drivers', 'Event', 'Laps', 'Sessions', 'Telemetry', 'Tracks', 'Weather']\n"
|
32 |
+
]
|
33 |
+
},
|
34 |
+
{
|
35 |
+
"data": {
|
36 |
+
"text/plain": [
|
37 |
+
"\"[(1, 1271, 'VER', 80.0, 7087, 2, 26.0, 0, 8, -89.39, 4453.24, 0.0, 0, '2023-03-04 15:04:00.840000'), (2, 1271, 'VER', 82.0, 7112, 2, 24.0, 0, 8, -87.0, 4498.0, 0.0, 0, '2023-03-04 15:04:01.039000'), (3, 1271, 'VER', 85.0, 7401, 2, 25.0, 0, 8, -85.0, 4564.0, 0.0, 0, '2023-03-04 15:04:01.318000'), (4, 1271, 'VER', 90.0, 7908, 2, 31.0, 0, 8, -83.0, 4608.0, 0.0, 0, '2023-03-04 15:04:01.498000'), (5, 1271, 'VER', 92.0, 8132, 2, 35.0, 0, 8, -81.84, 4636.77, 0.0, 0, '2023-03-04 15:04:01.604000'), (6, 1271, 'VER', 95.0, 8378, 2, 36.0, 0, 8, -80.0, 4681.0, 0.0, 0, '2023-03-04 15:04:01.759000'), (7, 1271, 'VER', 99.0, 8625, 2, 37.0, 0, 8, -78.7, 4704.75, 0.0, 0, '2023-03-04 15:04:01.844000'), (8, 1271, 'VER', 101.0, 8925, 2, 40.0, 0, 8, -77.0, 4736.0, 0.0, 0, '2023-03-04 15:04:01.959000'), (9, 1271, 'VER', 103.0, 9225, 2, 43.0, 0, 8, -75.0, 4793.0, 0.0, 0, '2023-03-04 15:04:02.159000'), (10, 1271, 'VER', 105.0, 9526, 2, 46.0, 0, 8, -74.3, 4810.45, 0.0, 0, '2023-03-04 15:04:02.204000')]\""
|
38 |
+
]
|
39 |
+
},
|
40 |
+
"execution_count": 2,
|
41 |
+
"metadata": {},
|
42 |
+
"output_type": "execute_result"
|
43 |
+
}
|
44 |
+
],
|
45 |
+
"source": [
|
46 |
+
"from langchain_community.utilities import SQLDatabase\n",
|
47 |
+
"\n",
|
48 |
+
"db = SQLDatabase.from_uri(\"sqlite:///Bahrain_2023_Q.db\")\n",
|
49 |
+
"print(db.dialect)\n",
|
50 |
+
"print(db.get_usable_table_names())\n",
|
51 |
+
"db.run(\"SELECT * FROM Telemetry LIMIT 10;\")"
|
52 |
+
]
|
53 |
+
},
|
54 |
+
{
|
55 |
+
"cell_type": "code",
|
56 |
+
"execution_count": 3,
|
57 |
+
"metadata": {},
|
58 |
+
"outputs": [],
|
59 |
+
"source": [
|
60 |
+
"from langchain_openai import ChatOpenAI\n",
|
61 |
+
"\n",
|
62 |
+
"llm = ChatOpenAI(model=\"gpt-4o-mini\")"
|
63 |
+
]
|
64 |
+
},
|
65 |
+
{
|
66 |
+
"cell_type": "code",
|
67 |
+
"execution_count": 4,
|
68 |
+
"metadata": {},
|
69 |
+
"outputs": [
|
70 |
+
{
|
71 |
+
"data": {
|
72 |
+
"text/plain": [
|
73 |
+
"'SQLQuery: \\n```sql\\nSELECT \"driver_name\", MIN(\"lap_time_in_seconds\") AS \"fastest_lap_time\"\\nFROM \"Laps\"\\nGROUP BY \"driver_name\"\\nORDER BY \"fastest_lap_time\"\\nLIMIT 1;\\n```'"
|
74 |
+
]
|
75 |
+
},
|
76 |
+
"execution_count": 4,
|
77 |
+
"metadata": {},
|
78 |
+
"output_type": "execute_result"
|
79 |
+
}
|
80 |
+
],
|
81 |
+
"source": [
|
82 |
+
"from langchain.chains import create_sql_query_chain\n",
|
83 |
+
"\n",
|
84 |
+
"chain = create_sql_query_chain(llm, db, )\n",
|
85 |
+
"response = chain.invoke({\"question\": \"Who is the fastest driver?\"})\n",
|
86 |
+
"response"
|
87 |
+
]
|
88 |
+
},
|
89 |
+
{
|
90 |
+
"cell_type": "code",
|
91 |
+
"execution_count": 10,
|
92 |
+
"metadata": {},
|
93 |
+
"outputs": [],
|
94 |
+
"source": [
|
95 |
+
"from langchain_core.output_parsers import StrOutputParser\n",
|
96 |
+
"from langchain_core.prompts import ChatPromptTemplate\n",
|
97 |
+
"\n",
|
98 |
+
"system = \"\"\"Double check the user's {dialect} query for common mistakes, including:\n",
|
99 |
+
"- Only return SQL Query not anything else like ```sql ... ```\n",
|
100 |
+
"- Using NOT IN with NULL values\n",
|
101 |
+
"- Using UNION when UNION ALL should have been used\n",
|
102 |
+
"- Using BETWEEN for exclusive ranges\n",
|
103 |
+
"- Data type mismatch in predicates\\\n",
|
104 |
+
"- Using the correct number of arguments for functions\n",
|
105 |
+
"- Casting to the correct data type\n",
|
106 |
+
"- Using the proper columns for joins\n",
|
107 |
+
"\n",
|
108 |
+
"If there are any of the above mistakes, rewrite the query.\n",
|
109 |
+
"If there are no mistakes, just reproduce the original query with no further commentary.\n",
|
110 |
+
"\n",
|
111 |
+
"Output the final SQL query only.\"\"\"\n",
|
112 |
+
"prompt = ChatPromptTemplate.from_messages(\n",
|
113 |
+
" [(\"system\", system), (\"human\", \"{query}\")]\n",
|
114 |
+
").partial(dialect=db.dialect)\n",
|
115 |
+
"validation_chain = prompt | llm | StrOutputParser()\n",
|
116 |
+
"\n",
|
117 |
+
"full_chain = {\"query\": chain} | validation_chain"
|
118 |
+
]
|
119 |
+
},
|
120 |
+
{
|
121 |
+
"cell_type": "code",
|
122 |
+
"execution_count": 11,
|
123 |
+
"metadata": {},
|
124 |
+
"outputs": [
|
125 |
+
{
|
126 |
+
"name": "stdout",
|
127 |
+
"output_type": "stream",
|
128 |
+
"text": [
|
129 |
+
"SELECT \"driver_name\", MIN(\"lap_time_in_seconds\") AS \"fastest_lap_time\"\n",
|
130 |
+
"FROM \"Laps\"\n",
|
131 |
+
"GROUP BY \"driver_name\"\n",
|
132 |
+
"ORDER BY \"fastest_lap_time\" ASC\n",
|
133 |
+
"LIMIT 1;\n"
|
134 |
+
]
|
135 |
+
}
|
136 |
+
],
|
137 |
+
"source": [
|
138 |
+
"query = full_chain.invoke(\n",
|
139 |
+
" {\n",
|
140 |
+
" \"question\": \"WWho is the fastest driver?\"\n",
|
141 |
+
" }\n",
|
142 |
+
")\n",
|
143 |
+
"print(query)"
|
144 |
+
]
|
145 |
+
},
|
146 |
+
{
|
147 |
+
"cell_type": "code",
|
148 |
+
"execution_count": 12,
|
149 |
+
"metadata": {},
|
150 |
+
"outputs": [
|
151 |
+
{
|
152 |
+
"data": {
|
153 |
+
"text/plain": [
|
154 |
+
"\"[('VER', 89.708)]\""
|
155 |
+
]
|
156 |
+
},
|
157 |
+
"execution_count": 12,
|
158 |
+
"metadata": {},
|
159 |
+
"output_type": "execute_result"
|
160 |
+
}
|
161 |
+
],
|
162 |
+
"source": [
|
163 |
+
"db.run(query)"
|
164 |
+
]
|
165 |
+
},
|
166 |
+
{
|
167 |
+
"cell_type": "code",
|
168 |
+
"execution_count": 13,
|
169 |
+
"metadata": {},
|
170 |
+
"outputs": [
|
171 |
+
{
|
172 |
+
"name": "stdout",
|
173 |
+
"output_type": "stream",
|
174 |
+
"text": [
|
175 |
+
"You are a SQLite expert. Given an input question, first create a syntactically correct SQLite query to run, then look at the results of the query and return the answer to the input question.\n",
|
176 |
+
"Unless the user specifies in the question a specific number of examples to obtain, query for at most 5 results using the LIMIT clause as per SQLite. You can order the results to return the most informative data in the database.\n",
|
177 |
+
"Never query for all columns from a table. You must query only the columns that are needed to answer the question. Wrap each column name in double quotes (\") to denote them as delimited identifiers.\n",
|
178 |
+
"Pay attention to use only the column names you can see in the tables below. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table.\n",
|
179 |
+
"Pay attention to use date('now') function to get the current date, if the question involves \"today\".\n",
|
180 |
+
"\n",
|
181 |
+
"Use the following format:\n",
|
182 |
+
"\n",
|
183 |
+
"Question: Question here\n",
|
184 |
+
"SQLQuery: SQL Query to run\n",
|
185 |
+
"SQLResult: Result of the SQLQuery\n",
|
186 |
+
"Answer: Final answer here\n",
|
187 |
+
"\n",
|
188 |
+
"Only use the following tables:\n",
|
189 |
+
"\u001b[33;1m\u001b[1;3m{table_info}\u001b[0m\n",
|
190 |
+
"\n",
|
191 |
+
"Question: \u001b[33;1m\u001b[1;3m{input}\u001b[0m\n"
|
192 |
+
]
|
193 |
+
}
|
194 |
+
],
|
195 |
+
"source": [
|
196 |
+
"chain.get_prompts()[0].pretty_print()"
|
197 |
+
]
|
198 |
+
},
|
199 |
+
{
|
200 |
+
"cell_type": "code",
|
201 |
+
"execution_count": 14,
|
202 |
+
"metadata": {},
|
203 |
+
"outputs": [
|
204 |
+
{
|
205 |
+
"name": "stdout",
|
206 |
+
"output_type": "stream",
|
207 |
+
"text": [
|
208 |
+
"You are a SQLite expert. Given an input question, first create a syntactically correct SQLite query to run, then look at the results of the query and return the answer to the input question.\n",
|
209 |
+
"Unless the user specifies in the question a specific number of examples to obtain, query for at most 5 results using the LIMIT clause as per SQLite. You can order the results to return the most informative data in the database.\n",
|
210 |
+
"Never query for all columns from a table. You must query only the columns that are needed to answer the question. Wrap each column name in double quotes (\") to denote them as delimited identifiers.\n",
|
211 |
+
"Pay attention to use only the column names you can see in the tables below. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table.\n",
|
212 |
+
"Pay attention to use date('now') function to get the current date, if the question involves \"today\".\n",
|
213 |
+
"\n",
|
214 |
+
"Use the following format:\n",
|
215 |
+
"\n",
|
216 |
+
"Question: Question here\n",
|
217 |
+
"SQLQuery: SQL Query to run\n",
|
218 |
+
"SQLResult: Result of the SQLQuery\n",
|
219 |
+
"Answer: Final answer here\n",
|
220 |
+
"\n",
|
221 |
+
"Only use the following tables:\n",
|
222 |
+
"\u001b[33;1m\u001b[1;3m{table_info}\u001b[0m\n",
|
223 |
+
"\n",
|
224 |
+
"Question: \u001b[33;1m\u001b[1;3m{input}\u001b[0m\n"
|
225 |
+
]
|
226 |
+
}
|
227 |
+
],
|
228 |
+
"source": [
|
229 |
+
"full_chain.get_prompts()[0].pretty_print()"
|
230 |
+
]
|
231 |
+
},
|
232 |
+
{
|
233 |
+
"cell_type": "code",
|
234 |
+
"execution_count": 15,
|
235 |
+
"metadata": {},
|
236 |
+
"outputs": [
|
237 |
+
{
|
238 |
+
"data": {
|
239 |
+
"text/plain": [
|
240 |
+
"\"[('VER', 89.708)]\""
|
241 |
+
]
|
242 |
+
},
|
243 |
+
"execution_count": 15,
|
244 |
+
"metadata": {},
|
245 |
+
"output_type": "execute_result"
|
246 |
+
}
|
247 |
+
],
|
248 |
+
"source": [
|
249 |
+
"from langchain_community.tools.sql_database.tool import QuerySQLDataBaseTool\n",
|
250 |
+
"\n",
|
251 |
+
"execute_query = QuerySQLDataBaseTool(db=db)\n",
|
252 |
+
"write_query = create_sql_query_chain(llm, db)\n",
|
253 |
+
"chain = write_query | validation_chain | execute_query\n",
|
254 |
+
"chain.invoke({\"question\": \"Who is the fastest driver?\"})"
|
255 |
+
]
|
256 |
+
},
|
257 |
+
{
|
258 |
+
"cell_type": "markdown",
|
259 |
+
"metadata": {},
|
260 |
+
"source": [
|
261 |
+
"## Answer the question\n"
|
262 |
+
]
|
263 |
+
},
|
264 |
+
{
|
265 |
+
"cell_type": "code",
|
266 |
+
"execution_count": 17,
|
267 |
+
"metadata": {},
|
268 |
+
"outputs": [
|
269 |
+
{
|
270 |
+
"data": {
|
271 |
+
"text/plain": [
|
272 |
+
"'The fastest driver is VER, with a fastest lap time of 89.708 seconds.'"
|
273 |
+
]
|
274 |
+
},
|
275 |
+
"execution_count": 17,
|
276 |
+
"metadata": {},
|
277 |
+
"output_type": "execute_result"
|
278 |
+
}
|
279 |
+
],
|
280 |
+
"source": [
|
281 |
+
"from operator import itemgetter\n",
|
282 |
+
"\n",
|
283 |
+
"from langchain_core.output_parsers import StrOutputParser\n",
|
284 |
+
"from langchain_core.prompts import PromptTemplate\n",
|
285 |
+
"from langchain_core.runnables import RunnablePassthrough\n",
|
286 |
+
"\n",
|
287 |
+
"answer_prompt = PromptTemplate.from_template(\n",
|
288 |
+
" \"\"\"Given the following user question, corresponding SQL query, and SQL result, answer the user question.\n",
|
289 |
+
"\n",
|
290 |
+
"Question: {question}\n",
|
291 |
+
"SQL Query: {query}\n",
|
292 |
+
"SQL Result: {result}\n",
|
293 |
+
"Answer: \"\"\"\n",
|
294 |
+
")\n",
|
295 |
+
"\n",
|
296 |
+
"chain = (\n",
|
297 |
+
" RunnablePassthrough.assign(query=write_query).assign(\n",
|
298 |
+
" result=itemgetter(\"query\") | validation_chain | execute_query\n",
|
299 |
+
" )\n",
|
300 |
+
" | answer_prompt\n",
|
301 |
+
" | llm\n",
|
302 |
+
" | StrOutputParser()\n",
|
303 |
+
")\n",
|
304 |
+
"\n",
|
305 |
+
"chain.invoke({\"question\": \"Who is the fastest driver?\"})"
|
306 |
+
]
|
307 |
+
}
|
308 |
+
],
|
309 |
+
"metadata": {
|
310 |
+
"kernelspec": {
|
311 |
+
"display_name": "llama",
|
312 |
+
"language": "python",
|
313 |
+
"name": "python3"
|
314 |
+
},
|
315 |
+
"language_info": {
|
316 |
+
"codemirror_mode": {
|
317 |
+
"name": "ipython",
|
318 |
+
"version": 3
|
319 |
+
},
|
320 |
+
"file_extension": ".py",
|
321 |
+
"mimetype": "text/x-python",
|
322 |
+
"name": "python",
|
323 |
+
"nbconvert_exporter": "python",
|
324 |
+
"pygments_lexer": "ipython3",
|
325 |
+
"version": "3.11.9"
|
326 |
+
}
|
327 |
+
},
|
328 |
+
"nbformat": 4,
|
329 |
+
"nbformat_minor": 2
|
330 |
+
}
|