ApiLogicServer 14.3.20__py3-none-any.whl → 14.4.0__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 (107) hide show
  1. api_logic_server_cli/api_logic_server.py +5 -14
  2. api_logic_server_cli/api_logic_server_info.yaml +3 -3
  3. api_logic_server_cli/cli.py +52 -5
  4. api_logic_server_cli/create_from_model/__pycache__/create_db_from_model.cpython-312.pyc +0 -0
  5. api_logic_server_cli/create_from_model/__pycache__/ont_build.cpython-312.pyc +0 -0
  6. api_logic_server_cli/create_from_model/__pycache__/ont_create.cpython-312.pyc +0 -0
  7. api_logic_server_cli/create_from_model/create_db_from_model.py +2 -0
  8. api_logic_server_cli/create_from_model/ont_build.py +19 -14
  9. api_logic_server_cli/create_from_model/ont_create.py +5 -5
  10. api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/static/.DS_Store +0 -0
  11. api_logic_server_cli/database/nw-gold-fix.sql +62 -0
  12. api_logic_server_cli/database/nw-gold.sqlite +0 -0
  13. api_logic_server_cli/fragments/docker-compose.yml +27 -0
  14. api_logic_server_cli/genai/genai.py +43 -11
  15. api_logic_server_cli/genai/genai_graphics.py +379 -0
  16. api_logic_server_cli/genai/genai_logic_builder.py +2 -2
  17. api_logic_server_cli/genai/genai_svcs.py +24 -8
  18. api_logic_server_cli/manager.py +19 -10
  19. api_logic_server_cli/prototypes/.DS_Store +0 -0
  20. api_logic_server_cli/prototypes/base/.DS_Store +0 -0
  21. api_logic_server_cli/prototypes/base/.vscode/launch.json +19 -0
  22. api_logic_server_cli/prototypes/base/api/expose_api_models.py +3 -1
  23. api_logic_server_cli/prototypes/base/api_logic_server_run.py +5 -2
  24. api_logic_server_cli/prototypes/base/config/activate_logicbank.py +1 -0
  25. api_logic_server_cli/prototypes/base/config/config.py +95 -24
  26. api_logic_server_cli/prototypes/base/config/logging.yml +1 -0
  27. api_logic_server_cli/prototypes/base/config/server_setup.py +33 -1
  28. api_logic_server_cli/prototypes/base/database/test_data/readme.md +3 -1
  29. api_logic_server_cli/prototypes/base/devops/docker-standard-image/docker-compose-standard-image.yml +7 -2
  30. api_logic_server_cli/prototypes/base/docs/graphics/readme.md +12 -0
  31. api_logic_server_cli/prototypes/base/docs/training/logic_bank_api.prompt +314 -0
  32. api_logic_server_cli/prototypes/base/docs/training/logic_example.py +41 -0
  33. api_logic_server_cli/prototypes/base/integration/kafka/kafka_producer.py +7 -3
  34. api_logic_server_cli/prototypes/base/integration/system/FlaskKafka.py +5 -1
  35. api_logic_server_cli/prototypes/base/security/authentication_provider/keycloak/auth_provider.py +1 -1
  36. api_logic_server_cli/prototypes/base/security/declare_security.py +4 -0
  37. api_logic_server_cli/prototypes/base/ui/admin/admin_loader.py +3 -1
  38. api_logic_server_cli/prototypes/base/ui/templates/bar_chart.jinja +64 -0
  39. api_logic_server_cli/prototypes/genai_demo/ui/admin/admin.yaml +1 -1
  40. api_logic_server_cli/prototypes/manager/README.md +56 -5
  41. api_logic_server_cli/prototypes/manager/system/genai/.DS_Store +0 -0
  42. api_logic_server_cli/prototypes/manager/system/genai/examples/.DS_Store +0 -0
  43. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/.DS_Store +0 -0
  44. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.prompt +0 -8
  45. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example +90 -60
  46. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_docs_logic/docs/002_create_db_models.prompt +4 -4
  47. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_docs_logic/docs/003_create_db_models.response +77 -47
  48. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_informal.prompt +1 -1
  49. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/dashboard_services.jinja +83 -0
  50. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/graphics_dashboard_WIP.py +34 -0
  51. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/graphics_services_api_xxx.py +32 -0
  52. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/graphics_services_db.jinja +46 -0
  53. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/graphics_services_db_each_method.jinja +36 -0
  54. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/html_template.jinja +76 -0
  55. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/index.html +19 -0
  56. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/sales_by_region.jinja +63 -0
  57. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/graphics.prompt +22 -0
  58. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/graphics_request.prompt +5 -0
  59. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/response_format.prompt +15 -0
  60. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/sqlite_inserts.prompt +2 -0
  61. api_logic_server_cli/prototypes/manager/system/install-ApiLogicServer-dev/install-ApiLogicServer-dev.ps1 +100 -0
  62. api_logic_server_cli/prototypes/manager/system/install-ApiLogicServer-dev/install-ApiLogicServer-dev.sh +116 -0
  63. api_logic_server_cli/prototypes/manager/system/install-ApiLogicServer-dev/readme.md +7 -0
  64. api_logic_server_cli/prototypes/manager/system/style-guide.yaml +2 -2
  65. api_logic_server_cli/prototypes/manager/webgenai/README.md +6 -0
  66. api_logic_server_cli/prototypes/nw/docs/graphics/count_orders_by_category.prompt +1 -0
  67. api_logic_server_cli/prototypes/nw/docs/graphics/order_count_by_month.prompt +1 -0
  68. api_logic_server_cli/prototypes/nw/docs/graphics/request copy.json +892 -0
  69. api_logic_server_cli/prototypes/nw/docs/graphics/request.json +6 -0
  70. api_logic_server_cli/prototypes/nw/docs/graphics/response.json +17 -0
  71. api_logic_server_cli/prototypes/nw/docs/graphics/response.yaml +59 -0
  72. api_logic_server_cli/prototypes/nw/docs/graphics/sales_by_category.prompt +1 -0
  73. api_logic_server_cli/prototypes/nw/ui/admin/home.js +5 -4
  74. api_logic_server_cli/prototypes/nw/ui/app_model_custom.yaml +851 -1082
  75. api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/count_orders_by_category.prompt +1 -0
  76. api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/request copy.json +892 -0
  77. api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/request.json +6 -0
  78. api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/response.json +17 -0
  79. api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/response.yaml +59 -0
  80. api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/sales_by_category.prompt +1 -0
  81. api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/sales_by_employee.prompt +1 -0
  82. api_logic_server_cli/prototypes/nw_no_cust/integration/mcp/1_langchain_loader.py +19 -0
  83. api_logic_server_cli/prototypes/nw_no_cust/integration/mcp/2_gpt_mcp_prompt.txt +19 -0
  84. api_logic_server_cli/prototypes/nw_no_cust/integration/mcp/3_executor_test_agent.py +38 -0
  85. api_logic_server_cli/prototypes/nw_no_cust/integration/mcp/README.md +17 -0
  86. api_logic_server_cli/prototypes/nw_no_cust/integration/mcp/resources/curl.txt +4 -0
  87. api_logic_server_cli/prototypes/nw_no_cust/integration/mcp/resources/nw_swagger_3.yaml +16660 -0
  88. api_logic_server_cli/prototypes/nw_no_cust/integration/mcp/run_executor.py +23 -0
  89. api_logic_server_cli/prototypes/ont_app/ontimize_seed/nginx/nginx.conf +2 -2
  90. api_logic_server_cli/prototypes/ont_app/ontimize_seed/package.json +6 -6
  91. api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/app/app.config.ts +2 -1
  92. api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/environments/environment.prod.ts +5 -5
  93. api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/environments/environment.ts +5 -5
  94. api_logic_server_cli/prototypes/ont_app/templates/app_config.jinja +1 -1
  95. api_logic_server_cli/prototypes/ont_app/templates/detail_template.html +1 -1
  96. api_logic_server_cli/prototypes/ont_app/templates/new_template.html +16 -16
  97. apilogicserver-14.4.0.dist-info/METADATA +76 -0
  98. {apilogicserver-14.3.20.dist-info → apilogicserver-14.4.0.dist-info}/RECORD +102 -59
  99. {apilogicserver-14.3.20.dist-info → apilogicserver-14.4.0.dist-info}/WHEEL +1 -1
  100. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/zsqlite_inserts_iterations.prompt +0 -29
  101. api_logic_server_cli/prototypes/manager/webgenai/docker-compose-webg.yml +0 -33
  102. api_logic_server_cli/prototypes/manager/webgenai/webg_config/license.json +0 -6
  103. api_logic_server_cli/prototypes/manager/webgenai/webg_config/web_genai.txt +0 -13
  104. apilogicserver-14.3.20.dist-info/METADATA +0 -167
  105. {apilogicserver-14.3.20.dist-info → apilogicserver-14.4.0.dist-info}/entry_points.txt +0 -0
  106. {apilogicserver-14.3.20.dist-info → apilogicserver-14.4.0.dist-info}/licenses/LICENSE +0 -0
  107. {apilogicserver-14.3.20.dist-info → apilogicserver-14.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,379 @@
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
+
26
+
27
+ K_data_model_prompt = "Use SQLAlchemy to create"
28
+
29
+ log = logging.getLogger(__name__)
30
+
31
+ class GenAIGraphics(object):
32
+ """ 4/3/2025
33
+ Adds Graphics to projects (genai project or als project):
34
+ * adds `database/database_discovery` file to project (methods on database.models classes)
35
+ * adds `api/api_discovery` file to project (dashboard services - calls db methods, above)
36
+ * adds `docs/graphics` prompt files to **wg** project (graphics prompt files)
37
+
38
+ Invoked from:
39
+ 1. **New GenAI Project:** for newly created project (e,g, mgr system/genai/examples/genai_demo/genai_demo.prompt)
40
+ * `--using` is None ==> Docs folder already has WGResponse.graphics[]
41
+ * see api_logic_server_cli/genai/genai_svcs.py#insert_logic_into_created_project
42
+ 2. **Existing Project:** `als genai-graphics [--using]` # existing project
43
+ * `--using` ==> Call ChatGPT for WGResponse.graphics, default = `<project>/docs/graphics/*.prompt`
44
+ * note: dbml not rebuilt after rebuild-from-db
45
+ 3. **Existing WG Project:** in-place (do not create new project with new test data)
46
+ * Same as #1, but requires WG UI change ('in place', 'graphics' button, ...) to use genai_graphics cmd
47
+
48
+ Testing:
49
+ * BLT to create manager
50
+ * Test from source: launch.json (in group 3) has `Add Graphics to blt/samples/nw...`
51
+ * Note: uses `<mgr>/system/genai/graphics_templates`
52
+ * Don't forget to copy these back to `api_logic_server_cli/prototypes/manager/system/genai/graphics_templates`
53
+ * Optionally: update `bypass_for_debug` to True to skip ChatGPT call
54
+
55
+ Persistence model for graphics: docs/response.json (also in docs/graphics/response.json, for als customization)
56
+ * Requirements:
57
+ 1. Fail-safe: do not let WG projects fail due to graphics - but alert user, just once
58
+ 2. Iterable: do not lose graphics on WG iteration
59
+ * Running design: 4/12
60
+ * Fail-safe: implemented in dashboard_service.py -- for each <graphic.name> query:
61
+ 1. if exists(docs/graphics/<graphics.name>.err), bypass the query
62
+ 2. wrap each dashboard_service query in a try/except block
63
+ 3. if exception,
64
+ * return "graphics failed" to iFrame so user can see it
65
+ * create docs/graphics/<graphics>.err to inhibit future calls
66
+ * For wg (--using == None):
67
+ * iterations build on docs/response.json, so graphics are preserved
68
+ * todo: verify add-rules
69
+ * this code creates docs/graphics/<graphics.name>.prompt
70
+ * preserves graphics when opening project in als mode
71
+ * ALS developers manage their own docs/graphics
72
+
73
+ Open Issues
74
+ * How to enforce licensing? eg, create stubbed api/api_discovery/dashboard_services.py
75
+ * How can user delete or alter the graphics? Activate the Project Summary Graphics button [replace]
76
+ * Graphics for wg projects (not showing)
77
+ * No Graphics (just shows {} )
78
+
79
+ """
80
+
81
+ def __init__(self, project: Project, using: str, genai_version: str, replace_with: str):
82
+ """
83
+ Add graphics to existing projects - [see docs](https://apilogicserver.github.io/Docs/WebGenAI-CLI/#add-graphics-to-existing-projects)
84
+
85
+ Called to inject graphics into existing project, by:
86
+ 1. genai#insert_logic for NEW WG projects (--replace_with = '!new-wg', using already-built docs dir)
87
+ 2. cli for EXISTING projects (add from docs/graphics, or update per replace_with)
88
+
89
+ Args:
90
+ project (Project): Project object
91
+ using (str): path to graphics prompt files (set by genai#insert_logic, or None, for existing project)
92
+ replace_with (str): (request type): '!using' (default), '!new-wg', '!delete', '!retry`, or '!request'
93
+ genai_version (str): GenAI version to use
94
+ """
95
+
96
+ self.project = project
97
+ self.using = using
98
+ self.manager_path = genai_svcs.get_manager_path()
99
+ self.start_time = time.time()
100
+ self.replace_with = replace_with
101
+ ''' '!using', '!new-wg', '!delete', '!retry`, or '!request Graph Sales... '''
102
+ graphics_response_path = self.project.project_directory_path.joinpath('docs/graphics/response.json') # assume existing project
103
+ if replace_with == '!new-wg': # if new webgenai, response already prepared here
104
+ graphics_response_path = self.project.project_directory_path.joinpath('docs/response.json')
105
+
106
+ log.info(f"\nGenAIGraphics start...")
107
+ log.info(f"... args:")
108
+ log.info(f"..... self.replace_with: {self.replace_with}")
109
+ log.info(f"..... self.using: {self.using}")
110
+ log.info(f"..... graphics_response_path: {graphics_response_path}")
111
+ log.info(f"..... self.project.project_directory_actual: {self.project.project_directory_actual}")
112
+ log.info(f"..... self.project.project_directory_path: {str(self.project.project_directory_path)}")
113
+
114
+ if self.replace_with != '!new-wg' and self.replace_with != '!using' : # update existing genai project
115
+ replaced_graphics = self.graphics_replace_with_in_existing_project()
116
+ if self.replace_with == '!delete': # we are done (else create docs/graphics prompts for processing below)
117
+ log.info(f"... update existing genai project - delete graphics")
118
+ return
119
+ log.info(f"... update genai existing project - from docs/graphics prompts with {replaced_graphics}")
120
+
121
+ if replace_with == '!new-wg':
122
+ log.info(f"... NEW WG project - already built: docs/002_create_db_models.prompt")
123
+ else:
124
+ log.info(f"... EXISTING project - process prompts in docs/graphics")
125
+ if bypass_for_debug := False:
126
+ pass # uses already-built docs/graphics/response.json
127
+ else:
128
+ prompt = genai_svcs.read_and_expand_prompt(self.manager_path.joinpath('system/genai/prompt_inserts/graphics_request.prompt'))
129
+ prompt_lines = prompt.split('\n') # ChatGPT instructions
130
+ prompt_lines.extend(self.append_data_model()) # add data model
131
+ prompt_lines.extend(self.append_graphics_files()) # and the users's requests from graphics files
132
+ prompt_str = "\n".join(prompt_lines)
133
+
134
+ prompt_messages : List[ Dict[str, str] ] = [] # prompt/response conversation to be sent to ChatGPT
135
+ prompt = genai_svcs.get_prompt_you_are()
136
+ prompt["content"] = prompt_str
137
+ prompt_messages.append( prompt )
138
+ genai_svcs.call_chatgpt(messages = prompt_messages,
139
+ using = self.project.project_directory_path.joinpath('docs/graphics'),
140
+ api_version=genai_version)
141
+
142
+ self.create_data_class_methods(graphics_response_path)
143
+ self.create_graphics_dashboard_service(graphics_response_path)
144
+ self.create_genai_graphics_prompts(graphics_response_path)
145
+ log.info(f"\ngenai-graphics completed in [{str(int(time.time() - self.start_time))} secs] \n")
146
+ pass
147
+
148
+ def create_data_class_methods(self, graphics_response_path: Path):
149
+ """ Process graphics response from ChatGPT docs/graphics/response.json
150
+ * 'graphics' attributes map directly (by name) to <mgr>/system/genai/graphics_templates
151
+ """
152
+
153
+ shutil.copy(self.manager_path.joinpath('system/genai/graphics_templates/graphics_services_db.jinja'),
154
+ self.project.project_directory_path.joinpath('database/database_discovery/graphics_services.py')) # all the db-class methods are created in this file
155
+
156
+ # open and read the graphics_response_path json file
157
+ assert graphics_response_path.exists(), f'Graphics response file not found: {graphics_response_path}'
158
+ with open(graphics_response_path, 'r') as file:
159
+ graphics_response = json.load(file)
160
+ log.info(f'... create_data_class_methods - from {graphics_response_path}')
161
+ graphics = graphics_response['graphics']
162
+ for each_graphic in graphics: # add each service to api/api_discovery
163
+ self.fix_sqlalchemy_query(each_graphic)
164
+ env = Environment(loader=FileSystemLoader(self.manager_path.joinpath('system/genai/graphics_templates')))
165
+
166
+ template = env.get_template('graphics_services_db_each_method.jinja')
167
+ rendered_result = template.render( **each_graphic )
168
+ with open(self.project.project_directory_path.joinpath(f'database/database_discovery/graphics_services.py'), 'a') as out_file:
169
+ out_file.write(rendered_result)
170
+
171
+ log.info(f'..... added db class method: {each_graphic['name']} to database_discovery')
172
+ pass
173
+
174
+
175
+ """ note it needs the /1 - what is that about?
176
+ curl -X 'GET' \
177
+ 'http://localhost:5656/api/Category/sales_by_category' \
178
+ -H 'accept: application/vnd.api+json' \
179
+ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTc0MzY5MTc3OCwianRpIjoiNDBlYzZkNGMtMzk4My00OGEwLTgxMjQtYzQwY2RmYWFiZWRhIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6InUxIiwibmJmIjoxNzQzNjkxNzc4LCJleHAiOjE3NDM3MDUwOTh9.WLDxdkp3PIsgUqR0t9-ymQDR0eOAECdQsgS_3YTqAQ0'
180
+ """
181
+
182
+ def create_graphics_dashboard_service(self, graphics_response_path: Path):
183
+ """ Process graphics response from ChatGPT graphics_response_path """
184
+
185
+ # open and read the graphics_response_path json file
186
+ assert graphics_response_path.exists(), f'Graphics response file not found: {graphics_response_path}'
187
+ with open(graphics_response_path, 'r') as file:
188
+ graphics_response = json.load(file)
189
+ if 'graphics' not in graphics_response:
190
+ log.error(f'No graphics found in {graphics_response_path}')
191
+ return
192
+ graphics = graphics_response['graphics']
193
+
194
+ env = Environment(loader=FileSystemLoader(self.manager_path.joinpath('system/genai/graphics_templates')))
195
+ iframe_templates= []
196
+ has_iframe = False
197
+ iframe_links = []
198
+ dashboards = []
199
+ cnt = 0
200
+ template = env.get_template('dashboard_services.jinja')
201
+ for each_graphic in graphics: # add each service to api/api_discovery
202
+ cnt += 1
203
+ server = '{server}'
204
+ iframe = f'iframe_{cnt} = iframe_template.format(url=f"{server}chart_graphics/{each_graphic['name']}")\n'
205
+ iframe_templates.append(iframe)
206
+ link = "{"+ f'iframe_{cnt}' + "}"
207
+ iframe_links.append(f'{link}')
208
+ sqlalchemy_query = each_graphic['class_x_axis']
209
+
210
+ # create the dashboard service query (skip if .err file exists, create .err file if query fails)
211
+ # typical failure: xxx
212
+ db = f"""
213
+ previously_failed = Path('docs/graphics/{each_graphic['name']}.err').exists()
214
+ if previously_failed:
215
+ pass # query has previously failed, so skip it
216
+ else:
217
+ try:
218
+ results = models.{sqlalchemy_query}.{each_graphic['name']}(None)
219
+ color = 'rgba(75, 192, 192, 0.2)'
220
+ dashboard{cnt} = template.render(result=results, color=color)
221
+ dashboard_result['{each_graphic['name']}']= dashboard{cnt}
222
+ except Exception as e:
223
+ msg = f"GenAI query creation error on models.{sqlalchemy_query}.{each_graphic['name']}: " + str(e)
224
+ dashboard_result['{each_graphic['name']}'] = msg
225
+ app_logger.error(msg)
226
+ with open('docs/graphics//{each_graphic['name']}.err', 'w') as err_file:
227
+ err_file.write(msg) # this logs the error to prevent future calls
228
+
229
+ """
230
+ dashboards.append(db)
231
+
232
+ rendered_result = template.render(iframe_templates=iframe_templates, iframe_links=" ".join(iframe_links), has_iframe=cnt > 0 , dashboards= dashboards)
233
+ with open(self.project.project_directory_path.joinpath(f'api/api_discovery/dashboard_services.py'), 'w') as out_file:
234
+ out_file.write(rendered_result)
235
+ log.info(f'... create_graphics_dashboard_service - created api/api_discovery/dashboard_services.py')
236
+
237
+ for each_graphic in graphics:
238
+ self.fix_sqlalchemy_query(each_graphic)
239
+ template = env.get_template('html_template.jinja')
240
+ rendered_result = template.render( **each_graphic )
241
+ with open(self.project.project_directory_path.joinpath(f'api/api_discovery/{each_graphic['name']}.html'), 'w') as out_file:
242
+ out_file.write(rendered_result)
243
+
244
+ with open(self.project.project_directory_path.joinpath(f'api/api_discovery/{each_graphic['name']}.sql'), 'w') as out_file:
245
+ sql_query = "System Error: missing sql_query - check WGResult format"
246
+ if 'sql_query' in each_graphic:
247
+ sql_query = each_graphic['sql_query']
248
+ out_file.write(sql_query)
249
+
250
+ log.info(f'..... added dashboard query: {each_graphic['name']} to api_discovery')
251
+
252
+ pass
253
+
254
+ def create_genai_graphics_prompts(self, graphics_response_path: Path):
255
+ """ if genai project, create graphics prompt file: docs/graphics/wg_graphics.prompt
256
+ """
257
+ if self.using is not None:
258
+ return # it's an als request, not a genai project (todo: confirm this works on iterations)
259
+
260
+ # open and read the graphics_response_path json file
261
+ assert graphics_response_path.exists(), f'Graphics response file not found: {graphics_response_path}'
262
+ with open(graphics_response_path, 'r') as file:
263
+ graphics_response = json.load(file)
264
+ log.info(f'... create_genai_graphics_prompts - from {graphics_response_path}')
265
+ graphics = graphics_response['graphics']
266
+ graphics_prompt = ''
267
+ graphics_prompt_count = 0
268
+ for each_graphic in graphics: # add each prompt to docs/graphics
269
+ graphics_prompt_count += 1
270
+ graphics_prompt += each_graphic['prompt'] + '\n'
271
+ with open(self.project.project_directory_path.joinpath(f'docs/graphics/wg_graphics.prompt'), 'w') as out_file:
272
+ out_file.write(graphics_prompt)
273
+ log.info(f'..... added docs/graphics/wg_graphics.prompt')
274
+ pass
275
+
276
+ def graphics_replace_with_in_existing_project(self) -> str:
277
+ """
278
+ Delete graphics for wg project (als projects - just delete the docs/graphics files)
279
+ 1. Update docs/*.prompt to remove lines starting with Graphics
280
+ * Prevents reappearance on iteration
281
+ 2. Presume (!) not necessary to delete the graphics[] in docs/*.response files
282
+ 3. If delete, rename the api/api_discovery/dashboard_services.py file so it won't be called
283
+
284
+ This does not rebuild / create a new project - operates on current project.
285
+
286
+ If self.replace_with is retry or request, build docs/graphics/wg_graphics.prompt
287
+ """
288
+
289
+ # for all docs/*.prompt files, alter lines where the first characters are 'Graph' (case insensitive)
290
+ docs_dir = self.project.project_directory_path.joinpath('docs')
291
+ replaced_count = 0
292
+ replaced_prompts = ''
293
+ log.info(f"... graphics_replace_with_in_existing_project - self.replace_with: {self.replace_with}")
294
+
295
+ for prompt_file in docs_dir.glob('*.prompt'):
296
+ with open(prompt_file, 'r') as file:
297
+ lines = file.readlines()
298
+ line_number = 0
299
+ with open(prompt_file, 'w') as file:
300
+ for line in lines:
301
+ line_number += 1
302
+ if not line.strip().lower().startswith('graph '):
303
+ file.write(line)
304
+ else:
305
+ replaced_prompts += line
306
+ replaced_count += 1
307
+ if self.replace_with == '!retry': # we are just doing retry - don't replace, use existing wg_graphics
308
+ file.write(line)
309
+ continue
310
+ elif self.replace_with.startswith('!request'): # replacing - replace ==> docs and doc/graphics
311
+ if replaced_count == 1:
312
+ file.write(self.replace_with + '\n')
313
+ graphics_prompt = self.replace_with[8:]
314
+ with open(self.project.project_directory_path.joinpath(f'docs/graphics/wg_graphics.prompt'), 'w') as out_file:
315
+ out_file.write(self.replace_with)
316
+ else:
317
+ assert self.replace_with == '!delete', f"genai_graphics - expected !delete, got: {self.replace_with}"
318
+ pass # removing line
319
+
320
+ log.info(f"..... completed - processed {replaced_count} graph line(s).")
321
+
322
+ # Stop graphics: rename the old dashboard_services.py file to dashboard_services.pyZ
323
+ dashboard_service_path = self.project.project_directory_path.joinpath('api/api_discovery/dashboard_services.py')
324
+ if dashboard_service_path.exists():
325
+ renamed_path = dashboard_service_path.with_suffix('.pyZ')
326
+ dashboard_service_path.rename(renamed_path)
327
+ log.info(f"..... Renamed existing dashboard_services.py to {renamed_path}")
328
+ else:
329
+ log.info(f'.. Note: {dashboard_service_path} not found')
330
+ return replaced_prompts
331
+
332
+ def fix_sqlalchemy_query(self, graphic: Dict):
333
+ """ Fix the SQLAlchemy query for the graphic """
334
+ graphic['sqlalchemy_query'] = graphic['sqlalchemy_query'].replace('\\n', '\n')
335
+ graphic['sqlalchemy_query'] = graphic['sqlalchemy_query'].replace('\"', '"')
336
+ graphic['sqlalchemy_query'] = graphic['sqlalchemy_query'].replace('.isnot', '.is_not')
337
+
338
+ # this part is for readability, not 'fixing'
339
+ graphic['sqlalchemy_query'] = graphic['sqlalchemy_query'].replace('session.query', '(session.query')
340
+ graphic['sqlalchemy_query'] = graphic['sqlalchemy_query'] + ')'
341
+ graphic['sqlalchemy_query'] = graphic['sqlalchemy_query'].replace(').', ')\n .')
342
+ pass
343
+
344
+ def append_data_model(self) -> List[str]:
345
+ """ Get the data model
346
+
347
+ Returns:
348
+ list[str]: the data model lines
349
+ """
350
+
351
+ data_model_lines = []
352
+ data_model_lines.extend(['Here is the data model - please use it to create the graphics:'])
353
+ data_model_path = self.project.project_directory_path.joinpath('database/models.py')
354
+ assert data_model_path.exists(), f"Data model file not found: {data_model_path}"
355
+ with open(data_model_path, 'r') as file:
356
+ prompt_lines = file.readlines()
357
+ data_model_lines.extend(prompt_lines)
358
+ data_model_lines.extend(['End of data model'])
359
+ return data_model_lines
360
+
361
+ def append_graphics_files(self) -> List[str]:
362
+ """ Get graphics files (typically from project/docs/graphics)
363
+ * 1 file per graphic
364
+
365
+ Returns:
366
+ list: logic_files
367
+ """
368
+
369
+ graphics_lines = []
370
+ if Path(self.using).is_dir(): # conversation from directory
371
+ for each_file in sorted(Path(self.using).iterdir()):
372
+ if each_file.is_file() and each_file.suffix == '.prompt':
373
+ # read lines from each_file, and append to prompt
374
+ with open(each_file, 'r') as file:
375
+ prompt_lines = file.readlines()
376
+ graphics_lines.extend(prompt_lines)
377
+ return graphics_lines
378
+
379
+
@@ -27,7 +27,7 @@ log = logging.getLogger(__name__)
27
27
 
28
28
  class GenAILogic(object):
29
29
  """
30
- Called by cli for *existing* project
30
+ Called by cli for *existing* project (must be wg project with docs models)
31
31
 
32
32
  * **Create logic** from *logic files* eg `<project>/docs/logic/check_credit.prompt`
33
33
 
@@ -113,7 +113,7 @@ class GenAILogic(object):
113
113
  return learning_requests # TODO - what if no learning requests?
114
114
 
115
115
  def get_learnings_and_data_model(self) -> List[Dict[str, str]]:
116
- """ Get prompts from the docs dir (so GPT knows model, learnings)
116
+ """ Get prompts from the docs dir (so GPT knows model, learnings) -- **not** from database/models.py
117
117
 
118
118
  Most often, adding logic to new project, which looks like:
119
119
 
@@ -42,6 +42,20 @@ class Model(BaseModel):
42
42
  description: str
43
43
  name: str
44
44
 
45
+ class Graphic(BaseModel):
46
+ sqlalchemy_query: str # sqlalchemy query using group by, returns result = { "result": [ ("name", "value") ] }
47
+ sql_query: str # sql query using group by, returns result = { "result": [ ("name", "value") ] }
48
+ classes_used: str # comma-delimited list of classes used in sqlalchemy_query
49
+ class_x_axis: str # name of class for x axis
50
+ name: str # suggested Python name for sqlalchemy_query - unique
51
+ prompt: str # prompt used to create the graphic
52
+ title: str # expanded name
53
+ xAxis: str # caption for x axis
54
+ yAxis: str # caption for y axis
55
+ dashboard: bool # whether appears on home page
56
+ graph_type: str # Bar, Line, Pie
57
+ html_code: str # create a java script app to show a bar chart from sqlalchemy_query result
58
+
45
59
  class TestDataRow(BaseModel):
46
60
  test_data_row_variable: str # the Python test data row variable
47
61
  code: str # Python code to create a test data row instance
@@ -50,6 +64,7 @@ class WGResult(BaseModel): # must match system/genai/prompt_inserts/response_fo
50
64
  # response: str # result
51
65
  models : List[Model] # list of sqlalchemy classes in the response
52
66
  rules : List[Rule] # list rule declarations
67
+ graphics: List[Graphic] # list of graphs
53
68
  test_data: str
54
69
  test_data_rows: List[TestDataRow] # list of test data rows
55
70
  test_data_sqlite: str # test data as sqlite INSERT statements
@@ -729,10 +744,10 @@ def get_create_prompt__with_inserts(arg_prompt_inserts: str='', raw_prompt: str=
729
744
  pre_post = file.read() # eg, Use SQLAlchemy to create a sqlite database named system/genai/temp/create_db_models.sqlite, with
730
745
  prompt_result = pre_post.replace('{{prompt}}', raw_prompt)
731
746
  if for_iteration:
732
- # Update the prior response - be sure not to lose classes and test data already created.
733
- prompt_result = 'Update the prior response - be sure not to lose classes and test data already created.' \
734
- + '\n\n' + prompt_result
735
- log.debug(f'.. iteration inserted: Update the prior response')
747
+ # Update the prior response - be sure not to lose classes, attributes, rules and test data already created.
748
+ iteration_prompt = read_and_expand_prompt(get_manager_path().joinpath(f'system/genai/prompt_inserts/iteration.prompt'))
749
+ prompt_result = iteration_prompt + '\n\n' + prompt_result
750
+ log.debug(f'.. iteration inserted: Update the prior response, using prompt_inserts/iteration.prompt')
736
751
  #log.debug(f'.... iteration prompt result: {prompt_result}')
737
752
 
738
753
  prompt_lines = prompt_result.split('\n')
@@ -758,10 +773,11 @@ def get_create_prompt__with_inserts(arg_prompt_inserts: str='', raw_prompt: str=
758
773
  do_logic = False
759
774
  prompt_line_number += 1
760
775
 
761
- response_format_file_name = get_manager_path().joinpath(f'system/genai/prompt_inserts/response_format.prompt')
762
- with open(response_format_file_name, 'r') as file:
763
- response_format = file.readlines()
764
- prompt_lines.extend(response_format)
776
+ if format_not_requested := False: # FIXME - double format definition on create; others??
777
+ response_format_file_name = get_manager_path().joinpath(f'system/genai/prompt_inserts/response_format.prompt')
778
+ with open(response_format_file_name, 'r') as file:
779
+ response_format = file.readlines()
780
+ prompt_lines.extend(response_format)
765
781
 
766
782
  prompt_result = "\n".join(prompt_lines) # back to a string
767
783
  pass
@@ -12,7 +12,7 @@ import api_logic_server_cli.api_logic_server as PR
12
12
 
13
13
  def create_manager(clean: bool, open_with: str, api_logic_server_path: Path,
14
14
  volume: str = "", open_manager: bool = True, samples: bool = True):
15
- """Implements als start to create manager - called from api_logic_server_cli/cli.py
15
+ """Implements `als start` to create manager - called from api_logic_server_cli/cli.py
16
16
 
17
17
  create Manager at os.getcwd(), including:
18
18
 
@@ -27,11 +27,11 @@ def create_manager(clean: bool, open_with: str, api_logic_server_path: Path,
27
27
  Bit tricky to find find cli in subdirectories of the lib path for manager run launches
28
28
 
29
29
  Args:
30
- clean (bool): _description_
31
- open_with (str): _description_
30
+ clean (bool): Overlay existing manager (projects and web_genai retained)
31
+ open_with (str): IDE to use
32
32
  api_logic_server_path (Path): _description_
33
33
  volume (str, optional): _description_. Defaults to "".
34
- open_manager (bool, optional): _description_. Defaults to True.
34
+ open_manager (bool, optional): Whether to open IDE at Manager. Defaults to True.
35
35
  """
36
36
 
37
37
  log = logging.getLogger(__name__)
@@ -68,8 +68,9 @@ def create_manager(clean: bool, open_with: str, api_logic_server_path: Path,
68
68
  docker_volume = volume + '/'
69
69
 
70
70
  to_dir = Path(os.getcwd())
71
+ """ location for creating (cleaning) the Manager """
71
72
  path = Path(__file__)
72
- from_dir = api_logic_server_path.joinpath('prototypes/manager')
73
+ from_dir_proto_mgr = api_logic_server_path.joinpath('prototypes/manager')
73
74
  to_dir_str = str(to_dir)
74
75
  to_dir_check = Path(to_dir).joinpath('venv')
75
76
  if not Path(to_dir).joinpath('venv').exists():
@@ -85,12 +86,12 @@ def create_manager(clean: bool, open_with: str, api_logic_server_path: Path,
85
86
  if to_dir_check.exists() and not clean:
86
87
  manager_exists = True
87
88
  if env_path.exists():
88
- log.info(f" Using manager at: {to_dir}\n\n")
89
+ log.info(f" Using existing manager (update .env APILOGICSERVER_AUTO_OPEN, only) at: {to_dir}\n\n")
89
90
  else:
90
91
  log.info(f" Refreshing .env in manager at: {to_dir}\n\n")
91
- copied_env = shutil.copy(src=from_dir.joinpath('settings.txt'), dst=to_dir.joinpath('.env'))
92
+ copied_env = shutil.copy(src=from_dir_proto_mgr.joinpath('settings.txt'), dst=to_dir.joinpath('.env'))
92
93
  os.remove(to_dir.joinpath('settings.txt'))
93
- else:
94
+ else: # new Manager, or clean existing manager
94
95
  mgr_save_level = log.level
95
96
  codegen_logger = logging.getLogger('sqlacodegen_wrapper.sqlacodegen.sqlacodegen.codegen')
96
97
  codegen_logger_save_level= codegen_logger.level
@@ -99,8 +100,16 @@ def create_manager(clean: bool, open_with: str, api_logic_server_path: Path,
99
100
  if to_dir_check.exists():
100
101
  log.info(f" Cleaning manager at: {to_dir}\n\n")
101
102
 
102
- copied_path = shutil.copytree(src=from_dir, dst=to_dir, dirs_exist_ok=True) # issue: not permitted
103
- copied_env = shutil.copy(src=from_dir.joinpath('settings.txt'), dst=to_dir.joinpath('.env'))
103
+ # create the manager system files and shell scripts (samples created below).
104
+ copied_path = shutil.copytree(src=from_dir_proto_mgr, dst=to_dir, dirs_exist_ok=True, ) # issue: not permitted
105
+ copied_env = shutil.copy(src=from_dir_proto_mgr.joinpath('settings.txt'), dst=to_dir.joinpath('.env'))
106
+ web_genai_docker = to_dir.joinpath('webgenai/docker-compose.yml')
107
+ if web_genai_docker.exists():
108
+ log.debug(' .. WebGenAI docker_compose unaltered (license preserved)')
109
+ else:
110
+ log.debug(' .. WebGenAI docker_compose created')
111
+ copied_env = shutil.copy(src=api_logic_server_path.joinpath('fragments/docker-compose.yml'),
112
+ dst=web_genai_docker)
104
113
  os.remove(to_dir.joinpath('settings.txt'))
105
114
  log.debug(f" .. created manager\n")
106
115
 
@@ -22,6 +22,25 @@
22
22
  "console": "internalConsole",
23
23
  "internalConsoleOptions": "openOnSessionStart"
24
24
  },
25
+ {
26
+ "name": "ApiLogicServer DEBUG",
27
+ "type": "debugpy",
28
+ "cwd": "${workspaceFolder}",
29
+ "env": {
30
+ "PYTHONPATH": "",
31
+ "EXPERIMENT": "",
32
+ "PYTHONHASHSEED": "0",
33
+ "APILOGICPROJECT_LOGGING_CONFIG": "config/logging.yml",
34
+ "APILOGICPROJECT_STOP_OK": "True",
35
+ "APILOGICPROJECT_DEBUG": "False"},
36
+ "request": "launch",
37
+ "program": "api_logic_server_run.py",
38
+ "redirectOutput": true,
39
+ "justMyCode": false,
40
+ "args": ["--flask_host=localhost", "--port=5656", "--swagger_port=5656", "--swagger_host=localhost", "--verbose=False"],
41
+ "console": "internalConsole",
42
+ "internalConsoleOptions": "openOnSessionStart"
43
+ },
25
44
  {
26
45
  "name": " - API Logic Server - VERBOSE",
27
46
  "type": "debugpy",
@@ -4,6 +4,8 @@ import importlib
4
4
  import pathlib
5
5
  import logging as logging
6
6
  import flask_sqlalchemy
7
+ from config.config import Args
8
+ from config import config
7
9
 
8
10
  # use absolute path import for easier multi-{app,model,db} support
9
11
  database = __import__('database')
@@ -40,7 +42,7 @@ def expose_models(api, method_decorators = []):
40
42
  """
41
43
 
42
44
  debug_inspect_list = inspect.getmembers(database.models)
43
- pass
45
+
44
46
  # Get all the subclasses of the Base class and expose them in the api
45
47
  for name, obj in inspect.getmembers(database.models):
46
48
  if inspect.isclass(obj) and issubclass(obj, database.models.SAFRSBaseX) and obj is not database.models.SAFRSBaseX:
@@ -31,7 +31,7 @@ import os, logging, logging.config, sys, yaml # failure here means venv probabl
31
31
  from flask_sqlalchemy import SQLAlchemy
32
32
  import json
33
33
  from pathlib import Path
34
- from config.config import Args
34
+ from config.config import Args # sets up logging
35
35
  from config import server_setup
36
36
 
37
37
  current_path = os.path.abspath(os.path.dirname(__file__))
@@ -68,8 +68,11 @@ import ui.admin.admin_loader as AdminLoader
68
68
  from security.system.authentication import configure_auth
69
69
  import oracledb
70
70
 
71
+ if os.getenv("EXPERIMENT") == '+':
72
+ app_logger = logging.getLogger("api_logic_server_app")
73
+ else:
74
+ app_logger = server_setup.logging_setup()
71
75
 
72
- app_logger = server_setup.logging_setup()
73
76
 
74
77
 
75
78
  # ==================================
@@ -40,6 +40,7 @@ def activate_logicbank(session, constraint_handler):
40
40
  app_logger.exception(e)
41
41
  if not os.environ.get("WG_PROJECT") == "True":
42
42
  # Continue if inside WebGenAI
43
+ # see: https://apilogicserver.github.io/Docs/WebGenAI-CLI/#wg_rules-and-ide-rules
43
44
  raise e
44
45
  logic_logger.setLevel(logic_logger_level)
45
46