pam-python 0.2.0__tar.gz → 0.2.1__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 (116) hide show
  1. {pam_python-0.2.0 → pam_python-0.2.1}/PKG-INFO +49 -49
  2. {pam_python-0.2.0 → pam_python-0.2.1}/README.md +43 -26
  3. {pam_python-0.2.0 → pam_python-0.2.1}/pam/__init__.py +1 -1
  4. pam_python-0.2.1/pam/cli.py +354 -0
  5. {pam_python-0.2.0 → pam_python-0.2.1}/pam/models/request_command.py +4 -0
  6. pam_python-0.2.1/pam/request_file_format.py +14 -0
  7. pam_python-0.2.1/pam/result_batch_uploader.py +227 -0
  8. {pam_python-0.2.0 → pam_python-0.2.1}/pam/server.py +22 -2
  9. {pam_python-0.2.0 → pam_python-0.2.1}/pam/service.py +38 -25
  10. {pam_python-0.2.0 → pam_python-0.2.1}/pam/templates/init/AGENT.md +48 -68
  11. {pam_python-0.2.0 → pam_python-0.2.1}/pam/templates/init/REPORTS.md +2 -2
  12. pam_python-0.2.1/pam/templates/init/dockerignore.tmpl +6 -0
  13. pam_python-0.2.1/pam/templates/project/pip/Dockerfile +16 -0
  14. {pam_python-0.2.0/pam/templates/init → pam_python-0.2.1/pam/templates/project/pip}/requirements.txt +0 -2
  15. pam_python-0.2.1/pam/templates/project/uv/Dockerfile +20 -0
  16. pam_python-0.2.1/pam/templates/project/uv/pyproject.toml +12 -0
  17. pam_python-0.2.1/pam/templates/project/uv/python-version +1 -0
  18. pam_python-0.2.1/pam/templates/service/pandas/functions.tmpl +13 -0
  19. {pam_python-0.2.0/pam/templates/service → pam_python-0.2.1/pam/templates/service/pandas}/service.test.tmpl +31 -46
  20. pam_python-0.2.1/pam/templates/service/pandas/service_class.tmpl +65 -0
  21. pam_python-0.2.1/pam/templates/service/polars/functions.tmpl +11 -0
  22. pam_python-0.2.1/pam/templates/service/polars/service.test.tmpl +122 -0
  23. pam_python-0.2.1/pam/templates/service/polars/service_class.tmpl +59 -0
  24. {pam_python-0.2.0 → pam_python-0.2.1}/pam/tester_task.py +22 -7
  25. {pam_python-0.2.0 → pam_python-0.2.1}/pam/utils.py +2 -4
  26. {pam_python-0.2.0 → pam_python-0.2.1}/pam_python.egg-info/PKG-INFO +49 -49
  27. {pam_python-0.2.0 → pam_python-0.2.1}/pam_python.egg-info/SOURCES.txt +14 -7
  28. pam_python-0.2.1/pam_python.egg-info/requires.txt +3 -0
  29. pam_python-0.2.1/pyproject.toml +54 -0
  30. pam_python-0.2.1/tests/test_cli_templates.py +133 -0
  31. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_result_batch_uploader.py +28 -2
  32. pam_python-0.2.1/tests/test_service_dataframe_output.py +41 -0
  33. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_sqlite_download_v2.py +21 -0
  34. pam_python-0.2.0/pam/cli.py +0 -226
  35. pam_python-0.2.0/pam/request_file_format.py +0 -5
  36. pam_python-0.2.0/pam/result_batch_uploader.py +0 -179
  37. pam_python-0.2.0/pam/templates/docker/Dockerfile +0 -21
  38. pam_python-0.2.0/pam/templates/init/dockerignore.tmpl +0 -4
  39. pam_python-0.2.0/pam/templates/service/functions.tmpl +0 -22
  40. pam_python-0.2.0/pam/templates/service/service_class.tmpl +0 -81
  41. pam_python-0.2.0/pam_python.egg-info/requires.txt +0 -9
  42. pam_python-0.2.0/setup.py +0 -43
  43. pam_python-0.2.0/tests/test_cli_templates.py +0 -78
  44. {pam_python-0.2.0 → pam_python-0.2.1}/LICENSE.txt +0 -0
  45. {pam_python-0.2.0 → pam_python-0.2.1}/pam/api.py +0 -0
  46. {pam_python-0.2.0 → pam_python-0.2.1}/pam/interface_task_manager.py +0 -0
  47. {pam_python-0.2.0 → pam_python-0.2.1}/pam/logger.py +0 -0
  48. {pam_python-0.2.0 → pam_python-0.2.1}/pam/models/__init__.py +0 -0
  49. {pam_python-0.2.0 → pam_python-0.2.1}/pam/models/sqlite_download.py +0 -0
  50. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/__init__.py +0 -0
  51. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/area.py +0 -0
  52. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/axis.py +0 -0
  53. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/bar.py +0 -0
  54. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/box_plot.py +0 -0
  55. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/bubble.py +0 -0
  56. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/candlestick.py +0 -0
  57. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/collection.py +0 -0
  58. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/definition.py +0 -0
  59. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/donut.py +0 -0
  60. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/errors.py +0 -0
  61. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/fields.py +0 -0
  62. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/heatmap.py +0 -0
  63. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/line.py +0 -0
  64. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/manager.py +0 -0
  65. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/metric.py +0 -0
  66. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/pie.py +0 -0
  67. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/polar_area.py +0 -0
  68. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/radar.py +0 -0
  69. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/radial_bar.py +0 -0
  70. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/range_area.py +0 -0
  71. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/range_bar.py +0 -0
  72. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/range_transform.py +0 -0
  73. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/record.py +0 -0
  74. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/report.py +0 -0
  75. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/scatter.py +0 -0
  76. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/schema.py +0 -0
  77. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/store.py +0 -0
  78. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/table.py +0 -0
  79. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/transport.py +0 -0
  80. {pam_python-0.2.0 → pam_python-0.2.1}/pam/reports/treemap.py +0 -0
  81. {pam_python-0.2.0 → pam_python-0.2.1}/pam/sqlite.py +0 -0
  82. {pam_python-0.2.0 → pam_python-0.2.1}/pam/task_manager.py +0 -0
  83. {pam_python-0.2.0 → pam_python-0.2.1}/pam/temp_file_utils.py +0 -0
  84. {pam_python-0.2.0 → pam_python-0.2.1}/pam/templates/buildcmd/pamb +0 -0
  85. {pam_python-0.2.0 → pam_python-0.2.1}/pam/templates/buildcmd/pamb-base.sh +0 -0
  86. {pam_python-0.2.0 → pam_python-0.2.1}/pam/templates/init/gitignore.tmpl +0 -0
  87. {pam_python-0.2.0 → pam_python-0.2.1}/pam/templates/init/main.tmpl +0 -0
  88. {pam_python-0.2.0 → pam_python-0.2.1}/pam/templates/init/pylintrc.tmpl +0 -0
  89. {pam_python-0.2.0 → pam_python-0.2.1}/pam/templates/init/run_unit_test.bat +0 -0
  90. {pam_python-0.2.0 → pam_python-0.2.1}/pam/templates/init/run_unit_test.ps1 +0 -0
  91. {pam_python-0.2.0 → pam_python-0.2.1}/pam/templates/init/run_unit_test.sh +0 -0
  92. {pam_python-0.2.0/pam/templates/service → pam_python-0.2.1/pam/templates/service/common}/service.yaml +1 -1
  93. {pam_python-0.2.0 → pam_python-0.2.1}/pam_python.egg-info/dependency_links.txt +0 -0
  94. {pam_python-0.2.0 → pam_python-0.2.1}/pam_python.egg-info/entry_points.txt +0 -0
  95. {pam_python-0.2.0 → pam_python-0.2.1}/pam_python.egg-info/top_level.txt +0 -0
  96. {pam_python-0.2.0 → pam_python-0.2.1}/setup.cfg +0 -0
  97. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_range_transform.py +0 -0
  98. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_report_store.py +0 -0
  99. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_area_report.py +0 -0
  100. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_bar_report.py +0 -0
  101. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_box_plot_report.py +0 -0
  102. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_bubble_report.py +0 -0
  103. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_candlestick_report.py +0 -0
  104. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_donut_report.py +0 -0
  105. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_heatmap_report.py +0 -0
  106. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_line_report.py +0 -0
  107. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_metric_report.py +0 -0
  108. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_pie_report.py +0 -0
  109. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_polar_area_report.py +0 -0
  110. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_radar_report.py +0 -0
  111. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_radial_bar_report.py +0 -0
  112. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_range_area_report.py +0 -0
  113. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_range_bar_report.py +0 -0
  114. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_scatter_report.py +0 -0
  115. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_table_report.py +0 -0
  116. {pam_python-0.2.0 → pam_python-0.2.1}/tests/test_typed_treemap_report.py +0 -0
