agentic-data-contracts 0.2.3__tar.gz → 0.2.4__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 (77) hide show
  1. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/CHANGELOG.md +8 -0
  2. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/PKG-INFO +4 -2
  3. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/README.md +3 -1
  4. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/pyproject.toml +1 -1
  5. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/adapters/base.py +1 -0
  6. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/adapters/duckdb.py +12 -0
  7. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/core/contract.py +18 -0
  8. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/tools/factory.py +9 -0
  9. agentic_data_contracts-0.2.4/tests/test_core/test_wildcard_tables.py +106 -0
  10. agentic_data_contracts-0.2.4/tests/test_tools/test_wildcard_tools.py +76 -0
  11. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/uv.lock +1 -1
  12. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/.github/dependabot.yml +0 -0
  13. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/.github/workflows/ci.yml +0 -0
  14. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/.gitignore +0 -0
  15. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/.pre-commit-config.yaml +0 -0
  16. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/.python-version +0 -0
  17. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/CLAUDE.md +0 -0
  18. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/LICENSE +0 -0
  19. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/docs/architecture.md +0 -0
  20. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/examples/revenue_agent/agent.py +0 -0
  21. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/examples/revenue_agent/contract.yml +0 -0
  22. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/examples/revenue_agent/semantic.yml +0 -0
  23. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/examples/revenue_agent/setup_db.py +0 -0
  24. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/__init__.py +0 -0
  25. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/adapters/__init__.py +0 -0
  26. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/bridge/__init__.py +0 -0
  27. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/bridge/compiler.py +0 -0
  28. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/core/__init__.py +0 -0
  29. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/core/schema.py +0 -0
  30. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/core/session.py +0 -0
  31. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/py.typed +0 -0
  32. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/semantic/__init__.py +0 -0
  33. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/semantic/base.py +0 -0
  34. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/semantic/cube.py +0 -0
  35. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/semantic/dbt.py +0 -0
  36. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/semantic/yaml_source.py +0 -0
  37. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/tools/__init__.py +0 -0
  38. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/tools/middleware.py +0 -0
  39. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/tools/sdk.py +0 -0
  40. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/validation/__init__.py +0 -0
  41. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/validation/checkers.py +0 -0
  42. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/validation/explain.py +0 -0
  43. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/src/agentic_data_contracts/validation/validator.py +0 -0
  44. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/__init__.py +0 -0
  45. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/conftest.py +0 -0
  46. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/fixtures/minimal_contract.yml +0 -0
  47. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/fixtures/sample_cube_schema.yml +0 -0
  48. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/fixtures/sample_dbt_manifest.json +0 -0
  49. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/fixtures/semantic_source.yml +0 -0
  50. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/fixtures/valid_contract.yml +0 -0
  51. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_adapters/__init__.py +0 -0
  52. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_adapters/test_duckdb.py +0 -0
  53. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_bridge/__init__.py +0 -0
  54. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_bridge/test_compiler.py +0 -0
  55. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_core/__init__.py +0 -0
  56. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_core/test_contract.py +0 -0
  57. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_core/test_load_semantic_source.py +0 -0
  58. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_core/test_schema.py +0 -0
  59. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_core/test_sdk_config.py +0 -0
  60. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_core/test_session.py +0 -0
  61. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_core/test_system_prompt_metrics.py +0 -0
  62. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_public_api.py +0 -0
  63. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_semantic/__init__.py +0 -0
  64. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_semantic/test_cube.py +0 -0
  65. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_semantic/test_dbt.py +0 -0
  66. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_semantic/test_search.py +0 -0
  67. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_semantic/test_yaml_source.py +0 -0
  68. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_tools/__init__.py +0 -0
  69. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_tools/test_auto_load.py +0 -0
  70. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_tools/test_factory.py +0 -0
  71. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_tools/test_middleware.py +0 -0
  72. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_tools/test_sdk.py +0 -0
  73. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_tools/test_semantic_tools.py +0 -0
  74. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_validation/__init__.py +0 -0
  75. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_validation/test_checkers.py +0 -0
  76. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_validation/test_explain.py +0 -0
  77. {agentic_data_contracts-0.2.3 → agentic_data_contracts-0.2.4}/tests/test_validation/test_validator.py +0 -0
