sqlite-jev 0.1.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.
@@ -0,0 +1,110 @@
1
+ name: Release
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ build:
12
+ name: ${{ matrix.platform }}
13
+ runs-on: ${{ matrix.runner }}
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ include:
18
+ - runner: ubuntu-24.04
19
+ platform: linux-x86_64
20
+ - runner: ubuntu-24.04-arm
21
+ platform: linux-aarch64
22
+ - runner: macos-15-intel
23
+ platform: macos-x86_64
24
+ - runner: macos-15
25
+ platform: macos-arm64
26
+ steps:
27
+ - uses: actions/checkout@v7
28
+ - name: Verify tag matches package version
29
+ shell: bash
30
+ env:
31
+ RELEASE_TAG: ${{ github.event.release.tag_name }}
32
+ run: test "${RELEASE_TAG#v}" = "$(python3 -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')"
33
+ - name: Install Linux dependencies
34
+ if: runner.os == 'Linux'
35
+ run: sudo apt-get update && sudo apt-get install -y sqlite3 libsqlite3-dev curl
36
+ - name: Install macOS dependencies
37
+ if: runner.os == 'macOS'
38
+ run: |
39
+ brew install sqlite
40
+ echo "$(brew --prefix sqlite)/bin" >> "$GITHUB_PATH"
41
+ echo "CPPFLAGS=-I$(brew --prefix sqlite)/include" >> "$GITHUB_ENV"
42
+ - run: make test
43
+ - name: Build native extension archive
44
+ shell: bash
45
+ env:
46
+ RELEASE_TAG: ${{ github.event.release.tag_name }}
47
+ run: make package PACKAGE_NAME="sqlite-jev-${RELEASE_TAG#v}-${{ matrix.platform }}"
48
+ - name: Build and test Python wheel
49
+ uses: pypa/cibuildwheel@v3.3.0
50
+ env:
51
+ CIBW_ARCHS: native
52
+ - uses: actions/upload-artifact@v7
53
+ with:
54
+ name: native-extension-${{ matrix.platform }}
55
+ path: build/*.tar.gz
56
+ if-no-files-found: error
57
+ - uses: actions/upload-artifact@v7
58
+ with:
59
+ name: python-wheel-${{ matrix.platform }}
60
+ path: wheelhouse/*.whl
61
+ if-no-files-found: error
62
+
63
+ sdist:
64
+ name: Build source distribution
65
+ runs-on: ubuntu-latest
66
+ steps:
67
+ - uses: actions/checkout@v7
68
+ - uses: astral-sh/setup-uv@v10.1.0
69
+ - run: uv build --sdist
70
+ - uses: actions/upload-artifact@v7
71
+ with:
72
+ name: python-sdist
73
+ path: dist/*.tar.gz
74
+ if-no-files-found: error
75
+
76
+ release-assets:
77
+ name: Upload GitHub release assets
78
+ needs: [build, sdist]
79
+ runs-on: ubuntu-24.04
80
+ permissions:
81
+ contents: write
82
+ steps:
83
+ - uses: actions/download-artifact@v8
84
+ with:
85
+ path: dist
86
+ merge-multiple: true
87
+ - name: Write checksums
88
+ working-directory: dist
89
+ run: sha256sum *.tar.gz *.whl > SHA256SUMS
90
+ - uses: softprops/action-gh-release@v3
91
+ with:
92
+ tag_name: ${{ github.event.release.tag_name }}
93
+ files: dist/*
94
+
95
+ pypi-publish:
96
+ name: Upload release to PyPI
97
+ needs: [build, sdist, release-assets]
98
+ runs-on: ubuntu-latest
99
+ permissions:
100
+ id-token: write
101
+ contents: read
102
+ steps:
103
+ - uses: astral-sh/setup-uv@v10.1.0
104
+ - uses: actions/download-artifact@v8
105
+ with:
106
+ pattern: python-*
107
+ path: dist
108
+ merge-multiple: true
109
+ - name: Publish with PyPI trusted publishing
110
+ run: uv publish --trusted-publishing always dist/*
@@ -0,0 +1,42 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ name: ${{ matrix.name }}
14
+ runs-on: ${{ matrix.runner }}
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ include:
19
+ - name: Linux x86_64
20
+ runner: ubuntu-24.04
21
+ - name: Linux arm64
22
+ runner: ubuntu-24.04-arm
23
+ - name: macOS Intel
24
+ runner: macos-15-intel
25
+ - name: macOS Apple Silicon
26
+ runner: macos-15
27
+ steps:
28
+ - uses: actions/checkout@v7
29
+ - name: Install Linux dependencies
30
+ if: runner.os == 'Linux'
31
+ run: sudo apt-get update && sudo apt-get install -y sqlite3 libsqlite3-dev curl
32
+ - name: Install macOS dependencies
33
+ if: runner.os == 'macOS'
34
+ run: |
35
+ brew install sqlite
36
+ echo "$(brew --prefix sqlite)/bin" >> "$GITHUB_PATH"
37
+ echo "CPPFLAGS=-I$(brew --prefix sqlite)/include" >> "$GITHUB_ENV"
38
+ - run: make test
39
+ - name: Build and test Python wheel
40
+ uses: pypa/cibuildwheel@v3.3.0
41
+ env:
42
+ CIBW_ARCHS: native
@@ -0,0 +1,24 @@
1
+ name: TypeSafe integration
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ schedule:
6
+ # January, March, May, July, September, and November.
7
+ - cron: "17 9 1 */2 *"
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ live-smoke:
14
+ name: Live API smoke test
15
+ runs-on: ubuntu-24.04
16
+ env:
17
+ TYPESAFE_API_KEY: ${{ secrets.TYPESAFE_API_KEY }}
18
+ steps:
19
+ - uses: actions/checkout@v7
20
+ - name: Require integration secret
21
+ run: test -n "$TYPESAFE_API_KEY"
22
+ - name: Install dependencies
23
+ run: sudo apt-get update && sudo apt-get install -y sqlite3 libsqlite3-dev curl
24
+ - run: make integration-test
@@ -0,0 +1,10 @@
1
+ .env
2
+ build/
3
+ dist/
4
+ wheelhouse/
5
+ pg-jev/
6
+ *.egg-info/
7
+ .venv/
8
+ __pycache__/
9
+ *.db
10
+ *.so
@@ -0,0 +1,28 @@
1
+ cmake_minimum_required(VERSION 3.15)
2
+ project(sqlite_jev VERSION 0.1.0 LANGUAGES C)
3
+
4
+ add_library(jev MODULE src/jev.c)
5
+ set_target_properties(jev PROPERTIES
6
+ C_STANDARD 11
7
+ C_STANDARD_REQUIRED YES
8
+ PREFIX ""
9
+ )
10
+ target_compile_definitions(jev PRIVATE JEV_VERSION="${PROJECT_VERSION}")
11
+
12
+ if(APPLE)
13
+ set_target_properties(jev PROPERTIES SUFFIX ".dylib")
14
+ target_link_options(jev PRIVATE "-undefined" "dynamic_lookup")
15
+ elseif(UNIX)
16
+ target_link_libraries(jev PRIVATE ${CMAKE_DL_LIBS})
17
+ endif()
18
+
19
+ if(MSVC)
20
+ target_compile_options(jev PRIVATE /W4)
21
+ else()
22
+ target_compile_options(jev PRIVATE -Wall -Wextra -Wpedantic)
23
+ endif()
24
+
25
+ install(TARGETS jev
26
+ LIBRARY DESTINATION sqlite_jev
27
+ RUNTIME DESTINATION sqlite_jev
28
+ )
@@ -0,0 +1,64 @@
1
+ CC ?= cc
2
+ CFLAGS ?= -O2 -g
3
+ CFLAGS += -fPIC -std=c11 -Wall -Wextra -Wpedantic
4
+ CPPFLAGS ?=
5
+ LDFLAGS ?=
6
+
7
+ VERSION := $(shell sed -n 's/^version = "\([^"]*\)"/\1/p' pyproject.toml | head -1)
8
+ CPPFLAGS += -DJEV_VERSION='"$(VERSION)"'
9
+ BUILD_DIR := build
10
+ UNAME_S := $(shell uname -s)
11
+ UNAME_M := $(shell uname -m)
12
+
13
+ ifeq ($(UNAME_S),Darwin)
14
+ EXT_SUFFIX := dylib
15
+ SHARED_FLAGS := -dynamiclib -undefined dynamic_lookup
16
+ DL_LIBS :=
17
+ PLATFORM := macos
18
+ else
19
+ EXT_SUFFIX := so
20
+ SHARED_FLAGS := -shared
21
+ DL_LIBS := -ldl
22
+ PLATFORM := linux
23
+ endif
24
+
25
+ EXTENSION := $(BUILD_DIR)/jev.$(EXT_SUFFIX)
26
+ PACKAGE_NAME ?= sqlite-jev-$(VERSION)-$(PLATFORM)-$(UNAME_M)
27
+ PACKAGE_DIR := $(BUILD_DIR)/$(PACKAGE_NAME)
28
+
29
+ .PHONY: all clean test integration-test live-test package wheel wheel-test
30
+
31
+ all: $(EXTENSION)
32
+
33
+ $(EXTENSION): src/jev.c | $(BUILD_DIR)
34
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(SHARED_FLAGS) -o $@ $< $(LDFLAGS) $(DL_LIBS)
35
+
36
+ $(BUILD_DIR):
37
+ mkdir -p $@
38
+
39
+ test: $(EXTENSION)
40
+ ./test/run.sh
41
+
42
+ live-test: $(EXTENSION)
43
+ ./examples/run_ticket_triage.sh
44
+
45
+ integration-test: $(EXTENSION)
46
+ ./test/run_live.sh
47
+
48
+ package: $(EXTENSION)
49
+ rm -rf $(PACKAGE_DIR)
50
+ mkdir -p $(PACKAGE_DIR)
51
+ cp $(EXTENSION) $(PACKAGE_DIR)/
52
+ cp README.md pyproject.toml $(PACKAGE_DIR)/
53
+ tar -czf $(BUILD_DIR)/$(PACKAGE_NAME).tar.gz -C $(BUILD_DIR) $(PACKAGE_NAME)
54
+ @echo "Created $(BUILD_DIR)/$(PACKAGE_NAME).tar.gz"
55
+
56
+ wheel:
57
+ rm -rf dist
58
+ uv build --wheel
59
+
60
+ wheel-test: wheel
61
+ ./scripts/test_wheel.sh "$$(find dist -maxdepth 1 -name '*.whl' -print -quit)"
62
+
63
+ clean:
64
+ rm -rf $(BUILD_DIR)
@@ -0,0 +1,259 @@
1
+ Metadata-Version: 2.2
2
+ Name: sqlite-jev
3
+ Version: 0.1.0
4
+ Summary: Batched natural-language judgments for SQLite, powered by TypeSafe Jev
5
+ Classifier: Programming Language :: Python :: 3
6
+ Classifier: Programming Language :: SQL
7
+ Classifier: Operating System :: MacOS
8
+ Classifier: Operating System :: POSIX :: Linux
9
+ Classifier: Topic :: Database
10
+ Project-URL: Source, https://github.com/mgaitan/sqlite-jev
11
+ Project-URL: Issues, https://github.com/mgaitan/sqlite-jev/issues
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+
15
+ # sqlite-jev
16
+
17
+ Natural-language predicates, classification, and scoring for SQLite, powered by
18
+ [TypeSafe Jev](https://docs.typesafe.ai). It is inspired by
19
+ [`pg-jev`](https://github.com/realZachi/pg-jev), but shaped around SQLite's loadable-extension
20
+ and virtual-table APIs.
21
+
22
+ ```sql
23
+ .load ./build/jev
24
+
25
+ SELECT t.id, t.subject, round(j.probability, 3) AS urgency
26
+ FROM jev_rows(
27
+ 'tickets',
28
+ 'The customer explicitly expresses urgency or says work is blocked',
29
+ 'noul',
30
+ NULL,
31
+ json_array('subject', 'message')
32
+ ) AS j
33
+ JOIN tickets AS t ON t.rowid = j.source_rowid
34
+ WHERE j.probability >= 0.6;
35
+ ```
36
+
37
+ `jev_rows` reads the selected columns, puts up to 40 rows in one shared state, asks one
38
+ question per row, and returns a virtual table that can be joined to the source by `rowid`.
39
+ Results are cached for the lifetime of the SQLite connection.
40
+
41
+ ## Build
42
+
43
+ Requirements:
44
+
45
+ - SQLite 3.45 or newer, built with JSON support and loadable extensions
46
+ - A C11 compiler and `make`
47
+ - The `libcurl` runtime (`libcurl.so.4` on Linux or `libcurl.4.dylib` on macOS)
48
+ - A TypeSafe API key
49
+
50
+ The current development machine already has all of these; no extra system package is needed.
51
+
52
+ ```bash
53
+ make
54
+ export TYPESAFE_API_KEY=...
55
+ sqlite3 my.db
56
+ ```
57
+
58
+ Inside SQLite:
59
+
60
+ ```sql
61
+ .load ./build/jev
62
+ SELECT jev_version();
63
+ ```
64
+
65
+ `TYPESAFE_API_KEY` is the only environment variable used for the API key.
66
+
67
+ ## Batched table queries
68
+
69
+ The signature is:
70
+
71
+ ```sql
72
+ jev_rows(table_name, question [, kind [, criteria [, columns]]])
73
+ ```
74
+
75
+ It returns `source_rowid`, `answer`, `probability`, `choice`, `score`, and `confidence`.
76
+ Columns that do not apply to the chosen primitive are `NULL`.
77
+
78
+ ### Boolean judgment (Noul)
79
+
80
+ ```sql
81
+ SELECT
82
+ t.id,
83
+ j.probability AS yes_probability,
84
+ 1.0 - j.probability AS no_probability,
85
+ CASE WHEN j.probability >= 0.7 THEN 'yes' ELSE 'no' END AS answer
86
+ FROM jev_rows(
87
+ 'tickets',
88
+ 'The ticket explicitly asks for a refund',
89
+ 'noul',
90
+ NULL,
91
+ json_array('subject', 'message')
92
+ ) AS j
93
+ JOIN tickets AS t ON t.rowid = j.source_rowid
94
+ ORDER BY t.id;
95
+ ```
96
+
97
+ A Noul returns the probability of `yes`; `no` is its complement. Choosing the larger one is
98
+ equivalent to a `0.5` threshold. Keep the threshold in SQL so it can rise with the cost of a
99
+ false positive. The scalar `jev(state, condition)` uses `0.5` unless given a third argument.
100
+
101
+ ### Classification (Choice)
102
+
103
+ ```sql
104
+ SELECT t.id, j.choice AS team, j.confidence
105
+ FROM jev_rows(
106
+ 'tickets',
107
+ 'Which team should handle the main request?',
108
+ 'choice',
109
+ json_object(
110
+ 'billing', 'Charges, invoices, payment methods, or refunds',
111
+ 'technical', 'Bugs, failures, or integrations',
112
+ 'sales', 'Pricing, plans, demos, or new purchases',
113
+ 'other', 'Anything outside those teams'
114
+ ),
115
+ json_array('subject', 'message')
116
+ ) AS j
117
+ JOIN tickets AS t ON t.rowid = j.source_rowid;
118
+ ```
119
+
120
+ ### Ordered rating (Score)
121
+
122
+ ```sql
123
+ SELECT t.id, j.score, j.confidence
124
+ FROM jev_rows(
125
+ 'tickets',
126
+ 'How frustrated is the customer?',
127
+ 'score',
128
+ json_array('Calm and factual', 'Frustrated but civil', 'Very angry or abusive'),
129
+ json_array('message')
130
+ ) AS j
131
+ JOIN tickets AS t ON t.rowid = j.source_rowid;
132
+ ```
133
+
134
+ For accuracy and cost, include only the columns the judgment needs. `jev_rows` requires a
135
+ rowid table. To prefilter a large data set, materialize the filtered rows into a temporary
136
+ table and evaluate that table.
137
+
138
+ ## Scalar functions
139
+
140
+ Scalar functions are convenient for one record. When scanning a table, prefer `jev_rows` so
141
+ Jev can evaluate many questions in a single request.
142
+
143
+ | Function | Result |
144
+ | --- | --- |
145
+ | `jev(state, condition [, threshold])` | Boolean Noul predicate; default threshold is `0.5` |
146
+ | `jev_prob(state, condition)` | Noul probability from 0 to 1 |
147
+ | `jev_choice(state, question, criteria_json)` | Most likely Choice key |
148
+ | `jev_score(state, question, levels_json)` | Probability-weighted Score level |
149
+ | `jev_score_norm(state, question, levels_json)` | Score normalized to 0 through 1 |
150
+ | `jev_confidence(state, question, kind, criteria_json)` | Choice or Score confidence |
151
+ | `jev_eval(state, question [, kind [, criteria_json]])` | Full answer JSON |
152
+ | `jev_stats()` | Connection-local usage and cache statistics |
153
+ | `jev_cache_clear()` | Clears the connection-local answer cache |
154
+ | `jev_version()` | Extension version |
155
+
156
+ Pass structured state with SQLite JSON functions:
157
+
158
+ ```sql
159
+ SELECT jev_prob(
160
+ json_object('subject', subject, 'message', message),
161
+ 'The customer explicitly expresses urgency'
162
+ )
163
+ FROM tickets
164
+ WHERE id = 42;
165
+ ```
166
+
167
+ ## Configuration
168
+
169
+ Configuration is connection-local:
170
+
171
+ ```sql
172
+ SELECT jev_config('model', 'jev-latest');
173
+ SELECT jev_config('batch_size', 40);
174
+ SELECT jev_config('max_rows', 500);
175
+ SELECT jev_config('timeout', 90);
176
+ SELECT jev_config('api_url', 'https://api.typesafe.ai/v1/systemone');
177
+ SELECT jev_config('api_key', '...');
178
+ ```
179
+
180
+ `max_rows` is a spend guard. A `jev_rows` scan above the limit fails before sending any data.
181
+ The API key is never returned by `jev_config`; it reports only `set` or `unset`.
182
+
183
+ ## Python
184
+
185
+ Add the platform package to a project and load it with the Python wrapper:
186
+
187
+ ```bash
188
+ uv add sqlite-jev
189
+ ```
190
+
191
+ ```python
192
+ import sqlite3
193
+ import sqlite_jev
194
+
195
+ connection = sqlite_jev.load(sqlite3.connect(":memory:"))
196
+ ```
197
+
198
+ The package contains the same native extension as the standalone release, has no runtime
199
+ Python dependencies, and supports maintained Python versions starting with Python 3.10. The
200
+ interpreter's `sqlite3` module must have loadable-extension support enabled.
201
+
202
+ To load a standalone build manually instead:
203
+
204
+ ```python
205
+ import sqlite3
206
+
207
+ connection = sqlite3.connect(":memory:")
208
+ connection.enable_load_extension(True)
209
+ connection.load_extension("/path/to/jev.so") # use jev.dylib on macOS
210
+ connection.enable_load_extension(False)
211
+
212
+ version = connection.execute("select jev_version()").fetchone()[0]
213
+ ```
214
+
215
+ ## Demo and tests
216
+
217
+ The deterministic suite uses a local mock server and never calls TypeSafe:
218
+
219
+ ```bash
220
+ make test
221
+ ```
222
+
223
+ The ticket-triage demo makes two live API requests, one for urgency and one for routing:
224
+
225
+ ```bash
226
+ make live-test
227
+ ```
228
+
229
+ See [`examples/ticket_triage.sql`](examples/ticket_triage.sql) for the complete query.
230
+
231
+ `make integration-test` runs a smaller live smoke test. In GitHub Actions it runs once every
232
+ two months and on manual dispatch using the `TYPESAFE_API_KEY` repository secret. It checks the API contract,
233
+ batching, and answer shapes; semantic expectations remain in the deterministic mock suite so
234
+ normal model variation cannot make pull requests flaky.
235
+
236
+ ## Releases
237
+
238
+ Tags named `vX.Y.Z` build and publish four archives through GitHub Actions:
239
+
240
+ - Linux x86_64 and arm64
241
+ - macOS Intel and Apple Silicon
242
+
243
+ Each archive contains the native extension, this README, and its `pyproject.toml`. The release
244
+ also includes a `py3-none-<platform>` Python wheel and `SHA256SUMS`. CI installs each wheel
245
+ with `cibuildwheel` and verifies that `sqlite_jev.load()` enables the SQL API. The same tag
246
+ publishes the wheels and source distribution to PyPI through trusted publishing.
247
+
248
+ ## Important limits
249
+
250
+ - Row contents are sent to TypeSafe. Do not evaluate data you are not allowed to share.
251
+ - This is a semantic full scan, not an index. Apply deterministic SQLite filters first and
252
+ materialize a small candidate table.
253
+ - Cache entries live only for the current database connection and are keyed by row content,
254
+ model, question, primitive, and criteria.
255
+ - Jev should make narrow judgments. Keep counting, arithmetic, and date comparison in SQL.
256
+ - Text stored in a row can steer model behavior. Test adversarial content and use conservative
257
+ probability or confidence thresholds before automating consequential actions.
258
+ - `libcurl` is loaded dynamically so building does not require the curl development headers.
259
+ Linux and macOS are the currently tested platforms.