cvec 1.4.3__tar.gz → 1.5.2__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. cvec-1.5.2/.claude/settings.json +51 -0
  2. cvec-1.5.2/.claude/settings.local.json +28 -0
  3. cvec-1.5.2/.github/workflows/ci.yml +33 -0
  4. cvec-1.5.2/.github/workflows/license-check-python.yml +17 -0
  5. cvec-1.5.2/.github/workflows/pyproject-license-check.yml +17 -0
  6. cvec-1.5.2/.gitignore +177 -0
  7. cvec-1.5.2/CLAUDE.md +1 -0
  8. {cvec-1.4.3 → cvec-1.5.2}/PKG-INFO +132 -11
  9. {cvec-1.4.3 → cvec-1.5.2}/README.md +126 -4
  10. cvec-1.5.2/examples/add_agent_post_example.py +52 -0
  11. cvec-1.5.2/examples/add_metric_data_arrow_example.py +34 -0
  12. cvec-1.5.2/examples/add_metric_data_example.py +34 -0
  13. cvec-1.5.2/examples/add_multiple_metrics_and_get_spans_for_them.py +127 -0
  14. cvec-1.5.2/examples/eav_example.py +29 -0
  15. cvec-1.5.2/examples/get_metric_arrow_example.py +26 -0
  16. cvec-1.5.2/examples/get_metric_data_objects_example.py +21 -0
  17. cvec-1.5.2/examples/get_metrics_example.py +20 -0
  18. cvec-1.5.2/examples/get_modeling_metrics_data_arrow_example.py +26 -0
  19. cvec-1.5.2/examples/get_modeling_metrics_data_example.py +33 -0
  20. cvec-1.5.2/examples/get_modeling_metrics_example.py +18 -0
  21. cvec-1.5.2/examples/get_spans_example.py +25 -0
  22. cvec-1.5.2/examples/show_eav_schema.py +27 -0
  23. {cvec-1.4.3 → cvec-1.5.2}/pyproject.toml +14 -12
  24. cvec-1.5.2/scripts/csv_import.py +228 -0
  25. {cvec-1.4.3 → cvec-1.5.2}/src/cvec/cvec.py +89 -12
  26. cvec-1.5.2/src/cvec/http_cache.py +40 -0
  27. {cvec-1.4.3 → cvec-1.5.2}/src/cvec/models/agent_post.py +1 -0
  28. cvec-1.5.2/tests/__init__.py +0 -0
  29. cvec-1.5.2/tests/test_cvec.py +899 -0
  30. cvec-1.5.2/tests/test_http_cache.py +407 -0
  31. cvec-1.5.2/tests/test_http_compression.py +226 -0
  32. cvec-1.5.2/tests/test_modeling.py +163 -0
  33. cvec-1.5.2/tests/test_token_refresh.py +162 -0
  34. cvec-1.5.2/uv.lock +519 -0
  35. {cvec-1.4.3 → cvec-1.5.2}/LICENSE +0 -0
  36. {cvec-1.4.3 → cvec-1.5.2}/src/cvec/__init__.py +0 -0
  37. {cvec-1.4.3 → cvec-1.5.2}/src/cvec/models/__init__.py +0 -0
  38. {cvec-1.4.3 → cvec-1.5.2}/src/cvec/models/eav_column.py +0 -0
  39. {cvec-1.4.3 → cvec-1.5.2}/src/cvec/models/eav_filter.py +0 -0
  40. {cvec-1.4.3 → cvec-1.5.2}/src/cvec/models/eav_table.py +0 -0
  41. {cvec-1.4.3 → cvec-1.5.2}/src/cvec/models/metric.py +0 -0
  42. {cvec-1.4.3 → cvec-1.5.2}/src/cvec/models/span.py +0 -0
  43. {cvec-1.4.3 → cvec-1.5.2}/src/cvec/py.typed +0 -0
  44. {cvec-1.4.3 → cvec-1.5.2}/src/cvec/utils/__init__.py +0 -0
  45. {cvec-1.4.3 → cvec-1.5.2}/src/cvec/utils/arrow_converter.py +0 -0
