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.
- sqlite_jev-0.1.0/.github/workflows/cd.yml +110 -0
- sqlite_jev-0.1.0/.github/workflows/ci.yml +42 -0
- sqlite_jev-0.1.0/.github/workflows/integration.yml +24 -0
- sqlite_jev-0.1.0/.gitignore +10 -0
- sqlite_jev-0.1.0/CMakeLists.txt +28 -0
- sqlite_jev-0.1.0/Makefile +64 -0
- sqlite_jev-0.1.0/PKG-INFO +259 -0
- sqlite_jev-0.1.0/README.md +245 -0
- sqlite_jev-0.1.0/examples/run_ticket_triage.sh +17 -0
- sqlite_jev-0.1.0/examples/ticket_triage.sql +69 -0
- sqlite_jev-0.1.0/pyproject.toml +33 -0
- sqlite_jev-0.1.0/python/sqlite_jev/__init__.py +31 -0
- sqlite_jev-0.1.0/scripts/test_wheel.sh +16 -0
- sqlite_jev-0.1.0/src/jev.c +1258 -0
- sqlite_jev-0.1.0/test/expected.out +17 -0
- sqlite_jev-0.1.0/test/live_smoke.sql +36 -0
- sqlite_jev-0.1.0/test/mock_api.py +110 -0
- sqlite_jev-0.1.0/test/python_package.py +34 -0
- sqlite_jev-0.1.0/test/run.sh +18 -0
- sqlite_jev-0.1.0/test/run_live.sh +27 -0
- sqlite_jev-0.1.0/test/test.sql +68 -0
|
@@ -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,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.
|