quillsql 2.2.7__tar.gz → 2.2.8__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.
- {quillsql-2.2.7/quillsql.egg-info → quillsql-2.2.8}/PKG-INFO +42 -1
- quillsql-2.2.8/README.md +87 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql/core.py +76 -1
- {quillsql-2.2.7 → quillsql-2.2.8/quillsql.egg-info}/PKG-INFO +43 -2
- {quillsql-2.2.7 → quillsql-2.2.8}/setup.py +1 -1
- quillsql-2.2.7/README.md +0 -46
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql/__init__.py +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql/assets/__init__.py +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql/assets/pgtypes.py +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql/db/__init__.py +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql/db/bigquery.py +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql/db/cached_connection.py +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql/db/db_helper.py +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql/db/postgres.py +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql/error.py +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql/utils/__init__.py +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql/utils/filters.py +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql/utils/pivot_template.py +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql/utils/post_quill_executor.py +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql/utils/run_query_processes.py +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql/utils/schema_conversion.py +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql/utils/tenants.py +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql.egg-info/SOURCES.txt +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql.egg-info/dependency_links.txt +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql.egg-info/requires.txt +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/quillsql.egg-info/top_level.txt +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/setup.cfg +0 -0
- {quillsql-2.2.7 → quillsql-2.2.8}/tests/test_core.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quillsql
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.8
|
|
4
4
|
Summary: Quill SDK for Python.
|
|
5
5
|
Home-page: https://github.com/quill-sql/quill-python
|
|
6
6
|
Author: Quill
|
|
@@ -67,3 +67,44 @@ async def quill_post(data: Request, user: dict = Depends(authenticate_jwt)):
|
|
|
67
67
|
|
|
68
68
|
Then you can run your app like normally. Pass in this route to our react library
|
|
69
69
|
on the frontend and you all set!
|
|
70
|
+
|
|
71
|
+
## Streaming
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from quillsql import Quill
|
|
75
|
+
from fastapi.responses import StreamingResponse
|
|
76
|
+
import asyncio
|
|
77
|
+
|
|
78
|
+
quill = Quill(
|
|
79
|
+
private_key=os.getenv("QULL_PRIVATE_KEY"),
|
|
80
|
+
database_connection_string=os.getenv("POSTGRES_READ"),
|
|
81
|
+
database_type="postgresql"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
@app.post("/quill-stream")
|
|
85
|
+
async def quill_post(data: Request, user: dict = Depends(authenticate_jwt)):
|
|
86
|
+
# assuming user fetched via auth middleware has an userId
|
|
87
|
+
user_id = user["user_id"]
|
|
88
|
+
body = await data.json()
|
|
89
|
+
metadata = body.get("metadata")
|
|
90
|
+
|
|
91
|
+
quill_stream = quill.stream(
|
|
92
|
+
tenants=[{"tenantField": "user_id", "tenantIds": [user_id]}],
|
|
93
|
+
metadata=metadata,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
async def event_generator():
|
|
97
|
+
# Full event types list: https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol#data-stream-protocol
|
|
98
|
+
async for event in quill_stream:
|
|
99
|
+
if event["type"] == "start":
|
|
100
|
+
pass
|
|
101
|
+
elif event["type"] == "text-delta":
|
|
102
|
+
yield event['delta']
|
|
103
|
+
elif event["type"] == "finish":
|
|
104
|
+
return
|
|
105
|
+
elif event["type"] == "error":
|
|
106
|
+
yield event['errorText']
|
|
107
|
+
await asyncio.sleep(0)
|
|
108
|
+
|
|
109
|
+
return StreamingResponse(event_generator(), media_type="text/event-stream")
|
|
110
|
+
```
|
quillsql-2.2.8/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Quill Python SDK
|
|
2
|
+
|
|
3
|
+
## Quickstart
|
|
4
|
+
|
|
5
|
+
First, install the quillsql package by running:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
$ pip install quillsql
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Then, add a `/quill` endpoint to your existing python server. For example, if
|
|
12
|
+
you were running a FASTAPI app, you would just add the endpoint like this:
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from quillsql import Quill
|
|
16
|
+
|
|
17
|
+
quill = Quill(
|
|
18
|
+
private_key=os.getenv("QULL_PRIVATE_KEY"),
|
|
19
|
+
database_connection_string=os.getenv("POSTGRES_READ"),
|
|
20
|
+
database_type="postgresql"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
security = HTTPBearer()
|
|
24
|
+
|
|
25
|
+
async def authenticate_jwt(token: str = Depends(security)):
|
|
26
|
+
# Your JWT validation logic here
|
|
27
|
+
# Return user object or raise HTTPException
|
|
28
|
+
user = validate_jwt_token(token.credentials)
|
|
29
|
+
return user
|
|
30
|
+
|
|
31
|
+
@app.post("/quill")
|
|
32
|
+
async def quill_post(data: Request, user: dict = Depends(authenticate_jwt)):
|
|
33
|
+
# assuming user fetched via auth middleware has an userId
|
|
34
|
+
user_id = user["user_id"]
|
|
35
|
+
body = await data.json()
|
|
36
|
+
metadata = body.get("metadata")
|
|
37
|
+
|
|
38
|
+
result = quill.query(
|
|
39
|
+
tenants=[{"tenantField": "user_id", "tenantIds": [user_id]}],
|
|
40
|
+
metadata=metadata
|
|
41
|
+
)
|
|
42
|
+
return result
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Then you can run your app like normally. Pass in this route to our react library
|
|
46
|
+
on the frontend and you all set!
|
|
47
|
+
|
|
48
|
+
## Streaming
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from quillsql import Quill
|
|
52
|
+
from fastapi.responses import StreamingResponse
|
|
53
|
+
import asyncio
|
|
54
|
+
|
|
55
|
+
quill = Quill(
|
|
56
|
+
private_key=os.getenv("QULL_PRIVATE_KEY"),
|
|
57
|
+
database_connection_string=os.getenv("POSTGRES_READ"),
|
|
58
|
+
database_type="postgresql"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
@app.post("/quill-stream")
|
|
62
|
+
async def quill_post(data: Request, user: dict = Depends(authenticate_jwt)):
|
|
63
|
+
# assuming user fetched via auth middleware has an userId
|
|
64
|
+
user_id = user["user_id"]
|
|
65
|
+
body = await data.json()
|
|
66
|
+
metadata = body.get("metadata")
|
|
67
|
+
|
|
68
|
+
quill_stream = quill.stream(
|
|
69
|
+
tenants=[{"tenantField": "user_id", "tenantIds": [user_id]}],
|
|
70
|
+
metadata=metadata,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
async def event_generator():
|
|
74
|
+
# Full event types list: https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol#data-stream-protocol
|
|
75
|
+
async for event in quill_stream:
|
|
76
|
+
if event["type"] == "start":
|
|
77
|
+
pass
|
|
78
|
+
elif event["type"] == "text-delta":
|
|
79
|
+
yield event['delta']
|
|
80
|
+
elif event["type"] == "finish":
|
|
81
|
+
return
|
|
82
|
+
elif event["type"] == "error":
|
|
83
|
+
yield event['errorText']
|
|
84
|
+
await asyncio.sleep(0)
|
|
85
|
+
|
|
86
|
+
return StreamingResponse(event_generator(), media_type="text/event-stream")
|
|
87
|
+
```
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import codecs
|
|
2
3
|
from dotenv import load_dotenv
|
|
3
4
|
|
|
4
5
|
import requests
|
|
@@ -29,7 +30,7 @@ load_dotenv()
|
|
|
29
30
|
|
|
30
31
|
ENV = os.getenv("PYTHON_ENV")
|
|
31
32
|
DEV_HOST = "http://localhost:8080"
|
|
32
|
-
PROD_HOST = "https://quill
|
|
33
|
+
PROD_HOST = "https://api.quill.co"
|
|
33
34
|
HOST = DEV_HOST if ENV == "development" else PROD_HOST
|
|
34
35
|
|
|
35
36
|
SINGLE_TENANT = "QUILL_SINGLE_TENANT"
|
|
@@ -386,6 +387,80 @@ class Quill:
|
|
|
386
387
|
"status": "error",
|
|
387
388
|
"data": responseMetadata,
|
|
388
389
|
}
|
|
390
|
+
|
|
391
|
+
async def stream(
|
|
392
|
+
self,
|
|
393
|
+
tenants,
|
|
394
|
+
metadata,
|
|
395
|
+
flags=None,
|
|
396
|
+
):
|
|
397
|
+
if not tenants:
|
|
398
|
+
raise ValueError("You may not pass an empty tenants array.")
|
|
399
|
+
|
|
400
|
+
if not metadata:
|
|
401
|
+
yield {"type": "error", "errorText": "Missing metadata."}
|
|
402
|
+
return
|
|
403
|
+
|
|
404
|
+
task = metadata.get("task")
|
|
405
|
+
if not task:
|
|
406
|
+
yield {"type": "error", "errorText": "Missing task."}
|
|
407
|
+
return
|
|
408
|
+
|
|
409
|
+
try:
|
|
410
|
+
# Set tenant IDs in the connection
|
|
411
|
+
self.target_connection.tenant_ids = extract_tenant_ids(tenants)
|
|
412
|
+
|
|
413
|
+
# Handle tenant flags synthesis
|
|
414
|
+
tenant_flags = None
|
|
415
|
+
if tenants[0] == SINGLE_TENANT and flags:
|
|
416
|
+
if flags and isinstance(flags[0], dict):
|
|
417
|
+
tenant_flags = [{'tenantField': SINGLE_TENANT, 'flags': flags}]
|
|
418
|
+
else:
|
|
419
|
+
tenant_flags = flags
|
|
420
|
+
|
|
421
|
+
payload = {
|
|
422
|
+
**metadata,
|
|
423
|
+
"tenants": tenants,
|
|
424
|
+
"flags": tenant_flags,
|
|
425
|
+
}
|
|
426
|
+
# Custom JSON Encoder to handle Enums
|
|
427
|
+
class EnumEncoder(json.JSONEncoder):
|
|
428
|
+
def default(self, obj):
|
|
429
|
+
if isinstance(obj, Enum):
|
|
430
|
+
return obj.value # Convert enum to its value (string in this case)
|
|
431
|
+
return super().default(obj)
|
|
432
|
+
url = f"{self.baseUrl}/sdk/{task}"
|
|
433
|
+
headers = {"Authorization": f"Bearer {self.private_key}", "Content-Type": "application/json","Accept": "text/event-stream"}
|
|
434
|
+
encoded = json.dumps(payload, cls=EnumEncoder)
|
|
435
|
+
|
|
436
|
+
resp = requests.post(url, data=encoded, headers=headers, stream=True)
|
|
437
|
+
decoder = codecs.getincrementaldecoder('utf-8')()
|
|
438
|
+
buf = ""
|
|
439
|
+
for chunk in resp.iter_content(chunk_size=4096):
|
|
440
|
+
buf += decoder.decode(chunk)
|
|
441
|
+
while "\n\n" in buf:
|
|
442
|
+
raw_event, buf = buf.split("\n\n", 1)
|
|
443
|
+
data_lines = []
|
|
444
|
+
for line in raw_event.splitlines():
|
|
445
|
+
if line.startswith("data:"):
|
|
446
|
+
data_lines.append(line[len("data:"):].strip())
|
|
447
|
+
if not data_lines:
|
|
448
|
+
continue
|
|
449
|
+
payload = "\n".join(data_lines)
|
|
450
|
+
if payload == "[DONE]":
|
|
451
|
+
break
|
|
452
|
+
yield json.loads(payload)
|
|
453
|
+
|
|
454
|
+
# flush any partial code points at the end
|
|
455
|
+
buf += decoder.decode(b"", final=True)
|
|
456
|
+
yield buf
|
|
457
|
+
return
|
|
458
|
+
except Exception as err:
|
|
459
|
+
yield {
|
|
460
|
+
"type": "error",
|
|
461
|
+
"errorText": str(err).splitlines()[0],
|
|
462
|
+
}
|
|
463
|
+
return
|
|
389
464
|
|
|
390
465
|
def apply_limit(self, query, limit):
|
|
391
466
|
# Simple logic: if query already has a limit, don't add another
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: quillsql
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.8
|
|
4
4
|
Summary: Quill SDK for Python.
|
|
5
5
|
Home-page: https://github.com/quill-sql/quill-python
|
|
6
6
|
Author: Quill
|
|
@@ -67,3 +67,44 @@ async def quill_post(data: Request, user: dict = Depends(authenticate_jwt)):
|
|
|
67
67
|
|
|
68
68
|
Then you can run your app like normally. Pass in this route to our react library
|
|
69
69
|
on the frontend and you all set!
|
|
70
|
+
|
|
71
|
+
## Streaming
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from quillsql import Quill
|
|
75
|
+
from fastapi.responses import StreamingResponse
|
|
76
|
+
import asyncio
|
|
77
|
+
|
|
78
|
+
quill = Quill(
|
|
79
|
+
private_key=os.getenv("QULL_PRIVATE_KEY"),
|
|
80
|
+
database_connection_string=os.getenv("POSTGRES_READ"),
|
|
81
|
+
database_type="postgresql"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
@app.post("/quill-stream")
|
|
85
|
+
async def quill_post(data: Request, user: dict = Depends(authenticate_jwt)):
|
|
86
|
+
# assuming user fetched via auth middleware has an userId
|
|
87
|
+
user_id = user["user_id"]
|
|
88
|
+
body = await data.json()
|
|
89
|
+
metadata = body.get("metadata")
|
|
90
|
+
|
|
91
|
+
quill_stream = quill.stream(
|
|
92
|
+
tenants=[{"tenantField": "user_id", "tenantIds": [user_id]}],
|
|
93
|
+
metadata=metadata,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
async def event_generator():
|
|
97
|
+
# Full event types list: https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol#data-stream-protocol
|
|
98
|
+
async for event in quill_stream:
|
|
99
|
+
if event["type"] == "start":
|
|
100
|
+
pass
|
|
101
|
+
elif event["type"] == "text-delta":
|
|
102
|
+
yield event['delta']
|
|
103
|
+
elif event["type"] == "finish":
|
|
104
|
+
return
|
|
105
|
+
elif event["type"] == "error":
|
|
106
|
+
yield event['errorText']
|
|
107
|
+
await asyncio.sleep(0)
|
|
108
|
+
|
|
109
|
+
return StreamingResponse(event_generator(), media_type="text/event-stream")
|
|
110
|
+
```
|
quillsql-2.2.7/README.md
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# Quill Python SDK
|
|
2
|
-
|
|
3
|
-
## Quickstart
|
|
4
|
-
|
|
5
|
-
First, install the quillsql package by running:
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
$ pip install quillsql
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
Then, add a `/quill` endpoint to your existing python server. For example, if
|
|
12
|
-
you were running a FASTAPI app, you would just add the endpoint like this:
|
|
13
|
-
|
|
14
|
-
```python
|
|
15
|
-
from quillsql import Quill
|
|
16
|
-
|
|
17
|
-
quill = Quill(
|
|
18
|
-
private_key=os.getenv("QULL_PRIVATE_KEY"),
|
|
19
|
-
database_connection_string=os.getenv("POSTGRES_READ"),
|
|
20
|
-
database_type="postgresql"
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
security = HTTPBearer()
|
|
24
|
-
|
|
25
|
-
async def authenticate_jwt(token: str = Depends(security)):
|
|
26
|
-
# Your JWT validation logic here
|
|
27
|
-
# Return user object or raise HTTPException
|
|
28
|
-
user = validate_jwt_token(token.credentials)
|
|
29
|
-
return user
|
|
30
|
-
|
|
31
|
-
@app.post("/quill")
|
|
32
|
-
async def quill_post(data: Request, user: dict = Depends(authenticate_jwt)):
|
|
33
|
-
# assuming user fetched via auth middleware has an userId
|
|
34
|
-
user_id = user["user_id"]
|
|
35
|
-
body = await data.json()
|
|
36
|
-
metadata = body.get("metadata")
|
|
37
|
-
|
|
38
|
-
result = quill.query(
|
|
39
|
-
tenants=[{"tenantField": "user_id", "tenantIds": [user_id]}],
|
|
40
|
-
metadata=metadata
|
|
41
|
-
)
|
|
42
|
-
return result
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Then you can run your app like normally. Pass in this route to our react library
|
|
46
|
-
on the frontend and you all set!
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|