@@ -2,6 +2,14 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.2.4] - 2026-03-29
6
+
7
+ ### Added
8
+
9
+ - **Wildcard table support**: Use `tables: ["*"]` in `allowed_tables` to allow all tables in a schema, discovered from the database at runtime via `adapter.list_tables()`
10
+ - **`DataContract.resolve_tables(adapter)`**: Expands wildcard entries using the database adapter; called automatically by `create_tools()` when an adapter is provided
11
+ - **`DatabaseAdapter.list_tables(schema)`**: New protocol method for listing tables in a schema; implemented in `DuckDBAdapter` via `information_schema.tables`
12
+
5
13
  ## [0.2.3] - 2026-03-29
6
14
 
7
15
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-data-contracts
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: YAML-first data contract governance for AI agents
5
5
  Project-URL: Homepage, https://github.com/flyersworder/agentic-data-contracts
6
6
  Project-URL: Repository, https://github.com/flyersworder/agentic-data-contracts
@@ -117,7 +117,9 @@ semantic:
117
117
  path: "./semantic.yml"
118
118
  allowed_tables:
119
119
  - schema: analytics
120
- tables: [orders, customers, subscriptions]
120
+ tables: ["*"] # all tables in schema (discovered from database)
121
+ - schema: marketing
122
+ tables: [campaigns] # or list specific tables
121
123
  forbidden_operations: [DELETE, DROP, TRUNCATE, UPDATE, INSERT]
122
124
  rules:
123
125
  - name: tenant_isolation
@@ -64,7 +64,9 @@ semantic:
64
64
  path: "./semantic.yml"
65
65
  allowed_tables:
66
66
  - schema: analytics
67
- tables: [orders, customers, subscriptions]
67
+ tables: ["*"] # all tables in schema (discovered from database)
68
+ - schema: marketing
69
+ tables: [campaigns] # or list specific tables
68
70
  forbidden_operations: [DELETE, DROP, TRUNCATE, UPDATE, INSERT]
69
71
  rules:
70
72
  - name: tenant_isolation
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentic-data-contracts"
3
- version = "0.2.3"
3
+ version = "0.2.4"
4
4
  description = "YAML-first data contract governance for AI agents"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -37,6 +37,7 @@ class DatabaseAdapter(Protocol):
37
37
  def execute(self, sql: str) -> QueryResult: ...
38
38
  def explain(self, sql: str) -> ExplainResult: ...
39
39
  def describe_table(self, schema: str, table: str) -> TableSchema: ...
40
+ def list_tables(self, schema: str) -> list[str]: ...
40
41
 
41
42
  @property
42
43
  def dialect(self) -> str: ...
@@ -59,6 +59,18 @@ class DuckDBAdapter:
59
59
  last_estimate = int(match.group(1))
60
60
  return last_estimate
61
61
 
62
+ def list_tables(self, schema: str) -> list[str]:
63
+ rows = self.connection.execute(
64
+ """
65
+ SELECT table_name
66
+ FROM information_schema.tables
67
+ WHERE table_schema = ?
68
+ ORDER BY table_name
69
+ """,
70
+ [schema],
71
+ ).fetchall()
72
+ return [row[0] for row in rows]
73
+
62
74
  def describe_table(self, schema: str, table: str) -> TableSchema:
