queryframe 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.
- queryframe-0.1.0/.github/workflows/ci.yml +48 -0
- queryframe-0.1.0/.github/workflows/release.yml +31 -0
- queryframe-0.1.0/.gitignore +40 -0
- queryframe-0.1.0/CONTRIBUTING.md +30 -0
- queryframe-0.1.0/LICENSE +21 -0
- queryframe-0.1.0/PKG-INFO +360 -0
- queryframe-0.1.0/README.md +298 -0
- queryframe-0.1.0/chart_shoe_me_a_pie_chart_.html +14 -0
- queryframe-0.1.0/demo.py +107 -0
- queryframe-0.1.0/examples/advanced_viz.py +44 -0
- queryframe-0.1.0/examples/custom_provider.py +40 -0
- queryframe-0.1.0/examples/local_ollama.py +33 -0
- queryframe-0.1.0/examples/quickstart.py +28 -0
- queryframe-0.1.0/pyproject.toml +99 -0
- queryframe-0.1.0/src/queryframe/__init__.py +15 -0
- queryframe-0.1.0/src/queryframe/cache/__init__.py +0 -0
- queryframe-0.1.0/src/queryframe/cache/disk.py +135 -0
- queryframe-0.1.0/src/queryframe/cache/hasher.py +33 -0
- queryframe-0.1.0/src/queryframe/cache/memory.py +104 -0
- queryframe-0.1.0/src/queryframe/core/__init__.py +0 -0
- queryframe-0.1.0/src/queryframe/core/accessor.py +65 -0
- queryframe-0.1.0/src/queryframe/core/config.py +96 -0
- queryframe-0.1.0/src/queryframe/core/engine.py +280 -0
- queryframe-0.1.0/src/queryframe/core/result.py +95 -0
- queryframe-0.1.0/src/queryframe/core/schema.py +99 -0
- queryframe-0.1.0/src/queryframe/llm/__init__.py +0 -0
- queryframe-0.1.0/src/queryframe/llm/anthropic.py +63 -0
- queryframe-0.1.0/src/queryframe/llm/base.py +39 -0
- queryframe-0.1.0/src/queryframe/llm/gemini.py +74 -0
- queryframe-0.1.0/src/queryframe/llm/lmstudio.py +76 -0
- queryframe-0.1.0/src/queryframe/llm/ollama.py +135 -0
- queryframe-0.1.0/src/queryframe/llm/openai.py +67 -0
- queryframe-0.1.0/src/queryframe/llm/prompt/__init__.py +0 -0
- queryframe-0.1.0/src/queryframe/llm/prompt/builder.py +134 -0
- queryframe-0.1.0/src/queryframe/llm/prompt/compressor.py +47 -0
- queryframe-0.1.0/src/queryframe/llm/prompt/templates.py +92 -0
- queryframe-0.1.0/src/queryframe/llm/registry.py +113 -0
- queryframe-0.1.0/src/queryframe/memory/__init__.py +0 -0
- queryframe-0.1.0/src/queryframe/memory/context.py +38 -0
- queryframe-0.1.0/src/queryframe/memory/conversation.py +70 -0
- queryframe-0.1.0/src/queryframe/py.typed +0 -0
- queryframe-0.1.0/src/queryframe/sandbox/__init__.py +0 -0
- queryframe-0.1.0/src/queryframe/sandbox/executor.py +136 -0
- queryframe-0.1.0/src/queryframe/sandbox/restricted.py +108 -0
- queryframe-0.1.0/src/queryframe/sandbox/timeout.py +47 -0
- queryframe-0.1.0/src/queryframe/sandbox/validator.py +127 -0
- queryframe-0.1.0/src/queryframe/utils/__init__.py +0 -0
- queryframe-0.1.0/src/queryframe/utils/dataframe.py +33 -0
- queryframe-0.1.0/src/queryframe/utils/errors.py +83 -0
- queryframe-0.1.0/src/queryframe/utils/logger.py +18 -0
- queryframe-0.1.0/src/queryframe/viz/__init__.py +0 -0
- queryframe-0.1.0/src/queryframe/viz/altair_renderer.py +215 -0
- queryframe-0.1.0/src/queryframe/viz/base.py +34 -0
- queryframe-0.1.0/src/queryframe/viz/chart_types.py +84 -0
- queryframe-0.1.0/src/queryframe/viz/matplotlib_renderer.py +217 -0
- queryframe-0.1.0/src/queryframe/viz/plotly_renderer.py +236 -0
- queryframe-0.1.0/src/queryframe/viz/selector.py +128 -0
- queryframe-0.1.0/src/queryframe/viz/style.py +94 -0
- queryframe-0.1.0/src/queryframe/viz/theme.py +126 -0
- queryframe-0.1.0/tests/__init__.py +0 -0
- queryframe-0.1.0/tests/conftest.py +78 -0
- queryframe-0.1.0/tests/e2e/__init__.py +0 -0
- queryframe-0.1.0/tests/e2e/test_live.py +356 -0
- queryframe-0.1.0/tests/integration/__init__.py +0 -0
- queryframe-0.1.0/tests/integration/test_engine.py +86 -0
- queryframe-0.1.0/tests/integration/test_sandbox.py +87 -0
- queryframe-0.1.0/tests/unit/__init__.py +0 -0
- queryframe-0.1.0/tests/unit/test_accessor.py +135 -0
- queryframe-0.1.0/tests/unit/test_cache.py +118 -0
- queryframe-0.1.0/tests/unit/test_compressor.py +171 -0
- queryframe-0.1.0/tests/unit/test_config.py +73 -0
- queryframe-0.1.0/tests/unit/test_conversation.py +57 -0
- queryframe-0.1.0/tests/unit/test_dataframe_utils.py +112 -0
- queryframe-0.1.0/tests/unit/test_disk_cache.py +135 -0
- queryframe-0.1.0/tests/unit/test_matplotlib_renderer.py +430 -0
- queryframe-0.1.0/tests/unit/test_plotly_renderer.py +354 -0
- queryframe-0.1.0/tests/unit/test_prompt_builder.py +86 -0
- queryframe-0.1.0/tests/unit/test_result.py +260 -0
- queryframe-0.1.0/tests/unit/test_schema.py +85 -0
- queryframe-0.1.0/tests/unit/test_selector.py +225 -0
- queryframe-0.1.0/tests/unit/test_style.py +306 -0
- queryframe-0.1.0/tests/unit/test_validator.py +235 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: |
|
|
26
|
+
python -m pip install --upgrade pip
|
|
27
|
+
pip install -e ".[dev,plotly,matplotlib]"
|
|
28
|
+
|
|
29
|
+
- name: Lint with ruff
|
|
30
|
+
run: ruff check src/ tests/
|
|
31
|
+
|
|
32
|
+
- name: Type check with mypy
|
|
33
|
+
run: mypy src/queryframe/ --ignore-missing-imports
|
|
34
|
+
continue-on-error: true
|
|
35
|
+
|
|
36
|
+
- name: Run tests
|
|
37
|
+
run: pytest --cov=queryframe --cov-report=term-missing --cov-fail-under=80
|
|
38
|
+
|
|
39
|
+
lint:
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v4
|
|
43
|
+
- uses: actions/setup-python@v5
|
|
44
|
+
with:
|
|
45
|
+
python-version: "3.12"
|
|
46
|
+
- run: pip install ruff
|
|
47
|
+
- run: ruff check src/ tests/
|
|
48
|
+
- run: ruff format --check src/ tests/
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Release to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
id-token: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
publish:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
environment: release
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.12"
|
|
23
|
+
|
|
24
|
+
- name: Install build tools
|
|
25
|
+
run: pip install build
|
|
26
|
+
|
|
27
|
+
- name: Build package
|
|
28
|
+
run: python -m build
|
|
29
|
+
|
|
30
|
+
- name: Publish to PyPI
|
|
31
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*$py.class
|
|
4
|
+
*.egg-info/
|
|
5
|
+
*.egg
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
.eggs/
|
|
9
|
+
*.so
|
|
10
|
+
.Python
|
|
11
|
+
|
|
12
|
+
# Virtual environments
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
env/
|
|
16
|
+
|
|
17
|
+
# IDE
|
|
18
|
+
.idea/
|
|
19
|
+
.vscode/
|
|
20
|
+
*.swp
|
|
21
|
+
*.swo
|
|
22
|
+
|
|
23
|
+
# Testing
|
|
24
|
+
.pytest_cache/
|
|
25
|
+
.coverage
|
|
26
|
+
htmlcov/
|
|
27
|
+
.mypy_cache/
|
|
28
|
+
.ruff_cache/
|
|
29
|
+
|
|
30
|
+
# OS
|
|
31
|
+
.DS_Store
|
|
32
|
+
Thumbs.db
|
|
33
|
+
|
|
34
|
+
# Secrets
|
|
35
|
+
.env
|
|
36
|
+
.env.local
|
|
37
|
+
*.key
|
|
38
|
+
|
|
39
|
+
# QueryFrame cache
|
|
40
|
+
~/.queryframe/
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Contributing to QueryFrame
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in contributing!
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
1. Fork the repository
|
|
8
|
+
2. Clone your fork: `git clone https://github.com/YOUR_USERNAME/queryframe.git`
|
|
9
|
+
3. Install in dev mode: `pip install -e ".[dev,all]"`
|
|
10
|
+
4. Create a branch: `git checkout -b feature/your-feature`
|
|
11
|
+
|
|
12
|
+
## Development Workflow
|
|
13
|
+
|
|
14
|
+
1. Write tests first (TDD)
|
|
15
|
+
2. Run tests: `pytest`
|
|
16
|
+
3. Lint: `ruff check src/ tests/`
|
|
17
|
+
4. Format: `ruff format src/ tests/`
|
|
18
|
+
5. Type check: `mypy src/queryframe/`
|
|
19
|
+
|
|
20
|
+
## Pull Request Guidelines
|
|
21
|
+
|
|
22
|
+
- Keep PRs focused — one feature or fix per PR
|
|
23
|
+
- Include tests for new functionality
|
|
24
|
+
- Maintain 80%+ test coverage
|
|
25
|
+
- Follow existing code style
|
|
26
|
+
- Update documentation if needed
|
|
27
|
+
|
|
28
|
+
## Security
|
|
29
|
+
|
|
30
|
+
If you discover a security vulnerability, please email security@movargroup.com instead of opening an issue.
|
queryframe-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Movar Group
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: queryframe
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Super fast natural language data visualization and analysis for pandas DataFrames
|
|
5
|
+
Project-URL: Homepage, https://github.com/movar-group/queryframe
|
|
6
|
+
Project-URL: Documentation, https://github.com/movar-group/queryframe#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/movar-group/queryframe
|
|
8
|
+
Project-URL: Issues, https://github.com/movar-group/queryframe/issues
|
|
9
|
+
Author: Movar Group
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ai,dataframe,llm,natural-language,pandas,visualization
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: jinja2>=3.1
|
|
25
|
+
Requires-Dist: pandas>=1.5
|
|
26
|
+
Requires-Dist: xxhash>=3.0
|
|
27
|
+
Provides-Extra: all
|
|
28
|
+
Requires-Dist: altair>=5.0; extra == 'all'
|
|
29
|
+
Requires-Dist: anthropic>=0.30; extra == 'all'
|
|
30
|
+
Requires-Dist: google-genai>=1.0; extra == 'all'
|
|
31
|
+
Requires-Dist: httpx>=0.27; extra == 'all'
|
|
32
|
+
Requires-Dist: matplotlib>=3.5; extra == 'all'
|
|
33
|
+
Requires-Dist: openai>=1.0; extra == 'all'
|
|
34
|
+
Requires-Dist: plotly>=5.0; extra == 'all'
|
|
35
|
+
Requires-Dist: seaborn>=0.12; extra == 'all'
|
|
36
|
+
Provides-Extra: altair
|
|
37
|
+
Requires-Dist: altair>=5.0; extra == 'altair'
|
|
38
|
+
Provides-Extra: anthropic
|
|
39
|
+
Requires-Dist: anthropic>=0.30; extra == 'anthropic'
|
|
40
|
+
Provides-Extra: dev
|
|
41
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
42
|
+
Requires-Dist: pandas-stubs>=2.0; extra == 'dev'
|
|
43
|
+
Requires-Dist: pre-commit>=3.0; extra == 'dev'
|
|
44
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
45
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
46
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
47
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
48
|
+
Provides-Extra: gemini
|
|
49
|
+
Requires-Dist: google-genai>=1.0; extra == 'gemini'
|
|
50
|
+
Provides-Extra: lmstudio
|
|
51
|
+
Requires-Dist: openai>=1.0; extra == 'lmstudio'
|
|
52
|
+
Provides-Extra: matplotlib
|
|
53
|
+
Requires-Dist: matplotlib>=3.5; extra == 'matplotlib'
|
|
54
|
+
Requires-Dist: seaborn>=0.12; extra == 'matplotlib'
|
|
55
|
+
Provides-Extra: ollama
|
|
56
|
+
Requires-Dist: httpx>=0.27; extra == 'ollama'
|
|
57
|
+
Provides-Extra: openai
|
|
58
|
+
Requires-Dist: openai>=1.0; extra == 'openai'
|
|
59
|
+
Provides-Extra: plotly
|
|
60
|
+
Requires-Dist: plotly>=5.0; extra == 'plotly'
|
|
61
|
+
Description-Content-Type: text/markdown
|
|
62
|
+
|
|
63
|
+
# QueryFrame
|
|
64
|
+
|
|
65
|
+
**Super fast natural language data visualization and analysis for pandas DataFrames.**
|
|
66
|
+
|
|
67
|
+
QueryFrame lets you ask questions about your data in plain English and get instant answers, charts, and insights. It's the faster, safer, more flexible alternative to PandasAI.
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
import pandas as pd
|
|
71
|
+
import queryframe as qf
|
|
72
|
+
|
|
73
|
+
df = pd.read_csv("sales.csv")
|
|
74
|
+
|
|
75
|
+
# Ask anything
|
|
76
|
+
result = qf.ask(df, "what is the average revenue by region?")
|
|
77
|
+
print(result.data)
|
|
78
|
+
|
|
79
|
+
# Visualize instantly
|
|
80
|
+
result = df.qf.ask("show me a bar chart of sales by product")
|
|
81
|
+
result.show()
|
|
82
|
+
|
|
83
|
+
# Chain queries
|
|
84
|
+
result = qf.ask(df, "top 5 customers by spend").save("top_customers.html")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Why QueryFrame over PandasAI?
|
|
88
|
+
|
|
89
|
+
| Feature | QueryFrame | PandasAI |
|
|
90
|
+
|---------|-----------|----------|
|
|
91
|
+
| Speed | Smart caching, minimal prompts | Sends full schema every query |
|
|
92
|
+
| Safety | AST-validated sandbox | Raw `exec()` |
|
|
93
|
+
| Local models | First-class Ollama + LM Studio | Limited support |
|
|
94
|
+
| Visualizations | Auto-selects Plotly/Matplotlib/Altair | Mostly matplotlib |
|
|
95
|
+
| Follow-ups | Conversation memory | Stateless |
|
|
96
|
+
| Token usage | Compressed schemas, 3 sample rows | Verbose, 5 sample rows |
|
|
97
|
+
|
|
98
|
+
## Installation
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Core (no LLM provider included)
|
|
102
|
+
pip install queryframe
|
|
103
|
+
|
|
104
|
+
# With your preferred provider
|
|
105
|
+
pip install queryframe[openai] # OpenAI
|
|
106
|
+
pip install queryframe[anthropic] # Claude
|
|
107
|
+
pip install queryframe[gemini] # Google Gemini
|
|
108
|
+
pip install queryframe[ollama] # Ollama (local)
|
|
109
|
+
pip install queryframe[lmstudio] # LM Studio (local)
|
|
110
|
+
|
|
111
|
+
# With visualization libraries
|
|
112
|
+
pip install queryframe[plotly] # Interactive charts (recommended)
|
|
113
|
+
pip install queryframe[matplotlib] # Static charts (includes seaborn)
|
|
114
|
+
pip install queryframe[altair] # Declarative charts
|
|
115
|
+
|
|
116
|
+
# Everything
|
|
117
|
+
pip install queryframe[all]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Quick Start
|
|
121
|
+
|
|
122
|
+
### 1. Set your API key (cloud providers)
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
export OPENAI_API_KEY="sk-..."
|
|
126
|
+
# or
|
|
127
|
+
export ANTHROPIC_API_KEY="sk-ant-..."
|
|
128
|
+
# or
|
|
129
|
+
export GOOGLE_API_KEY="..."
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 2. Use it
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
import pandas as pd
|
|
136
|
+
import queryframe as qf
|
|
137
|
+
|
|
138
|
+
df = pd.DataFrame({
|
|
139
|
+
"product": ["Laptop", "Phone", "Tablet"],
|
|
140
|
+
"price": [999, 699, 449],
|
|
141
|
+
"units_sold": [150, 500, 200],
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
# Natural language queries
|
|
145
|
+
result = qf.ask(df, "which product generated the most revenue?")
|
|
146
|
+
print(result.data) # The answer
|
|
147
|
+
print(result.code) # Generated pandas code
|
|
148
|
+
print(result.explanation) # Human-readable explanation
|
|
149
|
+
|
|
150
|
+
# Visualizations
|
|
151
|
+
result = qf.ask(df, "bar chart of revenue by product")
|
|
152
|
+
result.show() # Display interactive chart
|
|
153
|
+
result.save("chart.html") # Export
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Local Models (Ollama / LM Studio)
|
|
157
|
+
|
|
158
|
+
QueryFrame has first-class support for local models — no API keys, no data leaves your machine.
|
|
159
|
+
|
|
160
|
+
### Ollama
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# Start Ollama
|
|
164
|
+
ollama serve
|
|
165
|
+
|
|
166
|
+
# Pull a model
|
|
167
|
+
ollama pull llama3.1
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
import queryframe as qf
|
|
172
|
+
|
|
173
|
+
qf.configure(provider="ollama", model="llama3.1")
|
|
174
|
+
result = qf.ask(df, "average sales by region")
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### LM Studio
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
import queryframe as qf
|
|
181
|
+
|
|
182
|
+
# LM Studio runs at localhost:1234 by default
|
|
183
|
+
qf.configure(provider="lmstudio")
|
|
184
|
+
result = qf.ask(df, "show me the top 10 products")
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## All Providers
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
from queryframe import QueryEngine, QueryFrameConfig
|
|
191
|
+
|
|
192
|
+
# OpenAI
|
|
193
|
+
engine = QueryEngine(config=QueryFrameConfig(
|
|
194
|
+
provider="openai", model="gpt-4o-mini"
|
|
195
|
+
))
|
|
196
|
+
|
|
197
|
+
# Anthropic Claude
|
|
198
|
+
engine = QueryEngine(config=QueryFrameConfig(
|
|
199
|
+
provider="anthropic", model="claude-sonnet-4-20250514"
|
|
200
|
+
))
|
|
201
|
+
|
|
202
|
+
# Google Gemini
|
|
203
|
+
engine = QueryEngine(config=QueryFrameConfig(
|
|
204
|
+
provider="gemini", model="gemini-2.0-flash"
|
|
205
|
+
))
|
|
206
|
+
|
|
207
|
+
# Ollama
|
|
208
|
+
engine = QueryEngine(config=QueryFrameConfig(
|
|
209
|
+
provider="ollama", model="llama3.1"
|
|
210
|
+
))
|
|
211
|
+
|
|
212
|
+
# LM Studio
|
|
213
|
+
engine = QueryEngine(config=QueryFrameConfig(
|
|
214
|
+
provider="lmstudio"
|
|
215
|
+
))
|
|
216
|
+
|
|
217
|
+
# Auto-detect (checks env vars, then local servers)
|
|
218
|
+
engine = QueryEngine() # Just works
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Visualization
|
|
222
|
+
|
|
223
|
+
QueryFrame auto-selects the best visualization library:
|
|
224
|
+
- **Notebooks** → Plotly (interactive)
|
|
225
|
+
- **Scripts** → Matplotlib (static)
|
|
226
|
+
- **Override** → `qf.ask(df, "...", viz="altair")`
|
|
227
|
+
|
|
228
|
+
Supported chart types: `bar`, `line`, `scatter`, `pie`, `histogram`, `heatmap`, `box`, `area`, `violin`, `treemap`, `funnel`
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
# Auto-select
|
|
232
|
+
result = qf.ask(df, "show trend of sales over time") # → line chart
|
|
233
|
+
|
|
234
|
+
# Force specific library
|
|
235
|
+
result = qf.ask(df, "bar chart of revenue", viz="matplotlib")
|
|
236
|
+
|
|
237
|
+
# Re-render with different library
|
|
238
|
+
result = qf.ask(df, "sales by region").viz("altair")
|
|
239
|
+
|
|
240
|
+
# Save to file
|
|
241
|
+
result.save("chart.png") # static image
|
|
242
|
+
result.save("chart.html") # interactive HTML
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Chainable API
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
# Chain operations
|
|
249
|
+
result = (
|
|
250
|
+
qf.ask(df, "total revenue by product")
|
|
251
|
+
.save("revenue.html")
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
# Follow-up queries (uses conversation memory)
|
|
255
|
+
r1 = qf.ask(df, "show me sales by region")
|
|
256
|
+
r2 = r1.ask("now filter to just Q4") # "it" = sales by region
|
|
257
|
+
r3 = r2.ask("which region had the highest?") # context preserved
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Caching
|
|
261
|
+
|
|
262
|
+
Repeated queries are instant (< 5ms vs 2-5s for LLM calls):
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
# First call: hits the LLM (~2s)
|
|
266
|
+
result = qf.ask(df, "average sales")
|
|
267
|
+
print(result.cached) # False
|
|
268
|
+
|
|
269
|
+
# Same query: from cache (~1ms)
|
|
270
|
+
result = qf.ask(df, "average sales")
|
|
271
|
+
print(result.cached) # True
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Configuration
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
import queryframe as qf
|
|
278
|
+
|
|
279
|
+
# Via configure()
|
|
280
|
+
qf.configure(
|
|
281
|
+
provider="openai",
|
|
282
|
+
model="gpt-4o",
|
|
283
|
+
cache_enabled=True,
|
|
284
|
+
viz_mode="plotly", # auto, plotly, matplotlib, altair
|
|
285
|
+
timeout=30, # seconds
|
|
286
|
+
max_retries=2,
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
# Via environment variables
|
|
290
|
+
# QF_PROVIDER=openai
|
|
291
|
+
# QF_MODEL=gpt-4o
|
|
292
|
+
# QF_VIZ=plotly
|
|
293
|
+
# QF_TIMEOUT=30
|
|
294
|
+
# QF_VERBOSE=true
|
|
295
|
+
# QF_LOG_LEVEL=DEBUG
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Security
|
|
299
|
+
|
|
300
|
+
QueryFrame takes security seriously:
|
|
301
|
+
|
|
302
|
+
1. **AST Validation** — All LLM-generated code is parsed and validated before execution. Dangerous operations (`import os`, `exec`, `eval`, `open`, etc.) are rejected.
|
|
303
|
+
2. **Restricted Builtins** — Only safe builtins are available in the sandbox (no `__import__`, `getattr`, `globals`, etc.)
|
|
304
|
+
3. **Execution Timeout** — Code that runs too long is killed (default: 30s)
|
|
305
|
+
4. **DataFrame Isolation** — The LLM code operates on a copy of your DataFrame, never the original
|
|
306
|
+
5. **No Network Access** — Sandboxed code cannot make network requests
|
|
307
|
+
|
|
308
|
+
## Architecture
|
|
309
|
+
|
|
310
|
+
```
|
|
311
|
+
df.ask("show me sales by region")
|
|
312
|
+
│
|
|
313
|
+
▼
|
|
314
|
+
┌─────────────┐ ┌──────────┐ ┌────────────┐
|
|
315
|
+
│ Cache Check │────▸│ Schema │────▸│ Prompt │
|
|
316
|
+
│ (< 1ms) │ │ Extract │ │ Builder │
|
|
317
|
+
└─────────────┘ └──────────┘ └────────────┘
|
|
318
|
+
│
|
|
319
|
+
▼
|
|
320
|
+
┌─────────────┐ ┌──────────┐ ┌────────────┐
|
|
321
|
+
│ Viz Render │◂────│ Sandbox │◂────│ LLM │
|
|
322
|
+
│ (auto-pick) │ │ Execute │ │ Provider │
|
|
323
|
+
└─────────────┘ └──────────┘ └────────────┘
|
|
324
|
+
│
|
|
325
|
+
┌────────────┐
|
|
326
|
+
│ QueryResult│
|
|
327
|
+
│ .data │
|
|
328
|
+
│ .chart │
|
|
329
|
+
│ .code │
|
|
330
|
+
└────────────┘
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Development
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
# Clone
|
|
337
|
+
git clone https://github.com/movar-group/queryframe.git
|
|
338
|
+
cd queryframe
|
|
339
|
+
|
|
340
|
+
# Install in dev mode
|
|
341
|
+
pip install -e ".[dev,all]"
|
|
342
|
+
|
|
343
|
+
# Run tests
|
|
344
|
+
pytest
|
|
345
|
+
|
|
346
|
+
# Lint
|
|
347
|
+
ruff check src/ tests/
|
|
348
|
+
ruff format src/ tests/
|
|
349
|
+
|
|
350
|
+
# Type check
|
|
351
|
+
mypy src/queryframe/
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## License
|
|
355
|
+
|
|
356
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
357
|
+
|
|
358
|
+
## Contributing
|
|
359
|
+
|
|
360
|
+
Contributions welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) before submitting a PR.
|