ApiLogicServer 15.4.3__py3-none-any.whl → 16.0.0__py3-none-any.whl

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 (26) hide show
  1. api_logic_server_cli/add_cust/add_cust.py +6 -2
  2. api_logic_server_cli/api_logic_server.py +2 -1
  3. api_logic_server_cli/database/basic_demo.sqlite +0 -0
  4. api_logic_server_cli/prototypes/base/.github/.copilot-instructions.md +228 -76
  5. api_logic_server_cli/prototypes/base/docs/training/OVERVIEW.md +64 -0
  6. api_logic_server_cli/prototypes/base/docs/training/README.md +140 -0
  7. api_logic_server_cli/prototypes/base/docs/training/genai_logic_patterns.md +443 -0
  8. api_logic_server_cli/prototypes/base/docs/training/logic_bank_api.prompt +22 -0
  9. api_logic_server_cli/prototypes/base/docs/training/logic_bank_patterns.prompt +445 -0
  10. api_logic_server_cli/prototypes/base/docs/training/probabilistic_logic.prompt +1074 -0
  11. api_logic_server_cli/prototypes/base/docs/training/probabilistic_logic_guide.md +444 -0
  12. api_logic_server_cli/prototypes/base/docs/training/probabilistic_template.py +326 -0
  13. api_logic_server_cli/prototypes/base/logic/logic_discovery/auto_discovery.py +8 -9
  14. api_logic_server_cli/prototypes/basic_demo/.github/.copilot-instructions.md +326 -142
  15. api_logic_server_cli/prototypes/basic_demo/.github/welcome.md +15 -1
  16. api_logic_server_cli/prototypes/basic_demo/customizations/database/db.sqlite +0 -0
  17. api_logic_server_cli/prototypes/basic_demo/iteration/database/db.sqlite +0 -0
  18. api_logic_server_cli/prototypes/manager/.github/.copilot-instructions.md +61 -155
  19. api_logic_server_cli/prototypes/manager/.github/welcome.md +43 -0
  20. api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/.github/.copilot-instructions.md +502 -76
  21. {apilogicserver-15.4.3.dist-info → apilogicserver-16.0.0.dist-info}/METADATA +1 -1
  22. {apilogicserver-15.4.3.dist-info → apilogicserver-16.0.0.dist-info}/RECORD +26 -18
  23. {apilogicserver-15.4.3.dist-info → apilogicserver-16.0.0.dist-info}/WHEEL +0 -0
  24. {apilogicserver-15.4.3.dist-info → apilogicserver-16.0.0.dist-info}/entry_points.txt +0 -0
  25. {apilogicserver-15.4.3.dist-info → apilogicserver-16.0.0.dist-info}/licenses/LICENSE +0 -0
  26. {apilogicserver-15.4.3.dist-info → apilogicserver-16.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,445 @@
1
+ ---
2
+ title: LogicBank Patterns - The Hitchhiker's Guide
3
+ description: General patterns for working with LogicBank rules (deterministic and probabilistic)
4
+ source: Generic training for ApiLogicServer projects with GenAI integration
5
+ usage: AI assistants read this for general LogicBank patterns across ALL rule types
6
+ version: 1.0
7
+ date: Nov 14, 2025
8
+ related:
9
+ - logic_bank_api.prompt (deterministic rule API)
10
+ - logic_bank_api_probabilistic.prompt (AI/probabilistic rule API)
11
+ changelog:
12
+ - 1.0 (Nov 14, 2025): Extracted general patterns from probabilistic prompt for reuse
13
+ ---
14
+
15
+ # LogicBank Patterns - The Hitchhiker's Guide
16
+
17
+ This document contains general patterns for working with LogicBank rules.
18
+ These patterns apply to ALL rule types (deterministic and probabilistic).
19
+
20
+ For specific rule APIs, see:
21
+ - `docs/training/logic_bank_api.prompt` - Deterministic rules (sum, count, formula, constraint, etc.)
22
+ - `docs/training/logic_bank_api_probabilistic.prompt` - Probabilistic rules (AI value computation)
23
+
24
+ ---
25
+
26
+ =============================================================================
27
+ PATTERN 1: Event Handler Signature
28
+ =============================================================================
29
+
30
+ ALL event handlers (early_row_event, commit_row_event, row_event) receive THREE parameters.
31
+
32
+ ✅ REQUIRED SIGNATURE:
33
+ ```python
34
+ def my_handler(row: models.MyTable, old_row: models.MyTable, logic_row: LogicRow):
35
+ """
36
+ Event handler signature - ALL THREE PARAMETERS REQUIRED
37
+
38
+ Args:
39
+ row: Current state of the row (with changes)
40
+ old_row: Previous state before changes (for detecting what changed)
41
+ logic_row: LogicBank's wrapper with rule execution methods
42
+ """
43
+ logic_row.log(f"Processing {row.__class__.__name__}")
44
+ # Your logic here
45
+ ```
46
+
47
+ ❌ WRONG: Trying to "get" logic_row
48
+ ```python
49
+ def my_handler(row: models.MyTable):
50
+ logic_row = LogicRow.get_logic_row(row) # ❌ This method does NOT exist!
51
+ ```
52
+
53
+ ❌ WRONG: Missing parameters
54
+ ```python
55
+ def my_handler(row: models.MyTable, logic_row: LogicRow): # ❌ Missing old_row
56
+ pass
57
+ ```
58
+
59
+ REGISTRATION:
60
+ ```python
61
+ # Option 1: Direct registration (LogicBank passes all three params)
62
+ Rule.early_row_event(on_class=models.MyTable, calling=my_handler)
63
+
64
+ # Option 2: Lambda wrapper (if you need to pass additional args)
65
+ Rule.early_row_event(
66
+ on_class=models.MyTable,
67
+ calling=lambda row, old_row, logic_row: my_handler(row, old_row, logic_row, extra_arg)
68
+ )
69
+ ```
70
+
71
+ WHY THREE PARAMETERS:
72
+ - `row` - Access current values, make changes
73
+ - `old_row` - Detect what changed (if row.price != old_row.price)
74
+ - `logic_row` - Access LogicBank methods (.log(), .new_logic_row(), .insert(), etc.)
75
+
76
+ ---
77
+
78
+ =============================================================================
79
+ PATTERN 2: Logging with logic_row.log()
80
+ =============================================================================
81
+
82
+ ALWAYS use logic_row.log() for rule execution logging (not app_logger).
83
+
84
+ ✅ CORRECT: Use logic_row.log()
85
+ ```python
86
+ def my_handler(row: models.Item, old_row, logic_row: LogicRow):
87
+ logic_row.log(f"Processing Item - quantity={row.quantity}")
88
+
89
+ if row.product.count_suppliers > 0:
90
+ logic_row.log(f"Product has {row.product.count_suppliers} suppliers")
91
+ else:
92
+ logic_row.log("No suppliers available, using default price")
93
+ ```
94
+
95
+ ❌ WRONG: Using app_logger for rule logic
96
+ ```python
97
+ def my_handler(row: models.Item, old_row, logic_row: LogicRow):
98
+ app_logger.info(f"Item {row.id} - Product has suppliers") # ❌ Wrong!
99
+ ```
100
+
101
+ BENEFITS of logic_row.log():
102
+ - ✅ Automatic indentation showing rule cascade depth
103
+ - ✅ Grouped with related logic execution in trace output
104
+ - ✅ Visible in logic trace (helps debugging)
105
+ - ✅ No need to import logging module
106
+ - ✅ Shows execution context (which rule fired)
107
+
108
+ WHEN TO USE app_logger:
109
+ - System startup messages
110
+ - Configuration loading
111
+ - Errors outside rule execution
112
+ - Non-rule application logic
113
+
114
+ EXAMPLE OUTPUT:
115
+ ```
116
+ Logic Phase: ROW LOGIC (sqlalchemy before_flush) - 2025-11-14 06:19:03,372 - logic_logger - INF
117
+ ..Item[None] {Insert - client} Id: None, order_id: 1, product_id: 6, quantity: 10, unit_price: None, amount: None row: 0x107e4a950 session: 0x107e4a8d0 ins_upd_dlt: ins - 2025-11-14 06:19:03,373 - logic_logger - INF
118
+ ....Processing Item - quantity=10 - 2025-11-14 06:19:03,373 - logic_logger - INF
119
+ ....Product has 2 suppliers - 2025-11-14 06:19:03,374 - logic_logger - INF
120
+ ......Creating SysSupplierReq for AI selection - 2025-11-14 06:19:03,375 - logic_logger - INF
121
+ ```
122
+
123
+ Note the indentation (dots) showing call depth!
124
+
125
+ ---
126
+
127
+ =============================================================================
128
+ PATTERN 3: Request Pattern with new_logic_row()
129
+ =============================================================================
130
+
131
+ Use the Request Pattern for audit trails, workflows, and AI integration.
132
+
133
+ ✅ CORRECT: Pass MODEL CLASS to new_logic_row
134
+ ```python
135
+ def create_audit_trail(row: models.Order, old_row, logic_row: LogicRow):
136
+ """Create audit request object using Request Pattern"""
137
+
138
+ # Step 1: Create request object (pass CLASS not instance)
139
+ request_logic_row = logic_row.new_logic_row(models.OrderAuditReq)
140
+
141
+ # Step 2: Get the instance from .row property
142
+ request = request_logic_row.row
143
+
144
+ # Step 3: Set attributes on the instance
145
+ request.order_id = row.id
146
+ request.customer_id = row.customer_id
147
+ request.action = "order_created"
148
+ request.request_data = {"amount": float(row.amount_total)}
149
+
150
+ # Step 4: Insert using logic_row (triggers any events on request table)
151
+ request_logic_row.insert(reason="Order audit trail")
152
+
153
+ # Step 5: Access results if needed
154
+ logic_row.log(f"Audit created with ID {request.id}")
155
+ ```
156
+
157
+ ❌ WRONG: Creating instance first
158
+ ```python
159
+ def create_audit_trail(row: models.Order, old_row, logic_row: LogicRow):
160
+ # ❌ Don't create instance yourself
161
+ request = models.OrderAuditReq()
162
+
163
+ # ❌ This will fail with TypeError: object is not callable
164
+ request_logic_row = logic_row.new_logic_row(request)
165
+ ```
166
+
167
+ THE METHOD SIGNATURE:
168
+ ```python
169
+ logic_row.new_logic_row(a_class: type) -> LogicRow
170
+ ```
171
+
172
+ WHAT IT RETURNS:
173
+ - Returns a LogicRow wrapper (not the instance directly)
174
+ - Access instance via `.row` property
175
+ - Use returned logic_row for .insert(), .link(), etc.
176
+
177
+ WHY THIS PATTERN:
178
+ - LogicBank needs to track the new row in the session
179
+ - Enables rule execution on the new row
180
+ - Maintains parent-child relationships
181
+ - Supports cascading logic across related objects
182
+
183
+ COMMON USE CASES:
184
+ 1. **Audit trails** - Track who did what when
185
+ 2. **Workflows** - Create approval requests, notifications
186
+ 3. **AI integration** - Create request objects for AI to populate
187
+ 4. **Derived objects** - Generate summary records, reports
188
+
189
+ ---
190
+
191
+ =============================================================================
192
+ PATTERN 4: Rule API Syntax Reference
193
+ =============================================================================
194
+
195
+ Always consult docs/training/logic_bank_api.prompt for complete API details.
196
+
197
+ COMMON PARAMETERS BY RULE TYPE:
198
+
199
+ Rule.sum() - NO 'calling' parameter
200
+ Rule.sum(derive: Column, as_sum_of: any, where: Callable = None, insert_parent: bool = False)
201
+ ✅ Use 'where' for filtering
202
+ ❌ NO 'calling' parameter
203
+
204
+ Rule.count() - NO 'calling' parameter
205
+ Rule.count(derive: Column, as_count_of: type, where: Callable = None, insert_parent: bool = False)
206
+ ✅ Use 'where' for filtering
207
+ ❌ NO 'calling' parameter
208
+
209
+ Rule.formula() - HAS 'calling' parameter (for functions only)
210
+ Rule.formula(derive: Column, as_expression: Callable = None, calling: Callable = None, no_prune: bool = False)
211
+ ✅ Use 'as_expression' for simple expressions
212
+ ✅ Use 'calling' for complex functions (must be callable, not bool)
213
+ ❌ Never use calling=False or calling=True
214
+
215
+ Rule.constraint() - HAS 'calling' parameter (for functions only)
216
+ Rule.constraint(validate: type, as_condition: Callable = None, calling: Callable = None, error_msg: str = "")
217
+ ✅ Use 'as_condition' for simple lambda conditions
218
+ ✅ Use 'calling' for complex validation functions
219
+ ❌ Never use calling=False or calling=True
220
+
221
+ Rule.copy() - NO 'calling' parameter
222
+ Rule.copy(derive: Column, from_parent: any)
223
+
224
+ Rule.parent_check() - NO 'calling' parameter
225
+ Rule.parent_check(validate: type, error_msg: str = "")
226
+
227
+ EXAMPLES:
228
+
229
+ ✅ CORRECT: Rule.count with where
230
+ ```python
231
+ Rule.count(
232
+ derive=models.Customer.unshipped_order_count,
233
+ as_count_of=models.Order,
234
+ where=lambda row: row.date_shipped is None
235
+ )
236
+ ```
237
+
238
+ ❌ WRONG: Rule.count with calling
239
+ ```python
240
+ Rule.count(
241
+ derive=models.Customer.unshipped_order_count,
242
+ as_count_of=models.Order,
243
+ calling=lambda row: row.date_shipped is None # ❌ 'calling' not valid!
244
+ )
245
+ ```
246
+
247
+ ✅ CORRECT: Rule.formula with conditional
248
+ ```python
249
+ Rule.formula(
250
+ derive=models.Item.unit_price,
251
+ as_expression=lambda row: (
252
+ row.product.unit_price if row.product.count_suppliers == 0
253
+ else row.unit_price # Preserve value from event
254
+ )
255
+ )
256
+ ```
257
+
258
+ ❌ WRONG: Rule.formula with calling=False
259
+ ```python
260
+ Rule.formula(
261
+ derive=models.Item.unit_price,
262
+ calling=False # ❌ calling must be callable or omitted!
263
+ )
264
+ ```
265
+
266
+ ---
267
+
268
+ =============================================================================
269
+ PATTERN 5: Common Anti-Patterns (What NOT to Do)
270
+ =============================================================================
271
+
272
+ ❌ DON'T: Try to "get" logic_row
273
+ ```python
274
+ # This method does NOT exist
275
+ logic_row = LogicRow.get_logic_row(row)
276
+ ```
277
+
278
+ ❌ DON'T: Use app_logger in rule code
279
+ ```python
280
+ # Use logic_row.log() instead
281
+ app_logger.info("Processing item")
282
+ ```
283
+
284
+ ❌ DON'T: Create instances before new_logic_row
285
+ ```python
286
+ # Pass CLASS to new_logic_row, not instance
287
+ request = models.AuditReq()
288
+ logic_row.new_logic_row(request) # ❌ TypeError!
289
+ ```
290
+
291
+ ❌ DON'T: Use wrong parameters for rules
292
+ ```python
293
+ # Rule.count/sum/copy don't have 'calling'
294
+ Rule.count(derive=..., as_count_of=..., calling=...) # ❌ Invalid!
295
+ ```
296
+
297
+ ❌ DON'T: Use calling with boolean values
298
+ ```python
299
+ # calling must be a function, not bool
300
+ Rule.formula(derive=..., calling=False) # ❌ Invalid!
301
+ ```
302
+
303
+ ❌ DON'T: Forget to copy AI results to target
304
+ ```python
305
+ # AI populates request table, you must copy to target
306
+ request_logic_row.insert()
307
+ # ❌ Missing: row.unit_price = request.chosen_unit_price
308
+ ```
309
+
310
+ ❌ DON'T: Skip event handler parameters
311
+ ```python
312
+ # Event handlers need all THREE parameters
313
+ def my_handler(row): # ❌ Missing old_row and logic_row
314
+ pass
315
+ ```
316
+
317
+ ---
318
+
319
+ =============================================================================
320
+ PATTERN 6: Type Handling for Database Fields
321
+ =============================================================================
322
+
323
+ When setting values in event handlers or custom code, use correct Python types for database columns.
324
+
325
+ ✅ FOREIGN KEY (ID) FIELDS - Use int
326
+ ```python
327
+ def my_handler(row, old_row, logic_row: LogicRow):
328
+ # ✅ CORRECT: Foreign keys must be int for SQLite
329
+ row.customer_id = 123 # int
330
+ row.supplier_id = int(some_value) # Ensure it's int
331
+ ```
332
+
333
+ ❌ WRONG: Using Decimal for foreign keys
334
+ ```python
335
+ row.customer_id = Decimal('123') # ❌ SQLite error: Decimal not supported for INTEGER FK
336
+ ```
337
+
338
+ ✅ MONETARY FIELDS - Use Decimal for precision
339
+ ```python
340
+ from decimal import Decimal
341
+
342
+ def my_handler(row, old_row, logic_row: LogicRow):
343
+ # ✅ CORRECT: Monetary values as Decimal
344
+ row.unit_price = Decimal('19.99')
345
+ row.amount = Decimal(str(quantity * price)) # Convert via string for precision
346
+ ```
347
+
348
+ ✅ PATTERN SUMMARY:
349
+ ```python
350
+ # Foreign keys and IDs
351
+ if '_id' in field_name or field_name.endswith('_id'):
352
+ value = int(value) # Must be int for SQLite INTEGER columns
353
+
354
+ # Monetary fields
355
+ elif '_price' in field_name or '_cost' in field_name or '_amount' in field_name:
356
+ value = Decimal(str(value)) # Use Decimal for precision
357
+
358
+ # Other numerics
359
+ else:
360
+ value = float(value) or int(value) # Based on column type
361
+ ```
362
+
363
+ WHY THIS MATTERS:
364
+ - SQLite INTEGER columns (foreign keys) don't support Decimal type
365
+ - Monetary calculations need Decimal to avoid floating-point errors
366
+ - Type mismatches cause "type not supported" database errors
367
+
368
+ COMMON ERRORS:
369
+ ```python
370
+ # ❌ WRONG: Decimal for foreign key
371
+ row.order_id = Decimal('42') # Database error!
372
+
373
+ # ❌ WRONG: Float for money (precision loss)
374
+ row.unit_price = 19.99 # May lose precision in calculations
375
+
376
+ # ✅ CORRECT: Proper types
377
+ row.order_id = 42 # int for FK
378
+ row.unit_price = Decimal('19.99') # Decimal for money
379
+ ```
380
+
381
+ =============================================================================
382
+ PATTERN 7: Testing and Debugging Patterns
383
+ =============================================================================
384
+
385
+ ✅ USE logic_row.log() EXTENSIVELY during development
386
+ ```python
387
+ def my_handler(row, old_row, logic_row: LogicRow):
388
+ logic_row.log("=== Starting my_handler ===")
389
+ logic_row.log(f"Row state: quantity={row.quantity}, price={row.unit_price}")
390
+
391
+ if row.quantity != old_row.quantity:
392
+ logic_row.log(f"Quantity changed: {old_row.quantity} -> {row.quantity}")
393
+
394
+ # ... your logic
395
+
396
+ logic_row.log(f"=== Completed my_handler, result={row.amount} ===")
397
+ ```
398
+
399
+ ✅ CHECK old_row to detect changes
400
+ ```python
401
+ def update_handler(row, old_row, logic_row: LogicRow):
402
+ if row.status != old_row.status:
403
+ logic_row.log(f"Status changed: {old_row.status} -> {row.status}")
404
+ # Take action on status change
405
+ ```
406
+
407
+ ✅ USE logic_row.is_inserted(), is_updated(), is_deleted()
408
+ ```python
409
+ def audit_handler(row, old_row, logic_row: LogicRow):
410
+ if logic_row.is_inserted():
411
+ logic_row.log("New row created")
412
+ elif logic_row.is_updated():
413
+ logic_row.log("Row updated")
414
+ elif logic_row.is_deleted():
415
+ logic_row.log("Row deleted")
416
+ ```
417
+
418
+ ✅ TRACE rule execution with PYTHONPATH
419
+ ```bash
420
+ # Enable verbose logic logging
421
+ export APILOGICSERVER_VERBOSE=True
422
+ python api_logic_server_run.py
423
+ ```
424
+
425
+ ---
426
+
427
+ =============================================================================
428
+ SUMMARY: Quick Reference
429
+ =============================================================================
430
+
431
+ 1. **Event handlers**: def handler(row, old_row, logic_row) - ALL THREE
432
+ 2. **Logging**: Use logic_row.log() not app_logger
433
+ 3. **Request Pattern**: new_logic_row(ModelClass) returns LogicRow with .row
434
+ 4. **Rule APIs**: Check logic_bank_api.prompt for correct parameters
435
+ 5. **Anti-patterns**: No get_logic_row(), no calling=False, no app_logger in rules
436
+ 6. **Type handling**: int for FKs, Decimal for money
437
+ 7. **Testing**: logic_row.log(), check old_row, use is_inserted/updated/deleted
438
+
439
+ For rule-specific APIs and examples:
440
+ - Deterministic rules → docs/training/logic_bank_api.prompt
441
+ - Probabilistic rules → docs/training/logic_bank_api_probabilistic.prompt
442
+
443
+ ---
444
+
445
+ END OF GENERAL PATTERNS