@@ -1,39 +1,22 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pam-python
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Pam Python Library
5
- Home-page: https://github.com/heart/pam-python
6
- Author: Narongrit Kanhanoi
7
- Author-email: narongrit@pams.ai
5
+ Author-email: Narongrit Kanhanoi <narongrit@pams.ai>
6
+ Project-URL: Homepage, https://github.com/heart/pam-python
8
7
  Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3.9
10
- Classifier: License :: Other/Proprietary License
8
+ Classifier: Programming Language :: Python :: 3.12
11
9
  Classifier: Operating System :: OS Independent
12
10
  Classifier: Intended Audience :: Developers
13
11
  Classifier: Intended Audience :: Customer Service
14
12
  Classifier: Topic :: Software Development :: Libraries
15
- Requires-Python: >=3.9
13
+ Requires-Python: <3.13,>=3.12
16
14
  Description-Content-Type: text/markdown
17
15
  License-File: LICENSE.txt
18
- Requires-Dist: setuptools>=70.0.0
19
16
  Requires-Dist: Flask>=3.0.2
20
- Requires-Dist: aiohttp>=3.11.11
21
- Requires-Dist: pandas>=2.2.3
22
- Requires-Dist: dask>=2024.12.1
23
- Requires-Dist: dask-expr>=1.1.21
24
17
  Requires-Dist: requests>=2.32.3