63
75
  rows = self.connection.execute(
64
76
  """
@@ -14,6 +14,7 @@ from agentic_data_contracts.core.schema import (
14
14
  )
15
15
 
16
16
  if TYPE_CHECKING:
17
+ from agentic_data_contracts.adapters.base import DatabaseAdapter
17
18
  from agentic_data_contracts.semantic.base import SemanticSource
18
19
 
19
20
 
@@ -38,10 +39,27 @@ class DataContract:
38
39
  schema = DataContractSchema.model_validate(raw)
39
40
  return cls(schema=schema)
40
41
 
42
+ def has_wildcard_tables(self) -> bool:
43
+ """Check if any schema uses wildcard ('*') for tables."""
44
+ return any("*" in entry.tables for entry in self.schema.semantic.allowed_tables)
45
+
46
+ def resolve_tables(self, adapter: DatabaseAdapter) -> None:
47
+ """Expand wildcard tables using the database adapter.
48
+
49
+ Replaces ["*"] entries with actual table names from the database.
50
+ Call this once after creating the adapter. Results are cached
51
+ on the schema object.
52
+ """
53
+ for entry in self.schema.semantic.allowed_tables:
54
+ if "*" in entry.tables:
55
+ entry.tables = adapter.list_tables(entry.schema_)
56
+
41
57
  def allowed_table_names(self) -> list[str]:
42
58
  names: list[str] = []
43
59
  for entry in self.schema.semantic.allowed_tables:
44
60
  for table in entry.tables:
61
+ if table == "*":
62
+ continue # unresolved wildcard — skip
45
63
  names.append(f"{entry.schema_}.{table}")
46
64
  return names
47
65
 
@@ -41,6 +41,10 @@ def create_tools(
41
41
  if semantic_source is None:
42
42
  semantic_source = contract.load_semantic_source()
43
43
 
44
+ # Resolve wildcard tables if adapter is available
45
+ if adapter is not None and contract.has_wildcard_tables():
46
+ contract.resolve_tables(adapter)
47
+
44
48
  dialect = adapter.dialect if adapter else None
45
49
  validator = Validator(contract, dialect=dialect, explain_adapter=adapter)
46
50
 
@@ -60,6 +64,11 @@ def create_tools(
60
64
  for entry in contract.schema.semantic.allowed_tables:
61
65
  if schema_filter and entry.schema_ != schema_filter:
62
66
  continue
67
+ if "*" in entry.tables:
68
+ return _text_response(
69
+ f"Schema '{entry.schema_}' uses wildcard tables"
70
+ " but no database adapter is available to resolve them."
71
+ )
63
72
  for table in entry.tables:
64
73
  info: dict[str, Any] = {
65
74
  "schema": entry.schema_,
@@ -0,0 +1,106 @@
1
+ """Tests for wildcard table support in allowed_tables."""
2
+
3
+ from agentic_data_contracts.adapters.duckdb import DuckDBAdapter
4
+ from agentic_data_contracts.core.contract import DataContract
5
+ from agentic_data_contracts.core.schema import (
6
+ AllowedTable,
7
+ DataContractSchema,
8
+ SemanticConfig,
9
+ )
10
+
11
+
12
+ def _make_contract(tables_config: list[dict]) -> DataContract:
13
+ allowed = [AllowedTable.model_validate(t) for t in tables_config]
14
+ schema = DataContractSchema(
15
+ name="test",
16
+ semantic=SemanticConfig(allowed_tables=allowed),
17
+ )
18
+ return DataContract(schema)
19
+
20
+
21
+ def _make_adapter() -> DuckDBAdapter:
22
+ db = DuckDBAdapter(":memory:")
23
+ db.connection.execute("""
24
+ CREATE SCHEMA IF NOT EXISTS analytics;
25
+ CREATE TABLE analytics.orders (id INTEGER);
26
+ CREATE TABLE analytics.customers (id INTEGER);
27
+ CREATE TABLE analytics.products (id INTEGER);
28
+ CREATE SCHEMA IF NOT EXISTS raw;
29
+ CREATE TABLE raw.events (id INTEGER);
30
+ """)
31
+ return db
32
+
33
+
34
+ def test_has_wildcard_tables_true() -> None:
35
+ dc = _make_contract([{"schema": "analytics", "tables": ["*"]}])
36
+ assert dc.has_wildcard_tables()
37
+
38
+
39
+ def test_has_wildcard_tables_false() -> None:
40
+ dc = _make_contract(
41
+ [
42
+ {"schema": "analytics", "tables": ["orders"]},
43
+ ]
44
+ )
45
+ assert not dc.has_wildcard_tables()
46
+
47
+
48
+ def test_resolve_tables_expands_wildcard() -> None:
49
+ dc = _make_contract([{"schema": "analytics", "tables": ["*"]}])
50
+ adapter = _make_adapter()
51
+ dc.resolve_tables(adapter)
52
+
53
+ names = dc.allowed_table_names()
54
+ assert "analytics.orders" in names
55
+ assert "analytics.customers" in names
56
+ assert "analytics.products" in names
57
+ assert not any(n.startswith("raw.") for n in names)
58
+
59
+
60
+ def test_resolve_tables_mixed() -> None:
61
+ dc = _make_contract(
62
+ [
63
+ {"schema": "analytics", "tables": ["*"]},
64
+ {"schema": "raw", "tables": []},
65
+ ]
66
+ )
67
+ adapter = _make_adapter()
68
+ dc.resolve_tables(adapter)
69
+
70
+ names = dc.allowed_table_names()
71
+ assert "analytics.orders" in names
72
+ assert not any(n.startswith("raw.") for n in names)
73
+
74
+
75
+ def test_resolve_tables_preserves_explicit() -> None:
76
+ dc = _make_contract(
77
+ [
78
+ {"schema": "analytics", "tables": ["orders"]},
79
+ ]
80
+ )
81
+ adapter = _make_adapter()
82
+ dc.resolve_tables(adapter)
83
+
84
+ names = dc.allowed_table_names()
85
+ assert names == ["analytics.orders"]
86
+
87
+
88
+ def test_unresolved_wildcard_skipped() -> None:
89
+ dc = _make_contract([{"schema": "analytics", "tables": ["*"]}])
90
+ # Without calling resolve_tables, wildcard is skipped
91
+ names = dc.allowed_table_names()
92
+ assert names == []
93
+
94
+
95
+ def test_adapter_list_tables() -> None:
96
+ adapter = _make_adapter()
97
+ tables = adapter.list_tables("analytics")
98
+ assert "orders" in tables
99
+ assert "customers" in tables
100
+ assert "products" in tables
101
+
102
+
103
+ def test_adapter_list_tables_empty_schema() -> None:
104
+ adapter = _make_adapter()
105
+ tables = adapter.list_tables("nonexistent")
106
+ assert tables == []
@@ -0,0 +1,76 @@
1
+ """Tests for tools with wildcard table resolution."""
2
+
3
+ import json
4
+
5
+ import pytest
6
+
7
+ from agentic_data_contracts.adapters.duckdb import DuckDBAdapter
8
+ from agentic_data_contracts.core.contract import DataContract
9
+ from agentic_data_contracts.core.schema import (
10
+ AllowedTable,
11
+ DataContractSchema,
12
+ SemanticConfig,
13
+ )
14
+ from agentic_data_contracts.tools.factory import create_tools
15
+
16
+
17
+ @pytest.fixture
18
+ def adapter() -> DuckDBAdapter:
19
+ db = DuckDBAdapter(":memory:")
20
+ db.connection.execute("""
21
+ CREATE SCHEMA IF NOT EXISTS analytics;
22
+ CREATE TABLE analytics.orders (id INTEGER, amount DECIMAL);
23
+ CREATE TABLE analytics.customers (id INTEGER, name VARCHAR);
24
+ INSERT INTO analytics.orders VALUES (1, 100.00);
25
+ """)
26
+ return db
27
+
28
+
29
+ @pytest.fixture
30
+ def wildcard_contract() -> DataContract:
31
+ schema = DataContractSchema(
32
+ name="test",
33
+ semantic=SemanticConfig(
34
+ allowed_tables=[
35
+ AllowedTable.model_validate({"schema": "analytics", "tables": ["*"]}),
36
+ ],
37
+ ),
38
+ )
39
+ return DataContract(schema)
40
+
41
+
42
+ @pytest.mark.asyncio
43
+ async def test_list_tables_after_wildcard_resolve(
44
+ wildcard_contract: DataContract, adapter: DuckDBAdapter
45
+ ) -> None:
46
+ tools = create_tools(wildcard_contract, adapter=adapter)
47
+ tool = next(t for t in tools if t.name == "list_tables")
48
+ result = await tool.callable({})
49
+ text = result["content"][0]["text"]
50
+ data = json.loads(text)
51
+ table_names = [t["table"] for t in data["tables"]]
52
+ assert "orders" in table_names
53
+ assert "customers" in table_names
54
+
55
+
56
+ @pytest.mark.asyncio
57
+ async def test_run_query_with_wildcard_tables(
58
+ wildcard_contract: DataContract, adapter: DuckDBAdapter
59
+ ) -> None:
60
+ tools = create_tools(wildcard_contract, adapter=adapter)
61
+ tool = next(t for t in tools if t.name == "run_query")
62
+ result = await tool.callable({"sql": "SELECT id, amount FROM analytics.orders"})
63
+ text = result["content"][0]["text"]
64
+ assert "100" in text
65
+
66
+
67
+ @pytest.mark.asyncio
68
+ async def test_validate_query_with_wildcard_tables(
69
+ wildcard_contract: DataContract, adapter: DuckDBAdapter
70
+ ) -> None:
71
+ tools = create_tools(wildcard_contract, adapter=adapter)
72
+ tool = next(t for t in tools if t.name == "validate_query")
73
+ # analytics.orders should be allowed after wildcard resolution
74
+ result = await tool.callable({"sql": "SELECT id FROM analytics.orders"})
75
+ text = result["content"][0]["text"]
76
+ assert "valid" in text.lower()
@@ -9,7 +9,7 @@ resolution-markers = [
9
9
 
10
10
  [[package]]
11
11
  name = "agentic-data-contracts"
12
- version = "0.2.3"
12
+ version = "0.2.4"
13
13
  source = { editable = "." }
14
14
  dependencies = [
15
15
  { name = "pydantic" },