ApiLogicServer 16.0.0__py3-none-any.whl → 16.0.3__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.03" # 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/25/2025 - 16.00.03: 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"\
@@ -1,66 +1,133 @@
1
- ---
2
- title: Copilot Instructions for GenAI-Logic Projects
3
- Description: Project-level instructions for working with generated projects
4
- Source: ApiLogicServer-src/prototypes/base/.github/.copilot-instructions.md
5
- Propagation: CLI create command → created projects (non-basic_demo)
6
- Instrucions: Changes must be merged from api_logic_server_cli/prototypes/basic_demo/.github - see instructions there
7
- Usage: AI assistants read this when user opens any created project
8
- version: 3.0
9
- changelog:
10
- - 3.0 (Nov 17, 2025) - Major streamlining: removed duplicate sections, consolidated MCP content, simplified workflows
11
- - 2.9 (Nov 17, 2025) - MANDATORY training file reading workflow (STOP command)
12
- - 2.8 (Nov 16, 2025) - Probabilistic Logic support
1
+ <!--
2
+ ═══════════════════════════════════════════════════════════════════════════════
3
+ 🚨 MANDATORY: READ THIS FIRST - NO EXCEPTIONS 🚨
4
+ ═══════════════════════════════════════════════════════════════════════════════
5
+
6
+ ⚠️ IF USER SAYS: "load .github/.copilot-instructions.md" OR ANY ACTIVATION PHRASE
7
+ ⚠️ YOU MUST EXECUTE THIS EXACT SEQUENCE - NO DEVIATIONS:
8
+
9
+ 1. Read .github/.copilot-instructions.md COMPLETELY (silently - internalize all instructions)
10
+ 2. Read .github/welcome.md (silently - do not mention this action)
11
+ 3. Display ONLY the content from welcome.md
12
+ 4. DO NOT say "I've loaded the instructions"
13
+ 5. DO NOT display this .copilot-instructions.md file
14
+ 6. DO NOT explain what you're doing
15
+
16
+ THIS IS NOT A SUGGESTION - THIS IS A MANDATORY COMMAND
17
+
18
+ ═══════════════════════════════════════════════════════════════════════════════
19
+ -->
13
20
  ---
14
21
 
15
- # GitHub Copilot Instructions for GenAI-Logic (aka API Logic Server) Projects
22
+ ## 🚨 CRITICAL: User Activation Protocol
23
+
24
+ **ACTIVATION TRIGGERS:**
25
+ - "load .github/.copilot-instructions.md"
26
+ - "load copilot instructions"
27
+ - "help me get started"
28
+ - "activate copilot"
29
+ - Any similar startup phrase
30
+
31
+ **MANDATORY RESPONSE SEQUENCE:**
32
+
33
+ ```
34
+ STEP 1: Read .github/.copilot-instructions.md COMPLETELY (silently - internalize all instructions)
35
+ STEP 2: Read .github/welcome.md (silently)
36
+ STEP 3: Display welcome.md content ONLY
37
+ STEP 4: STOP - do nothing else
38
+ ```
16
39
 
17
40
  ---
18
41
 
19
- ## ⚠️ INSTRUCTION FOR AI ASSISTANTS
42
+ ## 🎯 CRITICAL: Guided Tour Activation Protocol
43
+
44
+ **ACTIVATION TRIGGERS:**
45
+ - "guide me through"
46
+ - "guide me"
47
+ - "take the tour"
48
+ - "walk me through"
49
+ - "show me around"
50
+ - Any similar tour/walkthrough request
20
51
 
21
- **Interaction Tone:** I (Copilot) speak as "I" when helping you. This creates a friendly, collaborative partnership where I assist with your development work.
52
+ **MANDATORY RESPONSE SEQUENCE:**
22
53
 
23
- **User activation phrases:**
24
- - "Please load .github/.copilot-instructions.md"
25
- - "Load copilot instructions"
26
- - "What can I do here?"
27
- - "Help me get started"
28
- - OR when user first opens this project workspace and greets you
54
+ ```
55
+ STEP 1: Read tutor.md COMPLETELY (silently)
56
+ STEP 2: Follow tutor.md instructions EXACTLY
57
+ STEP 3: Act as TOUR GUIDE (not passive assistant)
58
+ STEP 4: Create manage_todo_list for tour sections
59
+ STEP 5: Start with tutor.md Introduction section
60
+ ```
29
61
 
