ApiLogicServer 14.3.7__py3-none-any.whl → 14.3.14__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- api_logic_server_cli/api_logic_server.py +4 -2
- api_logic_server_cli/api_logic_server_info.yaml +3 -3
- api_logic_server_cli/cli.py +1 -1
- api_logic_server_cli/create_from_model/__pycache__/ont_build.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/ont_build.py +8 -8
- api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/asset-manifest.json +3 -3
- api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/build-0213.txt +1 -0
- api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/index.html +1 -1
- api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/static/js/main.7c8c0e37.js +3 -0
- api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/static/js/{main.bfe80d1d.js.map → main.7c8c0e37.js.map} +1 -1
- api_logic_server_cli/database/nw-gold.sqlite +0 -0
- api_logic_server_cli/genai/genai.py +17 -279
- api_logic_server_cli/genai/genai_fatal_excp.py +5 -0
- api_logic_server_cli/genai/genai_svcs.py +14 -2
- api_logic_server_cli/genai/genai_utils.py +1 -1
- api_logic_server_cli/prototypes/base/api/expose_api_models.py +1 -1
- api_logic_server_cli/prototypes/base/api/system/expression_parser.py +1 -1
- api_logic_server_cli/prototypes/base/api_logic_server_run.py +2 -2
- api_logic_server_cli/prototypes/base/devops/docker-image/env.list +22 -2
- api_logic_server_cli/prototypes/base/devops/docker-standard-image/docker-compose-standard-image.yml +28 -0
- api_logic_server_cli/prototypes/base/devops/docker-standard-image/env.list +55 -0
- api_logic_server_cli/prototypes/base/devops/readme-devops.md +13 -1
- api_logic_server_cli/prototypes/base/docs/logic/readme.md +6 -2
- api_logic_server_cli/prototypes/base/integration/kafka/kafka_producer.py +2 -3
- api_logic_server_cli/prototypes/base/logic/readme_declare_logic.md +8 -0
- api_logic_server_cli/prototypes/base/security/declare_security.py +2 -0
- api_logic_server_cli/prototypes/genai_demo/logic/declare_logic.py +3 -3
- api_logic_server_cli/prototypes/manager/.vscode/ApiLogicServer.code-workspace +2 -2
- api_logic_server_cli/prototypes/manager/.vscode/launch.json +20 -20
- api_logic_server_cli/prototypes/manager/system/Manager_workspace.code-workspace +2 -3
- 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_formal.prompt +0 -2
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_informal.prompt +0 -2
- api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/002_create_db_models.prompt +3 -132
- api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/Invoice Made Ready.png +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/readme.md +3 -3
- api_logic_server_cli/prototypes/manager/system/genai/learning_requests/logic_bank_api.prompt +7 -0
- api_logic_server_cli/prototypes/manager/system/genai/webg_local/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/webg_local/run_web_genai.sh +10 -0
- api_logic_server_cli/prototypes/manager/system/genai/webg_local/webg-projects/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/webg_local/webg-projects/by-ulid/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/webg_local/webg-projects/public/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/webg_local/webg-projects/wgadmin/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/webg_local/webg-temp/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/webg_local/webg_config/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/webg_local/webg_config/web_genai.txt +8 -0
- api_logic_server_cli/prototypes/nw/logic/declare_logic.py +1 -1
- api_logic_server_cli/sqlacodegen_wrapper/sqlacodegen/sqlacodegen/codegen.py +2 -2
- {ApiLogicServer-14.3.7.dist-info → apilogicserver-14.3.14.dist-info}/METADATA +3 -3
- {ApiLogicServer-14.3.7.dist-info → apilogicserver-14.3.14.dist-info}/RECORD +55 -42
- {ApiLogicServer-14.3.7.dist-info → apilogicserver-14.3.14.dist-info}/WHEEL +1 -1
- api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/build-0106.txt +0 -1
- api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/static/js/main.bfe80d1d.js +0 -3
- /api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/static/js/{main.bfe80d1d.js.LICENSE.txt → main.7c8c0e37.js.LICENSE.txt} +0 -0
- {ApiLogicServer-14.3.7.dist-info → apilogicserver-14.3.14.dist-info}/LICENSE +0 -0
- {ApiLogicServer-14.3.7.dist-info → apilogicserver-14.3.14.dist-info}/entry_points.txt +0 -0
- {ApiLogicServer-14.3.7.dist-info → apilogicserver-14.3.14.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
@@ -315,8 +315,14 @@ class GenAI(object):
|
|
|
315
315
|
log.debug(f'.. from file: {self.project.genai_using}')
|
|
316
316
|
raw_prompt = file.read()
|
|
317
317
|
prompt = self.get_prompt__with_inserts(raw_prompt=raw_prompt, for_iteration=False) # insert db-specific logic
|
|
318
|
-
self.logic_enabled =
|
|
319
|
-
if
|
|
318
|
+
self.logic_enabled = True
|
|
319
|
+
if os.environ.get("APILOGICPROJECT_LOGIC_ENABLED") is not None and \
|
|
320
|
+
os.environ.get("APILOGICPROJECT_LOGIC_ENABLED") == 'False':
|
|
321
|
+
self.logic_enabled = False
|
|
322
|
+
log.info("*** Initial Logic Disabled: {self.logic_enabled}")
|
|
323
|
+
else:
|
|
324
|
+
log.debug(f'.. Initial Logic enabled: {self.logic_enabled}')
|
|
325
|
+
if self.logic_enabled == True or ('LogicBank' in prompt and K_LogicBankOff not in prompt): # if prompt has logic, we need to insert the training
|
|
320
326
|
prompt_messages.extend( self.get_prompt_learning_requests())
|
|
321
327
|
self.logic_enabled = True
|
|
322
328
|
if prompt.startswith('You are a '): # if it's a preset, we need to insert the prompt
|
|
@@ -329,7 +335,7 @@ class GenAI(object):
|
|
|
329
335
|
active_rules_json_path = Path(self.project.genai_using).joinpath('logic/active_rules.json')
|
|
330
336
|
# assert active_rules_json_path.exists(), f"Missing active_rules.json: {active_rules_json_path}"
|
|
331
337
|
if not active_rules_json_path.exists():
|
|
332
|
-
log.info("*** Internal error: --active_rules specified, but no --using/logic/active_rules.json found - try to
|
|
338
|
+
log.info("*** Internal error: --active_rules specified, but no --using/logic/active_rules.json found - try to proceed")
|
|
333
339
|
else:
|
|
334
340
|
with open(active_rules_json_path, 'r') as file:
|
|
335
341
|
active_rules_str = file.read()
|
|
@@ -576,10 +582,18 @@ class GenAI(object):
|
|
|
576
582
|
Save prompts / responses to system/genai/temp/{project}/genai.response
|
|
577
583
|
|
|
578
584
|
Copy system/genai/temp/create_db_models.py to system/genai/temp/{project}/create_db_models.py
|
|
585
|
+
|
|
586
|
+
delete system/genai/temp/create_db_models.sqlite (avoid table collisions)
|
|
579
587
|
"""
|
|
580
588
|
try:
|
|
581
589
|
to_dir = Path(os.getcwd())
|
|
582
590
|
gen_temp_dir = Path(to_dir).joinpath(f'system/genai/temp')
|
|
591
|
+
|
|
592
|
+
# delete system/genai/temp/create_db_models.sqlite
|
|
593
|
+
sqlite_file_path = Path(gen_temp_dir).joinpath('create_db_models.sqlite')
|
|
594
|
+
if sqlite_file_path.exists():
|
|
595
|
+
os.remove(sqlite_file_path)
|
|
596
|
+
|
|
583
597
|
to_dir_save_dir = Path(to_dir).joinpath(f'system/genai/temp/{self.project.project_name_last_node}')
|
|
584
598
|
""" project work files saved to system/genai/temp/<project> """
|
|
585
599
|
log.info(f'.. saving work files to: system/genai/temp/{self.project.project_name_last_node}')
|
|
@@ -639,282 +653,6 @@ class GenAI(object):
|
|
|
639
653
|
pass # intentional try/catch/bury - it's just diagnostics, so don't fail
|
|
640
654
|
debug_string = "good breakpoint - return to main driver, and execute create_db_models.py"
|
|
641
655
|
|
|
642
|
-
def z_fix_and_write_model_file(self):
|
|
643
|
-
"""
|
|
644
|
-
1. from response, create model file / models lines
|
|
645
|
-
2. from response, create model file / test lines
|
|
646
|
-
3. ChatGPT work-arounds (decimal, indent, bogus relns, etc etc)
|
|
647
|
-
4. Ensure the sqlite url is correct: sqlite:///system/genai/temp/create_db_models.sqlite
|
|
648
|
-
5. write model file to self.project.from_model
|
|
649
|
-
|
|
650
|
-
Args:
|
|
651
|
-
response_data (str): the chatgpt response
|
|
652
|
-
|
|
653
|
-
"""
|
|
654
|
-
|
|
655
|
-
def insert_model_lines(models, create_db_model_lines):
|
|
656
|
-
|
|
657
|
-
def get_model_class_lines(model: DotMap) -> list[str]:
|
|
658
|
-
"""Get the model class from the model, with MAJOR fixes
|
|
659
|
-
|
|
660
|
-
Args:
|
|
661
|
-
model (Model): the model
|
|
662
|
-
|
|
663
|
-
Returns:
|
|
664
|
-
stlist[str]: the model class lines, fixed up
|
|
665
|
-
"""
|
|
666
|
-
|
|
667
|
-
create_db_model_lines = list()
|
|
668
|
-
create_db_model_lines.append('\n\n')
|
|
669
|
-
class_lines = model.code.split('\n')
|
|
670
|
-
line_num = 0
|
|
671
|
-
indents_to_remove = 0
|
|
672
|
-
for each_line in class_lines:
|
|
673
|
-
line_num += 1
|
|
674
|
-
''' decimal issues
|
|
675
|
-
|
|
676
|
-
1. bad import: see Run: tests/test_databases/ai-created/genai_demo/genai_demo_decimal
|
|
677
|
-
from decimal import Decimal # Decimal fix: needs to be from decimal import DECIMAL
|
|
678
|
-
|
|
679
|
-
2. Missing missing import: from SQLAlchemy import .... DECIMAL
|
|
680
|
-
|
|
681
|
-
3. Column(Decimal) -> Column(DECIMAL)
|
|
682
|
-
see in: tests/test_databases/ai-created/budget_allocation/budget_allocations/budget_allocations_3_decimal
|
|
683
|
-
|
|
684
|
-
4. Bad syntax on test data: see Run: blt/time_cards_decimal from RESPONSE
|
|
685
|
-
got: balance=DECIMAL('100.50')
|
|
686
|
-
needed: balance=1000.0
|
|
687
|
-
fixed with import in create_db_models_prefix.py
|
|
688
|
-
|
|
689
|
-
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
|
|
690
|
-
got: or Decimal('0.00')
|
|
691
|
-
needed: or decimal.Decimal('0.00')
|
|
692
|
-
|
|
693
|
-
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
|
|
694
|
-
got: or DECIMAL('
|
|
695
|
-
needed: or decimal.Decimal('0.00')
|
|
696
|
-
'''
|
|
697
|
-
|
|
698
|
-
# TODO - seeing several \\ in the response - should be \ (I think)
|
|
699
|
-
if "= Table(" in each_line: # tests/test_databases/ai-created/time_cards/time_card_kw_arg/genai.response
|
|
700
|
-
log.debug(f'.. fix_and_write_model_file detects table - raise excp to trigger retry')
|
|
701
|
-
self.post_error = "ChatGPT Response contains table (not class) definitions: " + each_line
|
|
702
|
-
if 'sqlite:///' in each_line: # must be sqlite:///system/genai/temp/create_db_models.sqlite
|
|
703
|
-
current_url_rest = each_line.split('sqlite:///')[1]
|
|
704
|
-
quote_type = "'"
|
|
705
|
-
if '"' in current_url_rest:
|
|
706
|
-
quote_type = '"' # eg, tests/test_databases/ai-created/time_cards/time_card_decimal/genai.response
|
|
707
|
-
current_url = current_url_rest.split(quote_type)[0]
|
|
708
|
-
proper_url = 'system/genai/temp/create_db_models.sqlite'
|
|
709
|
-
each_line = each_line.replace(current_url, proper_url)
|
|
710
|
-
if current_url != proper_url:
|
|
711
|
-
log.debug(f'.. fixed sqlite url: {current_url} -> system/genai/temp/create_db_models.sqlite')
|
|
712
|
-
if 'Decimal,' in each_line: # SQLAlchemy import
|
|
713
|
-
each_line = each_line.replace('Decimal,', 'DECIMAL,')
|
|
714
|
-
# other Decimal bugs: see api_logic_server_cli/prototypes/manager/system/genai/reference/errors/chatgpt_decimal.txt
|
|
715
|
-
if ', Decimal' in each_line: # Cap'n K, at your service
|
|
716
|
-
each_line = each_line.replace(', Decimal', ', DECIMAL')
|
|
717
|
-
if 'rom decimal import Decimal' in each_line:
|
|
718
|
-
each_line = each_line.replace('from decimal import Decimal', 'import decimal')
|
|
719
|
-
if '=Decimal(' in each_line:
|
|
720
|
-
each_line = each_line.replace('=Decimal(', '=decimal.Decimal(')
|
|
721
|
-
if ' Decimal(' in each_line:
|
|
722
|
-
each_line = each_line.replace(' Decimal(', ' decimal.Decimal(')
|
|
723
|
-
if 'Column(Decimal' in each_line:
|
|
724
|
-
each_line = each_line.replace('Column(Decimal', 'Column(DECIMAL')
|
|
725
|
-
if "DECIMAL('" in each_line:
|
|
726
|
-
each_line = each_line.replace("DECIMAL('", "decimal.Decimal('")
|
|
727
|
-
if 'end_time(datetime' in each_line: # tests/test_databases/ai-created/time_cards/time_card_kw_arg/genai.response
|
|
728
|
-
each_line = each_line.replace('end_time(datetime', 'end_time=datetime')
|
|
729
|
-
if 'datetime.date.today' in each_line:
|
|
730
|
-
each_line = each_line.replace('datetime.today', 'end_time=datetime')
|
|
731
|
-
if indents_to_remove > 0:
|
|
732
|
-
each_line = each_line[indents_to_remove:]
|
|
733
|
-
if 'relationship(' in each_line and self.project.genai_use_relns == False:
|
|
734
|
-
# airport4 fails with could not determine join condition between parent/child tables on relationship Airport.flights
|
|
735
|
-
if each_line.startswith(' '):
|
|
736
|
-
each_line = each_line.replace(' ', ' # ')
|
|
737
|
-
else: # sometimes it puts relns outside the class (so, outdented)
|
|
738
|
-
each_line = '# ' + each_line
|
|
739
|
-
if 'sqlite:///system/genai/temp/model.sqlite': # fix prior version
|
|
740
|
-
each_line = each_line.replace('sqlite:///system/genai/temp/model.sqlite',
|
|
741
|
-
'sqlite:///system/genai/temp/create_db_models.sqlite')
|
|
742
|
-
|
|
743
|
-
# logicbank fixes
|
|
744
|
-
if 'from logic_bank' in each_line: # we do our own imports
|
|
745
|
-
each_line = each_line.replace('from', '# from')
|
|
746
|
-
if 'LogicBank.activate' in each_line:
|
|
747
|
-
each_line = each_line.replace('LogicBank.activate', '# LogicBank.activate')
|
|
748
|
-
|
|
749
|
-
create_db_model_lines.append(each_line + '\n')
|
|
750
|
-
return create_db_model_lines
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
did_base = False
|
|
754
|
-
for each_model in models:
|
|
755
|
-
model_lines = get_model_class_lines(model=each_model)
|
|
756
|
-
for each_line in model_lines:
|
|
757
|
-
each_fixed_line = each_line.replace('sa.', '') # sometimes it puts sa. in front of Column
|
|
758
|
-
if 'Base = declarative_base()' in each_fixed_line: # sometimes created for each class
|
|
759
|
-
if did_base:
|
|
760
|
-
each_fixed_line = '# ' + each_fixed_line
|
|
761
|
-
did_base = True
|
|
762
|
-
if 'datetime.datetime.utcnow' in each_fixed_line:
|
|
763
|
-
each_fixed_line = each_fixed_line.replace('datetime.datetime.utcnow', 'datetime.now()')
|
|
764
|
-
if 'Column(date' in each_fixed_line:
|
|
765
|
-
each_fixed_line = each_fixed_line.replace('Column(dat', 'column(Date')
|
|
766
|
-
create_db_model_lines.append(each_fixed_line)
|
|
767
|
-
|
|
768
|
-
model_code = "\n".join(model_lines)
|
|
769
|
-
if '\\n' in model_code:
|
|
770
|
-
log.debug(f'.. fix_and_write_model_file detects \\n - attempting fix')
|
|
771
|
-
model_code = model_code.replace('\\n', '\n')
|
|
772
|
-
try:
|
|
773
|
-
ast.parse(model_code)
|
|
774
|
-
except SyntaxError as exc:
|
|
775
|
-
log.error(f"Model Class Error: {model_code}")
|
|
776
|
-
self.post_error = f"Model Class Error: {exc}"
|
|
777
|
-
return create_db_model_lines
|
|
778
|
-
|
|
779
|
-
def insert_test_data_lines(test_data_lines : list[str]) -> list[str]:
|
|
780
|
-
"""Insert test data lines into the model file
|
|
781
|
-
|
|
782
|
-
Args:
|
|
783
|
-
test_data_lines (list(str)):
|
|
784
|
-
* initially header (engine =, sesssion =)
|
|
785
|
-
* this function appends CPT test data
|
|
786
|
-
|
|
787
|
-
Returns:
|
|
788
|
-
list[str]: variable names for the test data rows (for create_all)
|
|
789
|
-
"""
|
|
790
|
-
|
|
791
|
-
def fix_test_data_line(each_fixed_line: str) -> str:
|
|
792
|
-
"""Fix the test data line
|
|
793
|
-
|
|
794
|
-
Args:
|
|
795
|
-
each_fixed_line (str): the test data line
|
|
796
|
-
|
|
797
|
-
Returns:
|
|
798
|
-
str: the fixed test data line
|
|
799
|
-
"""
|
|
800
|
-
|
|
801
|
-
if '=null' in each_fixed_line:
|
|
802
|
-
each_fixed_line = each_fixed_line.replace('=None', '=date')
|
|
803
|
-
if '=datetime' in each_fixed_line:
|
|
804
|
-
each_fixed_line = each_fixed_line.replace('=datetime.date', '=date')
|
|
805
|
-
if 'datetime.datetime.utcnow' in each_fixed_line:
|
|
806
|
-
each_fixed_line = each_fixed_line.replace('datetime.datetime.utcnow', 'datetime.now()')
|
|
807
|
-
if 'datetime.date.today' in each_fixed_line:
|
|
808
|
-
each_fixed_line = each_fixed_line.replace('datetime.date.today', 'datetime.today')
|
|
809
|
-
if 'engine = create_engine' in each_fixed_line: # CBT sometimes has engine = create_engine, so do we!
|
|
810
|
-
each_fixed_line = each_fixed_line.replace('engine = create_engine', '# engine = create_engine')
|
|
811
|
-
check_for_row_name = False
|
|
812
|
-
if each_fixed_line.startswith('Base') or each_fixed_line.startswith('engine'):
|
|
813
|
-
check_for_row_name = False
|
|
814
|
-
if 'Base.metadata.create_all(engine)' in each_fixed_line:
|
|
815
|
-
each_fixed_line = each_fixed_line.replace('Base.metadata.create_all(engine)', '# Base.metadata.create_all(engine)')
|
|
816
|
-
return each_fixed_line
|
|
817
|
-
|
|
818
|
-
row_names = list()
|
|
819
|
-
use_test_data_rows = True # CPT test data, new format - test_data_rows (*way* less variable)
|
|
820
|
-
if use_test_data_rows & hasattr(self.response_dict, 'test_data_rows'):
|
|
821
|
-
test_data_rows = self.response_dict.test_data_rows
|
|
822
|
-
log.debug(f'.... test_data_rows: {len(test_data_rows)}')
|
|
823
|
-
for each_row in test_data_rows:
|
|
824
|
-
each_fixed_line = fix_test_data_line(each_row.code)
|
|
825
|
-
test_data_lines.append(each_fixed_line)
|
|
826
|
-
row_names.append(each_row.test_data_row_variable)
|
|
827
|
-
pass
|
|
828
|
-
else: # CPT test data, old format - rows, plus session, engine etc (quite variable)
|
|
829
|
-
test_data_lines_ori = self.response_dict.test_data.split('\n') # gpt response
|
|
830
|
-
log.debug(f'.... test_data_lines...')
|
|
831
|
-
for each_line in test_data_lines_ori:
|
|
832
|
-
each_fixed_line = fix_test_data_line(each_line)
|
|
833
|
-
check_for_row_name = True
|
|
834
|
-
test_data_lines.append(each_fixed_line) # append the fixed test data line
|
|
835
|
-
if check_for_row_name and ' = ' in each_line and '(' in each_line: # CPT test data might have: tests = []
|
|
836
|
-
assign = each_line.split(' = ')[0]
|
|
837
|
-
# no tokens for: Session = sessionmaker(bind=engine) or session = Session()
|
|
838
|
-
if '.' not in assign and 'Session' not in each_line and 'session.' not in each_line:
|
|
839
|
-
row_names.append(assign)
|
|
840
|
-
return row_names
|
|
841
|
-
|
|
842
|
-
create_db_model_lines = list()
|
|
843
|
-
create_db_model_lines.append(f'# using resolved_model {self.resolved_model}')
|
|
844
|
-
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)
|
|
845
|
-
genai_svcs.get_lines_from_file(f'system/genai/create_db_models_inserts/create_db_models_imports.py'))
|
|
846
|
-
create_db_model_lines.append("\nfrom sqlalchemy.dialects.sqlite import *\n") # specific for genai
|
|
847
|
-
|
|
848
|
-
models = self.response_dict.models
|
|
849
|
-
|
|
850
|
-
# Usage inside the class
|
|
851
|
-
create_db_model_lines = insert_model_lines(models, create_db_model_lines)
|
|
852
|
-
|
|
853
|
-
with open(f'{self.project.from_model}', "w") as create_db_model_file:
|
|
854
|
-
create_db_model_file.write("".join(create_db_model_lines))
|
|
855
|
-
create_db_model_file.write("\n\n# end of model classes\n\n")
|
|
856
|
-
|
|
857
|
-
# classes done, create db and add test_data code
|
|
858
|
-
test_data_lines = genai_svcs.get_lines_from_file(f'system/genai/create_db_models_inserts/create_db_models_create_db.py')
|
|
859
|
-
test_data_lines.append('session.commit()')
|
|
860
|
-
|
|
861
|
-
row_names = insert_test_data_lines(test_data_lines)
|
|
862
|
-
|
|
863
|
-
test_data_lines.append('\n\n')
|
|
864
|
-
row_name_list = ', '.join(row_names)
|
|
865
|
-
add_rows = f'session.add_all([{row_name_list}])'
|
|
866
|
-
test_data_lines.append(add_rows )
|
|
867
|
-
test_data_lines.append('session.commit()')
|
|
868
|
-
test_data_lines.append('# end of test data\n\n')
|
|
869
|
-
|
|
870
|
-
test_data_lines_result = []
|
|
871
|
-
for line in test_data_lines:
|
|
872
|
-
test_data_lines_result += line.split('\n')
|
|
873
|
-
|
|
874
|
-
with open(f'{self.project.from_model}', "a") as create_db_model_file:
|
|
875
|
-
create_db_model_file.write("\ntry:\n ")
|
|
876
|
-
create_db_model_file.write("\n ".join(test_data_lines_result))
|
|
877
|
-
create_db_model_file.write("\nexcept Exception as exc:\n")
|
|
878
|
-
create_db_model_file.write(" print(f'Test Data Error: {exc}')\n")
|
|
879
|
-
|
|
880
|
-
log.debug(f'.. code for db creation and test data: {self.project.from_model}')
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
def get_lines_from_fileZZ(self, file_name: str) -> list[str]:
|
|
884
|
-
"""Get lines from a file todo: migrate to svcs
|
|
885
|
-
|
|
886
|
-
Args:
|
|
887
|
-
file_name (str): the file name
|
|
888
|
-
|
|
889
|
-
Returns:
|
|
890
|
-
list[str]: the lines from the file
|
|
891
|
-
"""
|
|
892
|
-
|
|
893
|
-
with open(file_name, "r") as file:
|
|
894
|
-
lines = file.readlines()
|
|
895
|
-
return lines
|
|
896
|
-
|
|
897
|
-
def z_get_and_save_raw_response_data(self, completion: object, response_dict: dict):
|
|
898
|
-
"""
|
|
899
|
-
Write response_dict --> system/genai/temp/chatgpt_original/retry.response
|
|
900
|
-
"""
|
|
901
|
-
|
|
902
|
-
''' TODO - is exception used instead of return_code...
|
|
903
|
-
# Check if the request was successful
|
|
904
|
-
if completion.status_code == 400:
|
|
905
|
-
raise Exception("Bad ChatGPT Request: " + completion.text)
|
|
906
|
-
|
|
907
|
-
if completion.status_code != 200:
|
|
908
|
-
print("Error:", completion.status_code, completion.text) # eg, You exceeded your current quota
|
|
909
|
-
'''
|
|
910
|
-
with open(f'system/genai/temp/chatgpt_original.response', "w") as response_file: # save for debug
|
|
911
|
-
json.dump(response_dict, response_file, indent=4)
|
|
912
|
-
with open(f'system/genai/temp/chatgpt_retry.response', "w") as response_file: # repair this & retry
|
|
913
|
-
json.dump(response_dict, response_file, indent=4)
|
|
914
|
-
return
|
|
915
|
-
|
|
916
|
-
def z_genai_cli_rule_suggesions_unused(project_name: str) -> dict:
|
|
917
|
-
pass
|
|
918
656
|
|
|
919
657
|
def genai_cli_with_retry(using: str, db_url: str, repaired_response: str, genai_version: str,
|
|
920
658
|
retries: int, opt_locking: str, prompt_inserts: str, quote: bool,
|
|
@@ -10,7 +10,7 @@ from pathlib import Path
|
|
|
10
10
|
import os
|
|
11
11
|
import sys
|
|
12
12
|
import create_from_model.api_logic_server_utils as utils
|
|
13
|
-
|
|
13
|
+
from api_logic_server_cli.genai.genai_fatal_excp import GenAIException
|
|
14
14
|
import time
|
|
15
15
|
from openai import OpenAI
|
|
16
16
|
import json
|
|
@@ -322,6 +322,11 @@ def model2code(model: DotMap) -> str:
|
|
|
322
322
|
tree = ast.parse(model_code.replace('\\n', '\n'))
|
|
323
323
|
except Exception as exc:
|
|
324
324
|
raise exc
|
|
325
|
+
# check for reserved words... these can fail before sqlacodegen can fix, and might inter-relate, so quit
|
|
326
|
+
if model.name in ['column', 'Column', 'table', 'Table', 'session', 'Session', 'base', 'Base']:
|
|
327
|
+
log.error(f"Reserved word in model name: {model.name}")
|
|
328
|
+
raise GenAIException(f"Reserved word in model name: {model.name}")
|
|
329
|
+
|
|
325
330
|
|
|
326
331
|
# Function to add a docstring to a class node
|
|
327
332
|
def add_docstring_to_class(node, docstring):
|
|
@@ -418,7 +423,7 @@ def fix_model_lines(model: DotMap, use_relns: bool = True, post_error: str = Non
|
|
|
418
423
|
if current_url != proper_url:
|
|
419
424
|
log.debug(f'.. fixed sqlite url: {current_url} -> system/genai/temp/create_db_models.sqlite')
|
|
420
425
|
if 'class ' in each_line:
|
|
421
|
-
# yes, tempting fix... but it fails in SqlAlchemy with missing __tablename__
|
|
426
|
+
# yes, tempting to fix... but it fails in SqlAlchemy with missing __tablename__
|
|
422
427
|
# each_line = each_line.replace(':', '(Base):') # sometimes it forgets the Base
|
|
423
428
|
if 'Base' not in each_line:
|
|
424
429
|
log.debug(f'.. fix_and_write_model_file detects class with no Base - raise excp to trigger retry')
|
|
@@ -469,6 +474,13 @@ def fix_and_write_model_file(response_dict: DotMap, save_dir: str, post_error:
|
|
|
469
474
|
try: # based on model_lines
|
|
470
475
|
model_code = model2code(each_model)
|
|
471
476
|
log.info(f"Added description to model: {each_model.name}: {model_code}")
|
|
477
|
+
except GenAIException as exc:
|
|
478
|
+
''' this does not work - creates a duplicate class, so let's just bail
|
|
479
|
+
if post_error is not None:
|
|
480
|
+
post_error = exc.args[0]
|
|
481
|
+
continue
|
|
482
|
+
'''
|
|
483
|
+
raise exc
|
|
472
484
|
except Exception as exc:
|
|
473
485
|
log.error(f"Failed to add description to model: {exc}")
|
|
474
486
|
log.debug(f"model: {each_model}")
|
|
@@ -236,7 +236,7 @@ class GenAIUtils:
|
|
|
236
236
|
create_fixup_files(self)
|
|
237
237
|
log.info(f".. fixup complete: {self.using}/fixup")
|
|
238
238
|
log.info(f".. .. next step: cd <manager> eg, cd ..")
|
|
239
|
-
log.info(f".. .. and then, create fixed project: als genai --
|
|
239
|
+
log.info(f".. .. and then, create fixed project: als genai --repaired-response={self.project.project_name}/{self.using}/fixup/response_fixup.json --project-name=fixed_project")
|
|
240
240
|
|
|
241
241
|
def import_genai_project(self) -> None:
|
|
242
242
|
"""
|
|
@@ -40,7 +40,7 @@ def expose_models(api, method_decorators = []):
|
|
|
40
40
|
"""
|
|
41
41
|
|
|
42
42
|
debug_inspect_list = inspect.getmembers(database.models)
|
|
43
|
-
pass
|
|
43
|
+
pass
|
|
44
44
|
# Get all the subclasses of the Base class and expose them in the api
|
|
45
45
|
for name, obj in inspect.getmembers(database.models):
|
|
46
46
|
if inspect.isclass(obj) and issubclass(obj, database.models.SAFRSBaseX) and obj is not database.models.SAFRSBaseX:
|
|
@@ -78,7 +78,7 @@ app_logger = server_setup.logging_setup()
|
|
|
78
78
|
|
|
79
79
|
flask_app = Flask("API Logic Server", template_folder='ui/templates') # templates to load ui/admin/admin.yaml
|
|
80
80
|
|
|
81
|
-
CORS(flask_app, resources=[{r"/api/*": {"origins": "*"}}],
|
|
81
|
+
CORS(flask_app, resources=[{r"/api/*": {"origins": "*"}},{r"/ontimizeweb/*": {"origins": "*"}}],
|
|
82
82
|
allow_headers=["Content-Type", "Authorization", "Access-Control-Allow-Credentials"],supports_credentials=True)
|
|
83
83
|
|
|
84
84
|
args = server_setup.get_args(flask_app) # creation defaults
|
|
@@ -100,7 +100,7 @@ server_setup.api_logic_server_setup(flask_app, args)
|
|
|
100
100
|
AdminLoader.admin_events(flask_app = flask_app, args = args, validation_error = ValidationError)
|
|
101
101
|
|
|
102
102
|
if __name__ == "__main__":
|
|
103
|
-
msg = f'API Logic Project loaded (not WSGI), version
|
|
103
|
+
msg = f'API Logic Project loaded (not WSGI), version: api_logic_server_version\n'
|
|
104
104
|
msg += f'.. startup message: {start_up_message}\n'
|
|
105
105
|
if server_setup.is_docker():
|
|
106
106
|
msg += f' (running from docker container at flask_host: {args.flask_host} - may require refresh)\n'
|
|
@@ -40,11 +40,31 @@
|
|
|
40
40
|
# whether to invoke dbinit befoce connecting...
|
|
41
41
|
# APILOGICSERVER_ORACLE_THICK=~/Downloads/instantclient_19_16
|
|
42
42
|
|
|
43
|
+
# enables aggregate defaulting, and defaulting for all numerics and strings
|
|
44
|
+
# used to initialize rows prior to logic, to avoid excessive None testing
|
|
45
|
+
# AGGREGATE_DEFAULTS=True
|
|
46
|
+
# ALL_DEFAULTS=True
|
|
47
|
+
|
|
43
48
|
APILOGICPROJECT_VERBOSE=True
|
|
44
49
|
|
|
45
50
|
# APILOGICPROJECT_LOG_CONFIG=
|
|
46
|
-
|
|
51
|
+
# name of log.yml file (eg, config/logging_prod.yml)
|
|
47
52
|
|
|
48
53
|
# APILOGICPROJECT_STOP_OK=FALSE
|
|
49
|
-
|
|
54
|
+
# dev only - enable stop url: http://localhost:5656/stop?msg=reason
|
|
55
|
+
|
|
56
|
+
# APILOGICPROJECT_KAFKA_PRODUCER = '{"bootstrap.servers": "localhost:9092"}' # , "client.id": "aaa.b.c.d"}'
|
|
57
|
+
# APILOGICPROJECT_KAFKA_CONSUMER = '{"bootstrap.servers": "localhost:9092", "group.id": "als-default-group1"}'KAFKA_CONSUMER = None # comment out to enable Kafka consumer
|
|
50
58
|
|
|
59
|
+
# N8N Webhook Args
|
|
60
|
+
# see https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/?utm_source=n8n_app&utm_medium=node_settings_modal-credential_link&utm_campaign=n8n-nodes-base.webhook#path
|
|
61
|
+
# APILOGICPROJECT_wh_scheme = "http"
|
|
62
|
+
# APILOGICPROJECT_wh_server = "localhost" # or cloud.n8n.io...
|
|
63
|
+
# APILOGICPROJECT_wh_port = 5678
|
|
64
|
+
# APILOGICPROJECT_wh_endpoint = "webhook-test"
|
|
65
|
+
# APILOGICPROJECT_wh_path = "002fa0e8-f7aa-4e04-b4e3-e81aa29c6e69"
|
|
66
|
+
# APILOGICPROJECT_token = "YWRtaW46cA=="
|
|
67
|
+
# APILOGICPROJECT_N8N_PRODUCER = {"authorization": f"Basic {token}", "n8n_url": f'"{wh_scheme}://{wh_server}:{wh_port}/{wh_endpoint}/{wh_path}"'}
|
|
68
|
+
# Or enter the n8n_url directly:
|
|
69
|
+
# APILOGICPROJECT_N8N_PRODUCER = {"authorization": f"Basic {token}","n8n_url":"http://localhost:5678/webhook-test/002fa0e8-f7aa-4e04-b4e3-e81aa29c6e69"}
|
|
70
|
+
# APILOGICPROJECT_N8N_PRODUCER = None # comment out to enable N8N producer
|
api_logic_server_cli/prototypes/base/devops/docker-standard-image/docker-compose-standard-image.yml
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
# cd <project>
|
|
3
|
+
# sh ./devops/docker-standard-image/docker-compose.sh
|
|
4
|
+
# --> builds, runs at localhost:5656
|
|
5
|
+
|
|
6
|
+
# docker-compose -f devops/docker-standard-image/docker-compose-standard-image.yml up
|
|
7
|
+
# docker-compose -f devops/docker-standard-image/docker-compose-standard-image.yml down
|
|
8
|
+
|
|
9
|
+
# if you have run docker compose up (above), you must run docker compose down to run directly:
|
|
10
|
+
# docker run -it --rm --name api_logic_project -p 5656:5656 --env-file ./devops/docker-standard-image/env.list -v ./:/app apilogicserver/api_logic_server python3 /app/api_logic_server_run.py
|
|
11
|
+
|
|
12
|
+
services:
|
|
13
|
+
api-logic-server:
|
|
14
|
+
image: apilogicserver/api_logic_server
|
|
15
|
+
container_name: api_logic_project
|
|
16
|
+
environment:
|
|
17
|
+
- APILOGICPROJECT_VERBOSE=true
|
|
18
|
+
- SECURITY_ENABLED=true
|
|
19
|
+
env_file:
|
|
20
|
+
- ./env.list
|
|
21
|
+
volumes:
|
|
22
|
+
- ./../..:/app
|
|
23
|
+
ports:
|
|
24
|
+
- 5656:5656
|
|
25
|
+
command: python3 /app/api_logic_server_run.py
|
|
26
|
+
stdin_open: true
|
|
27
|
+
tty: true
|
|
28
|
+
restart: unless-stopped
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# these values override the Config values, and the CLI arguments
|
|
2
|
+
# the values below are for testing - uncomment and view them on console log on server start
|
|
3
|
+
# #als: configure environment variables
|
|
4
|
+
|
|
5
|
+
# ip to which flask will be bound (default: 0.0.0.0)
|
|
6
|
+
# APILOGICPROJECT_FLASK_HOST=flask-host-e
|
|
7
|
+
|
|
8
|
+
# port (Flask) (default: 5656)
|
|
9
|
+
# APILOGICPROJECT_PORT=port-e
|
|
10
|
+
|
|
11
|
+
# ip clients use to access API (default: localhost)
|
|
12
|
+
# APILOGICPROJECT_SWAGGER_HOST=swagger-host-e
|
|
13
|
+
|
|
14
|
+
# swagger port (eg, 443 for codespaces) (default: 5656)
|
|
15
|
+
# APILOGICPROJECT_SWAGGER_PORT=swagger-port-e
|
|
16
|
+
|
|
17
|
+
# http or https (default: http)
|
|
18
|
+
# APILOGICPROJECT_HTTP_SCHEME=http-scheme-e
|
|
19
|
+
|
|
20
|
+
# APILOGICPROJECT_HTTP_SCHEME=http
|
|
21
|
+
|
|
22
|
+
# for reverse proxy cases where the entire URI must be specified
|
|
23
|
+
# APILOGICPROJECT_CLIENT_URI=httpe://hoste:porte
|
|
24
|
+
|
|
25
|
+
# TODO specify database uri's here, e.g:
|
|
26
|
+
# APILOGICPROJECT_SQLALCHEMY_DATABASE_URI=postgresql://postgres:p@postgresql-container/basic_demo
|
|
27
|
+
# APILOGICPROJECT_SQLALCHEMY_DATABASE_URI_AUTHENTICATION=postgresql://postgres:p@postgresql-container/authdb
|
|
28
|
+
# APILOGICPROJECT_SQLALCHEMY_DATABASE_URI=mysql+pymysql://root:p@mysql-container:3306/basic_demo
|
|
29
|
+
# APILOGICPROJECT_SQLALCHEMY_DATABASE_URI_AUTHENTICATION=mysql+pymysql://root:p@mysql-container:3306/authdb
|
|
30
|
+
|
|
31
|
+
# APILOGICPROJECT_SECURITY_ENABLED=false
|
|
32
|
+
# APILOGICPROJECT_KEYCLOAK_REALM=kcals
|
|
33
|
+
# APILOGICPROJECT_KEYCLOAK_BASE=http://localhost:8080//realms/kcals
|
|
34
|
+
# APILOGICPROJECT_KEYCLOAK_BASE_URL=http://localhost:8080
|
|
35
|
+
# APILOGICPROJECT_KEYCLOAK_CLIENT_ID=alsclient
|
|
36
|
+
|
|
37
|
+
# required if you are not running from venv or docker apilogicserver/api_logic_server
|
|
38
|
+
# APILOGICPROJECT_APILOGICSERVERHOME=src/ApiLogicServer-src
|
|
39
|
+
|
|
40
|
+
# whether to invoke dbinit befoce connecting...
|
|
41
|
+
# APILOGICSERVER_ORACLE_THICK=~/Downloads/instantclient_19_16
|
|
42
|
+
|
|
43
|
+
# enables aggregate defaulting, and defaulting for all numerics and strings
|
|
44
|
+
# used to initialize rows prior to logic, to avoid excessive None testing
|
|
45
|
+
# AGGREGATE_DEFAULTS=True
|
|
46
|
+
# ALL_DEFAULTS=True
|
|
47
|
+
|
|
48
|
+
APILOGICPROJECT_VERBOSE=True
|
|
49
|
+
|
|
50
|
+
# APILOGICPROJECT_LOG_CONFIG=
|
|
51
|
+
# name of log.yml file (eg, config/logging_prod.yml)
|
|
52
|
+
|
|
53
|
+
# APILOGICPROJECT_STOP_OK=FALSE
|
|
54
|
+
# dev only - enable stop url: http://localhost:5656/stop?msg=reason
|
|
55
|
+
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
## Using the standard container
|
|
2
|
+
|
|
3
|
+
There are many, many ways of using docker.
|
|
4
|
+
|
|
5
|
+
1. You can use the existing apilogicserver/api_logic_server container as shown in `devops/docker-standard-image`.
|
|
6
|
+
|
|
7
|
+
2. Or, you can create your own container, as described in the next section.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## Creating per-project containers
|
|
12
|
+
|
|
13
|
+
Use these directories to deploy your system to the cloud. This means you can provide a preview of [working software](https://apilogicserver.github.io/Docs/Working-Software-Now/) for your team:
|
|
2
14
|
|
|
3
15
|
* **Developers** can use the API to begin custom User Interface development
|
|
4
16
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
### Add Natural
|
|
1
|
+
### Add Natural Language Logic to Your Project
|
|
2
2
|
|
|
3
|
-
You can add Natural Language logic files to this directory, e.g
|
|
3
|
+
You can add Natural Language logic files to this directory, e.g. in `genai_demo/docs/logic`:
|
|
4
|
+
|
|
5
|
+
* `valid_names.prompt`: Customer and Product Names cannot be 'x'
|
|
6
|
+
* `valid_currency.prompt`: Customer credit limits cannot be negative; Product prices must be positive.
|
|
4
7
|
|
|
5
8
|
Then, use GenAI to create executable logic in your `logic/logic_discovery` directory, e.g.,
|
|
6
9
|
|
|
@@ -13,3 +16,4 @@ Notes:
|
|
|
13
16
|
1. Ensure derived attributes exist in the data model (see [Database Design Changes](https://apilogicserver.github.io/Docs/Database-Changes/))
|
|
14
17
|
2. Be sure to initialize such attributes in your database
|
|
15
18
|
3. For more information, [click here](https://apilogicserver.github.io/Docs/WebGenAI-CLI/#add-logic-to-existing-projects)
|
|
19
|
+
4. Consider renaming your logic files afterward (`valid_names.z-prompt`)so they are skipped on future runs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
2
|
|
|
3
|
-
Version 2.
|
|
3
|
+
Version 2.1
|
|
4
4
|
|
|
5
5
|
Invoked at server start (api_logic_server_run.py -> config/setup.py)
|
|
6
6
|
|
|
@@ -117,5 +117,4 @@ def send_kafka_message(kafka_topic: str, kafka_key: str = None, msg: str="", jso
|
|
|
117
117
|
|
|
118
118
|
|
|
119
119
|
def send_row_to_kafka(row: object, old_row: object, logic_row: LogicRow, with_args: dict):
|
|
120
|
-
|
|
121
|
-
send_kafka_message(logic_row=logic_row, kafka_topic=with_args["topic"])
|
|
120
|
+
send_kafka_message(logic_row=logic_row, kafka_topic=with_args["topic"])
|
|
@@ -10,6 +10,14 @@ This describes how to use Logic; for more information, [see here](https://apilog
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
## Natural Language vs. IDE
|
|
14
|
+
|
|
15
|
+
If you are using WebGenAI, you can specify rules in Natural Language. You can also augment them in the IDE using code completion. There are some important usage guidelines.
|
|
16
|
+
|
|
17
|
+
> You should generally not alter any files in the `wg_rules` directory. For more information, see [WebGenAI](https://apilogicserver.github.io/Docs/WebGenAI/), and [WebGenAI Logic](https://apilogicserver.github.io/Docs/WebGenAI-CLI.md#natural-language-logic).
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
13
21
|
## Examples
|
|
14
22
|
Examples from tutorial project:
|
|
15
23
|
* Examples drawn from [tutorial project](https://github.com/ApiLogicServer/demo/blob/main/logic/declare_logic.py)
|
|
@@ -33,7 +33,9 @@ class Roles():
|
|
|
33
33
|
read_only = "readonly"
|
|
34
34
|
admin = "CS_ADMIN"
|
|
35
35
|
public="public" # p1/p (no roles, but gets public)
|
|
36
|
+
sa="sa"
|
|
36
37
|
|
|
38
|
+
DefaultRolePermission(to_role=Roles.sa, can_read=True, can_update=True, can_insert=True, can_delete=True)
|
|
37
39
|
DefaultRolePermission(to_role=Roles.tenant, can_read=True, can_delete=True)
|
|
38
40
|
DefaultRolePermission(to_role=Roles.admin, can_read=True, can_insert=True,can_update=True, can_delete=True)
|
|
39
41
|
DefaultRolePermission(to_role=Roles.manager, can_read=True, can_insert=True,can_update=True, can_delete=False)
|
|
@@ -46,13 +46,13 @@ def declare_logic():
|
|
|
46
46
|
Rule.sum(derive=Order.amount_total, as_sum_of=Item.amount)
|
|
47
47
|
|
|
48
48
|
def derive_amount(row: models.Item, old_row: models.Item, logic_row: LogicRow):
|
|
49
|
-
amount = row.
|
|
50
|
-
if row.
|
|
49
|
+
amount = row.quantity * row.unit_price
|
|
50
|
+
if row.product.carbon_neutral == True and row.quantity >= 10:
|
|
51
51
|
amount = amount * Decimal(0.9) # breakpoint here
|
|
52
52
|
return amount
|
|
53
53
|
|
|
54
54
|
# Items.Amount = Quantity * UnitPrice with discount for CarbonNeutral products.
|
|
55
|
-
Rule.formula(derive=models.Item.
|
|
55
|
+
Rule.formula(derive=models.Item.amount,
|
|
56
56
|
calling=derive_amount)
|
|
57
57
|
|
|
58
58
|
# Item.unit_price is copied from Product.unit_price.
|