querychat 0.2.1__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {querychat-0.2.1 → querychat-0.3.0}/.gitignore +5 -0
- {querychat-0.2.1 → querychat-0.3.0}/PKG-INFO +6 -6
- querychat-0.3.0/pkg-py/src/querychat/__init__.py +15 -0
- querychat-0.2.1/pkg-py/src/querychat/datasource.py → querychat-0.3.0/pkg-py/src/querychat/_datasource.py +170 -53
- querychat-0.3.0/pkg-py/src/querychat/_deprecated.py +108 -0
- querychat-0.3.0/pkg-py/src/querychat/_icons.py +20 -0
- querychat-0.3.0/pkg-py/src/querychat/_querychat.py +733 -0
- querychat-0.3.0/pkg-py/src/querychat/_querychat_module.py +197 -0
- querychat-0.3.0/pkg-py/src/querychat/_utils.py +157 -0
- querychat-0.3.0/pkg-py/src/querychat/data/__init__.py +68 -0
- querychat-0.3.0/pkg-py/src/querychat/data/tips.csv.gz +0 -0
- querychat-0.3.0/pkg-py/src/querychat/data/titanic.csv.gz +0 -0
- querychat-0.3.0/pkg-py/src/querychat/express/__init__.py +3 -0
- querychat-0.3.0/pkg-py/src/querychat/prompts/prompt.md +148 -0
- querychat-0.3.0/pkg-py/src/querychat/prompts/tool-query.md +34 -0
- querychat-0.3.0/pkg-py/src/querychat/prompts/tool-reset-dashboard.md +12 -0
- querychat-0.3.0/pkg-py/src/querychat/prompts/tool-update-dashboard.md +28 -0
- querychat-0.3.0/pkg-py/src/querychat/static/js/querychat.js +20 -0
- querychat-0.3.0/pkg-py/src/querychat/tools.py +248 -0
- querychat-0.3.0/pkg-py/src/querychat/types/__init__.py +9 -0
- {querychat-0.2.1 → querychat-0.3.0}/pyproject.toml +15 -10
- querychat-0.2.1/pkg-py/src/querychat/__init__.py +0 -13
- querychat-0.2.1/pkg-py/src/querychat/_utils.py +0 -48
- querychat-0.2.1/pkg-py/src/querychat/prompt/prompt.md +0 -103
- querychat-0.2.1/pkg-py/src/querychat/querychat.py +0 -663
- {querychat-0.2.1 → querychat-0.3.0}/LICENSE.md +0 -0
- {querychat-0.2.1 → querychat-0.3.0}/pkg-py/LICENSE +0 -0
- {querychat-0.2.1 → querychat-0.3.0}/pkg-py/README.md +0 -0
- {querychat-0.2.1 → querychat-0.3.0}/pkg-py/src/querychat/static/css/styles.css +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: querychat
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Chat with your data using natural language
|
|
5
5
|
Project-URL: Homepage, https://github.com/posit-dev/querychat
|
|
6
6
|
Project-URL: Repository, https://github.com/posit-dev/querychat
|
|
@@ -31,20 +31,20 @@ License: # MIT License
|
|
|
31
31
|
SOFTWARE.
|
|
32
32
|
License-File: LICENSE.md
|
|
33
33
|
Classifier: Programming Language :: Python
|
|
34
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
35
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
36
34
|
Classifier: Programming Language :: Python :: 3.10
|
|
37
35
|
Classifier: Programming Language :: Python :: 3.11
|
|
38
36
|
Classifier: Programming Language :: Python :: 3.12
|
|
39
37
|
Classifier: Programming Language :: Python :: 3.13
|
|
40
|
-
|
|
41
|
-
Requires-
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
39
|
+
Requires-Python: >=3.10
|
|
40
|
+
Requires-Dist: chatlas>=0.13.2
|
|
42
41
|
Requires-Dist: chevron
|
|
43
42
|
Requires-Dist: duckdb
|
|
44
43
|
Requires-Dist: htmltools
|
|
45
44
|
Requires-Dist: narwhals
|
|
46
45
|
Requires-Dist: pandas
|
|
47
|
-
Requires-Dist: shiny
|
|
46
|
+
Requires-Dist: shiny>=1.5.1
|
|
47
|
+
Requires-Dist: shinychat>=0.2.8
|
|
48
48
|
Requires-Dist: shinywidgets
|
|
49
49
|
Requires-Dist: sqlalchemy>=2.0.0
|
|
50
50
|
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from ._deprecated import greeting, init, sidebar, system_prompt
|
|
2
|
+
from ._deprecated import mod_server as server
|
|
3
|
+
from ._deprecated import mod_ui as ui
|
|
4
|
+
from ._querychat import QueryChat
|
|
5
|
+
|
|
6
|
+
__all__ = (
|
|
7
|
+
"QueryChat",
|
|
8
|
+
# TODO(lifecycle): Remove these deprecated functions when we reach v1.0
|
|
9
|
+
"greeting",
|
|
10
|
+
"init",
|
|
11
|
+
"server",
|
|
12
|
+
"sidebar",
|
|
13
|
+
"system_prompt",
|
|
14
|
+
"ui",
|
|
15
|
+
)
|
|
@@ -1,94 +1,162 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
4
5
|
|
|
5
6
|
import duckdb
|
|
6
|
-
import narwhals as nw
|
|
7
|
+
import narwhals.stable.v1 as nw
|
|
7
8
|
import pandas as pd
|
|
8
9
|
from sqlalchemy import inspect, text
|
|
9
10
|
from sqlalchemy.sql import sqltypes
|
|
10
11
|
|
|
11
12
|
if TYPE_CHECKING:
|
|
13
|
+
from narwhals.stable.v1.typing import IntoFrame
|
|
12
14
|
from sqlalchemy.engine import Connection, Engine
|
|
13
15
|
|
|
14
16
|
|
|
15
|
-
class DataSource(
|
|
16
|
-
|
|
17
|
+
class DataSource(ABC):
|
|
18
|
+
"""
|
|
19
|
+
An abstract class defining the interface for data sources used by QueryChat.
|
|
20
|
+
|
|
21
|
+
Attributes
|
|
22
|
+
----------
|
|
23
|
+
table_name
|
|
24
|
+
Name of the table to be used in SQL queries.
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
table_name: str
|
|
17
29
|
|
|
18
|
-
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def get_db_type(self) -> str:
|
|
32
|
+
"""Name for the database behind the SQL execution."""
|
|
33
|
+
...
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def get_schema(self, *, categorical_threshold: int) -> str:
|
|
19
37
|
"""
|
|
20
38
|
Return schema information about the table as a string.
|
|
21
39
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
categorical_threshold
|
|
43
|
+
Maximum number of unique values for a text column to be considered
|
|
44
|
+
categorical
|
|
25
45
|
|
|
26
|
-
Returns
|
|
46
|
+
Returns
|
|
47
|
+
-------
|
|
48
|
+
:
|
|
27
49
|
A string containing the schema information in a format suitable for
|
|
28
50
|
prompting an LLM about the data structure
|
|
29
51
|
|
|
30
52
|
"""
|
|
31
53
|
...
|
|
32
54
|
|
|
55
|
+
@abstractmethod
|
|
33
56
|
def execute_query(self, query: str) -> pd.DataFrame:
|
|
34
57
|
"""
|
|
35
58
|
Execute SQL query and return results as DataFrame.
|
|
36
59
|
|
|
37
|
-
|
|
38
|
-
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
query
|
|
63
|
+
SQL query to execute
|
|
39
64
|
|
|
40
|
-
Returns
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
:
|
|
41
68
|
Query results as a pandas DataFrame
|
|
42
69
|
|
|
43
70
|
"""
|
|
44
71
|
...
|
|
45
72
|
|
|
73
|
+
@abstractmethod
|
|
46
74
|
def get_data(self) -> pd.DataFrame:
|
|
47
75
|
"""
|
|
48
76
|
Return the unfiltered data as a DataFrame.
|
|
49
77
|
|
|
50
|
-
Returns
|
|
78
|
+
Returns
|
|
79
|
+
-------
|
|
80
|
+
:
|
|
51
81
|
The complete dataset as a pandas DataFrame
|
|
52
82
|
|
|
53
83
|
"""
|
|
54
84
|
...
|
|
55
85
|
|
|
86
|
+
@abstractmethod
|
|
87
|
+
def cleanup(self) -> None:
|
|
88
|
+
"""
|
|
89
|
+
Clean up resources associated with the data source.
|
|
90
|
+
|
|
91
|
+
This method should clean up any connections or resources used by the
|
|
92
|
+
data source.
|
|
93
|
+
|
|
94
|
+
Returns
|
|
95
|
+
-------
|
|
96
|
+
None
|
|
97
|
+
|
|
98
|
+
"""
|
|
56
99
|
|
|
57
|
-
|
|
100
|
+
|
|
101
|
+
class DataFrameSource(DataSource):
|
|
58
102
|
"""A DataSource implementation that wraps a pandas DataFrame using DuckDB."""
|
|
59
103
|
|
|
60
|
-
|
|
104
|
+
_df: nw.DataFrame | nw.LazyFrame
|
|
61
105
|
|
|
62
|
-
def __init__(self, df:
|
|
106
|
+
def __init__(self, df: IntoFrame, table_name: str):
|
|
63
107
|
"""
|
|
64
108
|
Initialize with a pandas DataFrame.
|
|
65
109
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
110
|
+
Parameters
|
|
111
|
+
----------
|
|
112
|
+
df
|
|
113
|
+
The DataFrame to wrap
|
|
114
|
+
table_name
|
|
115
|
+
Name of the table in SQL queries
|
|
69
116
|
|
|
70
117
|
"""
|
|
71
118
|
self._conn = duckdb.connect(database=":memory:")
|
|
72
|
-
self._df = df
|
|
73
|
-
self.
|
|
74
|
-
|
|
119
|
+
self._df = nw.from_native(df)
|
|
120
|
+
self.table_name = table_name
|
|
121
|
+
# TODO(@gadenbuie): If the data frame is already SQL-backed, maybe we shouldn't be making a new copy here.
|
|
122
|
+
self._conn.register(table_name, self._df.lazy().collect().to_pandas())
|
|
123
|
+
|
|
124
|
+
def get_db_type(self) -> str:
|
|
125
|
+
"""
|
|
126
|
+
Get the database type.
|
|
127
|
+
|
|
128
|
+
Returns
|
|
129
|
+
-------
|
|
130
|
+
:
|
|
131
|
+
The string "DuckDB"
|
|
132
|
+
|
|
133
|
+
"""
|
|
134
|
+
return "DuckDB"
|
|
75
135
|
|
|
76
136
|
def get_schema(self, *, categorical_threshold: int) -> str:
|
|
77
137
|
"""
|
|
78
138
|
Generate schema information from DataFrame.
|
|
79
139
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
140
|
+
Parameters
|
|
141
|
+
----------
|
|
142
|
+
categorical_threshold
|
|
143
|
+
Maximum number of unique values for a text column to be considered
|
|
144
|
+
categorical
|
|
84
145
|
|
|
85
|
-
Returns
|
|
146
|
+
Returns
|
|
147
|
+
-------
|
|
148
|
+
:
|
|
86
149
|
String describing the schema
|
|
87
150
|
|
|
88
151
|
"""
|
|
89
|
-
|
|
152
|
+
schema = [f"Table: {self.table_name}", "Columns:"]
|
|
90
153
|
|
|
91
|
-
|
|
154
|
+
# Ensure we're working with a DataFrame, not a LazyFrame
|
|
155
|
+
ndf = (
|
|
156
|
+
self._df.head(10).collect()
|
|
157
|
+
if isinstance(self._df, nw.LazyFrame)
|
|
158
|
+
else self._df
|
|
159
|
+
)
|
|
92
160
|
|
|
93
161
|
for column in ndf.columns:
|
|
94
162
|
# Map pandas dtypes to SQL-like types
|
|
@@ -132,10 +200,14 @@ class DataFrameSource:
|
|
|
132
200
|
"""
|
|
133
201
|
Execute query using DuckDB.
|
|
134
202
|
|
|
135
|
-
|
|
136
|
-
|
|
203
|
+
Parameters
|
|
204
|
+
----------
|
|
205
|
+
query
|
|
206
|
+
SQL query to execute
|
|
137
207
|
|
|
138
|
-
Returns
|
|
208
|
+
Returns
|
|
209
|
+
-------
|
|
210
|
+
:
|
|
139
211
|
Query results as pandas DataFrame
|
|
140
212
|
|
|
141
213
|
"""
|
|
@@ -145,39 +217,66 @@ class DataFrameSource:
|
|
|
145
217
|
"""
|
|
146
218
|
Return the unfiltered data as a DataFrame.
|
|
147
219
|
|
|
148
|
-
Returns
|
|
220
|
+
Returns
|
|
221
|
+
-------
|
|
222
|
+
:
|
|
149
223
|
The complete dataset as a pandas DataFrame
|
|
150
224
|
|
|
151
225
|
"""
|
|
152
|
-
return self._df
|
|
226
|
+
# TODO(@gadenbuie): This should just return `self._df` and not a pandas DataFrame
|
|
227
|
+
return self._df.lazy().collect().to_pandas()
|
|
153
228
|
|
|
229
|
+
def cleanup(self) -> None:
|
|
230
|
+
"""
|
|
231
|
+
Close the DuckDB connection.
|
|
154
232
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
233
|
+
Returns
|
|
234
|
+
-------
|
|
235
|
+
None
|
|
158
236
|
|
|
159
|
-
|
|
237
|
+
"""
|
|
238
|
+
if self._conn:
|
|
239
|
+
self._conn.close()
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class SQLAlchemySource(DataSource):
|
|
160
243
|
"""
|
|
244
|
+
A DataSource implementation that supports multiple SQL databases via
|
|
245
|
+
SQLAlchemy.
|
|
161
246
|
|
|
162
|
-
|
|
247
|
+
Supports various databases including PostgreSQL, MySQL, SQLite, Snowflake,
|
|
248
|
+
and Databricks.
|
|
249
|
+
"""
|
|
163
250
|
|
|
164
251
|
def __init__(self, engine: Engine, table_name: str):
|
|
165
252
|
"""
|
|
166
253
|
Initialize with a SQLAlchemy engine.
|
|
167
254
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
255
|
+
Parameters
|
|
256
|
+
----------
|
|
257
|
+
engine
|
|
258
|
+
SQLAlchemy engine
|
|
259
|
+
table_name
|
|
260
|
+
Name of the table to query
|
|
171
261
|
|
|
172
262
|
"""
|
|
173
263
|
self._engine = engine
|
|
174
|
-
self.
|
|
264
|
+
self.table_name = table_name
|
|
175
265
|
|
|
176
266
|
# Validate table exists
|
|
177
267
|
inspector = inspect(self._engine)
|
|
178
268
|
if not inspector.has_table(table_name):
|
|
179
269
|
raise ValueError(f"Table '{table_name}' not found in database")
|
|
180
270
|
|
|
271
|
+
def get_db_type(self) -> str:
|
|
272
|
+
"""
|
|
273
|
+
Get the database type.
|
|
274
|
+
|
|
275
|
+
Returns the specific database type (e.g., POSTGRESQL, MYSQL, SQLITE) by
|
|
276
|
+
inspecting the SQLAlchemy engine. Removes " SQL" suffix if present.
|
|
277
|
+
"""
|
|
278
|
+
return self._engine.dialect.name.upper().replace(" SQL", "")
|
|
279
|
+
|
|
181
280
|
def get_schema(self, *, categorical_threshold: int) -> str: # noqa: PLR0912
|
|
182
281
|
"""
|
|
183
282
|
Generate schema information from database table.
|
|
@@ -187,9 +286,9 @@ class SQLAlchemySource:
|
|
|
187
286
|
|
|
188
287
|
"""
|
|
189
288
|
inspector = inspect(self._engine)
|
|
190
|
-
columns = inspector.get_columns(self.
|
|
289
|
+
columns = inspector.get_columns(self.table_name)
|
|
191
290
|
|
|
192
|
-
schema = [f"Table: {self.
|
|
291
|
+
schema = [f"Table: {self.table_name}", "Columns:"]
|
|
193
292
|
|
|
194
293
|
# Build a single query to get all column statistics
|
|
195
294
|
select_parts = []
|
|
@@ -236,13 +335,13 @@ class SQLAlchemySource:
|
|
|
236
335
|
if select_parts:
|
|
237
336
|
try:
|
|
238
337
|
stats_query = text(
|
|
239
|
-
f"SELECT {', '.join(select_parts)} FROM {self.
|
|
338
|
+
f"SELECT {', '.join(select_parts)} FROM {self.table_name}",
|
|
240
339
|
)
|
|
241
340
|
with self._get_connection() as conn:
|
|
242
341
|
result = conn.execute(stats_query).fetchone()
|
|
243
342
|
if result:
|
|
244
343
|
# Convert result to dict for easier access
|
|
245
|
-
column_stats = dict(zip(result._fields, result))
|
|
344
|
+
column_stats = dict(zip(result._fields, result, strict=False))
|
|
246
345
|
except Exception: # noqa: S110
|
|
247
346
|
pass # Fall back to no statistics if query fails
|
|
248
347
|
|
|
@@ -264,7 +363,7 @@ class SQLAlchemySource:
|
|
|
264
363
|
# Build UNION query for all categorical columns
|
|
265
364
|
union_parts = [
|
|
266
365
|
f"SELECT '{col_name}' as column_name, {col_name} as value "
|
|
267
|
-
f"FROM {self.
|
|
366
|
+
f"FROM {self.table_name} WHERE {col_name} IS NOT NULL "
|
|
268
367
|
f"GROUP BY {col_name}"
|
|
269
368
|
for col_name in text_cols_to_query
|
|
270
369
|
]
|
|
@@ -317,10 +416,14 @@ class SQLAlchemySource:
|
|
|
317
416
|
"""
|
|
318
417
|
Execute SQL query and return results as DataFrame.
|
|
319
418
|
|
|
320
|
-
|
|
321
|
-
|
|
419
|
+
Parameters
|
|
420
|
+
----------
|
|
421
|
+
query
|
|
422
|
+
SQL query to execute
|
|
322
423
|
|
|
323
|
-
Returns
|
|
424
|
+
Returns
|
|
425
|
+
-------
|
|
426
|
+
:
|
|
324
427
|
Query results as pandas DataFrame
|
|
325
428
|
|
|
326
429
|
"""
|
|
@@ -331,11 +434,13 @@ class SQLAlchemySource:
|
|
|
331
434
|
"""
|
|
332
435
|
Return the unfiltered data as a DataFrame.
|
|
333
436
|
|
|
334
|
-
Returns
|
|
437
|
+
Returns
|
|
438
|
+
-------
|
|
439
|
+
:
|
|
335
440
|
The complete dataset as a pandas DataFrame
|
|
336
441
|
|
|
337
442
|
"""
|
|
338
|
-
return self.execute_query(f"SELECT * FROM {self.
|
|
443
|
+
return self.execute_query(f"SELECT * FROM {self.table_name}")
|
|
339
444
|
|
|
340
445
|
def _get_sql_type_name(self, type_: sqltypes.TypeEngine) -> str: # noqa: PLR0911
|
|
341
446
|
"""Convert SQLAlchemy type to SQL type name."""
|
|
@@ -361,3 +466,15 @@ class SQLAlchemySource:
|
|
|
361
466
|
def _get_connection(self) -> Connection:
|
|
362
467
|
"""Get a connection to use for queries."""
|
|
363
468
|
return self._engine.connect()
|
|
469
|
+
|
|
470
|
+
def cleanup(self) -> None:
|
|
471
|
+
"""
|
|
472
|
+
Dispose of the SQLAlchemy engine.
|
|
473
|
+
|
|
474
|
+
Returns
|
|
475
|
+
-------
|
|
476
|
+
None
|
|
477
|
+
|
|
478
|
+
"""
|
|
479
|
+
if self._engine:
|
|
480
|
+
self._engine.dispose()
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Optional, Union
|
|
4
|
+
|
|
5
|
+
from shiny import Inputs, Outputs, Session, module, ui
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import chatlas
|
|
11
|
+
import sqlalchemy
|
|
12
|
+
from narwhals.stable.v1.typing import IntoFrame
|
|
13
|
+
|
|
14
|
+
from ._datasource import DataSource
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def init(
|
|
18
|
+
data_source: IntoFrame | sqlalchemy.Engine,
|
|
19
|
+
table_name: str,
|
|
20
|
+
*,
|
|
21
|
+
greeting: Optional[str | Path] = None,
|
|
22
|
+
data_description: Optional[str | Path] = None,
|
|
23
|
+
extra_instructions: Optional[str | Path] = None,
|
|
24
|
+
prompt_template: Optional[str | Path] = None,
|
|
25
|
+
system_prompt_override: Optional[str] = None,
|
|
26
|
+
client: Optional[Union[chatlas.Chat, str]] = None,
|
|
27
|
+
):
|
|
28
|
+
"""
|
|
29
|
+
Initialize querychat with any compliant data source.
|
|
30
|
+
|
|
31
|
+
**Deprecated.** Use `QueryChat()` instead.
|
|
32
|
+
"""
|
|
33
|
+
raise RuntimeError("init() is deprecated. Use QueryChat() instead.")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@module.ui
|
|
37
|
+
def mod_ui(**kwargs) -> ui.TagList:
|
|
38
|
+
"""
|
|
39
|
+
Create the UI for the querychat component.
|
|
40
|
+
|
|
41
|
+
**Deprecated.** Use `QueryChat.ui()` instead.
|
|
42
|
+
"""
|
|
43
|
+
raise RuntimeError("mod_ui() is deprecated. Use QueryChat.ui() instead.")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@module.server
|
|
47
|
+
def mod_server(
|
|
48
|
+
input: Inputs,
|
|
49
|
+
output: Outputs,
|
|
50
|
+
session: Session,
|
|
51
|
+
querychat_config: Any,
|
|
52
|
+
):
|
|
53
|
+
"""
|
|
54
|
+
Initialize the querychat server.
|
|
55
|
+
|
|
56
|
+
**Deprecated.** Use `QueryChat.server()` instead.
|
|
57
|
+
"""
|
|
58
|
+
raise RuntimeError("mod_server() is deprecated. Use QueryChat.server() instead.")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def sidebar(
|
|
62
|
+
id: str,
|
|
63
|
+
width: int = 400,
|
|
64
|
+
height: str = "100%",
|
|
65
|
+
**kwargs,
|
|
66
|
+
) -> ui.Sidebar:
|
|
67
|
+
"""
|
|
68
|
+
Create a sidebar containing the querychat UI.
|
|
69
|
+
|
|
70
|
+
**Deprecated.** Use `QueryChat.sidebar()` instead.
|
|
71
|
+
"""
|
|
72
|
+
raise RuntimeError("sidebar() is deprecated. Use QueryChat.sidebar() instead.")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def system_prompt(
|
|
76
|
+
data_source: DataSource,
|
|
77
|
+
*,
|
|
78
|
+
data_description: Optional[str | Path] = None,
|
|
79
|
+
extra_instructions: Optional[str | Path] = None,
|
|
80
|
+
categorical_threshold: int = 20,
|
|
81
|
+
prompt_template: Optional[str | Path] = None,
|
|
82
|
+
) -> str:
|
|
83
|
+
"""
|
|
84
|
+
Create a system prompt for the chat model based on a data source's schema
|
|
85
|
+
and optional additional context and instructions.
|
|
86
|
+
|
|
87
|
+
**Deprecated.** Use `QueryChat.set_system_prompt()` instead.
|
|
88
|
+
"""
|
|
89
|
+
raise RuntimeError(
|
|
90
|
+
"system_prompt() is deprecated. Use QueryChat.set_system_prompt() instead."
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def greeting(
|
|
95
|
+
querychat_config,
|
|
96
|
+
*,
|
|
97
|
+
generate: bool = True,
|
|
98
|
+
stream: bool = False,
|
|
99
|
+
**kwargs,
|
|
100
|
+
) -> str | None:
|
|
101
|
+
"""
|
|
102
|
+
Generate or retrieve a greeting message.
|
|
103
|
+
|
|
104
|
+
**Deprecated.** Use `QueryChat.generate_greeting()` instead.
|
|
105
|
+
"""
|
|
106
|
+
raise RuntimeError(
|
|
107
|
+
"greeting() is deprecated. Use QueryChat.generate_greeting() instead."
|
|
108
|
+
)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from shiny import ui
|
|
4
|
+
|
|
5
|
+
ICON_NAMES = Literal["arrow-counterclockwise", "funnel-fill", "terminal-fill", "table"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def bs_icon(name: ICON_NAMES) -> ui.HTML:
|
|
9
|
+
"""Get Bootstrap icon SVG by name."""
|
|
10
|
+
if name not in BS_ICONS:
|
|
11
|
+
raise ValueError(f"Unknown Bootstrap icon: {name}")
|
|
12
|
+
return ui.HTML(BS_ICONS[name])
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
BS_ICONS = {
|
|
16
|
+
"arrow-counterclockwise": '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="bi bi-arrow-counterclockwise" style="height:1em;width:1em;fill:currentColor;vertical-align:-0.125em;" aria-hidden="true" role="img"><path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z"></path><path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z"></path></svg>',
|
|
17
|
+
"funnel-fill": '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-funnel-fill" viewBox="0 0 16 16"><path d="M1.5 1.5A.5.5 0 0 1 2 1h12a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.128.334L10 8.692V13.5a.5.5 0 0 1-.342.474l-3 1A.5.5 0 0 1 6 14.5V8.692L1.628 3.834A.5.5 0 0 1 1.5 3.5z"/></svg>',
|
|
18
|
+
"terminal-fill": '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="bi bi-terminal-fill " style="height:1em;width:1em;fill:currentColor;vertical-align:-0.125em;" aria-hidden="true" role="img" ><path d="M0 3a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3zm9.5 5.5h-3a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1zm-6.354-.354a.5.5 0 1 0 .708.708l2-2a.5.5 0 0 0 0-.708l-2-2a.5.5 0 1 0-.708.708L4.793 6.5 3.146 8.146z"></path></svg>',
|
|
19
|
+
"table": '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="bi bi-table " style="height:1em;width:1em;fill:currentColor;vertical-align:-0.125em;" aria-hidden="true" role="img" ><path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm15 2h-4v3h4V4zm0 4h-4v3h4V8zm0 4h-4v3h3a1 1 0 0 0 1-1v-2zm-5 3v-3H6v3h4zm-5 0v-3H1v2a1 1 0 0 0 1 1h3zm-4-4h4V8H1v3zm0-4h4V4H1v3zm5-3v3h4V4H6zm4 4H6v3h4V8z"></path></svg>',
|
|
20
|
+
}
|