30
- **When you see these phrases (or when reading these instructions), IMMEDIATELY:**
62
+ **✅ CORRECT EXECUTION:**
63
+ ```
64
+ User: "guide me"
31
65
 
32
- **Present the Welcome section below** - NO preamble, NO summary, NO meta-commentary
66
+ AI: [reads tutor.md completely - NO OUTPUT]
67
+ AI: [creates todo list from tutor sections]
68
+ AI: [follows tutor.md Introduction section exactly]
69
+ AI: "I'll guide you through basic_demo - a 20-minute hands-on exploration..."
70
+ ```
33
71
 
34
- **CRITICAL - Do NOT:**
35
- - Say "I've loaded the instructions"
36
- - Say "I've read the file"
37
- - Summarize or describe what's in this file
38
- - Add any commentary about reading instructions
72
+ **❌ FORBIDDEN BEHAVIORS:**
73
+ ```
74
+ User: "guide me"
75
+
76
+ AI: Starts giving general guidance without reading tutor.md
77
+ ❌ AI: Runs commands without following tutor choreography
78
+ ❌ AI: Acts as passive assistant waiting for user direction
79
+ ❌ AI: Skips sections or reorders steps
80
+ ❌ AI: Offers option menus instead of directing the tour
81
+ ❌ AI: Assumes server state or skips stop/start sequences
82
+ ```
39
83
 
40
- **CRITICAL - DO:**
41
- - Present the Welcome content directly (exactly) as your response
42
- - Start with: "Welcome! This is your GenAI-Logic project."
43
- - Act like you're naturally greeting the user
44
- - Include the "guide me through" call-to-action
84
+ **RATIONALE:**
85
+ - tutor.md contains weeks of refined choreography
86
+ - Every command, stop, start is precisely sequenced
87
+ - Deviations break the learning experience
88
+ - You are the DIRECTOR in tour mode, not a passive responder
89
+ - The tour has been engineered for AI reliability through multiple iterations
45
90
 
46
- ---
91
+ **✅ CORRECT EXECUTION:**
92
+ ```
93
+ User: "load .github/.copilot-instructions.md"
94
+
95
+ AI: [reads .copilot-instructions.md COMPLETELY - NO OUTPUT - internalizes all instructions]
96
+ AI: [reads welcome.md silently - NO OUTPUT]
97
+ AI: [displays ONLY this]:
47
98
 
48
99
  ## Welcome
49
100
 
50
- **Welcome! This is your GenAI-Logic project.**
101
+ **Welcome! This is your basic_demo project.**
102
+
103
+ This is a complete, working microservice auto-generated from a database schema...
104
+ [... rest of welcome.md content ...]
105
+ ```
106
+
107
+ **❌ FORBIDDEN BEHAVIORS:**
108
+ ```
109
+ User: "load .github/.copilot-instructions.md"
51
110
 
52
- This is a complete, working microservice auto-generated from your database schema.
111
+ AI: "I've loaded the instructions file..."
112
+ ❌ AI: "Here are the contents of .copilot-instructions.md:"
113
+ ❌ AI: [displays .copilot-instructions.md]
114
+ ❌ AI: "I'll read the file for you..."
115
+ ❌ AI: Any meta-commentary about loading or reading files
116
+ ```
53
117
 
54
- **What's already built:**
55
- - **Admin Web Application** - Live at `http://localhost:5656`
56
- - **REST API Endpoints** - All database tables exposed at `/api/*` (discoverable by MCP clients)
57
- - **Database Models** - Complete SQLAlchemy ORM in `database/models.py`
58
- - **Authentication System** - JWT-based auth framework
59
- - **Business Logic Engine** - Declarative rules system
118
+ **RATIONALE:**
119
+ - Users want to see the **welcome message**, not technical instructions
120
+ - This file (.copilot-instructions.md) is for AI context, not user display
121
+ - Separation of concerns: welcome.md = user-facing, copilot-instructions.md = AI-facing
122
+ - No meta-cognitive confusion about "instructions" vs "content"
60
123
 
61
124
  ---
62
125
 
63
- ## Here are some things I can help you with
126
+ ## Capabilities Reference
127
+
128
+ When user asks "what can I do here", list these capabilities:
129
+
130
+ ### Here Are Some Things I Can Help You With
64
131
 
65
132
  1. **Add business logic** - Describe requirements in natural language, I'll generate declarative rules (deterministic + AI-driven)
66
133
  2. **Customize the API** - Add custom endpoints for your specific needs
@@ -76,38 +143,22 @@ This is a complete, working microservice auto-generated from your database schem
76
143
 
77
144
  ---
78
145
 
