querychat 0.2.2__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.2 → querychat-0.3.0}/.gitignore +5 -0
- {querychat-0.2.2 → querychat-0.3.0}/PKG-INFO +6 -6
- querychat-0.3.0/pkg-py/src/querychat/__init__.py +15 -0
- querychat-0.2.2/pkg-py/src/querychat/datasource.py → querychat-0.3.0/pkg-py/src/querychat/_datasource.py +155 -47
- 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.2 → querychat-0.3.0}/pyproject.toml +14 -10
- querychat-0.2.2/pkg-py/src/querychat/__init__.py +0 -13
- querychat-0.2.2/pkg-py/src/querychat/_utils.py +0 -48
- querychat-0.2.2/pkg-py/src/querychat/prompt/prompt.md +0 -103
- querychat-0.2.2/pkg-py/src/querychat/querychat.py +0 -660
- {querychat-0.2.2 → querychat-0.3.0}/LICENSE.md +0 -0
- {querychat-0.2.2 → querychat-0.3.0}/pkg-py/LICENSE +0 -0
- {querychat-0.2.2 → querychat-0.3.0}/pkg-py/README.md +0 -0
- {querychat-0.2.2 → 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,6 +1,7 @@
|
|
|
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
7
|
import narwhals.stable.v1 as nw
|
|
@@ -13,83 +14,142 @@ if TYPE_CHECKING:
|
|
|
13
14
|
from sqlalchemy.engine import Connection, Engine
|
|
14
15
|
|
|
15
16
|
|
|
16
|
-
class DataSource(
|
|
17
|
-
|
|
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
|
|
18
29
|
|
|
19
|
-
|
|
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:
|
|
20
37
|
"""
|
|
21
38
|
Return schema information about the table as a string.
|
|
22
39
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
categorical_threshold
|
|
43
|
+
Maximum number of unique values for a text column to be considered
|
|
44
|
+
categorical
|
|
26
45
|
|
|
27
|
-
Returns
|
|
46
|
+
Returns
|
|
47
|
+
-------
|
|
48
|
+
:
|
|
28
49
|
A string containing the schema information in a format suitable for
|
|
29
50
|
prompting an LLM about the data structure
|
|
30
51
|
|
|
31
52
|
"""
|
|
32
53
|
...
|
|
33
54
|
|
|
55
|
+
@abstractmethod
|
|
34
56
|
def execute_query(self, query: str) -> pd.DataFrame:
|
|
35
57
|
"""
|
|
36
58
|
Execute SQL query and return results as DataFrame.
|
|
37
59
|
|
|
38
|
-
|
|
39
|
-
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
query
|
|
63
|
+
SQL query to execute
|
|
40
64
|
|
|
41
|
-
Returns
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
:
|
|
42
68
|
Query results as a pandas DataFrame
|
|
43
69
|
|
|
44
70
|
"""
|
|
45
71
|
...
|
|
46
72
|
|
|
73
|
+
@abstractmethod
|
|
47
74
|
def get_data(self) -> pd.DataFrame:
|
|
48
75
|
"""
|
|
49
76
|
Return the unfiltered data as a DataFrame.
|
|
50
77
|
|
|
51
|
-
Returns
|
|
78
|
+
Returns
|
|
79
|
+
-------
|
|
80
|
+
:
|
|
52
81
|
The complete dataset as a pandas DataFrame
|
|
53
82
|
|
|
54
83
|
"""
|
|
55
84
|
...
|
|
56
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
|
+
"""
|
|
57
99
|
|
|
58
|
-
|
|
100
|
+
|
|
101
|
+
class DataFrameSource(DataSource):
|
|
59
102
|
"""A DataSource implementation that wraps a pandas DataFrame using DuckDB."""
|
|
60
103
|
|
|
61
|
-
db_engine: ClassVar[str] = "DuckDB"
|
|
62
104
|
_df: nw.DataFrame | nw.LazyFrame
|
|
63
105
|
|
|
64
106
|
def __init__(self, df: IntoFrame, table_name: str):
|
|
65
107
|
"""
|
|
66
108
|
Initialize with a pandas DataFrame.
|
|
67
109
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
110
|
+
Parameters
|
|
111
|
+
----------
|
|
112
|
+
df
|
|
113
|
+
The DataFrame to wrap
|
|
114
|
+
table_name
|
|
115
|
+
Name of the table in SQL queries
|
|
71
116
|
|
|
72
117
|
"""
|
|
73
118
|
self._conn = duckdb.connect(database=":memory:")
|
|
74
119
|
self._df = nw.from_native(df)
|
|
75
|
-
self.
|
|
120
|
+
self.table_name = table_name
|
|
76
121
|
# TODO(@gadenbuie): If the data frame is already SQL-backed, maybe we shouldn't be making a new copy here.
|
|
77
122
|
self._conn.register(table_name, self._df.lazy().collect().to_pandas())
|
|
78
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"
|
|
135
|
+
|
|
79
136
|
def get_schema(self, *, categorical_threshold: int) -> str:
|
|
80
137
|
"""
|
|
81
138
|
Generate schema information from DataFrame.
|
|
82
139
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
140
|
+
Parameters
|
|
141
|
+
----------
|
|
142
|
+
categorical_threshold
|
|
143
|
+
Maximum number of unique values for a text column to be considered
|
|
144
|
+
categorical
|
|
87
145
|
|
|
88
|
-
Returns
|
|
146
|
+
Returns
|
|
147
|
+
-------
|
|
148
|
+
:
|
|
89
149
|
String describing the schema
|
|
90
150
|
|
|
91
151
|
"""
|
|
92
|
-
schema = [f"Table: {self.
|
|
152
|
+
schema = [f"Table: {self.table_name}", "Columns:"]
|
|
93
153
|
|
|
94
154
|
# Ensure we're working with a DataFrame, not a LazyFrame
|
|
95
155
|
ndf = (
|
|
@@ -140,10 +200,14 @@ class DataFrameSource:
|
|
|
140
200
|
"""
|
|
141
201
|
Execute query using DuckDB.
|
|
142
202
|
|
|
143
|
-
|
|
144
|
-
|
|
203
|
+
Parameters
|
|
204
|
+
----------
|
|
205
|
+
query
|
|
206
|
+
SQL query to execute
|
|
145
207
|
|
|
146
|
-
Returns
|
|
208
|
+
Returns
|
|
209
|
+
-------
|
|
210
|
+
:
|
|
147
211
|
Query results as pandas DataFrame
|
|
148
212
|
|
|
149
213
|
"""
|
|
@@ -153,40 +217,66 @@ class DataFrameSource:
|
|
|
153
217
|
"""
|
|
154
218
|
Return the unfiltered data as a DataFrame.
|
|
155
219
|
|
|
156
|
-
Returns
|
|
220
|
+
Returns
|
|
221
|
+
-------
|
|
222
|
+
:
|
|
157
223
|
The complete dataset as a pandas DataFrame
|
|
158
224
|
|
|
159
225
|
"""
|
|
160
226
|
# TODO(@gadenbuie): This should just return `self._df` and not a pandas DataFrame
|
|
161
227
|
return self._df.lazy().collect().to_pandas()
|
|
162
228
|
|
|
229
|
+
def cleanup(self) -> None:
|
|
230
|
+
"""
|
|
231
|
+
Close the DuckDB connection.
|
|
163
232
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
233
|
+
Returns
|
|
234
|
+
-------
|
|
235
|
+
None
|
|
167
236
|
|
|
168
|
-
|
|
237
|
+
"""
|
|
238
|
+
if self._conn:
|
|
239
|
+
self._conn.close()
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class SQLAlchemySource(DataSource):
|
|
169
243
|
"""
|
|
244
|
+
A DataSource implementation that supports multiple SQL databases via
|
|
245
|
+
SQLAlchemy.
|
|
170
246
|
|
|
171
|
-
|
|
247
|
+
Supports various databases including PostgreSQL, MySQL, SQLite, Snowflake,
|
|
248
|
+
and Databricks.
|
|
249
|
+
"""
|
|
172
250
|
|
|
173
251
|
def __init__(self, engine: Engine, table_name: str):
|
|
174
252
|
"""
|
|
175
253
|
Initialize with a SQLAlchemy engine.
|
|
176
254
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
255
|
+
Parameters
|
|
256
|
+
----------
|
|
257
|
+
engine
|
|
258
|
+
SQLAlchemy engine
|
|
259
|
+
table_name
|
|
260
|
+
Name of the table to query
|
|
180
261
|
|
|
181
262
|
"""
|
|
182
263
|
self._engine = engine
|
|
183
|
-
self.
|
|
264
|
+
self.table_name = table_name
|
|
184
265
|
|
|
185
266
|
# Validate table exists
|
|
186
267
|
inspector = inspect(self._engine)
|
|
187
268
|
if not inspector.has_table(table_name):
|
|
188
269
|
raise ValueError(f"Table '{table_name}' not found in database")
|
|
189
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
|
+
|
|
190
280
|
def get_schema(self, *, categorical_threshold: int) -> str: # noqa: PLR0912
|
|
191
281
|
"""
|
|
192
282
|
Generate schema information from database table.
|
|
@@ -196,9 +286,9 @@ class SQLAlchemySource:
|
|
|
196
286
|
|
|
197
287
|
"""
|
|
198
288
|
inspector = inspect(self._engine)
|
|
199
|
-
columns = inspector.get_columns(self.
|
|
289
|
+
columns = inspector.get_columns(self.table_name)
|
|
200
290
|
|
|
201
|
-
schema = [f"Table: {self.
|
|
291
|
+
schema = [f"Table: {self.table_name}", "Columns:"]
|
|
202
292
|
|
|
203
293
|
# Build a single query to get all column statistics
|
|
204
294
|
select_parts = []
|
|
@@ -245,13 +335,13 @@ class SQLAlchemySource:
|
|
|
245
335
|
if select_parts:
|
|
246
336
|
try:
|
|
247
337
|
stats_query = text(
|
|
248
|
-
f"SELECT {', '.join(select_parts)} FROM {self.
|
|
338
|
+
f"SELECT {', '.join(select_parts)} FROM {self.table_name}",
|
|
249
339
|
)
|
|
250
340
|
with self._get_connection() as conn:
|
|
251
341
|
result = conn.execute(stats_query).fetchone()
|
|
252
342
|
if result:
|
|
253
343
|
# Convert result to dict for easier access
|
|
254
|
-
column_stats = dict(zip(result._fields, result))
|
|
344
|
+
column_stats = dict(zip(result._fields, result, strict=False))
|
|
255
345
|
except Exception: # noqa: S110
|
|
256
346
|
pass # Fall back to no statistics if query fails
|
|
257
347
|
|
|
@@ -273,7 +363,7 @@ class SQLAlchemySource:
|
|
|
273
363
|
# Build UNION query for all categorical columns
|
|
274
364
|
union_parts = [
|
|
275
365
|
f"SELECT '{col_name}' as column_name, {col_name} as value "
|
|
276
|
-
f"FROM {self.
|
|
366
|
+
f"FROM {self.table_name} WHERE {col_name} IS NOT NULL "
|
|
277
367
|
f"GROUP BY {col_name}"
|
|
278
368
|
for col_name in text_cols_to_query
|
|
279
369
|
]
|
|
@@ -326,10 +416,14 @@ class SQLAlchemySource:
|
|
|
326
416
|
"""
|
|
327
417
|
Execute SQL query and return results as DataFrame.
|
|
328
418
|
|
|
329
|
-
|
|
330
|
-
|
|
419
|
+
Parameters
|
|
420
|
+
----------
|
|
421
|
+
query
|
|
422
|
+
SQL query to execute
|
|
331
423
|
|
|
332
|
-
Returns
|
|
424
|
+
Returns
|
|
425
|
+
-------
|
|
426
|
+
:
|
|
333
427
|
Query results as pandas DataFrame
|
|
334
428
|
|
|
335
429
|
"""
|
|
@@ -340,11 +434,13 @@ class SQLAlchemySource:
|
|
|
340
434
|
"""
|
|
341
435
|
Return the unfiltered data as a DataFrame.
|
|
342
436
|
|
|
343
|
-
Returns
|
|
437
|
+
Returns
|
|
438
|
+
-------
|
|
439
|
+
:
|
|
344
440
|
The complete dataset as a pandas DataFrame
|
|
345
441
|
|
|
346
442
|
"""
|
|
347
|
-
return self.execute_query(f"SELECT * FROM {self.
|
|
443
|
+
return self.execute_query(f"SELECT * FROM {self.table_name}")
|
|
348
444
|
|
|
349
445
|
def _get_sql_type_name(self, type_: sqltypes.TypeEngine) -> str: # noqa: PLR0911
|
|
350
446
|
"""Convert SQLAlchemy type to SQL type name."""
|
|
@@ -370,3 +466,15 @@ class SQLAlchemySource:
|
|
|
370
466
|
def _get_connection(self) -> Connection:
|
|
371
467
|
"""Get a connection to use for queries."""
|
|
372
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
|
+
}
|