hatidata-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.
@@ -0,0 +1,183 @@
1
+ Metadata-Version: 2.4
2
+ Name: hatidata-agent
3
+ Version: 0.1.0
4
+ Summary: HatiData Agent SDK — RAM for Agents
5
+ Author-email: Marviy Pte Ltd <eng@hatidata.com>
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://hatidata.com
8
+ Project-URL: Documentation, https://docs.hatiosai.com/hatidata
9
+ Project-URL: Repository, https://github.com/HatiOS-AI/HatiData-Core
10
+ Project-URL: Changelog, https://github.com/HatiOS-AI/HatiData-Core/releases
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Database
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: psycopg2-binary>=2.9
24
+ Provides-Extra: async
25
+ Requires-Dist: asyncpg>=0.29; extra == "async"
26
+ Provides-Extra: langchain
27
+ Requires-Dist: langchain-community>=0.2; extra == "langchain"
28
+ Provides-Extra: mcp
29
+ Requires-Dist: mcp>=1.0; extra == "mcp"
30
+ Provides-Extra: all
31
+ Requires-Dist: asyncpg>=0.29; extra == "all"
32
+ Requires-Dist: langchain-community>=0.2; extra == "all"
33
+ Requires-Dist: mcp>=1.0; extra == "all"
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=8; extra == "dev"
36
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
37
+
38
+ # HatiData Agent SDK
39
+
40
+ [![PyPI version](https://img.shields.io/pypi/v/hatidata-agent.svg)](https://pypi.org/project/hatidata-agent/)
41
+ [![Python versions](https://img.shields.io/pypi/pyversions/hatidata-agent.svg)](https://pypi.org/project/hatidata-agent/)
42
+ [![License](https://img.shields.io/pypi/l/hatidata-agent.svg)](https://github.com/HatiOS-AI/HatiData-Core/blob/main/LICENSE)
43
+
44
+ **RAM for Agents** — Python SDK for AI agents to query HatiData's in-VPC data warehouse with sub-10ms latency via Postgres wire protocol.
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ pip install hatidata-agent
50
+
51
+ # With async support
52
+ pip install hatidata-agent[async]
53
+
54
+ # With LangChain support
55
+ pip install hatidata-agent[langchain]
56
+
57
+ # With MCP server
58
+ pip install hatidata-agent[mcp]
59
+
60
+ # Everything
61
+ pip install hatidata-agent[all]
62
+ ```
63
+
64
+ ## Quick Start
65
+
66
+ ```python
67
+ from hatidata_agent import HatiDataAgent
68
+
69
+ agent = HatiDataAgent(
70
+ host="proxy.internal",
71
+ port=5439,
72
+ agent_id="my-agent",
73
+ framework="langchain",
74
+ )
75
+
76
+ # Simple query
77
+ rows = agent.query("SELECT * FROM customers WHERE status = 'active' LIMIT 10")
78
+ print(rows) # [{"id": 1, "name": "Acme Corp", ...}, ...]
79
+ ```
80
+
81
+ ## Features
82
+
83
+ - **Sub-10ms query latency** -- In-VPC execution, no data leaves your network
84
+ - **Postgres wire protocol** -- Works with any Postgres client library
85
+ - **Reasoning chain tracking** -- Multi-step audit trails for agent workflows
86
+ - **RAG context retrieval** -- Full-text and vector similarity search
87
+ - **LangChain integration** -- Drop-in `SQLDatabase` replacement for LangChain agents
88
+ - **MCP server** -- Expose HatiData as tools for Claude and other MCP-compatible agents
89
+ - **Agent identification** -- Per-agent billing, priority scheduling, and audit via Postgres startup parameters
90
+ - **Snowflake SQL compatible** -- Bring existing queries without rewrites
91
+
92
+ ## Reasoning Chain Tracking
93
+
94
+ Track multi-step reasoning chains for audit and debugging:
95
+
96
+ ```python
97
+ with agent.reasoning_chain("req-001") as chain:
98
+ # Step 0: Discover tables
99
+ tables = chain.query("SELECT table_name FROM information_schema.tables")
100
+
101
+ # Step 1: Get relevant data
102
+ customers = chain.query("SELECT * FROM customers WHERE tier = 'enterprise'", step=1)
103
+
104
+ # Step 2: Aggregate
105
+ revenue = chain.query("SELECT SUM(revenue) FROM orders WHERE customer_id IN (...)", step=2)
106
+ ```
107
+
108
+ ## RAG Context Retrieval
109
+
110
+ ```python
111
+ # Full-text search
112
+ context = agent.get_context("customers", "enterprise accounts in US", top_k=5)
113
+
114
+ # Vector similarity search
115
+ context = agent.get_rag_context("docs", "embedding", query_vector, top_k=10)
116
+ ```
117
+
118
+ ## LangChain Integration
119
+
120
+ ```python
121
+ from hatidata_agent.langchain import HatiDataSQLDatabase
122
+ from langchain.agents import create_sql_agent
123
+
124
+ db = HatiDataSQLDatabase(
125
+ host="proxy.internal",
126
+ port=5439,
127
+ agent_id="sql-agent-1",
128
+ )
129
+
130
+ agent = create_sql_agent(llm=llm, db=db, verbose=True)
131
+ result = agent.run("How many enterprise customers do we have?")
132
+ ```
133
+
134
+ ## MCP Server
135
+
136
+ Run as an MCP server for Claude and other MCP-compatible agents:
137
+
138
+ ```bash
139
+ # Start the MCP server
140
+ hatidata-mcp-server --host proxy.internal --port 5439
141
+
142
+ # Or add to Claude Code's MCP config:
143
+ # ~/.claude/mcp.json
144
+ {
145
+ "mcpServers": {
146
+ "hatidata": {
147
+ "command": "hatidata-mcp-server",
148
+ "args": ["--host", "proxy.internal", "--port", "5439"]
149
+ }
150
+ }
151
+ }
152
+ ```
153
+
154
+ ### MCP Tools
155
+
156
+ | Tool | Description |
157
+ |------|-------------|
158
+ | `query` | Execute SQL and return JSON results |
159
+ | `list_tables` | List available tables |
160
+ | `describe_table` | Get table schema |
161
+ | `get_context` | RAG context retrieval via full-text search |
162
+
163
+ ## Agent Identification
164
+
165
+ The SDK automatically identifies agents via Postgres startup parameters:
166
+
167
+ | Parameter | Purpose |
168
+ |-----------|---------|
169
+ | `hatidata_agent_id` | Unique agent identifier |
170
+ | `hatidata_framework` | AI framework (langchain, crewai, autogen, etc.) |
171
+ | `hatidata_priority` | Scheduling priority (low, normal, high, critical) |
172
+ | `hatidata_request_id` | Request/reasoning chain ID |
173
+ | `hatidata_reasoning_step` | Step number within a chain |
174
+
175
+ These enable per-agent billing, priority scheduling, audit trails, and the Agent Tax Report showing savings vs legacy cloud warehouses.
176
+
177
+ ## Documentation
178
+
179
+ Full documentation is available at [docs.hatiosai.com/hatidata](https://docs.hatiosai.com/hatidata).
180
+
181
+ ## License
182
+
183
+ Apache License 2.0. Copyright (c) Marviy Pte Ltd. See [LICENSE](https://github.com/HatiOS-AI/HatiData-Core/blob/main/LICENSE) for details.
@@ -0,0 +1,146 @@
1
+ # HatiData Agent SDK
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/hatidata-agent.svg)](https://pypi.org/project/hatidata-agent/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/hatidata-agent.svg)](https://pypi.org/project/hatidata-agent/)
5
+ [![License](https://img.shields.io/pypi/l/hatidata-agent.svg)](https://github.com/HatiOS-AI/HatiData-Core/blob/main/LICENSE)
6
+
7
+ **RAM for Agents** — Python SDK for AI agents to query HatiData's in-VPC data warehouse with sub-10ms latency via Postgres wire protocol.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pip install hatidata-agent
13
+
14
+ # With async support
15
+ pip install hatidata-agent[async]
16
+
17
+ # With LangChain support
18
+ pip install hatidata-agent[langchain]
19
+
20
+ # With MCP server
21
+ pip install hatidata-agent[mcp]
22
+
23
+ # Everything
24
+ pip install hatidata-agent[all]
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ```python
30
+ from hatidata_agent import HatiDataAgent
31
+
32
+ agent = HatiDataAgent(
33
+ host="proxy.internal",
34
+ port=5439,
35
+ agent_id="my-agent",
36
+ framework="langchain",
37
+ )
38
+
39
+ # Simple query
40
+ rows = agent.query("SELECT * FROM customers WHERE status = 'active' LIMIT 10")
41
+ print(rows) # [{"id": 1, "name": "Acme Corp", ...}, ...]
42
+ ```
43
+
44
+ ## Features
45
+
46
+ - **Sub-10ms query latency** -- In-VPC execution, no data leaves your network
47
+ - **Postgres wire protocol** -- Works with any Postgres client library
48
+ - **Reasoning chain tracking** -- Multi-step audit trails for agent workflows
49
+ - **RAG context retrieval** -- Full-text and vector similarity search
50
+ - **LangChain integration** -- Drop-in `SQLDatabase` replacement for LangChain agents
51
+ - **MCP server** -- Expose HatiData as tools for Claude and other MCP-compatible agents
52
+ - **Agent identification** -- Per-agent billing, priority scheduling, and audit via Postgres startup parameters
53
+ - **Snowflake SQL compatible** -- Bring existing queries without rewrites
54
+
55
+ ## Reasoning Chain Tracking
56
+
57
+ Track multi-step reasoning chains for audit and debugging:
58
+
59
+ ```python
60
+ with agent.reasoning_chain("req-001") as chain:
61
+ # Step 0: Discover tables
62
+ tables = chain.query("SELECT table_name FROM information_schema.tables")
63
+
64
+ # Step 1: Get relevant data
65
+ customers = chain.query("SELECT * FROM customers WHERE tier = 'enterprise'", step=1)
66
+
67
+ # Step 2: Aggregate
68
+ revenue = chain.query("SELECT SUM(revenue) FROM orders WHERE customer_id IN (...)", step=2)
69
+ ```
70
+
71
+ ## RAG Context Retrieval
72
+
73
+ ```python
74
+ # Full-text search
75
+ context = agent.get_context("customers", "enterprise accounts in US", top_k=5)
76
+
77
+ # Vector similarity search
78
+ context = agent.get_rag_context("docs", "embedding", query_vector, top_k=10)
79
+ ```
80
+
81
+ ## LangChain Integration
82
+
83
+ ```python
84
+ from hatidata_agent.langchain import HatiDataSQLDatabase
85
+ from langchain.agents import create_sql_agent
86
+
87
+ db = HatiDataSQLDatabase(
88
+ host="proxy.internal",
89
+ port=5439,
90
+ agent_id="sql-agent-1",
91
+ )
92
+
93
+ agent = create_sql_agent(llm=llm, db=db, verbose=True)
94
+ result = agent.run("How many enterprise customers do we have?")
95
+ ```
96
+
97
+ ## MCP Server
98
+
99
+ Run as an MCP server for Claude and other MCP-compatible agents:
100
+
101
+ ```bash
102
+ # Start the MCP server
103
+ hatidata-mcp-server --host proxy.internal --port 5439
104
+
105
+ # Or add to Claude Code's MCP config:
106
+ # ~/.claude/mcp.json
107
+ {
108
+ "mcpServers": {
109
+ "hatidata": {
110
+ "command": "hatidata-mcp-server",
111
+ "args": ["--host", "proxy.internal", "--port", "5439"]
112
+ }
113
+ }
114
+ }
115
+ ```
116
+
117
+ ### MCP Tools
118
+
119
+ | Tool | Description |
120
+ |------|-------------|
121
+ | `query` | Execute SQL and return JSON results |
122
+ | `list_tables` | List available tables |
123
+ | `describe_table` | Get table schema |
124
+ | `get_context` | RAG context retrieval via full-text search |
125
+
126
+ ## Agent Identification
127
+
128
+ The SDK automatically identifies agents via Postgres startup parameters:
129
+
130
+ | Parameter | Purpose |
131
+ |-----------|---------|
132
+ | `hatidata_agent_id` | Unique agent identifier |
133
+ | `hatidata_framework` | AI framework (langchain, crewai, autogen, etc.) |
134
+ | `hatidata_priority` | Scheduling priority (low, normal, high, critical) |
135
+ | `hatidata_request_id` | Request/reasoning chain ID |
136
+ | `hatidata_reasoning_step` | Step number within a chain |
137
+
138
+ These enable per-agent billing, priority scheduling, audit trails, and the Agent Tax Report showing savings vs legacy cloud warehouses.
139
+
140
+ ## Documentation
141
+
142
+ Full documentation is available at [docs.hatiosai.com/hatidata](https://docs.hatiosai.com/hatidata).
143
+
144
+ ## License
145
+
146
+ Apache License 2.0. Copyright (c) Marviy Pte Ltd. See [LICENSE](https://github.com/HatiOS-AI/HatiData-Core/blob/main/LICENSE) for details.
@@ -0,0 +1,23 @@
1
+ """HatiData Agent SDK — Sub-10ms SQL for AI agents.
2
+
3
+ Provides agent-aware database access to HatiData's in-VPC data warehouse
4
+ via the Postgres wire protocol. Agents identify themselves through startup
5
+ parameters, enabling per-agent billing, scheduling, and audit trails.
6
+
7
+ Quick start::
8
+
9
+ from hatidata_agent import HatiDataAgent
10
+
11
+ agent = HatiDataAgent(
12
+ host="localhost",
13
+ port=5439,
14
+ agent_id="my-agent",
15
+ framework="langchain",
16
+ )
17
+ rows = agent.query("SELECT * FROM customers WHERE status = 'active' LIMIT 10")
18
+ """
19
+
20
+ from hatidata_agent.client import HatiDataAgent
21
+
22
+ __version__ = "0.1.0"
23
+ __all__ = ["HatiDataAgent"]
@@ -0,0 +1,283 @@
1
+ """HatiData agent client — Postgres wire protocol with agent startup params.
2
+
3
+ Usage::
4
+
5
+ from hatidata_agent import HatiDataAgent
6
+
7
+ agent = HatiDataAgent(
8
+ host="proxy.internal",
9
+ port=5439,
10
+ agent_id="data-analyst-agent",
11
+ framework="langchain",
12
+ database="analytics",
13
+ )
14
+
15
+ # Simple query
16
+ rows = agent.query("SELECT COUNT(*) FROM orders")
17
+
18
+ # With reasoning chain tracking
19
+ with agent.reasoning_chain("req-001") as chain:
20
+ tables = chain.query("SELECT table_name FROM information_schema.tables")
21
+ data = chain.query("SELECT * FROM customers LIMIT 10", step=1)
22
+ summary = chain.query("SELECT AVG(revenue) FROM orders", step=2)
23
+
24
+ # RAG context retrieval
25
+ context = agent.get_context("customers", "enterprise accounts", top_k=5)
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import uuid
31
+ from contextlib import contextmanager
32
+ from typing import Any, Generator, Optional
33
+
34
+ import psycopg2
35
+ import psycopg2.extras
36
+
37
+
38
+ class HatiDataAgent:
39
+ """Agent-aware HatiData client using psycopg2.
40
+
41
+ Connects via Postgres wire protocol and identifies itself through
42
+ startup parameters that the proxy reads for billing, scheduling,
43
+ and audit.
44
+
45
+ Args:
46
+ host: Proxy hostname.
47
+ port: Proxy port (default 5439).
48
+ agent_id: Unique identifier for this agent instance.
49
+ framework: AI framework name (langchain, crewai, autogen, etc.).
50
+ database: Database name (default "hatidata").
51
+ user: Username (default "agent").
52
+ password: Password (default empty for dev mode).
53
+ priority: Query priority (low, normal, high, critical).
54
+ connect_timeout: Connection timeout in seconds.
55
+ """
56
+
57
+ def __init__(
58
+ self,
59
+ host: str = "localhost",
60
+ port: int = 5439,
61
+ agent_id: str = "",
62
+ framework: str = "custom",
63
+ database: str = "hatidata",
64
+ user: str = "agent",
65
+ password: str = "",
66
+ priority: str = "normal",
67
+ connect_timeout: int = 10,
68
+ ):
69
+ self.host = host
70
+ self.port = port
71
+ self.agent_id = agent_id or f"agent-{uuid.uuid4().hex[:8]}"
72
+ self.framework = framework
73
+ self.database = database
74
+ self.user = user
75
+ self.password = password
76
+ self.priority = priority
77
+ self.connect_timeout = connect_timeout
78
+ self._conn: Optional[psycopg2.extensions.connection] = None
79
+
80
+ def _get_connection(self) -> psycopg2.extensions.connection:
81
+ """Get or create a connection with agent startup params."""
82
+ if self._conn is None or self._conn.closed:
83
+ # Agent identification via startup parameters.
84
+ # These are read by the HatiData proxy during connection setup.
85
+ options = (
86
+ f"-c hatidata_agent_id={self.agent_id} "
87
+ f"-c hatidata_framework={self.framework} "
88
+ f"-c hatidata_priority={self.priority}"
89
+ )
90
+
91
+ self._conn = psycopg2.connect(
92
+ host=self.host,
93
+ port=self.port,
94
+ dbname=self.database,
95
+ user=self.user,
96
+ password=self.password,
97
+ options=options,
98
+ connect_timeout=self.connect_timeout,
99
+ application_name=f"hatidata-agent/{self.framework}",
100
+ )
101
+ self._conn.autocommit = True
102
+
103
+ return self._conn
104
+
105
+ def query(
106
+ self,
107
+ sql: str,
108
+ params: Optional[tuple[Any, ...]] = None,
109
+ request_id: Optional[str] = None,
110
+ ) -> list[dict[str, Any]]:
111
+ """Execute a SQL query and return results as a list of dicts.
112
+
113
+ Args:
114
+ sql: SQL query string.
115
+ params: Optional query parameters (for parameterized queries).
116
+ request_id: Optional request ID for tracing.
117
+
118
+ Returns:
119
+ List of row dicts with column names as keys.
120
+ """
121
+ conn = self._get_connection()
122
+
123
+ # Set request_id for this query if provided.
124
+ if request_id:
125
+ with conn.cursor() as cur:
126
+ cur.execute(f"SET hatidata_request_id = '{request_id}'")
127
+
128
+ with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
129
+ cur.execute(sql, params)
130
+ if cur.description:
131
+ return [dict(row) for row in cur.fetchall()]
132
+ return []
133
+
134
+ def execute(self, sql: str, params: Optional[tuple[Any, ...]] = None) -> int:
135
+ """Execute a SQL statement (INSERT, UPDATE, DELETE) and return row count.
136
+
137
+ Args:
138
+ sql: SQL statement.
139
+ params: Optional query parameters.
140
+
141
+ Returns:
142
+ Number of affected rows.
143
+ """
144
+ conn = self._get_connection()
145
+ with conn.cursor() as cur:
146
+ cur.execute(sql, params)
147
+ return cur.rowcount
148
+
149
+ def get_context(
150
+ self,
151
+ table: str,
152
+ search_query: str,
153
+ top_k: int = 10,
154
+ ) -> list[dict[str, Any]]:
155
+ """Retrieve RAG context using HatiData's built-in context function.
156
+
157
+ This uses the `hatidata_context()` UDF which the proxy rewrites
158
+ to an efficient full-text search subquery.
159
+
160
+ Args:
161
+ table: Table name to search.
162
+ search_query: Natural language search query.
163
+ top_k: Maximum number of results.
164
+
165
+ Returns:
166
+ List of matching rows as dicts.
167
+ """
168
+ sql = f"SELECT * FROM hatidata_context('{table}', '{search_query}', {top_k})"
169
+ return self.query(sql)
170
+
171
+ def get_rag_context(
172
+ self,
173
+ table: str,
174
+ embedding_col: str,
175
+ vector: list[float],
176
+ top_k: int = 10,
177
+ ) -> list[dict[str, Any]]:
178
+ """Retrieve RAG context via vector similarity search.
179
+
180
+ Args:
181
+ table: Table name containing embeddings.
182
+ embedding_col: Column name with embedding vectors.
183
+ vector: Query embedding vector.
184
+ top_k: Maximum number of results.
185
+
186
+ Returns:
187
+ List of matching rows ordered by similarity.
188
+ """
189
+ vector_str = f"[{', '.join(str(v) for v in vector)}]"
190
+ sql = (
191
+ f"SELECT * FROM hatidata_rag_context('{table}', '{embedding_col}', "
192
+ f"{vector_str}, {top_k})"
193
+ )
194
+ return self.query(sql)
195
+
196
+ @contextmanager
197
+ def reasoning_chain(
198
+ self,
199
+ request_id: Optional[str] = None,
200
+ parent_request_id: Optional[str] = None,
201
+ ) -> Generator[ReasoningChain, None, None]:
202
+ """Context manager for tracking a multi-step reasoning chain.
203
+
204
+ Usage::
205
+
206
+ with agent.reasoning_chain("req-001") as chain:
207
+ tables = chain.query("SELECT table_name FROM information_schema.tables")
208
+ data = chain.query("SELECT * FROM orders LIMIT 10", step=1)
209
+
210
+ Args:
211
+ request_id: Unique ID for this reasoning chain.
212
+ parent_request_id: Parent chain ID for nested reasoning.
213
+ """
214
+ rid = request_id or f"req-{uuid.uuid4().hex[:12]}"
215
+ chain = ReasoningChain(self, rid, parent_request_id)
216
+ try:
217
+ yield chain
218
+ finally:
219
+ chain._finalize()
220
+
221
+ def close(self) -> None:
222
+ """Close the underlying database connection."""
223
+ if self._conn and not self._conn.closed:
224
+ self._conn.close()
225
+ self._conn = None
226
+
227
+ def __enter__(self) -> HatiDataAgent:
228
+ return self
229
+
230
+ def __exit__(self, *args: Any) -> None:
231
+ self.close()
232
+
233
+
234
+ class ReasoningChain:
235
+ """Tracks a multi-step reasoning chain with auto-incrementing steps."""
236
+
237
+ def __init__(
238
+ self,
239
+ agent: HatiDataAgent,
240
+ request_id: str,
241
+ parent_request_id: Optional[str] = None,
242
+ ):
243
+ self._agent = agent
244
+ self.request_id = request_id
245
+ self.parent_request_id = parent_request_id
246
+ self._step = 0
247
+
248
+ def query(
249
+ self,
250
+ sql: str,
251
+ params: Optional[tuple[Any, ...]] = None,
252
+ step: Optional[int] = None,
253
+ ) -> list[dict[str, Any]]:
254
+ """Execute a query as part of this reasoning chain.
255
+
256
+ Args:
257
+ sql: SQL query.
258
+ params: Optional query parameters.
259
+ step: Explicit step number (auto-increments if not provided).
260
+ """
261
+ current_step = step if step is not None else self._step
262
+ self._step = current_step + 1
263
+
264
+ conn = self._agent._get_connection()
265
+ with conn.cursor() as cur:
266
+ cur.execute(f"SET hatidata_request_id = '{self.request_id}'")
267
+ cur.execute(f"SET hatidata_reasoning_step = '{current_step}'")
268
+ if self.parent_request_id:
269
+ cur.execute(
270
+ f"SET hatidata_parent_request_id = '{self.parent_request_id}'"
271
+ )
272
+
273
+ return self._agent.query(sql, params)
274
+
275
+ def _finalize(self) -> None:
276
+ """Reset session vars after chain completes."""
277
+ try:
278
+ conn = self._agent._get_connection()
279
+ with conn.cursor() as cur:
280
+ cur.execute("SET hatidata_request_id = ''")
281
+ cur.execute("SET hatidata_reasoning_step = ''")
282
+ except Exception:
283
+ pass
@@ -0,0 +1,169 @@
1
+ """LangChain integration for HatiData agent queries.
2
+
3
+ Provides a LangChain-compatible SQLDatabase wrapper that automatically
4
+ identifies itself as a LangChain agent, enabling per-agent billing,
5
+ scheduling, and audit in HatiData.
6
+
7
+ Usage::
8
+
9
+ from hatidata_agent.langchain import HatiDataSQLDatabase
10
+ from langchain.agents import create_sql_agent
11
+
12
+ db = HatiDataSQLDatabase(
13
+ host="proxy.internal",
14
+ port=5439,
15
+ agent_id="sql-agent-1",
16
+ )
17
+
18
+ agent = create_sql_agent(llm=llm, db=db, verbose=True)
19
+ result = agent.run("How many active enterprise customers do we have?")
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ from typing import Any, Optional, Sequence
25
+
26
+ from hatidata_agent.client import HatiDataAgent
27
+
28
+
29
+ class HatiDataSQLDatabase:
30
+ """LangChain-compatible SQL database wrapper for HatiData.
31
+
32
+ Implements the interface expected by LangChain's SQL agent tools:
33
+ - `run(command)` — Execute SQL and return string result
34
+ - `get_usable_table_names()` — List available tables
35
+ - `get_table_info(table_names)` — Get DDL/schema for tables
36
+ - `dialect` — Database dialect identifier
37
+
38
+ This wrapper automatically sets `framework="langchain"` for
39
+ agent identification in HatiData's billing and audit systems.
40
+ """
41
+
42
+ dialect = "postgresql"
43
+
44
+ def __init__(
45
+ self,
46
+ host: str = "localhost",
47
+ port: int = 5439,
48
+ agent_id: str = "langchain-agent",
49
+ database: str = "hatidata",
50
+ user: str = "agent",
51
+ password: str = "",
52
+ priority: str = "normal",
53
+ include_tables: Optional[Sequence[str]] = None,
54
+ sample_rows_in_table_info: int = 3,
55
+ ):
56
+ self._agent = HatiDataAgent(
57
+ host=host,
58
+ port=port,
59
+ agent_id=agent_id,
60
+ framework="langchain",
61
+ database=database,
62
+ user=user,
63
+ password=password,
64
+ priority=priority,
65
+ )
66
+ self._include_tables = set(include_tables) if include_tables else None
67
+ self._sample_rows = sample_rows_in_table_info
68
+
69
+ def run(self, command: str, fetch: str = "all") -> str:
70
+ """Execute SQL and return results as a string.
71
+
72
+ Args:
73
+ command: SQL query or statement.
74
+ fetch: "all" to fetch all rows, "one" for single row.
75
+
76
+ Returns:
77
+ String representation of the results.
78
+ """
79
+ rows = self._agent.query(command)
80
+ if not rows:
81
+ return ""
82
+ if fetch == "one" and rows:
83
+ return str(rows[0])
84
+ return str(rows)
85
+
86
+ def run_no_throw(self, command: str, fetch: str = "all") -> str:
87
+ """Execute SQL, returning error message on failure instead of raising."""
88
+ try:
89
+ return self.run(command, fetch)
90
+ except Exception as e:
91
+ return f"Error: {e}"
92
+
93
+ def get_usable_table_names(self) -> list[str]:
94
+ """Return list of table names available for querying."""
95
+ rows = self._agent.query(
96
+ "SELECT table_name FROM information_schema.tables "
97
+ "WHERE table_schema = 'main' ORDER BY table_name"
98
+ )
99
+ tables = [row["table_name"] for row in rows]
100
+
101
+ if self._include_tables:
102
+ tables = [t for t in tables if t in self._include_tables]
103
+
104
+ return tables
105
+
106
+ def get_table_info(self, table_names: Optional[list[str]] = None) -> str:
107
+ """Get CREATE TABLE DDL and sample rows for the given tables.
108
+
109
+ Args:
110
+ table_names: Tables to describe. If None, describes all usable tables.
111
+
112
+ Returns:
113
+ Multi-line string with DDL and sample rows for each table.
114
+ """
115
+ if table_names is None:
116
+ table_names = self.get_usable_table_names()
117
+
118
+ info_parts = []
119
+ for table in table_names:
120
+ # Get column info.
121
+ cols = self._agent.query(
122
+ f"SELECT column_name, data_type, is_nullable "
123
+ f"FROM information_schema.columns "
124
+ f"WHERE table_name = '{table}' ORDER BY ordinal_position"
125
+ )
126
+
127
+ # Build CREATE TABLE statement.
128
+ col_defs = []
129
+ for col in cols:
130
+ nullable = "" if col.get("is_nullable") == "YES" else " NOT NULL"
131
+ col_defs.append(
132
+ f" {col['column_name']} {col['data_type']}{nullable}"
133
+ )
134
+
135
+ ddl = f"CREATE TABLE {table} (\n" + ",\n".join(col_defs) + "\n);"
136
+
137
+ # Sample rows.
138
+ sample = ""
139
+ if self._sample_rows > 0:
140
+ rows = self._agent.query(
141
+ f"SELECT * FROM {table} LIMIT {self._sample_rows}"
142
+ )
143
+ if rows:
144
+ sample = (
145
+ f"\n\n/*\n{self._sample_rows} rows from {table} table:\n"
146
+ + "\t".join(rows[0].keys())
147
+ + "\n"
148
+ )
149
+ for row in rows:
150
+ sample += "\t".join(str(v) for v in row.values()) + "\n"
151
+ sample += "*/"
152
+
153
+ info_parts.append(f"{ddl}{sample}")
154
+
155
+ return "\n\n".join(info_parts)
156
+
157
+ def get_table_info_no_throw(
158
+ self, table_names: Optional[list[str]] = None
159
+ ) -> str:
160
+ """Get table info, returning error message on failure."""
161
+ try:
162
+ return self.get_table_info(table_names)
163
+ except Exception as e:
164
+ return f"Error: {e}"
165
+
166
+ @property
167
+ def table_info(self) -> str:
168
+ """Property for LangChain compatibility."""
169
+ return self.get_table_info()
@@ -0,0 +1,268 @@
1
+ """MCP server for HatiData agent access.
2
+
3
+ Exposes HatiData as an MCP (Model Context Protocol) server, allowing
4
+ Claude and other MCP-compatible agents to query the data warehouse
5
+ directly. Tools provided:
6
+
7
+ - `query` — Execute SQL and return results
8
+ - `list_tables` — List available tables
9
+ - `describe_table` — Get schema for a table
10
+ - `get_context` — RAG context retrieval via full-text search
11
+
12
+ Usage::
13
+
14
+ # Start the MCP server (stdio transport)
15
+ hatidata-mcp-server --host localhost --port 5439
16
+
17
+ # Or in Python
18
+ from hatidata_agent.mcp_server import create_server
19
+ server = create_server(host="localhost", port=5439)
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import argparse
25
+ import json
26
+ import sys
27
+ from typing import Any
28
+
29
+ from hatidata_agent.client import HatiDataAgent
30
+
31
+
32
+ def create_tools(agent: HatiDataAgent) -> list[dict[str, Any]]:
33
+ """Create MCP tool definitions."""
34
+ return [
35
+ {
36
+ "name": "query",
37
+ "description": (
38
+ "Execute a SQL query against the HatiData warehouse. "
39
+ "Returns results as a JSON array of objects. "
40
+ "Supports Snowflake-compatible SQL syntax."
41
+ ),
42
+ "inputSchema": {
43
+ "type": "object",
44
+ "properties": {
45
+ "sql": {
46
+ "type": "string",
47
+ "description": "The SQL query to execute",
48
+ },
49
+ },
50
+ "required": ["sql"],
51
+ },
52
+ },
53
+ {
54
+ "name": "list_tables",
55
+ "description": "List all available tables in the database.",
56
+ "inputSchema": {
57
+ "type": "object",
58
+ "properties": {},
59
+ },
60
+ },
61
+ {
62
+ "name": "describe_table",
63
+ "description": "Get the schema (columns, types) for a specific table.",
64
+ "inputSchema": {
65
+ "type": "object",
66
+ "properties": {
67
+ "table_name": {
68
+ "type": "string",
69
+ "description": "Name of the table to describe",
70
+ },
71
+ },
72
+ "required": ["table_name"],
73
+ },
74
+ },
75
+ {
76
+ "name": "get_context",
77
+ "description": (
78
+ "Retrieve relevant rows from a table using full-text search. "
79
+ "Useful for RAG context retrieval."
80
+ ),
81
+ "inputSchema": {
82
+ "type": "object",
83
+ "properties": {
84
+ "table": {
85
+ "type": "string",
86
+ "description": "Table to search",
87
+ },
88
+ "search_query": {
89
+ "type": "string",
90
+ "description": "Natural language search query",
91
+ },
92
+ "top_k": {
93
+ "type": "integer",
94
+ "description": "Maximum results (default 10)",
95
+ "default": 10,
96
+ },
97
+ },
98
+ "required": ["table", "search_query"],
99
+ },
100
+ },
101
+ ]
102
+
103
+
104
+ def handle_tool_call(
105
+ agent: HatiDataAgent, tool_name: str, arguments: dict[str, Any]
106
+ ) -> dict[str, Any]:
107
+ """Handle an MCP tool call and return the result."""
108
+ try:
109
+ if tool_name == "query":
110
+ rows = agent.query(arguments["sql"])
111
+ return {
112
+ "content": [
113
+ {"type": "text", "text": json.dumps(rows, default=str)},
114
+ ],
115
+ }
116
+
117
+ elif tool_name == "list_tables":
118
+ rows = agent.query(
119
+ "SELECT table_name FROM information_schema.tables "
120
+ "WHERE table_schema = 'main' ORDER BY table_name"
121
+ )
122
+ tables = [row["table_name"] for row in rows]
123
+ return {
124
+ "content": [
125
+ {"type": "text", "text": json.dumps(tables)},
126
+ ],
127
+ }
128
+
129
+ elif tool_name == "describe_table":
130
+ table = arguments["table_name"]
131
+ rows = agent.query(
132
+ f"SELECT column_name, data_type, is_nullable "
133
+ f"FROM information_schema.columns "
134
+ f"WHERE table_name = '{table}' ORDER BY ordinal_position"
135
+ )
136
+ return {
137
+ "content": [
138
+ {"type": "text", "text": json.dumps(rows, default=str)},
139
+ ],
140
+ }
141
+
142
+ elif tool_name == "get_context":
143
+ rows = agent.get_context(
144
+ table=arguments["table"],
145
+ search_query=arguments["search_query"],
146
+ top_k=arguments.get("top_k", 10),
147
+ )
148
+ return {
149
+ "content": [
150
+ {"type": "text", "text": json.dumps(rows, default=str)},
151
+ ],
152
+ }
153
+
154
+ else:
155
+ return {
156
+ "content": [
157
+ {"type": "text", "text": f"Unknown tool: {tool_name}"},
158
+ ],
159
+ "isError": True,
160
+ }
161
+
162
+ except Exception as e:
163
+ return {
164
+ "content": [
165
+ {"type": "text", "text": f"Error: {e}"},
166
+ ],
167
+ "isError": True,
168
+ }
169
+
170
+
171
+ def run_stdio_server(agent: HatiDataAgent) -> None:
172
+ """Run MCP server over stdio transport (JSON-RPC)."""
173
+ tools = create_tools(agent)
174
+
175
+ # Server info
176
+ server_info = {
177
+ "name": "hatidata",
178
+ "version": "0.1.0",
179
+ "capabilities": {
180
+ "tools": {},
181
+ },
182
+ }
183
+
184
+ for line in sys.stdin:
185
+ line = line.strip()
186
+ if not line:
187
+ continue
188
+
189
+ try:
190
+ request = json.loads(line)
191
+ except json.JSONDecodeError:
192
+ continue
193
+
194
+ method = request.get("method", "")
195
+ req_id = request.get("id")
196
+
197
+ if method == "initialize":
198
+ response = {
199
+ "jsonrpc": "2.0",
200
+ "id": req_id,
201
+ "result": {
202
+ "protocolVersion": "2024-11-05",
203
+ "serverInfo": server_info,
204
+ "capabilities": server_info["capabilities"],
205
+ },
206
+ }
207
+
208
+ elif method == "tools/list":
209
+ response = {
210
+ "jsonrpc": "2.0",
211
+ "id": req_id,
212
+ "result": {"tools": tools},
213
+ }
214
+
215
+ elif method == "tools/call":
216
+ params = request.get("params", {})
217
+ tool_name = params.get("name", "")
218
+ arguments = params.get("arguments", {})
219
+ result = handle_tool_call(agent, tool_name, arguments)
220
+ response = {
221
+ "jsonrpc": "2.0",
222
+ "id": req_id,
223
+ "result": result,
224
+ }
225
+
226
+ elif method == "notifications/initialized":
227
+ continue # No response needed for notifications.
228
+
229
+ else:
230
+ response = {
231
+ "jsonrpc": "2.0",
232
+ "id": req_id,
233
+ "error": {
234
+ "code": -32601,
235
+ "message": f"Method not found: {method}",
236
+ },
237
+ }
238
+
239
+ sys.stdout.write(json.dumps(response) + "\n")
240
+ sys.stdout.flush()
241
+
242
+
243
+ def main() -> None:
244
+ """Entry point for the MCP server CLI."""
245
+ parser = argparse.ArgumentParser(description="HatiData MCP Server")
246
+ parser.add_argument("--host", default="localhost", help="Proxy host")
247
+ parser.add_argument("--port", type=int, default=5439, help="Proxy port")
248
+ parser.add_argument("--agent-id", default="mcp-agent", help="Agent ID")
249
+ parser.add_argument("--database", default="hatidata", help="Database")
250
+ parser.add_argument("--user", default="agent", help="Username")
251
+ parser.add_argument("--password", default="", help="Password")
252
+ args = parser.parse_args()
253
+
254
+ agent = HatiDataAgent(
255
+ host=args.host,
256
+ port=args.port,
257
+ agent_id=args.agent_id,
258
+ framework="anthropic",
259
+ database=args.database,
260
+ user=args.user,
261
+ password=args.password,
262
+ )
263
+
264
+ run_stdio_server(agent)
265
+
266
+
267
+ if __name__ == "__main__":
268
+ main()
@@ -0,0 +1,183 @@
1
+ Metadata-Version: 2.4
2
+ Name: hatidata-agent
3
+ Version: 0.1.0
4
+ Summary: HatiData Agent SDK — RAM for Agents
5
+ Author-email: Marviy Pte Ltd <eng@hatidata.com>
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://hatidata.com
8
+ Project-URL: Documentation, https://docs.hatiosai.com/hatidata
9
+ Project-URL: Repository, https://github.com/HatiOS-AI/HatiData-Core
10
+ Project-URL: Changelog, https://github.com/HatiOS-AI/HatiData-Core/releases
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Database
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: psycopg2-binary>=2.9
24
+ Provides-Extra: async
25
+ Requires-Dist: asyncpg>=0.29; extra == "async"
26
+ Provides-Extra: langchain
27
+ Requires-Dist: langchain-community>=0.2; extra == "langchain"
28
+ Provides-Extra: mcp
29
+ Requires-Dist: mcp>=1.0; extra == "mcp"
30
+ Provides-Extra: all
31
+ Requires-Dist: asyncpg>=0.29; extra == "all"
32
+ Requires-Dist: langchain-community>=0.2; extra == "all"
33
+ Requires-Dist: mcp>=1.0; extra == "all"
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=8; extra == "dev"
36
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
37
+
38
+ # HatiData Agent SDK
39
+
40
+ [![PyPI version](https://img.shields.io/pypi/v/hatidata-agent.svg)](https://pypi.org/project/hatidata-agent/)
41
+ [![Python versions](https://img.shields.io/pypi/pyversions/hatidata-agent.svg)](https://pypi.org/project/hatidata-agent/)
42
+ [![License](https://img.shields.io/pypi/l/hatidata-agent.svg)](https://github.com/HatiOS-AI/HatiData-Core/blob/main/LICENSE)
43
+
44
+ **RAM for Agents** — Python SDK for AI agents to query HatiData's in-VPC data warehouse with sub-10ms latency via Postgres wire protocol.
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ pip install hatidata-agent
50
+
51
+ # With async support
52
+ pip install hatidata-agent[async]
53
+
54
+ # With LangChain support
55
+ pip install hatidata-agent[langchain]
56
+
57
+ # With MCP server
58
+ pip install hatidata-agent[mcp]
59
+
60
+ # Everything
61
+ pip install hatidata-agent[all]
62
+ ```
63
+
64
+ ## Quick Start
65
+
66
+ ```python
67
+ from hatidata_agent import HatiDataAgent
68
+
69
+ agent = HatiDataAgent(
70
+ host="proxy.internal",
71
+ port=5439,
72
+ agent_id="my-agent",
73
+ framework="langchain",
74
+ )
75
+
76
+ # Simple query
77
+ rows = agent.query("SELECT * FROM customers WHERE status = 'active' LIMIT 10")
78
+ print(rows) # [{"id": 1, "name": "Acme Corp", ...}, ...]
79
+ ```
80
+
81
+ ## Features
82
+
83
+ - **Sub-10ms query latency** -- In-VPC execution, no data leaves your network
84
+ - **Postgres wire protocol** -- Works with any Postgres client library
85
+ - **Reasoning chain tracking** -- Multi-step audit trails for agent workflows
86
+ - **RAG context retrieval** -- Full-text and vector similarity search
87
+ - **LangChain integration** -- Drop-in `SQLDatabase` replacement for LangChain agents
88
+ - **MCP server** -- Expose HatiData as tools for Claude and other MCP-compatible agents
89
+ - **Agent identification** -- Per-agent billing, priority scheduling, and audit via Postgres startup parameters
90
+ - **Snowflake SQL compatible** -- Bring existing queries without rewrites
91
+
92
+ ## Reasoning Chain Tracking
93
+
94
+ Track multi-step reasoning chains for audit and debugging:
95
+
96
+ ```python
97
+ with agent.reasoning_chain("req-001") as chain:
98
+ # Step 0: Discover tables
99
+ tables = chain.query("SELECT table_name FROM information_schema.tables")
100
+
101
+ # Step 1: Get relevant data
102
+ customers = chain.query("SELECT * FROM customers WHERE tier = 'enterprise'", step=1)
103
+
104
+ # Step 2: Aggregate
105
+ revenue = chain.query("SELECT SUM(revenue) FROM orders WHERE customer_id IN (...)", step=2)
106
+ ```
107
+
108
+ ## RAG Context Retrieval
109
+
110
+ ```python
111
+ # Full-text search
112
+ context = agent.get_context("customers", "enterprise accounts in US", top_k=5)
113
+
114
+ # Vector similarity search
115
+ context = agent.get_rag_context("docs", "embedding", query_vector, top_k=10)
116
+ ```
117
+
118
+ ## LangChain Integration
119
+
120
+ ```python
121
+ from hatidata_agent.langchain import HatiDataSQLDatabase
122
+ from langchain.agents import create_sql_agent
123
+
124
+ db = HatiDataSQLDatabase(
125
+ host="proxy.internal",
126
+ port=5439,
127
+ agent_id="sql-agent-1",
128
+ )
129
+
130
+ agent = create_sql_agent(llm=llm, db=db, verbose=True)
131
+ result = agent.run("How many enterprise customers do we have?")
132
+ ```
133
+
134
+ ## MCP Server
135
+
136
+ Run as an MCP server for Claude and other MCP-compatible agents:
137
+
138
+ ```bash
139
+ # Start the MCP server
140
+ hatidata-mcp-server --host proxy.internal --port 5439
141
+
142
+ # Or add to Claude Code's MCP config:
143
+ # ~/.claude/mcp.json
144
+ {
145
+ "mcpServers": {
146
+ "hatidata": {
147
+ "command": "hatidata-mcp-server",
148
+ "args": ["--host", "proxy.internal", "--port", "5439"]
149
+ }
150
+ }
151
+ }
152
+ ```
153
+
154
+ ### MCP Tools
155
+
156
+ | Tool | Description |
157
+ |------|-------------|
158
+ | `query` | Execute SQL and return JSON results |
159
+ | `list_tables` | List available tables |
160
+ | `describe_table` | Get table schema |
161
+ | `get_context` | RAG context retrieval via full-text search |
162
+
163
+ ## Agent Identification
164
+
165
+ The SDK automatically identifies agents via Postgres startup parameters:
166
+
167
+ | Parameter | Purpose |
168
+ |-----------|---------|
169
+ | `hatidata_agent_id` | Unique agent identifier |
170
+ | `hatidata_framework` | AI framework (langchain, crewai, autogen, etc.) |
171
+ | `hatidata_priority` | Scheduling priority (low, normal, high, critical) |
172
+ | `hatidata_request_id` | Request/reasoning chain ID |
173
+ | `hatidata_reasoning_step` | Step number within a chain |
174
+
175
+ These enable per-agent billing, priority scheduling, audit trails, and the Agent Tax Report showing savings vs legacy cloud warehouses.
176
+
177
+ ## Documentation
178
+
179
+ Full documentation is available at [docs.hatiosai.com/hatidata](https://docs.hatiosai.com/hatidata).
180
+
181
+ ## License
182
+
183
+ Apache License 2.0. Copyright (c) Marviy Pte Ltd. See [LICENSE](https://github.com/HatiOS-AI/HatiData-Core/blob/main/LICENSE) for details.
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ hatidata_agent/__init__.py
4
+ hatidata_agent/client.py
5
+ hatidata_agent/langchain.py
6
+ hatidata_agent/mcp_server.py
7
+ hatidata_agent.egg-info/PKG-INFO
8
+ hatidata_agent.egg-info/SOURCES.txt
9
+ hatidata_agent.egg-info/dependency_links.txt
10
+ hatidata_agent.egg-info/entry_points.txt
11
+ hatidata_agent.egg-info/requires.txt
12
+ hatidata_agent.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ hatidata-mcp-server = hatidata_agent.mcp_server:main
@@ -0,0 +1,19 @@
1
+ psycopg2-binary>=2.9
2
+
3
+ [all]
4
+ asyncpg>=0.29
5
+ langchain-community>=0.2
6
+ mcp>=1.0
7
+
8
+ [async]
9
+ asyncpg>=0.29
10
+
11
+ [dev]
12
+ pytest>=8
13
+ pytest-asyncio>=0.23
14
+
15
+ [langchain]
16
+ langchain-community>=0.2
17
+
18
+ [mcp]
19
+ mcp>=1.0
@@ -0,0 +1,2 @@
1
+ dist
2
+ hatidata_agent
@@ -0,0 +1,55 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "hatidata-agent"
7
+ version = "0.1.0"
8
+ description = "HatiData Agent SDK — RAM for Agents"
9
+ readme = "README.md"
10
+ license = {text = "Apache-2.0"}
11
+ requires-python = ">=3.9"
12
+ authors = [
13
+ {name = "Marviy Pte Ltd", email = "eng@hatidata.com"},
14
+ ]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "License :: OSI Approved :: Apache Software License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Topic :: Database",
25
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
26
+ ]
27
+ dependencies = [
28
+ "psycopg2-binary>=2.9",
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ async = ["asyncpg>=0.29"]
33
+ langchain = ["langchain-community>=0.2"]
34
+ mcp = ["mcp>=1.0"]
35
+ all = [
36
+ "asyncpg>=0.29",
37
+ "langchain-community>=0.2",
38
+ "mcp>=1.0",
39
+ ]
40
+ dev = [
41
+ "pytest>=8",
42
+ "pytest-asyncio>=0.23",
43
+ ]
44
+
45
+ [project.urls]
46
+ Homepage = "https://hatidata.com"
47
+ Documentation = "https://docs.hatiosai.com/hatidata"
48
+ Repository = "https://github.com/HatiOS-AI/HatiData-Core"
49
+ Changelog = "https://github.com/HatiOS-AI/HatiData-Core/releases"
50
+
51
+ [project.scripts]
52
+ hatidata-mcp-server = "hatidata_agent.mcp_server:main"
53
+
54
+ [tool.setuptools.packages.find]
55
+ where = ["."]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+