datachat-agent 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.
Files changed (109) hide show
  1. datachat_agent-0.1.0/.env.example +73 -0
  2. datachat_agent-0.1.0/.github/workflows/ci.yml +95 -0
  3. datachat_agent-0.1.0/.github/workflows/release.yml +104 -0
  4. datachat_agent-0.1.0/.gitignore +30 -0
  5. datachat_agent-0.1.0/Dockerfile +11 -0
  6. datachat_agent-0.1.0/LICENSE +21 -0
  7. datachat_agent-0.1.0/PKG-INFO +353 -0
  8. datachat_agent-0.1.0/README.md +304 -0
  9. datachat_agent-0.1.0/docker-compose.yml +77 -0
  10. datachat_agent-0.1.0/docs/CHECKLIST.md +122 -0
  11. datachat_agent-0.1.0/docs/RELEASING.md +51 -0
  12. datachat_agent-0.1.0/eval/datasets/erp_demo.py +350 -0
  13. datachat_agent-0.1.0/eval/datasets/make_pdfs.py +212 -0
  14. datachat_agent-0.1.0/packages/datachat_agent/__init__.py +17 -0
  15. datachat_agent-0.1.0/packages/datachat_agent/agent/__init__.py +0 -0
  16. datachat_agent-0.1.0/packages/datachat_agent/agent/answer.py +162 -0
  17. datachat_agent-0.1.0/packages/datachat_agent/agent/answer_plan.py +186 -0
  18. datachat_agent-0.1.0/packages/datachat_agent/agent/cited_answer.py +156 -0
  19. datachat_agent-0.1.0/packages/datachat_agent/agent/doc_planner.py +47 -0
  20. datachat_agent-0.1.0/packages/datachat_agent/agent/graph.py +227 -0
  21. datachat_agent-0.1.0/packages/datachat_agent/agent/knowledge.py +61 -0
  22. datachat_agent-0.1.0/packages/datachat_agent/agent/router.py +46 -0
  23. datachat_agent-0.1.0/packages/datachat_agent/agent/service.py +61 -0
  24. datachat_agent-0.1.0/packages/datachat_agent/agent/sessions.py +52 -0
  25. datachat_agent-0.1.0/packages/datachat_agent/agent/tools.py +229 -0
  26. datachat_agent-0.1.0/packages/datachat_agent/cli.py +279 -0
  27. datachat_agent-0.1.0/packages/datachat_agent/client.py +413 -0
  28. datachat_agent-0.1.0/packages/datachat_agent/config.py +84 -0
  29. datachat_agent-0.1.0/packages/datachat_agent/core/__init__.py +0 -0
  30. datachat_agent-0.1.0/packages/datachat_agent/core/embeddings.py +124 -0
  31. datachat_agent-0.1.0/packages/datachat_agent/core/llm.py +85 -0
  32. datachat_agent-0.1.0/packages/datachat_agent/core/pii.py +99 -0
  33. datachat_agent-0.1.0/packages/datachat_agent/docs/__init__.py +0 -0
  34. datachat_agent-0.1.0/packages/datachat_agent/docs/chunker.py +89 -0
  35. datachat_agent-0.1.0/packages/datachat_agent/docs/extract.py +93 -0
  36. datachat_agent-0.1.0/packages/datachat_agent/docs/ingest.py +132 -0
  37. datachat_agent-0.1.0/packages/datachat_agent/docs/items.py +192 -0
  38. datachat_agent-0.1.0/packages/datachat_agent/docs/search.py +140 -0
  39. datachat_agent-0.1.0/packages/datachat_agent/mcp_server.py +170 -0
  40. datachat_agent-0.1.0/packages/datachat_agent/migrations/env.py +30 -0
  41. datachat_agent-0.1.0/packages/datachat_agent/migrations/script.py.mako +25 -0
  42. datachat_agent-0.1.0/packages/datachat_agent/migrations/versions/0001_initial_schema.py +268 -0
  43. datachat_agent-0.1.0/packages/datachat_agent/migrations/versions/0002_agent_sessions_glossary_examples.py +112 -0
  44. datachat_agent-0.1.0/packages/datachat_agent/migrations/versions/0003_document_heading_date.py +26 -0
  45. datachat_agent-0.1.0/packages/datachat_agent/migrations/versions/0004_limits_feedback.py +30 -0
  46. datachat_agent-0.1.0/packages/datachat_agent/migrations/versions/0005_publishable_keys.py +38 -0
  47. datachat_agent-0.1.0/packages/datachat_agent/security.py +74 -0
  48. datachat_agent-0.1.0/packages/datachat_agent/server/__init__.py +0 -0
  49. datachat_agent-0.1.0/packages/datachat_agent/server/app.py +85 -0
  50. datachat_agent-0.1.0/packages/datachat_agent/server/auth.py +88 -0
  51. datachat_agent-0.1.0/packages/datachat_agent/server/db.py +52 -0
  52. datachat_agent-0.1.0/packages/datachat_agent/server/jobs.py +86 -0
  53. datachat_agent-0.1.0/packages/datachat_agent/server/limits.py +101 -0
  54. datachat_agent-0.1.0/packages/datachat_agent/server/models.py +277 -0
  55. datachat_agent-0.1.0/packages/datachat_agent/server/routes/__init__.py +0 -0
  56. datachat_agent-0.1.0/packages/datachat_agent/server/routes/ask.py +395 -0
  57. datachat_agent-0.1.0/packages/datachat_agent/server/routes/connections.py +287 -0
  58. datachat_agent-0.1.0/packages/datachat_agent/server/routes/documents.py +231 -0
  59. datachat_agent-0.1.0/packages/datachat_agent/server/routes/feedback.py +54 -0
  60. datachat_agent-0.1.0/packages/datachat_agent/server/routes/health.py +10 -0
  61. datachat_agent-0.1.0/packages/datachat_agent/server/routes/keys.py +113 -0
  62. datachat_agent-0.1.0/packages/datachat_agent/server/routes/knowledge.py +238 -0
  63. datachat_agent-0.1.0/packages/datachat_agent/server/routes/schema_items.py +74 -0
  64. datachat_agent-0.1.0/packages/datachat_agent/server/routes/usage.py +50 -0
  65. datachat_agent-0.1.0/packages/datachat_agent/server/services.py +54 -0
  66. datachat_agent-0.1.0/packages/datachat_agent/server/static/widget.js +62 -0
  67. datachat_agent-0.1.0/packages/datachat_agent/server/worker.py +44 -0
  68. datachat_agent-0.1.0/packages/datachat_agent/sql/__init__.py +0 -0
  69. datachat_agent-0.1.0/packages/datachat_agent/sql/connector.py +161 -0
  70. datachat_agent-0.1.0/packages/datachat_agent/sql/executor.py +110 -0
  71. datachat_agent-0.1.0/packages/datachat_agent/sql/generator.py +111 -0
  72. datachat_agent-0.1.0/packages/datachat_agent/sql/pipeline.py +120 -0
  73. datachat_agent-0.1.0/packages/datachat_agent/sql/schema_scanner.py +143 -0
  74. datachat_agent-0.1.0/packages/datachat_agent/sql/schema_store.py +270 -0
  75. datachat_agent-0.1.0/packages/datachat_agent/sql/validator.py +292 -0
  76. datachat_agent-0.1.0/pyproject.toml +78 -0
  77. datachat_agent-0.1.0/sdk-js/README.md +45 -0
  78. datachat_agent-0.1.0/sdk-js/package-lock.json +394 -0
  79. datachat_agent-0.1.0/sdk-js/package.json +30 -0
  80. datachat_agent-0.1.0/sdk-js/src/index.ts +245 -0
  81. datachat_agent-0.1.0/sdk-js/test/client.test.js +126 -0
  82. datachat_agent-0.1.0/sdk-js/tsconfig.json +14 -0
  83. datachat_agent-0.1.0/tests/__init__.py +0 -0
  84. datachat_agent-0.1.0/tests/conftest.py +31 -0
  85. datachat_agent-0.1.0/tests/fakes.py +64 -0
  86. datachat_agent-0.1.0/tests/integration/__init__.py +0 -0
  87. datachat_agent-0.1.0/tests/integration/conftest.py +156 -0
  88. datachat_agent-0.1.0/tests/integration/test_ask.py +415 -0
  89. datachat_agent-0.1.0/tests/integration/test_connections.py +175 -0
  90. datachat_agent-0.1.0/tests/integration/test_documents.py +443 -0
  91. datachat_agent-0.1.0/tests/integration/test_library.py +146 -0
  92. datachat_agent-0.1.0/tests/integration/test_mcp.py +99 -0
  93. datachat_agent-0.1.0/tests/integration/test_publishable_keys.py +202 -0
  94. datachat_agent-0.1.0/tests/integration/test_sql_pipeline.py +205 -0
  95. datachat_agent-0.1.0/tests/integration/test_week5.py +341 -0
  96. datachat_agent-0.1.0/tests/test_agent_units.py +187 -0
  97. datachat_agent-0.1.0/tests/test_api.py +48 -0
  98. datachat_agent-0.1.0/tests/test_llm.py +94 -0
  99. datachat_agent-0.1.0/tests/test_pii.py +80 -0
  100. datachat_agent-0.1.0/tests/test_security.py +44 -0
  101. datachat_agent-0.1.0/tests/test_validator.py +272 -0
  102. datachat_agent-0.1.0/widget/package-lock.json +2550 -0
  103. datachat_agent-0.1.0/widget/package.json +22 -0
  104. datachat_agent-0.1.0/widget/src/Widget.tsx +192 -0
  105. datachat_agent-0.1.0/widget/src/main.tsx +59 -0
  106. datachat_agent-0.1.0/widget/src/styles.ts +58 -0
  107. datachat_agent-0.1.0/widget/test/widget.test.tsx +131 -0
  108. datachat_agent-0.1.0/widget/tsconfig.json +14 -0
  109. datachat_agent-0.1.0/widget/vite.config.ts +27 -0
