|
10 | 10 | import chatlas |
11 | 11 | import chevron |
12 | 12 | import sqlalchemy |
13 | | -from shiny import ui |
| 13 | +from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui |
14 | 14 | from shiny.session import get_current_session |
| 15 | +from shinychat import output_markdown_stream |
15 | 16 |
|
| 17 | +from ._icons import bs_icon |
| 18 | +from ._querychat_impl import ( |
| 19 | + normalize_data_source, |
| 20 | +) |
16 | 21 | from ._querychat_module import ModServerResult, mod_server, mod_ui |
17 | 22 | from .datasource import DataFrameSource, DataSource, SQLAlchemySource |
18 | 23 |
|
@@ -134,6 +139,99 @@ def __init__( |
134 | 139 | # Populated when ._server() gets called (in an active session) |
135 | 140 | self._server_values: ModServerResult | None = None |
136 | 141 |
|
| 142 | + def app( |
| 143 | + self, *, bookmark_store: Literal["url", "server", "disable"] = "url" |
| 144 | + ) -> App: |
| 145 | + """ |
| 146 | + Quickly chat with a dataset. |
| 147 | +
|
| 148 | + Creates a Shiny app with a chat sidebar and data table view -- providing a |
| 149 | + quick-and-easy way to start chatting with your data. |
| 150 | +
|
| 151 | + Parameters |
| 152 | + ---------- |
| 153 | + bookmark_store |
| 154 | + The bookmarking store to use for the Shiny app. Options are: |
| 155 | + - `"url"`: Store bookmarks in the URL (default). |
| 156 | + - `"server"`: Store bookmarks on the server. |
| 157 | + - `"disable"`: Disable bookmarking. |
| 158 | +
|
| 159 | + Returns |
| 160 | + ------- |
| 161 | + : |
| 162 | + A Shiny App object that can be run with `app.run()` or served with `shiny run`. |
| 163 | +
|
| 164 | + """ |
| 165 | + enable_bookmarking = bookmark_store != "disable" |
| 166 | + table_name = self.data_source.table_name |
| 167 | + |
| 168 | + def app_ui(request): |
| 169 | + return ui.page_sidebar( |
| 170 | + self.sidebar(), |
| 171 | + ui.card( |
| 172 | + ui.card_header( |
| 173 | + ui.div( |
| 174 | + ui.div( |
| 175 | + bs_icon("terminal-fill"), |
| 176 | + ui.output_text("query_title", inline=True), |
| 177 | + class_="d-flex align-items-center gap-2", |
| 178 | + ), |
| 179 | + ui.output_ui("ui_reset", inline=True), |
| 180 | + class_="hstack gap-3", |
| 181 | + ), |
| 182 | + ), |
| 183 | + ui.output_ui("sql_output"), |
| 184 | + fill=False, |
| 185 | + style="max-height: 33%;", |
| 186 | + ), |
| 187 | + ui.card( |
| 188 | + ui.card_header(bs_icon("table"), " Data"), |
| 189 | + ui.output_data_frame("dt"), |
| 190 | + ), |
| 191 | + title=ui.span("querychat with ", ui.code(table_name)), |
| 192 | + class_="bslib-page-dashboard", |
| 193 | + fillable=True, |
| 194 | + ) |
| 195 | + |
| 196 | + def app_server(input: Inputs, output: Outputs, session: Session): |
| 197 | + self._server(enable_bookmarking=enable_bookmarking) |
| 198 | + |
| 199 | + @render.text |
| 200 | + def query_title(): |
| 201 | + return self.title() or "SQL Query" |
| 202 | + |
| 203 | + @render.ui |
| 204 | + def ui_reset(): |
| 205 | + req(self.sql()) |
| 206 | + return ui.input_action_button( |
| 207 | + "reset_query", |
| 208 | + "Reset Query", |
| 209 | + class_="btn btn-outline-danger btn-sm lh-1 ms-auto", |
| 210 | + ) |
| 211 | + |
| 212 | + @reactive.effect |
| 213 | + @reactive.event(input.reset_query) |
| 214 | + def _(): |
| 215 | + self.sql("") |
| 216 | + self.title(None) |
| 217 | + |
| 218 | + @render.data_frame |
| 219 | + def dt(): |
| 220 | + return self.df() |
| 221 | + |
| 222 | + @render.ui |
| 223 | + def sql_output(): |
| 224 | + sql = self.sql() or f"SELECT * FROM {table_name}" |
| 225 | + sql_code = f"```sql\n{sql}\n```" |
| 226 | + return output_markdown_stream( |
| 227 | + "sql_code", |
| 228 | + content=sql_code, |
| 229 | + auto_scroll=False, |
| 230 | + width="100%", |
| 231 | + ) |
| 232 | + |
| 233 | + return App(app_ui, app_server, bookmark_store=bookmark_store) |
| 234 | + |
137 | 235 | def sidebar( |
138 | 236 | self, |
139 | 237 | *, |
@@ -184,7 +282,7 @@ def ui(self, **kwargs): |
184 | 282 | """ |
185 | 283 | return mod_ui(self.id, **kwargs) |
186 | 284 |
|
187 | | - def _server(self): |
| 285 | + def _server(self, *, enable_bookmarking: bool = True) -> None: |
188 | 286 | """ |
189 | 287 | Initialize the server module. |
190 | 288 |
|
@@ -212,6 +310,7 @@ def _server(self): |
212 | 310 | system_prompt=self.system_prompt, |
213 | 311 | greeting=self.greeting, |
214 | 312 | client=self.client, |
| 313 | + enable_bookmarking=enable_bookmarking, |
215 | 314 | ) |
216 | 315 |
|
217 | 316 | return |
|
0 commit comments