ApiLogicServer 15.0.12__py3-none-any.whl → 15.0.19__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 -1
- api_logic_server_cli/api_logic_server_info.yaml +2 -2
- api_logic_server_cli/cli.py +44 -1
- api_logic_server_cli/create_from_model/__pycache__/api_logic_server_utils.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/api_logic_server_utils.py +14 -0
- api_logic_server_cli/genai/genai_admin_app copy.py +150 -0
- api_logic_server_cli/genai/genai_admin_app.py +155 -0
- api_logic_server_cli/genai/genai_svcs.py +49 -10
- api_logic_server_cli/logging.yml +5 -0
- api_logic_server_cli/prototypes/base/config/config.py +9 -15
- api_logic_server_cli/prototypes/base/docs/training/admin_app_1_context.prompt.md +3 -0
- api_logic_server_cli/prototypes/base/docs/training/admin_app_2_functionality.prompt.md +69 -0
- api_logic_server_cli/prototypes/base/docs/training/admin_app_3_architecture.prompt.md +29 -0
- api_logic_server_cli/prototypes/base/docs/training/admin_app_unused.md +156 -0
- api_logic_server_cli/prototypes/base/integration/mcp/mcp_client_executor.py +15 -12
- api_logic_server_cli/prototypes/basic_demo/customizations/docs/mcp_learning/mcp_discovery.json +108 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/app_learning/Admin-App-Learning-Prompt.md +154 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/app_learning/notes.md +7 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/README.md +17 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/README_create_react_app.md +70 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/package-lock.json +18469 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/package.json +44 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/public/favicon.ico +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/public/index.html +43 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/public/logo192.png +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/public/logo512.png +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/public/manifest.json +25 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/public/robots.txt +3 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/src/App.css +38 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/src/App.js +25 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/src/App.test.js +8 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/src/dataProvider.js +110 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/src/index.css +13 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/src/index.js +17 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/src/logo.svg +1 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/src/reportWebVitals.js +13 -0
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/react-admin-template/src/setupTests.js +5 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/.DS_Store +0 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/README.md +19 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/README_create_react_app.md +70 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/package-lock.json +18469 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/package.json +44 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/public/favicon.ico +0 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/public/index.html +43 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/public/logo192.png +0 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/public/logo512.png +0 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/public/manifest.json +25 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/public/robots.txt +3 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/App.css +38 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/App.js +59 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/App.test.js +8 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/Category.js +82 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/Customer.js +116 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/CustomerDemographic.js +74 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/Department.js +99 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/Employee.js +149 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/EmployeeAudit.js +94 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/EmployeeTerritory.js +98 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/Location.js +91 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/Order.js +116 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/OrderDetail.js +121 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/Product.js +105 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/Region.js +70 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/Shipper.js +64 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/Supplier.js +103 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/Territory.js +84 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/Union.js +77 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/dataProvider.js +110 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/index.css +13 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/index.js +17 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/logo.svg +1 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/reportWebVitals.js +13 -0
- api_logic_server_cli/prototypes/nw/ui/react_admin/src/setupTests.js +5 -0
- {apilogicserver-15.0.12.dist-info → apilogicserver-15.0.19.dist-info}/METADATA +1 -1
- {apilogicserver-15.0.12.dist-info → apilogicserver-15.0.19.dist-info}/RECORD +80 -15
- {apilogicserver-15.0.12.dist-info → apilogicserver-15.0.19.dist-info}/WHEEL +0 -0
- {apilogicserver-15.0.12.dist-info → apilogicserver-15.0.19.dist-info}/entry_points.txt +0 -0
- {apilogicserver-15.0.12.dist-info → apilogicserver-15.0.19.dist-info}/licenses/LICENSE +0 -0
- {apilogicserver-15.0.12.dist-info → apilogicserver-15.0.19.dist-info}/top_level.txt +0 -0
|
@@ -12,9 +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__ = "15.00.
|
|
15
|
+
__version__ = "15.00.19" # last public release: 15.00.19 (15.00.12)
|
|
16
16
|
recent_changes = \
|
|
17
17
|
f'\n\nRecent Changes:\n' +\
|
|
18
|
+
"\t06/17/2024 - 15.00.19: Tech Preview: als genai-app \n"\
|
|
18
19
|
"\t06/10/2024 - 15.00.12: MCP Security, win fixes for readme, graphics quotes \n"\
|
|
19
20
|
"\t06/08/2024 - 15.00.10: MCP, optional shortening of stacktrace lines, bugfix[92] \n"\
|
|
20
21
|
"\t05/16/2024 - 14.05.00: safrs 3.1.7, running mcp preview \n"\
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
last_created_date: June
|
|
1
|
+
last_created_date: June 17, 2025 12:36:00
|
|
2
2
|
last_created_project_name: ../../../servers/basic_demo
|
|
3
|
-
last_created_version: 15.00.
|
|
3
|
+
last_created_version: 15.00.18
|
api_logic_server_cli/cli.py
CHANGED
|
@@ -751,7 +751,7 @@ def genai_logic(ctx, using, genai_version: str, retries: int, suggest: click.BOO
|
|
|
751
751
|
@click.pass_context
|
|
752
752
|
def genai_graphics(ctx, using, genai_version: str, replace_with: str):
|
|
753
753
|
"""
|
|
754
|
-
Adds
|
|
754
|
+
Adds graphics to current project.
|
|
755
755
|
"""
|
|
756
756
|
global command
|
|
757
757
|
project_dir = resolve_blank_project_name('')
|
|
@@ -779,6 +779,49 @@ def genai_graphics(ctx, using, genai_version: str, replace_with: str):
|
|
|
779
779
|
log.info("")
|
|
780
780
|
|
|
781
781
|
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
@main.command("genai-app", cls=HideDunderCommand)
|
|
785
|
+
@click.option('--app-name', 'app_name',
|
|
786
|
+
default='react_admin',
|
|
787
|
+
help="Name of generated app in ui/")
|
|
788
|
+
@click.option('--schema',
|
|
789
|
+
default='admin.yaml',
|
|
790
|
+
help="Model file in ui/admin/")
|
|
791
|
+
@click.option('--genai-version', 'genai_version',
|
|
792
|
+
default='gpt-4o',
|
|
793
|
+
help="Eg, gpt-3.5-turbo, gpt-4o")
|
|
794
|
+
@click.pass_context
|
|
795
|
+
def genai_admin_app(ctx, app_name: str, schema: str, genai_version: str):
|
|
796
|
+
"""
|
|
797
|
+
Adds a customizable react app to project
|
|
798
|
+
"""
|
|
799
|
+
global command
|
|
800
|
+
project_dir = resolve_blank_project_name('')
|
|
801
|
+
project_name = Path(project_dir).name
|
|
802
|
+
project = PR.ProjectRun(command="add_security",
|
|
803
|
+
project_name=project_name,
|
|
804
|
+
db_url="",
|
|
805
|
+
execute=False
|
|
806
|
+
)
|
|
807
|
+
project.project_directory, project.api_name, project.merge_into_prototype = \
|
|
808
|
+
create_utils.get_project_directory_and_api_name(project)
|
|
809
|
+
project.project_directory_actual = os.path.abspath(os.getcwd()) # make path absolute, not relative (no /../)
|
|
810
|
+
project.project_directory_path = Path(project.project_directory_actual)
|
|
811
|
+
models_py_path = project.project_directory_path.joinpath('database/models.py')
|
|
812
|
+
project.abs_db_url, project.nw_db_status, project.model_file_name = \
|
|
813
|
+
create_utils.get_abs_db_url("0. Using Sample DB", project, is_auth=True)
|
|
814
|
+
|
|
815
|
+
if not models_py_path.exists():
|
|
816
|
+
log.info(f'... Error - does not appear to be a project: {str(project.project_directory_path)}')
|
|
817
|
+
log.info(f'... Typical usage - cd into project, use --project_name=. \n')
|
|
818
|
+
exit (1)
|
|
819
|
+
from api_logic_server_cli.genai.genai_admin_app import GenAIAdminApp
|
|
820
|
+
genai_admin = GenAIAdminApp(project=project, app_name=app_name, schema=schema, genai_version=genai_version)
|
|
821
|
+
pass
|
|
822
|
+
log.info("")
|
|
823
|
+
|
|
824
|
+
|
|
782
825
|
@main.command("genai-create", cls=HideDunderCommand)
|
|
783
826
|
@click.option('--project-name', 'project_name',
|
|
784
827
|
default=f'{last_created_project_name}',
|
|
Binary file
|
|
@@ -374,7 +374,21 @@ def get_ontimize_apps(project_dir_path):
|
|
|
374
374
|
result.append(name)
|
|
375
375
|
log.debug(f"Found {len(result)} Ontimize app(s)")
|
|
376
376
|
return result
|
|
377
|
+
# genai_core/fs_utils.py
|
|
377
378
|
|
|
379
|
+
|
|
380
|
+
def ensure_dir(path):
|
|
381
|
+
os.makedirs(path, exist_ok=True)
|
|
382
|
+
|
|
383
|
+
def write_file(path, content):
|
|
384
|
+
ensure_dir(os.path.dirname(path))
|
|
385
|
+
with open(path, 'w', encoding='utf-8') as f:
|
|
386
|
+
f.write(content)
|
|
387
|
+
|
|
388
|
+
def read_file(path):
|
|
389
|
+
with open(path, 'r', encoding='utf-8') as f:
|
|
390
|
+
return f.read()
|
|
391
|
+
|
|
378
392
|
def does_file_contain(search_for: str, in_file: str) -> bool:
|
|
379
393
|
""" returns True if <search_for> is <in_file> """
|
|
380
394
|
with open(Path(in_file), 'r+') as fp:
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
from typing import Dict, List
|
|
3
|
+
from api_logic_server_cli.cli_args_project import Project
|
|
4
|
+
import logging
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import importlib
|
|
7
|
+
from api_logic_server_cli.genai.genai_utils import call_chatgpt
|
|
8
|
+
import requests
|
|
9
|
+
import os, time
|
|
10
|
+
import datetime
|
|
11
|
+
import create_from_model.api_logic_server_utils as utils
|
|
12
|
+
import time
|
|
13
|
+
from openai import OpenAI
|
|
14
|
+
from api_logic_server_cli.genai.genai_svcs import WGResult
|
|
15
|
+
from api_logic_server_cli.genai.genai_svcs import Rule
|
|
16
|
+
import api_logic_server_cli.genai.genai_svcs as genai_svcs
|
|
17
|
+
import json
|
|
18
|
+
from typing import List, Dict
|
|
19
|
+
from pydantic import BaseModel
|
|
20
|
+
from dotmap import DotMap
|
|
21
|
+
from natsort import natsorted
|
|
22
|
+
import glob
|
|
23
|
+
import create_from_model.api_logic_server_utils as create_utils
|
|
24
|
+
from jinja2 import Environment, FileSystemLoader
|
|
25
|
+
import os
|
|
26
|
+
import json
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from openai import OpenAI
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
K_data_model_prompt = "Use SQLAlchemy to create"
|
|
32
|
+
|
|
33
|
+
log = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
class GenAIAdminApp:
|
|
36
|
+
""" 6/13/2025
|
|
37
|
+
Creates ui/admin_app_react.zip, a source version of ui/admin/admin.yaml.
|
|
38
|
+
|
|
39
|
+
Users without JS/HTML background can use Nat Lang to customize ("Vibe for dummies").
|
|
40
|
+
|
|
41
|
+
Called by CLI for existing projects.
|
|
42
|
+
|
|
43
|
+
* The constructor project arg provides project.project_directory_path, to provide meta data to ChatGPT:
|
|
44
|
+
* docs/db.dbml describes the schema
|
|
45
|
+
* docs/mcp_learning/mcp_discovery.json describes the JSON:API
|
|
46
|
+
* docs/training/admin_app.md describes the app functionality and architecture
|
|
47
|
+
|
|
48
|
+
* Basic steps
|
|
49
|
+
* Step 1 – Parse Schema
|
|
50
|
+
* Step 2 – Generate <each_resource.js> (per schema)
|
|
51
|
+
* Step 3 – Generate `App.js`
|
|
52
|
+
* Step 4 – Custom dataProvider.js
|
|
53
|
+
* provide alternative for React Admin's default data provider for REST APIs,
|
|
54
|
+
* believe that is: 'ra-data-simple-rest'
|
|
55
|
+
|
|
56
|
+
Testing:
|
|
57
|
+
* BLT to create manager
|
|
58
|
+
* Use basic_demo
|
|
59
|
+
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def __init__(self, project: Project, genai_version: OpenAI):
|
|
64
|
+
self.project = project
|
|
65
|
+
self.schema_path = project.project_directory_path / "docs/db.dbml"
|
|
66
|
+
self.discovery_path = project.project_directory_path / "docs/mcp_learning/mcp_discovery.json"
|
|
67
|
+
self.genai_version = genai_version
|
|
68
|
+
|
|
69
|
+
self.manager_path = genai_svcs.get_manager_path()
|
|
70
|
+
|
|
71
|
+
self.start_time = time.time()
|
|
72
|
+
self.prompt = self.compose_prompt()
|
|
73
|
+
self.response = self.get_response(self.prompt)
|
|
74
|
+
self.content = self.response.choices[0].message.content
|
|
75
|
+
self.write_output(self.content)
|
|
76
|
+
return self.content
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def compose_prompt(self) -> str:
|
|
81
|
+
|
|
82
|
+
def load_prompt_parts(self):
|
|
83
|
+
"""
|
|
84
|
+
Step 2: load context, functionality, and architecture prompt sections
|
|
85
|
+
"""
|
|
86
|
+
context = (self.project_path / "docs/prompts/context.md").read_text()
|
|
87
|
+
functionality = (self.project_path / "docs/prompts/functionality.md").read_text()
|
|
88
|
+
architecture = (self.project_path / "docs/prompts/architecture.md").read_text()
|
|
89
|
+
return context, functionality, architecture
|
|
90
|
+
|
|
91
|
+
def load_schema(self) -> str:
|
|
92
|
+
return self.schema_path.read_text()
|
|
93
|
+
|
|
94
|
+
def load_discovery(self) -> str:
|
|
95
|
+
return json.dumps(json.loads(self.discovery_path.read_text()), indent=4)
|
|
96
|
+
|
|
97
|
+
"""
|
|
98
|
+
Step 3: compose the prompt by combining static prompt parts with schema and discovery
|
|
99
|
+
"""
|
|
100
|
+
context, functionality, architecture = self.load_prompt_parts()
|
|
101
|
+
schema = self.load_schema()
|
|
102
|
+
discovery = self.load_discovery()
|
|
103
|
+
|
|
104
|
+
prompt = f"""
|
|
105
|
+
{context}
|
|
106
|
+
|
|
107
|
+
The JSON:API backend is described by:
|
|
108
|
+
docs/db.dbml describes the schema:
|
|
109
|
+
|
|
110
|
+
```dbml
|
|
111
|
+
{schema}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
docs/mcp_learning/mcp_discovery.json describes the JSON:API:
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{discovery}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
{functionality}
|
|
121
|
+
|
|
122
|
+
{architecture}
|
|
123
|
+
"""
|
|
124
|
+
return prompt
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def get_response(self):
|
|
128
|
+
"""
|
|
129
|
+
Step 4: issue the prompt to the LLM to get generated source code
|
|
130
|
+
"""
|
|
131
|
+
response = self.client.chat.completions.create(
|
|
132
|
+
model="gpt-4o",
|
|
133
|
+
messages=[
|
|
134
|
+
{"role": "system", "content": "You are a senior React Admin developer."},
|
|
135
|
+
{"role": "user", "content": self.prompt}
|
|
136
|
+
],
|
|
137
|
+
temperature=0.3
|
|
138
|
+
)
|
|
139
|
+
return response
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def write_output(self, output: str, output_file: str = "admin_app_generated.txt"):
|
|
143
|
+
"""
|
|
144
|
+
Step 5: write generated source code to output file for inspection or extraction
|
|
145
|
+
"""
|
|
146
|
+
output_path = self.project_path / output_file
|
|
147
|
+
output_path.write_text(output)
|
|
148
|
+
print(f"Output written to {output_path}")
|
|
149
|
+
|
|
150
|
+
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
from typing import Dict, List
|
|
3
|
+
from api_logic_server_cli.cli_args_project import Project
|
|
4
|
+
import logging
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import importlib
|
|
7
|
+
from api_logic_server_cli.genai.genai_utils import call_chatgpt
|
|
8
|
+
import requests
|
|
9
|
+
import os, time
|
|
10
|
+
import datetime
|
|
11
|
+
import create_from_model.api_logic_server_utils as utils
|
|
12
|
+
import time
|
|
13
|
+
from openai import OpenAI
|
|
14
|
+
from api_logic_server_cli.genai.genai_svcs import WGResult
|
|
15
|
+
from api_logic_server_cli.genai.genai_svcs import Rule
|
|
16
|
+
import api_logic_server_cli.genai.genai_svcs as genai_svcs
|
|
17
|
+
import json
|
|
18
|
+
from typing import List, Dict
|
|
19
|
+
from pydantic import BaseModel
|
|
20
|
+
from dotmap import DotMap
|
|
21
|
+
from natsort import natsorted
|
|
22
|
+
import glob
|
|
23
|
+
import create_from_model.api_logic_server_utils as create_utils
|
|
24
|
+
from jinja2 import Environment, FileSystemLoader
|
|
25
|
+
import os
|
|
26
|
+
import json
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from openai import OpenAI
|
|
29
|
+
import yaml
|
|
30
|
+
import api_logic_server_cli.genai.genai_svcs as genai_svcs
|
|
31
|
+
|
|
32
|
+
log = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class JSResponseFormat(BaseModel): # must match system/genai/prompt_inserts/response_format.prompt
|
|
36
|
+
code : str # generated javascript code (only)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class GenAIAdminApp:
|
|
40
|
+
|
|
41
|
+
def __init__(self, project: Project, app_name: str, schema: str, genai_version: OpenAI): # TODO: type??
|
|
42
|
+
self.start_time = time.time()
|
|
43
|
+
|
|
44
|
+
self.api_version = genai_version
|
|
45
|
+
self.project_root = project.project_directory_path
|
|
46
|
+
self.dbml_path = self.project_root / "docs/db.dbml"
|
|
47
|
+
self.admin_yaml_path = self.project_root / f"ui/admin/{schema}"
|
|
48
|
+
self.discovery_path = self.project_root / "docs/mcp_learning/mcp_discovery.json"
|
|
49
|
+
|
|
50
|
+
self.ui_project_path = self.project_root / f"ui/{app_name}"
|
|
51
|
+
self.ui_src_path = self.ui_project_path / "src"
|
|
52
|
+
|
|
53
|
+
self.app_templates_path = genai_svcs.get_manager_path(use_env=True).joinpath('system/genai/app_templates')
|
|
54
|
+
self.react_admin_template_path = self.app_templates_path / 'react-admin-template'
|
|
55
|
+
self.prompts_path = self.app_templates_path / "app_learning"
|
|
56
|
+
self.admin_app_learning = utils.read_file(self.prompts_path / "Admin-App-Learning-Prompt.md")
|
|
57
|
+
self.image_url = self.prompts_path / 'Order-Page.png' # did not seem to help, made it 2x slower
|
|
58
|
+
|
|
59
|
+
# self.schema = utils.read_file(self.dbml_path)
|
|
60
|
+
self.schema_yaml = utils.read_file(self.admin_yaml_path)
|
|
61
|
+
self.schema = yaml.safe_load(self.schema_yaml)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
self.resources = {}
|
|
65
|
+
''' dict keyed by resource_name (todo: relns?) '''
|
|
66
|
+
self.resource_names = []
|
|
67
|
+
''' array of resource names '''
|
|
68
|
+
|
|
69
|
+
shutil.copytree(self.react_admin_template_path, self.ui_project_path, dirs_exist_ok=True)
|
|
70
|
+
|
|
71
|
+
# self.parse_resources()
|
|
72
|
+
self.a_generate_resource_files()
|
|
73
|
+
self.b_generate_app_js()
|
|
74
|
+
# comes from copytree, above -- self.c_generate_data_provider()
|
|
75
|
+
|
|
76
|
+
log.info(f"✅ Completed in [{str(int(time.time() - self.start_time))} secs] \n\n")
|
|
77
|
+
|
|
78
|
+
log.info(f"✅ Next Steps:\n")
|
|
79
|
+
log.info('Start the API Logic Project: F5')
|
|
80
|
+
log.info('> cd ui/react-admin')
|
|
81
|
+
log.info('> npm install')
|
|
82
|
+
log.info('> npm start')
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def a_generate_resource_files(self):
|
|
86
|
+
|
|
87
|
+
def fix_source(raw_source: str) -> str:
|
|
88
|
+
''' remove code occasional begin/end code markers '''
|
|
89
|
+
source_lines = raw_source.splitlines()
|
|
90
|
+
result_lines = []
|
|
91
|
+
found_start_marker = False
|
|
92
|
+
for each_line in source_lines:
|
|
93
|
+
if each_line.startswith("```"):
|
|
94
|
+
if each_line.startswith("```jsx") or each_line.startswith("```javascript"):
|
|
95
|
+
result_lines = []
|
|
96
|
+
continue
|
|
97
|
+
else:
|
|
98
|
+
break
|
|
99
|
+
result_lines.append(each_line)
|
|
100
|
+
|
|
101
|
+
# return source_lines as a string
|
|
102
|
+
return "\n".join(result_lines)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
for each_resource_name, each_resource in self.schema['resources'].items():
|
|
106
|
+
# image moves app gen time from 70 -> 130 secs
|
|
107
|
+
example_image_content_unused = [
|
|
108
|
+
{
|
|
109
|
+
"type": "text",
|
|
110
|
+
"text": "Here is a screenshot of the desired admin app layout. Use this as a visual guide to generate a React-Admin app that mimics the layout, structure, and joins."
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"type": "image_url",
|
|
114
|
+
"image_url": {
|
|
115
|
+
"url": "https://apilogicserver.github.io/Docs/images/ui-admin/Order-Page-Learning.png"
|
|
116
|
+
# "url": f"attachment:/{str(self.image_url)}"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
messages = [
|
|
121
|
+
{"role": "user", "content": "You are a helpful expert in react and JavaScript"},
|
|
122
|
+
{"role": "user", "content": self.admin_app_learning},
|
|
123
|
+
# {"role": "user", "content": example_image_content},
|
|
124
|
+
{"role": "user", "content": f'Schema:\n{self.schema_yaml}'},
|
|
125
|
+
{"role": "user", "content": f'Generate the full javascript source code for the `{each_resource_name}.js` React Admin file, formatted as a JSResponseFormat'}]
|
|
126
|
+
save_response = self.project_root / f"docs/admin_app/{each_resource_name}"
|
|
127
|
+
output = genai_svcs.call_chatgpt(messages = messages,
|
|
128
|
+
api_version=self.api_version,
|
|
129
|
+
using=save_response,
|
|
130
|
+
response_as=JSResponseFormat)
|
|
131
|
+
response_dict = json.loads(output)
|
|
132
|
+
target_file = self.ui_src_path / f"{each_resource_name}.js"
|
|
133
|
+
source_code = fix_source(response_dict['code'])
|
|
134
|
+
utils.write_file(target_file, source_code)
|
|
135
|
+
log.info(f"\n✅ Wrote: {target_file}")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def b_generate_app_js(self):
|
|
139
|
+
messages = []
|
|
140
|
+
messages = [
|
|
141
|
+
{"role": "user", "content": "You are a helpful expert in react and JavaScript"},
|
|
142
|
+
{"role": "user", "content": self.admin_app_learning},
|
|
143
|
+
{"role": "user", "content": f'Schema:\n{self.schema_yaml}'},
|
|
144
|
+
{"role": "user", "content": f'Generate the complete App.js that wires together the above resources. for the `app.js` React Admin file, formatted as a JSResponseFormat.'}]
|
|
145
|
+
save_response = self.project_root / f"docs/admin_app/app.js"
|
|
146
|
+
output = genai_svcs.call_chatgpt(messages = messages,
|
|
147
|
+
api_version=self.api_version,
|
|
148
|
+
using=save_response,
|
|
149
|
+
response_as=JSResponseFormat)
|
|
150
|
+
response_dict = json.loads(output)
|
|
151
|
+
target_file = self.ui_src_path / "App.js"
|
|
152
|
+
utils.write_file(target_file, response_dict['code'])
|
|
153
|
+
|
|
154
|
+
log.info(f"✅ Wrote: {target_file}\n")
|
|
155
|
+
|
|
@@ -883,7 +883,7 @@ def select_messages(messages: List[Dict], messages_out: List[Dict], message_sele
|
|
|
883
883
|
return result
|
|
884
884
|
|
|
885
885
|
|
|
886
|
-
def call_chatgpt(messages: List[Dict[str, str]], api_version: str, using: str) -> str:
|
|
886
|
+
def call_chatgpt(messages: List[Dict[str, str]], api_version: str, using: str, response_as: dict=WGResult) -> str:
|
|
887
887
|
"""call ChatGPT with messages
|
|
888
888
|
|
|
889
889
|
Args:
|
|
@@ -893,6 +893,25 @@ def call_chatgpt(messages: List[Dict[str, str]], api_version: str, using: str) -
|
|
|
893
893
|
Returns:
|
|
894
894
|
str: response from ChatGPT
|
|
895
895
|
"""
|
|
896
|
+
|
|
897
|
+
def admin_app_path(request_path: Path, suffix: str) -> Path:
|
|
898
|
+
'''
|
|
899
|
+
returns path = remove the last node of the path, and prepend that to _request.json
|
|
900
|
+
# Example: if request_path is /a/b/c/d/request.json, make it /a/b/c/d_request.json
|
|
901
|
+
'''
|
|
902
|
+
parent = request_path.parent
|
|
903
|
+
last_node = parent.name
|
|
904
|
+
grandparent = parent.parent
|
|
905
|
+
request_path = grandparent.joinpath(f"{last_node}_{suffix}.json")
|
|
906
|
+
request_path.parent.mkdir(parents=True, exist_ok=True)
|
|
907
|
+
return request_path
|
|
908
|
+
|
|
909
|
+
def string_to_lines(dict_long_string: dict) -> dict:
|
|
910
|
+
result = dict_long_string
|
|
911
|
+
if isinstance(result[1]['content'], str):
|
|
912
|
+
result[1]['content'] = result[1]['content'].split('\n')
|
|
913
|
+
return result
|
|
914
|
+
|
|
896
915
|
try:
|
|
897
916
|
start_time = time.time()
|
|
898
917
|
model = api_version
|
|
@@ -901,26 +920,46 @@ def call_chatgpt(messages: List[Dict[str, str]], api_version: str, using: str) -
|
|
|
901
920
|
if model is None or model == "*": # system default chatgpt model
|
|
902
921
|
model = "gpt-4o-2024-08-06" # 33 sec
|
|
903
922
|
# model = "o3-mini" # 130 sec
|
|
904
|
-
|
|
905
|
-
|
|
923
|
+
request_path = Path(using).joinpath('request.json')
|
|
924
|
+
if 'admin_app' in str(request_path):
|
|
925
|
+
request_path = admin_app_path(request_path, 'request_raw')
|
|
926
|
+
with open(request_path, "w") as request_file: # save for debug
|
|
927
|
+
json.dump(messages, request_file, indent=4)
|
|
928
|
+
request_path = admin_app_path(request_path, 'request')
|
|
929
|
+
messages_for_print = messages
|
|
930
|
+
# make the long string better for viewing - convert to lines.
|
|
931
|
+
messages_for_print = [msg.copy() if isinstance(msg, dict) else msg for msg in messages]
|
|
932
|
+
messages_for_print = string_to_lines(messages_for_print) # Removed to avoid altering messages
|
|
933
|
+
with open(request_path, "w") as request_file: # save for debug
|
|
934
|
+
json.dump(messages_for_print, request_file, indent=4)
|
|
906
935
|
log.info(f'.. saved request: {using}/request.json')
|
|
907
936
|
client = get_ai_client()
|
|
937
|
+
# response_format = "json_object" if wg_response else {"type": "text"}
|
|
908
938
|
completion = client.beta.chat.completions.parse(
|
|
909
939
|
messages=messages,
|
|
910
|
-
response_format=
|
|
940
|
+
response_format=response_as,
|
|
911
941
|
# temperature=self.project.genai_temperature, values .1 and .7 made students / charges fail
|
|
912
942
|
model=model # for own model, use "ft:gpt-4o-2024-08-06:personal:logicbank:ARY904vS"
|
|
913
943
|
)
|
|
914
944
|
log.info(f'ChatGPT ({str(int(time.time() - start_time))} secs) - response at: system/genai/temp/chatgpt_original.response')
|
|
915
945
|
|
|
916
946
|
data = completion.choices[0].message.content
|
|
947
|
+
|
|
917
948
|
response_dict = json.loads(data)
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
949
|
+
request_path = Path(using).joinpath('request.json')
|
|
950
|
+
if 'admin_app' in str(request_path):
|
|
951
|
+
request_path = admin_app_path(request_path, 'response')
|
|
952
|
+
with open(request_path, "w") as request_file: # save for debug
|
|
953
|
+
json.dump(data, request_file, indent=4)
|
|
954
|
+
else:
|
|
955
|
+
with open(Path(using).joinpath('response.json'), "w") as response_file: # save for debug
|
|
956
|
+
json.dump(response_dict, response_file, indent=4)
|
|
957
|
+
with open(Path(using).joinpath('response.yaml'), "w") as response_file:
|
|
958
|
+
yaml.dump(response_dict, response_file, default_flow_style=False, default_style='|')
|
|
959
|
+
request_path = Path(using).joinpath('request.json')
|
|
960
|
+
request_path = admin_app_path(request_path, 'response_raw')
|
|
961
|
+
with open(Path(request_path), "w") as response_file: # save for debug
|
|
962
|
+
response_file.writelines(data)
|
|
924
963
|
log.debug(f'.. call_chatgpt saved response: {using}/response.json')
|
|
925
964
|
return data # this is a string...
|
|
926
965
|
except Exception as inst:
|
api_logic_server_cli/logging.yml
CHANGED
|
@@ -162,24 +162,18 @@ class Config:
|
|
|
162
162
|
''' keycloak client id '''
|
|
163
163
|
|
|
164
164
|
SECURITY_ENABLED = os.getenv("SECURITY_ENABLED",False)
|
|
165
|
-
SECURITY_PROVIDER = None
|
|
165
|
+
SECURITY_PROVIDER = os.getenv('SECURITY_PROVIDER', None) # type: ignore # type: str
|
|
166
166
|
if os.getenv('SECURITY_ENABLED'): # e.g. export SECURITY_ENABLED=true
|
|
167
|
-
security_export = os.getenv('SECURITY_ENABLED') # type: ignore # type: str
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
SECURITY_ENABLED = False # to remove env: unset SECURITY_ENABLED
|
|
171
|
-
else:
|
|
172
|
-
SECURITY_ENABLED = True
|
|
173
|
-
app_logger.debug(f'Security .. overridden from env variable: {SECURITY_ENABLED}')
|
|
167
|
+
security_export = os.getenv('SECURITY_ENABLED','false').lower() # type: ignore # type: str
|
|
168
|
+
SECURITY_ENABLED = security_export not in ["false", "no"] # NO SEC
|
|
169
|
+
app_logger.debug(f'Security .. overridden from env variable SECURITY_ENABLED: {SECURITY_ENABLED}')
|
|
174
170
|
if SECURITY_ENABLED:
|
|
175
|
-
from security.authentication_provider.sql.auth_provider import Authentication_Provider
|
|
171
|
+
from security.authentication_provider.sql.auth_provider import Authentication_Provider as SQL_Authentication_Provider
|
|
172
|
+
from security.authentication_provider.keycloak.auth_provider import Authentication_Provider as KC_Authentication_Provider
|
|
176
173
|
# typically, authentication_provider is [ keycloak | sql ]
|
|
177
|
-
SECURITY_PROVIDER =
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
app_logger.info(f'config.py - security disabled')
|
|
181
|
-
|
|
182
|
-
app_logger.info(f'SECURITY_PROVIDER={SECURITY_PROVIDER}')
|
|
174
|
+
SECURITY_PROVIDER = KC_Authentication_Provider if "keycloak" in str(SECURITY_PROVIDER).lower() else SQL_Authentication_Provider
|
|
175
|
+
|
|
176
|
+
app_logger.info(f'config.py - security enabled: {SECURITY_ENABLED} using SECURITY_PROVIDER: {str(SECURITY_PROVIDER)}\n')
|
|
183
177
|
|
|
184
178
|
# Begin Multi-Database URLs (from ApiLogicServer add-db...)
|
|
185
179
|
auth_db_path = str(project_path.joinpath('database/authentication_db.sqlite'))
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
## App Features
|
|
4
|
+
|
|
5
|
+
### Multi-Page
|
|
6
|
+
|
|
7
|
+
For each resource:
|
|
8
|
+
- Create a **List page** showing 7 user-friendly columns
|
|
9
|
+
- Add **pagination**, **sorting**, and **filtering**
|
|
10
|
+
- Link each row to a **Display (Show) page**
|
|
11
|
+
|
|
12
|
+
### Multi-Resource
|
|
13
|
+
|
|
14
|
+
Each **Display Page** should:
|
|
15
|
+
- Show all fields in a **multi-column layout**
|
|
16
|
+
- Include a **tab sheet** (`<TabbedShowLayout>`) for each related resource using `<ReferenceManyField>`
|
|
17
|
+
- Link child rows to their own display page
|
|
18
|
+
|
|
19
|
+
Example:
|
|
20
|
+
- Customer Display has tab for OrderList
|
|
21
|
+
- Each Order in the tab links to Order Display
|
|
22
|
+
|
|
23
|
+
### Automatic Joins
|
|
24
|
+
|
|
25
|
+
For foreign keys:
|
|
26
|
+
- Display joined value (e.g., `product.name` instead of `product_id`)
|
|
27
|
+
- Use first string field from parent table containing `name`, `title`, or `description`
|
|
28
|
+
|
|
29
|
+
Primary key fields:
|
|
30
|
+
- Display at the end of forms/lists
|
|
31
|
+
|
|
32
|
+
### Lookups (Foreign Keys)
|
|
33
|
+
|
|
34
|
+
For foreign key fields:
|
|
35
|
+
- Provide auto-complete dropdown (`<ReferenceInput>`)
|
|
36
|
+
- For numeric foreign keys, use the joined string field as lookup text
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Per-Resource Files (Required)
|
|
42
|
+
|
|
43
|
+
For each resource (`Customer`, `Order`, `Product`, `Item`):
|
|
44
|
+
- Create a source file under `src/`, e.g., `Customer.js`
|
|
45
|
+
- **Each file must fully implement**:
|
|
46
|
+
- `CustomerList`
|
|
47
|
+
- `CustomerShow`
|
|
48
|
+
- `CustomerCreate`
|
|
49
|
+
- `CustomerEdit`
|
|
50
|
+
|
|
51
|
+
Use:
|
|
52
|
+
- `<ReferenceField>` for foreign key displays
|
|
53
|
+
- `<ReferenceInput>` for foreign key input
|
|
54
|
+
- `<ReferenceManyField>` for tabbed child lists
|
|
55
|
+
- `<TabbedShowLayout>` for display pages
|
|
56
|
+
|
|
57
|
+
Do **not leave any file empty**.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## App Wiring
|
|
62
|
+
|
|
63
|
+
In `App.js`:
|
|
64
|
+
- Import each resource file
|
|
65
|
+
- Register them in `<Admin>` using:
|
|
66
|
+
|
|
67
|
+
```jsx
|
|
68
|
+
<Resource name="Customer" list={CustomerList} show={CustomerShow} edit={CustomerEdit} create={CustomerCreate} />
|
|
69
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
|
|
2
|
+
## Architecture
|
|
3
|
+
|
|
4
|
+
- **Framework**: React 18 + react-admin 4.x
|
|
5
|
+
- **Data Provider**: Custom `dataProvider.js` using `fetchUtils` (no external `ra-jsonapi-client`)
|
|
6
|
+
- Must support: `getList`, `getOne`, `getMany`, `getManyReference`, `create`, `update`, `delete`
|
|
7
|
+
- Must support: filters, joins, sorting, pagination
|
|
8
|
+
- **Backend**: JSON:API per `mcp_discovery.json`
|
|
9
|
+
- **CORS**: Ensure API allows `http://localhost:3000`
|
|
10
|
+
```py
|
|
11
|
+
from flask_cors import CORS
|
|
12
|
+
CORS(app, origins='*') # or restrict to localhost:3000
|
|
13
|
+
```
|
|
14
|
+
- **Project Setup**:
|
|
15
|
+
- Use `create-react-app`
|
|
16
|
+
- Include: `react-admin`, `@mui/material`, `@emotion/react`, `@emotion/styled`, `react-router-dom`
|
|
17
|
+
- Do not use any deprecated or unmaintained libraries
|
|
18
|
+
- Include complete and correct `App.js`, `index.js`, `dataProvider.js`, and `index.html`
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Run Instructions
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install
|
|
26
|
+
npm start
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Then open in browser: `http://localhost:3000`
|