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.
Files changed (41) hide show
  1. {ApiLogicServer-12.0.4.dist-info → ApiLogicServer-12.1.0.dist-info}/METADATA +2 -1
  2. {ApiLogicServer-12.0.4.dist-info → ApiLogicServer-12.1.0.dist-info}/RECORD +39 -24
  3. {ApiLogicServer-12.0.4.dist-info → ApiLogicServer-12.1.0.dist-info}/WHEEL +1 -1
  4. api_logic_server_cli/api_logic_server.py +8 -201
  5. api_logic_server_cli/api_logic_server_info.yaml +3 -3
  6. api_logic_server_cli/cli.py +9 -79
  7. api_logic_server_cli/create_from_model/__pycache__/dbml.cpython-312.pyc +0 -0
  8. api_logic_server_cli/create_from_model/__pycache__/meta_model.cpython-312.pyc +0 -0
  9. api_logic_server_cli/create_from_model/__pycache__/model_creation_services.cpython-312.pyc +0 -0
  10. api_logic_server_cli/create_from_model/meta_model.py +1 -1
  11. api_logic_server_cli/create_from_model/model_creation_services.py +3 -1
  12. api_logic_server_cli/genai.py +273 -151
  13. api_logic_server_cli/genaiZ.py +752 -0
  14. api_logic_server_cli/prototypes/genai_demo/logic/declare_logic.py +11 -12
  15. api_logic_server_cli/prototypes/manager/.vscode/launch.json +78 -1
  16. api_logic_server_cli/prototypes/manager/README.md +16 -3
  17. api_logic_server_cli/prototypes/manager/system/genai/create_db_models_inserts/create_db_models_create_db.py +10 -0
  18. api_logic_server_cli/prototypes/manager/system/genai/create_db_models_inserts/create_db_models_imports.py +19 -0
  19. api_logic_server_cli/prototypes/manager/system/genai/create_db_models_inserts/create_db_models_test_data.py +7 -0
  20. api_logic_server_cli/prototypes/manager/system/genai/examples/emp_depts/emp_dept.prompt +4 -0
  21. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.prompt +1 -1
  22. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example +56 -130
  23. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example_z +130 -0
  24. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_informal.prompt +10 -0
  25. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iterative_logic/genai_demo_iterative_logic_000.response +1 -0
  26. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iterative_logic/genai_demo_iterative_logic_001.prompt +171 -0
  27. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iterative_logic/genai_demo_iterative_logic_002.prompt +21 -0
  28. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iterative_logic/genai_demo_iterative_logic_003.response +94 -0
  29. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iterative_logic/genai_demo_iterative_logic_004.prompt +6 -0
  30. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iterative_logic/genai_demo_iterative_logic_005.response_example +122 -0
  31. api_logic_server_cli/prototypes/manager/system/genai/learning_requests/logic_bank_api.prompt +42 -5
  32. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/logic_inserts.prompt +1 -0
  33. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/response_format.prompt +6 -0
  34. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/sqlite_inserts.prompt +11 -2
  35. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/sqlite_inserts_iterations.prompt +25 -0
  36. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/web_genai copy.prompt +15 -0
  37. api_logic_server_cli/prototypes/manager/system/genai/examples/emp_depts/emp_dept_explicit.prompt +0 -7
  38. api_logic_server_cli/prototypes/manager/system/genai/examples/emp_depts/emp_dept_implicit_fails.prompt +0 -5
  39. {ApiLogicServer-12.0.4.dist-info → ApiLogicServer-12.1.0.dist-info}/LICENSE +0 -0
  40. {ApiLogicServer-12.0.4.dist-info → ApiLogicServer-12.1.0.dist-info}/entry_points.txt +0 -0
  41. {ApiLogicServer-12.0.4.dist-info → ApiLogicServer-12.1.0.dist-info}/top_level.txt +0 -0