@@ -0,0 +1,51 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "uv run python-claude session start"
9
+ },
10
+ {
11
+ "type": "command",
12
+ "command": "uv run python-claude git status"
13
+ }
14
+ ]
15
+ }
16
+ ],
17
+ "Stop": [
18
+ {
19
+ "hooks": [
20
+ {
21
+ "type": "command",
22
+ "command": "uv run python-claude ruff format"
23
+ },
24
+ {
25
+ "type": "command",
26
+ "command": "uv run python-claude ruff check"
27
+ },
28
+ {
29
+ "type": "command",
30
+ "command": "uv run python-claude mypy"
31
+ },
32
+ {
33
+ "type": "command",
34
+ "command": "uv run python-claude pytest"
35
+ }
36
+ ]
37
+ }
38
+ ],
39
+ "PostToolUse": [
40
+ {
41
+ "matcher": "Write|Edit",
42
+ "hooks": [
43
+ {
44
+ "type": "command",
45
+ "command": "uv run python-claude edited"
46
+ }
47
+ ]
48
+ }
49
+ ]
50
+ }
51
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(chmod:*)",
5
+ "Bash(python:*)",
6
+ "Bash(git commit:*)",
7
+ "Bash(pytest:*)",
8
+ "Bash(python3 -m pytest:*)",
9
+ "Bash(poetry run pytest:*)",
10
+ "Bash(git add:*)",
11
+ "Bash(poetry run mypy:*)",
12
+ "Bash(git checkout:*)",
13
+ "Bash(poetry run ruff:*)",
14
+ "Bash(poetry install:*)",
15
+ "Bash(tree:*)",
16
+ "Bash(git mv:*)",
17
+ "WebFetch(domain:github.com)",
18
+ "WebFetch(domain:pypi.org)",
19
+ "Bash(poetry run python:*)",
20
+ "Bash(poetry add:*)",
21
+ "Bash(git fetch:*)",
22
+ "Bash(git rebase:*)",
23
+ "Bash(git rm:*)"
24
+ ],
25
+ "deny": [],
26
+ "ask": []
27
+ }
28
+ }
@@ -0,0 +1,33 @@
1
+ name: Continuous Integration
2
+
3
+ on:
4
+ pull_request:
5
+ paths:
6
+ - .github/workflows/ci.yml
7
+ - uv.lock
8
+ - pyproject.toml
9
+ - src/**
10
+ - tests/**
11
+ push:
12
+ paths:
13
+ - .github/workflows/ci.yml
14
+ - uv.lock
15
+ - pyproject.toml
16
+ - src/**
17
+ - tests/**
18
+
19
+ jobs:
20
+ build:
21
+ runs-on: ubuntu-24.04
22
+ strategy:
23
+ matrix:
24
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
25
+
26
+ steps:
27
+ - name: Checkout
28
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
29
+
30
+ - uses: CVector-Energy/python-test@uv
31
+ with:
32
+ python-version: ${{ matrix.python-version }}
33
+ src-dirs: .
@@ -0,0 +1,17 @@
1
+ name: Python License Check
2
+
3
+ on:
4
+ push:
5
+
6
+ jobs:
7
+ license-check:
8
+ runs-on: ubuntu-24.04
9
+
10
+ steps:
11
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
12
+
13
+ - name: Check Python licenses
14
+ uses: CVector-Energy/pyproject-license-check@main
15
+ with:
16
+ app-id: ${{ vars.APP_ID }}
17
+ app-private-key: ${{ secrets.APP_PRIVATE_KEY }}
@@ -0,0 +1,17 @@
1
+ name: Python License Check
2
+
3
+ on:
4
+ push:
5
+
6
+ jobs:
7
+ license-check:
8
+ runs-on: ubuntu-24.04
9
+
10
+ steps:
11
+ - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
12
+
13
+ - name: Check Python licenses
14
+ uses: CVector-Energy/pyproject-license-check@main
15
+ with:
16
+ app-id: ${{ vars.APP_ID }}
17
+ app-private-key: ${{ secrets.APP_PRIVATE_KEY }}
cvec-1.5.2/.gitignore ADDED
@@ -0,0 +1,177 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ #uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+
110
+ # pdm
111
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112
+ #pdm.lock
113
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114
+ # in version control.
115
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116
+ .pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121
+ __pypackages__/
122
+
123
+ # Celery stuff
124
+ celerybeat-schedule
125
+ celerybeat.pid
126
+
127
+ # SageMath parsed files
128
+ *.sage.py
129
+
130
+ # Environments
131
+ .env
132
+ .venv
133
+ env/
134
+ venv/
135
+ ENV/
136
+ env.bak/
137
+ venv.bak/
138
+
139
+ # Spyder project settings
140
+ .spyderproject
141
+ .spyproject
142
+
143
+ # Rope project settings
144
+ .ropeproject
145
+
146
+ # mkdocs documentation
147
+ /site
148
+
149
+ # mypy
150
+ .mypy_cache/
151
+ .dmypy.json
152
+ dmypy.json
153
+
154
+ # Pyre type checker
155
+ .pyre/
156
+
157
+ # pytype static type analyzer
158
+ .pytype/
159
+
160
+ # Cython debug symbols
161
+ cython_debug/
162
+
163
+ # PyCharm
164
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
167
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
168
+ #.idea/
169
+
170
+ # Ruff stuff:
171
+ .ruff_cache/
172
+
173
+ # PyPI configuration file
174
+ .pypirc
175
+ .aider*
176
+
177
+ .vscode/
cvec-1.5.2/CLAUDE.md ADDED
@@ -0,0 +1 @@
1
+ - Name files for their main export, transforming Python names from PascalCase to underscore_case. For example, the file that defines the EAVFilter class should be named eav_filter.py.
@@ -1,23 +1,23 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cvec
3
- Version: 1.4.3
3
+ Version: 1.5.2
4
4
  Summary: SDK for CVector Energy
5
+ Author-email: CVector <support@cvector.energy>
5
6
  License-Expression: MIT
6
7
  License-File: LICENSE
7
- Author: CVector
8
- Author-email: support@cvector.energy
9
- Requires-Python: >=3.10
8
+ Classifier: Intended Audience :: Developers
10
9
  Classifier: License :: OSI Approved :: MIT License
11
10
  Classifier: Operating System :: OS Independent
12
11
  Classifier: Programming Language :: Python :: 3
13
- Classifier: Intended Audience :: Developers
14
12
  Classifier: Programming Language :: Python :: 3.10
15
13
  Classifier: Programming Language :: Python :: 3.11
16
14
  Classifier: Programming Language :: Python :: 3.12
17
15
  Classifier: Programming Language :: Python :: 3.13
18
16
  Classifier: Programming Language :: Python :: 3.14
19
- Requires-Dist: pyarrow (>=22.0.0)
20
- Requires-Dist: pydantic (>=2.12.0)
17
+ Requires-Python: >=3.10
18
+ Requires-Dist: brotli<2,>=1.2.0
19
+ Requires-Dist: pyarrow>=22.0.0
20
+ Requires-Dist: pydantic>=2.12.0
21
21
  Description-Content-Type: text/markdown
22
22
 
23
23
  # CVec Client Library
@@ -55,7 +55,7 @@ import cvec
55
55
  from datetime import datetime
56
56
  ```
57
57
 
58
- Construct the CVec client. The host, tenant, and api_key can be given through parameters to the constructor or from the environment variables CVEC_HOST, and CVEC_API_KEY:
58
+ Construct the CVec client. The host and api_key can be given through parameters to the constructor or from the environment variables CVEC_HOST and CVEC_API_KEY:
59
59
 
60
60
  ```
61
61
  cvec = cvec.CVec()
@@ -110,7 +110,7 @@ mygroup/myedge/compressor01/motor/power_kw
110
110
 
111
111
  ### Metric Data
112
112
 
113
- The main content for a metric is a set of points where the metric value changed. These are returned as a Pandas Dataframe with columns for name, time, value_double, value_string.
113
+ The main content for a metric is a set of points where the metric value changed. These are returned with columns for name, time, value_double, value_string.
114
114
 
115
115
  To get all of the value changes for all metrics at 10am on 2025-05-14, run:
116
116
 
@@ -136,6 +136,17 @@ Example output:
136
136
 
137
137
  [46257 rows x 4 columns]
138
138
  ```
139
+ #### Pandas Data Frames
140
+
141
+ Use the `get_metric_arrow` function to efficiently load data into a pandas DataFrame like this:
142
+
143
+ ```python
144
+ import pandas as pd
145
+ import pyarrow as pa
146
+
147
+ reader = pa.ipc.open_file(cvec.get_metric_arrow(names=["tag1", "tag2"]))
148
+ df = reader.read_pandas()
149
+ ```
139
150
 
140
151
  ### Adding Metric Data
141
152
 
@@ -220,9 +231,9 @@ The script automatically:
220
231
 
221
232
  The SDK provides an API client class named `CVec` with the following functions.
222
233
 
223
- ## `__init__(?host, ?tenant, ?api_key, ?default_start_at, ?default_end_at)`
234
+ ## `__init__(?host, ?api_key, ?default_start_at, ?default_end_at)`
224
235
 
225
- Setup the SDK with the given host and API Key. The host and API key are loaded from environment variables CVEC_HOST, CVEC_API_KEY, if they are not given as arguments to the constructor. The `default_start_at` and `default_end_at` can provide a default query time interval for API methods.
236
+ Setup the SDK with the given host and API Key. The host and API key are loaded from environment variables CVEC_HOST and CVEC_API_KEY if they are not given as arguments to the constructor. The tenant ID is automatically fetched from the host's `/config` endpoint. The `default_start_at` and `default_end_at` can provide a default query time interval for API methods.
226
237
 
227
238
  ## `get_spans(name, ?start_at, ?end_at, ?limit)`
228
239
 
@@ -284,3 +295,113 @@ Fetch actual data values from modeling metrics within a time range in Apache Arr
284
295
 
285
296
  Returns Arrow IPC format data that can be read using `pyarrow.ipc.open_file()`.
286
297
 
298
+ ## `get_eav_tables()`
299
+
300
+ Get all EAV (Entity-Attribute-Value) tables for the tenant. EAV tables store semi-structured data where each row represents an entity with flexible attributes.
301
+
302
+ Returns a list of `EAVTable` objects, each containing:
303
+ - `id`: The table's UUID
304
+ - `tenant_id`: The tenant ID
305
+ - `name`: Human-readable table name
306
+ - `created_at`: When the table was created
307
+ - `updated_at`: When the table was last updated
308
+
309
+ Example:
310
+ ```python
311
+ tables = cvec.get_eav_tables()
312
+ for table in tables:
313
+ print(f"{table.name} (id: {table.id})")
314
+ ```
315
+
316
+ ## `get_eav_columns(table_id)`
317
+
318
+ Get all columns for a specific EAV table.
319
+
320
+ - `table_id`: The UUID of the EAV table
321
+
322
+ Returns a list of `EAVColumn` objects, each containing:
323
+ - `eav_table_id`: The parent table's UUID
324
+ - `eav_column_id`: The column's ID (used for queries)
325
+ - `name`: Human-readable column name
326
+ - `type`: Data type (`"number"`, `"string"`, or `"boolean"`)
327
+ - `created_at`: When the column was created
328
+
329
+ Example:
330
+ ```python
331
+ columns = cvec.get_eav_columns("00000000-0000-0000-0000-000000000000")
332
+ for column in columns:
333
+ print(f" {column.name} ({column.type}, id: {column.eav_column_id})")
334
+ ```
335
+
336
+ ## `select_from_eav(table_name, ?column_names, ?filters)`
337
+
338
+ Query pivoted data from EAV tables using human-readable names. This is the recommended method for most use cases as it allows you to work with table and column names instead of UUIDs.
339
+
340
+ - `table_name`: Name of the EAV table to query
341
+ - `column_names`: Optional list of column names to include. If `None`, all columns are returned.
342
+ - `filters`: Optional list of `EAVFilter` objects to filter results
343
+
344
+ Each `EAVFilter` must use `column_name` and can specify:
345
+ - `column_name`: The column name to filter on (required)
346
+ - `numeric_min`: Minimum numeric value (inclusive)
347
+ - `numeric_max`: Maximum numeric value (exclusive)
348
+ - `string_value`: Exact string value to match
349
+ - `boolean_value`: Boolean value to match
350
+
351
+ Returns a list of dictionaries (maximum 1000 rows), each representing a row with an `id` field and fields for each requested column.
352
+
353
+ Example:
354
+ ```python
355
+ from cvec import CVec, EAVFilter
356
+
357
+ # Query with filters
358
+ filters = [
359
+ EAVFilter(column_name="Weight", numeric_min=100, numeric_max=200),
360
+ EAVFilter(column_name="Status", string_value="ACTIVE"),
361
+ ]
362
+
363
+ rows = cvec.select_from_eav(
364
+ table_name="Production Data",
365
+ column_names=["Date", "Weight", "Status"],
366
+ filters=filters,
367
+ )
368
+
369
+ for row in rows:
370
+ print(row)
371
+ ```
372
+
373
+ ## `select_from_eav_id(table_id, ?column_ids, ?filters)`
374
+
375
+ Query pivoted data from EAV tables using table and column IDs directly. This is a lower-level method for cases where you already have the UUIDs and want to avoid name lookups.
376
+
377
+ - `table_id`: UUID of the EAV table to query
378
+ - `column_ids`: Optional list of column IDs to include. If `None`, all columns are returned.
379
+ - `filters`: Optional list of `EAVFilter` objects to filter results
380
+
381
+ Each `EAVFilter` must use `column_id` and can specify:
382
+ - `column_id`: The column ID to filter on (required)
383
+ - `numeric_min`: Minimum numeric value (inclusive)
384
+ - `numeric_max`: Maximum numeric value (exclusive)
385
+ - `string_value`: Exact string value to match
386
+ - `boolean_value`: Boolean value to match
387
+
388
+ Returns a list of dictionaries (up to 1000), each representing a row. Each row has a field for each column plus an `id` (the "row ID").
389
+
390
+ Example:
391
+ ```python
392
+ from cvec import EAVFilter
393
+
394
+ filters = [
395
+ EAVFilter(column_id="abcd", numeric_min=100, numeric_max=200),
396
+ EAVFilter(column_id="efgh", string_value="ACTIVE"),
397
+ ]
398
+
399
+ rows = cvec.select_from_eav_id(
400
+ table_id="00000000-0000-0000-0000-000000000000",
401
+ column_ids=["abcd", "efgh", "ijkl"],
402
+ filters=filters,
403
+ )
404
+
405
+ for row in rows:
406
+ print(f"ID: {row['id']}, Values: {row}")
407
+ ```
@@ -33,7 +33,7 @@ import cvec
33
33
  from datetime import datetime
34
34
  ```
35
35
 
36
- Construct the CVec client. The host, tenant, and api_key can be given through parameters to the constructor or from the environment variables CVEC_HOST, and CVEC_API_KEY:
36
+ Construct the CVec client. The host and api_key can be given through parameters to the constructor or from the environment variables CVEC_HOST and CVEC_API_KEY:
37
37
 
38
38
  ```
39
39
  cvec = cvec.CVec()
@@ -88,7 +88,7 @@ mygroup/myedge/compressor01/motor/power_kw
88
88
 
89
89
  ### Metric Data
90
90
 
91
- The main content for a metric is a set of points where the metric value changed. These are returned as a Pandas Dataframe with columns for name, time, value_double, value_string.
91
+ The main content for a metric is a set of points where the metric value changed. These are returned with columns for name, time, value_double, value_string.
92
92
 
93
93
  To get all of the value changes for all metrics at 10am on 2025-05-14, run:
94
94
 
@@ -114,6 +114,17 @@ Example output:
114
114
 
115
115
  [46257 rows x 4 columns]
116
116
  ```
117
+ #### Pandas Data Frames
118
+
119
+ Use the `get_metric_arrow` function to efficiently load data into a pandas DataFrame like this:
120
+
121
+ ```python
122
+ import pandas as pd
123
+ import pyarrow as pa
124
+
125
+ reader = pa.ipc.open_file(cvec.get_metric_arrow(names=["tag1", "tag2"]))
126
+ df = reader.read_pandas()
127
+ ```
117
128
 
118
129
  ### Adding Metric Data
119
130
 
@@ -198,9 +209,9 @@ The script automatically:
198
209
 
199
210
  The SDK provides an API client class named `CVec` with the following functions.
200
211
 
201
- ## `__init__(?host, ?tenant, ?api_key, ?default_start_at, ?default_end_at)`
212
+ ## `__init__(?host, ?api_key, ?default_start_at, ?default_end_at)`
202
213
 
203
- Setup the SDK with the given host and API Key. The host and API key are loaded from environment variables CVEC_HOST, CVEC_API_KEY, if they are not given as arguments to the constructor. The `default_start_at` and `default_end_at` can provide a default query time interval for API methods.
214
+ Setup the SDK with the given host and API Key. The host and API key are loaded from environment variables CVEC_HOST and CVEC_API_KEY if they are not given as arguments to the constructor. The tenant ID is automatically fetched from the host's `/config` endpoint. The `default_start_at` and `default_end_at` can provide a default query time interval for API methods.
204
215
 
205
216
  ## `get_spans(name, ?start_at, ?end_at, ?limit)`
206
217
 
@@ -261,3 +272,114 @@ Fetch actual data values from modeling metrics within a time range in Apache Arr
261
272
  - `end_at`: Optional end time for the query (uses class default if not specified)
262
273
 
263
274
  Returns Arrow IPC format data that can be read using `pyarrow.ipc.open_file()`.
275
+
276
+ ## `get_eav_tables()`
277
+
278
+ Get all EAV (Entity-Attribute-Value) tables for the tenant. EAV tables store semi-structured data where each row represents an entity with flexible attributes.
279
+
280
+ Returns a list of `EAVTable` objects, each containing:
281
+ - `id`: The table's UUID
282
+ - `tenant_id`: The tenant ID
283
+ - `name`: Human-readable table name
284
+ - `created_at`: When the table was created
285
+ - `updated_at`: When the table was last updated
286
+
287
+ Example:
288
+ ```python
289
+ tables = cvec.get_eav_tables()
290
+ for table in tables:
291
+ print(f"{table.name} (id: {table.id})")
292
+ ```
293
+
294
+ ## `get_eav_columns(table_id)`
295
+
296
+ Get all columns for a specific EAV table.
297
+
298
+ - `table_id`: The UUID of the EAV table
299
+
300
+ Returns a list of `EAVColumn` objects, each containing:
301
+ - `eav_table_id`: The parent table's UUID
302
+ - `eav_column_id`: The column's ID (used for queries)
303
+ - `name`: Human-readable column name
304
+ - `type`: Data type (`"number"`, `"string"`, or `"boolean"`)
305
+ - `created_at`: When the column was created
306
+
307
+ Example:
308
+ ```python
309
+ columns = cvec.get_eav_columns("00000000-0000-0000-0000-000000000000")
310
+ for column in columns:
311
+ print(f" {column.name} ({column.type}, id: {column.eav_column_id})")
312
+ ```
313
+
314
+ ## `select_from_eav(table_name, ?column_names, ?filters)`
315
+
316
+ Query pivoted data from EAV tables using human-readable names. This is the recommended method for most use cases as it allows you to work with table and column names instead of UUIDs.
317
+
318
+ - `table_name`: Name of the EAV table to query
319
+ - `column_names`: Optional list of column names to include. If `None`, all columns are returned.
320
+ - `filters`: Optional list of `EAVFilter` objects to filter results
321
+
322
+ Each `EAVFilter` must use `column_name` and can specify:
323
+ - `column_name`: The column name to filter on (required)
324
+ - `numeric_min`: Minimum numeric value (inclusive)
325
+ - `numeric_max`: Maximum numeric value (exclusive)
326
+ - `string_value`: Exact string value to match
327
+ - `boolean_value`: Boolean value to match
328
+
329
+ Returns a list of dictionaries (maximum 1000 rows), each representing a row with an `id` field and fields for each requested column.
330
+
331
+ Example:
332
+ ```python
333
+ from cvec import CVec, EAVFilter
334
+
335
+ # Query with filters
336
+ filters = [
337
+ EAVFilter(column_name="Weight", numeric_min=100, numeric_max=200),
338
+ EAVFilter(column_name="Status", string_value="ACTIVE"),
339
+ ]
340
+
341
+ rows = cvec.select_from_eav(
342
+ table_name="Production Data",
343
+ column_names=["Date", "Weight", "Status"],
344
+ filters=filters,
345
+ )
346
+
347
+ for row in rows:
348
+ print(row)
349
+ ```
350
+
351
+ ## `select_from_eav_id(table_id, ?column_ids, ?filters)`
352
+
353
+ Query pivoted data from EAV tables using table and column IDs directly. This is a lower-level method for cases where you already have the UUIDs and want to avoid name lookups.
354
+
355
+ - `table_id`: UUID of the EAV table to query
356
+ - `column_ids`: Optional list of column IDs to include. If `None`, all columns are returned.
357
+ - `filters`: Optional list of `EAVFilter` objects to filter results
358
+
359
+ Each `EAVFilter` must use `column_id` and can specify:
360
+ - `column_id`: The column ID to filter on (required)
361
+ - `numeric_min`: Minimum numeric value (inclusive)
362
+ - `numeric_max`: Maximum numeric value (exclusive)
363
+ - `string_value`: Exact string value to match
364
+ - `boolean_value`: Boolean value to match
365
+
366
+ Returns a list of dictionaries (up to 1000), each representing a row. Each row has a field for each column plus an `id` (the "row ID").
367
+
368
+ Example:
369
+ ```python
370
+ from cvec import EAVFilter
371
+
372
+ filters = [
373
+ EAVFilter(column_id="abcd", numeric_min=100, numeric_max=200),
374
+ EAVFilter(column_id="efgh", string_value="ACTIVE"),
375
+ ]
376
+
377
+ rows = cvec.select_from_eav_id(
378
+ table_id="00000000-0000-0000-0000-000000000000",
379
+ column_ids=["abcd", "efgh", "ijkl"],
380
+ filters=filters,
381
+ )
382
+
383
+ for row in rows:
384
+ print(f"ID: {row['id']}, Values: {row}")
385
+ ```