Skip to content

Commit a0e5d33

Browse files
authored
chore: sql() reactive returns NULL or None if not set (#149)
* chore: `sql()` reactive returns `NULL` or `None` if not set * chore: Add changelog items * chore: clean up imports * chore(pkg-py): Merge "breaking changes" and "changes"
1 parent 24d1c60 commit a0e5d33

File tree

12 files changed

+30
-25
lines changed

12 files changed

+30
-25
lines changed

pkg-py/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [UNRELEASED]
99

10-
### Changes
10+
### Breaking Changes
1111

1212
* The entire functional API (i.e., `init()`, `sidebar()`, `server()`, etc) has been hard deprecated in favor of a simpler OOP-based API. Namely, the new `QueryChat()` class is now the main entry point (instead of `init()`) and has methods to replace old functions (e.g., `.sidebar()`, `.server()`, etc). (#101)
1313

14+
* The `.sql()` method now returns `None` instead of `""` (empty string) when no query has been set, aligning with the behavior of `.title()` for consistency. Most code using the `or` operator or `req()` for falsy checks will continue working without changes. Code that explicitly checks `sql() == ""` should be updated to use falsy checks (`if not sql()`) or explicit null checks (`if sql() is None`). (#146)
15+
1416
### New features
1517

1618
* New `QueryChat.app()` method enables quicker/easier chatting with a dataset. (#104)

pkg-py/src/querychat/_querychat.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def ui_reset():
142142
@reactive.effect
143143
@reactive.event(input.reset_query)
144144
def _():
145-
vals.sql.set("")
145+
vals.sql.set(None)
146146
vals.title.set(None)
147147

148148
@render.data_frame
@@ -586,12 +586,12 @@ def df(self) -> pd.DataFrame:
586586
return self._vals.df()
587587

588588
@overload
589-
def sql(self, query: None = None) -> str: ...
589+
def sql(self, query: None = None) -> str | None: ...
590590

591591
@overload
592592
def sql(self, query: str) -> bool: ...
593593

594-
def sql(self, query: Optional[str] = None) -> str | bool:
594+
def sql(self, query: Optional[str] = None) -> str | None | bool:
595595
"""
596596
Reactively read (or set) the current SQL query that is in effect.
597597
@@ -604,7 +604,7 @@ def sql(self, query: Optional[str] = None) -> str | bool:
604604
-------
605605
:
606606
If no `query` is provided, returns the current SQL query as a string
607-
(possibly `""` if no query has been set). If a `query` is provided,
607+
(or `None` if no query has been set). If a `query` is provided,
608608
returns `True` if the query was changed to a new value, or `False`
609609
if it was the same as the current value.
610610

pkg-py/src/querychat/_querychat_module.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class ServerValues:
6464
sql
6565
A reactive Value containing the current SQL query string. Access the value
6666
by calling `.sql()`, or set it with `.sql.set("SELECT ...")`.
67-
An empty string `""` indicates no query has been set.
67+
Returns `None` if no query has been set.
6868
title
6969
A reactive Value containing the current title for the query. The LLM
7070
provides this title when generating a new SQL query. Access it with
@@ -78,7 +78,7 @@ class ServerValues:
7878
"""
7979

8080
df: Callable[[], pd.DataFrame]
81-
sql: ReactiveString
81+
sql: ReactiveStringOrNone
8282
title: ReactiveStringOrNone
8383
client: chatlas.Chat
8484

@@ -95,7 +95,7 @@ def mod_server(
9595
enable_bookmarking: bool,
9696
):
9797
# Reactive values to store state
98-
sql = ReactiveString("")
98+
sql = ReactiveStringOrNone(None)
9999
title = ReactiveStringOrNone(None)
100100
has_greeted = reactive.value[bool](False) # noqa: FBT003
101101

@@ -115,10 +115,11 @@ def mod_server(
115115
# Execute query when SQL changes
116116
@reactive.calc
117117
def filtered_df():
118-
if sql.get() == "":
118+
query = sql.get()
119+
if not query:
119120
return data_source.get_data()
120121
else:
121-
return data_source.execute_query(sql.get())
122+
return data_source.execute_query(query)
122123

123124
# Chat UI logic
124125
chat_ui = shinychat.Chat(CHAT_ID)

pkg-py/src/querychat/tools.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from collections.abc import Callable
1515

1616
from ._datasource import DataSource
17-
from ._querychat_module import ReactiveString, ReactiveStringOrNone
17+
from ._querychat_module import ReactiveStringOrNone
1818

1919

2020
def _read_prompt_template(filename: str, **kwargs) -> str:
@@ -26,7 +26,7 @@ def _read_prompt_template(filename: str, **kwargs) -> str:
2626

2727
def _update_dashboard_impl(
2828
data_source: DataSource,
29-
current_query: ReactiveString,
29+
current_query: ReactiveStringOrNone,
3030
current_title: ReactiveStringOrNone,
3131
) -> Callable[[str, str], ContentToolResult]:
3232
"""Create the implementation function for updating the dashboard."""
@@ -78,7 +78,7 @@ def update_dashboard(query: str, title: str) -> ContentToolResult:
7878

7979
def tool_update_dashboard(
8080
data_source: DataSource,
81-
current_query: ReactiveString,
81+
current_query: ReactiveStringOrNone,
8282
current_title: ReactiveStringOrNone,
8383
) -> Tool:
8484
"""
@@ -115,14 +115,14 @@ def tool_update_dashboard(
115115

116116

117117
def _reset_dashboard_impl(
118-
current_query: ReactiveString,
118+
current_query: ReactiveStringOrNone,
119119
current_title: ReactiveStringOrNone,
120120
) -> Callable[[], ContentToolResult]:
121121
"""Create the implementation function for resetting the dashboard."""
122122

123123
def reset_dashboard() -> ContentToolResult:
124124
# Reset current query and title
125-
current_query.set("")
125+
current_query.set(None)
126126
current_title.set(None)
127127

128128
# Add Reset Filter button
@@ -151,7 +151,7 @@ def reset_dashboard() -> ContentToolResult:
151151

152152

153153
def tool_reset_dashboard(
154-
current_query: ReactiveString,
154+
current_query: ReactiveStringOrNone,
155155
current_title: ReactiveStringOrNone,
156156
) -> Tool:
157157
"""

pkg-r/NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# querychat (development version)
22

3+
* **Breaking change:** The `$sql()` method now returns `NULL` instead of `""` (empty string) when no query has been set, aligning with the behavior of `$title()` for consistency. Most code using `isTruthy()` or similar falsy checks will continue working without changes. Code that explicitly checks `sql() == ""` should be updated to use falsy checks (e.g., `!isTruthy(sql())`) or explicit null checks (`is.null(sql())`). (#146)
4+
35
* Tool detail cards can now be expanded or collapsed by default when querychat runs a query or updates the dashboard via the `querychat.tool_details` R option or the `QUERYCHAT_TOOL_DETAILS` environment variable. Valid values are `"expanded"`, `"collapsed"`, or `"default"`. (#137)
46

57
* Added bookmarking support to `QueryChat$server()` and `querychat_app()`. When bookmarking is enabled (via `bookmark_store = "url"` or `"server"` in `querychat_app()` or `$app_obj()`, or via `enable_bookmarking = TRUE` in `$server()`), the chat state (including current query, title, and chat history) will be saved and restored with Shiny bookmarks. (#107)

pkg-r/R/DataSource.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ DataFrameSource <- R6::R6Class(
170170
#' @return A data frame with query results
171171
execute_query = function(query) {
172172
check_string(query, allow_null = TRUE, allow_empty = TRUE)
173-
if (is.null(query) || query == "") {
173+
if (is.null(query) || !nzchar(query)) {
174174
query <- paste0(
175175
"SELECT * FROM ",
176176
DBI::dbQuoteIdentifier(private$conn, self$table_name)
@@ -334,7 +334,7 @@ DBISource <- R6::R6Class(
334334
#' @return A data frame with query results
335335
execute_query = function(query) {
336336
check_string(query, allow_null = TRUE, allow_empty = TRUE)
337-
if (is.null(query) || query == "") {
337+
if (is.null(query) || !nzchar(query)) {
338338
query <- paste0(
339339
"SELECT * FROM ",
340340
DBI::dbQuoteIdentifier(private$conn, self$table_name)

pkg-r/R/QueryChat.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ QueryChat <- R6::R6Class(
333333
})
334334

335335
shiny::observeEvent(input$reset_query, label = "on_reset_query", {
336-
qc_vals$sql("")
336+
qc_vals$sql(NULL)
337337
qc_vals$title(NULL)
338338
})
339339

pkg-r/R/querychat_module.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ mod_server <- function(
2929
) {
3030
shiny::moduleServer(id, function(input, output, session) {
3131
current_title <- shiny::reactiveVal(NULL, label = "current_title")
32-
current_query <- shiny::reactiveVal("", label = "current_query")
32+
current_query <- shiny::reactiveVal(NULL, label = "current_query")
3333
has_greeted <- shiny::reactiveVal(FALSE, label = "has_greeted")
3434
filtered_df <- shiny::reactive(label = "filtered_df", {
3535
data_source$execute_query(query = DBI::SQL(current_query()))
@@ -47,7 +47,7 @@ mod_server <- function(
4747
}
4848

4949
reset_query <- function() {
50-
current_query("")
50+
current_query(NULL)
5151
current_title(NULL)
5252
querychat_tool_result(action = "reset")
5353
}

pkg-r/R/querychat_tools.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ querychat_tool_result <- function(
159159
action <- arg_match(action, c("update", "query", "reset"))
160160

161161
if (action == "reset") {
162-
query <- ""
162+
query <- NULL
163163
title <- NULL
164164
}
165165

pkg-r/inst/examples-shiny/02-sidebar-app/app.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ server <- function(input, output, session) {
8383
# Render the SQL query
8484
output$sql_query <- renderText({
8585
query <- qc_vals$sql()
86-
if (query == "") {
86+
if (is.null(query) || !nzchar(query)) {
8787
"No filter applied - showing all data"
8888
} else {
8989
query

0 commit comments

Comments
 (0)