ApiLogicServer 14.5.4__py3-none-any.whl → 14.5.14__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 (56) hide show
  1. api_logic_server_cli/api_logic_server.py +4 -122
  2. api_logic_server_cli/api_logic_server_info.yaml +3 -3
  3. api_logic_server_cli/create_from_model/__pycache__/api_logic_server_utils.cpython-312.pyc +0 -0
  4. api_logic_server_cli/create_from_model/__pycache__/dbml.cpython-312.pyc +0 -0
  5. api_logic_server_cli/create_from_model/api_logic_server_utils.py +29 -10
  6. api_logic_server_cli/create_from_model/dbml.py +16 -12
  7. api_logic_server_cli/database/basic_demo.sqlite +0 -0
  8. api_logic_server_cli/genai/genai.py +4 -0
  9. api_logic_server_cli/genai/genai_svcs.py +10 -2
  10. api_logic_server_cli/manager.py +3 -1
  11. api_logic_server_cli/prototypes/base/.vscode/launch.json +40 -0
  12. api_logic_server_cli/prototypes/base/api/api_discovery/mcp_discovery.py +58 -0
  13. api_logic_server_cli/prototypes/base/database/system/SAFRSBaseX.py +68 -2
  14. api_logic_server_cli/prototypes/base/database/system/SAFRSBaseX.pyZ +73 -0
  15. api_logic_server_cli/prototypes/{basic_demo/customizations/integration → base/integration/mcp}/.DS_Store +0 -0
  16. api_logic_server_cli/prototypes/base/integration/mcp/README_mcp.md +15 -0
  17. api_logic_server_cli/prototypes/{basic_demo/customizations → base}/integration/mcp/mcp_client_executor.py +91 -103
  18. api_logic_server_cli/prototypes/base/integration/mcp/mcp_schema.txt +47 -0
  19. api_logic_server_cli/prototypes/base/integration/mcp/mcp_server_discovery.json +9 -0
  20. api_logic_server_cli/prototypes/base/integration/mcp/test_notes.txt +37 -0
  21. api_logic_server_cli/prototypes/basic_demo/README.md +251 -91
  22. api_logic_server_cli/prototypes/basic_demo/customizations/api/api_discovery/mcp_discovery.py +1 -1
  23. api_logic_server_cli/prototypes/basic_demo/customizations/database/db.sqlite +0 -0
  24. api_logic_server_cli/prototypes/basic_demo/customizations/database/models.py +27 -12
  25. api_logic_server_cli/prototypes/basic_demo/customizations/database/system/SAFRSBaseX.py +1 -1
  26. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/Zmcp_client_executor.py +294 -0
  27. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/mcp_tool_context.json +25 -0
  28. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/test_notes.txt +37 -0
  29. api_logic_server_cli/prototypes/basic_demo/customizations/logic/declare_logic.py +1 -20
  30. api_logic_server_cli/prototypes/basic_demo/customizations/logic/logic_discovery/email_request.py +47 -0
  31. api_logic_server_cli/prototypes/basic_demo/customizations/logic/logic_discovery/mcp_client_executor_request.py +320 -0
  32. api_logic_server_cli/prototypes/basic_demo/customizations/logic/logic_discovery/simple_constraints.py +25 -0
  33. api_logic_server_cli/prototypes/basic_demo/customizations/ui/admin/admin.yaml +39 -32
  34. api_logic_server_cli/prototypes/basic_demo/iteration/database/db.sqlite +0 -0
  35. api_logic_server_cli/prototypes/basic_demo/iteration/ui/admin/admin.yaml +39 -44
  36. api_logic_server_cli/prototypes/manager/.vscode/launch.json +21 -0
  37. api_logic_server_cli/prototypes/manager/{README.md → READMEz.md} +4 -4
  38. api_logic_server_cli/prototypes/manager/REAMDE.md +1057 -0
  39. api_logic_server_cli/prototypes/manager/system/genai/mcp_learning/mcp.prompt +27 -0
  40. api_logic_server_cli/prototypes/manager/system/install-ApiLogicServer-dev/install-ApiLogicServer-dev.sh +2 -1
  41. {apilogicserver-14.5.4.dist-info → apilogicserver-14.5.14.dist-info}/METADATA +1 -1
  42. {apilogicserver-14.5.4.dist-info → apilogicserver-14.5.14.dist-info}/RECORD +46 -42
  43. {apilogicserver-14.5.4.dist-info → apilogicserver-14.5.14.dist-info}/WHEEL +1 -1
  44. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/3_executor_test_agent.py +0 -52
  45. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/README_functon.md +0 -201
  46. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/ai_plugin.json +0 -17
  47. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/nw-swagger_3.json +0 -1731
  48. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/snippets.txt +0 -5
  49. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3 genai_demo_with_get.json +0 -1731
  50. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3.json +0 -1782
  51. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3_genai_demo.json +0 -264
  52. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3_genai_demo_with_update.json +0 -1782
  53. api_logic_server_cli/prototypes/genai_demo/logic/logic_discovery/auto_discovery.py +0 -52
  54. {apilogicserver-14.5.4.dist-info → apilogicserver-14.5.14.dist-info}/entry_points.txt +0 -0
  55. {apilogicserver-14.5.4.dist-info → apilogicserver-14.5.14.dist-info}/licenses/LICENSE +0 -0
  56. {apilogicserver-14.5.4.dist-info → apilogicserver-14.5.14.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,320 @@
1
+ """
2
+ This simulates the MCP Client Executor,
3
+ which takes a natural language query and converts it into a tool context block:
4
+
5
+ 1. Discovers MCP servers (from config)
6
+ 2. Queries OpenAI's GPT-4 model to obtain the tool context based on a provided schema and a natural language query
7
+ 3. Processes the tool context (calls the indicated MCP (als) endpoints)
8
+
9
+ Notes:
10
+ * See: integration/mcp/README_mcp.md
11
+ * python api_logic_server_run.py
12
+
13
+ """
14
+
15
+ import json
16
+ import os, sys
17
+ from typing import Dict, List
18
+ import openai
19
+ import requests
20
+ from logic_bank.exec_row_logic.logic_row import LogicRow
21
+ from logic_bank.logic_bank import Rule
22
+ from database import models
23
+ from logic_bank.util import ConstraintException
24
+
25
+ # Set your OpenAI API key
26
+ openai.api_key = os.getenv("APILOGICSERVER_CHATGPT_APIKEY")
27
+
28
+ server_url = os.getenv("APILOGICSERVER_URL", "http://localhost:5656/api")
29
+
30
+ # debug settings
31
+ test_type = 'orchestration' # 'simple_get' or 'orchestration'
32
+ create_tool_context_from_llm = True
33
+ ''' set to False to bypass LLM call and save 2-3 secs in testing '''
34
+ use_test_schema = False
35
+ ''' True means bypass discovery, use hard-coded schedma file '''
36
+
37
+ def discover_mcp_servers():
38
+ """ Discover the MCP servers by calling the /api/.well-known/mcp.json endpoint.
39
+ This function retrieves the list of available MCP servers and their capabilities.
40
+ """
41
+ global server_url, use_test_schema
42
+
43
+ # create schema_text (for prompt), by reading integration/mcp/mcp_schema.txt
44
+
45
+ # find the servers - read the mcp_server_discovery.json file
46
+ discovery_file_path = os.path.join(os.path.dirname(__file__), "../../integration/mcp/mcp_server_discovery.json")
47
+ try:
48
+ with open(discovery_file_path, "r") as discovery_file:
49
+ discovery_data = json.load(discovery_file)
50
+ print(f"\n1. Discovered MCP servers from config file: {discovery_file_path}:" + json.dumps(discovery_data, indent=4))
51
+ except FileNotFoundError:
52
+ print(f"Discovery file not found at {discovery_file_path}.")
53
+ except json.JSONDecodeError as e:
54
+ print(f"Error decoding JSON from {discovery_file_path}: {e}")
55
+
56
+ for each_server in discovery_data["servers"]:
57
+ discovery_url = each_server["schema_url"]
58
+
59
+ # Call the discovery_url to get the MCP/API schema
60
+ try:
61
+ response = requests.get(discovery_url)
62
+ if response.status_code == 200:
63
+ api_schema = response.json()
64
+ print()
65
+ request_print = json.dumps(api_schema, indent=4)[0:400] + '\n... etc' # limit for readability
66
+ print(f"\n\nAPI Schema from discovery schema_url: {discovery_url}:\n" + request_print)
67
+ else:
68
+ print(f"Failed to retrieve API schema from {discovery_url}: {response.status_code}")
69
+ except requests.RequestException as e:
70
+ print(f"Error calling OpenAPI URL: {e}")
71
+ return json.dumps(api_schema)
72
+
73
+
74
+ def get_user_nl_query_and_training(query: str):
75
+ """ Get the natural language query from the user.
76
+ Add training for the LLM to generate a tool context block.
77
+
78
+ """
79
+
80
+ global test_type
81
+ # read file docs/mcp_learning/mcp.prompt
82
+ prompt_file_path = os.path.join(os.path.dirname(__file__), "../../docs/mcp_learning/mcp.prompt")
83
+ if os.path.exists(prompt_file_path):
84
+ with open(prompt_file_path, "r") as prompt_file:
85
+ training_prompt = prompt_file.read()
86
+ # print(f"\nLoaded training prompt from {prompt_file_path}:\n{training_prompt}")
87
+ else:
88
+ training_prompt = ""
89
+ print(f"Prompt file not found at {prompt_file_path}.")
90
+ return query + "\n\n" + training_prompt
91
+
92
+
93
+ def query_llm_with_nl(schema_text, nl_query):
94
+ """
95
+ Query the LLM with a natural language query and schema text to generate a tool context block.
96
+
97
+ It handles both orchestration and simple GET requests.
98
+ """
99
+
100
+ global test_type, create_tool_context_from_llm
101
+
102
+ content = f"Natural language query:\n {nl_query}\nSchema:\n{schema_text}"
103
+ messages = [
104
+ {
105
+ "role": "system",
106
+ "content": "You are an API planner that converts natural language queries into MCP Tool Context blocks using JSON:API. Return only the tool context as JSON."
107
+ },
108
+ {
109
+ "role": "user",
110
+ "content": f"{content}"
111
+ }
112
+ ]
113
+
114
+ request_print = content[0:1200] + '\n... etc' # limit for readability
115
+ print("\n\n2a. LLM request:\n", request_print)
116
+ # print("\n2b. NL Query:\n", nl_query)
117
+ # print("\n2c. schema_text: (truncated) \n")
118
+ # schema_print = json.dumps(json.loads(schema_text), indent=4)[:400] # limit for readability
119
+ # print(schema_print)
120
+
121
+ if create_tool_context_from_llm: # takes 2-3 seconds...
122
+ response = openai.chat.completions.create(
123
+ model="gpt-4",
124
+ messages=messages,
125
+ temperature=0.2
126
+ )
127
+
128
+ tool_context_str = response.choices[0].message.content
129
+ tool_context_str_no_cr = tool_context_str.replace("\n", '') # convert single quotes to double quotes
130
+ try:
131
+ tool_context = json.loads(tool_context_str_no_cr)
132
+ except json.JSONDecodeError:
133
+ print("Failed to decode JSON from response:", tool_context_str)
134
+ return None
135
+
136
+ print("\n2d. generated tool context from LLM:\n", json.dumps(tool_context, indent=4))
137
+
138
+ if "resources" not in tool_context:
139
+ raise ConstraintException("GenAI Error - LLM response does not contain 'resources'.")
140
+ return tool_context
141
+
142
+
143
+ def process_tool_context(tool_context):
144
+ """ Process the orchestration request by executing multiple tool context blocks.
145
+ This executes the tool context blocks against a live JSON:API server.
146
+ It handles both GET and POST requests, and it can
147
+ orchestrate multiple requests based on the provided tool context.
148
+
149
+ Note the orchestration is processed by the client executor (here), not the server executor.
150
+
151
+ Research:
152
+
153
+ 1. How is this a "USB", since the request was specific about JSON:API?
154
+ 2. How is it clear to loop through the tool_context[0] and call tool_context[1]?
155
+ """
156
+ global server_url
157
+
158
+ def get_query_param_filter(query_params):
159
+ """ return json:api filter
160
+
161
+ eg
162
+ curl -qg 'http://localhost:5656/api/Order?filter=[{"name":"date_shipped","op":"eq","val":null},{"name":"CreatedOn","op":"lt","val":"2023-07-14"}]'
163
+
164
+ curl -qg 'http://localhost:5656/api/Order?filter=[{"name":"date_shipped","op":"gt","val":"2023-07-14"}]'
165
+ curl -qg 'http://localhost:5656/api/Order?filter=[{"name":"date_shipped","op":"eq","val":null}]'
166
+ curl -qg 'http://localhost:5656/api/Customer?filter=[{"name":"credit_limit","op":"gt","val":"1000"}]'
167
+
168
+ query_params might be simple:
169
+ "query_params": [ {"name": "credit_limit", "op": "gt", "val": "1000"} ]
170
+ ==> ?filter=[{"name":"credit_limit","op":"gt","val":"1000"}]
171
+
172
+ or a list:
173
+ "query_params": [
174
+ {
175
+ "name": "date_shipped",
176
+ "op": "eq",
177
+ "val": None
178
+ },
179
+ {
180
+ "name": "date_created",
181
+ "op": "lt",
182
+ "val": "2023-07-14"
183
+ }
184
+ ],
185
+
186
+ """
187
+
188
+ added_rows = 0
189
+
190
+ query_param_filter = ''
191
+ assert isinstance(query_params, list), "Query Params filter expected to be a list"
192
+ query_param_filter = 'filter=' + str(query_params)
193
+ # use urlencode to convert to JSON:API format...
194
+ # val urllib.parse.quote() or urllib.parse.urlencode()
195
+ # tool instructions... filtering, email etc "null"
196
+ query_param_filter = query_param_filter.replace("'", '"') # convert single quotes to double quotes
197
+ query_param_filter = query_param_filter.replace("None", 'null')
198
+ query_param_filter = query_param_filter.replace('"null"', 'null')
199
+ # query_param_filter = query_param_filter.replace("date_created", 'CreatedOn') # TODO - why this name?
200
+ return query_param_filter # end get_query_param_filter
201
+
202
+ def move_fields(src: dict, dest: dict, context_data: dict):
203
+ """ Move fields from src to dest, replacing any variables with their values from context_data."""
204
+ for variable_name, value in src.items():
205
+ move_value = value
206
+ if move_value.startswith("{") and move_value.endswith("}"):
207
+ # strip the braces, and get the name after the first dot, # eg: "{Order.customer_id}" ==> "customer_id"``
208
+ move_name = move_value[1:-1] # strip the braces
209
+ if '.' in move_value:
210
+ move_name = move_name.split('.', 1)[1]
211
+ move_value = context_data['attributes'][move_name]
212
+ dest[variable_name] = move_value
213
+ return dest
214
+
215
+ def print_get_response(query_param_filter, mcp_response):
216
+ """ Print the response from the GET request. """
217
+ print("\n3. MCP Server (als) GET filter(query_param_filter):\n", query_param_filter)
218
+ print(" GET Response:\n", mcp_response.text)
219
+ results : List[Dict] = mcp_response.json()['data']
220
+ # print results in a table format
221
+ if results:
222
+ # Get all unique keys from all result dicts
223
+ keys = set()
224
+ for row in results:
225
+ if isinstance(row, dict):
226
+ keys.update(row.keys())
227
+ keys = list(keys)
228
+ # Print header
229
+ print("\n| " + " | ".join(keys) + " |")
230
+ print("|" + "|".join(["---"] * len(keys)) + "|")
231
+ # Print rows
232
+ for row in results:
233
+ print("| " + " | ".join(str(row.get(k, "")) for k in keys) + " |")
234
+ else:
235
+ print("No results found.")
236
+
237
+ assert isinstance(tool_context, (dict, list)), "Tool context expected to be a dictionary"
238
+ context_data = {}
239
+ added_rows = 0
240
+
241
+ for each_block in tool_context["resources"]:
242
+ if process_tool_context := True:
243
+ if each_block["method"] == "GET":
244
+ query_param_filter = get_query_param_filter(each_block["query_params"])
245
+ headers = {"Content-Type": "application/vnd.api+json"}
246
+ if "headers" in each_block:
247
+ headers.update(each_block["headers"])
248
+ mcp_response = requests.get(
249
+ url = each_block["base_url"] + each_block["path"],
250
+ headers=headers,
251
+ params=query_param_filter
252
+ )
253
+ context_data = mcp_response.json()['data'] # result rows...
254
+ print_get_response(query_param_filter, mcp_response)
255
+ elif each_block["method"] in ["POST"]:
256
+ for each_order in context_data:
257
+ url = each_block["base_url"] + each_block["path"]
258
+ json_update_data = { 'data': {"type": each_block["path"][1:], 'attributes': {} } }
259
+ json_update_data_attributes = json_update_data["data"]["attributes"]
260
+ move_fields( src= each_block["body"], dest=json_update_data_attributes, context_data=each_order)
261
+ # eg: POST http://localhost:5656/api/SysEmail {'data': {'type': 'SysEmail', 'attributes': {'customer_id': 5, 'message': {'to': '{{ order.customer_id }}', 'subject': 'Discount for your order', 'body': 'Dear customer, you have a discount for your recent order. Thank you for shopping with us.'}}}}
262
+ headers = {"Content-Type": "application/vnd.api+json"}
263
+ if "headers" in each_block:
264
+ headers.update(each_block["headers"])
265
+ mcp_response = requests.post(
266
+ url=url,
267
+ headers=headers,
268
+ json=json_update_data
269
+ )
270
+ added_rows += 1
271
+ pass
272
+ print("\n3. MCP Server (als) POST Response:\n", mcp_response.text)
273
+ if added_rows > 0:
274
+ print(f"...Added {added_rows} rows to the database; last row (only) shown above.")
275
+ return mcp_response
276
+
277
+
278
+
279
+ def declare_logic():
280
+ """
281
+ This illustrates the request pattern.
282
+
283
+ The request pattern is a common pattern in API Logic Server,
284
+ where an insert triggers service invocation, such as sending email.
285
+
286
+ The Email table includes the columns for the email (e,g, recipient, subject, message).
287
+
288
+ Using a request object enables you to wrap the service call with logic, eg:
289
+
290
+ * *email requirement: do not send mail if customer has opted out*
291
+
292
+ See: https://apilogicserver.github.io/Docs/Integration-MCP/#3a-logic-request-pattern
293
+ """
294
+
295
+
296
+ def mcp_client_executor(row: models.SysMcp, old_row: models.SysMcp, logic_row: LogicRow):
297
+ """
298
+
299
+ #als: create an MCP request
300
+
301
+ curl -X 'POST' 'http://localhost:5656/api/SysMcp/' -H 'accept: application/vnd.api+json' -H 'Content-Type: application/json' -d '{ "data": { "attributes": {"request": "List the orders date_shipped is null and CreatedOn before 2023-07-14, and send a discount email (subject: '\''Discount Offer'\'') to the customer for each one."}, "type": "SysMcp"}}'
302
+
303
+ Args:
304
+ row (Mcp): inserted MCP with prompt
305
+ old_row (Mcp): n/a
306
+ logic_row (LogicRow): bundles curr/old row, with ins/upd/dlt logic
307
+ """
308
+ schema_text = discover_mcp_servers() # see: 1-discovery-from-als
309
+
310
+ query_example = "List the orders date_shipped is null and CreatedOn before 2023-07-14, and send a discount email (subject: 'Discount Offer') to the customer for each one."
311
+ query = row.request
312
+ prompt = get_user_nl_query_and_training(query)
313
+
314
+ tool_context = query_llm_with_nl(schema_text, prompt) # see: 2-tool-context-from-LLM
315
+
316
+ mcp_response = process_tool_context(tool_context) # see: 3-MCP-server response
317
+
318
+ print("\nTest complete.\n")
319
+
320
+ Rule.row_event(on_class=models.SysMcp, calling=mcp_client_executor) # see above
@@ -0,0 +1,25 @@
1
+ import datetime
2
+ from decimal import Decimal
3
+ from logic_bank.exec_row_logic.logic_row import LogicRow
4
+ from logic_bank.extensions.rule_extensions import RuleExtension
5
+ from logic_bank.logic_bank import Rule
6
+ from database import models
7
+ import api.system.opt_locking.opt_locking as opt_locking
8
+ from security.system.authorization import Grant
9
+ import logging
10
+ from flask import jsonify
11
+
12
+ app_logger = logging.getLogger(__name__)
13
+
14
+ def declare_logic():
15
+ """
16
+ Simple constraints for error testing, e.g.:
17
+
18
+ * Set a breakpoint on `as_condition`
19
+ * Observe the row values in the debugger
20
+ """
21
+
22
+ Rule.constraint(validate=models.Customer,
23
+ as_condition=lambda row: row.name != 'x',
24
+ error_msg="Customer name cannot be 'x'")
25
+
@@ -1,17 +1,12 @@
1
1
  about:
2
- date: May 13, 2025 20:12:31
3
- merged:
4
- at: May 13, 2025 20:15:21
5
- new_attributes: 'Customer.email Order.CreatedOn '
6
- new_resources: 'Email '
7
- new_tab_groups: 'Customer.EmailList '
2
+ date: May 26, 2025 06:57:17
8
3
  recent_changes: works with modified safrs-react-admin
9
4
  version: 0.0.0
10
5
  api_root: '{http_type}://{swagger_host}:{port}/{api}'
11
6
  authentication: '{system-default}'
12
7
  info:
13
8
  number_relationships: 4
14
- number_tables: 5
9
+ number_tables: 6
15
10
  info_toggle_checked: true
16
11
  resources:
17
12
  Customer:
@@ -24,10 +19,10 @@ resources:
24
19
  type: DECIMAL
25
20
  - name: credit_limit
26
21
  type: DECIMAL
27
- - name: id
28
22
  - name: email
29
23
  - name: email_opt_out
30
- type: BOOLEAN
24
+ type: Boolean
25
+ - name: id
31
26
  tab_groups:
32
27
  - direction: tomany
33
28
  fks:
@@ -37,29 +32,10 @@ resources:
37
32
  - direction: tomany
38
33
  fks:
39
34
  - customer_id
40
- name: EmailList
41
- resource: Email
35
+ name: SysEmailList
36
+ resource: SysEmail
42
37
  type: Customer
43
38
  user_key: name
44
- Email:
45
- attributes:
46
- - label: ' id*'
47
- name: id
48
- search: true
49
- sort: true
50
- - name: customer_id
51
- required: true
52
- - name: message
53
- - name: CreatedOn
54
- type: DATE
55
- tab_groups:
56
- - direction: toone
57
- fks:
58
- - customer_id
59
- name: customer
60
- resource: Customer
61
- type: Email
62
- user_key: id
63
39
  Item:
64
40
  attributes:
65
41
  - label: ' id*'
@@ -97,12 +73,12 @@ resources:
97
73
  - name: customer_id
98
74
  required: true
99
75
  - name: notes
76
+ - name: CreatedOn
77
+ type: DATE
100
78
  - name: amount_total
101
79
  type: DECIMAL
102
80
  - name: date_shipped
103
81
  type: DATE
104
- - name: CreatedOn
105
- type: DATE
106
82
  tab_groups:
107
83
  - direction: tomany
108
84
  fks:
@@ -133,6 +109,37 @@ resources:
133
109
  resource: Item
134
110
  type: Product
135
111
  user_key: name
112
+ SysEmail:
113
+ attributes:
114
+ - label: ' id*'
115
+ name: id
116
+ search: true
117
+ sort: true
118
+ - name: customer_id
119
+ required: true
120
+ - name: message
121
+ - name: subject
122
+ - name: CreatedOn
123
+ type: DATE
124
+ tab_groups:
125
+ - direction: toone
126
+ fks:
127
+ - customer_id
128
+ name: customer
129
+ resource: Customer
130
+ type: SysEmail
131
+ user_key: id
132
+ SysMcp:
133
+ attributes:
134
+ - label: ' id*'
135
+ name: id
136
+ search: true
137
+ sort: true
138
+ - name: request
139
+ - name: request_prompt
140
+ - name: completion
141
+ type: SysMcp
142
+ user_key: id
136
143
  settings:
137
144
  HomeJS: /admin-app/home.js
138
145
  max_list_columns: 8
@@ -1,17 +1,12 @@
1
1
  about:
2
- date: May 13, 2025 20:12:31
3
- merged:
4
- at: May 13, 2025 20:15:21
5
- new_attributes: 'Customer.email Order.CreatedOn '
6
- new_resources: 'Email '
7
- new_tab_groups: 'Customer.EmailList '
2
+ date: May 26, 2025 06:57:17
8
3
  recent_changes: works with modified safrs-react-admin
9
4
  version: 0.0.0
10
5
  api_root: '{http_type}://{swagger_host}:{port}/{api}'
11
6
  authentication: '{system-default}'
12
7
  info:
13
8
  number_relationships: 4
14
- number_tables: 5
9
+ number_tables: 6
15
10
  info_toggle_checked: true
16
11
  resources:
17
12
  Customer:
@@ -24,14 +19,10 @@ resources:
24
19
  type: DECIMAL
25
20
  - name: credit_limit
26
21
  type: DECIMAL
27
- - name: id
28
22
  - name: email
29
23
  - name: email_opt_out
30
- type: BOOLEAN
31
- description: Defines the Customer entity with a unique name, balance, and credit
32
- limit.
33
- info_list: Defines the Customer entity with a unique name, balance, and credit
34
- limit.
24
+ type: Boolean
25
+ - name: id
35
26
  tab_groups:
36
27
  - direction: tomany
37
28
  fks:
@@ -41,29 +32,10 @@ resources:
41
32
  - direction: tomany
42
33
  fks:
43
34
  - customer_id
44
- name: EmailList
45
- resource: Email
35
+ name: SysEmailList
36
+ resource: SysEmail
46
37
  type: Customer
47
38
  user_key: name
48
- Email:
49
- attributes:
50
- - label: ' id*'
51
- name: id
52
- search: true
53
- sort: true
54
- - name: customer_id
55
- required: true
56
- - name: message
57
- - name: CreatedOn
58
- type: DATE
59
- tab_groups:
60
- - direction: toone
61
- fks:
62
- - customer_id
63
- name: customer
64
- resource: Customer
65
- type: Email
66
- user_key: id
67
39
  Item:
68
40
  attributes:
69
41
  - label: ' id*'
@@ -79,8 +51,6 @@ resources:
79
51
  type: DECIMAL
80
52
  - name: unit_price
81
53
  type: DECIMAL
82
- description: Defines the Item entity with quantity, amounts, and unit price details.
83
- info_list: Defines the Item entity with quantity, amounts, and unit price details.
84
54
  tab_groups:
85
55
  - direction: toone
86
56
  fks:
@@ -103,16 +73,12 @@ resources:
103
73
  - name: customer_id
104
74
  required: true
105
75
  - name: notes
76
+ - name: CreatedOn
77
+ type: DATE
106
78
  - name: amount_total
107
79
  type: DECIMAL
108
80
  - name: date_shipped
109
81
  type: DATE
110
- - name: CreatedOn
111
- type: DATE
112
- description: Defines the Order entity which belongs to a customer. Includes notes
113
- and amount total.
114
- info_list: Defines the Order entity which belongs to a customer. Includes notes
115
- and amount total.
116
82
  tab_groups:
117
83
  - direction: tomany
118
84
  fks:
@@ -137,8 +103,6 @@ resources:
137
103
  - name: carbon_neutral
138
104
  type: BOOLEAN
139
105
  - name: id
140
- description: Defines the Product entity with a unique name and unit price.
141
- info_list: Defines the Product entity with a unique name and unit price.
142
106
  tab_groups:
143
107
  - direction: tomany
144
108
  fks:
@@ -147,6 +111,37 @@ resources:
147
111
  resource: Item
148
112
  type: Product
149
113
  user_key: name
114
+ SysEmail:
115
+ attributes:
116
+ - label: ' id*'
117
+ name: id
118
+ search: true
119
+ sort: true
120
+ - name: customer_id
121
+ required: true
122
+ - name: message
123
+ - name: subject
124
+ - name: CreatedOn
125
+ type: DATE
126
+ tab_groups:
127
+ - direction: toone
128
+ fks:
129
+ - customer_id
130
+ name: customer
131
+ resource: Customer
132
+ type: SysEmail
133
+ user_key: id
134
+ SysMcp:
135
+ attributes:
136
+ - label: ' id*'
137
+ name: id
138
+ search: true
139
+ sort: true
140
+ - name: request
141
+ - name: request_prompt
142
+ - name: completion
143
+ type: SysMcp
144
+ user_key: id
150
145
  settings:
151
146
  HomeJS: /admin-app/home.js
152
147
  max_list_columns: 8
@@ -103,6 +103,27 @@
103
103
  "console": "internalConsole",
104
104
  "internalConsoleOptions": "openOnSessionStart"
105
105
  },