@@ -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
- k_update_prior_response = ', by updating the prior response.'
14
- """ , by updating the prior response. """
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
- data = {"model": api_version, "messages": self.messages}
97
- response = requests.post(url, headers=self.headers, json=data)
98
- create_db_models = self.get_and_save_raw_response_data(response)
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 file:
102
- create_db_models = file.read()
103
- self.create_db_models = create_db_models
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(create_db_models) # write create_db_models.py for db creation
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 += k_update_prior_response
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
- assert Path(f'system/genai/prompt_inserts/{prompt_inserts}').exists(), \
240
- f"Missing prompt_inserts file: {prompt_inserts}" # eg api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/sqlite_inserts.prompt
241
- log.debug(f'get_prompt__with_inserts: {str(os.getcwd())} / {prompt_inserts}')
242
- with open(f'system/genai/prompt_inserts/{prompt_inserts}', 'r') as file:
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:\n\n"
335
- for each_line in self.create_db_models.split('\n'):
336
- if in_logic:
337
- if each_line.startswith(' '): # indent => still in logic
338
- each_repaired_line = self.remove_logic_halluncinations(each_line=each_line)
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, response_data: str):
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
- model_class = "# created from response - used to create database and project\n"
396
- model_class += "# should run without error\n"
397
- model_class += "# if not, check for decimal, indent, or import issues\n\n"
398
- with open(f'system/genai/create_db_models_inserts/create_db_models_prefix.py', "r") as inserts:
399
- model_lines = inserts.readlines()
400
- for each_line in model_lines:
401
- model_class += each_line + '\n'
402
-
403
- response_array = response_data.split('\n')
404
- line_num = 0
405
- writing = False
406
- indents_to_remove = 0
407
- for each_line in response_array:
408
- line_num += 1
409
- if "```python" in each_line:
410
- writing = True
411
- # count spaces before "```"
412
- # next_line = response_array[line_num+1]
413
- position = each_line.find("```")
414
- if position > 0:
415
- indents_to_remove = each_line[:position].count(' ')
416
- elif "```" in each_line:
417
- writing = False
418
- elif writing: # ChatGPT work-arounds
419
- ''' decimal issues
420
-
421
- 1. bad import: see Run: tests/test_databases/ai-created/genai_demo/genai_demo_decimal
422
- from decimal import Decimal # Decimal fix: needs to be from decimal import DECIMAL
423
-
424
- 2. Missing missing import: from SQLAlchemy import .... DECIMAL
425
-
426
- 3. Column(Decimal) -> Column(DECIMAL)
427
- see in: tests/test_databases/ai-created/budget_allocation/budget_allocations/budget_allocations_3_decimal
428
-
429
- 4. Bad syntax on test data: see Run: blt/time_cards_decimal from RESPONSE
430
- got: balance=DECIMAL('100.50')
431
- needed: balance=1000.0
432
- fixed with import in create_db_models_prefix.py
433
-
434
- 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
435
- got: or Decimal('0.00')
436
- needed: or decimal.Decimal('0.00')
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'{self.project.project_name}_{str(file_num).zfill(3)}.{suffix}'
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'{self.project.project_name}_{str(file_num).zfill(3)}.{suffix}'
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
- message_file.write(self.create_db_models)
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 project diagnostic files: {str(gen_temp_dir)}\n\n")
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 get_headers_with_openai_api_key(self) -> dict:
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, response) -> str:
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 response.status_code == 400:
576
- raise Exception("Bad ChatGPT Request: " + response.text)
695
+ if completion.status_code == 400:
696
+ raise Exception("Bad ChatGPT Request: " + completion.text)
577
697
 
578
- if response.status_code != 200:
579
- print("Error:", response.status_code, response.text) # eg, You exceeded your current quota
698
+ if completion.status_code != 200:
699
+ print("Error:", completion.status_code, completion.text) # eg, You exceeded your current quota
700
+ '''
580
701
 
581
- response_data = response.json()['choices'][0]['message']['content']
582
- file_name = f'system/genai/temp/chatgpt_original.response'
583
- with open(f'system/genai/temp/chatgpt_original.response', "w") as model_file: # save for debug
584
- model_file.write(response_data)
585
- file_name = model_file.name
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
- """ cli caller provides using, or repaired_response & using
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}")