25
- Requires-Dist: gunicorn>=23.0.0
26
- Requires-Dist: pyarrow>=19.0.1
27
- Dynamic: author
28
- Dynamic: author-email
29
- Dynamic: classifier
30
- Dynamic: description
31
- Dynamic: description-content-type
32
- Dynamic: home-page
18
+ Requires-Dist: PyYAML>=6.0.2
33
19
  Dynamic: license-file
34
- Dynamic: requires-dist
35
- Dynamic: requires-python
36
- Dynamic: summary
37
20
 
38
21
  # pam-python-data-plugin-framework
39
22
 
@@ -41,8 +24,6 @@ This repository provides the `pam` CLI and runtime framework to build Data Plugi
41
24
 
42
25
  This README is a practical, step-by-step guide you can follow to create and run a real service.
43
26
 
44
- Current release target: **0.2.0**
45
-
46
27
  **What you get**
47
28
 
48
29
  - CLI to initialize a project and scaffold services
@@ -72,25 +53,26 @@ Current release target: **0.2.0**
72
53
 
73
54
  **Prerequisites**
74
55
 
75
- - Python 3.9+
76
- - `pip` and a working virtual environment
56
+ - Python 3.12
57
+ - `uv` (recommended) or `pip`
77
58
 
78
59
  ---
79
60
 
80
61
  **Install**
81
- Create a project folder and a virtual environment.
62
+ Recommended installation:
82
63
 
83
64
  ```bash
84
65
  mkdir my_data_plugin
85
66
  cd my_data_plugin
86
- python3 -m venv venv
87
- source venv/bin/activate
67
+ uv tool install pam-python
88
68
  ```
89
69
 
90
- Install the framework:
70
+ Pip remains supported:
91
71
 
92
72
  ```bash
93
- pip install pam-python
73
+ python3 -m venv venv
74
+ source venv/bin/activate
75
+ python -m pip install pam-python
94
76
  ```
95
77
 
96
78
  ---
@@ -103,23 +85,26 @@ rules and `REPORTS.md` for the managed-report public API and copyable examples.
103
85
  pam init
