Skip to content

Commit 1657070

Browse files
authored
Merge pull request #215 from kracekumar/add-logs-in-llm-command
[LLM] Add logs while executing llm command
2 parents 62e8de6 + 002cde1 commit 1657070

4 files changed

Lines changed: 28 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
## TBD
33

44
### Features
5+
* Add logs while invoking `\llm`and `\\m+` command. [(#215)](https://github.com/dbcli/litecli/pull/215)
56
* Support `--help` in the `\llm`and `\llm+` command. ([#214](https://github.com/dbcli/litecli/pull/214))
67
* Make the history file location configurable. ([#206](https://github.com/dbcli/litecli/issues/206))
78

litecli/main.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,9 +448,12 @@ def one_iteration(text=None):
448448
try:
449449
start = time()
450450
cur = self.sqlexecute.conn and self.sqlexecute.conn.cursor()
451-
context, sql = special.handle_llm(text, cur)
451+
context, sql, duration = special.handle_llm(text, cur)
452452
if context:
453+
click.echo("LLM Reponse:")
453454
click.echo(context)
455+
click.echo('---')
456+
click.echo(f"Time: {duration:.2f} seconds")
454457
text = self.prompt_app.prompt(default=sql)
455458
except KeyboardInterrupt:
456459
return

litecli/packages/special/llm.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import sys
88
from runpy import run_module
99
from typing import Optional, Tuple
10+
from time import time
1011

1112
import click
1213

@@ -191,9 +192,8 @@ def ensure_litecli_template(replace=False):
191192
run_external_cmd("llm", PROMPT, "--save", "litecli")
192193
return
193194

194-
195195
@export
196-
def handle_llm(text, cur) -> Tuple[str, Optional[str]]:
196+
def handle_llm(text, cur) -> Tuple[str, Optional[str], float]:
197197
"""This function handles the special command `\\llm`.
198198
199199
If it deals with a question that results in a SQL query then it will return
@@ -254,25 +254,32 @@ def handle_llm(text, cur) -> Tuple[str, Optional[str]]:
254254
if not use_context:
255255
args = parts
256256
if capture_output:
257+
click.echo("Calling llm command")
258+
start = time()
257259
_, result = run_external_cmd("llm", *args, capture_output=capture_output)
260+
end = time()
258261
match = re.search(_SQL_CODE_FENCE, result, re.DOTALL)
259262
if match:
260263
sql = match.group(1).strip()
261264
else:
262265
output = [(None, None, None, result)]
263266
raise FinishIteration(output)
264267

265-
return result if verbose else "", sql
268+
return result if verbose else "", sql, end - start
266269
else:
267270
run_external_cmd("llm", *args, restart_cli=restart)
268271
raise FinishIteration(None)
269272

270273
try:
271274
ensure_litecli_template()
275+
# Measure end to end llm command invocation.
276+
# This measures the internal DB command to pull the schema and llm command
277+
start = time()
272278
context, sql = sql_using_llm(cur=cur, question=arg, verbose=verbose)
279+
end = time()
273280
if not verbose:
274281
context = ""
275-
return context, sql
282+
return context, sql, end - start
276283
except Exception as e:
277284
# Something went wrong. Raise an exception and bail.
278285
raise RuntimeError(e)
@@ -301,6 +308,7 @@ def sql_using_llm(cur, question=None, verbose=False) -> Tuple[str, Optional[str]
301308
WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%'
302309
ORDER BY 1
303310
"""
311+
click.echo("Preparing schema information to feed the llm")
304312
sample_row_query = "SELECT * FROM {table} LIMIT 1"
305313
log.debug(schema_query)
306314
cur.execute(schema_query)
@@ -332,7 +340,9 @@ def sql_using_llm(cur, question=None, verbose=False) -> Tuple[str, Optional[str]
332340
question,
333341
" ", # Dummy argument to prevent llm from waiting on stdin
334342
]
343+
click.echo("Invoking llm command with schema information")
335344
_, result = run_external_cmd("llm", *args, capture_output=True)
345+
click.echo("Received response from the llm command")
336346
match = re.search(_SQL_CODE_FENCE, result, re.DOTALL)
337347
if match:
338348
sql = match.group(1).strip()

tests/test_llm_special.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,15 @@ def test_llm_command_with_c_flag_and_fenced_sql(mock_run_cmd, mock_llm, executor
5959

6060
test_text = r"\llm -c 'Rewrite the SQL without CTE'"
6161

62-
result, sql = handle_llm(test_text, executor)
62+
result, sql, duration = handle_llm(test_text, executor)
6363

6464
# We expect the function to return (result, sql), but result might be "" if verbose is not set
6565
# By default, `verbose` is false unless text has something like \llm --verbose?
6666
# The function code: return result if verbose else "", sql
6767
# Our test_text doesn't set verbose => we expect "" for the returned context.
6868
assert result == ""
6969
assert sql == "SELECT * FROM table;"
70+
assert isinstance(duration, float)
7071

7172

7273
@patch("litecli.packages.special.llm.llm")
@@ -133,7 +134,7 @@ def test_llm_command_with_prompt(mock_sql_using_llm, mock_ensure_template, mock_
133134
mock_sql_using_llm.return_value = ("context from LLM", "SELECT 1;")
134135

135136
test_text = r"\llm prompt 'Magic happening here?'"
136-
context, sql = handle_llm(test_text, executor)
137+
context, sql, duration = handle_llm(test_text, executor)
137138

138139
# ensure_litecli_template should be called
139140
mock_ensure_template.assert_called_once()
@@ -143,6 +144,7 @@ def test_llm_command_with_prompt(mock_sql_using_llm, mock_ensure_template, mock_
143144
mock_sql_using_llm.assert_called()
144145
assert context == ""
145146
assert sql == "SELECT 1;"
147+
assert isinstance(duration, float)
146148

147149

148150
@patch("litecli.packages.special.llm.llm")
@@ -155,12 +157,13 @@ def test_llm_command_question_with_context(mock_sql_using_llm, mock_ensure_templ
155157
mock_sql_using_llm.return_value = ("You have context!", "SELECT 2;")
156158

157159
test_text = r"\llm 'Top 10 downloads by size.'"
158-
context, sql = handle_llm(test_text, executor)
160+
context, sql, duration = handle_llm(test_text, executor)
159161

160162
mock_ensure_template.assert_called_once()
161163
mock_sql_using_llm.assert_called()
162164
assert context == ""
163165
assert sql == "SELECT 2;"
166+
assert isinstance(duration, float)
164167

165168

166169
@patch("litecli.packages.special.llm.llm")
@@ -173,7 +176,9 @@ def test_llm_command_question_verbose(mock_sql_using_llm, mock_ensure_template,
173176
mock_sql_using_llm.return_value = ("Verbose context, oh yeah!", "SELECT 42;")
174177

175178
test_text = r"\llm+ 'Top 10 downloads by size.'"
176-
context, sql = handle_llm(test_text, executor)
179+
context, sql, duration = handle_llm(test_text, executor)
177180

178181
assert context == "Verbose context, oh yeah!"
179182
assert sql == "SELECT 42;"
183+
184+
assert isinstance(duration, float)

0 commit comments

Comments
 (0)