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.
Files changed (57) hide show
  1. api_logic_server_cli/api_logic_server.py +4 -2
  2. api_logic_server_cli/api_logic_server_info.yaml +3 -3
  3. api_logic_server_cli/cli.py +1 -1
  4. api_logic_server_cli/create_from_model/__pycache__/ont_build.cpython-312.pyc +0 -0
  5. api_logic_server_cli/create_from_model/ont_build.py +8 -8
  6. api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/asset-manifest.json +3 -3
  7. api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/build-0213.txt +1 -0
  8. api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/index.html +1 -1
  9. api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/static/js/main.7c8c0e37.js +3 -0
  10. api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/static/js/{main.bfe80d1d.js.map → main.7c8c0e37.js.map} +1 -1
  11. api_logic_server_cli/database/nw-gold.sqlite +0 -0
  12. api_logic_server_cli/genai/genai.py +17 -279
  13. api_logic_server_cli/genai/genai_fatal_excp.py +5 -0
  14. api_logic_server_cli/genai/genai_svcs.py +14 -2
  15. api_logic_server_cli/genai/genai_utils.py +1 -1
  16. api_logic_server_cli/prototypes/base/api/expose_api_models.py +1 -1
  17. api_logic_server_cli/prototypes/base/api/system/expression_parser.py +1 -1
  18. api_logic_server_cli/prototypes/base/api_logic_server_run.py +2 -2
  19. api_logic_server_cli/prototypes/base/devops/docker-image/env.list +22 -2
  20. api_logic_server_cli/prototypes/base/devops/docker-standard-image/docker-compose-standard-image.yml +28 -0
  21. api_logic_server_cli/prototypes/base/devops/docker-standard-image/env.list +55 -0
  22. api_logic_server_cli/prototypes/base/devops/readme-devops.md +13 -1
  23. api_logic_server_cli/prototypes/base/docs/logic/readme.md +6 -2
  24. api_logic_server_cli/prototypes/base/integration/kafka/kafka_producer.py +2 -3
  25. api_logic_server_cli/prototypes/base/logic/readme_declare_logic.md +8 -0
  26. api_logic_server_cli/prototypes/base/security/declare_security.py +2 -0
  27. api_logic_server_cli/prototypes/genai_demo/logic/declare_logic.py +3 -3
  28. api_logic_server_cli/prototypes/manager/.vscode/ApiLogicServer.code-workspace +2 -2
  29. api_logic_server_cli/prototypes/manager/.vscode/launch.json +20 -20
  30. api_logic_server_cli/prototypes/manager/system/Manager_workspace.code-workspace +2 -3
  31. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.prompt +1 -1
  32. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_formal.prompt +0 -2
  33. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_informal.prompt +0 -2
  34. api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/002_create_db_models.prompt +3 -132
  35. api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/Invoice Made Ready.png +0 -0
  36. api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/readme.md +3 -3
  37. api_logic_server_cli/prototypes/manager/system/genai/learning_requests/logic_bank_api.prompt +7 -0
  38. api_logic_server_cli/prototypes/manager/system/genai/webg_local/.DS_Store +0 -0
  39. api_logic_server_cli/prototypes/manager/system/genai/webg_local/run_web_genai.sh +10 -0
  40. api_logic_server_cli/prototypes/manager/system/genai/webg_local/webg-projects/.DS_Store +0 -0
  41. api_logic_server_cli/prototypes/manager/system/genai/webg_local/webg-projects/by-ulid/.DS_Store +0 -0
  42. api_logic_server_cli/prototypes/manager/system/genai/webg_local/webg-projects/public/.DS_Store +0 -0
  43. api_logic_server_cli/prototypes/manager/system/genai/webg_local/webg-projects/wgadmin/.DS_Store +0 -0
  44. api_logic_server_cli/prototypes/manager/system/genai/webg_local/webg-temp/.DS_Store +0 -0
  45. api_logic_server_cli/prototypes/manager/system/genai/webg_local/webg_config/.DS_Store +0 -0
  46. api_logic_server_cli/prototypes/manager/system/genai/webg_local/webg_config/web_genai.txt +8 -0
  47. api_logic_server_cli/prototypes/nw/logic/declare_logic.py +1 -1
  48. api_logic_server_cli/sqlacodegen_wrapper/sqlacodegen/sqlacodegen/codegen.py +2 -2
  49. {ApiLogicServer-14.3.7.dist-info → apilogicserver-14.3.14.dist-info}/METADATA +3 -3
  50. {ApiLogicServer-14.3.7.dist-info → apilogicserver-14.3.14.dist-info}/RECORD +55 -42
  51. {ApiLogicServer-14.3.7.dist-info → apilogicserver-14.3.14.dist-info}/WHEEL +1 -1
  52. api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/build-0106.txt +0 -1
  53. api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/static/js/main.bfe80d1d.js +0 -3
  54. /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
  55. {ApiLogicServer-14.3.7.dist-info → apilogicserver-14.3.14.dist-info}/LICENSE +0 -0
  56. {ApiLogicServer-14.3.7.dist-info → apilogicserver-14.3.14.dist-info}/entry_points.txt +0 -0
  57. {ApiLogicServer-14.3.7.dist-info → apilogicserver-14.3.14.dist-info}/top_level.txt +0 -0
@@ -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 = False
319
- if 'LogicBank' in prompt and K_LogicBankOff not in prompt: # if prompt has logic, we need to insert the training
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 proced")
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,
@@ -0,0 +1,5 @@
1
+ class GenAIException(BaseException):
2
+ """Raise when unable to create db_models (missing Base, reserved word)"""
3
+
4
+ def __init__(self, message):
5
+ self.message = message
@@ -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 --using=genai_demo.prompt --repaired-response={self.project.project_name}/{self.using}/fixup/response_fixup.json --project-name=fixed_project")
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 #vh
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:
@@ -154,7 +154,7 @@ def fixup_sort(clz, data):
154
154
  return sort
155
155
  def fixup_data(data, sqltypes):
156
156
  new_data = None
157
- if data:
157
+ if data and isinstance(data, dict):
158
158
  new_data = {}
159
159
  for key, value in data.items():
160
160
  new_data[key] = value
@@ -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 {api_logic_server__version}\n'
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
- """ name of log.yml file (eg, config/logging_prod.yml) """
51
+ # name of log.yml file (eg, config/logging_prod.yml)
47
52
 
48
53
  # APILOGICPROJECT_STOP_OK=FALSE
49
- """ dev only - enable stop url: http://localhost:5656/stop?msg=reason """
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
@@ -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
- Use these directories to rapidly 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:
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
+ &nbsp;
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 Languate Logic to Your Project
1
+ ### Add Natural Language Logic to Your Project
2
2
 
3
- You can add Natural Language logic files to this directory, e.g., `check_credit.prompt`.
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.0
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
- if logic_row.row.date_shipped is not None:
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
  &nbsp;
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
+ &nbsp;
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.Quantity * row.UnitPrice
50
- if row.Product.CarbonNeutral == True and row.Quantity >= 10:
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.Amount,
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.