104
86
  ```
105
87
 
106
- When `requirements.txt` already exists, you will be prompted to choose how to proceed:
107
-
108
- - overwrite
109
- - keep
110
- - merge
88
+ `pam init` asks you to select `uv` (default) or `pip`. A uv project contains
89
+ `pyproject.toml`, `.python-version`, and `uv.lock`; a pip project contains
90
+ `requirements.txt`. The two templates are independent and are never mixed.
111
91
 
112
- `pam init` now writes a project baseline `requirements.txt`. It pins `pam-python` to the scaffold version and includes core runtime/test dependencies used by the generated project. It does not copy the current machine's `pip freeze`.
92
+ Initialization only supports a new project directory and will not migrate or
93
+ overwrite an existing project.
113
94
 
114
95
  ---
115
96
 
116
97
  **Create a Service**
117
- Generate a service scaffold. Do not hand-create service templates.
118
98
 
119
99
  ```bash
120
100
  pam new service rfm_segment
121
101
  ```
122
102
 
103
+ The generator asks you to select Polars (default) or Pandas. Polars services use
104
+ lazy Parquet processing. Pandas services also receive Parquet and install
105
+ `pandas` plus `pyarrow`. The selected direct dependencies are added to the
106
+ project automatically.
107
+
123
108
  This creates a new folder (e.g. `rfm_segment/`) with:
124
109
 
125
110
  - a service class (`RfmSegmentSvc.py`)
@@ -141,7 +126,8 @@ The runtime calls your service in two main phases.
141
126
  2. `on_data_input`
142
127
 
143
128
  - Called when CDP sends input files
144
- - `req.input_files` contains ordered CSV files
129
+ - `req.input_files` contains ordered Parquet files
130
+ - `req.file_format` is the typed format sent by PAM
145
131
  - Should also return quickly (use a thread if needed)
146
132
 
147
133
  When your service is done:
@@ -169,7 +155,11 @@ Notes:
169
155
  ---
170
156
 
171
157
  **Uploading Results in Batches**
172
- If your service produces too many rows (or too few per event), use the batch uploader to handle chunking and flushing automatically.
158
+ Polars templates pass `LazyFrame` results directly to `_upload_result(...)`, which
159
+ streams them to the CSV upload boundary. Do not collect or convert to Pandas first.
160
+
161
+ For eager Polars or Pandas DataFrames, use the batch uploader to handle chunking
162
+ and flushing automatically.
173
163
 
174
164
  Recommended usage:
175
165
 
@@ -189,6 +179,7 @@ Notes:
189
179
  - `options` may be passed to `upload(...)` and are forwarded to `_upload_result`.
190
180
  - Complete batches upload immediately; `flush()` sends the remaining rows.
191
181
  - `get_status()` returns buffered rows, uploaded rows, and uploaded batch counts per stream.
182
+ - `ResultBatchUploader` intentionally rejects Polars `LazyFrame`; upload it directly.
192
183
 
193
184
  ---
194
185
 
@@ -225,7 +216,8 @@ Important rules:
225
216
  The generated `main.py` runs the Flask server.
226
217
 
227
218
  ```bash
228
- python main.py
219
+ uv run python main.py # uv project
220
+ python main.py # pip project
229
221
  ```
230
222
 
231
223
  By default it binds to `0.0.0.0:8000`. You can override with:
@@ -238,9 +230,13 @@ export SERVER_PORT=8000
238
230
  ---
239
231
 
240
232
  **Testing a Service**
241
- Run tests through the generated script for your current shell. This keeps the
242
- active terminal's Python environment and project context instead of launching
243
- test discovery through the `pam` CLI.
233
+ For uv projects, use one cross-platform command:
234
+
235
+ ```bash
236
+ uv run python -m unittest discover -s rfm_segment -p "test_*.py"
237
+ ```
238
+
239
+ Pip projects include shell-specific scripts:
244
240
 
245
241
  macOS/Linux:
246
242
 
@@ -285,20 +281,23 @@ After `pam init` and one service:
285
281
  ├── AGENT.md
286
282
  ├── REPORTS.md
287
283
  ├── Dockerfile
284
+ ├── pyproject.toml
285
+ ├── uv.lock
286
+ ├── .python-version
288
287
  ├── rfm_segment/
289
288
  │ ├── RfmSegmentSvc.py
290
289
  │ ├── functions.py
291
290
  │ ├── service.yaml
292
291
  │ └── test_rfm_segment.py
293
- ├── requirements.txt
294
- └── run_unit_test.sh
292
+ └── .pam-project
295
293
  ```
296
294
 
297
295
  ---
