ApiLogicServer 12.1.0__py3-none-any.whl → 12.2.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.
- {ApiLogicServer-12.1.0.dist-info → ApiLogicServer-12.2.0.dist-info}/METADATA +1 -1
- {ApiLogicServer-12.1.0.dist-info → ApiLogicServer-12.2.0.dist-info}/RECORD +47 -39
- {ApiLogicServer-12.1.0.dist-info → ApiLogicServer-12.2.0.dist-info}/WHEEL +1 -1
- api_logic_server_cli/api_logic_server.py +15 -4
- api_logic_server_cli/api_logic_server_info.yaml +3 -3
- api_logic_server_cli/cli.py +20 -9
- api_logic_server_cli/cli_args_base.py +2 -0
- api_logic_server_cli/cli_args_project.py +9 -3
- api_logic_server_cli/create_from_model/__pycache__/ont_create.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/__pycache__/ui_admin_creator.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/ont_create.py +3 -1
- api_logic_server_cli/create_from_model/ui_admin_creator.py +6 -4
- api_logic_server_cli/genai.py +387 -287
- api_logic_server_cli/logging.yml +5 -0
- api_logic_server_cli/prototypes/.DS_Store +0 -0
- api_logic_server_cli/prototypes/base/api_logic_server_run.py +0 -2
- api_logic_server_cli/prototypes/base/config/server_setup.py +15 -1
- api_logic_server_cli/prototypes/base/integration/kafka/kafka_consumer.py +1 -1
- api_logic_server_cli/prototypes/base/integration/kafka/kafka_producer.py +1 -1
- api_logic_server_cli/prototypes/base/readme.md +21 -8
- api_logic_server_cli/prototypes/manager/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/.vscode/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/.vscode/launch.json +20 -0
- api_logic_server_cli/prototypes/manager/README.md +25 -1
- api_logic_server_cli/prototypes/manager/system/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/create_db_models_inserts/create_db_models_create_db.py +1 -0
- api_logic_server_cli/prototypes/manager/system/genai/create_db_models_inserts/create_db_models_imports.py +10 -7
- api_logic_server_cli/prototypes/manager/system/genai/create_db_models_inserts/create_db_models_test_data.py +1 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example +99 -22
- api_logic_server_cli/prototypes/manager/system/genai/learning_requests/logic_bank_api.prompt +120 -7
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/response_format.prompt +26 -2
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/sqlite_inserts.prompt +10 -4
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/{sqlite_inserts_iterations.prompt → zsqlite_inserts_iterations.prompt} +5 -2
- api_logic_server_cli/prototypes/manager/system/genai/retry/conv/create_db_models.py +96 -0
- api_logic_server_cli/prototypes/manager/system/genai/retry/conv/inf-1_iter_1_1_000.response +1 -0
- api_logic_server_cli/prototypes/manager/system/genai/retry/conv/inf-1_iter_1_1_001.prompt +208 -0
- api_logic_server_cli/prototypes/manager/system/genai/retry/conv/inf-1_iter_1_1_002.prompt +89 -0
- api_logic_server_cli/prototypes/manager/system/genai/retry/conv/inf-1_iter_1_1_003.prompt +40 -0
- api_logic_server_cli/prototypes/manager/system/genai/retry/conv/inf-1_iter_1_1_004.response +57 -0
- api_logic_server_cli/prototypes/manager/system/genai/retry/conv/inf-1_iter_1_1_005.response +57 -0
- api_logic_server_cli/prototypes/manager/system/genai/retry/readme.md +1 -0
- api_logic_server_cli/prototypes/manager/system/genai/retry/retry.response +57 -0
- api_logic_server_cli/genaiZ.py +0 -752
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example_z +0 -130
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/web_genai copy.prompt +0 -15
- api_logic_server_cli/prototypes/manager/system/secrets.txt +0 -6
- {ApiLogicServer-12.1.0.dist-info → ApiLogicServer-12.2.0.dist-info}/LICENSE +0 -0
- {ApiLogicServer-12.1.0.dist-info → ApiLogicServer-12.2.0.dist-info}/entry_points.txt +0 -0
- {ApiLogicServer-12.1.0.dist-info → ApiLogicServer-12.2.0.dist-info}/top_level.txt +0 -0
api_logic_server_cli/genai.py
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import sys
|
|
2
3
|
import time
|
|
4
|
+
import traceback
|
|
3
5
|
from typing import Dict, List
|
|
4
6
|
from api_logic_server_cli.cli_args_project import Project
|
|
5
7
|
import logging
|
|
6
8
|
from pathlib import Path
|
|
7
9
|
import importlib
|
|
8
10
|
import requests
|
|
9
|
-
import os
|
|
11
|
+
import os,re
|
|
10
12
|
import create_from_model.api_logic_server_utils as utils
|
|
11
13
|
import shutil
|
|
12
14
|
import openai
|
|
@@ -14,25 +16,46 @@ from openai import OpenAI
|
|
|
14
16
|
from typing import List, Dict
|
|
15
17
|
from pydantic import BaseModel
|
|
16
18
|
from dotmap import DotMap
|
|
19
|
+
import importlib.util
|
|
17
20
|
|
|
18
21
|
log = logging.getLogger(__name__)
|
|
19
22
|
|
|
23
|
+
K_LogicBankOff = "LBX"
|
|
24
|
+
''' Disable Logic (for demos) '''
|
|
25
|
+
|
|
20
26
|
class Rule(BaseModel):
|
|
21
27
|
name: str
|
|
22
28
|
description: str
|
|
29
|
+
use_case: str
|
|
23
30
|
code: str # logicbank rule code
|
|
24
31
|
|
|
25
32
|
class Model(BaseModel):
|
|
26
33
|
classname: str
|
|
27
34
|
code: str # sqlalchemy model code
|
|
35
|
+
sqlite_create: str # sqlite create table statement
|
|
28
36
|
description: str
|
|
29
37
|
name: str
|
|
30
38
|
|
|
39
|
+
class TestDataRow(BaseModel):
|
|
40
|
+
test_data_row_variable: str # the Python test data row variable
|
|
41
|
+
code: str # Python code to create a test data row instance
|
|
42
|
+
|
|
31
43
|
class WGResult(BaseModel): # must match system/genai/prompt_inserts/response_format.prompt
|
|
32
44
|
# response: str # result
|
|
33
45
|
models : List[Model] # list of sqlalchemy classes in the response
|
|
34
46
|
rules : List[Rule] # list rule declarations
|
|
35
47
|
test_data: str
|
|
48
|
+
test_data_rows: List[TestDataRow] # list of test data rows
|
|
49
|
+
test_data_sqlite: str # test data as sqlite INSERT statements
|
|
50
|
+
name: str # suggest a short name for the project
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def import_module_from_path(module_name, file_path):
|
|
54
|
+
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
|
55
|
+
module = importlib.util.module_from_spec(spec)
|
|
56
|
+
sys.modules[module_name] = module
|
|
57
|
+
spec.loader.exec_module(module)
|
|
58
|
+
return module
|
|
36
59
|
|
|
37
60
|
|
|
38
61
|
class GenAI(object):
|
|
@@ -51,12 +74,11 @@ class GenAI(object):
|
|
|
51
74
|
The key argument is `--using`
|
|
52
75
|
* It can be a file, dir (conversation) or text argument.
|
|
53
76
|
* It's "stem" denotes the project name to be created at cwd
|
|
54
|
-
* `self.project.genai_using`
|
|
77
|
+
* `self.project.genai_using` (not used by WebGenAI)
|
|
55
78
|
|
|
56
|
-
The (rarely used) `--repaired_response`
|
|
79
|
+
The (rarely used) `--repaired_response` --> `self.project.genai_repaired_response`
|
|
57
80
|
* is for retry from corrected response
|
|
58
81
|
* `--using` is required to get the project name, to be created at cwd
|
|
59
|
-
* `self.project.genai_repaired_response`
|
|
60
82
|
|
|
61
83
|
__init__() is the main driver (work directory is <manager>/system/genai/temp/)
|
|
62
84
|
|
|
@@ -76,28 +98,10 @@ class GenAI(object):
|
|
|
76
98
|
|
|
77
99
|
see key_module_map() for key methods
|
|
78
100
|
|
|
79
|
-
|
|
80
|
-
##### Explore interim copilot access:
|
|
81
|
-
|
|
82
|
-
VSCode/Copilot-chat can turn prompts into logic, so can we automate with API?
|
|
83
|
-
|
|
84
|
-
https://stackoverflow.com/questions/76741410/how-to-invoke-github-copilot-programmatically
|
|
85
|
-
https://docs.google.com/document/d/1o0TeNQtuT6moWU1bOq2K20IbSw4YhV1x_aFnKwo_XeU/edit#heading=h.3xmoi7pevsnp
|
|
86
|
-
https://code.visualstudio.com/api/extension-guides/chat
|
|
87
|
-
https://code.visualstudio.com/api/extension-guides/language-model
|
|
88
|
-
https://github.com/B00TK1D/copilot-api
|
|
89
|
-
|
|
90
|
-
### Or use ChatGPT:
|
|
91
|
-
|
|
92
|
-
Not sure vscode/copilot is best approach, since we'd like to activate this during project creation
|
|
93
|
-
(eg on web/GenAI - not using vscode).
|
|
94
|
-
|
|
95
|
-
* Thomas suggests there are ways to "teach" ChatGPT about Logic Bank. This is a *great* idea.
|
|
96
|
-
|
|
97
|
-
https://platform.openai.com/docs/guides/fine-tuning/create-a-fine-tuned-model
|
|
101
|
+
https://platform.openai.com/finetune/ftjob-2i1wkh4t4l855NKCovJeHExs?filter=all
|
|
98
102
|
"""
|
|
99
103
|
|
|
100
|
-
self.project = project
|
|
104
|
+
self.project = project # als project info (cli args etc)
|
|
101
105
|
log.info(f'\nGenAI [{self.project.project_name}] creating microservice from: {self.project.genai_using}')
|
|
102
106
|
if self.project.genai_repaired_response != '':
|
|
103
107
|
log.info(f'.. retry from [repaired] response file: {self.project.genai_repaired_response}')
|
|
@@ -109,28 +113,39 @@ class GenAI(object):
|
|
|
109
113
|
""" eg, if response contains table defs, save_prompt_messages_to_system_genai_temp_project raises an exception to trigger retry """
|
|
110
114
|
self.prompt = ""
|
|
111
115
|
""" `--using` - can come from file or text argument """
|
|
112
|
-
|
|
116
|
+
self.logic_enabled = True
|
|
117
|
+
""" K_LogicBankOff is used for demos, where we don't want to create logic """
|
|
113
118
|
self.messages = self.get_prompt_messages() # compute self.messages, from file, dir or text argument
|
|
114
119
|
|
|
115
120
|
if self.project.genai_repaired_response == '': # normal path - get response from ChatGPT
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
#
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
121
|
+
try:
|
|
122
|
+
api_version = f'{self.project.genai_version}' # eg, "gpt-4o"
|
|
123
|
+
start_time = time.time()
|
|
124
|
+
db_key = os.getenv("APILOGICSERVER_CHATGPT_APIKEY")
|
|
125
|
+
client = OpenAI(api_key=os.getenv("APILOGICSERVER_CHATGPT_APIKEY"))
|
|
126
|
+
model = api_version
|
|
127
|
+
if model == "": # default from CLI is '', meaning fall back to env variable or system default...
|
|
128
|
+
model = os.getenv("APILOGICSERVER_CHATGPT_MODEL")
|
|
129
|
+
if model is None or model == "*": # system default chatgpt model
|
|
130
|
+
model = "gpt-4o-2024-08-06"
|
|
131
|
+
self.resolved_model = model
|
|
132
|
+
completion = client.beta.chat.completions.parse(
|
|
133
|
+
messages=self.messages, response_format=WGResult,
|
|
134
|
+
# temperature=self.project.genai_temperature, values .1 and .7 made students / charges fail
|
|
135
|
+
model=model # for own model, use "ft:gpt-4o-2024-08-06:personal:logicbank:ARY904vS"
|
|
136
|
+
)
|
|
137
|
+
log.debug(f'ChatGPT ({str(int(time.time() - start_time))} secs) - response at: system/genai/temp/chatgpt_original.response')
|
|
138
|
+
|
|
139
|
+
data = completion.choices[0].message.content
|
|
140
|
+
response_dict = json.loads(data)
|
|
141
|
+
self.get_and_save_raw_response_data(completion=completion, response_dict=response_dict)
|
|
142
|
+
# print(json.dumps(json.loads(data), indent=4))
|
|
143
|
+
pass
|
|
144
|
+
except Exception as inst:
|
|
145
|
+
log.error(f"\n\nError: ChatGPT call failed\n{inst}\n\n")
|
|
146
|
+
sys.exit('ChatGPT call failed - please see https://apilogicserver.github.io/Docs/WebGenAI-CLI/#configuration')
|
|
133
147
|
else: # for retry from corrected response... eg system/genai/temp/chatgpt_retry.response
|
|
148
|
+
self.resolved_model = "(n/a: model not used for repaired response)"
|
|
134
149
|
log.debug(f'\nUsing [corrected] response from: {self.project.genai_repaired_response}')
|
|
135
150
|
with open(self.project.genai_repaired_response, 'r') as response_file:
|
|
136
151
|
response_dict = json.load(response_file)
|
|
@@ -138,7 +153,7 @@ class GenAI(object):
|
|
|
138
153
|
self.response_dict = DotMap(response_dict)
|
|
139
154
|
""" the raw response data from ChatGPT which will be fixed & saved create_db_models.py """
|
|
140
155
|
|
|
141
|
-
|
|
156
|
+
self.get_valid_project_name()
|
|
142
157
|
|
|
143
158
|
self.fix_and_write_model_file() # write create_db_models.py for db creation, & logic
|
|
144
159
|
self.save_prompt_messages_to_system_genai_temp_project() # save prompts, response and models.py
|
|
@@ -154,13 +169,15 @@ class GenAI(object):
|
|
|
154
169
|
if self.project.genai_repaired_response == '': # clean up unless retrying from chatgpt_original.response
|
|
155
170
|
Path('system/genai/temp/chatgpt_original.response').unlink(missing_ok=True)
|
|
156
171
|
Path('system/genai/temp/chatgpt_retry.response').unlink(missing_ok=True)
|
|
157
|
-
Path('system/genai/temp/create_db_models.sqlite').unlink(missing_ok=True)
|
|
158
172
|
|
|
159
173
|
def create_presets(self, prompt_messages: List[Dict[str, str]]):
|
|
160
174
|
""" Create presets - you are a data modelling expert, and logicbank api etc """
|
|
161
175
|
pass
|
|
162
176
|
|
|
163
|
-
|
|
177
|
+
you_are = "You are a data modelling expert and python software architect who expands on user input ideas. You create data models with at least 4 tables"
|
|
178
|
+
if self.project.genai_tables > 0:
|
|
179
|
+
you_are = you_are.replace('4', str(self.project.genai_tables))
|
|
180
|
+
starting_message = {"role": "system", "content": you_are}
|
|
164
181
|
prompt_messages.append( starting_message)
|
|
165
182
|
|
|
166
183
|
learning_requests = self.get_learning_requests()
|
|
@@ -169,6 +186,28 @@ class GenAI(object):
|
|
|
169
186
|
log.debug(f'.. conv[000] presets: {starting_message}')
|
|
170
187
|
log.debug(f'.. conv[001] presets: {learning_requests[0]["content"][:30]}...')
|
|
171
188
|
return len(learning_requests)
|
|
189
|
+
|
|
190
|
+
def chatgpt_excp(self):
|
|
191
|
+
# https://apilogicserver.github.io/Docs/WebGenAI-CLI/
|
|
192
|
+
pass
|
|
193
|
+
|
|
194
|
+
def get_valid_project_name(self):
|
|
195
|
+
""" Get a valid project name from the project name
|
|
196
|
+
Takes a string and returns a valid filename constructed from the string.
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
# Replace invalid characters with underscores
|
|
200
|
+
valid_name = re.sub(r'[ \\/*?:"<>|\t\n\r\x0b\x0c]', '_', self.response_dict.name)
|
|
201
|
+
valid_name = valid_name.strip() # Remove leading and trailing spaces
|
|
202
|
+
valid_name = valid_name[:255] # Limit the filename length
|
|
203
|
+
if self.project.project_name == '_genai_default':
|
|
204
|
+
log.debug(f'.. project name: {valid_name} (from response: {self.response_dict.name})')
|
|
205
|
+
self.response_dict.name = valid_name
|
|
206
|
+
self.project.project_name = self.response_dict.name
|
|
207
|
+
self.project.project_name_last_node = self.response_dict.name
|
|
208
|
+
else:
|
|
209
|
+
self.project.directory_setup() # avoid names like "system/genai/temp/TBD"
|
|
210
|
+
return
|
|
172
211
|
|
|
173
212
|
def get_prompt_messages(self) -> List[Dict[str, str]]:
|
|
174
213
|
""" Get prompt from file, dir (conversation) or text argument
|
|
@@ -212,7 +251,7 @@ class GenAI(object):
|
|
|
212
251
|
response_count += 1
|
|
213
252
|
else:
|
|
214
253
|
request_count += 1 # rebuild response with *all* tables
|
|
215
|
-
if request_count >
|
|
254
|
+
if request_count > 2: # Run Config: genai AUTO DEALERSHIP CONVERSATION
|
|
216
255
|
if 'updating the prior response' not in prompt:
|
|
217
256
|
prompt = self.get_prompt__with_inserts(raw_prompt=prompt, for_iteration=True)
|
|
218
257
|
prompt_messages.append( {"role": role, "content": prompt})
|
|
@@ -271,27 +310,43 @@ class GenAI(object):
|
|
|
271
310
|
elif 'mysql' in self.project.db_url:
|
|
272
311
|
prompt_inserts = f'mysql_inserts.prompt'
|
|
273
312
|
|
|
274
|
-
if prompt_inserts
|
|
313
|
+
if prompt_inserts == "*":
|
|
314
|
+
pass # '*' means caller has computed their own prompt -- no inserts
|
|
315
|
+
else: # do prompt engineering (inserts)
|
|
275
316
|
prompt_eng_file_name = f'system/genai/prompt_inserts/{prompt_inserts}'
|
|
276
|
-
if for_iteration:
|
|
277
|
-
prompt_eng_file_name = prompt_eng_file_name.replace('.', '_iterations.')
|
|
278
317
|
assert Path(prompt_eng_file_name).exists(), \
|
|
279
318
|
f"Missing prompt_inserts file: {prompt_eng_file_name}" # eg api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/sqlite_inserts.prompt
|
|
280
|
-
log.debug(f'get_prompt__with_inserts: {str(os.getcwd())}
|
|
319
|
+
log.debug(f'get_prompt__with_inserts: {str(os.getcwd())} \n .. merged with: {prompt_eng_file_name}')
|
|
281
320
|
with open(prompt_eng_file_name, 'r') as file:
|
|
282
321
|
pre_post = file.read() # eg, Use SQLAlchemy to create a sqlite database named system/genai/temp/create_db_models.sqlite, with
|
|
283
322
|
prompt_result = pre_post.replace('{{prompt}}', raw_prompt)
|
|
323
|
+
if for_iteration:
|
|
324
|
+
# Update the prior response - be sure not to lose classes and test data already created.
|
|
325
|
+
prompt_result = 'Update the prior response - be sure not to lose classes and test data already created.' \
|
|
326
|
+
+ '\n\n' + prompt_result
|
|
327
|
+
log.debug(f'.. iteration inserted: Update the prior response')
|
|
328
|
+
log.debug(f'.... iteration prompt result: {prompt_result}')
|
|
284
329
|
|
|
285
330
|
prompt_lines = prompt_result.split('\n')
|
|
286
331
|
prompt_line_number = 0
|
|
332
|
+
do_logic = True
|
|
287
333
|
for each_line in prompt_lines:
|
|
288
|
-
if
|
|
334
|
+
if 'Create multiple rows of test data' in each_line:
|
|
335
|
+
if self.project.genai_test_data_rows > 0:
|
|
336
|
+
each_line = each_line.replace(
|
|
337
|
+
f'Create multiple rows',
|
|
338
|
+
f'Create {self.project.genai_test_data_rows} rows')
|
|
339
|
+
prompt_lines[prompt_line_number] = each_line
|
|
340
|
+
log.debug(f'.. inserted explicit test data: {each_line}')
|
|
341
|
+
if K_LogicBankOff in each_line:
|
|
342
|
+
self.logic_enabled = False # for demos
|
|
343
|
+
if "LogicBank" in each_line and do_logic == True:
|
|
289
344
|
log.debug(f'.. inserted: {each_line}')
|
|
290
345
|
prompt_eng_logic_file_name = f'system/genai/prompt_inserts/logic_inserts.prompt'
|
|
291
346
|
with open(prompt_eng_logic_file_name, 'r') as file:
|
|
292
347
|
prompt_logic = file.read() # eg, Use SQLAlchemy to...
|
|
293
348
|
prompt_lines[prompt_line_number] = prompt_logic
|
|
294
|
-
|
|
349
|
+
do_logic = False
|
|
295
350
|
prompt_line_number += 1
|
|
296
351
|
|
|
297
352
|
response_format_file_name = f'system/genai/prompt_inserts/response_format.prompt'
|
|
@@ -347,39 +402,6 @@ class GenAI(object):
|
|
|
347
402
|
logic_text += ' ' + each_line + '\n'
|
|
348
403
|
return logic_text
|
|
349
404
|
|
|
350
|
-
@staticmethod
|
|
351
|
-
def remove_logic_halluncinations(each_line: str) -> str:
|
|
352
|
-
"""remove hallucinations from logic
|
|
353
|
-
|
|
354
|
-
eg: Rule.setup()
|
|
355
|
-
|
|
356
|
-
Args:
|
|
357
|
-
each_line (str): _description_
|
|
358
|
-
|
|
359
|
-
Returns:
|
|
360
|
-
str: _description_
|
|
361
|
-
""" """ """
|
|
362
|
-
return_line = each_line
|
|
363
|
-
if each_line.startswith(' Rule.') or each_line.startswith(' DeclareRule.'):
|
|
364
|
-
if 'Rule.sum' in each_line:
|
|
365
|
-
pass
|
|
366
|
-
elif 'Rule.count' in each_line:
|
|
367
|
-
pass
|
|
368
|
-
elif 'Rule.formula' in each_line:
|
|
369
|
-
pass
|
|
370
|
-
elif 'Rule.copy' in each_line:
|
|
371
|
-
pass
|
|
372
|
-
elif 'Rule.constraint' in each_line:
|
|
373
|
-
pass
|
|
374
|
-
elif 'Rule.allocate' in each_line:
|
|
375
|
-
pass
|
|
376
|
-
elif 'Rule.calculate' in each_line:
|
|
377
|
-
return_line = each_line.replace('Rule.calculate', 'Rule.copy')
|
|
378
|
-
else:
|
|
379
|
-
return_line = each_line.replace(' ', ' # ')
|
|
380
|
-
log.debug(f'.. removed hallucination: {each_line}')
|
|
381
|
-
return return_line
|
|
382
|
-
|
|
383
405
|
def insert_logic_into_created_project(self): # TODO - redesign if conversation
|
|
384
406
|
"""Called *after project created* to insert prompt logic into
|
|
385
407
|
1. declare_logic.py (as comment)
|
|
@@ -388,19 +410,62 @@ class GenAI(object):
|
|
|
388
410
|
Also creates the doc directory for record of prompt, response.
|
|
389
411
|
"""
|
|
390
412
|
|
|
413
|
+
def remove_logic_halluncinations(each_line: str) -> str:
|
|
414
|
+
"""remove hallucinations from logic
|
|
415
|
+
|
|
416
|
+
eg: Rule.setup()
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
each_line (str): _description_
|
|
420
|
+
|
|
421
|
+
Returns:
|
|
422
|
+
str: _description_
|
|
423
|
+
""" """ """
|
|
424
|
+
return_line = each_line
|
|
425
|
+
if each_line.startswith('Rule.'):
|
|
426
|
+
# Sometimes indents left out (EmpDepts) - "code": "Rule.sum(derive=Department.salary_total, as_sum_of=Employee.salary)\nRule.constraint(validate=Department,\n as_condition=lambda row: row.salary_total <= row.budget,\n error_msg=\"Department salary total ({row.salary_total}) exceeds budget ({row.budget})\")"
|
|
427
|
+
each_line = " " + each_line # add missing indent
|
|
428
|
+
log.debug(f'.. fixed hallucination/indent: {each_line}')
|
|
429
|
+
if each_line.startswith(' Rule.') or each_line.startswith(' DeclareRule.'):
|
|
430
|
+
if 'Rule.sum' in each_line:
|
|
431
|
+
pass
|
|
432
|
+
elif 'Rule.count' in each_line:
|
|
433
|
+
pass
|
|
434
|
+
elif 'Rule.formula' in each_line:
|
|
435
|
+
pass
|
|
436
|
+
elif 'Rule.copy' in each_line:
|
|
437
|
+
pass
|
|
438
|
+
elif 'Rule.constraint' in each_line:
|
|
439
|
+
pass
|
|
440
|
+
elif 'Rule.allocate' in each_line:
|
|
441
|
+
pass
|
|
442
|
+
elif 'Rule.calculate' in each_line:
|
|
443
|
+
return_line = each_line.replace('Rule.calculate', 'Rule.copy')
|
|
444
|
+
else:
|
|
445
|
+
return_line = each_line.replace(' ', ' # ')
|
|
446
|
+
log.debug(f'.. removed hallucination: {each_line}')
|
|
447
|
+
return return_line
|
|
448
|
+
|
|
449
|
+
logic_enabled = True
|
|
391
450
|
logic_file = self.project.project_directory_path.joinpath('logic/declare_logic.py')
|
|
392
451
|
in_logic = False
|
|
393
452
|
translated_logic = "\n # Logic from GenAI: (or, use your IDE w/ code completion)\n"
|
|
394
453
|
for each_rule in self.response_dict.rules:
|
|
395
454
|
comment_line = each_rule.description
|
|
396
455
|
translated_logic += f'\n # {comment_line}\n'
|
|
397
|
-
|
|
398
|
-
if '
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
456
|
+
code_lines = each_rule.code.split('\n')
|
|
457
|
+
if '\n' in each_rule.code:
|
|
458
|
+
debug_string = "good breakpoint - multi-line rule"
|
|
459
|
+
for each_line in code_lines:
|
|
460
|
+
if 'declare_logic.py' not in each_line:
|
|
461
|
+
each_repaired_line = remove_logic_halluncinations(each_line=each_line)
|
|
462
|
+
if not each_repaired_line.startswith(' '): # sometimes in indents, sometimes not
|
|
463
|
+
each_repaired_line = ' ' + each_repaired_line
|
|
464
|
+
if 'def declare_logic' not in each_repaired_line:
|
|
465
|
+
translated_logic += each_repaired_line + '\n'
|
|
466
|
+
if self.logic_enabled == False:
|
|
467
|
+
translated_logic = "\n # Logic from GenAI: (or, use your IDE w/ code completion)\n"
|
|
468
|
+
translated_logic += "\n # LogicBank Disabled \n"
|
|
404
469
|
translated_logic += "\n # End Logic from GenAI\n\n"
|
|
405
470
|
utils.insert_lines_at(lines=translated_logic,
|
|
406
471
|
file_name=logic_file,
|
|
@@ -450,51 +515,209 @@ class GenAI(object):
|
|
|
450
515
|
response_data (str): the chatgpt response
|
|
451
516
|
|
|
452
517
|
"""
|
|
453
|
-
create_db_model_lines = list()
|
|
454
|
-
create_db_model_lines.extend(self.get_lines_from_file(f'system/genai/create_db_models_inserts/create_db_models_imports.py'))
|
|
455
518
|
|
|
519
|
+
def get_model_class_lines(model: DotMap) -> list[str]:
|
|
520
|
+
"""Get the model class from the model
|
|
521
|
+
|
|
522
|
+
Args:
|
|
523
|
+
model (Model): the model
|
|
524
|
+
|
|
525
|
+
Returns:
|
|
526
|
+
stlist[str]: the model class lines, fixed up
|
|
527
|
+
"""
|
|
528
|
+
|
|
529
|
+
create_db_model_lines = list()
|
|
530
|
+
create_db_model_lines.append('\n\n')
|
|
531
|
+
class_lines = model.code.split('\n')
|
|
532
|
+
line_num = 0
|
|
533
|
+
indents_to_remove = 0
|
|
534
|
+
for each_line in class_lines:
|
|
535
|
+
line_num += 1
|
|
536
|
+
''' decimal issues
|
|
537
|
+
|
|
538
|
+
1. bad import: see Run: tests/test_databases/ai-created/genai_demo/genai_demo_decimal
|
|
539
|
+
from decimal import Decimal # Decimal fix: needs to be from decimal import DECIMAL
|
|
540
|
+
|
|
541
|
+
2. Missing missing import: from SQLAlchemy import .... DECIMAL
|
|
542
|
+
|
|
543
|
+
3. Column(Decimal) -> Column(DECIMAL)
|
|
544
|
+
see in: tests/test_databases/ai-created/budget_allocation/budget_allocations/budget_allocations_3_decimal
|
|
545
|
+
|
|
546
|
+
4. Bad syntax on test data: see Run: blt/time_cards_decimal from RESPONSE
|
|
547
|
+
got: balance=DECIMAL('100.50')
|
|
548
|
+
needed: balance=1000.0
|
|
549
|
+
fixed with import in create_db_models_prefix.py
|
|
550
|
+
|
|
551
|
+
5. Bad syntax on test data cals: see api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_conversation_bad_decimal/genai_demo_03.response
|
|
552
|
+
got: or Decimal('0.00')
|
|
553
|
+
needed: or decimal.Decimal('0.00')
|
|
554
|
+
|
|
555
|
+
6. Bad syntax on test data cals: see api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_conversation_bad_decimal_2/genai_demo_conversation_002.response
|
|
556
|
+
got: or DECIMAL('
|
|
557
|
+
needed: or decimal.Decimal('0.00')
|
|
558
|
+
'''
|
|
559
|
+
if "= Table(" in each_line: # tests/test_databases/ai-created/time_cards/time_card_kw_arg/genai.response
|
|
560
|
+
log.debug(f'.. fix_and_write_model_file detects table - raise excp to trigger retry')
|
|
561
|
+
self.post_error = "ChatGPT Response contains table (not class) definitions: " + each_line
|
|
562
|
+
if 'sqlite:///' in each_line: # must be sqlite:///system/genai/temp/create_db_models.sqlite
|
|
563
|
+
current_url_rest = each_line.split('sqlite:///')[1]
|
|
564
|
+
quote_type = "'"
|
|
565
|
+
if '"' in current_url_rest:
|
|
566
|
+
quote_type = '"' # eg, tests/test_databases/ai-created/time_cards/time_card_decimal/genai.response
|
|
567
|
+
current_url = current_url_rest.split(quote_type)[0]
|
|
568
|
+
proper_url = 'system/genai/temp/create_db_models.sqlite'
|
|
569
|
+
each_line = each_line.replace(current_url, proper_url)
|
|
570
|
+
if current_url != proper_url:
|
|
571
|
+
log.debug(f'.. fixed sqlite url: {current_url} -> system/genai/temp/create_db_models.sqlite')
|
|
572
|
+
if 'Decimal,' in each_line: # SQLAlchemy import
|
|
573
|
+
each_line = each_line.replace('Decimal,', 'DECIMAL,')
|
|
574
|
+
# other Decimal bugs: see api_logic_server_cli/prototypes/manager/system/genai/reference/errors/chatgpt_decimal.txt
|
|
575
|
+
if ', Decimal' in each_line: # Cap'n K, at your service
|
|
576
|
+
each_line = each_line.replace(', Decimal', ', DECIMAL')
|
|
577
|
+
if 'rom decimal import Decimal' in each_line:
|
|
578
|
+
each_line = each_line.replace('from decimal import Decimal', 'import decimal')
|
|
579
|
+
if '=Decimal(' in each_line:
|
|
580
|
+
each_line = each_line.replace('=Decimal(', '=decimal.Decimal(')
|
|
581
|
+
if ' Decimal(' in each_line:
|
|
582
|
+
each_line = each_line.replace(' Decimal(', ' decimal.Decimal(')
|
|
583
|
+
if 'Column(Decimal)' in each_line:
|
|
584
|
+
each_line = each_line.replace('Column(Decimal)', 'Column(DECIMAL)')
|
|
585
|
+
if "DECIMAL('" in each_line:
|
|
586
|
+
each_line = each_line.replace("DECIMAL('", "decimal.Decimal('")
|
|
587
|
+
if 'end_time(datetime' in each_line: # tests/test_databases/ai-created/time_cards/time_card_kw_arg/genai.response
|
|
588
|
+
each_line = each_line.replace('end_time(datetime', 'end_time=datetime')
|
|
589
|
+
if indents_to_remove > 0:
|
|
590
|
+
each_line = each_line[indents_to_remove:]
|
|
591
|
+
if 'relationship(' in each_line and self.project.genai_use_relns == False:
|
|
592
|
+
# airport4 fails with could not determine join condition between parent/child tables on relationship Airport.flights
|
|
593
|
+
if each_line.startswith(' '):
|
|
594
|
+
each_line = each_line.replace(' ', ' # ')
|
|
595
|
+
else: # sometimes it puts relns outside the class (so, outdented)
|
|
596
|
+
each_line = '# ' + each_line
|
|
597
|
+
if 'sqlite:///system/genai/temp/model.sqlite': # fix prior version
|
|
598
|
+
each_line = each_line.replace('sqlite:///system/genai/temp/model.sqlite',
|
|
599
|
+
'sqlite:///system/genai/temp/create_db_models.sqlite')
|
|
600
|
+
|
|
601
|
+
# logicbank fixes
|
|
602
|
+
if 'from logic_bank' in each_line: # we do our own imports
|
|
603
|
+
each_line = each_line.replace('from', '# from')
|
|
604
|
+
if 'LogicBank.activate' in each_line:
|
|
605
|
+
each_line = each_line.replace('LogicBank.activate', '# LogicBank.activate')
|
|
606
|
+
|
|
607
|
+
create_db_model_lines.append(each_line + '\n')
|
|
608
|
+
return create_db_model_lines
|
|
609
|
+
|
|
610
|
+
def fix_model_lines(models, create_db_model_lines):
|
|
611
|
+
did_base = False
|
|
612
|
+
for each_model in models:
|
|
613
|
+
model_lines = get_model_class_lines(model=each_model)
|
|
614
|
+
for each_line in model_lines:
|
|
615
|
+
each_fixed_line = each_line.replace('sa.', '') # sometimes it puts sa. in front of Column
|
|
616
|
+
if 'Base = declarative_base()' in each_fixed_line: # sometimes created for each class
|
|
617
|
+
if did_base:
|
|
618
|
+
each_fixed_line = '# ' + each_fixed_line
|
|
619
|
+
did_base = True
|
|
620
|
+
if 'datetime.datetime.utcnow' in each_fixed_line:
|
|
621
|
+
each_fixed_line = each_fixed_line.replace('datetime.datetime.utcnow', 'datetime.now()')
|
|
622
|
+
if 'Column(date' in each_fixed_line:
|
|
623
|
+
each_fixed_line = each_fixed_line.replace('Column(dat', 'column(Date')
|
|
624
|
+
create_db_model_lines.append(each_fixed_line)
|
|
625
|
+
return create_db_model_lines
|
|
626
|
+
|
|
627
|
+
def insert_test_data_lines(test_data_lines : list[str]) -> list[str]:
|
|
628
|
+
"""Insert test data lines into the model file
|
|
629
|
+
|
|
630
|
+
Args:
|
|
631
|
+
test_data_lines (list(str)):
|
|
632
|
+
* initially header (engine =, sesssion =)
|
|
633
|
+
* this function appends CPT test data
|
|
634
|
+
|
|
635
|
+
Returns:
|
|
636
|
+
list[str]: variable names for the test data rows (for create_all)
|
|
637
|
+
"""
|
|
638
|
+
|
|
639
|
+
def fix_test_data_line(each_fixed_line: str) -> str:
|
|
640
|
+
"""Fix the test data line
|
|
641
|
+
|
|
642
|
+
Args:
|
|
643
|
+
each_fixed_line (str): the test data line
|
|
644
|
+
|
|
645
|
+
Returns:
|
|
646
|
+
str: the fixed test data line
|
|
647
|
+
"""
|
|
648
|
+
|
|
649
|
+
if '=datetime' in each_fixed_line:
|
|
650
|
+
each_fixed_line = each_fixed_line.replace('=datetime.date', '=date')
|
|
651
|
+
if 'datetime.datetime.utcnow' in each_fixed_line:
|
|
652
|
+
each_fixed_line = each_fixed_line.replace('datetime.datetime.utcnow', 'datetime.now()')
|
|
653
|
+
if 'engine = create_engine' in each_fixed_line: # CBT sometimes has engine = create_engine, so do we!
|
|
654
|
+
each_fixed_line = each_fixed_line.replace('engine = create_engine', '# engine = create_engine')
|
|
655
|
+
check_for_row_name = False
|
|
656
|
+
if each_fixed_line.startswith('Base') or each_fixed_line.startswith('engine'):
|
|
657
|
+
check_for_row_name = False
|
|
658
|
+
if 'Base.metadata.create_all(engine)' in each_fixed_line:
|
|
659
|
+
each_fixed_line = each_fixed_line.replace('Base.metadata.create_all(engine)', '# Base.metadata.create_all(engine)')
|
|
660
|
+
return each_fixed_line
|
|
661
|
+
|
|
662
|
+
row_names = list()
|
|
663
|
+
use_test_data_rows = True # CPT test data, new format - test_data_rows (*way* less variable)
|
|
664
|
+
if use_test_data_rows & hasattr(self.response_dict, 'test_data_rows'):
|
|
665
|
+
test_data_rows = self.response_dict.test_data_rows
|
|
666
|
+
log.debug(f'.... test_data_rows: {len(test_data_rows)}')
|
|
667
|
+
for each_row in test_data_rows:
|
|
668
|
+
each_fixed_line = fix_test_data_line(each_row.code)
|
|
669
|
+
test_data_lines.append(each_fixed_line)
|
|
670
|
+
row_names.append(each_row.test_data_row_variable)
|
|
671
|
+
pass
|
|
672
|
+
else: # CPT test data, old format - rows, plus session, engine etc (quite variable)
|
|
673
|
+
test_data_lines_ori = self.response_dict.test_data.split('\n') # gpt response
|
|
674
|
+
log.debug(f'.... test_data_lines...')
|
|
675
|
+
for each_line in test_data_lines_ori:
|
|
676
|
+
each_fixed_line = fix_test_data_line(each_line)
|
|
677
|
+
check_for_row_name = True
|
|
678
|
+
test_data_lines.append(each_fixed_line) # append the fixed test data line
|
|
679
|
+
if check_for_row_name and ' = ' in each_line and '(' in each_line: # CPT test data might have: tests = []
|
|
680
|
+
assign = each_line.split(' = ')[0]
|
|
681
|
+
# no tokens for: Session = sessionmaker(bind=engine) or session = Session()
|
|
682
|
+
if '.' not in assign and 'Session' not in each_line and 'session.' not in each_line:
|
|
683
|
+
row_names.append(assign)
|
|
684
|
+
return row_names
|
|
685
|
+
|
|
686
|
+
create_db_model_lines = list()
|
|
687
|
+
create_db_model_lines.append(f'# using resolved_model {self.resolved_model}')
|
|
688
|
+
create_db_model_lines.extend( # imports for classes (comes from api_logic_server_cli/prototypes/manager/system/genai/create_db_models_inserts/create_db_models_imports.py)
|
|
689
|
+
self.get_lines_from_file(f'system/genai/create_db_models_inserts/create_db_models_imports.py'))
|
|
690
|
+
create_db_model_lines.append("\nfrom sqlalchemy.dialects.sqlite import *\n") # specific for genai
|
|
691
|
+
|
|
456
692
|
models = self.response_dict.models
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
for each_line in model_lines:
|
|
461
|
-
each_fixed_line = each_line.replace('sa.', '') # sometimes it puts sa. in front of Column
|
|
462
|
-
if 'Base = declarative_base()' in each_fixed_line: # sometimes created for each class
|
|
463
|
-
if did_base:
|
|
464
|
-
each_fixed_line = '# ' + each_fixed_line
|
|
465
|
-
did_base = True
|
|
466
|
-
create_db_model_lines.append(each_fixed_line)
|
|
467
|
-
|
|
468
|
-
create_db_model_lines.extend(self.get_lines_from_file(f'system/genai/create_db_models_inserts/create_db_models_create_db.py'))
|
|
469
|
-
|
|
470
|
-
test_data_lines = self.response_dict.test_data.split('\n')
|
|
471
|
-
row_names = list()
|
|
472
|
-
for each_line in test_data_lines:
|
|
473
|
-
each_fixed_line = each_line
|
|
474
|
-
if '=datetime' in each_fixed_line:
|
|
475
|
-
each_fixed_line = each_fixed_line.replace('=datetime.date', '=date')
|
|
476
|
-
if 'engine = create_engine' in each_fixed_line: # CBT sometimes has engine = create_engine, so do we!
|
|
477
|
-
each_fixed_line = each_fixed_line.replace('engine = create_engine', '# engine = create_engine')
|
|
478
|
-
if 'Base.metadata.create_all(engine)e' in each_fixed_line:
|
|
479
|
-
each_fixed_line = each_fixed_line.replace('Base.metadata.create_all(engine)', '# Base.metadata.create_all(engine)')
|
|
480
|
-
create_db_model_lines.append(each_fixed_line + '\n')
|
|
481
|
-
if ' = ' in each_line and '(' in each_line: # CPT test data might have: tests = []
|
|
482
|
-
assign = each_line.split(' = ')[0]
|
|
483
|
-
# no tokens for: Session = sessionmaker(bind=engine) or session = Session()
|
|
484
|
-
if '.' not in assign and 'Session' not in each_line and 'session.' not in each_line:
|
|
485
|
-
row_names.append(assign)
|
|
486
|
-
|
|
487
|
-
create_db_model_lines.append('\n\n')
|
|
488
|
-
row_name_list = ', '.join(row_names)
|
|
489
|
-
add_rows = f'session.add_all([{row_name_list}])'
|
|
490
|
-
create_db_model_lines.append(add_rows + '\n')
|
|
491
|
-
create_db_model_lines.append('session.commit()\n')
|
|
693
|
+
|
|
694
|
+
# Usage inside the class
|
|
695
|
+
create_db_model_lines = fix_model_lines(models, create_db_model_lines)
|
|
492
696
|
|
|
493
697
|
with open(f'{self.project.from_model}', "w") as create_db_model_file:
|
|
494
|
-
|
|
495
|
-
|
|
698
|
+
create_db_model_file.write("".join(create_db_model_lines))
|
|
699
|
+
create_db_model_file.write("\n\n# end of model classes\n\n")
|
|
700
|
+
|
|
701
|
+
# classes done, create db and add test_data code
|
|
702
|
+
test_data_lines = self.get_lines_from_file(f'system/genai/create_db_models_inserts/create_db_models_create_db.py')
|
|
703
|
+
test_data_lines.append('session.commit()')
|
|
496
704
|
|
|
497
|
-
|
|
705
|
+
row_names = insert_test_data_lines(test_data_lines)
|
|
706
|
+
|
|
707
|
+
test_data_lines.append('\n\n')
|
|
708
|
+
row_name_list = ', '.join(row_names)
|
|
709
|
+
add_rows = f'session.add_all([{row_name_list}])'
|
|
710
|
+
test_data_lines.append(add_rows )
|
|
711
|
+
test_data_lines.append('session.commit()')
|
|
712
|
+
test_data_lines.append('# end of test data\n\n')
|
|
713
|
+
|
|
714
|
+
with open(f'{self.project.from_model}', "a") as create_db_model_file:
|
|
715
|
+
create_db_model_file.write("try:\n ")
|
|
716
|
+
create_db_model_file.write("\n ".join(test_data_lines))
|
|
717
|
+
create_db_model_file.write("except Exception as exc:\n")
|
|
718
|
+
create_db_model_file.write(" print(f'Test Data Error: {exc}')\n")
|
|
719
|
+
|
|
720
|
+
log.debug(f'.. code for db creation and test data: {self.project.from_model}')
|
|
498
721
|
|
|
499
722
|
def get_lines_from_file(self, file_name: str) -> list[str]:
|
|
500
723
|
"""Get lines from a file
|
|
@@ -510,105 +733,6 @@ class GenAI(object):
|
|
|
510
733
|
lines = file.readlines()
|
|
511
734
|
return lines
|
|
512
735
|
|
|
513
|
-
def get_model_test_data_lines_ZZ(self, model: DotMap) -> list[str]:
|
|
514
|
-
create_db_model_lines = list()
|
|
515
|
-
create_db_model_lines.append('\n\n')
|
|
516
|
-
sample_data = model.sample_data
|
|
517
|
-
for each_line in sample_data:
|
|
518
|
-
create_db_model_lines.append(each_line + '\n')
|
|
519
|
-
return create_db_model_lines
|
|
520
|
-
|
|
521
|
-
def get_model_class_lines(self, model: DotMap) -> list[str]:
|
|
522
|
-
"""Get the model class from the model
|
|
523
|
-
|
|
524
|
-
Args:
|
|
525
|
-
model (Model): the model
|
|
526
|
-
|
|
527
|
-
Returns:
|
|
528
|
-
stlist[str]: the model class lines, fixed up
|
|
529
|
-
"""
|
|
530
|
-
|
|
531
|
-
create_db_model_lines = list()
|
|
532
|
-
create_db_model_lines.append('\n\n')
|
|
533
|
-
class_lines = model.code.split('\n')
|
|
534
|
-
line_num = 0
|
|
535
|
-
indents_to_remove = 0
|
|
536
|
-
for each_line in class_lines:
|
|
537
|
-
line_num += 1
|
|
538
|
-
''' decimal issues
|
|
539
|
-
|
|
540
|
-
1. bad import: see Run: tests/test_databases/ai-created/genai_demo/genai_demo_decimal
|
|
541
|
-
from decimal import Decimal # Decimal fix: needs to be from decimal import DECIMAL
|
|
542
|
-
|
|
543
|
-
2. Missing missing import: from SQLAlchemy import .... DECIMAL
|
|
544
|
-
|
|
545
|
-
3. Column(Decimal) -> Column(DECIMAL)
|
|
546
|
-
see in: tests/test_databases/ai-created/budget_allocation/budget_allocations/budget_allocations_3_decimal
|
|
547
|
-
|
|
548
|
-
4. Bad syntax on test data: see Run: blt/time_cards_decimal from RESPONSE
|
|
549
|
-
got: balance=DECIMAL('100.50')
|
|
550
|
-
needed: balance=1000.0
|
|
551
|
-
fixed with import in create_db_models_prefix.py
|
|
552
|
-
|
|
553
|
-
5. Bad syntax on test data cals: see api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_conversation_bad_decimal/genai_demo_03.response
|
|
554
|
-
got: or Decimal('0.00')
|
|
555
|
-
needed: or decimal.Decimal('0.00')
|
|
556
|
-
|
|
557
|
-
6. Bad syntax on test data cals: see api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_conversation_bad_decimal_2/genai_demo_conversation_002.response
|
|
558
|
-
got: or DECIMAL('
|
|
559
|
-
needed: or decimal.Decimal('0.00')
|
|
560
|
-
'''
|
|
561
|
-
if "= Table(" in each_line: # tests/test_databases/ai-created/time_cards/time_card_kw_arg/genai.response
|
|
562
|
-
log.debug(f'.. fix_and_write_model_file detects table - raise excp to trigger retry')
|
|
563
|
-
self.post_error = "ChatGPT Response contains table (not class) definitions: " + each_line
|
|
564
|
-
if 'sqlite:///' in each_line: # must be sqlite:///system/genai/temp/create_db_models.sqlite
|
|
565
|
-
current_url_rest = each_line.split('sqlite:///')[1]
|
|
566
|
-
quote_type = "'"
|
|
567
|
-
if '"' in current_url_rest:
|
|
568
|
-
quote_type = '"' # eg, tests/test_databases/ai-created/time_cards/time_card_decimal/genai.response
|
|
569
|
-
current_url = current_url_rest.split(quote_type)[0]
|
|
570
|
-
proper_url = 'system/genai/temp/create_db_models.sqlite'
|
|
571
|
-
each_line = each_line.replace(current_url, proper_url)
|
|
572
|
-
if current_url != proper_url:
|
|
573
|
-
log.debug(f'.. fixed sqlite url: {current_url} -> system/genai/temp/create_db_models.sqlite')
|
|
574
|
-
if 'Decimal,' in each_line: # SQLAlchemy import
|
|
575
|
-
each_line = each_line.replace('Decimal,', 'DECIMAL,')
|
|
576
|
-
# other Decimal bugs: see api_logic_server_cli/prototypes/manager/system/genai/reference/errors/chatgpt_decimal.txt
|
|
577
|
-
if ', Decimal' in each_line: # Cap'n K, at your service
|
|
578
|
-
each_line = each_line.replace(', Decimal', ', DECIMAL')
|
|
579
|
-
if 'rom decimal import Decimal' in each_line:
|
|
580
|
-
each_line = each_line.replace('from decimal import Decimal', 'import decimal')
|
|
581
|
-
if '=Decimal(' in each_line:
|
|
582
|
-
each_line = each_line.replace('=Decimal(', '=decimal.Decimal(')
|
|
583
|
-
if ' Decimal(' in each_line:
|
|
584
|
-
each_line = each_line.replace(' Decimal(', ' decimal.Decimal(')
|
|
585
|
-
if 'Column(Decimal)' in each_line:
|
|
586
|
-
each_line = each_line.replace('Column(Decimal)', 'Column(DECIMAL)')
|
|
587
|
-
if "DECIMAL('" in each_line:
|
|
588
|
-
each_line = each_line.replace("DECIMAL('", "decimal.Decimal('")
|
|
589
|
-
if 'end_time(datetime' in each_line: # tests/test_databases/ai-created/time_cards/time_card_kw_arg/genai.response
|
|
590
|
-
each_line = each_line.replace('end_time(datetime', 'end_time=datetime')
|
|
591
|
-
if indents_to_remove > 0:
|
|
592
|
-
each_line = each_line[indents_to_remove:]
|
|
593
|
-
if 'relationship(' in each_line and self.project.genai_use_relns == False:
|
|
594
|
-
# airport4 fails with could not determine join condition between parent/child tables on relationship Airport.flights
|
|
595
|
-
if each_line.startswith(' '):
|
|
596
|
-
each_line = each_line.replace(' ', ' # ')
|
|
597
|
-
else: # sometimes it puts relns outside the class (so, outdented)
|
|
598
|
-
each_line = '# ' + each_line
|
|
599
|
-
if 'sqlite:///system/genai/temp/model.sqlite': # fix prior version
|
|
600
|
-
each_line = each_line.replace('sqlite:///system/genai/temp/model.sqlite',
|
|
601
|
-
'sqlite:///system/genai/temp/create_db_models.sqlite')
|
|
602
|
-
|
|
603
|
-
# logicbank fixes
|
|
604
|
-
if 'from logic_bank' in each_line: # we do our own imports
|
|
605
|
-
each_line = each_line.replace('from', '# from')
|
|
606
|
-
if 'LogicBank.activate' in each_line:
|
|
607
|
-
each_line = each_line.replace('LogicBank.activate', '# LogicBank.activate')
|
|
608
|
-
|
|
609
|
-
create_db_model_lines.append(each_line + '\n')
|
|
610
|
-
return create_db_model_lines
|
|
611
|
-
|
|
612
736
|
def save_prompt_messages_to_system_genai_temp_project(self):
|
|
613
737
|
"""
|
|
614
738
|
Save prompts / responses to system/genai/temp/{project}/genai.response
|
|
@@ -657,37 +781,10 @@ class GenAI(object):
|
|
|
657
781
|
log.error(f"\n\nError: {inst} \n..creating diagnostic files into dir: {str(gen_temp_dir)}\n\n")
|
|
658
782
|
pass # intentional try/catch/bury - it's just diagnostics, so don't fail
|
|
659
783
|
debug_string = "good breakpoint - return to main driver, and execute create_db_models.py"
|
|
660
|
-
|
|
661
|
-
def get_headers_with_openai_api_key_ZZ(self) -> dict:
|
|
662
|
-
"""
|
|
663
|
-
Returns:
|
|
664
|
-
dict: api header with OpenAI key (exits if not provided)
|
|
665
|
-
"""
|
|
666
|
-
|
|
667
|
-
pass # https://community.openai.com/t/how-do-i-call-chatgpt-api-with-python-code/554554
|
|
668
|
-
if os.getenv('APILOGICSERVER_CHATGPT_APIKEY'):
|
|
669
|
-
openai_api_key = os.getenv('APILOGICSERVER_CHATGPT_APIKEY')
|
|
670
|
-
else:
|
|
671
|
-
from dotenv import dotenv_values
|
|
672
|
-
secrets = dotenv_values("system/secrets.txt")
|
|
673
|
-
openai_api_key = secrets['APILOGICSERVER_CHATGPT_APIKEY']
|
|
674
|
-
if openai_api_key == 'your-api-key-here':
|
|
675
|
-
if os.getenv('APILOGICSERVER_CHATGPT_APIKEY'):
|
|
676
|
-
openai_api_key = os.getenv('APILOGICSERVER_CHATGPT_APIKEY')
|
|
677
|
-
else:
|
|
678
|
-
log.error("\n\nMissing env value: APILOGICSERVER_CHATGPT_APIKEY")
|
|
679
|
-
log.error("... Check your system/secrets file...\n")
|
|
680
|
-
exit(1)
|
|
681
|
-
headers = {
|
|
682
|
-
"Content-Type": "application/json",
|
|
683
|
-
"Authorization": f"Bearer {openai_api_key}"
|
|
684
|
-
}
|
|
685
|
-
return headers
|
|
686
784
|
|
|
687
785
|
def get_and_save_raw_response_data(self, completion: object, response_dict: dict):
|
|
688
786
|
"""
|
|
689
|
-
|
|
690
|
-
str: response_data
|
|
787
|
+
Write prompt --> system/genai/temp/chatgpt_original/retry.response
|
|
691
788
|
"""
|
|
692
789
|
|
|
693
790
|
''' TODO - is exception used instead of return_code...
|
|
@@ -698,7 +795,6 @@ class GenAI(object):
|
|
|
698
795
|
if completion.status_code != 200:
|
|
699
796
|
print("Error:", completion.status_code, completion.text) # eg, You exceeded your current quota
|
|
700
797
|
'''
|
|
701
|
-
|
|
702
798
|
with open(f'system/genai/temp/chatgpt_original.response', "w") as response_file: # save for debug
|
|
703
799
|
json.dump(response_dict, response_file, indent=4)
|
|
704
800
|
with open(f'system/genai/temp/chatgpt_retry.response', "w") as response_file: # repair this & retry
|
|
@@ -706,10 +802,11 @@ class GenAI(object):
|
|
|
706
802
|
return
|
|
707
803
|
|
|
708
804
|
|
|
709
|
-
def
|
|
805
|
+
def genai_cli_retry(using: str, db_url: str, repaired_response: str, genai_version: str,
|
|
710
806
|
retries: int, opt_locking: str, prompt_inserts: str, quote: bool,
|
|
711
|
-
use_relns: bool, project_name: str
|
|
712
|
-
|
|
807
|
+
use_relns: bool, project_name: str, tables: int, test_data_rows: int,
|
|
808
|
+
temperature: float) -> None:
|
|
809
|
+
""" CLI Caller: provides using, or repaired_response & using
|
|
713
810
|
|
|
714
811
|
Called from cli commands: genai, genai-create, genai-iterate
|
|
715
812
|
|
|
@@ -720,46 +817,48 @@ def genai(using, db_url, repaired_response: bool, genai_version: str,
|
|
|
720
817
|
import api_logic_server_cli.api_logic_server as PR
|
|
721
818
|
|
|
722
819
|
resolved_project_name = project_name
|
|
723
|
-
if
|
|
724
|
-
resolved_project_name
|
|
820
|
+
if repaired_response != "":
|
|
821
|
+
if resolved_project_name == '' or resolved_project_name is None:
|
|
822
|
+
resolved_project_name = Path(using).stem # project dir is the <cwd>/last node of using
|
|
725
823
|
resolved_project_name = resolved_project_name.replace(' ', '_')
|
|
824
|
+
start_time = time.time()
|
|
726
825
|
|
|
727
826
|
try_number = 1
|
|
728
827
|
genai_use_relns = use_relns
|
|
729
828
|
""" if 'unable to determine join condition', we retry this with False """
|
|
730
829
|
if repaired_response != "":
|
|
731
830
|
try_number = retries # if not calling GenAI, no need to retry:
|
|
732
|
-
|
|
831
|
+
failed = False
|
|
832
|
+
pr = PR.ProjectRun(command="create",
|
|
833
|
+
genai_version=genai_version,
|
|
834
|
+
genai_temperature = temperature,
|
|
835
|
+
genai_using=using, # the prompt file, or dir of prompt/response
|
|
836
|
+
repaired_response=repaired_response, # retry from [repaired] response file
|
|
837
|
+
opt_locking=opt_locking,
|
|
838
|
+
genai_prompt_inserts=prompt_inserts,
|
|
839
|
+
genai_use_relns=genai_use_relns,
|
|
840
|
+
quote=quote,
|
|
841
|
+
genai_tables=tables,
|
|
842
|
+
genai_test_data_rows=test_data_rows,
|
|
843
|
+
project_name=resolved_project_name, db_url=db_url,
|
|
844
|
+
execute=False)
|
|
733
845
|
if retries < 0: # for debug: catch exceptions at point of failure
|
|
734
|
-
|
|
735
|
-
genai_using=using, # the prompt file, or conversation dir
|
|
736
|
-
repaired_response=repaired_response, # retry from [repaired] response file
|
|
737
|
-
opt_locking=opt_locking,
|
|
738
|
-
genai_prompt_inserts=prompt_inserts,
|
|
739
|
-
genai_use_relns=genai_use_relns,
|
|
740
|
-
quote=quote,
|
|
741
|
-
project_name=resolved_project_name, db_url=db_url)
|
|
846
|
+
pr.create_project() # calls GenAI() - the main driver
|
|
742
847
|
log.info(f"GENAI successful")
|
|
743
848
|
else:
|
|
744
|
-
failed = False
|
|
745
849
|
while try_number <= retries:
|
|
746
850
|
try:
|
|
747
851
|
failed = False
|
|
748
|
-
|
|
749
|
-
genai_using=using, # the prompt file, or dir of prompt/response
|
|
750
|
-
repaired_response=repaired_response, # retry from [repaired] response file
|
|
751
|
-
opt_locking=opt_locking,
|
|
752
|
-
genai_prompt_inserts=prompt_inserts,
|
|
753
|
-
genai_use_relns=genai_use_relns,
|
|
754
|
-
quote=quote,
|
|
755
|
-
project_name=resolved_project_name, db_url=db_url)
|
|
852
|
+
pr.create_project() # calls GenAI() - the main driver
|
|
756
853
|
if do_force_failure := False:
|
|
757
854
|
if try_number < 3:
|
|
758
855
|
raise Exception("Forced Failure for Internal Testing")
|
|
759
856
|
break # success - exit the loop
|
|
760
857
|
except Exception as e: # almost certaily in api_logic_server_cli/create_from_model/create_db_from_model.py
|
|
858
|
+
log.error(traceback.format_exc())
|
|
761
859
|
log.error(f"\n\nGenai failed With Error: {e}")
|
|
762
|
-
|
|
860
|
+
if resolved_project_name == '_genai_default':
|
|
861
|
+
resolved_project_name = pr.project_name # defaulted in genai from response
|
|
763
862
|
if Path(using).is_dir():
|
|
764
863
|
log.debug('conversation dir, check in-place iteration')
|
|
765
864
|
'''
|
|
@@ -819,10 +918,11 @@ def genai(using, db_url, repaired_response: bool, genai_version: str,
|
|
|
819
918
|
try_number += 1
|
|
820
919
|
log.debug(f"\n\nRetry Genai #{try_number}\n")
|
|
821
920
|
pass # retry (retries times)
|
|
822
|
-
if failed == True:
|
|
921
|
+
if failed == True: # retries exhausted (if failed: threw "an integer is required" ??
|
|
922
|
+
pass # https://github.com/microsoft/debugpy/issues/1708
|
|
823
923
|
log.error(f"\n\nGenai Failed (Retries: {retries})")
|
|
824
924
|
exit(1)
|
|
825
|
-
log.info(f"
|
|
925
|
+
log.info(f"\nGENAI ({str(int(time.time() - start_time))} secs) successful on try {try_number}\n")
|
|
826
926
|
|
|
827
927
|
|
|
828
928
|
def key_module_map():
|
|
@@ -830,7 +930,7 @@ def key_module_map():
|
|
|
830
930
|
import api_logic_server_cli.api_logic_server as als
|
|
831
931
|
import api_logic_server_cli.create_from_model.create_db_from_model as create_db_from_model
|
|
832
932
|
|
|
833
|
-
|
|
933
|
+
genai_cli_retry() # called from cli.genai for retries
|
|
834
934
|
# try/catch/retry loop!
|
|
835
935
|
als.ProjectRun() # calls api_logic_server.ProjectRun
|
|
836
936
|
|