ApiLogicServer 16.0.0__py3-none-any.whl → 16.0.2__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.
@@ -12,10 +12,10 @@ ApiLogicServer CLI: given a database url, create [and run] customizable ApiLogic
12
12
  Called from api_logic_server_cli.py, by instantiating the ProjectRun object.
13
13
  '''
14
14
 
15
- __version__ = "16.00.00" # last public release: 15.04.01
15
+ __version__ = "16.00.02" # last public release: 15.04.01
16
16
  recent_changes = \
17
17
  f'\n\nRecent Changes:\n' +\
18
- "\t11/21/2025 - 16.00.00: Probabalistic logic - see basic_demo/readme_ai_mcp.md \n"\
18
+ "\t11/22/2025 - 16.00.02: Probabalistic logic (w/ fallback, cand*) - see basic_demo/readme_ai_mcp.md \n"\
19
19
  "\t11/12/2025 - 15.04.03: Copilot rules on updates, tighter basic_demo startup, allow for demo_ai_mcp/then tutorial \n"\
20
20
  "\t11/08/2025 - 15.04.00: Nat lang Copilot data access, fix Manager/Copilot startup, finding venv in project \n"\
21
21
  "\t10/29/2025 - 15.03.00: Stable Tutor 3.3 (working, vibe transtion) \n"\
@@ -264,12 +264,13 @@ For developers: Business logic is **typically half the system** in data-centric
264
264
 
265
265
  With declarative rules, you simply state business requirements. The engine handles dependencies, ordering, and optimization automatically.
266
266
 
267
- LogicBank provides **44X code reduction** (5 lines vs 220+ procedural) with automatic:
267
+ LogicBank provides **44X code reduction** (5 lines vs 220+ procedural). **At scale, this means a typical 100-table enterprise system would require ~1,000 declarative rules vs. 40,000+ lines of procedural code.** More critically, those 40,000 lines would contain bugs in foreign key change handling that the rules engine prevents by construction.
268
+
269
+ The engine provides automatic:
268
270
  - **Dependency tracking** - listens to SQLAlchemy ORM events at attribute level
269
271
  - **Cascading updates** - when Order.customer_id changes, adjusts BOTH old and new Customer.balance
270
272
  - **Transitive dependencies** - Item.quantity change cascades through Item.amount → Order.amount_total → Customer.balance
271
273
  - **Optimized execution** - uses deltas, not re-aggregation; automatic pruning
272
-
273
274
  **Why the Rules Engine is a Correctness Guarantee:**
274
275
 
275
276
  The "2 critical bugs" that even AI-generated procedural code missed:
@@ -20,18 +20,19 @@ Use only the methods provided below.
20
20
 
21
21
  IMPORTANT: Keep it simple! Use the built-in Rule methods with their parameters (like if_condition) rather than creating custom functions. The Rule methods are designed to handle common patterns directly.
22
22
 
23
- CRITICAL: Follow the FORMATTING GUIDELINE below (lines 23-31) - keep simple rules on single line for scannability.
23
+ CRITICAL: Keep simple rules on ONE LINE (no exceptions). Goal: Visual scannability - see rule count at a glance.
24
24
 
25
- FORMATTING GUIDELINE: Keep rules compact and scannable.
26
- - Simple rules: Single line preferred, but max about 150.
27
- - Complex rules: Multi-line is fine, but keep parameters together
28
- - Goal: Visual scannability - see rule count at a glance
29
-
30
- Example:
25
+ CORRECT - One rule per line:
31
26
  Rule.sum(derive=Customer.balance, as_sum_of=Order.amount_total, where=lambda row: row.date_shipped is None)
32
27
  Rule.constraint(validate=Customer, as_condition=lambda row: row.balance <= row.credit_limit, error_msg="balance exceeds credit")
33
28
  Rule.formula(derive=Item.amount, as_expression=lambda row: row.quantity * row.unit_price)
34
29
 
30
+ ❌ WRONG - Don't split simple rules:
31
+ Rule.sum(
32
+ derive=Customer.balance,
33
+ as_sum_of=Order.amount_total
34
+ )
35
+
35
36
 
36
37
  class Rule:
37
38
  """ Invoke these functions to declare rules """
@@ -3,7 +3,7 @@ title: LogicBank Probabilistic Rules API (AI Value Computation)
3
3
  description: Training document for translating natural language into probabilistic value computation rules
4
4
  source: Generic training for ApiLogicServer projects with probabilistic rules
5
5
  usage: AI assistants read this to generate probabilistic + deterministic rules implementations
6
- version: 3.1 - proper initialization of ai_request data
6
+ version: 3.1
7
7
  date: November 21, 2025
8
8
  prerequisites:
9
9
  - docs/training/genai_logic_patterns.md (CRITICAL import patterns, auto-discovery)
@@ -399,9 +399,9 @@ def select_supplier_via_ai(row: models.SysSupplierReq, old_row, logic_row: Logic
399
399
  populating AI Results: chosen_supplier_id and chosen_unit_price.
400
400
 
401
401
  Strategy:
402
- 1. Check for test context first (for reproducible testing)
403
- 2. If no test context and API key exists, call OpenAI
404
- 3. If no API key, use fallback (min cost)
402
+ 1. Load test context for INPUT conditions (world conditions like "Suez Canal blocked")
403
+ 2. Always try AI with those conditions
404
+ 3. If no API key or API fails, use fallback (min cost)
405
405
  """
406
406
  if not logic_row.is_inserted():
407
407
  return
@@ -417,7 +417,7 @@ def select_supplier_via_ai(row: models.SysSupplierReq, old_row, logic_row: Logic
417
417
  row.fallback_used = True
418
418
  return
419
419
 
420
- # Check for test context first (BEFORE API key check)
420
+ # Load test context for world conditions (not for predetermined supplier selection)
421
421
  from pathlib import Path
422
422
  import yaml
423
423
 
@@ -425,25 +425,17 @@ def select_supplier_via_ai(row: models.SysSupplierReq, old_row, logic_row: Logic
425
425
  project_root = current_file.parent.parent.parent.parent
426
426
  context_file = project_root / 'config' / 'ai_test_context.yaml'
427
427
 
428
- selected_supplier = None
429
-
428
+ test_context = {}
430
429
  if context_file.exists():
431
430
  with open(str(context_file), 'r') as f:
432
- test_context = yaml.safe_load(f)
433
- if test_context and 'selected_supplier_id' in test_context:
434
- supplier_id = test_context['selected_supplier_id']
435
- selected_supplier = next((s for s in suppliers if s.supplier_id == supplier_id), None)
436
- if selected_supplier:
437
- # Build candidate summary for request field
438
- candidate_summary = ', '.join([f"{s.supplier.name if s.supplier else 'Unknown'}(${s.unit_cost})" for s in suppliers])
439
- world = test_context.get('world_conditions', 'normal conditions')
440
- row.request = f"Select supplier for {product.name}: Candidates=[{candidate_summary}], World={world}"
441
- row.reason = f"TEST MODE: Selected {selected_supplier.supplier.name if selected_supplier.supplier else 'supplier'} (${selected_supplier.unit_cost}) - world: {world}"
442
- logic_row.log(f"Using test context: supplier {supplier_id}")
443
- row.fallback_used = False
431
+ test_context = yaml.safe_load(f) or {}
432
+
433
+ world_conditions = test_context.get('world_conditions', 'normal conditions')
444
434
 
445
- # If no test context, try AI (check for API key)
446
- if not selected_supplier:
435
+ selected_supplier = None
436
+
437
+ # Try AI (check for API key)
438
+ if True: # Always try AI unless no key
447
439
  api_key = os.getenv("APILOGICSERVER_CHATGPT_APIKEY")
448
440
  if api_key:
449
441
  try:
@@ -453,18 +445,22 @@ def select_supplier_via_ai(row: models.SysSupplierReq, old_row, logic_row: Logic
453
445
 
454
446
  client = OpenAI(api_key=api_key)
455
447
 
456
- # Build candidate data for prompt
448
+ # Build candidate data for prompt - include ALL supplier fields for AI decision
457
449
  candidate_data = []
458
450
  for supplier in suppliers:
451
+ supplier_obj = supplier.supplier
459
452
  candidate_data.append({
460
453
  'supplier_id': supplier.supplier_id,
461
- 'supplier_name': supplier.supplier.name if supplier.supplier else 'Unknown',
454
+ 'supplier_name': supplier_obj.name if supplier_obj else 'Unknown',
455
+ 'supplier_region': supplier_obj.region if supplier_obj else None,
456
+ 'supplier_contact': supplier_obj.contact_name if supplier_obj else None,
457
+ 'supplier_phone': supplier_obj.phone if supplier_obj else None,
458
+ 'supplier_email': supplier_obj.email if supplier_obj else None,
462
459
  'unit_cost': float(supplier.unit_cost) if supplier.unit_cost else 0.0,
463
- 'lead_time_days': supplier.lead_time_days if hasattr(supplier, 'lead_time_days') else None
460
+ 'lead_time_days': supplier.lead_time_days if hasattr(supplier, 'lead_time_days') else None,
461
+ 'supplier_part_number': supplier.supplier_part_number if hasattr(supplier, 'supplier_part_number') else None
464
462
  })
465
463
 
466
- world_conditions = test_context.get('world_conditions', 'normal conditions') if 'test_context' in locals() else 'normal conditions'
467
-
468
464
  prompt = f"""
469
465
  You are a supply chain optimization expert. Select the best supplier from the candidates below.
470
466
 
@@ -483,11 +479,14 @@ Respond with ONLY valid JSON in this exact format (no markdown, no code blocks):
483
479
  }}
484
480
  """
485
481
 
486
- # Populate request field with actual prompt summary
487
- candidate_list = ', '.join([c['supplier_name'] + '($' + str(c['unit_cost']) + ')' for c in candidate_data])
488
- row.request = f"AI Prompt: Product={product.name}, World={world_conditions}, Candidates={len(candidate_data)}: {candidate_list}"
482
+ # Populate request field with actual prompt summary including key fields
483
+ candidate_summary = ', '.join([
484
+ f"{c['supplier_name']}(${c['unit_cost']}, {c['supplier_region'] or 'unknown region'}, {c['lead_time_days'] or '?'}days)"
485
+ for c in candidate_data
486
+ ])
487
+ row.request = f"Select supplier for {product.name}: Candidates=[{candidate_summary}], World={world_conditions}"
489
488
 
490
- logic_row.log(f"Calling OpenAI API with {len(candidate_data)} candidates")
489
+ logic_row.log(f"Calling OpenAI API with {len(candidate_data)} candidates, world conditions: {world_conditions}")
491
490
 
492
491
  response = client.chat.completions.create(
493
492
  model="gpt-4o-2024-08-06",
@@ -508,7 +507,7 @@ Respond with ONLY valid JSON in this exact format (no markdown, no code blocks):
508
507
  selected_supplier = next((s for s in suppliers if s.supplier_id == ai_result['chosen_supplier_id']), None)
509
508
  if selected_supplier:
510
509
  supplier_name = selected_supplier.supplier.name if selected_supplier.supplier else 'Unknown'
511
- row.reason = f"AI: {supplier_name} (${selected_supplier.unit_cost}) - {ai_result.get('reason', 'No reason provided')}"
510
+ row.reason = f"Selected {supplier_name} (${selected_supplier.unit_cost}) - {ai_result.get('reason', 'No reason provided')}"
512
511
  row.fallback_used = False
513
512
  else:
514
513
  logic_row.log(f"AI selected invalid supplier_id {ai_result['chosen_supplier_id']}, using fallback")
@@ -582,15 +581,21 @@ def get_supplier_selection_from_ai(product_id: int, item_id: int, logic_row: Log
582
581
 
583
582
  ### Key Implementation Points
584
583
 
585
- **Test Context Priority:**
586
- - ALWAYS check test context BEFORE API key
587
- - Enables reproducible testing without API calls
584
+ **Test Context Usage:**
585
+ - Load test context for INPUT conditions (world_conditions like "Suez Canal blocked")
586
+ - Test context provides CONDITIONS for AI, NOT predetermined outputs
588
587
  - File: `config/ai_test_context.yaml`
588
+ - Example: `world_conditions: "Suez Canal blocked, use alternate shipping routes"`
589
+
590
+ **AI Strategy:**
591
+ - Always try AI if API key exists
592
+ - Pass world_conditions from test context to AI prompt
593
+ - AI makes decision based on those conditions
589
594
 
590
595
  **Fallback Strategy:**
591
596
  - When no suppliers: Set `fallback_used = True`, return early
592
- - When no test context and no API key: Use min cost
593
- - When no test context but have API key: Call API (or fallback for demo)
597
+ - When no API key: Use min cost fallback
598
+ - When API call fails: Use min cost fallback
594
599
 
595
600
  **Type Handling:**
596
601
  - Foreign keys (IDs): Must be `int` not `Decimal`
@@ -789,8 +794,10 @@ class Product(Base): # Has FK from SysSupplierReq.product_id
789
794
  class Item(Base): # Has FK from SysSupplierReq.item_id
790
795
  SysSupplierReqList : Mapped[List["SysSupplierReq"]] = relationship(back_populates="item")
791
796
 
792
- class Supplier(Base): # Has FK from SysSupplierReq.chosen_supplier_id
793
- SysSupplierReqList : Mapped[List["SysSupplierReq"]] = relationship(foreign_keys="SysSupplierReq.chosen_supplier_id")
797
+ # DO NOT add relationship to Supplier for chosen_supplier_id
798
+ # - This is an AI result field (not standard parent-child relationship)
799
+ # - Access via SysSupplierReq.chosen_supplier (unidirectional) is sufficient
800
+ # - Adding reverse relationship causes NoForeignKeysError
794
801
 
795
802
  # ✅ DO NOT add relationship to ProductSupplier (no FK exists)
796
803
  ```
@@ -154,11 +154,15 @@ def supplier_id_from_ai(row: models.SysSupplierReq, old_row, logic_row):
154
154
  if not has_api_key():
155
155
  min_supplier = min(suppliers, key=lambda s: s.unit_cost)
156
156
  else:
157
- # Call AI service
157
+ # Load test context for INPUT conditions (world_conditions)
158
+ test_context = load_test_context()
159
+ world_conditions = test_context.get('world_conditions', 'normal conditions')
160
+
161
+ # Call AI service with world conditions
158
162
  result = call_ai_service(
159
163
  candidates=suppliers,
160
164
  optimize_for='fastest reliable delivery',
161
- context=load_test_context()
165
+ world_conditions=world_conditions # Test context provides conditions, not outputs
162
166
  )
163
167
  min_supplier = result.chosen_supplier
164
168
 
@@ -281,14 +285,15 @@ def test_ai_handler_with_mock(mock_ai):
281
285
  'reason': 'Lowest cost'
282
286
  }
283
287
 
284
- # Create test context
288
+ # Create test context with INPUT conditions (not predetermined outputs)
289
+ # Example: test_context = {'world_conditions': 'Suez Canal blocked'}
285
290
  row = create_test_item()
286
291
  logic_row = create_test_logic_row(row)
287
292
 
288
- # Call handler
293
+ # Call handler (AI will make decision based on world conditions)
289
294
  result = get_supplier_price_from_ai(row, logic_row, ...)
290
295
 
291
- # Verify
296
+ # Verify AI was called with proper conditions
292
297
  assert result == 95.00
293
298
  assert mock_ai.called
294
299
  ```
@@ -421,6 +426,39 @@ audit_logic_row.insert(reason="AI")
421
426
 
422
427
  ---
423
428
 
429
+ ## Test Context: Input Conditions vs Output Mocking
430
+
431
+ **CRITICAL DISTINCTION:** Test context provides INPUT CONDITIONS for AI, NOT predetermined outputs.
432
+
433
+ **Purpose:**
434
+ - Test how AI responds to different scenarios (e.g., "Suez Canal blocked")
435
+ - Verify AI considers world conditions in its decision-making
436
+ - Enable repeatable testing with varying conditions
437
+
438
+ **Example Test Context (config/ai_test_context.yaml):**
439
+ ```yaml
440
+ # ✅ CORRECT - Provides input conditions
441
+ world_conditions: "Suez Canal blocked, alternate routes required"
442
+
443
+ # ❌ WRONG - Predetermines outputs (defeats AI testing)
444
+ # selected_supplier_id: 2 # Don't do this!
445
+ ```
446
+
447
+ **How It Works:**
448
+ 1. Load test context for `world_conditions`
449
+ 2. Pass conditions to AI prompt
450
+ 3. AI makes decision based on those conditions
451
+ 4. Verify AI selected appropriate supplier given the conditions
452
+
453
+ **Testing Strategy:**
454
+ - **Normal conditions:** AI should optimize for cost
455
+ - **Disrupted conditions:** AI should prioritize reliability/alternate routes
456
+ - **No API key:** System uses fallback (min cost)
457
+
458
+ **Key Insight:** Test context lets you verify AI adapts to different scenarios WITHOUT mocking the AI itself.
459
+
460
+ ---
461
+
424
462
  ## Summary
425
463
 
426
464
  **Probabilistic Logic Pattern:**
@@ -433,6 +471,7 @@ audit_logic_row.insert(reason="AI")
433
471
  **Key Benefits:**
434
472
  - Seamless integration with deterministic rules
435
473
  - Full audit trail of AI decisions
474
+ - Test context for scenario-based testing
436
475
  - Graceful fallback when AI unavailable
437
476
  - Testable at multiple levels
438
477
  - Reusable AI handlers across use cases
@@ -5,7 +5,7 @@ This template provides a clean reference implementation for AI value computation
5
5
  alongside deterministic rules, using the Request Pattern with early events.
6
6
 
7
7
  Pattern: Early event with wrapper function that returns populated request object
8
- version: 3.1 - proper initialization of ai_request data
8
+ version: 3.0
9
9
  date: November 21, 2025
10
10
  source: docs/training/probabilistic_template.py
11
11
 
@@ -136,31 +136,24 @@ def select_supplier_via_ai(row: models.SysSupplierReq, old_row, logic_row: Logic
136
136
  row.fallback_used = True
137
137
  return
138
138
 
139
- # Check for test context first (BEFORE API key check)
139
+ # Load test context for world conditions (not for predetermined supplier selection)
140
140
  from pathlib import Path
141
141
  import yaml
142
142
 
143
143
  config_dir = Path(__file__).resolve().parent.parent.parent.parent / 'config'
144
144
  context_file = config_dir / 'ai_test_context.yaml'
145
145
 
146
- selected_supplier = None
147
-
146
+ test_context = {}
148
147
  if context_file.exists():
149
148
  with open(str(context_file), 'r') as f:
150
- test_context = yaml.safe_load(f)
151
- if test_context and 'selected_supplier_id' in test_context:
152
- supplier_id = test_context['selected_supplier_id']
153
- selected_supplier = next((s for s in suppliers if s.supplier_id == supplier_id), None)
154
- if selected_supplier:
155
- candidate_summary = ', '.join([f"{s.supplier.name if s.supplier else 'Unknown'}(${s.unit_cost})" for s in suppliers])
156
- world = test_context.get('world_conditions', 'normal conditions')
157
- row.request = f"Select supplier for {product.name}: Candidates=[{candidate_summary}], World={world}"
158
- row.reason = f"TEST MODE: Selected {selected_supplier.supplier.name if selected_supplier.supplier else 'supplier'} (${selected_supplier.unit_cost}) - world: {world}"
159
- logic_row.log(f"Using test context: supplier {supplier_id}")
160
- row.fallback_used = False
149
+ test_context = yaml.safe_load(f) or {}
161
150
 
162
- # If no test context, try AI (check for API key)
163
- if not selected_supplier:
151
+ world_conditions = test_context.get('world_conditions', 'normal conditions')
152
+
153
+ selected_supplier = None
154
+
155
+ # Try AI (check for API key)
156
+ if True: # Always try AI unless no key
164
157
  api_key = os.getenv("APILOGICSERVER_CHATGPT_APIKEY")
165
158
  if api_key:
166
159
  try:
@@ -170,18 +163,22 @@ def select_supplier_via_ai(row: models.SysSupplierReq, old_row, logic_row: Logic
170
163
 
171
164
  client = OpenAI(api_key=api_key)
172
165
 
173
- # Build candidate data for prompt
166
+ # Build candidate data for prompt - include ALL supplier fields for AI decision
174
167
  candidate_data = []
175
168
  for supplier in suppliers:
169
+ supplier_obj = supplier.supplier
176
170
  candidate_data.append({
177
171
  'supplier_id': supplier.supplier_id,
178
- 'supplier_name': supplier.supplier.name if supplier.supplier else 'Unknown',
172
+ 'supplier_name': supplier_obj.name if supplier_obj else 'Unknown',
173
+ 'supplier_region': supplier_obj.region if supplier_obj else None,
174
+ 'supplier_contact': supplier_obj.contact_name if supplier_obj else None,
175
+ 'supplier_phone': supplier_obj.phone if supplier_obj else None,
176
+ 'supplier_email': supplier_obj.email if supplier_obj else None,
179
177
  'unit_cost': float(supplier.unit_cost) if supplier.unit_cost else 0.0,
180
- 'lead_time_days': supplier.lead_time_days if hasattr(supplier, 'lead_time_days') else None
178
+ 'lead_time_days': supplier.lead_time_days if hasattr(supplier, 'lead_time_days') else None,
179
+ 'supplier_part_number': supplier.supplier_part_number if hasattr(supplier, 'supplier_part_number') else None
181
180
  })
182
181
 
183
- world_conditions = test_context.get('world_conditions', 'normal conditions') if 'test_context' in locals() else 'normal conditions'
184
-
185
182
  prompt = f"""
186
183
  You are a supply chain optimization expert. Select the best supplier from the candidates below.
187
184
 
@@ -200,11 +197,14 @@ Respond with ONLY valid JSON in this exact format (no markdown, no code blocks):
200
197
  }}
201
198
  """
202
199
 
203
- # Populate request field with actual prompt summary
204
- candidate_list = ', '.join([c['supplier_name'] + '($' + str(c['unit_cost']) + ')' for c in candidate_data])
205
- row.request = f"AI Prompt: Product={product.name}, World={world_conditions}, Candidates={len(candidate_data)}: {candidate_list}"
200
+ # Populate request field with actual prompt summary including key fields
201
+ candidate_summary = ', '.join([
202
+ f"{c['supplier_name']}(${c['unit_cost']}, {c['supplier_region'] or 'unknown region'}, {c['lead_time_days'] or '?'}days)"
203
+ for c in candidate_data
204
+ ])
205
+ row.request = f"Select supplier for {product.name}: Candidates=[{candidate_summary}], World={world_conditions}"
206
206
 
207
- logic_row.log(f"Calling OpenAI API with {len(candidate_data)} candidates")
207
+ logic_row.log(f"Calling OpenAI API with {len(candidate_data)} candidates, world conditions: {world_conditions}")
208
208
 
209
209
  response = client.chat.completions.create(
210
210
  model="gpt-4o-2024-08-06",
@@ -225,7 +225,7 @@ Respond with ONLY valid JSON in this exact format (no markdown, no code blocks):
225
225
  selected_supplier = next((s for s in suppliers if s.supplier_id == ai_result['chosen_supplier_id']), None)
226
226
  if selected_supplier:
227
227
  supplier_name = selected_supplier.supplier.name if selected_supplier.supplier else 'Unknown'
228
- row.reason = f"AI: {supplier_name} (${selected_supplier.unit_cost}) - {ai_result.get('reason', 'No reason provided')}"
228
+ row.reason = f"Selected {supplier_name} (${selected_supplier.unit_cost}) - {ai_result.get('reason', 'No reason provided')}"
229
229
  row.fallback_used = False
230
230
  else:
231
231
  logic_row.log(f"AI selected invalid supplier_id {ai_result['chosen_supplier_id']}, using fallback")
@@ -312,7 +312,9 @@ For developers: Business logic is **typically half the system** in data-centric
312
312
 
313
313
  With declarative rules, you simply state business requirements. The engine handles dependencies, ordering, and optimization automatically.
314
314
 
315
- LogicBank provides **44X code reduction** (5 lines vs 220+ procedural) with automatic:
315
+ LogicBank provides **44X code reduction** (5 lines vs 220+ procedural). **At scale, this means a typical 100-table enterprise system would require ~1,000 declarative rules vs. 40,000+ lines of procedural code.** More critically, those 40,000 lines would contain bugs in foreign key change handling that the rules engine prevents by construction.
316
+
317
+ The engine provides automatic:
316
318
  - **Dependency tracking** - listens to SQLAlchemy ORM events at attribute level
317
319
  - **Cascading updates** - when Order.customer_id changes, adjusts BOTH old and new Customer.balance
318
320
  - **Transitive dependencies** - Item.quantity change cascades through Item.amount → Order.amount_total → Customer.balance
@@ -3,25 +3,25 @@ use: welcome for basic_demo - please start the tour
3
3
  instuctions: copy api_logic_server_cli/prototypes/base/.github/.copilot-instructions.md, then paste as the Welcome section
4
4
  ---
5
5
 
6
- ## Welcome
7
-
8
- **Welcome! This is your basic_demo project.**
6
+ ## Welcome to the `basic_demo` project
9
7
 
10
8
  This is a complete, working microservice auto-generated from a database schema - **uncustomized**, so you can see what to expect when you create projects from your own databases.
11
9
 
12
- **What's already created from introspecting the database schema:**
10
+ Created from database schema introspection:
13
11
  - **Admin Web Application** - Live at `http://localhost:5656`
14
- - **REST API Endpoints** - All database tables exposed at `/api/*`
12
+ - **REST API Endpoints** - All database tables exposed at MCP discoverable `/api/*`
15
13
  - **Database Models** - Complete SQLAlchemy ORM in `database/models.py`
16
14
  - **Authentication System** - JWT-based auth framework
17
15
  - **Business Logic Engine** - Declarative rules system
18
16
 
19
17
  ---
20
18
 
21
- **First, we'll briefly explore what's automatically created. Then I'll show you how I can help you customize it - adding logic, security, and API endpoints.**
19
+ **Ready to explore?** Just say me "guide me through" to begin the interactive tutorial
22
20
 
21
+ * Brief exploration of what's already created
22
+ * Customizing logic, security, and the API
23
23
 
24
- **Ready to explore?** Just say me "guide me through" to begin the interactive tutorial.
24
+ ---
25
25
 
26
26
  **Or, self-demo?** Open `readme_ai_mcp.md` to explore logic, ai and mcp.
27
27
 
@@ -20,5 +20,6 @@
20
20
  "workbench.editorAssociations": {
21
21
  "*.md": "vscode.markdown.preview.editor"
22
22
  },
23
+ "workbench.startupEditor": "readme",
23
24
  "workbench.colorTheme": "Arduino Light"
24
25
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ApiLogicServer
3
- Version: 16.0.0
3
+ Version: 16.0.2
4
4
  Author-email: Val Huber <apilogicserver@gmail.com>
5
5
  License-Expression: BSD-3-Clause
6
6
  Project-URL: Homepage, https://www.genai-logic.com
@@ -1,5 +1,5 @@
1
1
  api_logic_server_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- api_logic_server_cli/api_logic_server.py,sha256=WOTLdg7MkjeoeRvRyE1rfH8OSj_gTl5xMJODBx4qxDE,105152
2
+ api_logic_server_cli/api_logic_server.py,sha256=1f9EE8Q7qgEXx1rsGggnC_5rfOfIijIhHuS42jHPONM,105173
3
3
  api_logic_server_cli/api_logic_server_info.yaml,sha256=wzqFVXFXtbCPMFWmDG5k_cP4efr7YAhyhWIlYdpKGRc,132
4
4
  api_logic_server_cli/cli.py,sha256=xAqTOhq-OnXU2HEQgzsGC9yKrGcAgKUt_8b9U2bV5No,87831
5
5
  api_logic_server_cli/cli_args_base.py,sha256=7cVM6BeizwttYAwUu1FUyuLuvWufvgt0TFeA8FI6tu0,3304
@@ -423,7 +423,7 @@ api_logic_server_cli/prototypes/base/.devcontainer-option/For_VSCode.dockerfile,
423
423
  api_logic_server_cli/prototypes/base/.devcontainer-option/devcontainer.json,sha256=tk-mGd4XdmbpKUqUeGmcPMzX3RDc6am9-de8c-rFmSo,2361
424
424
  api_logic_server_cli/prototypes/base/.devcontainer-option/readme.md,sha256=-sSneMDne1fqEoox2hXUGmoO8ewgi34y7lJwGTidSpY,104
425
425
  api_logic_server_cli/prototypes/base/.devcontainer-option/setup.sh,sha256=pOvGjZ7jgRQzFkD93mNICmcC2y66Dexrq4bCnSSVwtU,310
426
- api_logic_server_cli/prototypes/base/.github/.copilot-instructions.md,sha256=-nizGXBHLOBFbbaBF8UpsFVA_Mv2_b4bVhMrAvWvxxs,50003
426
+ api_logic_server_cli/prototypes/base/.github/.copilot-instructions.md,sha256=3toZhOMxjmjr5jvGfSpqDU4kQGRMWAAg1UQIU0SZCvA,50293
427
427
  api_logic_server_cli/prototypes/base/.idea/runConfigurations/ApiLogicServer.xml,sha256=eFzhe9NH-VNjcPWbPsRQy5o-MugJR9IWklA1Fo8wtYg,1127
428
428
  api_logic_server_cli/prototypes/base/.idea/runConfigurations/Report_Behave_Logic.xml,sha256=I3jlEf-TPzc-1NY843v6AcQIQ8QJD3z9KvxTYSZWMtY,1306
429
429
  api_logic_server_cli/prototypes/base/.idea/runConfigurations/Run_Behave.xml,sha256=CTzF0P4w7o4FzOi-eSpru0HczSEGtJsKqkQ7VWRyxPc,1196
@@ -533,12 +533,12 @@ api_logic_server_cli/prototypes/base/docs/training/admin_app_1_context.prompt.md
533
533
  api_logic_server_cli/prototypes/base/docs/training/admin_app_2_functionality.prompt.md,sha256=iWMoAkETeQjWWPrNj1AcI4HFGLlgS0-HP9oBYXhdTNI,1705
534
534
  api_logic_server_cli/prototypes/base/docs/training/admin_app_3_architecture.prompt.md,sha256=5KtRBihPbxTQEvLJ51w104Z0HfDGFEmQc3ysek6EsXA,925
535
535
  api_logic_server_cli/prototypes/base/docs/training/genai_logic_patterns.md,sha256=STjzKjziXvsWd7hVLn3zBQLVPJi5II25IDCI8-IMYcs,14666
536
- api_logic_server_cli/prototypes/base/docs/training/logic_bank_api.prompt,sha256=flQB6tG59M6jXAxCBpLyR8osUbe9RlX5QyoAWAvIJWA,17106
536
+ api_logic_server_cli/prototypes/base/docs/training/logic_bank_api.prompt,sha256=acTiWoigb7BiajAHskqgh22wLy9fsW7WdAwynAe5ji0,17019
537
537
  api_logic_server_cli/prototypes/base/docs/training/logic_bank_patterns.prompt,sha256=qNdA0efdlZECT6DiL8o1Iw-pPPDa1KVRrtKV84x8KJA,14850
538
538
  api_logic_server_cli/prototypes/base/docs/training/logic_example.py,sha256=yAot6uHy1gu94ufeYDx3f0iJ1_czsDywSAdm_yu4E2o,1325
539
- api_logic_server_cli/prototypes/base/docs/training/probabilistic_logic.prompt,sha256=VKYV1ruTPAsK3AN4HgbFcQmuhTszYnpcK0trfFl9NQ4,42487
540
- api_logic_server_cli/prototypes/base/docs/training/probabilistic_logic_guide.md,sha256=rUgsWeRDUhIxMVTMgHBJizl9nCKXiMjPnKJyeCw6SXU,14922
541
- api_logic_server_cli/prototypes/base/docs/training/probabilistic_template.py,sha256=W3ei_nlmfwUhQirq-L_M75BNnckyQEFwpKwNjQjtnRE,14450
539
+ api_logic_server_cli/prototypes/base/docs/training/probabilistic_logic.prompt,sha256=xu06ynvBbp-KkCEqMI5h_Gn4k95dZIDUdQfI6gjRFR8,42549
540
+ api_logic_server_cli/prototypes/base/docs/training/probabilistic_logic_guide.md,sha256=ZPV7kWjo2S6lM2M_1aesh9WtFD0ME2tyr3HUCA4OEsE,16618
541
+ api_logic_server_cli/prototypes/base/docs/training/probabilistic_template.py,sha256=V9zldqIpcs2CR2ysy6ZoVab7FqzeSkb9b60aanGt97c,14204
542
542
  api_logic_server_cli/prototypes/base/docs/training/react_map.prompt.md,sha256=8B9bDjk4sL1t5LzYLKjuf78UPDmhj9assRZTIOvlwN4,891
543
543
  api_logic_server_cli/prototypes/base/docs/training/react_tree.prompt.md,sha256=Eoi4Q3H4x-PQOjonUjQ1o6xkiFtcEA_hg8tuFP-qk80,652
544
544
  api_logic_server_cli/prototypes/base/docs/training/readme_training.md,sha256=EilDTYaDPoGCD9ef5Efs8eDn8ZNQTgGN7_QnJ4LrO2s,120
@@ -628,8 +628,8 @@ api_logic_server_cli/prototypes/base/venv_setup/venv.sh,sha256=aWX9fa8fe6aO9ifBI
628
628
  api_logic_server_cli/prototypes/basic_demo/_config.yml,sha256=KIUQQpjgj7hP_Z2Fksq90E52UnbKnyom-v9L_eIfqZo,170
629
629
  api_logic_server_cli/prototypes/basic_demo/readme.md,sha256=Ii0WojbHMHpg6bgMlg9WyadzXVZePM2Nk89kmKHuGTM,18722
630
630
  api_logic_server_cli/prototypes/basic_demo/tutor.md,sha256=nlIkqcqkVXBDe3Rktcr4puZxrrTFDl1s_Id8nA5GwA4,45516
631
- api_logic_server_cli/prototypes/basic_demo/.github/.copilot-instructions.md,sha256=g8J1YNXgr8tFe9F-g-J9SArnUHsMC7q2cacgPMVKlXY,50943
632
- api_logic_server_cli/prototypes/basic_demo/.github/welcome.md,sha256=s8Ea7BD-3DZvPSgjPT_IkHP8XRzZfMfAhHhgIZpNFgw,1645
631
+ api_logic_server_cli/prototypes/basic_demo/.github/.copilot-instructions.md,sha256=F87jmsFW7M88z8Dwcy418p3SFfbU9xbmEuzqu3Wwso0,51234
632
+ api_logic_server_cli/prototypes/basic_demo/.github/welcome.md,sha256=c-lwKq6JDiLwMzqoRnn3bPX0GTYop9lt4mBAkfYbq6w,1553
633
633
  api_logic_server_cli/prototypes/basic_demo/_layouts/redirect.html,sha256=-0kMPGYI88fb787IzYmdi7ySZUhgpUlP0vodrg8-NRM,457
634
634
  api_logic_server_cli/prototypes/basic_demo/customizations/api/api_discovery/openapi.py,sha256=kLQ7Fn1J7tzuNJHBXF2AiwtzvQ-0JxJ6z-MfFryAtLk,3887
635
635
  api_logic_server_cli/prototypes/basic_demo/customizations/config/default.env,sha256=-rjXJrjR4vjMr9YCVYVchaJw7qMBlbvQ3KfR_wri_XM,412
@@ -816,7 +816,7 @@ api_logic_server_cli/prototypes/manager/.github/.copilot-instructions.md,sha256=
816
816
  api_logic_server_cli/prototypes/manager/.github/sync-to-dev-src.sh,sha256=hZbc5fJXnYiLWvN-OcQzYRsPBESmt6B9y581CczuwHM,1915
817
817
  api_logic_server_cli/prototypes/manager/.github/welcome.md,sha256=9Wtufrx0wRf_5jgne8K9T6Sntm2yhyR3bnFKL_ch_0M,1841
818
818
  api_logic_server_cli/prototypes/manager/.vscode/launch.json,sha256=B9NaDoTvJsXg1qeEuhG3RdRk4M2RpnpHBW6b8oXrBn4,33551
819
- api_logic_server_cli/prototypes/manager/.vscode/settings.json,sha256=Zj9oPObeI-KVh1l4b3uuF1vkezKomVqnhP241h6ZB-M,619
819
+ api_logic_server_cli/prototypes/manager/.vscode/settings.json,sha256=Ceq5Z34iC6hXNoPLahFHKAn39wjdh2VZqshkWAOXu9g,660
820
820
  api_logic_server_cli/prototypes/manager/samples/readme_samples.md,sha256=qSX2SpJnqK9PTu1quEw3GHJvQLNfCaJ-Fr3ljn6_MpE,3632
821
821
  api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/.env,sha256=VCYAc9rxtOuDpv4Og6QwVV8bhzipEGu4sf--kI4Lq5k,355
822
822
  api_logic_server_cli/prototypes/manager/samples/basic_demo_sample/.gitignore,sha256=PAO98cVvjgAL_mvXCMS_Vfk7bT2Vd1-j9a8_nB2qxqs,190
@@ -2872,9 +2872,9 @@ api_logic_server_cli/tools/mini_skel/database/system/SAFRSBaseX.py,sha256=p8C7AF
2872
2872
  api_logic_server_cli/tools/mini_skel/database/system/TestDataBase.py,sha256=U02SYqThsbY5g3DX7XGaiMxjZBuOpzvtPS6RfI1WQFg,371
2873
2873
  api_logic_server_cli/tools/mini_skel/logic/declare_logic.py,sha256=fTrlHyqMeZsw_TyEXFa1VlYBL7fzjZab5ONSXO7aApo,175
2874
2874
  api_logic_server_cli/tools/mini_skel/logic/load_verify_rules.py,sha256=Rr5bySJpYCZmNPF2h-phcPJ53nAOPcT_ohZpCD93-a0,7530
2875
- apilogicserver-16.0.0.dist-info/licenses/LICENSE,sha256=67BS7VC-Z8GpaR3wijngQJkHWV04qJrwQArVgn9ldoI,1485
2876
- apilogicserver-16.0.0.dist-info/METADATA,sha256=D77WBWxLWETNAd7ZKYtS3dIF1Bk9I3FZy8LGo--TIKs,26461
2877
- apilogicserver-16.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
2878
- apilogicserver-16.0.0.dist-info/entry_points.txt,sha256=W9EVNvf09h8n6rJChmVj2gzxVQ6BXXZa2x3wri0lFGc,259
2879
- apilogicserver-16.0.0.dist-info/top_level.txt,sha256=-r0AT_GEApleihg-jIh0OMvzzc0BO1RuhhOpE91H5qI,21
2880
- apilogicserver-16.0.0.dist-info/RECORD,,
2875
+ apilogicserver-16.0.2.dist-info/licenses/LICENSE,sha256=67BS7VC-Z8GpaR3wijngQJkHWV04qJrwQArVgn9ldoI,1485
2876
+ apilogicserver-16.0.2.dist-info/METADATA,sha256=Tir3NY5PD7F8wydkj02FC3bT9opFGavrtba7-RfcEfg,26461
2877
+ apilogicserver-16.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
2878
+ apilogicserver-16.0.2.dist-info/entry_points.txt,sha256=W9EVNvf09h8n6rJChmVj2gzxVQ6BXXZa2x3wri0lFGc,259
2879
+ apilogicserver-16.0.2.dist-info/top_level.txt,sha256=-r0AT_GEApleihg-jIh0OMvzzc0BO1RuhhOpE91H5qI,21
2880
+ apilogicserver-16.0.2.dist-info/RECORD,,