298
296
 
299
297
  **Troubleshooting**
300
298
 
301
- - If `pam` command is missing, ensure your virtualenv is activated.
299
+ - If `pam` is missing, verify the uv tool installation or activate the pip virtualenv.
300
+ - If uv initialization fails, verify `uv` is on `PATH` and package indexes are reachable.
302
301
  - If `pam new service` fails, confirm the service name is provided.
303
302
  - If temp cleanup is too frequent or too slow, adjust `TEMP_CLEAN_INTERVAL_HOURS` and `TEMP_CLEAN_DAYS`.
304
303
 
@@ -309,5 +308,6 @@ After `pam init` and one service:
309
308
  - Implement your logic in `functions.py`.
310
309
  - Wire it into `on_start` and `on_data_input` in your service class.
311
310
  - Use the temp utilities to write intermediate files.
312
- - Use `ResultBatchUploader` to return ordinary output to CDP.
311
+ - Keep Polars transformations lazy and upload the resulting `LazyFrame` directly.
312
+ - Use `ResultBatchUploader` only for eager Polars/Pandas frames that need row batching.
313
313
  - Read `REPORTS.md` before implementing a managed report.
@@ -4,8 +4,6 @@ This repository provides the `pam` CLI and runtime framework to build Data Plugi
4
4
 
5
5
  This README is a practical, step-by-step guide you can follow to create and run a real service.
6
6
 
7
- Current release target: **0.2.0**
8
-
9
7
  **What you get**
10
8
 
11
9
  - CLI to initialize a project and scaffold services
@@ -35,25 +33,26 @@ Current release target: **0.2.0**
35
33
 
36
34
  **Prerequisites**
37
35
 
38
- - Python 3.9+
39
- - `pip` and a working virtual environment
36
+ - Python 3.12
37
+ - `uv` (recommended) or `pip`
40
38
 
41
39
  ---
42
40
 
43
41
  **Install**
44
- Create a project folder and a virtual environment.
42
+ Recommended installation:
45
43
 
46
44
  ```bash
47
45
  mkdir my_data_plugin
48
46
  cd my_data_plugin
49
- python3 -m venv venv
50
- source venv/bin/activate
47
+ uv tool install pam-python
51
48
  ```
52
49
 
53
- Install the framework:
50
+ Pip remains supported:
54
51
 
55
52
  ```bash
56
- pip install pam-python
53
+ python3 -m venv venv
54
+ source venv/bin/activate
55
+ python -m pip install pam-python
57
56
  ```
58
57
 
59
58
  ---
@@ -66,23 +65,26 @@ rules and `REPORTS.md` for the managed-report public API and copyable examples.
66
65
  pam init
67
66
  ```
68
67
 
69
- When `requirements.txt` already exists, you will be prompted to choose how to proceed:
70
-
71
- - overwrite
72
- - keep
73
- - merge
68
+ `pam init` asks you to select `uv` (default) or `pip`. A uv project contains
69
+ `pyproject.toml`, `.python-version`, and `uv.lock`; a pip project contains
70
+ `requirements.txt`. The two templates are independent and are never mixed.
74
71
 
75
- `pam init` now writes a project baseline `requirements.txt`. It pins `pam-python` to the scaffold version and includes core runtime/test dependencies used by the generated project. It does not copy the current machine's `pip freeze`.
72
+ Initialization only supports a new project directory and will not migrate or
73
+ overwrite an existing project.
76
74
 
77
75
  ---
78
76
 
79
77
  **Create a Service**
80
- Generate a service scaffold. Do not hand-create service templates.
81
78
 
82
79
  ```bash
83
80
  pam new service rfm_segment