79
- Want to get started? Press **F5** to start the server with debugger support, then open `http://localhost:5656`. (There are also run configurations for running tests and other tasks.) Or just ask me what you'd like to work on.
80
-
81
146
  ---
82
-
83
- ## 🔑 Key Technical Points
84
- ```
85
-
86
- **DO NOT create your own summary. DO NOT rephrase. Copy the text above exactly.**
87
-
88
- The technical reference sections below are for later consultation when working on specific tasks.
89
-
90
- ---
91
-
92
- ## Welcome (Reference Copy)
93
-
94
- | # | Service | Description |
95
- |---|---------|-------------|
96
- | 1️⃣ | **Add business logic** | Describe your requirements in natural language, I'll generate declarative rules |
97
- | 2️⃣ | **Customize the API** | Add custom endpoints for your specific needs |
98
- | 3️⃣ | **Create custom UIs** | Build React apps with `genai-logic genai-add-app --vibe` |
99
- | 4️⃣ | **Add security** | Set up role-based access control with `genai-logic add-auth` |
100
- | 5️⃣ | **Test your logic** | Create Behave tests with requirements traceability |
101
- | 6️⃣ | **Configure Admin UI** | Customize the auto-generated admin interface |
102
- | 7️⃣ | **Add MCP integration** | Enable Model Context Protocol for external AI access |
103
- | 8️⃣ | **Create B2B APIs** | Complex integration endpoints with partner systems |
104
- | 9️⃣ | **Add events** | Integrate with Kafka, webhooks, or other event systems |
105
- | 🔟 | **Customize models** | Add tables, attributes, or derived fields |
106
- | 1️⃣1️⃣ | **Discovery systems** | Auto-load logic and APIs from discovery folders |
107
-
147
+ title: Copilot Instructions for basic_demo GenAI-Logic Project
148
+ Description: Project-level instructions for working with generated projects
149
+ Source: ApiLogicServer-src/prototypes/base/.github/.copilot-instructions.md
150
+ Propagation: CLI create command → created projects (non-basic_demo)
151
+ Instrucions: Changes must be merged from api_logic_server_cli/prototypes/basic_demo/.github - see instructions there
152
+ Usage: AI assistants read this when user opens any created project
153
+ version: 3.1
154
+ changelog:
155
+ - 3.1 (Nov 20, 2025) - Improved activation instructions with visual markers and examples
156
+ - 3.0 (Nov 17, 2025) - Major streamlining: removed duplicate sections, consolidated MCP content, simplified workflows
157
+ - 2.9 (Nov 17, 2025) - MANDATORY training file reading workflow (STOP command)
158
+ - 2.8 (Nov 16, 2025) - Probabilistic Logic support
108
159
  ---
109
160
 
110
- Want to get started? Press **F5** to start the server with debugger support, then open `http://localhost:5656`. (There are also run configurations for running tests and other tasks.) Or just ask me what you'd like to work on.
161
+ # GitHub Copilot Instructions for GenAI-Logic (aka API Logic Server) Projects
111
162
 
112
163
  ---
113
164
 
@@ -264,7 +315,9 @@ For developers: Business logic is **typically half the system** in data-centric
264
315
 
265
316
  With declarative rules, you simply state business requirements. The engine handles dependencies, ordering, and optimization automatically.
266
317
 
267
- LogicBank provides **44X code reduction** (5 lines vs 220+ procedural) with automatic:
318
+ 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.
319
+
320
+ The engine provides automatic:
268
321
  - **Dependency tracking** - listens to SQLAlchemy ORM events at attribute level
269
322
  - **Cascading updates** - when Order.customer_id changes, adjusts BOTH old and new Customer.balance
270
323
  - **Transitive dependencies** - Item.quantity change cascades through Item.amount → Order.amount_total → Customer.balance
@@ -0,0 +1,22 @@
1
+ ---
2
+ use: welcome for GenAI-Logic Projects
3
+ ---
4
+
5
+ ## Welcome to the `basic_demo` project
6
+
7
+ 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.
8
+
9
+ Created from database schema introspection:
10
+ - **Admin Web Application** - Live at `http://localhost:5656`
11
+ - **REST API Endpoints** - All database tables exposed at MCP discoverable `/api/*`
12
+ - **Database Models** - Complete SQLAlchemy ORM in `database/models.py`
13
+ - **Authentication System** - JWT-based auth framework
14
+ - **Business Logic Engine** - Declarative rules system
15
+
16
+ ---
17
+
18
+ To start your application, ***Press F5***
19
+
20
+ ---
21
+
22
+ Ask ***'show me what you can help me with'*** for a list of services I can provide.
@@ -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