anysql-sdk 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.
- anysql_sdk-0.1.0/.github/workflows/pr-check.yml +80 -0
- anysql_sdk-0.1.0/.github/workflows/release.yml +94 -0
- anysql_sdk-0.1.0/.gitignore +16 -0
- anysql_sdk-0.1.0/PKG-INFO +345 -0
- anysql_sdk-0.1.0/README.md +308 -0
- anysql_sdk-0.1.0/anysql/__init__.py +82 -0
- anysql_sdk-0.1.0/anysql/adapters/__init__.py +0 -0
- anysql_sdk-0.1.0/anysql/adapters/claude.py +103 -0
- anysql_sdk-0.1.0/anysql/adapters/generic.py +45 -0
- anysql_sdk-0.1.0/anysql/adapters/openai.py +108 -0
- anysql_sdk-0.1.0/anysql/cli.py +48 -0
- anysql_sdk-0.1.0/anysql/context.py +131 -0
- anysql_sdk-0.1.0/anysql/engine.py +386 -0
- anysql_sdk-0.1.0/anysql/schema.py +122 -0
- anysql_sdk-0.1.0/anysql/storage.py +73 -0
- anysql_sdk-0.1.0/anysql/tracers/__init__.py +0 -0
- anysql_sdk-0.1.0/anysql/tracers/agent.py +131 -0
- anysql_sdk-0.1.0/anysql/tracers/rag.py +116 -0
- anysql_sdk-0.1.0/docs/ANYSQL_CLAUDE_CODE.md +1648 -0
- anysql_sdk-0.1.0/docs/QUERIES.md +85 -0
- anysql_sdk-0.1.0/docs/plans/2026-03-09-anysql-implementation.md +2133 -0
- anysql_sdk-0.1.0/docs/plans/2026-03-09-anysql-v0.1.0-design.md +184 -0
- anysql_sdk-0.1.0/docs/plans/2026-03-09-pypi-cicd-design.md +68 -0
- anysql_sdk-0.1.0/docs/plans/2026-03-09-pypi-cicd-implementation.md +231 -0
- anysql_sdk-0.1.0/examples/realtime_claude_demo.py +152 -0
- anysql_sdk-0.1.0/examples/realtime_combined_demo.py +247 -0
- anysql_sdk-0.1.0/examples/realtime_openai_demo.py +158 -0
- anysql_sdk-0.1.0/pyproject.toml +52 -0
- anysql_sdk-0.1.0/tests/__init__.py +0 -0
- anysql_sdk-0.1.0/tests/test_adapters.py +199 -0
- anysql_sdk-0.1.0/tests/test_engine.py +239 -0
- anysql_sdk-0.1.0/tests/test_schema.py +91 -0
- anysql_sdk-0.1.0/tests/test_storage.py +95 -0
- anysql_sdk-0.1.0/tests/test_tracers.py +208 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# .github/workflows/pr-check.yml
|
|
2
|
+
name: PR Checks
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
pull_request:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
name: Tests (Python ${{ matrix.python-version }})
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
strategy:
|
|
16
|
+
fail-fast: false
|
|
17
|
+
matrix:
|
|
18
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: pip install -e ".[dev]"
|
|
26
|
+
- name: Run tests
|
|
27
|
+
run: pytest tests/ -v --tb=short
|
|
28
|
+
|
|
29
|
+
lint:
|
|
30
|
+
name: Lint
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
- uses: actions/setup-python@v5
|
|
35
|
+
with:
|
|
36
|
+
python-version: "3.12"
|
|
37
|
+
- name: Install dependencies
|
|
38
|
+
run: pip install -e ".[dev]"
|
|
39
|
+
- name: Check lint
|
|
40
|
+
run: ruff check anysql/
|
|
41
|
+
- name: Check format
|
|
42
|
+
run: ruff format --check anysql/
|
|
43
|
+
|
|
44
|
+
version-check:
|
|
45
|
+
name: Version Bump Check
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v4
|
|
49
|
+
with:
|
|
50
|
+
fetch-depth: 0
|
|
51
|
+
- uses: actions/setup-python@v5
|
|
52
|
+
with:
|
|
53
|
+
python-version: "3.12"
|
|
54
|
+
- name: Check version was bumped
|
|
55
|
+
run: |
|
|
56
|
+
MAIN_VERSION=$(git show origin/main:pyproject.toml | python3 -c "
|
|
57
|
+
import sys, re
|
|
58
|
+
content = sys.stdin.read()
|
|
59
|
+
match = re.search(r'^version\s*=\s*\"([^\"]+)\"', content, re.MULTILINE)
|
|
60
|
+
if not match:
|
|
61
|
+
print('ERROR: could not find version in origin/main:pyproject.toml', file=sys.stderr)
|
|
62
|
+
sys.exit(1)
|
|
63
|
+
print(match.group(1))
|
|
64
|
+
")
|
|
65
|
+
PR_VERSION=$(python3 -c "
|
|
66
|
+
import re, sys
|
|
67
|
+
content = open('pyproject.toml').read()
|
|
68
|
+
match = re.search(r'^version\s*=\s*\"([^\"]+)\"', content, re.MULTILINE)
|
|
69
|
+
if not match:
|
|
70
|
+
print('ERROR: could not find version in pyproject.toml', file=sys.stderr)
|
|
71
|
+
sys.exit(1)
|
|
72
|
+
print(match.group(1))
|
|
73
|
+
")
|
|
74
|
+
echo "main version: $MAIN_VERSION"
|
|
75
|
+
echo "PR version: $PR_VERSION"
|
|
76
|
+
if [ "$MAIN_VERSION" = "$PR_VERSION" ]; then
|
|
77
|
+
echo "ERROR: Version $PR_VERSION already exists on main — bump the version in pyproject.toml"
|
|
78
|
+
exit 1
|
|
79
|
+
fi
|
|
80
|
+
echo "OK: version bumped $MAIN_VERSION → $PR_VERSION"
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# .github/workflows/release.yml
|
|
2
|
+
name: Release
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
check-version:
|
|
13
|
+
name: Check version is new
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
outputs:
|
|
16
|
+
is_new: ${{ steps.check.outputs.is_new }}
|
|
17
|
+
version: ${{ steps.check.outputs.version }}
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
with:
|
|
21
|
+
fetch-depth: 0 # required: git tag -l needs full tag history
|
|
22
|
+
- name: Check if version tag exists
|
|
23
|
+
id: check
|
|
24
|
+
run: |
|
|
25
|
+
VERSION=$(python3 -c "
|
|
26
|
+
import re, sys
|
|
27
|
+
content = open('pyproject.toml').read()
|
|
28
|
+
match = re.search(r'^version\s*=\s*\"([^\"]+)\"', content, re.MULTILINE)
|
|
29
|
+
if not match:
|
|
30
|
+
print('ERROR: could not find version in pyproject.toml', file=sys.stderr)
|
|
31
|
+
sys.exit(1)
|
|
32
|
+
print(match.group(1))
|
|
33
|
+
")
|
|
34
|
+
echo "Detected version: $VERSION"
|
|
35
|
+
if git tag -l "v$VERSION" | grep -q "v$VERSION"; then
|
|
36
|
+
echo "Version v$VERSION already tagged — skipping release"
|
|
37
|
+
echo "is_new=false" >> $GITHUB_OUTPUT
|
|
38
|
+
else
|
|
39
|
+
echo "Version v$VERSION is new — proceeding with release"
|
|
40
|
+
echo "is_new=true" >> $GITHUB_OUTPUT
|
|
41
|
+
fi
|
|
42
|
+
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
43
|
+
|
|
44
|
+
build:
|
|
45
|
+
name: Build distribution
|
|
46
|
+
needs: check-version
|
|
47
|
+
if: needs.check-version.outputs.is_new == 'true'
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@v4
|
|
51
|
+
- uses: actions/setup-python@v5
|
|
52
|
+
with:
|
|
53
|
+
python-version: "3.12"
|
|
54
|
+
- name: Install build tools
|
|
55
|
+
run: pip install hatchling build
|
|
56
|
+
- name: Build wheel and sdist
|
|
57
|
+
run: python -m build
|
|
58
|
+
- name: Upload dist artifacts
|
|
59
|
+
uses: actions/upload-artifact@v4
|
|
60
|
+
with:
|
|
61
|
+
name: dist
|
|
62
|
+
path: dist/
|
|
63
|
+
|
|
64
|
+
publish:
|
|
65
|
+
name: Publish to PyPI
|
|
66
|
+
needs: [build, check-version]
|
|
67
|
+
if: needs.check-version.outputs.is_new == 'true' && needs.build.result == 'success'
|
|
68
|
+
runs-on: ubuntu-latest
|
|
69
|
+
environment: pypi
|
|
70
|
+
permissions:
|
|
71
|
+
id-token: write
|
|
72
|
+
steps:
|
|
73
|
+
- name: Download dist artifacts
|
|
74
|
+
uses: actions/download-artifact@v4
|
|
75
|
+
with:
|
|
76
|
+
name: dist
|
|
77
|
+
path: dist/
|
|
78
|
+
- name: Publish to PyPI
|
|
79
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
80
|
+
|
|
81
|
+
release:
|
|
82
|
+
name: Create GitHub Release
|
|
83
|
+
needs: [publish, check-version]
|
|
84
|
+
if: needs.check-version.outputs.is_new == 'true' && needs.publish.result == 'success'
|
|
85
|
+
runs-on: ubuntu-latest
|
|
86
|
+
permissions:
|
|
87
|
+
contents: write
|
|
88
|
+
steps:
|
|
89
|
+
- name: Create GitHub Release
|
|
90
|
+
uses: softprops/action-gh-release@v2
|
|
91
|
+
with:
|
|
92
|
+
tag_name: v${{ needs.check-version.outputs.version }}
|
|
93
|
+
name: anySQL v${{ needs.check-version.outputs.version }}
|
|
94
|
+
generate_release_notes: true
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: anysql-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: SQL analytics for AI systems — query LLM responses, agent traces, and RAG pipelines like a database
|
|
5
|
+
Project-URL: Homepage, https://anysql.org
|
|
6
|
+
Project-URL: Repository, https://github.com/sadayamuthu/anySQL
|
|
7
|
+
Project-URL: Documentation, https://docs.anysql.org
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Keywords: agents,ai,anthropic,duckdb,eval,llm,observability,openai,rag,sql
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: duckdb>=0.10.0
|
|
19
|
+
Requires-Dist: pandas>=2.0.0
|
|
20
|
+
Requires-Dist: pyarrow>=14.0.0
|
|
21
|
+
Provides-Extra: all
|
|
22
|
+
Requires-Dist: anthropic>=0.25.0; extra == 'all'
|
|
23
|
+
Requires-Dist: langchain>=0.2.0; extra == 'all'
|
|
24
|
+
Requires-Dist: openai>=1.0.0; extra == 'all'
|
|
25
|
+
Provides-Extra: anthropic
|
|
26
|
+
Requires-Dist: anthropic>=0.25.0; extra == 'anthropic'
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: black; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-asyncio; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
32
|
+
Provides-Extra: langchain
|
|
33
|
+
Requires-Dist: langchain>=0.2.0; extra == 'langchain'
|
|
34
|
+
Provides-Extra: openai
|
|
35
|
+
Requires-Dist: openai>=1.0.0; extra == 'openai'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
<div align="center">
|
|
39
|
+
|
|
40
|
+
# anySQL
|
|
41
|
+
|
|
42
|
+
<h3>SQL Analytics for AI Systems</h3>
|
|
43
|
+
|
|
44
|
+
<p>From vibes to queries.</p>
|
|
45
|
+
|
|
46
|
+
[](https://github.com/karthik/anysql/actions)
|
|
47
|
+
[](https://www.python.org/downloads/)
|
|
48
|
+
[](LICENSE)
|
|
49
|
+
[](https://pypi.org/project/anysql/)
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
[Quick Start](#quick-start) · [How It Works](#how-it-works) · [5 Use Cases](#the-5-use-cases) · [Installation](#installation) · [CLI Usage](#cli-usage) · [Examples](#examples)
|
|
54
|
+
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## What is anySQL?
|
|
60
|
+
|
|
61
|
+
anySQL is an open-source SQL analytics engine for AI systems. It lets engineers query LLM responses, agent traces, and RAG pipelines with standard SQL — powered by DuckDB in-memory, persisted to SQLite, with zero configuration.
|
|
62
|
+
|
|
63
|
+
AI engineers debug with `print()` statements, JSON log files, and pre-built dashboards that show what the tool designer thought you'd want to see. What's missing is raw SQL over normalized AI telemetry data — specifically the cross-layer JOIN that lets you ask whether your RAG pipeline is failing at retrieval or generation.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Quick Start
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Install
|
|
71
|
+
pip install anysql
|
|
72
|
+
|
|
73
|
+
# Install with provider support
|
|
74
|
+
pip install "anysql[openai]"
|
|
75
|
+
pip install "anysql[anthropic]"
|
|
76
|
+
pip install "anysql[all]" # OpenAI + Anthropic + LangChain
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
import anysql
|
|
81
|
+
|
|
82
|
+
# Initialize (in-memory by default, or pass a file path for persistence)
|
|
83
|
+
db = anysql.init()
|
|
84
|
+
|
|
85
|
+
# Wrap your OpenAI client — all calls are auto-logged
|
|
86
|
+
client = anysql.openai(openai_client)
|
|
87
|
+
|
|
88
|
+
# Wrap your Anthropic client
|
|
89
|
+
client = anysql.claude(anthropic_client)
|
|
90
|
+
|
|
91
|
+
# Tag pipeline runs for cost attribution
|
|
92
|
+
@anysql.context(feature="search", version="v2")
|
|
93
|
+
def run_search(query):
|
|
94
|
+
...
|
|
95
|
+
|
|
96
|
+
# Query anything with standard SQL
|
|
97
|
+
df = db.query("SELECT model, AVG(cost_usd) FROM llm_responses GROUP BY model")
|
|
98
|
+
|
|
99
|
+
# Or use built-in analytics methods
|
|
100
|
+
df = db.model_comparison() # UC1: multi-model comparison
|
|
101
|
+
df = db.prompt_regressions() # UC2: regression detection
|
|
102
|
+
df = db.cost_by_feature() # UC3: cost attribution
|
|
103
|
+
df = db.tool_failure_rates() # UC4: agent debugging
|
|
104
|
+
df = db.rag_failure_modes() # UC5: RAG forensics
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## How It Works
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
User Code
|
|
113
|
+
│
|
|
114
|
+
├── @anysql.context(feature="x") ← Python contextvars, sync+async safe
|
|
115
|
+
├── OpenAI/Claude wrapped client ← transparent proxy, one-line swap
|
|
116
|
+
├── AgentTracer (LangChain callback) ← manual or callback-based
|
|
117
|
+
└── RAGTracer.after_retrieval() ← auto-detects LangChain/LlamaIndex/dict
|
|
118
|
+
│
|
|
119
|
+
▼ insert()
|
|
120
|
+
AnySQL engine
|
|
121
|
+
├── in-memory buffer (dict lists per table)
|
|
122
|
+
├── SQLite persistence (JSON blobs, cross-session)
|
|
123
|
+
└── DuckDB (Arrow views, SQL at query time)
|
|
124
|
+
│
|
|
125
|
+
▼ query()
|
|
126
|
+
6 PyArrow tables as DuckDB views:
|
|
127
|
+
llm_responses, eval_results, pipeline_runs,
|
|
128
|
+
agent_tool_calls, agent_trace, rag_chunks
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Key design decisions:**
|
|
132
|
+
- Schema enforcement at Arrow layer — SQLite stores raw JSON, validation happens at query time
|
|
133
|
+
- Dot-namespace tables (`llm.responses`) map to flat SQL view names (`llm_responses`)
|
|
134
|
+
- Contextvars for thread-safe and async-safe tagging — no manual pass-through required
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## The 6 Canonical Tables
|
|
139
|
+
|
|
140
|
+
| Table | Use Cases | Join Keys |
|
|
141
|
+
|-------|-----------|-----------|
|
|
142
|
+
| `llm_responses` | UC1, UC2 | `response_id` |
|
|
143
|
+
| `eval_results` | UC1, UC2, UC5 | `response_id`, `run_id`, `query_id` |
|
|
144
|
+
| `pipeline_runs` | UC3 | `run_id`, `session_id` |
|
|
145
|
+
| `agent_tool_calls` | UC4 | `session_id` |
|
|
146
|
+
| `agent_trace` | UC4 | `session_id` |
|
|
147
|
+
| `rag_chunks` | UC5 | `query_id` ← cross-layer join key |
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## The 5 Use Cases
|
|
152
|
+
|
|
153
|
+
| UC | Name | Key Methods | What It Answers |
|
|
154
|
+
|----|------|-------------|-----------------|
|
|
155
|
+
| UC1 | Multi-Model Comparison | `model_comparison()`, `model_by_task()` | Which model performs best on my task? |
|
|
156
|
+
| UC2 | Prompt Regression Detection | `prompt_regressions()`, `eval_debt()`, `silent_degradation()` | Did my last prompt change break something? |
|
|
157
|
+
| UC3 | Cost Attribution | `cost_by_feature()`, `cost_anomalies()` | Which feature is burning my LLM budget? |
|
|
158
|
+
| UC4 | Agent Debugging | `tool_failure_rates()`, `loop_detector()`, `session_diff()`, `human_intervention_points()` | Where is my agent getting stuck? |
|
|
159
|
+
| UC5 | RAG Forensics | `rag_failure_modes()`, `chunk_quality_ranking()`, `similarity_calibration()` | Is my RAG failing at retrieval or generation? |
|
|
160
|
+
|
|
161
|
+
The cross-layer join (UC5) is the killer feature — `query_id` threads RAG retrieval to eval results, enabling retrieval vs. generation failure classification.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Installation
|
|
166
|
+
|
|
167
|
+
### From PyPI
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
pip install anysql
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Provider extras
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
pip install "anysql[openai]" # + openai>=1.0.0
|
|
177
|
+
pip install "anysql[anthropic]" # + anthropic>=0.25.0
|
|
178
|
+
pip install "anysql[langchain]" # + langchain>=0.2.0
|
|
179
|
+
pip install "anysql[all]" # everything
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## CLI Usage
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
# Run a SQL query against a persisted database
|
|
188
|
+
anysql query "SELECT model, COUNT(*) FROM llm_responses GROUP BY model"
|
|
189
|
+
|
|
190
|
+
# Show table row counts and basic stats
|
|
191
|
+
anysql stats
|
|
192
|
+
|
|
193
|
+
# Query a specific database file
|
|
194
|
+
anysql query "SELECT * FROM eval_results LIMIT 10" --db ./myproject.db
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Examples
|
|
200
|
+
|
|
201
|
+
Three runnable demos are included in `examples/`. All auto-detect missing API keys and fall back to mock mode — no downloads required.
|
|
202
|
+
|
|
203
|
+
| Demo | Dataset | Models |
|
|
204
|
+
|------|---------|--------|
|
|
205
|
+
| `realtime_openai_demo.py` | BBC News (2004–05), 12 articles | `gpt-4o`, `gpt-4o-mini` |
|
|
206
|
+
| `realtime_claude_demo.py` | AG News, 15 articles | `claude-sonnet-4-6`, `claude-haiku-4-5` |
|
|
207
|
+
| `realtime_combined_demo.py` | Reuters R8, 20 articles | All 4 models head-to-head |
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
# Run combined demo (works without API keys)
|
|
211
|
+
python examples/realtime_combined_demo.py
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Adapter Usage
|
|
217
|
+
|
|
218
|
+
### OpenAI
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
import openai
|
|
222
|
+
import anysql
|
|
223
|
+
|
|
224
|
+
db = anysql.init()
|
|
225
|
+
client = anysql.openai(openai.OpenAI())
|
|
226
|
+
|
|
227
|
+
# All calls now logged automatically
|
|
228
|
+
response = client.chat.completions.create(
|
|
229
|
+
model="gpt-4o",
|
|
230
|
+
messages=[{"role": "user", "content": "Summarize this article..."}]
|
|
231
|
+
)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Anthropic
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
import anthropic
|
|
238
|
+
import anysql
|
|
239
|
+
|
|
240
|
+
db = anysql.init()
|
|
241
|
+
client = anysql.claude(anthropic.Anthropic())
|
|
242
|
+
|
|
243
|
+
response = client.messages.create(
|
|
244
|
+
model="claude-sonnet-4-6",
|
|
245
|
+
max_tokens=1024,
|
|
246
|
+
messages=[{"role": "user", "content": "Classify this text..."}]
|
|
247
|
+
)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Agent Tracing
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
tracer = anysql.agent_tracer()
|
|
254
|
+
|
|
255
|
+
# Manual tracing
|
|
256
|
+
tracer.trace_tool_call(
|
|
257
|
+
session_id="sess-001",
|
|
258
|
+
tool_name="web_search",
|
|
259
|
+
input_data={"query": "latest news"},
|
|
260
|
+
output_data={"results": [...]},
|
|
261
|
+
success=True,
|
|
262
|
+
latency_ms=320,
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
# LangChain callback (automatic)
|
|
266
|
+
from langchain_openai import ChatOpenAI
|
|
267
|
+
llm = ChatOpenAI(callbacks=[tracer])
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### RAG Tracing
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
rag = anysql.rag_tracer()
|
|
274
|
+
|
|
275
|
+
query_id = rag.before_retrieval(query="What is anySQL?")
|
|
276
|
+
chunks = retriever.get_relevant_documents(query)
|
|
277
|
+
rag.after_retrieval(query_id=query_id, chunks=chunks)
|
|
278
|
+
|
|
279
|
+
# Record eval result with cross-layer join key
|
|
280
|
+
rag.record_eval(
|
|
281
|
+
query_id=query_id,
|
|
282
|
+
score=0.92,
|
|
283
|
+
passed=True,
|
|
284
|
+
eval_type="faithfulness",
|
|
285
|
+
)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Development
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
pip install -e ".[dev]"
|
|
294
|
+
|
|
295
|
+
pytest tests/ -v # Run tests
|
|
296
|
+
pytest tests/ --tb=short # Short failure output
|
|
297
|
+
ruff check anysql/ # Lint
|
|
298
|
+
ruff format anysql/ # Format
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Repository Structure
|
|
304
|
+
|
|
305
|
+
```
|
|
306
|
+
anysql/
|
|
307
|
+
├── anysql/
|
|
308
|
+
│ ├── __init__.py # Public API surface
|
|
309
|
+
│ ├── engine.py # DuckDB engine + UC analytics methods
|
|
310
|
+
│ ├── schema.py # 6 PyArrow schemas
|
|
311
|
+
│ ├── storage.py # SQLite persistence
|
|
312
|
+
│ ├── context.py # @context decorator + context_scope()
|
|
313
|
+
│ ├── cli.py # CLI entry point
|
|
314
|
+
│ ├── adapters/
|
|
315
|
+
│ │ ├── openai.py # OpenAI transparent proxy
|
|
316
|
+
│ │ ├── claude.py # Anthropic transparent proxy
|
|
317
|
+
│ │ └── generic.py # Generic JSON/dict adapter
|
|
318
|
+
│ └── tracers/
|
|
319
|
+
│ ├── agent.py # AgentTracer (manual + LangChain)
|
|
320
|
+
│ └── rag.py # RAGTracer (LangChain/LlamaIndex/dict)
|
|
321
|
+
├── tests/ # 94 tests, all passing
|
|
322
|
+
├── examples/ # 3 runnable demos
|
|
323
|
+
└── docs/
|
|
324
|
+
└── QUERIES.md # Canonical SQL query library
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## License
|
|
330
|
+
|
|
331
|
+
Apache 2.0
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
<div align="center">
|
|
336
|
+
|
|
337
|
+
**anySQL is an open-source SQL analytics engine for AI systems**
|
|
338
|
+
|
|
339
|
+
anySQL is managed by [OpenAstra](https://openastra.org) · [anysql.org](https://anysql.org)
|
|
340
|
+
|
|
341
|
+
[PyPI](https://pypi.org/project/anysql/) · [GitHub](https://github.com/karthik/anysql) · [Docs](https://docs.anysql.org)
|
|
342
|
+
|
|
343
|
+
</div>
|
|
344
|
+
|
|
345
|
+
---
|