irides-core 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 (45) hide show
  1. irides_core-0.1.0/PKG-INFO +254 -0
  2. irides_core-0.1.0/README.md +202 -0
  3. irides_core-0.1.0/__init__.py +2 -0
  4. irides_core-0.1.0/basic_test.py +78 -0
  5. irides_core-0.1.0/check_mysql_connection.py +23 -0
  6. irides_core-0.1.0/db_connector/__init__.py +12 -0
  7. irides_core-0.1.0/db_connector/ai_service.py +133 -0
  8. irides_core-0.1.0/db_connector/cache_manager.py +69 -0
  9. irides_core-0.1.0/db_connector/config_service.py +116 -0
  10. irides_core-0.1.0/db_connector/configurations.py +383 -0
  11. irides_core-0.1.0/db_connector/connectors/__init__.py +19 -0
  12. irides_core-0.1.0/db_connector/connectors/athena.py +204 -0
  13. irides_core-0.1.0/db_connector/connectors/duckdb.py +225 -0
  14. irides_core-0.1.0/db_connector/connectors/dynamodb.py +233 -0
  15. irides_core-0.1.0/db_connector/connectors/mongodb.py +337 -0
  16. irides_core-0.1.0/db_connector/connectors/mysql.py +264 -0
  17. irides_core-0.1.0/db_connector/connectors/postgres.py +341 -0
  18. irides_core-0.1.0/db_connector/connectors/sqlite.py +178 -0
  19. irides_core-0.1.0/db_connector/connectors/trino.py +247 -0
  20. irides_core-0.1.0/db_connector/exporting/__init__.py +4 -0
  21. irides_core-0.1.0/db_connector/exporting/artifact_store.py +79 -0
  22. irides_core-0.1.0/db_connector/exporting/markdown.py +94 -0
  23. irides_core-0.1.0/db_connector/exporting/models.py +20 -0
  24. irides_core-0.1.0/db_connector/exporting/okf.py +41 -0
  25. irides_core-0.1.0/db_connector/exporting/preformatters.py +60 -0
  26. irides_core-0.1.0/db_connector/interface.py +78 -0
  27. irides_core-0.1.0/db_connector/job_store.py +315 -0
  28. irides_core-0.1.0/db_connector/manager.py +75 -0
  29. irides_core-0.1.0/db_connector/models/__init__.py +7 -0
  30. irides_core-0.1.0/db_connector/models/column.py +10 -0
  31. irides_core-0.1.0/db_connector/models/instance.py +7 -0
  32. irides_core-0.1.0/db_connector/models/scan_job.py +37 -0
  33. irides_core-0.1.0/db_connector/models/schema.py +7 -0
  34. irides_core-0.1.0/db_connector/models/table.py +8 -0
  35. irides_core-0.1.0/db_connector/models/table_details.py +51 -0
  36. irides_core-0.1.0/db_connector/sql_utils.py +28 -0
  37. irides_core-0.1.0/db_connector/storage.py +325 -0
  38. irides_core-0.1.0/irides_core.egg-info/PKG-INFO +254 -0
  39. irides_core-0.1.0/irides_core.egg-info/SOURCES.txt +79 -0
  40. irides_core-0.1.0/irides_core.egg-info/dependency_links.txt +1 -0
  41. irides_core-0.1.0/irides_core.egg-info/requires.txt +39 -0
  42. irides_core-0.1.0/irides_core.egg-info/top_level.txt +1 -0
  43. irides_core-0.1.0/pyproject.toml +74 -0
  44. irides_core-0.1.0/setup.cfg +4 -0
  45. irides_core-0.1.0/setup.py +15 -0
