ApiLogicServer 12.0.4__py3-none-any.whl → 12.1.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.0.4.dist-info → ApiLogicServer-12.1.0.dist-info}/METADATA +2 -1
- {ApiLogicServer-12.0.4.dist-info → ApiLogicServer-12.1.0.dist-info}/RECORD +39 -24
- {ApiLogicServer-12.0.4.dist-info → ApiLogicServer-12.1.0.dist-info}/WHEEL +1 -1
- api_logic_server_cli/api_logic_server.py +8 -201
- api_logic_server_cli/api_logic_server_info.yaml +3 -3
- api_logic_server_cli/cli.py +9 -79
- api_logic_server_cli/create_from_model/__pycache__/dbml.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/__pycache__/meta_model.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/__pycache__/model_creation_services.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/meta_model.py +1 -1
- api_logic_server_cli/create_from_model/model_creation_services.py +3 -1
- api_logic_server_cli/genai.py +273 -151
- api_logic_server_cli/genaiZ.py +752 -0
- api_logic_server_cli/prototypes/genai_demo/logic/declare_logic.py +11 -12
- api_logic_server_cli/prototypes/manager/.vscode/launch.json +78 -1
- api_logic_server_cli/prototypes/manager/README.md +16 -3
- api_logic_server_cli/prototypes/manager/system/genai/create_db_models_inserts/create_db_models_create_db.py +10 -0
- api_logic_server_cli/prototypes/manager/system/genai/create_db_models_inserts/create_db_models_imports.py +19 -0
- api_logic_server_cli/prototypes/manager/system/genai/create_db_models_inserts/create_db_models_test_data.py +7 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/emp_depts/emp_dept.prompt +4 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.prompt +1 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example +56 -130
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example_z +130 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_informal.prompt +10 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iterative_logic/genai_demo_iterative_logic_000.response +1 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iterative_logic/genai_demo_iterative_logic_001.prompt +171 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iterative_logic/genai_demo_iterative_logic_002.prompt +21 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iterative_logic/genai_demo_iterative_logic_003.response +94 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iterative_logic/genai_demo_iterative_logic_004.prompt +6 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iterative_logic/genai_demo_iterative_logic_005.response_example +122 -0
- api_logic_server_cli/prototypes/manager/system/genai/learning_requests/logic_bank_api.prompt +42 -5
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/logic_inserts.prompt +1 -0
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/response_format.prompt +6 -0
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/sqlite_inserts.prompt +11 -2
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/sqlite_inserts_iterations.prompt +25 -0
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/web_genai copy.prompt +15 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/emp_depts/emp_dept_explicit.prompt +0 -7
- api_logic_server_cli/prototypes/manager/system/genai/examples/emp_depts/emp_dept_implicit_fails.prompt +0 -5
- {ApiLogicServer-12.0.4.dist-info → ApiLogicServer-12.1.0.dist-info}/LICENSE +0 -0
- {ApiLogicServer-12.0.4.dist-info → ApiLogicServer-12.1.0.dist-info}/entry_points.txt +0 -0
- {ApiLogicServer-12.0.4.dist-info → ApiLogicServer-12.1.0.dist-info}/top_level.txt +0 -0
api_logic_server_cli/genai.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import time
|
|
1
3
|
from typing import Dict, List
|
|
2
4
|
from api_logic_server_cli.cli_args_project import Project
|
|
3
5
|
import logging
|
|
@@ -7,11 +9,31 @@ import requests
|
|
|
7
9
|
import os
|
|
8
10
|
import create_from_model.api_logic_server_utils as utils
|
|
9
11
|
import shutil
|
|
12
|
+
import openai
|
|
13
|
+
from openai import OpenAI
|
|
14
|
+
from typing import List, Dict
|
|
15
|
+
from pydantic import BaseModel
|
|
16
|
+
from dotmap import DotMap
|
|
10
17
|
|
|
11
18
|
log = logging.getLogger(__name__)
|
|
12
19
|
|
|
13
|
-
|
|
14
|
-
|
|
20
|
+
class Rule(BaseModel):
|
|
21
|
+
name: str
|
|
22
|
+
description: str
|
|
23
|
+
code: str # logicbank rule code
|
|
24
|
+
|
|
25
|
+
class Model(BaseModel):
|
|
26
|
+
classname: str
|
|
27
|
+
code: str # sqlalchemy model code
|
|
28
|
+
description: str
|
|
29
|
+
name: str
|
|
30
|
+
|
|
31
|
+
class WGResult(BaseModel): # must match system/genai/prompt_inserts/response_format.prompt
|
|
32
|
+
# response: str # result
|
|
33
|
+
models : List[Model] # list of sqlalchemy classes in the response
|
|
34
|
+
rules : List[Rule] # list rule declarations
|
|
35
|
+
test_data: str
|
|
36
|
+
|
|
15
37
|
|
|
16
38
|
class GenAI(object):
|
|
17
39
|
""" Create project from genai prompt(s).
|
|
@@ -83,32 +105,46 @@ class GenAI(object):
|
|
|
83
105
|
self.project.from_model = f'system/genai/temp/create_db_models.py' # we always write the model to this file
|
|
84
106
|
self.ensure_system_dir_exists() # ~ manager, so we can write to system/genai/temp
|
|
85
107
|
self.delete_temp_files()
|
|
108
|
+
self.post_error = ""
|
|
109
|
+
""" eg, if response contains table defs, save_prompt_messages_to_system_genai_temp_project raises an exception to trigger retry """
|
|
86
110
|
self.prompt = ""
|
|
87
111
|
""" `--using` - can come from file or text argument """
|
|
88
112
|
|
|
89
113
|
self.messages = self.get_prompt_messages() # compute self.messages, from file, dir or text argument
|
|
90
114
|
|
|
91
115
|
if self.project.genai_repaired_response == '': # normal path - get response from ChatGPT
|
|
92
|
-
log.debug(f'.. ChatGPT - saving response to: system/genai/temp/chatgpt_original.response')
|
|
93
|
-
self.headers = self.get_headers_with_openai_api_key()
|
|
94
|
-
url = "https://api.openai.com/v1/chat/completions"
|
|
95
116
|
api_version = f'{self.project.genai_version}' # eg, "gpt-4o"
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
117
|
+
api_version = "gpt-4o-2024-08-06"
|
|
118
|
+
start_time = time.time()
|
|
119
|
+
client = OpenAI(api_key=os.getenv("APILOGICSERVER_CHATGPT_APIKEY"))
|
|
120
|
+
completion = client.beta.chat.completions.parse(
|
|
121
|
+
messages=self.messages, response_format=WGResult,
|
|
122
|
+
# temperature=0.0,
|
|
123
|
+
model=api_version
|
|
124
|
+
)
|
|
125
|
+
log.debug(f'ChatGPT ({str(int(time.time() - start_time))} secs) - response at: system/genai/temp/chatgpt_original.response')
|
|
126
|
+
|
|
127
|
+
data = completion.choices[0].message.content
|
|
128
|
+
response_dict = json.loads(data)
|
|
129
|
+
self.get_and_save_raw_response_data(completion=completion, response_dict=response_dict)
|
|
130
|
+
# print(json.dumps(json.loads(data), indent=4))
|
|
131
|
+
pass
|
|
132
|
+
|
|
99
133
|
else: # for retry from corrected response... eg system/genai/temp/chatgpt_retry.response
|
|
100
134
|
log.debug(f'\nUsing [corrected] response from: {self.project.genai_repaired_response}')
|
|
101
|
-
with open(self.project.genai_repaired_response, 'r') as
|
|
102
|
-
|
|
103
|
-
|
|
135
|
+
with open(self.project.genai_repaired_response, 'r') as response_file:
|
|
136
|
+
response_dict = json.load(response_file)
|
|
137
|
+
|
|
138
|
+
self.response_dict = DotMap(response_dict)
|
|
104
139
|
""" the raw response data from ChatGPT which will be fixed & saved create_db_models.py """
|
|
105
140
|
|
|
106
|
-
self.project.genai_logic = self.get_logic_from_prompt()
|
|
141
|
+
# self.project.genai_logic = self.get_logic_from_prompt()
|
|
107
142
|
|
|
108
|
-
self.fix_and_write_model_file(
|
|
143
|
+
self.fix_and_write_model_file() # write create_db_models.py for db creation, & logic
|
|
109
144
|
self.save_prompt_messages_to_system_genai_temp_project() # save prompts, response and models.py
|
|
110
145
|
if project.project_name_last_node == 'genai_demo_conversation':
|
|
111
146
|
debug_string = "good breakpoint - check create_db_models.py"
|
|
147
|
+
pass # if we've set self.post_error, we'll raise an exception to trigger retry
|
|
112
148
|
pass # return to api_logic_server.ProjectRun to create db/project from create_db_models.py
|
|
113
149
|
|
|
114
150
|
def delete_temp_files(self):
|
|
@@ -118,6 +154,7 @@ class GenAI(object):
|
|
|
118
154
|
if self.project.genai_repaired_response == '': # clean up unless retrying from chatgpt_original.response
|
|
119
155
|
Path('system/genai/temp/chatgpt_original.response').unlink(missing_ok=True)
|
|
120
156
|
Path('system/genai/temp/chatgpt_retry.response').unlink(missing_ok=True)
|
|
157
|
+
Path('system/genai/temp/create_db_models.sqlite').unlink(missing_ok=True)
|
|
121
158
|
|
|
122
159
|
def create_presets(self, prompt_messages: List[Dict[str, str]]):
|
|
123
160
|
""" Create presets - you are a data modelling expert, and logicbank api etc """
|
|
@@ -132,7 +169,6 @@ class GenAI(object):
|
|
|
132
169
|
log.debug(f'.. conv[000] presets: {starting_message}')
|
|
133
170
|
log.debug(f'.. conv[001] presets: {learning_requests[0]["content"][:30]}...')
|
|
134
171
|
return len(learning_requests)
|
|
135
|
-
|
|
136
172
|
|
|
137
173
|
def get_prompt_messages(self) -> List[Dict[str, str]]:
|
|
138
174
|
""" Get prompt from file, dir (conversation) or text argument
|
|
@@ -178,7 +214,7 @@ class GenAI(object):
|
|
|
178
214
|
request_count += 1 # rebuild response with *all* tables
|
|
179
215
|
if request_count > 1: # Run Config: genai AUTO DEALERSHIP CONVERSATION
|
|
180
216
|
if 'updating the prior response' not in prompt:
|
|
181
|
-
prompt
|
|
217
|
+
prompt = self.get_prompt__with_inserts(raw_prompt=prompt, for_iteration=True)
|
|
182
218
|
prompt_messages.append( {"role": role, "content": prompt})
|
|
183
219
|
else:
|
|
184
220
|
log.debug(f'.. .. conv ignores: {os.path.basename(each_file)}')
|
|
@@ -218,7 +254,7 @@ class GenAI(object):
|
|
|
218
254
|
learning_requests.append( {"role": "user", "content": learning_request_lines})
|
|
219
255
|
return learning_requests # TODO - what if no learning requests?
|
|
220
256
|
|
|
221
|
-
def get_prompt__with_inserts(self, raw_prompt: str) -> str:
|
|
257
|
+
def get_prompt__with_inserts(self, raw_prompt: str, for_iteration: bool = False) -> str:
|
|
222
258
|
""" prompt-engineering: insert db-specific logic into prompt
|
|
223
259
|
raw_prompt: the prompt from file or text argument
|
|
224
260
|
"""
|
|
@@ -236,12 +272,35 @@ class GenAI(object):
|
|
|
236
272
|
prompt_inserts = f'mysql_inserts.prompt'
|
|
237
273
|
|
|
238
274
|
if prompt_inserts != "*":
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
275
|
+
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
|
+
assert Path(prompt_eng_file_name).exists(), \
|
|
279
|
+
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())} / {prompt_eng_file_name}')
|
|
281
|
+
with open(prompt_eng_file_name, 'r') as file:
|
|
243
282
|
pre_post = file.read() # eg, Use SQLAlchemy to create a sqlite database named system/genai/temp/create_db_models.sqlite, with
|
|
244
283
|
prompt_result = pre_post.replace('{{prompt}}', raw_prompt)
|
|
284
|
+
|
|
285
|
+
prompt_lines = prompt_result.split('\n')
|
|
286
|
+
prompt_line_number = 0
|
|
287
|
+
for each_line in prompt_lines:
|
|
288
|
+
if "LogicBank" in each_line:
|
|
289
|
+
log.debug(f'.. inserted: {each_line}')
|
|
290
|
+
prompt_eng_logic_file_name = f'system/genai/prompt_inserts/logic_inserts.prompt'
|
|
291
|
+
with open(prompt_eng_logic_file_name, 'r') as file:
|
|
292
|
+
prompt_logic = file.read() # eg, Use SQLAlchemy to...
|
|
293
|
+
prompt_lines[prompt_line_number] = prompt_logic
|
|
294
|
+
break
|
|
295
|
+
prompt_line_number += 1
|
|
296
|
+
|
|
297
|
+
response_format_file_name = f'system/genai/prompt_inserts/response_format.prompt'
|
|
298
|
+
with open(response_format_file_name, 'r') as file:
|
|
299
|
+
response_format = file.readlines()
|
|
300
|
+
prompt_lines.extend(response_format)
|
|
301
|
+
|
|
302
|
+
prompt_result = "\n".join(prompt_lines) # back to a string
|
|
303
|
+
pass
|
|
245
304
|
return prompt_result
|
|
246
305
|
|
|
247
306
|
def ensure_system_dir_exists(self):
|
|
@@ -261,7 +320,7 @@ class GenAI(object):
|
|
|
261
320
|
copied_path = shutil.copytree(src=from_dir, dst=to_dir, dirs_exist_ok=True)
|
|
262
321
|
|
|
263
322
|
def get_logic_from_prompt(self) -> list[str]:
|
|
264
|
-
""" Get logic from ChatGPT prompt
|
|
323
|
+
""" Get logic from ChatGPT prompt (code after "Enforce")
|
|
265
324
|
|
|
266
325
|
Args:
|
|
267
326
|
|
|
@@ -331,18 +390,17 @@ class GenAI(object):
|
|
|
331
390
|
|
|
332
391
|
logic_file = self.project.project_directory_path.joinpath('logic/declare_logic.py')
|
|
333
392
|
in_logic = False
|
|
334
|
-
translated_logic = "\n # Logic from GenAI
|
|
335
|
-
for
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
393
|
+
translated_logic = "\n # Logic from GenAI: (or, use your IDE w/ code completion)\n"
|
|
394
|
+
for each_rule in self.response_dict.rules:
|
|
395
|
+
comment_line = each_rule.description
|
|
396
|
+
translated_logic += f'\n # {comment_line}\n'
|
|
397
|
+
each_line = each_rule.code
|
|
398
|
+
if 'declare_logic.py' not in each_line:
|
|
399
|
+
each_repaired_line = self.remove_logic_halluncinations(each_line=each_line)
|
|
400
|
+
if not each_repaired_line.startswith(' '): # sometimes in indents, sometimes not
|
|
401
|
+
each_repaired_line = ' ' + each_repaired_line
|
|
402
|
+
if 'def declare_logic' not in each_repaired_line:
|
|
339
403
|
translated_logic += each_repaired_line + '\n'
|
|
340
|
-
elif each_line.strip() == '': # blank => still in logic
|
|
341
|
-
pass
|
|
342
|
-
else: # no-indent => end of logic
|
|
343
|
-
in_logic = False
|
|
344
|
-
if "declare_logic()" in each_line:
|
|
345
|
-
in_logic = True
|
|
346
404
|
translated_logic += "\n # End Logic from GenAI\n\n"
|
|
347
405
|
utils.insert_lines_at(lines=translated_logic,
|
|
348
406
|
file_name=logic_file,
|
|
@@ -380,7 +438,7 @@ class GenAI(object):
|
|
|
380
438
|
log.error(f"\n\nERROR creating genai project docs: {docs_dir}\n\n{traceback.format_exc()}")
|
|
381
439
|
pass
|
|
382
440
|
|
|
383
|
-
def fix_and_write_model_file(self
|
|
441
|
+
def fix_and_write_model_file(self):
|
|
384
442
|
"""
|
|
385
443
|
1. break response data into lines
|
|
386
444
|
2. throw away instructions
|
|
@@ -392,104 +450,165 @@ class GenAI(object):
|
|
|
392
450
|
response_data (str): the chatgpt response
|
|
393
451
|
|
|
394
452
|
"""
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
for
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
'''
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
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
|
|
439
|
-
got: or DECIMAL('
|
|
440
|
-
needed: or decimal.Decimal('0.00')
|
|
441
|
-
'''
|
|
442
|
-
if 'sqlite:///' in each_line: # must be sqlite:///system/genai/temp/create_db_models.sqlite
|
|
443
|
-
current_url_rest = each_line.split('sqlite:///')[1]
|
|
444
|
-
quote_type = "'"
|
|
445
|
-
if '"' in current_url_rest:
|
|
446
|
-
quote_type = '"' # eg, tests/test_databases/ai-created/time_cards/time_card_decimal/genai.response
|
|
447
|
-
current_url = current_url_rest.split(quote_type)[0]
|
|
448
|
-
proper_url = 'system/genai/temp/create_db_models.sqlite'
|
|
449
|
-
each_line = each_line.replace(current_url, proper_url)
|
|
450
|
-
if current_url != proper_url:
|
|
451
|
-
log.debug(f'.. fixed sqlite url: {current_url} -> system/genai/temp/create_db_models.sqlite')
|
|
452
|
-
if 'Decimal,' in each_line: # SQLAlchemy import
|
|
453
|
-
each_line = each_line.replace('Decimal,', 'DECIMAL,')
|
|
454
|
-
# other Decimal bugs: see api_logic_server_cli/prototypes/manager/system/genai/reference/errors/chatgpt_decimal.txt
|
|
455
|
-
if ', Decimal' in each_line: # Cap'n K, at your service
|
|
456
|
-
each_line = each_line.replace(', Decimal', ', DECIMAL')
|
|
457
|
-
if 'rom decimal import Decimal' in each_line:
|
|
458
|
-
each_line = each_line.replace('from decimal import Decimal', 'import decimal')
|
|
459
|
-
if '=Decimal(' in each_line:
|
|
460
|
-
each_line = each_line.replace('=Decimal(', '=decimal.Decimal(')
|
|
461
|
-
if ' Decimal(' in each_line:
|
|
462
|
-
each_line = each_line.replace(' Decimal(', ' decimal.Decimal(')
|
|
463
|
-
if 'Column(Decimal)' in each_line:
|
|
464
|
-
each_line = each_line.replace('Column(Decimal)', 'Column(DECIMAL)')
|
|
465
|
-
if "DECIMAL('" in each_line:
|
|
466
|
-
each_line = each_line.replace("DECIMAL('", "decimal.Decimal('")
|
|
467
|
-
if 'end_time(datetime' in each_line: # tests/test_databases/ai-created/time_cards/time_card_kw_arg/genai.response
|
|
468
|
-
each_line = each_line.replace('end_time(datetime', 'end_time=datetime')
|
|
469
|
-
if indents_to_remove > 0:
|
|
470
|
-
each_line = each_line[indents_to_remove:]
|
|
471
|
-
if 'relationship(' in each_line and self.project.genai_use_relns == False:
|
|
472
|
-
# airport4 fails with could not determine join condition between parent/child tables on relationship Airport.flights
|
|
473
|
-
if each_line.startswith(' '):
|
|
474
|
-
each_line = each_line.replace(' ', ' # ')
|
|
475
|
-
else: # sometimes it puts relns outside the class (so, outdented)
|
|
476
|
-
each_line = '# ' + each_line
|
|
477
|
-
if 'sqlite:///system/genai/temp/model.sqlite': # fix prior version
|
|
478
|
-
each_line = each_line.replace('sqlite:///system/genai/temp/model.sqlite',
|
|
479
|
-
'sqlite:///system/genai/temp/create_db_models.sqlite')
|
|
480
|
-
|
|
481
|
-
# logicbank fixes
|
|
482
|
-
if 'from logic_bank' in each_line: # we do our own imports
|
|
483
|
-
each_line = each_line.replace('from', '# from')
|
|
484
|
-
if 'LogicBank.activate' in each_line:
|
|
485
|
-
each_line = each_line.replace('LogicBank.activate', '# LogicBank.activate')
|
|
486
|
-
|
|
487
|
-
model_class += each_line + '\n'
|
|
488
|
-
with open(f'{self.project.from_model}', "w") as model_file:
|
|
489
|
-
model_file.write(model_class)
|
|
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
|
+
|
|
456
|
+
models = self.response_dict.models
|
|
457
|
+
did_base = False
|
|
458
|
+
for each_model in models:
|
|
459
|
+
model_lines = self.get_model_class_lines(model=each_model)
|
|
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')
|
|
492
|
+
|
|
493
|
+
with open(f'{self.project.from_model}', "w") as create_db_model_file:
|
|
494
|
+
for line in create_db_model_lines:
|
|
495
|
+
create_db_model_file.write(f"{line}")
|
|
490
496
|
|
|
491
497
|
log.debug(f'.. model file created: {self.project.from_model}')
|
|
492
498
|
|
|
499
|
+
def get_lines_from_file(self, file_name: str) -> list[str]:
|
|
500
|
+
"""Get lines from a file
|
|
501
|
+
|
|
502
|
+
Args:
|
|
503
|
+
file_name (str): the file name
|
|
504
|
+
|
|
505
|
+
Returns:
|
|
506
|
+
list[str]: the lines from the file
|
|
507
|
+
"""
|
|
508
|
+
|
|
509
|
+
with open(file_name, "r") as file:
|
|
510
|
+
lines = file.readlines()
|
|
511
|
+
return lines
|
|
512
|
+
|
|
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
|
+
|
|
493
612
|
def save_prompt_messages_to_system_genai_temp_project(self):
|
|
494
613
|
"""
|
|
495
614
|
Save prompts / responses to system/genai/temp/{project}/genai.response
|
|
@@ -509,36 +628,37 @@ class GenAI(object):
|
|
|
509
628
|
if to_dir_save_dir.exists():
|
|
510
629
|
shutil.rmtree(to_dir_save_dir)
|
|
511
630
|
os.makedirs(to_dir_save_dir, exist_ok=True)
|
|
512
|
-
log.debug(f'save_prompt_messages_to_system_genai_temp_project()')
|
|
631
|
+
log.debug(f'save_prompt_messages_to_system_genai_temp_project() - {str(to_dir_save_dir)}')
|
|
513
632
|
|
|
514
633
|
if self.project.genai_repaired_response == '': # normal path, from --using
|
|
515
634
|
if write_prompt := True:
|
|
516
635
|
pass
|
|
517
636
|
file_num = 0
|
|
637
|
+
flat_project_name = Path(self.project.project_name).stem # in case project is dir/project-name
|
|
518
638
|
for each_message in self.messages:
|
|
519
639
|
suffix = 'prompt'
|
|
520
640
|
if each_message['role'] == 'system':
|
|
521
641
|
suffix = 'response'
|
|
522
|
-
file_name = f'{
|
|
642
|
+
file_name = f'{flat_project_name}_{str(file_num).zfill(3)}.{suffix}'
|
|
523
643
|
file_path = to_dir_save_dir.joinpath(file_name)
|
|
524
644
|
log.debug(f'.. saving[{file_name}] - {each_message["content"][:30]}...')
|
|
525
645
|
with open(file_path, "w") as message_file:
|
|
526
646
|
message_file.write(each_message['content'])
|
|
527
647
|
file_num += 1
|
|
528
648
|
suffix = 'response' # now add the this response
|
|
529
|
-
file_name = f'{
|
|
649
|
+
file_name = f'{flat_project_name}_{str(file_num).zfill(3)}.{suffix}' # FIXME
|
|
530
650
|
file_path = to_dir_save_dir.joinpath(file_name)
|
|
531
651
|
log.debug(f'.. saving[{file_name}] - {each_message["content"][:30]}...')
|
|
532
652
|
with open(file_path, "w") as message_file:
|
|
533
|
-
|
|
653
|
+
json.dump(self.response_dict.toDict(), message_file, indent=4)
|
|
534
654
|
shutil.copyfile(self.project.from_model, to_dir_save_dir.joinpath('create_db_models.py'))
|
|
535
655
|
except Exception as inst:
|
|
536
656
|
# FileNotFoundError(2, 'No such file or directory')
|
|
537
|
-
log.error(f"\n\nError {inst} creating
|
|
657
|
+
log.error(f"\n\nError: {inst} \n..creating diagnostic files into dir: {str(gen_temp_dir)}\n\n")
|
|
538
658
|
pass # intentional try/catch/bury - it's just diagnostics, so don't fail
|
|
539
659
|
debug_string = "good breakpoint - return to main driver, and execute create_db_models.py"
|
|
540
660
|
|
|
541
|
-
def
|
|
661
|
+
def get_headers_with_openai_api_key_ZZ(self) -> dict:
|
|
542
662
|
"""
|
|
543
663
|
Returns:
|
|
544
664
|
dict: api header with OpenAI key (exits if not provided)
|
|
@@ -564,35 +684,32 @@ class GenAI(object):
|
|
|
564
684
|
}
|
|
565
685
|
return headers
|
|
566
686
|
|
|
567
|
-
def get_and_save_raw_response_data(self,
|
|
687
|
+
def get_and_save_raw_response_data(self, completion: object, response_dict: dict):
|
|
568
688
|
"""
|
|
569
689
|
Returns:
|
|
570
690
|
str: response_data
|
|
571
691
|
"""
|
|
572
692
|
|
|
573
|
-
|
|
693
|
+
''' TODO - is exception used instead of return_code...
|
|
574
694
|
# Check if the request was successful
|
|
575
|
-
if
|
|
576
|
-
raise Exception("Bad ChatGPT Request: " +
|
|
695
|
+
if completion.status_code == 400:
|
|
696
|
+
raise Exception("Bad ChatGPT Request: " + completion.text)
|
|
577
697
|
|
|
578
|
-
if
|
|
579
|
-
print("Error:",
|
|
698
|
+
if completion.status_code != 200:
|
|
699
|
+
print("Error:", completion.status_code, completion.text) # eg, You exceeded your current quota
|
|
700
|
+
'''
|
|
580
701
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
with open(f'system/genai/temp/
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
with open(f'system/genai/temp/chatgpt_retry.response', "w") as model_file: # repair this & retry
|
|
587
|
-
model_file.write(response_data)
|
|
588
|
-
log.debug(f'.. stored raw response: {model_file.name}')
|
|
589
|
-
return response_data
|
|
702
|
+
with open(f'system/genai/temp/chatgpt_original.response', "w") as response_file: # save for debug
|
|
703
|
+
json.dump(response_dict, response_file, indent=4)
|
|
704
|
+
with open(f'system/genai/temp/chatgpt_retry.response', "w") as response_file: # repair this & retry
|
|
705
|
+
json.dump(response_dict, response_file, indent=4)
|
|
706
|
+
return
|
|
590
707
|
|
|
591
708
|
|
|
592
709
|
def genai(using, db_url, repaired_response: bool, genai_version: str,
|
|
593
710
|
retries: int, opt_locking: str, prompt_inserts: str, quote: bool,
|
|
594
711
|
use_relns: bool, project_name: str):
|
|
595
|
-
"""
|
|
712
|
+
""" CLI caller provides using, or repaired_response & using
|
|
596
713
|
|
|
597
714
|
Called from cli commands: genai, genai-create, genai-iterate
|
|
598
715
|
|
|
@@ -624,6 +741,7 @@ def genai(using, db_url, repaired_response: bool, genai_version: str,
|
|
|
624
741
|
project_name=resolved_project_name, db_url=db_url)
|
|
625
742
|
log.info(f"GENAI successful")
|
|
626
743
|
else:
|
|
744
|
+
failed = False
|
|
627
745
|
while try_number <= retries:
|
|
628
746
|
try:
|
|
629
747
|
failed = False
|
|
@@ -687,6 +805,9 @@ def genai(using, db_url, repaired_response: bool, genai_version: str,
|
|
|
687
805
|
# to_dir_save_dir.rename(to_dir_save_dir_retry)
|
|
688
806
|
assert to_dir_save_dir.is_dir(), f"\nInternal Error - missing save directory: {to_dir_save_dir}"
|
|
689
807
|
# assert to_dir_save_dir_retry.is_dir(), f"\nInternal Error - missing retry directory: {to_dir_save_dir_retry}"
|
|
808
|
+
log.debug(f'.. copying work files...')
|
|
809
|
+
log.debug(f'.... from: {to_dir_save_dir}')
|
|
810
|
+
log.debug(f'.... to: {to_dir_save_dir_retry}')
|
|
690
811
|
shutil.copytree(to_dir_save_dir, to_dir_save_dir_retry, dirs_exist_ok=True)
|
|
691
812
|
|
|
692
813
|
failed = True
|
|
@@ -696,8 +817,9 @@ def genai(using, db_url, repaired_response: bool, genai_version: str,
|
|
|
696
817
|
failed = False
|
|
697
818
|
else:
|
|
698
819
|
try_number += 1
|
|
820
|
+
log.debug(f"\n\nRetry Genai #{try_number}\n")
|
|
699
821
|
pass # retry (retries times)
|
|
700
|
-
if failed:
|
|
822
|
+
if failed == True: # retries exhausted (if failed: threw "an integer is required" ??
|
|
701
823
|
log.error(f"\n\nGenai Failed (Retries: {retries})")
|
|
702
824
|
exit(1)
|
|
703
825
|
log.info(f"GENAI successful on try {try_number}")
|