@@ -0,0 +1,73 @@
1
+ # Copy this file to .env and fill in YOUR OWN values.
2
+ # .env is git-ignored: your keys never leave your machine.
3
+
4
+ # --- DataChat's own Postgres (needs the pgvector extension) ---
5
+ DATABASE_URL=postgresql://datachat:datachat@localhost:5432/datachat
6
+ REDIS_URL=redis://localhost:6379/0
7
+
8
+ # Encrypts stored database passwords and LLM keys. Generate with: datachat-agent gen-secret
9
+ SECRET_KEY=
10
+
11
+ # --- LLM: bring your own key ---
12
+ # Any OpenAI-compatible endpoint works. Pick ONE block and uncomment it.
13
+ LLM_PROVIDER=openai_compatible
14
+
15
+ # Groq (free tier): https://console.groq.com/keys
16
+ # LLM_BASE_URL=https://api.groq.com/openai/v1
17
+ # LLM_MODEL=llama-3.3-70b-versatile
18
+
19
+ # Google Gemini (free tier): https://aistudio.google.com/apikey
20
+ # LLM_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai/
21
+ # LLM_MODEL=gemini-3.6-flash (names change often; check the models list in AI Studio)
22
+
23
+ # OpenRouter (models ending in :free): https://openrouter.ai/keys
24
+ # LLM_BASE_URL=https://openrouter.ai/api/v1
25
+ # LLM_MODEL=qwen/qwen-2.5-coder-32b-instruct:free
26
+
27
+ # Ollama (local, no key): https://ollama.com then: ollama pull qwen2.5-coder:7b
28
+ # LLM_BASE_URL=http://localhost:11434/v1
29
+ # LLM_MODEL=qwen2.5-coder:7b
30
+
31
+ # Claude: set LLM_PROVIDER=anthropic, leave LLM_BASE_URL empty
32
+ # LLM_MODEL=claude-sonnet-5-5
33
+
34
+ LLM_BASE_URL=
35
+ LLM_MODEL=
36
+ LLM_API_KEY=
37
+ # Free tiers get overloaded: list backup models (same provider and key) to try in order.
38
+ # LLM_FALLBACK_MODELS=gemini-3.5-flash,gemini-3.5-flash-lite
39
+ # Thinking models (Gemini 3.x) are faster and use fewer tokens with less reasoning:
40
+ # LLM_REASONING_EFFORT=low
41
+ # LLM_TIMEOUT_SECONDS=30
42
+ # LLM_MAX_RETRIES=1
43
+
44
+ # --- Embeddings (local and free by default) ---
45
+ # local: runs on your CPU, downloads ~70 MB once. openai_compatible: uses EMBED_BASE_URL/EMBED_API_KEY
46
+ # (falls back to the LLM settings).
47
+ EMBED_PROVIDER=local
48
+ EMBED_MODEL=BAAI/bge-small-en-v1.5
49
+ # Must match the model's vector size. Changing it later means re-creating the vector tables.
50
+ EMBED_DIM=384
51
+ # EMBED_BASE_URL=
52
+ # EMBED_API_KEY=
53
+
54
+ # --- Documents and background work ---
55
+ # MAX_PDF_MB=20
56
+ # sync: process uploads inside the request · inline: in the background of the API process (default)
57
+ # arq: in a separate `datachat-agent worker` process via Redis (what docker-compose uses)
58
+ # JOBS_BACKEND=inline
59
+
60
+ # --- Abuse and cost limits ---
61
+ # memory: per server process · redis: shared by all servers (use with several API servers)
62
+ # RATE_LIMIT_BACKEND=memory
63
+ # DEFAULT_RATE_LIMIT_PER_MINUTE=60
64
+ # DEFAULT_MONTHLY_TOKEN_LIMIT= (empty = no cap; per-tenant limits override it)
65
+
66
+ # Replace emails, phone numbers, card, Aadhaar and PAN numbers with placeholders before data
67
+ # is sent to the model; real values are put back into the answer.
68
+ # MASK_PII=false
69
+
70
+ # --- Limits on every query against a customer database ---
71
+ # SQL_TIMEOUT_SECONDS=10
72
+ # SQL_MAX_ROWS=500
73
+ # SQL_MAX_RESPONSE_BYTES=5000000
@@ -0,0 +1,95 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.11", "3.12"]
14
+ services:
15
+ postgres:
16
+ image: pgvector/pgvector:pg17
17
+ env:
18
+ POSTGRES_USER: datachat
19
+ POSTGRES_PASSWORD: datachat
20
+ POSTGRES_DB: datachat
21
+ ports: ["5432:5432"]
22
+ options: >-
23
+ --health-cmd "pg_isready -U datachat"
24
+ --health-interval 5s --health-timeout 5s --health-retries 10
25
+ redis:
26
+ image: redis:7-alpine
27
+ ports: ["6379:6379"]
28
+ mysql:
29
+ image: mysql:8.4
30
+ env:
31
+ MYSQL_ROOT_PASSWORD: rootpass
32
+ ports: ["3307:3306"]
33
+ options: >-
34
+ --health-cmd "mysqladmin ping -h 127.0.0.1 -uroot -prootpass --silent"
35
+ --health-interval 5s --health-timeout 5s --health-retries 20
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+ - uses: actions/setup-python@v5
39
+ with:
40
+ python-version: ${{ matrix.python-version }}
41
+ cache: pip
42
+ - name: Install
43
+ run: pip install -e ".[dev]"
44
+ - name: Lint
45
+ run: |
46
+ ruff check .
47
+ ruff format --check .
48
+ - name: Tests (unit + integration against Postgres and MySQL)
49
+ run: pytest -q -rs
50
+
51
+ docker:
52
+ runs-on: ubuntu-latest
53
+ steps:
54
+ - uses: actions/checkout@v4
55
+ - name: Build the self-host image
56
+ run: docker build -t datachat-agent:ci .
57
+ - name: CLI starts inside the image
58
+ run: docker run --rm datachat-agent:ci datachat-agent --help
59
+
60
+ js:
61
+ runs-on: ubuntu-latest
62
+ steps:
63
+ - uses: actions/checkout@v4
64
+ - uses: actions/setup-node@v4
65
+ with:
66
+ node-version: 22
67
+ - name: SDK (sdk-js)
68
+ working-directory: sdk-js
69
+ run: |
70
+ npm ci
71
+ npm test
72
+ - name: Widget (widget)
73
+ working-directory: widget
74
+ run: |
75
+ npm ci
76
+ npx tsc -p tsconfig.json
77
+ npm test
78
+ npm run build
79
+ - name: The widget served by the API is the freshly built one
80
+ run: cmp widget/dist/widget.js packages/datachat_agent/server/static/widget.js
81
+
82
+ package:
83
+ runs-on: ubuntu-latest
84
+ steps:
85
+ - uses: actions/checkout@v4
86
+ - uses: astral-sh/setup-uv@v6
87
+ - name: Build wheel and sdist
88
+ run: uv build
89
+ - name: Check package metadata
90
+ run: uvx twine check dist/*
91
+ - name: Wheel installs and starts
92
+ run: |
93
+ python -m venv /tmp/v && /tmp/v/bin/pip install dist/*.whl
94
+ /tmp/v/bin/datachat-agent --help
95
+ /tmp/v/bin/python -c "import datachat_agent, importlib.resources as r; assert r.files('datachat_agent').joinpath('server/static/widget.js').is_file()"
@@ -0,0 +1,104 @@
1
+ name: Release
2
+
3
+ # Push a tag like v0.1.0 to publish. The tag must match the version in pyproject.toml and
4
+ # sdk-js/package.json. See docs/RELEASING.md for the one-time PyPI and npm setup.
5
+ on:
6
+ push:
7
+ tags: ["v*"]
8
+
9
+ jobs:
10
+ verify:
11
+ runs-on: ubuntu-latest
12
+ outputs:
13
+ version: ${{ steps.v.outputs.version }}
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - id: v
17
+ name: Tag, Python and npm versions agree
18
+ run: |
19
+ tag="${GITHUB_REF_NAME#v}"
20
+ py=$(python -c "import tomllib;print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
21
+ js=$(node -p "require('./sdk-js/package.json').version")
22
+ echo "tag=$tag python=$py npm=$js"
23
+ [ "$tag" = "$py" ] && [ "$tag" = "$js" ] || { echo "Versions differ"; exit 1; }
24
+ echo "version=$tag" >> "$GITHUB_OUTPUT"
25
+
26
+ pypi:
27
+ needs: verify
28
+ runs-on: ubuntu-latest
29
+ environment: pypi
30
+ permissions:
31
+ id-token: write # trusted publishing: no PyPI password stored in GitHub
32
+ steps:
33
+ - uses: actions/checkout@v4
34
+ - uses: astral-sh/setup-uv@v6
35
+ - run: uv build
36
+ - run: uvx twine check dist/*
37
+ - uses: pypa/gh-action-pypi-publish@release/v1
38
+
39
+ npm:
40
+ needs: verify
41
+ # Off until npm is set up: Settings -> Secrets and variables -> Actions -> Variables ->
42
+ # PUBLISH_NPM = true (see docs/RELEASING.md).
43
+ if: vars.PUBLISH_NPM == 'true'
44
+ runs-on: ubuntu-latest
45
+ environment: npm
46
+ permissions:
47
+ id-token: write # trusted publishing and provenance
48
+ steps:
49
+ - uses: actions/checkout@v4
50
+ - uses: actions/setup-node@v4
51
+ with:
52
+ node-version: 22
53
+ registry-url: https://registry.npmjs.org
54
+ - run: npm install -g npm@latest # trusted publishing needs npm 11.5+
55
+ - working-directory: sdk-js
56
+ run: |
57
+ npm ci
58
+ npm test
59
+ npm publish --provenance --access public
60
+
61
+ docker:
62
+ needs: verify
63
+ runs-on: ubuntu-latest
64
+ permissions:
65
+ contents: read
66
+ packages: write
67
+ steps:
68
+ - uses: actions/checkout@v4
69
+ - uses: docker/login-action@v3
70
+ with:
71
+ registry: ghcr.io
72
+ username: ${{ github.actor }}
73
+ password: ${{ secrets.GITHUB_TOKEN }}
74
+ - id: meta
75
+ uses: docker/metadata-action@v5
76
+ with:
77
+ images: ghcr.io/${{ github.repository_owner }}/datachat-agent
78
+ tags: |
79
+ type=semver,pattern={{version}}
80
+ type=semver,pattern={{major}}.{{minor}}
81
+ type=raw,value=latest,enable=${{ !contains(github.ref_name, '-') }}
82
+ - uses: docker/build-push-action@v6
83
+ with:
84
+ context: .
85
+ push: true
86
+ tags: ${{ steps.meta.outputs.tags }}
87
+ labels: ${{ steps.meta.outputs.labels }}
88
+
89
+ github-release:
90
+ needs: [pypi, npm, docker]
91
+ # A skipped npm job (not set up yet) must not block the release; a failed one does.
92
+ if: >-
93
+ !cancelled() && needs.pypi.result == 'success' && needs.docker.result == 'success'
94
+ && needs.npm.result != 'failure'
95
+ runs-on: ubuntu-latest
96
+ permissions:
97
+ contents: write
98
+ steps:
99
+ - uses: actions/checkout@v4
100
+ - uses: softprops/action-gh-release@v2
101
+ with:
102
+ generate_release_notes: true
103
+ prerelease: ${{ contains(github.ref_name, '-') || startsWith(github.ref_name, 'v0.') }}
104
+ files: packages/datachat_agent/server/static/widget.js
@@ -0,0 +1,30 @@
1
+ # Secrets: never commit real keys, even free ones
2
+ .env
3
+ .env.*
4
+ !.env.example
5
+
6
+ # Python
7
+ __pycache__/
8
+ *.py[cod]
9
+ *.egg-info/
10
+ .venv/
11
+ venv/
12
+ build/
13
+ dist/
14
+ .pytest_cache/
15
+ .ruff_cache/
16
+ .coverage
17
+ htmlcov/
18
+
19
+ # Node (sdk-js, widget, console)
20
+ node_modules/
21
+
22
+ # Editors / OS
23
+ .vscode/
24
+ .idea/
25
+ .DS_Store
26
+ Thumbs.db
27
+
28
+ # Local data
29
+ *.sqlite3
30
+ uploads/
@@ -0,0 +1,11 @@
1
+ FROM python:3.12-slim
2
+
3
+ WORKDIR /app
4
+ ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
5
+
6
+ COPY pyproject.toml README.md ./
7
+ COPY packages ./packages
8
+ RUN pip install --no-cache-dir .
9
+
10
+ EXPOSE 8000
11
+ CMD ["sh", "-c", "datachat-agent init-db && datachat-agent serve --host 0.0.0.0 --port 8000"]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DataChat contributors
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,353 @@
1
+ Metadata-Version: 2.5
2
+ Name: datachat-agent
3
+ Version: 0.1.0
4
+ Summary: Open-source AI agent that answers questions from your SQL database and PDFs
5
+ Project-URL: Homepage, https://github.com/Ashish-devl/Data-chat-agent
6
+ Project-URL: Issues, https://github.com/Ashish-devl/Data-chat-agent/issues
7
+ Author: DataChat Agent contributors
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: agent,fastapi,llm,mcp,pdf,rag,text-to-sql
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Framework :: FastAPI
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Database :: Front-Ends
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: alembic>=1.13
22
+ Requires-Dist: arq>=0.26
23
+ Requires-Dist: asyncpg>=0.29
24
+ Requires-Dist: cryptography>=43
25
+ Requires-Dist: fastapi>=0.115
26
+ Requires-Dist: fastembed>=0.4
27
+ Requires-Dist: langchain-anthropic>=0.2
28
+ Requires-Dist: langchain-openai>=0.2
29
+ Requires-Dist: langgraph>=1.0
30
+ Requires-Dist: mcp>=2.3
31
+ Requires-Dist: pdfplumber>=0.11
32
+ Requires-Dist: pgvector>=0.3
33
+ Requires-Dist: psycopg[binary]>=3.2
34
+ Requires-Dist: pydantic-settings>=2.4
35
+ Requires-Dist: pydantic>=2.7
36
+ Requires-Dist: pymysql>=1.1
37
+ Requires-Dist: python-multipart>=0.0.9
38
+ Requires-Dist: sqlalchemy[asyncio]>=2.0.30
39
+ Requires-Dist: sqlglot>=25
40
+ Requires-Dist: typer>=0.12
41
+ Requires-Dist: uvicorn[standard]>=0.30
42
+ Provides-Extra: dev
43
+ Requires-Dist: httpx>=0.27; extra == 'dev'
44
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
45
+ Requires-Dist: pytest>=8; extra == 'dev'
46
+ Requires-Dist: reportlab>=4; extra == 'dev'
47
+ Requires-Dist: ruff>=0.6; extra == 'dev'
48
+ Description-Content-Type: text/markdown
49
+
50
+ # DataChat Agent
51
+
52
+ An open-source AI agent that answers plain-English questions from your SQL database and your PDFs, and shows the SQL or page each answer came from.
53
+
54
+ > Status: preview release 0.1.0 (week 6 of 8 toward v1.0). Not ready for production use yet.
55
+
56
+ ## Bring your own model key
57
+
58
+ DataChat Agent never ships with an LLM key. You use your own, from any OpenAI-compatible provider or Claude:
59
+
60
+ | Provider | Cost | Get a key |
61
+ | --- | --- | --- |
62
+ | Groq | Free tier | https://console.groq.com/keys |
63
+ | Google Gemini | Free tier | https://aistudio.google.com/apikey |
64
+ | OpenRouter (`:free` models) | Free | https://openrouter.ai/keys |
65
+ | Ollama | Free, runs locally | https://ollama.com |
66
+ | OpenAI, Claude | Paid | Their consoles |
67
+
68
+ ## Quickstart
69
+
70
+ ```bash
71
+ git clone https://github.com/Ashish-devl/Data-chat-agent.git
72
+ cd Data-chat-agent
73
+ cp .env.example .env # then fill in your LLM settings
74
+ pip install -e ".[dev]"
75
+ datachat-agent gen-secret # paste the output into SECRET_KEY in .env
76
+ datachat-agent check-llm # confirms your model key works
77
+ ```
78
+
79
+ Start Postgres (with pgvector) and Redis, then the API:
80
+
81
+ ```bash
82
+ docker compose up -d db redis
83
+ datachat-agent init-db
84
+ datachat-agent create-tenant "My company" # prints an admin API key, once
85
+ datachat-agent serve
86
+ ```
87
+
88
+ Or run everything in Docker: `docker compose up --build`.
89
+
90
+ API docs: http://localhost:8000/docs
91
+
92
+ ```bash
93
+ curl http://localhost:8000/v1/keys -H "Authorization: Bearer dc_live_..."
94
+ ```
95
+
96
+ ## Connect a database
97
+
98
+ DataChat Agent only ever reads. Create a database user that can `SELECT` and nothing else; the
99
+ connection test refuses users that can write.
100
+
101
+ ```bash
102
+ KEY="Authorization: Bearer dc_live_..."
103
+ curl -X POST localhost:8000/v1/connections -H "$KEY" -H "Content-Type: application/json" -d '{"name": "erp", "dsn": "postgresql://readonly:pass@host:5432/erp"}'
104
+ curl -X POST localhost:8000/v1/connections/<id>/test -H "$KEY" # must say "ok"
105
+ curl -X POST localhost:8000/v1/connections/<id>/scan -H "$KEY" # reads tables and columns
106
+ curl localhost:8000/v1/connections/<id>/schema -H "$KEY"
107
+ ```
108
+
109
+ Describe or hide columns (hidden columns can never be queried):
110
+
111
+ ```bash
112
+ curl -X PATCH localhost:8000/v1/schema-items/<item id> -H "$KEY" -H "Content-Type: application/json" -d '{"hidden": true}'
113
+ ```
114
+
115
+ Teach it your business language and give it worked examples:
116
+
117
+ ```bash
118
+ curl -X POST localhost:8000/v1/connections/<id>/terms -H "$KEY" -H "Content-Type: application/json" -d '{"term": "open PO", "definition": "a purchase order whose status is Open or Partially Received"}'
119
+ curl -X POST localhost:8000/v1/connections/<id>/examples -H "$KEY" -H "Content-Type: application/json" -d '{"question": "Suppliers in Ludhiana", "sql": "SELECT name FROM suppliers WHERE city = '"'"'Ludhiana'"'"'"}'
120
+ ```
121
+
122
+ ## Ask questions
123
+
124
+ ```bash
125
+ curl -X POST localhost:8000/v1/ask -H "$KEY" -H "Content-Type: application/json" -d '{"question": "Which production orders are delayed?", "session_id": "chat-1"}'
126
+ ```
127
+
128
+ ```json
129
+ {
130
+ "answer": "There are 13 delayed production orders in total ...",
131
+ "route": "database",
132
+ "sql": "SELECT ... LIMIT 500",
133
+ "columns": ["style_no", "qty_behind"],
134
+ "rows": [{"style_no": "26C1740", "qty_behind": 1390}],
135
+ "chart": {"type": "bar", "x": "style_no", "y": "qty_behind"},
136
+ "session_id": "chat-1",
137
+ "latency_ms": 7126
138
+ }
139
+ ```
140
+
141
+ Send the same `session_id` for follow-ups ("which buyers are those for?"); read a conversation
142
+ with `GET /v1/sessions/chat-1`. Pass `connection_id` when you have more than one database, and
143
+ `user_context` (e.g. `{"user_company": 7}`) when the connection uses row filters. Keys need the
144
+ `ask` scope.
145
+
146
+ ### Streaming
147
+
148
+ `POST /v1/ask/stream` takes the same body and answers as Server-Sent Events, so a chat window can
149
+ show the answer while it is being written:
150
+
151
+ ```text
152
+ event: meta data: {"session_id": "chat-1", "route": "database"}
153
+ event: data data: {"sql": "SELECT ...", "rows": [...], "row_count": 13}
154
+ event: token data: {"text": "There are 13 "}
155
+ event: token data: {"text": "delayed orders ..."}
156
+ event: done data: { ...the same body /v1/ask returns... }
157
+ ```
158
+
159
+ ### Feedback
160
+
161
+ ```bash
162
+ curl -X POST localhost:8000/v1/feedback -H "$KEY" -H "Content-Type: application/json" -d '{"query_id": "<from the answer>", "rating": "wrong", "comment": "Should include line 7"}'
163
+ ```
164
+
165
+ ### Limits and privacy
166
+
167
+ - Each API key may make `DEFAULT_RATE_LIMIT_PER_MINUTE` requests a minute (60 by default; 429 with
168
+ `Retry-After` beyond that). Use `RATE_LIMIT_BACKEND=redis` when several servers share the load.
169
+ - `DEFAULT_MONTHLY_TOKEN_LIMIT` caps model tokens per account per month. Operators set per-account
170
+ limits with `datachat-agent set-limits <tenant id> --per-minute 30 --monthly-tokens 500000` and
171
+ see them with `datachat-agent list-tenants`. `GET /v1/usage` shows an account its own use.
172
+ - `MASK_PII=true` replaces emails, phone numbers, card, Aadhaar and PAN numbers with placeholders
173
+ before anything reaches the model; the real values are put back into the answer.
174
+
175
+ ## Upload PDFs
176
+
177
+ ```bash
178
+ curl -X POST localhost:8000/v1/documents -H "$KEY" -F "file=@invoice.pdf;type=application/pdf"
179
+ curl localhost:8000/v1/documents -H "$KEY" # status: queued → ready
180
+ curl localhost:8000/v1/documents/<id>/items -H "$KEY" # line items from its tables
181
+ curl -o original.pdf localhost:8000/v1/documents/<id>/file -H "$KEY"
182
+ ```
183
+
184
+ PDFs with a text layer up to 20 MB; the same file uploaded twice is stored once. Text is split
185
+ into passages per page and searched by meaning and by keyword; tables become line items (item,
186
+ qty, unit, rate, amount). Answers from documents cite the file and page:
187
+
188
+ ```json
189
+ {
190
+ "answer": "The grand total of invoice KE/26-27/8231 is INR 773,550.00 [1].",
191
+ "route": "documents",
192
+ "citations": [{"ref": 1, "filename": "invoice_KE-26-27-8231.pdf", "page": 1,
193
+ "url": "/v1/documents/<id>/file#page=1"}]
194
+ }
195
+ ```
196
+
197
+ One question can use both sources ("Which orders are delayed, and what carton weight does the
198
+ manual allow?"). Uploads need a key with the `docs` scope. Scanned PDFs without a text layer are
199
+ not supported yet. Generate made-up sample PDFs with `python eval/datasets/make_pdfs.py out/`.
200
+
201
+ Processing runs in the background of the API by default. With Docker, a separate `worker`
202
+ service does it (`JOBS_BACKEND=arq`), and it also re-scans every database schema nightly.
203
+
204
+ Try the SQL step alone from the command line:
205
+
206
+ ```bash
207
+ datachat-agent ask-sql <connection id> "Which production orders are delayed?"
208
+ ```
209
+
210
+ ### Safety: three separate layers
211
+
212
+ 1. The database user must be read-only (checked on test and before every scan).
213
+ 2. Every query is parsed: one `SELECT` only, allowed tables only, no hidden columns, no admin
214
+ functions, `LIMIT 500` at most.
215
+ 3. Queries run in a read-only transaction with a 10-second timeout.
216
+
217
+ Per-user row filters (`"row_filters": {"orders": "company_id = :user_company"}`) are wrapped around
218
+ every use of the table; a question without the user's context is refused.
219
+
220
+ ## Five ways to use it
221
+
222
+ ### 1. Python library (no server)
223
+
224
+ ```bash
225
+ pip install datachat-agent
226
+ ```
227
+
228
+ ```python
229
+ from datachat_agent import DataChat
230
+
231
+ with DataChat("postgresql://readonly:secret@host/erp") as dc:
232
+ dc.scan_schema() # refuses a user that can write
233
+ dc.hide("employees", "salary") # never shown to the model, never queryable
234
+ dc.add_term("open PO", "status is Open or Partially Received")
235
+ dc.add_pdf("invoice_8231.pdf")
236
+ print(dc.ask("What is the total of invoice KE/26-27/8231?").answer)
237
+ ```
238
+
239
+ It stores what it learns in its own Postgres with pgvector (`DATABASE_URL`, or
240
+ `DataChat(..., storage_url=...)`). `ask` returns `answer`, `sql`, `rows`, `citations` and `chart`;
241
+ `aask`, `ascan_schema` and `aadd_pdf` are the async versions.
242
+
243
+ ### 2. Inside your FastAPI app
244
+
245
+ ```python
246
+ from datachat_agent import mount
247
+
248
+ mount(app, prefix="/ai") # serves /ai/v1/ask, /ai/v1/documents, ... and /ai/widget.js
249
+ ```
250
+
251
+ Run `datachat-agent init-db` once to create its tables.
252
+
253
+ ### 3. Self-hosted server (Docker)
254
+
255
+ `docker compose up --build` starts the API, a background worker, Postgres with pgvector and
256
+ Redis. Configure it with `.env`.
257
+
258
+ ### 4. HTTP API, JavaScript SDK and chat widget
259
+
260
+ The API is described above and at `/docs` on a running server. From JavaScript:
261
+
262
+ ```js
263
+ import { DataChatClient } from "datachat-agent"; // npm install datachat-agent
264
+ const dc = new DataChatClient({ baseUrl: "https://api.example.com", apiKey: "dc_live_..." });
265
+ const a = await dc.ask("Which production orders are delayed?");
266
+ ```
267
+
268
+ For a web page, create a **publishable** key that works only on your websites and can only ask:
269
+
270
+ ```bash
271
+ curl -X POST localhost:8000/v1/keys -H "$KEY" -H "Content-Type: application/json" -d '{"name": "website", "type": "publishable", "allowed_domains": ["erp.example.com"]}'
272
+ ```
273
+
274
+ then add one line to the page:
275
+
276
+ ```html
277
+ <script src="https://your-datachat-server/widget.js" data-key="dc_pub_..."
278
+ data-title="Ask about our data"></script>
279
+ ```
280
+
281
+ Optional: `data-connection`, `data-position="left"`, `data-color="#0f766e"`,
282
+ `data-placeholder`. The widget streams answers, shows a small table and the sources, and has
283
+ Helpful / Wrong buttons. Publishable keys cannot send `user_context`, so a connection with row
284
+ filters refuses widget questions rather than guessing whose rows to show.
285
+
286
+ ### 5. MCP server (Claude Desktop, IDEs)
287
+
288
+ ```bash
289
+ datachat-agent mcp --api-key dc_live_...
290
+ ```
291
+
292
+ Tools: `list_tables`, `describe_table`, `run_sql`, `search_documents`, `get_document_items` and
293
+ `ask`, all read-only. In Claude Desktop's `claude_desktop_config.json`:
294
+
295
+ ```json
296
+ {
297
+ "mcpServers": {
298
+ "datachat": {
299
+ "command": "datachat-agent",
300
+ "args": ["mcp"],
301
+ "env": {
302
+ "DATACHAT_API_KEY": "dc_live_...",
303
+ "DATABASE_URL": "postgresql://datachat:datachat@localhost:5432/datachat",
304
+ "SECRET_KEY": "...",
305
+ "LLM_BASE_URL": "...", "LLM_MODEL": "...", "LLM_API_KEY": "..."
306
+ }
307
+ }
308
+ }
309
+ }
310
+ ```
311
+
312
+ ### Command line
313
+
314
+ | Command | Does |
315
+ | --- | --- |
316
+ | `datachat-agent serve` / `worker` | API server / background worker |
317
+ | `datachat-agent init-db` | Create or upgrade tables |
318
+ | `datachat-agent create-tenant NAME` | New account and its first admin key |
319
+ | `datachat-agent scan CONNECTION_ID` | Re-read a database schema |
320
+ | `datachat-agent ingest TENANT_ID FILE...` | Upload and process PDFs |
321
+ | `datachat-agent ask-sql CONNECTION_ID "question"` | Try the SQL step |
322
+ | `datachat-agent mcp` | MCP server over stdio |
323
+ | `datachat-agent list-tenants` / `set-limits` | Usage and limits |
324
+ | `datachat-agent check-llm` / `gen-secret` | Test the model / make a SECRET_KEY |
325
+
326
+ ## Development
327
+
328
+ ```bash
329
+ docker compose up -d db redis
330
+ docker compose --profile dev up -d mysql # optional: MySQL tests
331
+ pytest # unit + integration tests
332
+ ruff check . && ruff format --check .
333
+ ```
334
+
335
+ Integration tests create a throwaway `datachat_test` database and load `erp_demo`, a made-up
336
+ garment-factory ERP (`eval/datasets/erp_demo.py`), into Postgres and MySQL. They are skipped when
337
+ the databases are not running. Load the sample yourself to try the API:
338
+
339
+ ```bash
340
+ python eval/datasets/erp_demo.py postgresql://datachat:datachat@localhost:5432/postgres
341
+ # then connect with: postgresql://erp_readonly:erp_readonly@localhost:5432/erp_demo
342
+ ```
343
+
344
+ After changing `server/models.py`, generate a migration with
345
+ `datachat-agent make-migration "what changed"`.
346
+
347
+ JavaScript parts: `cd sdk-js && npm ci && npm test`; `cd widget && npm ci && npm test && npm run build`,
348
+ then copy `widget/dist/widget.js` to `packages/datachat_agent/server/static/` (CI checks they
349
+ match). Releases: see [docs/RELEASING.md](docs/RELEASING.md).
350
+
351
+ ## Licence
352
+
353
+ MIT