@@ -0,0 +1,254 @@
1
+ Metadata-Version: 2.4
2
+ Name: irides-core
3
+ Version: 0.1.0
4
+ Summary: A Python library for extracting structured database metadata to provide reliable context to AI systems and data-driven applications.
5
+ Author-email: Gian Andrea Sechi <me@gianandreasechi.com>
6
+ License: Apache-2.0
7
+ Project-URL: Source Code, https://github.com/GianAndreaSechi/irides/core
8
+ Project-URL: Homepage, https://www.gianandreasechi.com
9
+ Keywords: database,introspection,metadata,irides,schema,redis
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
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
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: pydantic>=2.0
23
+ Requires-Dist: redis>=4.0
24
+ Requires-Dist: loguru>=0.7.0
25
+ Requires-Dist: python-dotenv>=1.0.0
26
+ Requires-Dist: PyYAML>=6.0
27
+ Provides-Extra: postgres
28
+ Requires-Dist: psycopg2-binary>=2.9.0; extra == "postgres"
29
+ Provides-Extra: mysql
30
+ Requires-Dist: mysql-connector-python>=8.0.0; extra == "mysql"
31
+ Provides-Extra: mongo
32
+ Requires-Dist: pymongo>=4.0.0; extra == "mongo"
33
+ Provides-Extra: duckdb
34
+ Requires-Dist: duckdb>=0.9.0; extra == "duckdb"
35
+ Provides-Extra: aws
36
+ Requires-Dist: boto3>=1.26.0; extra == "aws"
37
+ Provides-Extra: trino
38
+ Requires-Dist: trino>=0.320.0; extra == "trino"
39
+ Provides-Extra: ai
40
+ Requires-Dist: litellm>=1.0.0; extra == "ai"
41
+ Provides-Extra: all
42
+ Requires-Dist: psycopg2-binary>=2.9.0; extra == "all"
43
+ Requires-Dist: mysql-connector-python>=8.0.0; extra == "all"
44
+ Requires-Dist: pymongo>=4.0.0; extra == "all"
45
+ Requires-Dist: duckdb>=0.9.0; extra == "all"
46
+ Requires-Dist: boto3>=1.26.0; extra == "all"
47
+ Requires-Dist: trino>=0.320.0; extra == "all"
48
+ Requires-Dist: litellm>=1.0.0; extra == "all"
49
+ Provides-Extra: dev
50
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
51
+ Requires-Dist: pytest-asyncio>=0.20.0; extra == "dev"
52
+
53
+ # Core
54
+
55
+ Shared Python library used by both the **API** and the **Worker**. It provides database connector abstractions, Pydantic models, Redis cache management, configuration loading, metadata persistence, and the async job store.
56
+
57
+ ---
58
+
59
+ ## Package Structure
60
+
61
+ ```
62
+ core/db_connector/
63
+ ├── connectors/ # DB-specific connector implementations
64
+ │ ├── mysql.py
65
+ │ ├── postgres.py
66
+ │ ├── sqlite.py
67
+ │ ├── duckdb.py
68
+ │ ├── dynamodb.py
69
+ │ ├── mongodb.py
70
+ │ ├── athena.py
71
+ │ ├── trino.py
72
+ │ └── presto.py
73
+ ├── exporting/ # Multi-format artifact exporting (Markdown, OKF)
74
+ │ ├── __init__.py
75
+ │ ├── models.py # ExportFormat, ExportOptions
76
+ │ ├── preformatters.py # essential_record deterministic view
77
+ │ ├── markdown.py # Markdown table renderer
78
+ │ ├── okf.py # Open Knowledge Format (OKF v0.2) renderer
79
+ │ └── artifact_store.py # FileArtifactStore (atomic write, 0644, bundle index)
80
+ ├── models/ # Pydantic data models
81
+ │ ├── instance.py
82
+ │ ├── schema.py
83
+ │ ├── table.py
84
+ │ ├── column.py
85
+ │ ├── table_details.py # TableDescription, PrimaryKey, ForeignKey, Index, Partition
86
+ │ └── scan_job.py # ScanJob, ScanScope, ScanStatus
87
+ ├── interface.py # BaseConnector abstract class
88
+ ├── manager.py # ConnectorManager (auto-discovers BaseConnector implementations)
89
+ ├── cache_manager.py # Redis cache (get/set with prefix + TTL)
90
+ ├── config_service.py # ConfigService — resolves configs & instances to connectors
91
+ ├── configurations.py # DB configuration loading from environment variables
92
+ ├── storage.py # Metadata persistence (BaseMetadataStore + FileMetadataStore)
93
+ └── job_store.py # JobStore — Redis Stream queue + job metadata & result storage
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Key Modules
99
+
100
+ ### `exporting/`
101
+
102
+ Provides multi-format artifact exporting decoupled from raw JSON storage.
103
+
104
+ - **`ExportFormat`**: Enum supporting `markdown` and `okf` (Open Knowledge Format v0.2). Both are generated by default.
105
+ - **`ExportOptions`**: Controls derived artifact generation (`formats: list[ExportFormat]`, `preformat: bool = True`). Supports explicit opt-out.
106
+ - **`preformatters.py` (`essential_record`)**: Deterministic view preserving identity, summary, columns, keys, relations, unique non-primary indexes, partitions, owner, tags, and status. Excludes non-unique secondary indexes and verbose internal metadata to optimize token usage.
107
+ - **`markdown.py` (`render_markdown`)**: Generates structured Markdown tables, keys, relationships, indexes, and partitions. Used both for standalone Markdown output and the OKF document body.
108
+ - **`okf.py` (`render_okf`)**: Renders OKF v0.2 documents with YAML frontmatter (`type: Database Table`, title, description, tags, generator metadata, identifiers) followed by the Markdown body.
109
+ - **`artifact_store.py` (`FileArtifactStore`)**: Persists artifacts to `STORAGE_EXPORT_DIR` via atomic writes with readable `0644` file permissions, maintaining separate directory hierarchies for Markdown and OKF catalog bundles with an auto-updated `index.md`:
110
+
111
+ ```text
112
+ storage/
113
+ metadata/
114
+ {config}/{instance}/{schema}/{table}.json
115
+ exports/
116
+ markdown/
117
+ {config}/{instance}/{schema}/{table}.md
118
+ okf/
119
+ catalog/
120
+ index.md
121
+ {config}/{instance}/{schema}/{table}.md
122
+ ```
123
+
124
+ ### `storage.py`
125
+
126
+ Provides metadata persistence abstractions.
127
+
128
+ **`BaseMetadataStore`** — abstract interface with these methods:
129
+
130
+ | Method | Description |
131
+ |---|---|
132
+ | `save_table_metadata(...)` | Write or update a table metadata document and derived exports |
133
+ | `get_table_metadata(...)` | Read a stored document by config+instance+schema+table |
134
+ | `list_instances(page, page_size)` | Paginated list of all stored instance names |
135
+ | `list_databases(instance_name, page, page_size)` | Paginated list of databases for an instance |
136
+ | `list_tables_metadata(instance_name, database_name, page, page_size)` | Paginated list of table names |
137
+ | `find_table_metadata(instance_name, database_name, table_name)` | Look up a table across all configs |
138
+ | `update_table_metadata(instance_name, database_name, table_name, payload)` | Merge custom fields into a stored document and regenerate exports |
139
+
140
+ **`FileMetadataStore`** (default) — saves canonical JSON documents under `STORAGE_METADATA_DIR` and derived exports under `STORAGE_EXPORT_DIR`.
141
+
142
+ Key behaviours:
143
+
144
+ - **Decoupled export pipeline**: Derived artifacts (Markdown, OKF) are generated independently from JSON metadata persistence. Setting `save_metadata=False` still generates exports if requested.
145
+ - **Automatic export regeneration**: Calling `update_table_metadata` to merge human annotations automatically regenerates the corresponding Markdown and OKF documents.
146
+ - **Custom field carry-forward**: any key not in `_SYSTEM_KEYS` (`metadata_key`, `config_name`, `instance_name`, `schema_name`, `table_name`, `updated_at`, `schema_description`, `ai_documentation`) is preserved across re-describe calls. Human-added fields such as `owner`, `tags`, and `notes` survive schema refreshes.
147
+ - **`only_if_changed`**: when `True`, `save_table_metadata` skips the JSON write if `schema_description` is identical to the stored version, leaving `updated_at` and human annotations untouched, but exports the current state.
148
+ - **`ai_documentation` preservation**: if `ai_documentation=None` is passed, the existing stored AI doc is kept rather than overwritten.
149
+ - **Protected fields**: `update_table_metadata` silently ignores `metadata_key`, `config_name`, `instance_name`, `schema_name`, `table_name`, and `updated_at` in the payload — these are always managed by the system.
150
+
151
+ Paginated list responses follow this envelope:
152
+ ```json
153
+ {
154
+ "items": ["name_a", "name_b"],
155
+ "total": 2,
156
+ "page": 1,
157
+ "page_size": 20,
158
+ "pages": 1
159
+ }
160
+ ```
161
+
162
+ Use `get_metadata_store()` (factory function) to obtain the configured store. Set `METADATA_STORE_TYPE=file` (default) or extend with future backends (`s3`, `athena`).
163
+
164
+ ### `ai_service.py`
165
+ **`AIDocumentationService`**: Non-blocking integration with LiteLLM (`LITELLM_MODEL`, default `gpt-4o-mini`). Generates high-level domain summaries and column descriptions. When requested, generated docs are attached to `TableDescription.ai_documentation` with `ai_generation_status`; failures also include `ai_generation_error`. If `litellm` is uninstalled, API keys are missing, or network errors occur, it logs a warning and returns no documentation without throwing exceptions.
166
+
167
+ ### `configurations.py`
168
+ Reads database connection parameters from environment variables. `DB_TARGETS` supports any number of named targets for any connector.
169
+ - Supports `DB_CONFIG_FILE` environment variable to explicitly specify the path to a container `.env` file (e.g. `/app/api/.env`), falling back to default `load_dotenv()` discovery when unset.
170
+ - Target names from `DB_TARGETS` become API/MCP `config_name` values. Example: `DB_TARGETS=sales_mysql,analytics_pg` creates `sales_mysql` and `analytics_pg` configurations.
171
+ - Each target uses `DB_TARGET_<TARGET_KEY>_*` variables, where `<TARGET_KEY>` is the uppercased target name with non-alphanumeric characters replaced by underscores.
172
+ - Exact required and optional keys for each connector type are documented in the root README under **DB Configuration & Activation**.
173
+
174
+ ### `config_service.py`
175
+ Wraps `ConnectorManager` and `configurations`.
176
+ - **`list_instances(config_name, no_cache)`**: Uniformly lists instances for both multi-host configurations (MySQL/MariaDB) and flat configurations (Athena, DynamoDB, Trino, MongoDB, SQLite).
177
+ - **`resolve_instance_names(config_name, instance_name, no_cache)`**: Returns `[instance_name]` if specified, or all discovered instances if `instance_name` is `None`.
178
+ - **`_get_hosts(config_name)`**: Returns explicitly configured hosts, correctly recognizing both multi-host `hosts` collections and flat `host` parameters.
179
+ - **`configuration_matches_instance(config_name, instance_name, no_cache)`**: Checks whether an instance belongs to a given configuration.
180
+ - **`resolve_configurations_for_instance(instance_name, no_cache)`**: Centralized lookup returning all configuration names matching a target instance.
181
+ - **`_get_connector_for_host(config_name, host)`**: Returns the connector for a specific host, falling back to flat connection parameters when static host definitions are omitted.
182
+
183
+ ### `cache_manager.py`
184
+ Redis-backed cache for introspection results. All keys are prefixed with `CACHE_KEY_PREFIX`. Cache can be bypassed per-call with `no_cache=True`.
185
+
186
+ ### `job_store.py`
187
+ Manages async scan jobs via Redis:
188
+ - **Stream** (`{prefix}:scan:queue`) — job queue for workers (`scan-workers` consumer group). Stream messages serialize `export_formats` and `export_preformat` parameters.
189
+ - **Hash** (`{prefix}:scan:job:{job_id}`) — job metadata, scope, and status. Supports deserialization of new export options with backwards compatibility for legacy jobs with `save_markdown`.
190
+ - **List** (`{prefix}:scan:results:{job_id}`) — serialized `TableDescription` results with automatic TTL extensions on writes.
191
+ - **Sorted Set** (`{prefix}:scan:jobs`) — job index ordered by creation timestamp, automatically pruned of entries older than `RESULTS_TTL` via `zremrangebyscore` to prevent Redis memory leaks.
192
+
193
+ ---
194
+
195
+ ## Supported Databases
196
+
197
+ | Database | Connector Type | Configuration Style |
198
+ |---|---|---|
199
+ | MySQL / MariaDB | `mysql` | Named `DB_TARGETS` |
200
+ | PostgreSQL | `postgres` | Named `DB_TARGETS` |
201
+ | SQLite | `sqlite` | Named `DB_TARGETS` |
202
+ | DuckDB | `duckdb` | Named `DB_TARGETS` |
203
+ | Amazon DynamoDB | `dynamodb` | Named `DB_TARGETS` |
204
+ | Amazon Athena | `athena` | Named `DB_TARGETS` |
205
+ | MongoDB | `mongodb` | Named `DB_TARGETS` |
206
+ | Trino | `trino` | Named `DB_TARGETS` |
207
+ | Presto | `presto` | Named `DB_TARGETS` |
208
+
209
+ ---
210
+
211
+ ## Environment Variables
212
+
213
+ Copy `.env.example` to `.env` and configure as needed.
214
+
215
+ | Variable | Default | Description |
216
+ |---|---|---|
217
+ | `REDIS_HOST` | `localhost` | Redis host |
218
+ | `REDIS_PORT` | `6379` | Redis port |
219
+ | `REDIS_DB` | `0` | Redis database index |
220
+ | `REDIS_TTL_SECONDS` | `86400` | Introspection cache TTL (1 day) |
221
+ | `CACHE_KEY_PREFIX` | `irides` | Prefix for all Redis keys |
222
+ | `SCAN_RESULTS_TTL_SECONDS` | `604800` | Scan result retention in Redis (7 days) |
223
+ | `DB_CONFIG_FILE` | *(none)* | Explicit path to `.env` configuration file |
224
+ | `DB_TARGETS` | *(none)* | Comma-separated list of named DB targets |
225
+ | `STORAGE_METADATA_DIR` | `storage/metadata` | Metadata JSON output directory |
226
+ | `STORAGE_EXPORT_DIR` | `storage/exports` | Directory for generated Markdown and OKF exports |
227
+ | `METADATA_STORE_TYPE` | `file` | Metadata store backend (`file`; `s3`/`athena` planned) |
228
+ | `LITELLM_MODEL` | `gpt-4o-mini` | LiteLLM model for AI documentation |
229
+ | `LITELLM_API_KEY` | *(none)* | Optional provider API key override |
230
+ | `LITELLM_API_BASE` | *(none)* | Optional custom LiteLLM API base URL |
231
+
232
+ DB activation vars — see [root README](../README.md#db-configuration--activation).
233
+
234
+ ---
235
+
236
+ ## Installation
237
+
238
+ The core package is installed in editable mode by the API and Worker:
239
+
240
+ ```bash
241
+ pip install -e /path/to/core
242
+ # or via requirements.txt:
243
+ pip install -r requirements.txt
244
+ ```
245
+
246
+ ---
247
+
248
+ ## Adding a New Connector
249
+
250
+ 1. Create `core/db_connector/connectors/mydb.py` implementing `BaseConnector`.
251
+ 2. Export it from `core/db_connector/connectors/__init__.py`.
252
+ 3. Add its activation env var and config block to `core/db_connector/configurations.py`.
253
+
254
+ The `ConnectorManager` automatically discovers all classes that extend `BaseConnector`.
@@ -0,0 +1,202 @@
1
+ # Core
2
+
3
+ Shared Python library used by both the **API** and the **Worker**. It provides database connector abstractions, Pydantic models, Redis cache management, configuration loading, metadata persistence, and the async job store.
4
+
5
+ ---
6
+
7
+ ## Package Structure
8
+
9
+ ```
10
+ core/db_connector/
11
+ ├── connectors/ # DB-specific connector implementations
12
+ │ ├── mysql.py
13
+ │ ├── postgres.py
14
+ │ ├── sqlite.py
15
+ │ ├── duckdb.py
16
+ │ ├── dynamodb.py
17
+ │ ├── mongodb.py
18
+ │ ├── athena.py
19
+ │ ├── trino.py
20
+ │ └── presto.py
21
+ ├── exporting/ # Multi-format artifact exporting (Markdown, OKF)
22
+ │ ├── __init__.py
23
+ │ ├── models.py # ExportFormat, ExportOptions
24
+ │ ├── preformatters.py # essential_record deterministic view
25
+ │ ├── markdown.py # Markdown table renderer
26
+ │ ├── okf.py # Open Knowledge Format (OKF v0.2) renderer
27
+ │ └── artifact_store.py # FileArtifactStore (atomic write, 0644, bundle index)
28
+ ├── models/ # Pydantic data models
29
+ │ ├── instance.py
30
+ │ ├── schema.py
31
+ │ ├── table.py
32
+ │ ├── column.py
33
+ │ ├── table_details.py # TableDescription, PrimaryKey, ForeignKey, Index, Partition
34
+ │ └── scan_job.py # ScanJob, ScanScope, ScanStatus
35
+ ├── interface.py # BaseConnector abstract class
36
+ ├── manager.py # ConnectorManager (auto-discovers BaseConnector implementations)
37
+ ├── cache_manager.py # Redis cache (get/set with prefix + TTL)
38
+ ├── config_service.py # ConfigService — resolves configs & instances to connectors
39
+ ├── configurations.py # DB configuration loading from environment variables
40
+ ├── storage.py # Metadata persistence (BaseMetadataStore + FileMetadataStore)
41
+ └── job_store.py # JobStore — Redis Stream queue + job metadata & result storage
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Key Modules
47
+
48
+ ### `exporting/`
49
+
50
+ Provides multi-format artifact exporting decoupled from raw JSON storage.
51
+
52
+ - **`ExportFormat`**: Enum supporting `markdown` and `okf` (Open Knowledge Format v0.2). Both are generated by default.
53
+ - **`ExportOptions`**: Controls derived artifact generation (`formats: list[ExportFormat]`, `preformat: bool = True`). Supports explicit opt-out.
54
+ - **`preformatters.py` (`essential_record`)**: Deterministic view preserving identity, summary, columns, keys, relations, unique non-primary indexes, partitions, owner, tags, and status. Excludes non-unique secondary indexes and verbose internal metadata to optimize token usage.
55
+ - **`markdown.py` (`render_markdown`)**: Generates structured Markdown tables, keys, relationships, indexes, and partitions. Used both for standalone Markdown output and the OKF document body.
56
+ - **`okf.py` (`render_okf`)**: Renders OKF v0.2 documents with YAML frontmatter (`type: Database Table`, title, description, tags, generator metadata, identifiers) followed by the Markdown body.
57
+ - **`artifact_store.py` (`FileArtifactStore`)**: Persists artifacts to `STORAGE_EXPORT_DIR` via atomic writes with readable `0644` file permissions, maintaining separate directory hierarchies for Markdown and OKF catalog bundles with an auto-updated `index.md`:
58
+
59
+ ```text
60
+ storage/
61
+ metadata/
62
+ {config}/{instance}/{schema}/{table}.json
63
+ exports/
64
+ markdown/
65
+ {config}/{instance}/{schema}/{table}.md
66
+ okf/
67
+ catalog/
68
+ index.md
69
+ {config}/{instance}/{schema}/{table}.md
70
+ ```
71
+
72
+ ### `storage.py`
73
+
74
+ Provides metadata persistence abstractions.
75
+
76
+ **`BaseMetadataStore`** — abstract interface with these methods:
77
+
78
+ | Method | Description |
79
+ |---|---|
80
+ | `save_table_metadata(...)` | Write or update a table metadata document and derived exports |
81
+ | `get_table_metadata(...)` | Read a stored document by config+instance+schema+table |
82
+ | `list_instances(page, page_size)` | Paginated list of all stored instance names |
83
+ | `list_databases(instance_name, page, page_size)` | Paginated list of databases for an instance |
84
+ | `list_tables_metadata(instance_name, database_name, page, page_size)` | Paginated list of table names |
85
+ | `find_table_metadata(instance_name, database_name, table_name)` | Look up a table across all configs |
86
+ | `update_table_metadata(instance_name, database_name, table_name, payload)` | Merge custom fields into a stored document and regenerate exports |
87
+
88
+ **`FileMetadataStore`** (default) — saves canonical JSON documents under `STORAGE_METADATA_DIR` and derived exports under `STORAGE_EXPORT_DIR`.
89
+
90
+ Key behaviours:
91
+
92
+ - **Decoupled export pipeline**: Derived artifacts (Markdown, OKF) are generated independently from JSON metadata persistence. Setting `save_metadata=False` still generates exports if requested.
93
+ - **Automatic export regeneration**: Calling `update_table_metadata` to merge human annotations automatically regenerates the corresponding Markdown and OKF documents.
94
+ - **Custom field carry-forward**: any key not in `_SYSTEM_KEYS` (`metadata_key`, `config_name`, `instance_name`, `schema_name`, `table_name`, `updated_at`, `schema_description`, `ai_documentation`) is preserved across re-describe calls. Human-added fields such as `owner`, `tags`, and `notes` survive schema refreshes.
95
+ - **`only_if_changed`**: when `True`, `save_table_metadata` skips the JSON write if `schema_description` is identical to the stored version, leaving `updated_at` and human annotations untouched, but exports the current state.
96
+ - **`ai_documentation` preservation**: if `ai_documentation=None` is passed, the existing stored AI doc is kept rather than overwritten.
97
+ - **Protected fields**: `update_table_metadata` silently ignores `metadata_key`, `config_name`, `instance_name`, `schema_name`, `table_name`, and `updated_at` in the payload — these are always managed by the system.
98
+
99
+ Paginated list responses follow this envelope:
100
+ ```json
101
+ {
102
+ "items": ["name_a", "name_b"],
103
+ "total": 2,
104
+ "page": 1,
105
+ "page_size": 20,
106
+ "pages": 1
107
+ }
108
+ ```
109
+
110
+ Use `get_metadata_store()` (factory function) to obtain the configured store. Set `METADATA_STORE_TYPE=file` (default) or extend with future backends (`s3`, `athena`).
111
+
112
+ ### `ai_service.py`
113
+ **`AIDocumentationService`**: Non-blocking integration with LiteLLM (`LITELLM_MODEL`, default `gpt-4o-mini`). Generates high-level domain summaries and column descriptions. When requested, generated docs are attached to `TableDescription.ai_documentation` with `ai_generation_status`; failures also include `ai_generation_error`. If `litellm` is uninstalled, API keys are missing, or network errors occur, it logs a warning and returns no documentation without throwing exceptions.
114
+
115
+ ### `configurations.py`
116
+ Reads database connection parameters from environment variables. `DB_TARGETS` supports any number of named targets for any connector.
117
+ - Supports `DB_CONFIG_FILE` environment variable to explicitly specify the path to a container `.env` file (e.g. `/app/api/.env`), falling back to default `load_dotenv()` discovery when unset.
118
+ - Target names from `DB_TARGETS` become API/MCP `config_name` values. Example: `DB_TARGETS=sales_mysql,analytics_pg` creates `sales_mysql` and `analytics_pg` configurations.
119
+ - Each target uses `DB_TARGET_<TARGET_KEY>_*` variables, where `<TARGET_KEY>` is the uppercased target name with non-alphanumeric characters replaced by underscores.
120
+ - Exact required and optional keys for each connector type are documented in the root README under **DB Configuration & Activation**.
121
+
122
+ ### `config_service.py`
123
+ Wraps `ConnectorManager` and `configurations`.
124
+ - **`list_instances(config_name, no_cache)`**: Uniformly lists instances for both multi-host configurations (MySQL/MariaDB) and flat configurations (Athena, DynamoDB, Trino, MongoDB, SQLite).
125
+ - **`resolve_instance_names(config_name, instance_name, no_cache)`**: Returns `[instance_name]` if specified, or all discovered instances if `instance_name` is `None`.
126
+ - **`_get_hosts(config_name)`**: Returns explicitly configured hosts, correctly recognizing both multi-host `hosts` collections and flat `host` parameters.
127
+ - **`configuration_matches_instance(config_name, instance_name, no_cache)`**: Checks whether an instance belongs to a given configuration.
128
+ - **`resolve_configurations_for_instance(instance_name, no_cache)`**: Centralized lookup returning all configuration names matching a target instance.
129
+ - **`_get_connector_for_host(config_name, host)`**: Returns the connector for a specific host, falling back to flat connection parameters when static host definitions are omitted.
130
+
131
+ ### `cache_manager.py`
132
+ Redis-backed cache for introspection results. All keys are prefixed with `CACHE_KEY_PREFIX`. Cache can be bypassed per-call with `no_cache=True`.
133
+
134
+ ### `job_store.py`
135
+ Manages async scan jobs via Redis:
136
+ - **Stream** (`{prefix}:scan:queue`) — job queue for workers (`scan-workers` consumer group). Stream messages serialize `export_formats` and `export_preformat` parameters.
137
+ - **Hash** (`{prefix}:scan:job:{job_id}`) — job metadata, scope, and status. Supports deserialization of new export options with backwards compatibility for legacy jobs with `save_markdown`.
138
+ - **List** (`{prefix}:scan:results:{job_id}`) — serialized `TableDescription` results with automatic TTL extensions on writes.
139
+ - **Sorted Set** (`{prefix}:scan:jobs`) — job index ordered by creation timestamp, automatically pruned of entries older than `RESULTS_TTL` via `zremrangebyscore` to prevent Redis memory leaks.
140
+
141
+ ---
142
+
143
+ ## Supported Databases
144
+
145
+ | Database | Connector Type | Configuration Style |
146
+ |---|---|---|
147
+ | MySQL / MariaDB | `mysql` | Named `DB_TARGETS` |
148
+ | PostgreSQL | `postgres` | Named `DB_TARGETS` |
149
+ | SQLite | `sqlite` | Named `DB_TARGETS` |
150
+ | DuckDB | `duckdb` | Named `DB_TARGETS` |
151
+ | Amazon DynamoDB | `dynamodb` | Named `DB_TARGETS` |
152
+ | Amazon Athena | `athena` | Named `DB_TARGETS` |
153
+ | MongoDB | `mongodb` | Named `DB_TARGETS` |
154
+ | Trino | `trino` | Named `DB_TARGETS` |
155
+ | Presto | `presto` | Named `DB_TARGETS` |
156
+
157
+ ---
158
+
159
+ ## Environment Variables
160
+
161
+ Copy `.env.example` to `.env` and configure as needed.
162
+
163
+ | Variable | Default | Description |
164
+ |---|---|---|
165
+ | `REDIS_HOST` | `localhost` | Redis host |
166
+ | `REDIS_PORT` | `6379` | Redis port |
167
+ | `REDIS_DB` | `0` | Redis database index |
168
+ | `REDIS_TTL_SECONDS` | `86400` | Introspection cache TTL (1 day) |
169
+ | `CACHE_KEY_PREFIX` | `irides` | Prefix for all Redis keys |
170
+ | `SCAN_RESULTS_TTL_SECONDS` | `604800` | Scan result retention in Redis (7 days) |
171
+ | `DB_CONFIG_FILE` | *(none)* | Explicit path to `.env` configuration file |
172
+ | `DB_TARGETS` | *(none)* | Comma-separated list of named DB targets |
173
+ | `STORAGE_METADATA_DIR` | `storage/metadata` | Metadata JSON output directory |
174
+ | `STORAGE_EXPORT_DIR` | `storage/exports` | Directory for generated Markdown and OKF exports |
175
+ | `METADATA_STORE_TYPE` | `file` | Metadata store backend (`file`; `s3`/`athena` planned) |
176
+ | `LITELLM_MODEL` | `gpt-4o-mini` | LiteLLM model for AI documentation |
177
+ | `LITELLM_API_KEY` | *(none)* | Optional provider API key override |
178
+ | `LITELLM_API_BASE` | *(none)* | Optional custom LiteLLM API base URL |
179
+
180
+ DB activation vars — see [root README](../README.md#db-configuration--activation).
181
+
182
+ ---
183
+
184
+ ## Installation
185
+
186
+ The core package is installed in editable mode by the API and Worker:
187
+
188
+ ```bash
189
+ pip install -e /path/to/core
190
+ # or via requirements.txt:
191
+ pip install -r requirements.txt
192
+ ```
193
+
194
+ ---
195
+
196
+ ## Adding a New Connector
197
+
198
+ 1. Create `core/db_connector/connectors/mydb.py` implementing `BaseConnector`.
199
+ 2. Export it from `core/db_connector/connectors/__init__.py`.
200
+ 3. Add its activation env var and config block to `core/db_connector/configurations.py`.
201
+
202
+ The `ConnectorManager` automatically discovers all classes that extend `BaseConnector`.
@@ -0,0 +1,2 @@
1
+ """Irides core library."""
2
+
@@ -0,0 +1,78 @@
1
+ import sys
2
+
3
+ from core.db_connector.connectors.mysql import MySQLConnector
4
+ from core.db_connector.cache_manager import CacheManager
5
+ from core.db_connector.models import TableDescription
6
+ from loguru import logger
7
+
8
+ # Configuration for MySQL Test (ADJUST THESE VALUES)
9
+ MYSQL_CONFIG = {
10
+ "host": "<your_mysql_host>", # e.g., "localhost" or "mysql_test_container"
11
+ "user": "<your_mysql_user>", # e.g., "root"
12
+ "password": "<your_mysql_password>", # e.g., "password"
13
+ "port": 3306,
14
+ # "database": "test_db" # Optional, for specific database operations
15
+ }
16
+
17
+ def run_basic_mysql_test():
18
+ logger.info("--- Running Basic MySQL Test ---")
19
+ connector = None
20
+ try:
21
+ cache_manager = CacheManager()
22
+ connector = MySQLConnector(connection_params=MYSQL_CONFIG, cache_manager=cache_manager)
23
+ logger.info("MySQL connection successful!")
24
+
25
+ logger.info("\n--- Listing Instances ---")
26
+ instances = connector.list_instances()
27
+ for instance in instances:
28
+ logger.info(f"Instance: {instance.name} (Version: {instance.version})")
29
+
30
+ logger.info(f"\n--- Listing Schemas for Instance: {instance.name} ---")
31
+ schemas = connector.list_schemas(instance_name=instance.name)
32
+ for schema in schemas:
33
+ logger.info(f" Schema: {schema.name}")
34
+
35
+ logger.info(f"\n --- Listing Tables for Schema: {schema.name} ---")
36
+ tables = connector.list_tables(instance_name=instance.name, schema_name=schema.name)
37
+ if not tables:
38
+ logger.info(f" No tables found in schema: {schema.name}")
39
+ for table in tables:
40
+ logger.info(f" Table: {table.name}")
41
+
42
+ logger.info(f"\n --- Describing Table: {table.name} in Schema: {schema.name} ---")
43
+ table_desc: TableDescription = connector.describe_table(instance_name=instance.name, schema_name=schema.name, table_name=table.name)
44
+
45
+ logger.info(f" Columns:")
46
+ if not table_desc.columns:
47
+ logger.info(f" No columns found for table: {table.name}")
48
+ for col in table_desc.columns:
49
+ logger.info(f" - Name: {col.name}, Type: {col.data_type}, Nullable: {col.is_nullable}, Default: {col.default_value}")
50
+
51
+ if table_desc.primary_key:
52
+ logger.info(f" Primary Key: {', '.join(table_desc.primary_key.column_names)}")
53
+
54
+ if table_desc.foreign_keys:
55
+ logger.info(f" Foreign Keys:")
56
+ for fk in table_desc.foreign_keys:
57
+ logger.info(f" - Column: {fk.column_name} -> {fk.referenced_table}.{fk.referenced_column} (Constraint: {fk.constraint_name})")
58
+
59
+ if table_desc.indexes:
60
+ logger.info(f" Indexes:")
61
+ for idx in table_desc.indexes:
62
+ logger.info(f" - Name: {idx.name}, Columns: {', '.join(idx.column_names)}, Unique: {idx.is_unique}, Primary: {idx.is_primary}, Type: {idx.type}")
63
+
64
+ logger.info("-" * 40) # Separator for readability
65
+
66
+ except ConnectionError as e:
67
+ logger.error(f"MySQL connection failed: {e}")
68
+ logger.exception("Connection Error Traceback:")
69
+ sys.exit(1)
70
+ except Exception as e:
71
+ logger.error(f"An unexpected error occurred: {e}")
72
+ logger.exception("Unexpected Error Traceback:")
73
+ sys.exit(1)
74
+ finally:
75
+ pass
76
+
77
+ if __name__ == "__main__":
78
+ run_basic_mysql_test()
@@ -0,0 +1,23 @@
1
+ import sys
2
+ from core.db_connector.connectors.mysql import MySQLConnector
3
+ from core.db_connector.cache_manager import CacheManager
4
+ from loguru import logger
5
+
6
+ MYSQL_TEST_CONFIG = {
7
+ "host": "host.docker.internal",
8
+ "user": "root",
9
+ "password": "",
10
+ "port": 3306,
11
+ }
12
+
13
+ try:
14
+ logger.info("Attempting MySQL connection check...")
15
+ temp_conn = MySQLConnector(
16
+ connection_params=MYSQL_TEST_CONFIG,
17
+ cache_manager=CacheManager(),
18
+ )
19
+ logger.info("MySQL connection successful!")
20
+ except Exception as e:
21
+ logger.error(f"MySQL connection failed: {e}")
22
+ logger.exception("Connection Error Traceback:")
23
+ sys.exit(1) # Exit with error code if connection fails
@@ -0,0 +1,12 @@
1
+ from .manager import ConnectorManager
2
+ from .storage import BaseMetadataStore, FileMetadataStore, get_metadata_store
3
+ from .ai_service import AIDocumentationService
4
+
5
+ __all__ = [
6
+ "ConnectorManager",
7
+ "BaseMetadataStore",
8
+ "FileMetadataStore",
9
+ "get_metadata_store",
10
+ "AIDocumentationService",
11
+ ]
12
+