snowflake-cli-labs 2.3.0rc1__py3-none-any.whl → 2.4.0__py3-none-any.whl
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.
- snowflake/cli/__about__.py +1 -1
- snowflake/cli/api/__init__.py +2 -0
- snowflake/cli/api/cli_global_context.py +8 -1
- snowflake/cli/api/commands/decorators.py +2 -2
- snowflake/cli/api/commands/flags.py +49 -4
- snowflake/cli/api/commands/snow_typer.py +2 -0
- snowflake/cli/api/console/abc.py +2 -0
- snowflake/cli/api/console/console.py +6 -5
- snowflake/cli/api/constants.py +5 -0
- snowflake/cli/api/exceptions.py +12 -0
- snowflake/cli/api/identifiers.py +123 -0
- snowflake/cli/api/plugins/command/__init__.py +2 -0
- snowflake/cli/api/plugins/plugin_config.py +2 -0
- snowflake/cli/api/project/definition.py +2 -0
- snowflake/cli/api/project/errors.py +3 -3
- snowflake/cli/api/project/schemas/identifier_model.py +35 -0
- snowflake/cli/api/project/schemas/native_app/native_app.py +4 -0
- snowflake/cli/api/project/schemas/native_app/path_mapping.py +21 -3
- snowflake/cli/api/project/schemas/project_definition.py +58 -6
- snowflake/cli/api/project/schemas/snowpark/argument.py +2 -0
- snowflake/cli/api/project/schemas/snowpark/callable.py +8 -17
- snowflake/cli/api/project/schemas/streamlit/streamlit.py +2 -2
- snowflake/cli/api/project/schemas/updatable_model.py +2 -0
- snowflake/cli/api/project/util.py +2 -0
- snowflake/cli/api/secure_path.py +2 -0
- snowflake/cli/api/sql_execution.py +14 -54
- snowflake/cli/api/utils/cursor.py +2 -0
- snowflake/cli/api/utils/models.py +23 -0
- snowflake/cli/api/utils/naming_utils.py +0 -27
- snowflake/cli/api/utils/rendering.py +178 -23
- snowflake/cli/app/api_impl/plugin/plugin_config_provider_impl.py +2 -0
- snowflake/cli/app/cli_app.py +4 -1
- snowflake/cli/app/commands_registration/builtin_plugins.py +8 -0
- snowflake/cli/app/commands_registration/command_plugins_loader.py +2 -0
- snowflake/cli/app/commands_registration/commands_registration_with_callbacks.py +2 -0
- snowflake/cli/app/commands_registration/typer_registration.py +2 -0
- snowflake/cli/app/dev/pycharm_remote_debug.py +2 -0
- snowflake/cli/app/loggers.py +2 -0
- snowflake/cli/app/main_typer.py +1 -1
- snowflake/cli/app/printing.py +3 -1
- snowflake/cli/app/snow_connector.py +2 -2
- snowflake/cli/plugins/connection/commands.py +5 -14
- snowflake/cli/plugins/connection/util.py +1 -1
- snowflake/cli/plugins/cortex/__init__.py +0 -0
- snowflake/cli/plugins/cortex/commands.py +312 -0
- snowflake/cli/plugins/cortex/constants.py +3 -0
- snowflake/cli/plugins/cortex/manager.py +175 -0
- snowflake/cli/plugins/cortex/plugin_spec.py +16 -0
- snowflake/cli/plugins/cortex/types.py +8 -0
- snowflake/cli/plugins/git/commands.py +15 -0
- snowflake/cli/plugins/nativeapp/artifacts.py +368 -123
- snowflake/cli/plugins/nativeapp/codegen/artifact_processor.py +45 -0
- snowflake/cli/plugins/nativeapp/codegen/compiler.py +104 -0
- snowflake/cli/plugins/nativeapp/codegen/sandbox.py +2 -0
- snowflake/cli/plugins/nativeapp/codegen/snowpark/callback_source.py.jinja +181 -0
- snowflake/cli/plugins/nativeapp/codegen/snowpark/extension_function_utils.py +196 -0
- snowflake/cli/plugins/nativeapp/codegen/snowpark/models.py +47 -0
- snowflake/cli/plugins/nativeapp/codegen/snowpark/python_processor.py +489 -0
- snowflake/cli/plugins/nativeapp/commands.py +11 -4
- snowflake/cli/plugins/nativeapp/common_flags.py +12 -5
- snowflake/cli/plugins/nativeapp/constants.py +1 -0
- snowflake/cli/plugins/nativeapp/manager.py +49 -16
- snowflake/cli/plugins/nativeapp/policy.py +2 -0
- snowflake/cli/plugins/nativeapp/run_processor.py +28 -10
- snowflake/cli/plugins/nativeapp/teardown_processor.py +80 -8
- snowflake/cli/plugins/nativeapp/utils.py +7 -6
- snowflake/cli/plugins/nativeapp/version/commands.py +6 -5
- snowflake/cli/plugins/nativeapp/version/version_processor.py +2 -0
- snowflake/cli/plugins/notebook/commands.py +21 -0
- snowflake/cli/plugins/notebook/exceptions.py +6 -0
- snowflake/cli/plugins/notebook/manager.py +46 -3
- snowflake/cli/plugins/notebook/types.py +2 -0
- snowflake/cli/plugins/object/command_aliases.py +80 -0
- snowflake/cli/plugins/object/commands.py +10 -6
- snowflake/cli/plugins/object/common.py +2 -0
- snowflake/cli/plugins/object_stage_deprecated/__init__.py +1 -0
- snowflake/cli/plugins/object_stage_deprecated/plugin_spec.py +20 -0
- snowflake/cli/plugins/snowpark/commands.py +62 -6
- snowflake/cli/plugins/snowpark/common.py +17 -6
- snowflake/cli/plugins/spcs/compute_pool/commands.py +22 -1
- snowflake/cli/plugins/spcs/compute_pool/manager.py +2 -0
- snowflake/cli/plugins/spcs/image_repository/commands.py +25 -1
- snowflake/cli/plugins/spcs/image_repository/manager.py +3 -1
- snowflake/cli/plugins/spcs/services/commands.py +39 -5
- snowflake/cli/plugins/spcs/services/manager.py +2 -0
- snowflake/cli/plugins/sql/commands.py +13 -5
- snowflake/cli/plugins/sql/manager.py +40 -19
- snowflake/cli/plugins/stage/commands.py +29 -3
- snowflake/cli/plugins/stage/diff.py +2 -0
- snowflake/cli/plugins/streamlit/commands.py +26 -10
- snowflake/cli/plugins/streamlit/manager.py +9 -10
- {snowflake_cli_labs-2.3.0rc1.dist-info → snowflake_cli_labs-2.4.0.dist-info}/METADATA +4 -2
- {snowflake_cli_labs-2.3.0rc1.dist-info → snowflake_cli_labs-2.4.0.dist-info}/RECORD +97 -77
- /snowflake/cli/plugins/{object/stage_deprecated → object_stage_deprecated}/commands.py +0 -0
- {snowflake_cli_labs-2.3.0rc1.dist-info → snowflake_cli_labs-2.4.0.dist-info}/WHEEL +0 -0
- {snowflake_cli_labs-2.3.0rc1.dist-info → snowflake_cli_labs-2.4.0.dist-info}/entry_points.txt +0 -0
- {snowflake_cli_labs-2.3.0rc1.dist-info → snowflake_cli_labs-2.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
import typer
|
|
9
|
+
from click import UsageError
|
|
10
|
+
from snowflake.cli.api.cli_global_context import cli_context
|
|
11
|
+
from snowflake.cli.api.commands.flags import readable_file_option
|
|
12
|
+
from snowflake.cli.api.commands.snow_typer import SnowTyper
|
|
13
|
+
from snowflake.cli.api.output.types import (
|
|
14
|
+
CollectionResult,
|
|
15
|
+
CommandResult,
|
|
16
|
+
MessageResult,
|
|
17
|
+
)
|
|
18
|
+
from snowflake.cli.api.secure_path import SecurePath
|
|
19
|
+
from snowflake.cli.plugins.cortex.constants import DEFAULT_MODEL
|
|
20
|
+
from snowflake.cli.plugins.cortex.manager import CortexManager
|
|
21
|
+
from snowflake.cli.plugins.cortex.types import (
|
|
22
|
+
Language,
|
|
23
|
+
Model,
|
|
24
|
+
Question,
|
|
25
|
+
SourceDocument,
|
|
26
|
+
Text,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
app = SnowTyper(
|
|
30
|
+
name="cortex",
|
|
31
|
+
help="Provides access to Snowflake Cortex.",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
SEARCH_COMMAND_ENABLED = sys.version_info < (3, 12)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@app.command(
|
|
38
|
+
requires_connection=True,
|
|
39
|
+
hidden=not SEARCH_COMMAND_ENABLED,
|
|
40
|
+
)
|
|
41
|
+
def search(
|
|
42
|
+
query: str = typer.Argument(help="The search query string"),
|
|
43
|
+
service: str = typer.Option(
|
|
44
|
+
help="Cortex search service to be used. Example: --service my_cortex_service",
|
|
45
|
+
),
|
|
46
|
+
columns: Optional[List[str]] = typer.Option(
|
|
47
|
+
help='Columns that will be returned with the results. If none is provided, only search column will be included in results. Example --columns "foo" --columns "bar"',
|
|
48
|
+
default=None,
|
|
49
|
+
),
|
|
50
|
+
limit: int = typer.Option(help="Maximum number of results retrieved", default=1),
|
|
51
|
+
**options,
|
|
52
|
+
):
|
|
53
|
+
"""
|
|
54
|
+
Performs query search using Cortex Search Services.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
if not SEARCH_COMMAND_ENABLED:
|
|
58
|
+
raise click.ClickException(
|
|
59
|
+
"Cortex Search uses Snowflake Python API that currently does not support your Python version"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
from snowflake.core import Root
|
|
63
|
+
|
|
64
|
+
if not columns:
|
|
65
|
+
columns = []
|
|
66
|
+
|
|
67
|
+
conn = cli_context.connection
|
|
68
|
+
|
|
69
|
+
search_service = (
|
|
70
|
+
Root(conn)
|
|
71
|
+
.databases[conn.database]
|
|
72
|
+
.schemas[conn.schema]
|
|
73
|
+
.cortex_search_services[service]
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
response = search_service.search(
|
|
77
|
+
query=query, columns=columns, limit=limit, filter={}
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return CollectionResult(response.results)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@app.command(
|
|
84
|
+
name="complete",
|
|
85
|
+
requires_connection=True,
|
|
86
|
+
)
|
|
87
|
+
def complete(
|
|
88
|
+
text: Optional[str] = typer.Argument(
|
|
89
|
+
None,
|
|
90
|
+
help="Prompt to be used to generate a completion. Cannot be combined with --file option.",
|
|
91
|
+
show_default=False,
|
|
92
|
+
),
|
|
93
|
+
model: Optional[str] = typer.Option(
|
|
94
|
+
DEFAULT_MODEL,
|
|
95
|
+
"--model",
|
|
96
|
+
help="String specifying the model to be used.",
|
|
97
|
+
),
|
|
98
|
+
file: Optional[Path] = readable_file_option(
|
|
99
|
+
param_name="--file",
|
|
100
|
+
help_str="JSON file containing conversation history to be used to generate a completion. Cannot be combined with TEXT argument.",
|
|
101
|
+
),
|
|
102
|
+
**options,
|
|
103
|
+
) -> CommandResult:
|
|
104
|
+
"""
|
|
105
|
+
Given a prompt, the command generates a response using your choice of language model.
|
|
106
|
+
In the simplest use case, the prompt is a single string.
|
|
107
|
+
You may also provide a JSON file with conversation history including multiple prompts and responses for interactive chat-style usage.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
manager = CortexManager()
|
|
111
|
+
|
|
112
|
+
if text and file:
|
|
113
|
+
raise UsageError("--file option cannot be used together with TEXT argument.")
|
|
114
|
+
if text:
|
|
115
|
+
result_text = manager.complete_for_prompt(
|
|
116
|
+
text=Text(text),
|
|
117
|
+
model=Model(model),
|
|
118
|
+
)
|
|
119
|
+
elif file:
|
|
120
|
+
result_text = manager.complete_for_conversation(
|
|
121
|
+
conversation_json_file=SecurePath(file),
|
|
122
|
+
model=Model(model),
|
|
123
|
+
)
|
|
124
|
+
else:
|
|
125
|
+
raise UsageError("Either --file option or TEXT argument has to be provided.")
|
|
126
|
+
|
|
127
|
+
return MessageResult(result_text.strip())
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@app.command(
|
|
131
|
+
name="extract-answer",
|
|
132
|
+
requires_connection=True,
|
|
133
|
+
)
|
|
134
|
+
def extract_answer(
|
|
135
|
+
question: str = typer.Argument(
|
|
136
|
+
None,
|
|
137
|
+
help="String containing the question to be answered.",
|
|
138
|
+
show_default=False,
|
|
139
|
+
),
|
|
140
|
+
source_document_text: Optional[str] = typer.Argument(
|
|
141
|
+
None,
|
|
142
|
+
help="String containing the plain-text or JSON document that contains the answer to the question. Cannot be combined with --file option.",
|
|
143
|
+
show_default=False,
|
|
144
|
+
),
|
|
145
|
+
file: Optional[Path] = readable_file_option(
|
|
146
|
+
param_name="--file",
|
|
147
|
+
help_str="File containing the plain-text or JSON document that contains the answer to the question. Cannot be combined with SOURCE_DOCUMENT_TEXT argument.",
|
|
148
|
+
),
|
|
149
|
+
**options,
|
|
150
|
+
) -> CommandResult:
|
|
151
|
+
"""
|
|
152
|
+
Extracts an answer to a given question from a text document.
|
|
153
|
+
The document may be a plain-English document or a string representation of a semi-structured (JSON) data object.
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
manager = CortexManager()
|
|
157
|
+
|
|
158
|
+
if source_document_text and file:
|
|
159
|
+
raise UsageError(
|
|
160
|
+
"--file option cannot be used together with SOURCE_DOCUMENT_TEXT argument."
|
|
161
|
+
)
|
|
162
|
+
if source_document_text:
|
|
163
|
+
result_text = manager.extract_answer_from_source_document(
|
|
164
|
+
source_document=SourceDocument(source_document_text),
|
|
165
|
+
question=Question(question),
|
|
166
|
+
)
|
|
167
|
+
elif file:
|
|
168
|
+
result_text = manager.extract_answer_from_source_document_file(
|
|
169
|
+
source_document_input_file=SecurePath(file),
|
|
170
|
+
question=Question(question),
|
|
171
|
+
)
|
|
172
|
+
else:
|
|
173
|
+
raise UsageError(
|
|
174
|
+
"Either --file option or SOURCE_DOCUMENT_TEXT argument has to be provided."
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
return MessageResult(result_text.strip())
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@app.command(
|
|
181
|
+
name="sentiment",
|
|
182
|
+
requires_connection=True,
|
|
183
|
+
)
|
|
184
|
+
def sentiment(
|
|
185
|
+
text: Optional[str] = typer.Argument(
|
|
186
|
+
None,
|
|
187
|
+
help="String containing the text for which a sentiment score should be calculated. Cannot be combined with --file option.",
|
|
188
|
+
show_default=False,
|
|
189
|
+
),
|
|
190
|
+
file: Optional[Path] = readable_file_option(
|
|
191
|
+
param_name="--file",
|
|
192
|
+
help_str="File containing the text for which a sentiment score should be calculated. Cannot be combined with TEXT argument.",
|
|
193
|
+
),
|
|
194
|
+
**options,
|
|
195
|
+
) -> CommandResult:
|
|
196
|
+
"""
|
|
197
|
+
Returns sentiment as a score between -1 to 1
|
|
198
|
+
(with -1 being the most negative and 1 the most positive,
|
|
199
|
+
with values around 0 neutral) for the given English-language input text.
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
manager = CortexManager()
|
|
203
|
+
|
|
204
|
+
if text and file:
|
|
205
|
+
raise UsageError("--file option cannot be used together with TEXT argument.")
|
|
206
|
+
if text:
|
|
207
|
+
result_text = manager.calculate_sentiment_for_text(
|
|
208
|
+
text=Text(text),
|
|
209
|
+
)
|
|
210
|
+
elif file:
|
|
211
|
+
result_text = manager.calculate_sentiment_for_text_file(
|
|
212
|
+
text_file=SecurePath(file),
|
|
213
|
+
)
|
|
214
|
+
else:
|
|
215
|
+
raise UsageError("Either --file option or TEXT argument has to be provided.")
|
|
216
|
+
|
|
217
|
+
return MessageResult(result_text.strip())
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@app.command(
|
|
221
|
+
name="summarize",
|
|
222
|
+
requires_connection=True,
|
|
223
|
+
)
|
|
224
|
+
def summarize(
|
|
225
|
+
text: Optional[str] = typer.Argument(
|
|
226
|
+
None,
|
|
227
|
+
help="String containing the English text from which a summary should be generated. Cannot be combined with --file option.",
|
|
228
|
+
show_default=False,
|
|
229
|
+
),
|
|
230
|
+
file: Optional[Path] = readable_file_option(
|
|
231
|
+
param_name="--file",
|
|
232
|
+
help_str="File containing the English text from which a summary should be generated. Cannot be combined with TEXT argument.",
|
|
233
|
+
),
|
|
234
|
+
**options,
|
|
235
|
+
) -> CommandResult:
|
|
236
|
+
"""
|
|
237
|
+
Summarizes the given English-language input text.
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
manager = CortexManager()
|
|
241
|
+
|
|
242
|
+
if text and file:
|
|
243
|
+
raise UsageError("--file option cannot be used together with TEXT argument.")
|
|
244
|
+
if text:
|
|
245
|
+
result_text = manager.summarize_text(
|
|
246
|
+
text=Text(text),
|
|
247
|
+
)
|
|
248
|
+
elif file:
|
|
249
|
+
result_text = manager.summarize_text_file(
|
|
250
|
+
text_file=SecurePath(file),
|
|
251
|
+
)
|
|
252
|
+
else:
|
|
253
|
+
raise UsageError("Either --file option or TEXT argument has to be provided.")
|
|
254
|
+
|
|
255
|
+
return MessageResult(result_text.strip())
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
@app.command(
|
|
259
|
+
name="translate",
|
|
260
|
+
requires_connection=True,
|
|
261
|
+
)
|
|
262
|
+
def translate(
|
|
263
|
+
text: Optional[str] = typer.Argument(
|
|
264
|
+
None,
|
|
265
|
+
help="String containing the text to be translated. Cannot be combined with --file option.",
|
|
266
|
+
show_default=False,
|
|
267
|
+
),
|
|
268
|
+
from_language: Optional[str] = typer.Option(
|
|
269
|
+
None,
|
|
270
|
+
"--from",
|
|
271
|
+
help="String specifying the language code for the language the text is currently in. See Snowflake Cortex documentation for a list of supported language codes.",
|
|
272
|
+
show_default=False,
|
|
273
|
+
),
|
|
274
|
+
to_language: str = typer.Option(
|
|
275
|
+
...,
|
|
276
|
+
"--to",
|
|
277
|
+
help="String specifying the language code into which the text should be translated. See Snowflake Cortex documentation for a list of supported language codes.",
|
|
278
|
+
show_default=False,
|
|
279
|
+
),
|
|
280
|
+
file: Optional[Path] = readable_file_option(
|
|
281
|
+
param_name="--file",
|
|
282
|
+
help_str="File containing the text to be translated. Cannot be combined with TEXT argument.",
|
|
283
|
+
),
|
|
284
|
+
**options,
|
|
285
|
+
) -> CommandResult:
|
|
286
|
+
"""
|
|
287
|
+
Translates text from the indicated or detected source language to a target language.
|
|
288
|
+
"""
|
|
289
|
+
|
|
290
|
+
manager = CortexManager()
|
|
291
|
+
|
|
292
|
+
source_language = None if from_language is None else Language(from_language)
|
|
293
|
+
target_language = Language(to_language)
|
|
294
|
+
|
|
295
|
+
if text and file:
|
|
296
|
+
raise UsageError("--file option cannot be used together with TEXT argument.")
|
|
297
|
+
if text:
|
|
298
|
+
result_text = manager.translate_text(
|
|
299
|
+
text=Text(text),
|
|
300
|
+
source_language=source_language,
|
|
301
|
+
target_language=target_language,
|
|
302
|
+
)
|
|
303
|
+
elif file:
|
|
304
|
+
result_text = manager.translate_text_file(
|
|
305
|
+
text_file=SecurePath(file),
|
|
306
|
+
source_language=source_language,
|
|
307
|
+
target_language=target_language,
|
|
308
|
+
)
|
|
309
|
+
else:
|
|
310
|
+
raise UsageError("Either --file option or TEXT argument has to be provided.")
|
|
311
|
+
|
|
312
|
+
return MessageResult(result_text.strip())
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
from typing import Callable, Optional
|
|
6
|
+
|
|
7
|
+
from click import ClickException
|
|
8
|
+
from snowflake.cli.api.constants import DEFAULT_SIZE_LIMIT_MB
|
|
9
|
+
from snowflake.cli.api.exceptions import SnowflakeSQLExecutionError
|
|
10
|
+
from snowflake.cli.api.secure_path import SecurePath
|
|
11
|
+
from snowflake.cli.api.sql_execution import SqlExecutionMixin
|
|
12
|
+
from snowflake.cli.plugins.cortex.types import (
|
|
13
|
+
Language,
|
|
14
|
+
Model,
|
|
15
|
+
Question,
|
|
16
|
+
SourceDocument,
|
|
17
|
+
Text,
|
|
18
|
+
)
|
|
19
|
+
from snowflake.connector import ProgrammingError
|
|
20
|
+
from snowflake.connector.cursor import DictCursor
|
|
21
|
+
|
|
22
|
+
log = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class CortexManager(SqlExecutionMixin):
|
|
26
|
+
def complete_for_prompt(
|
|
27
|
+
self,
|
|
28
|
+
text: Text,
|
|
29
|
+
model: Model,
|
|
30
|
+
) -> str:
|
|
31
|
+
query = f"""\
|
|
32
|
+
SELECT SNOWFLAKE.CORTEX.COMPLETE(
|
|
33
|
+
'{model}',
|
|
34
|
+
'{self._escape_input(text)}'
|
|
35
|
+
) AS CORTEX_RESULT;"""
|
|
36
|
+
return self._query_cortex_result_str(query)
|
|
37
|
+
|
|
38
|
+
def complete_for_conversation(
|
|
39
|
+
self,
|
|
40
|
+
conversation_json_file: SecurePath,
|
|
41
|
+
model: Model,
|
|
42
|
+
) -> str:
|
|
43
|
+
json_content = conversation_json_file.read_text(
|
|
44
|
+
file_size_limit_mb=DEFAULT_SIZE_LIMIT_MB
|
|
45
|
+
)
|
|
46
|
+
query = f"""\
|
|
47
|
+
SELECT SNOWFLAKE.CORTEX.COMPLETE(
|
|
48
|
+
'{model}',
|
|
49
|
+
PARSE_JSON('{self._escape_input(json_content)}'),
|
|
50
|
+
{{}}
|
|
51
|
+
) AS CORTEX_RESULT;"""
|
|
52
|
+
raw_result = self._query_cortex_result_str(query)
|
|
53
|
+
json_result = json.loads(raw_result)
|
|
54
|
+
return self._extract_text_result_from_json_result(
|
|
55
|
+
lambda: json_result["choices"][0]["messages"]
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def extract_answer_from_source_document(
|
|
59
|
+
self,
|
|
60
|
+
source_document: SourceDocument,
|
|
61
|
+
question: Question,
|
|
62
|
+
) -> str:
|
|
63
|
+
query = f"""\
|
|
64
|
+
SELECT SNOWFLAKE.CORTEX.EXTRACT_ANSWER(
|
|
65
|
+
'{self._escape_input(source_document)}',
|
|
66
|
+
'{self._escape_input(question)}'
|
|
67
|
+
) AS CORTEX_RESULT;"""
|
|
68
|
+
raw_result = self._query_cortex_result_str(query)
|
|
69
|
+
json_result = json.loads(raw_result)
|
|
70
|
+
return self._extract_text_result_from_json_result(
|
|
71
|
+
lambda: json_result[0]["answer"]
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def extract_answer_from_source_document_file(
|
|
75
|
+
self,
|
|
76
|
+
source_document_input_file: SecurePath,
|
|
77
|
+
question: Question,
|
|
78
|
+
) -> str:
|
|
79
|
+
source_document_content = source_document_input_file.read_text(
|
|
80
|
+
file_size_limit_mb=DEFAULT_SIZE_LIMIT_MB
|
|
81
|
+
)
|
|
82
|
+
return self.extract_answer_from_source_document(
|
|
83
|
+
source_document=SourceDocument(source_document_content), question=question
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def calculate_sentiment_for_text(
|
|
87
|
+
self,
|
|
88
|
+
text: Text,
|
|
89
|
+
) -> str:
|
|
90
|
+
query = f"""\
|
|
91
|
+
SELECT SNOWFLAKE.CORTEX.SENTIMENT(
|
|
92
|
+
'{self._escape_input(text)}'
|
|
93
|
+
) AS CORTEX_RESULT;"""
|
|
94
|
+
return self._query_cortex_result_str(query)
|
|
95
|
+
|
|
96
|
+
def calculate_sentiment_for_text_file(
|
|
97
|
+
self,
|
|
98
|
+
text_file: SecurePath,
|
|
99
|
+
) -> str:
|
|
100
|
+
file_content = text_file.read_text(file_size_limit_mb=DEFAULT_SIZE_LIMIT_MB)
|
|
101
|
+
return self.calculate_sentiment_for_text(
|
|
102
|
+
text=Text(file_content),
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def summarize_text(
|
|
106
|
+
self,
|
|
107
|
+
text: Text,
|
|
108
|
+
) -> str:
|
|
109
|
+
query = f"""\
|
|
110
|
+
SELECT SNOWFLAKE.CORTEX.SUMMARIZE(
|
|
111
|
+
'{self._escape_input(text)}'
|
|
112
|
+
) AS CORTEX_RESULT;"""
|
|
113
|
+
return self._query_cortex_result_str(query)
|
|
114
|
+
|
|
115
|
+
def summarize_text_file(
|
|
116
|
+
self,
|
|
117
|
+
text_file: SecurePath,
|
|
118
|
+
) -> str:
|
|
119
|
+
file_content = text_file.read_text(file_size_limit_mb=DEFAULT_SIZE_LIMIT_MB)
|
|
120
|
+
return self.summarize_text(
|
|
121
|
+
text=Text(file_content),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def translate_text(
|
|
125
|
+
self,
|
|
126
|
+
text: Text,
|
|
127
|
+
source_language: Optional[Language],
|
|
128
|
+
target_language: Language,
|
|
129
|
+
) -> str:
|
|
130
|
+
query = f"""\
|
|
131
|
+
SELECT SNOWFLAKE.CORTEX.TRANSLATE(
|
|
132
|
+
'{self._escape_input(text)}',
|
|
133
|
+
'{source_language or ""}',
|
|
134
|
+
'{target_language}'
|
|
135
|
+
) AS CORTEX_RESULT;"""
|
|
136
|
+
return self._query_cortex_result_str(query)
|
|
137
|
+
|
|
138
|
+
def translate_text_file(
|
|
139
|
+
self,
|
|
140
|
+
text_file: SecurePath,
|
|
141
|
+
source_language: Optional[Language],
|
|
142
|
+
target_language: Language,
|
|
143
|
+
) -> str:
|
|
144
|
+
file_content = text_file.read_text(file_size_limit_mb=DEFAULT_SIZE_LIMIT_MB)
|
|
145
|
+
return self.translate_text(
|
|
146
|
+
text=Text(file_content),
|
|
147
|
+
source_language=source_language,
|
|
148
|
+
target_language=target_language,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
def _escape_input(plain_input: str):
|
|
153
|
+
# escape backslashes to not escape too much, this replace has to be the first one
|
|
154
|
+
# escape single quotes because they are wrapping the whole string in SQL
|
|
155
|
+
return plain_input.replace("\\", "\\\\").replace("'", "\\'")
|
|
156
|
+
|
|
157
|
+
@staticmethod
|
|
158
|
+
def _extract_text_result_from_json_result(
|
|
159
|
+
extract_function: Callable[[], str]
|
|
160
|
+
) -> str:
|
|
161
|
+
try:
|
|
162
|
+
return extract_function()
|
|
163
|
+
except (KeyError, IndexError) as ex:
|
|
164
|
+
log.debug("Cannot find Cortex result message in a response", exc_info=ex)
|
|
165
|
+
raise ClickException("Unexpected format of response from Snowflake")
|
|
166
|
+
|
|
167
|
+
def _query_cortex_result_str(self, query: str) -> str:
|
|
168
|
+
try:
|
|
169
|
+
cursor = self._execute_query(query, cursor_class=DictCursor)
|
|
170
|
+
if cursor.rowcount is None:
|
|
171
|
+
raise SnowflakeSQLExecutionError(query)
|
|
172
|
+
return str(cursor.fetchone()["CORTEX_RESULT"])
|
|
173
|
+
except ProgrammingError as ex:
|
|
174
|
+
log.debug("ProgrammingError occurred during SQL execution", exc_info=ex)
|
|
175
|
+
raise ClickException(str(ex))
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from snowflake.cli.api.plugins.command import (
|
|
2
|
+
SNOWCLI_ROOT_COMMAND_PATH,
|
|
3
|
+
CommandSpec,
|
|
4
|
+
CommandType,
|
|
5
|
+
plugin_hook_impl,
|
|
6
|
+
)
|
|
7
|
+
from snowflake.cli.plugins.cortex import commands
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@plugin_hook_impl
|
|
11
|
+
def command_spec():
|
|
12
|
+
return CommandSpec(
|
|
13
|
+
parent_command_path=SNOWCLI_ROOT_COMMAND_PATH,
|
|
14
|
+
command_type=CommandType.COMMAND_GROUP,
|
|
15
|
+
typer_instance=commands.app,
|
|
16
|
+
)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import logging
|
|
2
4
|
from typing import List, Optional
|
|
3
5
|
|
|
@@ -16,6 +18,10 @@ from snowflake.cli.api.constants import ObjectType
|
|
|
16
18
|
from snowflake.cli.api.output.types import CollectionResult, CommandResult, QueryResult
|
|
17
19
|
from snowflake.cli.api.utils.path_utils import is_stage_path
|
|
18
20
|
from snowflake.cli.plugins.git.manager import GitManager
|
|
21
|
+
from snowflake.cli.plugins.object.command_aliases import (
|
|
22
|
+
add_object_command_aliases,
|
|
23
|
+
scope_option,
|
|
24
|
+
)
|
|
19
25
|
from snowflake.cli.plugins.object.manager import ObjectManager
|
|
20
26
|
from snowflake.cli.plugins.stage.commands import get
|
|
21
27
|
from snowflake.cli.plugins.stage.manager import OnErrorType
|
|
@@ -50,6 +56,15 @@ RepoPathArgument = typer.Argument(
|
|
|
50
56
|
),
|
|
51
57
|
callback=_repo_path_argument_callback,
|
|
52
58
|
)
|
|
59
|
+
add_object_command_aliases(
|
|
60
|
+
app=app,
|
|
61
|
+
object_type=ObjectType.GIT_REPOSITORY,
|
|
62
|
+
name_argument=RepoNameArgument,
|
|
63
|
+
like_option=like_option(
|
|
64
|
+
help_example='`list --like "my%"` lists all git repositories with name that begin with “my”',
|
|
65
|
+
),
|
|
66
|
+
scope_option=scope_option(help_example="`list --in database my_db`"),
|
|
67
|
+
)
|
|
53
68
|
|
|
54
69
|
|
|
55
70
|
def _assure_repository_does_not_exist(om: ObjectManager, repository_name: str) -> None:
|