singlestoredb 1.16.1__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.
- singlestoredb/__init__.py +75 -0
- singlestoredb/ai/__init__.py +2 -0
- singlestoredb/ai/chat.py +139 -0
- singlestoredb/ai/embeddings.py +128 -0
- singlestoredb/alchemy/__init__.py +90 -0
- singlestoredb/apps/__init__.py +3 -0
- singlestoredb/apps/_cloud_functions.py +90 -0
- singlestoredb/apps/_config.py +72 -0
- singlestoredb/apps/_connection_info.py +18 -0
- singlestoredb/apps/_dashboards.py +47 -0
- singlestoredb/apps/_process.py +32 -0
- singlestoredb/apps/_python_udfs.py +100 -0
- singlestoredb/apps/_stdout_supress.py +30 -0
- singlestoredb/apps/_uvicorn_util.py +36 -0
- singlestoredb/auth.py +245 -0
- singlestoredb/config.py +484 -0
- singlestoredb/connection.py +1487 -0
- singlestoredb/converters.py +950 -0
- singlestoredb/docstring/__init__.py +33 -0
- singlestoredb/docstring/attrdoc.py +126 -0
- singlestoredb/docstring/common.py +230 -0
- singlestoredb/docstring/epydoc.py +267 -0
- singlestoredb/docstring/google.py +412 -0
- singlestoredb/docstring/numpydoc.py +562 -0
- singlestoredb/docstring/parser.py +100 -0
- singlestoredb/docstring/py.typed +1 -0
- singlestoredb/docstring/rest.py +256 -0
- singlestoredb/docstring/tests/__init__.py +1 -0
- singlestoredb/docstring/tests/_pydoctor.py +21 -0
- singlestoredb/docstring/tests/test_epydoc.py +729 -0
- singlestoredb/docstring/tests/test_google.py +1007 -0
- singlestoredb/docstring/tests/test_numpydoc.py +1100 -0
- singlestoredb/docstring/tests/test_parse_from_object.py +109 -0
- singlestoredb/docstring/tests/test_parser.py +248 -0
- singlestoredb/docstring/tests/test_rest.py +547 -0
- singlestoredb/docstring/tests/test_util.py +70 -0
- singlestoredb/docstring/util.py +141 -0
- singlestoredb/exceptions.py +120 -0
- singlestoredb/functions/__init__.py +16 -0
- singlestoredb/functions/decorator.py +201 -0
- singlestoredb/functions/dtypes.py +1793 -0
- singlestoredb/functions/ext/__init__.py +1 -0
- singlestoredb/functions/ext/arrow.py +375 -0
- singlestoredb/functions/ext/asgi.py +2133 -0
- singlestoredb/functions/ext/json.py +420 -0
- singlestoredb/functions/ext/mmap.py +413 -0
- singlestoredb/functions/ext/rowdat_1.py +724 -0
- singlestoredb/functions/ext/timer.py +89 -0
- singlestoredb/functions/ext/utils.py +218 -0
- singlestoredb/functions/signature.py +1578 -0
- singlestoredb/functions/typing/__init__.py +41 -0
- singlestoredb/functions/typing/numpy.py +20 -0
- singlestoredb/functions/typing/pandas.py +2 -0
- singlestoredb/functions/typing/polars.py +2 -0
- singlestoredb/functions/typing/pyarrow.py +2 -0
- singlestoredb/functions/utils.py +421 -0
- singlestoredb/fusion/__init__.py +11 -0
- singlestoredb/fusion/graphql.py +213 -0
- singlestoredb/fusion/handler.py +916 -0
- singlestoredb/fusion/handlers/__init__.py +0 -0
- singlestoredb/fusion/handlers/export.py +525 -0
- singlestoredb/fusion/handlers/files.py +690 -0
- singlestoredb/fusion/handlers/job.py +660 -0
- singlestoredb/fusion/handlers/models.py +250 -0
- singlestoredb/fusion/handlers/stage.py +502 -0
- singlestoredb/fusion/handlers/utils.py +324 -0
- singlestoredb/fusion/handlers/workspace.py +956 -0
- singlestoredb/fusion/registry.py +249 -0
- singlestoredb/fusion/result.py +399 -0
- singlestoredb/http/__init__.py +27 -0
- singlestoredb/http/connection.py +1267 -0
- singlestoredb/magics/__init__.py +34 -0
- singlestoredb/magics/run_personal.py +137 -0
- singlestoredb/magics/run_shared.py +134 -0
- singlestoredb/management/__init__.py +9 -0
- singlestoredb/management/billing_usage.py +148 -0
- singlestoredb/management/cluster.py +462 -0
- singlestoredb/management/export.py +295 -0
- singlestoredb/management/files.py +1102 -0
- singlestoredb/management/inference_api.py +105 -0
- singlestoredb/management/job.py +887 -0
- singlestoredb/management/manager.py +373 -0
- singlestoredb/management/organization.py +226 -0
- singlestoredb/management/region.py +169 -0
- singlestoredb/management/utils.py +423 -0
- singlestoredb/management/workspace.py +1927 -0
- singlestoredb/mysql/__init__.py +177 -0
- singlestoredb/mysql/_auth.py +298 -0
- singlestoredb/mysql/charset.py +214 -0
- singlestoredb/mysql/connection.py +2032 -0
- singlestoredb/mysql/constants/CLIENT.py +38 -0
- singlestoredb/mysql/constants/COMMAND.py +32 -0
- singlestoredb/mysql/constants/CR.py +78 -0
- singlestoredb/mysql/constants/ER.py +474 -0
- singlestoredb/mysql/constants/EXTENDED_TYPE.py +3 -0
- singlestoredb/mysql/constants/FIELD_TYPE.py +48 -0
- singlestoredb/mysql/constants/FLAG.py +15 -0
- singlestoredb/mysql/constants/SERVER_STATUS.py +10 -0
- singlestoredb/mysql/constants/VECTOR_TYPE.py +6 -0
- singlestoredb/mysql/constants/__init__.py +0 -0
- singlestoredb/mysql/converters.py +271 -0
- singlestoredb/mysql/cursors.py +896 -0
- singlestoredb/mysql/err.py +92 -0
- singlestoredb/mysql/optionfile.py +20 -0
- singlestoredb/mysql/protocol.py +450 -0
- singlestoredb/mysql/tests/__init__.py +19 -0
- singlestoredb/mysql/tests/base.py +126 -0
- singlestoredb/mysql/tests/conftest.py +37 -0
- singlestoredb/mysql/tests/test_DictCursor.py +132 -0
- singlestoredb/mysql/tests/test_SSCursor.py +141 -0
- singlestoredb/mysql/tests/test_basic.py +452 -0
- singlestoredb/mysql/tests/test_connection.py +851 -0
- singlestoredb/mysql/tests/test_converters.py +58 -0
- singlestoredb/mysql/tests/test_cursor.py +141 -0
- singlestoredb/mysql/tests/test_err.py +16 -0
- singlestoredb/mysql/tests/test_issues.py +514 -0
- singlestoredb/mysql/tests/test_load_local.py +75 -0
- singlestoredb/mysql/tests/test_nextset.py +88 -0
- singlestoredb/mysql/tests/test_optionfile.py +27 -0
- singlestoredb/mysql/tests/thirdparty/__init__.py +6 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +9 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/capabilities.py +323 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/dbapi20.py +865 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +110 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +224 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +101 -0
- singlestoredb/mysql/times.py +23 -0
- singlestoredb/notebook/__init__.py +16 -0
- singlestoredb/notebook/_objects.py +213 -0
- singlestoredb/notebook/_portal.py +352 -0
- singlestoredb/py.typed +0 -0
- singlestoredb/pytest.py +352 -0
- singlestoredb/server/__init__.py +0 -0
- singlestoredb/server/docker.py +452 -0
- singlestoredb/server/free_tier.py +267 -0
- singlestoredb/tests/__init__.py +0 -0
- singlestoredb/tests/alltypes.sql +307 -0
- singlestoredb/tests/alltypes_no_nulls.sql +208 -0
- singlestoredb/tests/empty.sql +0 -0
- singlestoredb/tests/ext_funcs/__init__.py +702 -0
- singlestoredb/tests/local_infile.csv +3 -0
- singlestoredb/tests/test.ipynb +18 -0
- singlestoredb/tests/test.sql +680 -0
- singlestoredb/tests/test2.ipynb +18 -0
- singlestoredb/tests/test2.sql +1 -0
- singlestoredb/tests/test_basics.py +1332 -0
- singlestoredb/tests/test_config.py +318 -0
- singlestoredb/tests/test_connection.py +3103 -0
- singlestoredb/tests/test_dbapi.py +27 -0
- singlestoredb/tests/test_exceptions.py +45 -0
- singlestoredb/tests/test_ext_func.py +1472 -0
- singlestoredb/tests/test_ext_func_data.py +1101 -0
- singlestoredb/tests/test_fusion.py +1527 -0
- singlestoredb/tests/test_http.py +288 -0
- singlestoredb/tests/test_management.py +1599 -0
- singlestoredb/tests/test_plugin.py +33 -0
- singlestoredb/tests/test_results.py +171 -0
- singlestoredb/tests/test_types.py +132 -0
- singlestoredb/tests/test_udf.py +737 -0
- singlestoredb/tests/test_udf_returns.py +459 -0
- singlestoredb/tests/test_vectorstore.py +51 -0
- singlestoredb/tests/test_xdict.py +333 -0
- singlestoredb/tests/utils.py +141 -0
- singlestoredb/types.py +373 -0
- singlestoredb/utils/__init__.py +0 -0
- singlestoredb/utils/config.py +950 -0
- singlestoredb/utils/convert_rows.py +69 -0
- singlestoredb/utils/debug.py +13 -0
- singlestoredb/utils/dtypes.py +205 -0
- singlestoredb/utils/events.py +65 -0
- singlestoredb/utils/mogrify.py +151 -0
- singlestoredb/utils/results.py +585 -0
- singlestoredb/utils/xdict.py +425 -0
- singlestoredb/vectorstore.py +192 -0
- singlestoredb/warnings.py +5 -0
- singlestoredb-1.16.1.dist-info/METADATA +165 -0
- singlestoredb-1.16.1.dist-info/RECORD +183 -0
- singlestoredb-1.16.1.dist-info/WHEEL +5 -0
- singlestoredb-1.16.1.dist-info/entry_points.txt +2 -0
- singlestoredb-1.16.1.dist-info/licenses/LICENSE +201 -0
- singlestoredb-1.16.1.dist-info/top_level.txt +3 -0
- sqlx/__init__.py +4 -0
- sqlx/magic.py +113 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
SingleStoreDB module.
|
|
4
|
+
|
|
5
|
+
Examples
|
|
6
|
+
--------
|
|
7
|
+
>>> import singlestoredb as s2
|
|
8
|
+
>>> conn = s2.connect('user:password@host/dbname')
|
|
9
|
+
>>> cur = conn.cursor()
|
|
10
|
+
>>> cur.execute('select * from customers')
|
|
11
|
+
>>> for row in cur:
|
|
12
|
+
... print(row)
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
__version__ = '1.16.1'
|
|
17
|
+
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from .config import options, get_option, set_option, describe_option
|
|
21
|
+
from .connection import connect, apilevel, threadsafety, paramstyle
|
|
22
|
+
from .exceptions import (
|
|
23
|
+
Warning, Error, InterfaceError, DatabaseError, OperationalError,
|
|
24
|
+
IntegrityError, InternalError, ProgrammingError, NotSupportedError,
|
|
25
|
+
DataError, ManagementError,
|
|
26
|
+
)
|
|
27
|
+
from .management import (
|
|
28
|
+
manage_cluster, manage_workspaces, manage_files, manage_regions,
|
|
29
|
+
)
|
|
30
|
+
from .types import (
|
|
31
|
+
Date, Time, Timestamp, DateFromTicks, TimeFromTicks, TimestampFromTicks,
|
|
32
|
+
Binary, STRING, BINARY, NUMBER, DATETIME, ROWID,
|
|
33
|
+
)
|
|
34
|
+
# These are only loaded if the singlestore-vectorstore package is available
|
|
35
|
+
try:
|
|
36
|
+
from .vectorstore import (
|
|
37
|
+
vector_db, IndexInterface, IndexList, IndexModel, MatchTypedDict,
|
|
38
|
+
Metric, IndexStatsTypedDict, NamespaceStatsTypedDict, Vector,
|
|
39
|
+
VectorDictMetadataValue, VectorMetadataTypedDict, VectorTuple,
|
|
40
|
+
VectorTupleWithMetadata, DeletionProtection, AndFilter, EqFilter,
|
|
41
|
+
ExactMatchFilter, FilterTypedDict, GteFilter, GtFilter, InFilter,
|
|
42
|
+
LteFilter, LtFilter, NeFilter, NinFilter, OrFilter, SimpleFilter,
|
|
43
|
+
)
|
|
44
|
+
except (ImportError, ModuleNotFoundError):
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
#
|
|
49
|
+
# This function is defined here to prevent the side-effect of
|
|
50
|
+
# attempting to load the SQLAlchemy dialect in the core SDK.
|
|
51
|
+
#
|
|
52
|
+
def create_engine(*args: Any, **kwargs: Any) -> Any:
|
|
53
|
+
"""
|
|
54
|
+
Create an SQLAlchemy engine for SingleStoreDB.
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
**kwargs : Any
|
|
59
|
+
The parameters taken here are the same as for
|
|
60
|
+
`sqlalchemy.create_engine`. However, this function can be
|
|
61
|
+
called without any parameters in order to inherit parameters
|
|
62
|
+
set by environment variables or parameters set in by
|
|
63
|
+
options in Python code.
|
|
64
|
+
|
|
65
|
+
See Also
|
|
66
|
+
--------
|
|
67
|
+
`sqlalchemy.create_engine`
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
SQLAlchemy engine
|
|
72
|
+
|
|
73
|
+
"""
|
|
74
|
+
from .alchemy import create_engine
|
|
75
|
+
return create_engine(*args, **kwargs)
|
singlestoredb/ai/chat.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Callable
|
|
4
|
+
from typing import Optional
|
|
5
|
+
from typing import Union
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
from singlestoredb import manage_workspaces
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from langchain_openai import ChatOpenAI
|
|
13
|
+
except ImportError:
|
|
14
|
+
raise ImportError(
|
|
15
|
+
'Could not import langchain_openai python package. '
|
|
16
|
+
'Please install it with `pip install langchain_openai`.',
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
from langchain_aws import ChatBedrockConverse
|
|
21
|
+
except ImportError:
|
|
22
|
+
raise ImportError(
|
|
23
|
+
'Could not import langchain-aws python package. '
|
|
24
|
+
'Please install it with `pip install langchain-aws`.',
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
import boto3
|
|
28
|
+
from botocore import UNSIGNED
|
|
29
|
+
from botocore.config import Config
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def SingleStoreChatFactory(
|
|
33
|
+
model_name: str,
|
|
34
|
+
api_key: Optional[str] = None,
|
|
35
|
+
streaming: bool = True,
|
|
36
|
+
http_client: Optional[httpx.Client] = None,
|
|
37
|
+
obo_token_getter: Optional[Callable[[], Optional[str]]] = None,
|
|
38
|
+
**kwargs: Any,
|
|
39
|
+
) -> Union[ChatOpenAI, ChatBedrockConverse]:
|
|
40
|
+
"""Return a chat model instance (ChatOpenAI or ChatBedrockConverse).
|
|
41
|
+
"""
|
|
42
|
+
inference_api_manager = (
|
|
43
|
+
manage_workspaces().organizations.current.inference_apis
|
|
44
|
+
)
|
|
45
|
+
info = inference_api_manager.get(model_name=model_name)
|
|
46
|
+
token_env = os.environ.get('SINGLESTOREDB_USER_TOKEN')
|
|
47
|
+
token = api_key if api_key is not None else token_env
|
|
48
|
+
|
|
49
|
+
if info.hosting_platform == 'Amazon':
|
|
50
|
+
# Instantiate Bedrock client
|
|
51
|
+
cfg_kwargs = {
|
|
52
|
+
'signature_version': UNSIGNED,
|
|
53
|
+
'retries': {'max_attempts': 1, 'mode': 'standard'},
|
|
54
|
+
}
|
|
55
|
+
# Extract timeouts from http_client if provided
|
|
56
|
+
t = http_client.timeout if http_client is not None else None
|
|
57
|
+
connect_timeout = None
|
|
58
|
+
read_timeout = None
|
|
59
|
+
if t is not None:
|
|
60
|
+
if isinstance(t, httpx.Timeout):
|
|
61
|
+
if t.connect is not None:
|
|
62
|
+
connect_timeout = float(t.connect)
|
|
63
|
+
if t.read is not None:
|
|
64
|
+
read_timeout = float(t.read)
|
|
65
|
+
if connect_timeout is None and read_timeout is not None:
|
|
66
|
+
connect_timeout = read_timeout
|
|
67
|
+
if read_timeout is None and connect_timeout is not None:
|
|
68
|
+
read_timeout = connect_timeout
|
|
69
|
+
elif isinstance(t, (int, float)):
|
|
70
|
+
connect_timeout = float(t)
|
|
71
|
+
read_timeout = float(t)
|
|
72
|
+
if read_timeout is not None:
|
|
73
|
+
cfg_kwargs['read_timeout'] = read_timeout
|
|
74
|
+
if connect_timeout is not None:
|
|
75
|
+
cfg_kwargs['connect_timeout'] = connect_timeout
|
|
76
|
+
|
|
77
|
+
cfg = Config(**cfg_kwargs)
|
|
78
|
+
client = boto3.client(
|
|
79
|
+
'bedrock-runtime',
|
|
80
|
+
endpoint_url=info.connection_url,
|
|
81
|
+
region_name='us-east-1',
|
|
82
|
+
aws_access_key_id='placeholder',
|
|
83
|
+
aws_secret_access_key='placeholder',
|
|
84
|
+
config=cfg,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def _inject_headers(request: Any, **_ignored: Any) -> None:
|
|
88
|
+
"""Inject dynamic auth/OBO headers prior to Bedrock sending."""
|
|
89
|
+
if obo_token_getter is not None:
|
|
90
|
+
obo_val = obo_token_getter()
|
|
91
|
+
if obo_val:
|
|
92
|
+
request.headers['X-S2-OBO'] = obo_val
|
|
93
|
+
if token:
|
|
94
|
+
request.headers['Authorization'] = f'Bearer {token}'
|
|
95
|
+
request.headers.pop('X-Amz-Date', None)
|
|
96
|
+
request.headers.pop('X-Amz-Security-Token', None)
|
|
97
|
+
|
|
98
|
+
emitter = client._endpoint._event_emitter
|
|
99
|
+
emitter.register_first(
|
|
100
|
+
'before-send.bedrock-runtime.Converse',
|
|
101
|
+
_inject_headers,
|
|
102
|
+
)
|
|
103
|
+
emitter.register_first(
|
|
104
|
+
'before-send.bedrock-runtime.ConverseStream',
|
|
105
|
+
_inject_headers,
|
|
106
|
+
)
|
|
107
|
+
emitter.register_first(
|
|
108
|
+
'before-send.bedrock-runtime.InvokeModel',
|
|
109
|
+
_inject_headers,
|
|
110
|
+
)
|
|
111
|
+
emitter.register_first(
|
|
112
|
+
'before-send.bedrock-runtime.InvokeModelWithResponseStream',
|
|
113
|
+
_inject_headers,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return ChatBedrockConverse(
|
|
117
|
+
model_id=model_name,
|
|
118
|
+
endpoint_url=info.connection_url,
|
|
119
|
+
region_name='us-east-1',
|
|
120
|
+
aws_access_key_id='placeholder',
|
|
121
|
+
aws_secret_access_key='placeholder',
|
|
122
|
+
disable_streaming=not streaming,
|
|
123
|
+
client=client,
|
|
124
|
+
**kwargs,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# OpenAI / Azure OpenAI path
|
|
128
|
+
openai_kwargs = dict(
|
|
129
|
+
base_url=info.connection_url,
|
|
130
|
+
api_key=token,
|
|
131
|
+
model=model_name,
|
|
132
|
+
streaming=streaming,
|
|
133
|
+
)
|
|
134
|
+
if http_client is not None:
|
|
135
|
+
openai_kwargs['http_client'] = http_client
|
|
136
|
+
return ChatOpenAI(
|
|
137
|
+
**openai_kwargs,
|
|
138
|
+
**kwargs,
|
|
139
|
+
)
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Callable
|
|
4
|
+
from typing import Optional
|
|
5
|
+
from typing import Union
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
from singlestoredb import manage_workspaces
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from langchain_openai import OpenAIEmbeddings
|
|
13
|
+
except ImportError:
|
|
14
|
+
raise ImportError(
|
|
15
|
+
'Could not import langchain_openai python package. '
|
|
16
|
+
'Please install it with `pip install langchain_openai`.',
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
from langchain_aws import BedrockEmbeddings
|
|
21
|
+
except ImportError:
|
|
22
|
+
raise ImportError(
|
|
23
|
+
'Could not import langchain-aws python package. '
|
|
24
|
+
'Please install it with `pip install langchain-aws`.',
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
import boto3
|
|
28
|
+
from botocore import UNSIGNED
|
|
29
|
+
from botocore.config import Config
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def SingleStoreEmbeddingsFactory(
|
|
33
|
+
model_name: str,
|
|
34
|
+
api_key: Optional[str] = None,
|
|
35
|
+
http_client: Optional[httpx.Client] = None,
|
|
36
|
+
obo_token_getter: Optional[Callable[[], Optional[str]]] = None,
|
|
37
|
+
**kwargs: Any,
|
|
38
|
+
) -> Union[OpenAIEmbeddings, BedrockEmbeddings]:
|
|
39
|
+
"""Return an embeddings model instance (OpenAIEmbeddings or BedrockEmbeddings).
|
|
40
|
+
"""
|
|
41
|
+
inference_api_manager = (
|
|
42
|
+
manage_workspaces().organizations.current.inference_apis
|
|
43
|
+
)
|
|
44
|
+
info = inference_api_manager.get(model_name=model_name)
|
|
45
|
+
token_env = os.environ.get('SINGLESTOREDB_USER_TOKEN')
|
|
46
|
+
token = api_key if api_key is not None else token_env
|
|
47
|
+
|
|
48
|
+
if info.hosting_platform == 'Amazon':
|
|
49
|
+
# Instantiate Bedrock client
|
|
50
|
+
cfg_kwargs = {
|
|
51
|
+
'signature_version': UNSIGNED,
|
|
52
|
+
'retries': {'max_attempts': 1, 'mode': 'standard'},
|
|
53
|
+
}
|
|
54
|
+
# Extract timeouts from http_client if provided
|
|
55
|
+
t = http_client.timeout if http_client is not None else None
|
|
56
|
+
connect_timeout = None
|
|
57
|
+
read_timeout = None
|
|
58
|
+
if t is not None:
|
|
59
|
+
if isinstance(t, httpx.Timeout):
|
|
60
|
+
if t.connect is not None:
|
|
61
|
+
connect_timeout = float(t.connect)
|
|
62
|
+
if t.read is not None:
|
|
63
|
+
read_timeout = float(t.read)
|
|
64
|
+
if connect_timeout is None and read_timeout is not None:
|
|
65
|
+
connect_timeout = read_timeout
|
|
66
|
+
if read_timeout is None and connect_timeout is not None:
|
|
67
|
+
read_timeout = connect_timeout
|
|
68
|
+
elif isinstance(t, (int, float)):
|
|
69
|
+
connect_timeout = float(t)
|
|
70
|
+
read_timeout = float(t)
|
|
71
|
+
if read_timeout is not None:
|
|
72
|
+
cfg_kwargs['read_timeout'] = read_timeout
|
|
73
|
+
if connect_timeout is not None:
|
|
74
|
+
cfg_kwargs['connect_timeout'] = connect_timeout
|
|
75
|
+
|
|
76
|
+
cfg = Config(**cfg_kwargs)
|
|
77
|
+
client = boto3.client(
|
|
78
|
+
'bedrock-runtime',
|
|
79
|
+
endpoint_url=info.connection_url,
|
|
80
|
+
region_name='us-east-1',
|
|
81
|
+
aws_access_key_id='placeholder',
|
|
82
|
+
aws_secret_access_key='placeholder',
|
|
83
|
+
config=cfg,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def _inject_headers(request: Any, **_ignored: Any) -> None:
|
|
87
|
+
"""Inject dynamic auth/OBO headers prior to Bedrock sending."""
|
|
88
|
+
if obo_token_getter is not None:
|
|
89
|
+
obo_val = obo_token_getter()
|
|
90
|
+
if obo_val:
|
|
91
|
+
request.headers['X-S2-OBO'] = obo_val
|
|
92
|
+
if token:
|
|
93
|
+
request.headers['Authorization'] = f'Bearer {token}'
|
|
94
|
+
request.headers.pop('X-Amz-Date', None)
|
|
95
|
+
request.headers.pop('X-Amz-Security-Token', None)
|
|
96
|
+
|
|
97
|
+
emitter = client._endpoint._event_emitter
|
|
98
|
+
emitter.register_first(
|
|
99
|
+
'before-send.bedrock-runtime.InvokeModel',
|
|
100
|
+
_inject_headers,
|
|
101
|
+
)
|
|
102
|
+
emitter.register_first(
|
|
103
|
+
'before-send.bedrock-runtime.InvokeModelWithResponseStream',
|
|
104
|
+
_inject_headers,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return BedrockEmbeddings(
|
|
108
|
+
model_id=model_name,
|
|
109
|
+
endpoint_url=info.connection_url,
|
|
110
|
+
region_name='us-east-1',
|
|
111
|
+
aws_access_key_id='placeholder',
|
|
112
|
+
aws_secret_access_key='placeholder',
|
|
113
|
+
client=client,
|
|
114
|
+
**kwargs,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# OpenAI / Azure OpenAI path
|
|
118
|
+
openai_kwargs = dict(
|
|
119
|
+
base_url=info.connection_url,
|
|
120
|
+
api_key=token,
|
|
121
|
+
model=model_name,
|
|
122
|
+
)
|
|
123
|
+
if http_client is not None:
|
|
124
|
+
openai_kwargs['http_client'] = http_client
|
|
125
|
+
return OpenAIEmbeddings(
|
|
126
|
+
**openai_kwargs,
|
|
127
|
+
**kwargs,
|
|
128
|
+
)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import inspect
|
|
3
|
+
from typing import Any
|
|
4
|
+
from urllib.parse import quote
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
import sqlalchemy
|
|
8
|
+
from sqlalchemy_singlestoredb import * # noqa: F403, F401
|
|
9
|
+
has_sqlalchemy = True
|
|
10
|
+
except ImportError:
|
|
11
|
+
import warnings
|
|
12
|
+
warnings.warn(
|
|
13
|
+
'sqlalchemy_singlestoredb must be installed to use this module',
|
|
14
|
+
RuntimeWarning,
|
|
15
|
+
)
|
|
16
|
+
has_sqlalchemy = False
|
|
17
|
+
|
|
18
|
+
from ..connection import build_params
|
|
19
|
+
from ..connection import connect
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def create_engine(*args: Any, **kwargs: Any) -> Any:
|
|
23
|
+
"""
|
|
24
|
+
Create an SQLAlchemy engine for SingleStoreDB.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
**kwargs : Any
|
|
29
|
+
The parameters taken here are the same as for
|
|
30
|
+
`sqlalchemy.create_engine`. However, this function can be
|
|
31
|
+
called without any parameters in order to inherit parameters
|
|
32
|
+
set by environment variables or parameters set in by
|
|
33
|
+
options in Python code.
|
|
34
|
+
|
|
35
|
+
See Also
|
|
36
|
+
--------
|
|
37
|
+
`sqlalchemy.create_engine`
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
SQLAlchemy engine
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
if not has_sqlalchemy:
|
|
45
|
+
raise RuntimeError('sqlalchemy_singlestoredb package is not installed')
|
|
46
|
+
|
|
47
|
+
if len(args) > 1:
|
|
48
|
+
raise ValueError(
|
|
49
|
+
'`args` can only have a single element '
|
|
50
|
+
'containing the database URL',
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if args:
|
|
54
|
+
kwargs['host'] = args[0]
|
|
55
|
+
|
|
56
|
+
conn_params = {}
|
|
57
|
+
sa_params = {}
|
|
58
|
+
|
|
59
|
+
conn_args = inspect.getfullargspec(connect).args
|
|
60
|
+
|
|
61
|
+
for key, value in kwargs.items():
|
|
62
|
+
if key in conn_args:
|
|
63
|
+
conn_params[key] = value
|
|
64
|
+
else:
|
|
65
|
+
sa_params[key] = value
|
|
66
|
+
|
|
67
|
+
params = build_params(**conn_params)
|
|
68
|
+
driver = params.pop('driver', None)
|
|
69
|
+
host = params.pop('host')
|
|
70
|
+
port = params.pop('port')
|
|
71
|
+
user = params.pop('user', None)
|
|
72
|
+
password = params.pop('password', None)
|
|
73
|
+
database = params.pop('database', '')
|
|
74
|
+
|
|
75
|
+
if not driver:
|
|
76
|
+
driver = 'singlestoredb+mysql'
|
|
77
|
+
elif not driver.startswith('singlestoredb'):
|
|
78
|
+
driver = f'singlestoredb+{driver}'
|
|
79
|
+
|
|
80
|
+
if user is not None and password is not None:
|
|
81
|
+
url = f'{driver}://{quote(user)}:{quote(password)}@' \
|
|
82
|
+
f'{host}:{port}/{quote(database)}'
|
|
83
|
+
elif user is not None:
|
|
84
|
+
url = f'{driver}://{quote(user)}@{host}:{port}/{quote(database)}'
|
|
85
|
+
elif password is not None:
|
|
86
|
+
url = f'{driver}://:{quote(password)}@{host}:{port}/{quote(database)}'
|
|
87
|
+
else:
|
|
88
|
+
url = f'{driver}://{host}:{port}/{quote(database)}'
|
|
89
|
+
|
|
90
|
+
return sqlalchemy.create_engine(url, connect_args=params, **sa_params)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import textwrap
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from ._config import AppConfig
|
|
6
|
+
from ._connection_info import ConnectionInfo
|
|
7
|
+
from ._process import kill_process_by_port
|
|
8
|
+
|
|
9
|
+
if typing.TYPE_CHECKING:
|
|
10
|
+
from fastapi import FastAPI
|
|
11
|
+
from ._uvicorn_util import AwaitableUvicornServer
|
|
12
|
+
|
|
13
|
+
# Keep track of currently running server
|
|
14
|
+
_running_server: 'typing.Optional[AwaitableUvicornServer]' = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def run_function_app(
|
|
18
|
+
app: 'FastAPI',
|
|
19
|
+
log_level: str = 'error',
|
|
20
|
+
kill_existing_app_server: bool = True,
|
|
21
|
+
) -> ConnectionInfo:
|
|
22
|
+
global _running_server
|
|
23
|
+
from ._uvicorn_util import AwaitableUvicornServer
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
import uvicorn
|
|
27
|
+
except ImportError:
|
|
28
|
+
raise ImportError('package uvicorn is required to run cloud functions')
|
|
29
|
+
try:
|
|
30
|
+
import fastapi
|
|
31
|
+
except ImportError:
|
|
32
|
+
raise ImportError('package fastapi is required to run cloud functions')
|
|
33
|
+
|
|
34
|
+
if not isinstance(app, fastapi.FastAPI):
|
|
35
|
+
raise TypeError('app is not an instance of FastAPI')
|
|
36
|
+
|
|
37
|
+
app_config = AppConfig.from_env()
|
|
38
|
+
|
|
39
|
+
if kill_existing_app_server:
|
|
40
|
+
# Shutdown the server gracefully if it was started by us.
|
|
41
|
+
# Since the uvicorn server doesn't start a new subprocess
|
|
42
|
+
# killing the process would result in kernel dying.
|
|
43
|
+
if _running_server is not None:
|
|
44
|
+
await _running_server.shutdown()
|
|
45
|
+
_running_server = None
|
|
46
|
+
|
|
47
|
+
# Kill if any other process is occupying the port
|
|
48
|
+
kill_process_by_port(app_config.listen_port)
|
|
49
|
+
|
|
50
|
+
# Add `GET /` route, used for liveness check
|
|
51
|
+
@app.get('/')
|
|
52
|
+
def ping() -> str:
|
|
53
|
+
return 'Success!'
|
|
54
|
+
|
|
55
|
+
app.root_path = app_config.base_path
|
|
56
|
+
|
|
57
|
+
config = uvicorn.Config(
|
|
58
|
+
app,
|
|
59
|
+
host='0.0.0.0',
|
|
60
|
+
port=app_config.listen_port,
|
|
61
|
+
log_level=log_level,
|
|
62
|
+
)
|
|
63
|
+
_running_server = AwaitableUvicornServer(config)
|
|
64
|
+
|
|
65
|
+
asyncio.create_task(_running_server.serve())
|
|
66
|
+
await _running_server.wait_for_startup()
|
|
67
|
+
|
|
68
|
+
connection_info = ConnectionInfo(app_config.base_url, app_config.token)
|
|
69
|
+
|
|
70
|
+
if app_config.running_interactively:
|
|
71
|
+
if app_config.is_gateway_enabled:
|
|
72
|
+
print(
|
|
73
|
+
'Cloud function available at '
|
|
74
|
+
f'{app_config.base_url}docs?authToken={app_config.token}',
|
|
75
|
+
)
|
|
76
|
+
else:
|
|
77
|
+
curl_header = f'-H "Authorization: Bearer {app_config.token}"'
|
|
78
|
+
curl_example = f'curl "{app_config.base_url}" {curl_header}'
|
|
79
|
+
print(
|
|
80
|
+
textwrap.dedent(f"""
|
|
81
|
+
Cloud function available at {app_config.base_url}
|
|
82
|
+
|
|
83
|
+
Auth Token: {app_config.token}
|
|
84
|
+
|
|
85
|
+
Curl example: {curl_example}
|
|
86
|
+
|
|
87
|
+
""").strip(),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
return connection_info
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class AppConfig:
|
|
8
|
+
listen_port: int
|
|
9
|
+
base_url: str
|
|
10
|
+
base_path: str
|
|
11
|
+
notebook_server_id: str
|
|
12
|
+
app_token: Optional[str]
|
|
13
|
+
user_token: Optional[str]
|
|
14
|
+
running_interactively: bool
|
|
15
|
+
is_gateway_enabled: bool
|
|
16
|
+
is_local_dev: bool
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def _read_variable(name: str) -> str:
|
|
20
|
+
value = os.environ.get(name)
|
|
21
|
+
if value is None:
|
|
22
|
+
raise RuntimeError(
|
|
23
|
+
f'Missing {name} environment variable. '
|
|
24
|
+
'Is the code running outside SingleStoreDB notebook environment?',
|
|
25
|
+
)
|
|
26
|
+
return value
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def from_env(cls) -> 'AppConfig':
|
|
30
|
+
port = cls._read_variable('SINGLESTOREDB_APP_LISTEN_PORT')
|
|
31
|
+
base_url = cls._read_variable('SINGLESTOREDB_APP_BASE_URL')
|
|
32
|
+
base_path = cls._read_variable('SINGLESTOREDB_APP_BASE_PATH')
|
|
33
|
+
notebook_server_id = cls._read_variable('SINGLESTOREDB_NOTEBOOK_SERVER_ID')
|
|
34
|
+
is_local_dev_env_var = cls._read_variable('SINGLESTOREDB_IS_LOCAL_DEV')
|
|
35
|
+
|
|
36
|
+
workload_type = os.environ.get('SINGLESTOREDB_WORKLOAD_TYPE')
|
|
37
|
+
running_interactively = workload_type == 'InteractiveNotebook'
|
|
38
|
+
|
|
39
|
+
is_gateway_enabled = 'SINGLESTOREDB_NOVA_GATEWAY_ENDPOINT' in os.environ
|
|
40
|
+
|
|
41
|
+
app_token = os.environ.get('SINGLESTOREDB_APP_TOKEN')
|
|
42
|
+
user_token = os.environ.get('SINGLESTOREDB_USER_TOKEN')
|
|
43
|
+
|
|
44
|
+
# Make sure the required variables are present
|
|
45
|
+
# and present useful error message if not
|
|
46
|
+
if running_interactively:
|
|
47
|
+
if is_gateway_enabled:
|
|
48
|
+
app_token = cls._read_variable('SINGLESTOREDB_APP_TOKEN')
|
|
49
|
+
else:
|
|
50
|
+
user_token = cls._read_variable('SINGLESTOREDB_USER_TOKEN')
|
|
51
|
+
|
|
52
|
+
return cls(
|
|
53
|
+
listen_port=int(port),
|
|
54
|
+
base_url=base_url,
|
|
55
|
+
base_path=base_path,
|
|
56
|
+
notebook_server_id=notebook_server_id,
|
|
57
|
+
app_token=app_token,
|
|
58
|
+
user_token=user_token,
|
|
59
|
+
running_interactively=running_interactively,
|
|
60
|
+
is_gateway_enabled=is_gateway_enabled,
|
|
61
|
+
is_local_dev=is_local_dev_env_var == 'true',
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def token(self) -> Optional[str]:
|
|
66
|
+
"""
|
|
67
|
+
Returns None if running non-interactively
|
|
68
|
+
"""
|
|
69
|
+
if self.is_gateway_enabled:
|
|
70
|
+
return self.app_token
|
|
71
|
+
else:
|
|
72
|
+
return self.user_token
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class ConnectionInfo:
|
|
9
|
+
url: str
|
|
10
|
+
|
|
11
|
+
# Only present in interactive mode
|
|
12
|
+
token: Optional[str]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class UdfConnectionInfo:
|
|
17
|
+
url: str
|
|
18
|
+
functions: Dict[str, Any]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from ._config import AppConfig
|
|
4
|
+
from ._process import kill_process_by_port
|
|
5
|
+
from ._stdout_supress import StdoutSuppressor
|
|
6
|
+
from singlestoredb.apps._connection_info import ConnectionInfo
|
|
7
|
+
|
|
8
|
+
if typing.TYPE_CHECKING:
|
|
9
|
+
from dash import Dash
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def run_dashboard_app(
|
|
13
|
+
app: 'Dash',
|
|
14
|
+
debug: bool = False,
|
|
15
|
+
kill_existing_app_server: bool = True,
|
|
16
|
+
) -> ConnectionInfo:
|
|
17
|
+
try:
|
|
18
|
+
from dash import Dash
|
|
19
|
+
except ImportError:
|
|
20
|
+
raise ImportError('package dash is required to run dashboards')
|
|
21
|
+
|
|
22
|
+
if not isinstance(app, Dash):
|
|
23
|
+
raise TypeError('app is not an instance of Dash App')
|
|
24
|
+
|
|
25
|
+
app_config = AppConfig.from_env()
|
|
26
|
+
|
|
27
|
+
if kill_existing_app_server:
|
|
28
|
+
kill_process_by_port(app_config.listen_port)
|
|
29
|
+
|
|
30
|
+
if app.config.requests_pathname_prefix is None or \
|
|
31
|
+
app.config.requests_pathname_prefix != app_config.base_path:
|
|
32
|
+
raise RuntimeError('''
|
|
33
|
+
requests_pathname_prefix of the Dash App is invalid. Please set
|
|
34
|
+
requests_pathname_prefix=os.environ['SINGLESTOREDB_APP_BASE_PATH']
|
|
35
|
+
while initializing the Dash App and retry''')
|
|
36
|
+
|
|
37
|
+
with StdoutSuppressor():
|
|
38
|
+
app.run(
|
|
39
|
+
host='0.0.0.0',
|
|
40
|
+
debug=debug,
|
|
41
|
+
port=str(app_config.listen_port),
|
|
42
|
+
jupyter_mode='external',
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if app_config.running_interactively:
|
|
46
|
+
print(f'Dash app available at {app_config.base_url}?authToken={app_config.token}')
|
|
47
|
+
return ConnectionInfo(app_config.base_url, app_config.token)
|