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.
- api_logic_server_cli/api_logic_server.py +2 -2
- api_logic_server_cli/prototypes/base/.github/.copilot-instructions.md +126 -73
- api_logic_server_cli/prototypes/base/.github/welcome.md +22 -0
- api_logic_server_cli/prototypes/base/docs/training/logic_bank_api.prompt +8 -7
- api_logic_server_cli/prototypes/base/docs/training/probabilistic_logic.prompt +45 -38
- api_logic_server_cli/prototypes/base/docs/training/probabilistic_logic_guide.md +44 -5
- api_logic_server_cli/prototypes/base/docs/training/probabilistic_template.py +27 -27
- api_logic_server_cli/prototypes/basic_demo/.github/welcome.md +7 -7
- api_logic_server_cli/prototypes/manager/.vscode/settings.json +1 -0
- {apilogicserver-16.0.0.dist-info → apilogicserver-16.0.3.dist-info}/METADATA +1 -1
- {apilogicserver-16.0.0.dist-info → apilogicserver-16.0.3.dist-info}/RECORD +15 -15
- api_logic_server_cli/prototypes/basic_demo/.github/.copilot-instructions.md +0 -1221
- {apilogicserver-16.0.0.dist-info → apilogicserver-16.0.3.dist-info}/WHEEL +0 -0
- {apilogicserver-16.0.0.dist-info → apilogicserver-16.0.3.dist-info}/entry_points.txt +0 -0
- {apilogicserver-16.0.0.dist-info → apilogicserver-16.0.3.dist-info}/licenses/LICENSE +0 -0
- {apilogicserver-16.0.0.dist-info → apilogicserver-16.0.3.dist-info}/top_level.txt +0 -0
|
@@ -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.
|
|
15
|
+
__version__ = "16.00.03" # last public release: 15.04.01
|
|
16
16
|
recent_changes = \
|
|
17
17
|
f'\n\nRecent Changes:\n' +\
|
|
18
|
-
"\t11/
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
**
|
|
52
|
+
**MANDATORY RESPONSE SEQUENCE:**
|
|
22
53
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
62
|
+
**✅ CORRECT EXECUTION:**
|
|
63
|
+
```
|
|
64
|
+
User: "guide me"
|
|
31
65
|
|
|
32
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
**
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
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
|
|
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
|
-
|
|
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
|
-
**
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
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
|
-
##
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
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)
|
|
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:
|
|
23
|
+
CRITICAL: Keep simple rules on ONE LINE (no exceptions). Goal: Visual scannability - see rule count at a glance.
|
|
24
24
|
|
|
25
|
-
|
|
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
|
|
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.
|
|
403
|
-
2.
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
434
|
-
|
|
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
|
-
|
|
446
|
-
|
|
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':
|
|
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
|
-
|
|
488
|
-
|
|
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"
|
|
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
|
|
586
|
-
-
|
|
587
|
-
-
|
|
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
|
|
593
|
-
- When
|
|
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
|
-
|
|
793
|
-
|
|
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
|
-
#
|
|
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
|
|
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
|