106
+ {
107
+ "name": " - 1.5 GENAI - repaired-response=genai_demo.response_example",
108
+ "type": "debugpy",
109
+ "request": "launch",
110
+ "program": "${workspaceFolder}/venv/lib/python3.12/site-packages/api_logic_server_cli/cli.py",
111
+ "redirectOutput": true,
112
+ "cwd": "${workspaceFolder}",
113
+ "env": {
114
+ "PYTHONPATH": "",
115
+ "SECURITY_ENABLED": "False",
116
+ "PYTHONHASHSEED": "0",
117
+ "APILOGICSERVER_DEBUG": "False",
118
+ "OPT_LOCKING": "optional"},
119
+ "justMyCode": false,
120
+ "args": [ "genai", "--retries=-1"
121
+ , "--using=enai_demo.prompt"
122
+ , "--project-name=genai_demo"
123
+ , "--repaired-response=system/genai/examples/genai_demo/genai_demo.response_example"],
124
+ "console": "internalConsole",
125
+ "internalConsoleOptions": "openOnSessionStart"
126
+ },
106
127
  {
107
128
  "name": "ApiLogicServer Create",
108
129
  "type": "debugpy",
@@ -1,7 +1,7 @@
1
1
  ---
2
- version info: 1.0 (01/11/2025)
2
+ version info: 2.0 (05/24/2025)
3
3
  ---
4
- ## Welcome to API Logic Server
4
+ ## Welcome to GenAI-Logic
5
5
 
6
6
  1. ***Instant microservices*** (APIs and Admin Apps) from a database or **GenAI prompt** -- 1 command
7
7
 
@@ -79,7 +79,7 @@ Created projects use standard Flask and SQLAlchemy; automation is provided by Lo
79
79
  als create --project-name=basic_demo --db-url=basic_demo
80
80
  ```
81
81
 
82
- <br>The self-demo provides:
82
+ <br>The basic_demo project provides:
83
83
  1. A quick look at logic, security and integration
84
84
  2. Integration includes Kafka and MCP (**[Model Context Protocol](https://apilogicserver.github.io/Docs/Integration-MCP/)**)
85
85
 
@@ -102,7 +102,7 @@ Then, try your own databases [(db-url examples here)](https://apilogicserver.git
102
102
 
103
103
  <br>You can do this with or without signup:
104
104
 
105
- 1. If you have signed up (see *To obtain a ChatGPT API Key*, below), this will create and open a project called `genai_demo` from `genai_demo.prompt` (available in left Explorer pane):
105
+ 1. If you have signed up (see *To obtain a ChatGPT API Key*, below), this will create a new database and project called `genai_demo`, and open the project. It's created using `genai_demo.prompt`, visible in left Explorer pane:
106
106
 
107
107
  ```bash
108
108
  als genai --using=system/genai/examples/genai_demo/genai_demo.prompt --project-name=genai_demo