84
81
  ```
85
82
 
83
+ The generator asks you to select Polars (default) or Pandas. Polars services use
84
+ lazy Parquet processing. Pandas services also receive Parquet and install
85
+ `pandas` plus `pyarrow`. The selected direct dependencies are added to the
86
+ project automatically.
87
+
86
88
  This creates a new folder (e.g. `rfm_segment/`) with:
87
89
 
88
90
  - a service class (`RfmSegmentSvc.py`)
@@ -104,7 +106,8 @@ The runtime calls your service in two main phases.
104
106
  2. `on_data_input`
105
107
 
106
108
  - Called when CDP sends input files
107
- - `req.input_files` contains ordered CSV files
109
+ - `req.input_files` contains ordered Parquet files
110
+ - `req.file_format` is the typed format sent by PAM
108
111
  - Should also return quickly (use a thread if needed)
109
112
 
110
113
  When your service is done:
@@ -132,7 +135,11 @@ Notes:
132
135
  ---
133
136
 
134
137
  **Uploading Results in Batches**
135
- If your service produces too many rows (or too few per event), use the batch uploader to handle chunking and flushing automatically.
138
+ Polars templates pass `LazyFrame` results directly to `_upload_result(...)`, which
139
+ streams them to the CSV upload boundary. Do not collect or convert to Pandas first.
140
+
141
+ For eager Polars or Pandas DataFrames, use the batch uploader to handle chunking
142
+ and flushing automatically.
136
143
 
137
144
  Recommended usage:
138
145
 
@@ -152,6 +159,7 @@ Notes:
152
159
  - `options` may be passed to `upload(...)` and are forwarded to `_upload_result`.
153
160
  - Complete batches upload immediately; `flush()` sends the remaining rows.
154
161
  - `get_status()` returns buffered rows, uploaded rows, and uploaded batch counts per stream.
162
+ - `ResultBatchUploader` intentionally rejects Polars `LazyFrame`; upload it directly.
155
163
 
156
164
  ---
157
165
 
@@ -188,7 +196,8 @@ Important rules:
188
196
  The generated `main.py` runs the Flask server.
189
197
 
190
198
  ```bash
191
- python main.py
199
+ uv run python main.py # uv project
200
+ python main.py # pip project
192
201
  ```
193
202
 
194
203
  By default it binds to `0.0.0.0:8000`. You can override with:
@@ -201,9 +210,13 @@ export SERVER_PORT=8000
201
210
  ---
202
211
 
203
212
  **Testing a Service**
204
- Run tests through the generated script for your current shell. This keeps the
205
- active terminal's Python environment and project context instead of launching
206
- test discovery through the `pam` CLI.
213
+ For uv projects, use one cross-platform command:
214
+
215
+ ```bash
216
+ uv run python -m unittest discover -s rfm_segment -p "test_*.py"
217
+ ```
218
+
219
+ Pip projects include shell-specific scripts:
207
220
 
208
221
  macOS/Linux:
209
222
 
@@ -248,20 +261,23 @@ After `pam init` and one service:
248
261
  ├── AGENT.md
249
262
  ├── REPORTS.md
250
263
  ├── Dockerfile
264
+ ├── pyproject.toml
265
+ ├── uv.lock
266
+ ├── .python-version
251
267
  ├── rfm_segment/
252
268
  │ ├── RfmSegmentSvc.py
253
269
  │ ├── functions.py
254
270
  │ ├── service.yaml
255
271
  │ └── test_rfm_segment.py
256
- ├── requirements.txt
257
- └── run_unit_test.sh
272
+ └── .pam-project
258
273
  ```
259
274
 
260
275
  ---
261
276
 
262
277
  **Troubleshooting**
263
278
 
264
- - If `pam` command is missing, ensure your virtualenv is activated.
279
+ - If `pam` is missing, verify the uv tool installation or activate the pip virtualenv.
280
+ - If uv initialization fails, verify `uv` is on `PATH` and package indexes are reachable.
265
281
  - If `pam new service` fails, confirm the service name is provided.
266
282
  - If temp cleanup is too frequent or too slow, adjust `TEMP_CLEAN_INTERVAL_HOURS` and `TEMP_CLEAN_DAYS`.
267
283
 
@@ -272,5 +288,6 @@ After `pam init` and one service:
272
288
  - Implement your logic in `functions.py`.
273
289
  - Wire it into `on_start` and `on_data_input` in your service class.
274
290
  - Use the temp utilities to write intermediate files.
275
- - Use `ResultBatchUploader` to return ordinary output to CDP.
291
+ - Keep Polars transformations lazy and upload the resulting `LazyFrame` directly.
292
+ - Use `ResultBatchUploader` only for eager Polars/Pandas frames that need row batching.
276
293
  - Read `REPORTS.md` before implementing a managed report.
@@ -1,3 +1,3 @@
1
1
  """PAM Data Plugin framework."""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.2.1"