ApiLogicServer 14.3.20__py3-none-any.whl → 14.3.25__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 (42) hide show
  1. api_logic_server_cli/api_logic_server.py +4 -3
  2. api_logic_server_cli/api_logic_server_info.yaml +3 -3
  3. api_logic_server_cli/cli.py +38 -0
  4. api_logic_server_cli/database/nw-gold.sqlite +0 -0
  5. api_logic_server_cli/genai/genai.py +2 -1
  6. api_logic_server_cli/genai/genai_graphics.py +165 -0
  7. api_logic_server_cli/genai/genai_logic_builder.py +2 -2
  8. api_logic_server_cli/genai/genai_svcs.py +8 -0
  9. api_logic_server_cli/prototypes/base/config/config.py +58 -30
  10. api_logic_server_cli/prototypes/base/docs/graphics/readme.md +12 -0
  11. api_logic_server_cli/prototypes/base/security/authentication_provider/keycloak/auth_provider.py +1 -1
  12. api_logic_server_cli/prototypes/base/security/declare_security.py +4 -0
  13. api_logic_server_cli/prototypes/base/ui/admin/admin_loader.py +3 -1
  14. api_logic_server_cli/prototypes/manager/README.md +30 -1
  15. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.prompt +2 -0
  16. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example +68 -60
  17. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/graphics_services.py +41 -0
  18. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/html_template.jinja +76 -0
  19. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/index.html +19 -0
  20. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/sales_by_region.jinja +63 -0
  21. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/service_template_jsonapi_rpc.jinja +37 -0
  22. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/service_template_unused.jinja +38 -0
  23. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/graphics.prompt +18 -0
  24. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/graphics_request.prompt +5 -0
  25. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/response_format.prompt +8 -0
  26. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/sqlite_inserts.prompt +2 -0
  27. api_logic_server_cli/prototypes/manager/webgenai/docker-compose.yml +27 -0
  28. api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/request copy.json +892 -0
  29. api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/request.json +6 -0
  30. api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/response.json +17 -0
  31. api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/response.yaml +59 -0
  32. api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/sales_by_category.prompt +1 -0
  33. {apilogicserver-14.3.20.dist-info → apilogicserver-14.3.25.dist-info}/METADATA +1 -1
  34. {apilogicserver-14.3.20.dist-info → apilogicserver-14.3.25.dist-info}/RECORD +38 -26
  35. {apilogicserver-14.3.20.dist-info → apilogicserver-14.3.25.dist-info}/WHEEL +1 -1
  36. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/zsqlite_inserts_iterations.prompt +0 -29
  37. api_logic_server_cli/prototypes/manager/webgenai/docker-compose-webg.yml +0 -33
  38. api_logic_server_cli/prototypes/manager/webgenai/webg_config/license.json +0 -6
  39. api_logic_server_cli/prototypes/manager/webgenai/webg_config/web_genai.txt +0 -13
  40. {apilogicserver-14.3.20.dist-info → apilogicserver-14.3.25.dist-info}/entry_points.txt +0 -0
  41. {apilogicserver-14.3.20.dist-info → apilogicserver-14.3.25.dist-info}/licenses/LICENSE +0 -0
  42. {apilogicserver-14.3.20.dist-info → apilogicserver-14.3.25.dist-info}/top_level.txt +0 -0
@@ -3,7 +3,7 @@
3
3
  '''
4
4
  ApiLogicServer CLI: given a database url, create [and run] customizable ApiLogicProject.
5
5
  * Basically clones prototype project (api_logic_server_cli/prototypes/base), and creates:
6
- * database/models.py for SQLAlchemy, using modified sqlacodegen & safrs metadata
6
+ * database/models.py for SQLAlchemy,` using modified sqlacodegen & safrs metadata
7
7
  * ui/admin/admin.yaml for the Admin App - using introspected models.py
8
8
  * api/expose_api_models.py for a safrs api - using introspected models.py
9
9
  * Special provisions for NW Sample, to show customizations.
@@ -12,10 +12,11 @@ ApiLogicServer CLI: given a database url, create [and run] customizable ApiLogic
12
12
  Called from api_logic_server_cli.py, by instantiating the ProjectRun object.
13
13
  '''
14
14
 
15
- __version__ = "14.03.20"
15
+ __version__ = "14.03.25"
16
16
  recent_changes = \
17
17
  f'\n\nRecent Changes:\n' +\
18
- "\t03/19/2024 - 14.03.20: [87] sra fix for home.js, licensed webgenai docker \n"\
18
+ "\t03/30/2024 - 14.03.25: WebGenAI fixes for Kafka and Keycloak \n"\
19
+ "\t03/19/2024 - 14.03.20: licensed webgenai docker, [87] sra fix for home.js \n"\
19
20
  "\t02/26/2024 - 14.03.14: [85: reserved words], genai_demo fixes \n"\
20
21
  "\t02/16/2024 - 14.03.12: Docker w/ std container, mgr assistant for local WebG \n"\
21
22
  "\t02/13/2024 - 14.03.11: fixes [78: Keycloak, 79: boolean defaulting, 80: Send If missing attribute] \n"\
@@ -1,3 +1,3 @@
1
- last_created_date: March 14, 2025 17:03:35
2
- last_created_project_name: samples/nw_sample_nocust
3
- last_created_version: 14.03.16
1
+ last_created_date: March 26, 2025 13:40:07
2
+ last_created_project_name: genai_demo
3
+ last_created_version: 14.03.22
@@ -738,6 +738,44 @@ def genai_logic(ctx, using, genai_version: str, retries: int, suggest: click.BOO
738
738
  log.info("")
739
739
 
740
740
 
741
+ @main.command("genai-graphics", cls=HideDunderCommand)
742
+ @click.option('--using',
743
+ default=f'docs/graphics',
744
+ help="File or dir")
745
+ @click.option('--genai-version', 'genai_version',
746
+ default='gpt-4o',
747
+ help="Eg, gpt-3.5-turbo, gpt-4o")
748
+ @click.pass_context
749
+ def genai_graphics(ctx, using, genai_version: str):
750
+ """
751
+ Adds (or suggests) logic to current project.
752
+ """
753
+ global command
754
+ project_dir = resolve_blank_project_name('')
755
+ project_name = Path(project_dir).name
756
+ project = PR.ProjectRun(command="add_security",
757
+ project_name=project_name,
758
+ db_url="",
759
+ execute=False
760
+ )
761
+ project.project_directory, project.api_name, project.merge_into_prototype = \
762
+ create_utils.get_project_directory_and_api_name(project)
763
+ project.project_directory_actual = os.path.abspath(os.getcwd()) # make path absolute, not relative (no /../)
764
+ project.project_directory_path = Path(project.project_directory_actual)
765
+ models_py_path = project.project_directory_path.joinpath('database/models.py')
766
+ project.abs_db_url, project.nw_db_status, project.model_file_name = \
767
+ create_utils.get_abs_db_url("0. Using Sample DB", project, is_auth=True)
768
+
769
+ if not models_py_path.exists():
770
+ log.info(f'... Error - does not appear to be a project: {str(project.project_directory_path)}')
771
+ log.info(f'... Typical usage - cd into project, use --project_name=. \n')
772
+ exit (1)
773
+ from api_logic_server_cli.genai.genai_graphics import GenAIGraphics
774
+ genai_graphics = GenAIGraphics(using=using, project=project, genai_version=genai_version)
775
+ pass
776
+ log.info("")
777
+
778
+
741
779
  @main.command("genai-create", cls=HideDunderCommand)
742
780
  @click.option('--project-name', 'project_name',
743
781
  default=f'{last_created_project_name}',
@@ -27,6 +27,7 @@ from api_logic_server_cli.genai.genai_svcs import K_LogicBankOff
27
27
  from api_logic_server_cli.genai.genai_svcs import K_LogicBankTraining
28
28
  from api_logic_server_cli.genai.genai_svcs import fix_and_write_model_file as fix_and_write_model_file_svcs
29
29
  import api_logic_server_cli.genai.genai_svcs as genai_svcs
30
+ from api_logic_server_cli.genai.genai_graphics import GenAIGraphics
30
31
 
31
32
  log = logging.getLogger(__name__)
32
33
 
@@ -575,7 +576,7 @@ class GenAI(object):
575
576
  except: # intentional try/catch/bury - it's just docs, so don't fail
576
577
  import traceback
577
578
  log.error(f"\n\nERROR creating genai project docs: {docs_dir}\n\n{traceback.format_exc()}")
578
- pass
579
+ genai_graphics = GenAIGraphics(project=self.project, using=None, genai_version=self.project.genai_version)
579
580
 
580
581
  def save_prompt_messages_to_system_genai_temp_project(self):
581
582
  """
@@ -0,0 +1,165 @@
1
+ import shutil
2
+ from typing import Dict, List
3
+ from api_logic_server_cli.cli_args_project import Project
4
+ import logging
5
+ from pathlib import Path
6
+ import importlib
7
+ from api_logic_server_cli.genai.genai_utils import call_chatgpt
8
+ import requests
9
+ import os
10
+ import datetime
11
+ import create_from_model.api_logic_server_utils as utils
12
+ import time
13
+ from openai import OpenAI
14
+ from api_logic_server_cli.genai.genai_svcs import WGResult
15
+ from api_logic_server_cli.genai.genai_svcs import Rule
16
+ import api_logic_server_cli.genai.genai_svcs as genai_svcs
17
+ import json
18
+ from typing import List, Dict
19
+ from pydantic import BaseModel
20
+ from dotmap import DotMap
21
+ from natsort import natsorted
22
+ import glob
23
+ import create_from_model.api_logic_server_utils as create_utils
24
+ from jinja2 import Environment, FileSystemLoader
25
+
26
+
27
+ K_data_model_prompt = "Use SQLAlchemy to create"
28
+
29
+ log = logging.getLogger(__name__)
30
+
31
+ class GenAIGraphics(object):
32
+ """
33
+ Adds Graphics to **existing** projects (genai project or als project):
34
+ * adds `api/api_discovery` file(s) to project
35
+ * adds html to `home.js` (? currently just creating a 1-off in api/api_discovery)
36
+
37
+ Invoked from:
38
+ 1. **New GenAI Project:** for newly created project (e,g, mgr system/genai/examples/genai_demo/genai_demo.prompt)
39
+ * `--using` is None ==> Docs folder already has WGResponse.graphics[]
40
+ 2. **Existing Project:** CLI/genai-graphics existing project, using *docs/graphics* eg
41
+ * `--using` ==> Call ChatGPT for WGResponse.graphics `<project>/docs/graphics/*.prompt`
42
+ * note: dbml not rebuilt after rebuild-from-db
43
+ 3. **Existing WG Project:** in-place (do not create new project with new test data)
44
+ * Same as #1, but requires WG UI change ('in place', 'graphics' button, ...) to use genai_graphics cmd
45
+
46
+
47
+ **Issue:** what is the persistence model for graphics? (eg, in docs/graphics, or docs/response.json, wg database??)
48
+ * if existing wg project, is docs/response.json updated?
49
+
50
+ Open Issues
51
+ * How to integrate with als/wg home.js?
52
+ * How to enforce licensing?
53
+ * How to choose graph vs chart?
54
+
55
+ """
56
+
57
+ def __init__(self, project: Project, using: str, genai_version: str):
58
+ """
59
+ Add graphics to existing projects - [see docs](https://apilogicserver.github.io/Docs/WebGenAI-CLI/#add-graphics-to-existing-projects)
60
+
61
+ see key_module_map() for key methods
62
+
63
+ """
64
+
65
+ self.project = project
66
+ self.project.genai_using = using
67
+ self.manager_path = genai_svcs.get_manager_path()
68
+
69
+ if using is None: # New GenAI Project: use docs/response.json
70
+ graphics_response_path = self.project.project_directory_path.joinpath('docs/response.json')
71
+ else: # Existing (any) Project - use graphics files -> ChatGPT
72
+ graphics_response_path = self.project.project_directory_path.joinpath('docs/graphics/response.json')
73
+ if bypass_for_debug := False:
74
+ pass
75
+ else:
76
+ prompt = genai_svcs.read_and_expand_prompt(self.manager_path.joinpath('system/genai/prompt_inserts/graphics_request.prompt'))
77
+ prompt_lines = prompt.split('\n') # ChatGPT instructions
78
+ prompt_lines.extend(self.append_data_model()) # add data model
79
+ prompt_lines.extend(self.append_graphics_files()) # and the users's requests from graphics files
80
+ prompt_str = "\n".join(prompt_lines)
81
+
82
+ prompt_messages : List[ Dict[str, str] ] = [] # prompt/response conversation to be sent to ChatGPT
83
+ prompt = genai_svcs.get_prompt_you_are()
84
+ prompt["content"] = prompt_str
85
+ prompt_messages.append( prompt )
86
+ genai_svcs.call_chatgpt(messages = prompt_messages,
87
+ using = self.project.project_directory_path.joinpath('docs/graphics'),
88
+ api_version=genai_version)
89
+
90
+ self.process_graphics_response(graphics_response_path)
91
+ pass
92
+
93
+ def process_graphics_response(self, graphics_response_path: Path):
94
+ """ Process graphics response from ChatGPT graphics_response_path """
95
+
96
+ graphic_services_header_path = self.manager_path.joinpath('system/genai/graphics_templates/graphics_services.py')
97
+ graphics_services_path = self.project.project_directory_path.joinpath('api/api_discovery/graphics_services.py')
98
+ shutil.copy(graphic_services_header_path, graphics_services_path)
99
+
100
+ # open and read the graphics_response_path json file
101
+ assert graphics_response_path.exists(), f'Graphics response file not found: {graphics_response_path}'
102
+ with open(graphics_response_path, 'r') as file:
103
+ graphics_response = json.load(file)
104
+ log.info(f'Graphics response loaded from {graphics_response_path}')
105
+ graphics = graphics_response['graphics']
106
+ for each_graphic in graphics: # add each service to api/api_discovery
107
+ self.fix_sqlalchemy_query(each_graphic)
108
+ env = Environment(loader=FileSystemLoader(self.manager_path.joinpath('system/genai/graphics_templates')))
109
+
110
+ template = env.get_template('service_template_jsonapi_rpc.jinja')
111
+ rendered_result = template.render( **each_graphic )
112
+ with open(self.project.project_directory_path.joinpath(f'api/api_discovery/graphics_services.py'), 'a') as out_file:
113
+ out_file.write(rendered_result)
114
+
115
+ template = env.get_template('html_template.jinja')
116
+ rendered_result = template.render( **each_graphic )
117
+ with open(self.project.project_directory_path.joinpath(f'api/api_discovery/{each_graphic['name']}.html'), 'w') as out_file:
118
+ out_file.write(rendered_result)
119
+
120
+ with open(self.project.project_directory_path.joinpath(f'api/api_discovery/{each_graphic['name']}.sql'), 'w') as out_file:
121
+ out_file.write(each_graphic['sql_query'])
122
+
123
+ log.info(f'.. added service: {each_graphic['name']} to api_discovery')
124
+ pass
125
+
126
+ def fix_sqlalchemy_query(self, graphic: Dict):
127
+ """ Fix the SQLAlchemy query for the graphic """
128
+ graphic['sqlalchemy_query'] = graphic['sqlalchemy_query'].replace('\\n', '\n')
129
+ graphic['sqlalchemy_query'] = graphic['sqlalchemy_query'].replace('\"', '"')
130
+ pass
131
+
132
+
133
+ def append_data_model(self) -> List[str]:
134
+ """ Get the data model
135
+
136
+ Returns:
137
+ list: logic_files
138
+ """
139
+
140
+ data_model_lines = []
141
+ data_model_path = self.project.project_directory_path.joinpath('database/models.py')
142
+ assert data_model_path.exists(), f"Data model file not found: {data_model_path}"
143
+ with open(data_model_path, 'r') as file:
144
+ prompt_lines = file.readlines()
145
+ data_model_lines.extend(prompt_lines)
146
+ return data_model_lines
147
+
148
+ def append_graphics_files(self) -> List[str]:
149
+ """ Get graphics files (typically from project)
150
+
151
+ Returns:
152
+ list: logic_files
153
+ """
154
+
155
+ graphics_lines = []
156
+ if Path(self.project.genai_using).is_dir(): # conversation from directory
157
+ for each_file in sorted(Path(self.project.genai_using).iterdir()):
158
+ if each_file.is_file() and each_file.suffix == '.prompt':
159
+ # read lines from each_file, and append to prompt
160
+ with open(each_file, 'r') as file:
161
+ prompt_lines = file.readlines()
162
+ graphics_lines.extend(prompt_lines)
163
+ return graphics_lines
164
+
165
+
@@ -27,7 +27,7 @@ log = logging.getLogger(__name__)
27
27
 
28
28
  class GenAILogic(object):
29
29
  """
30
- Called by cli for *existing* project
30
+ Called by cli for *existing* project (must be wg project with docs models)
31
31
 
32
32
  * **Create logic** from *logic files* eg `<project>/docs/logic/check_credit.prompt`
33
33
 
@@ -113,7 +113,7 @@ class GenAILogic(object):
113
113
  return learning_requests # TODO - what if no learning requests?
114
114
 
115
115
  def get_learnings_and_data_model(self) -> List[Dict[str, str]]:
116
- """ Get prompts from the docs dir (so GPT knows model, learnings)
116
+ """ Get prompts from the docs dir (so GPT knows model, learnings) -- **not** from database/models.py
117
117
 
118
118
  Most often, adding logic to new project, which looks like:
119
119
 
@@ -42,6 +42,13 @@ class Model(BaseModel):
42
42
  description: str
43
43
  name: str
44
44
 
45
+ class Graph(BaseModel):
46
+ sqlalchemy_query: str # sqlalchemy group by result = { "result": [ ("name", "value) ] }
47
+ sql_query: str # sql query using group by, returns result = { "result": [ ("name", "value") ] }
48
+ classes_used: str # comma-delimited list of classes used in sqlalchemy_query
49
+ name: str # suggested Python name for sqlalchemy_query
50
+ html_code: str # create a java script app to show a bar chart from sqlalchemy_query result
51
+
45
52
  class TestDataRow(BaseModel):
46
53
  test_data_row_variable: str # the Python test data row variable
47
54
  code: str # Python code to create a test data row instance
@@ -50,6 +57,7 @@ class WGResult(BaseModel): # must match system/genai/prompt_inserts/response_fo
50
57
  # response: str # result
51
58
  models : List[Model] # list of sqlalchemy classes in the response
52
59
  rules : List[Rule] # list rule declarations
60
+ graphics: List[Graph] # list of graphs
53
61
  test_data: str
54
62
  test_data_rows: List[TestDataRow] # list of test data rows
55
63
  test_data_sqlite: str # test data as sqlite INSERT statements
@@ -116,15 +116,16 @@ class Config:
116
116
  # als add-auth --provider-type=sql --db-url=
117
117
  # als add-auth --provider-type=keycloak --db-url=localhost
118
118
  # als add-auth --provider-type=keycloak --db-url=http://10.0.0.77:8080
119
- kc_base = 'http://localhost:8080' # e.g., 'http://localhost:8080'
119
+ kc_base = os.getenv('KEYCLOAK_BASE','https://localhost:8080')
120
+ #kc_base = 'http://localhost:8080'
120
121
  ''' keycloak location '''
121
- KEYCLOAK_REALM = 'kcals'
122
- KEYCLOAK_BASE = f'{kc_base}/realms/{KEYCLOAK_REALM}'
123
- KEYCLOAK_BASE_URL = f'{kc_base}'
124
- KEYCLOAK_CLIENT_ID = 'alsclient'
122
+ KEYCLOAK_REALM = os.getenv('KEYCLOAK_REALM','kcals')
123
+ KEYCLOAK_BASE = os.getenv('KEYCLOAK_BASE',f'{kc_base}')
124
+ KEYCLOAK_BASE_URL = os.getenv('KEYCLOAK_BASE_URL',f'{kc_base}/realms/{KEYCLOAK_REALM}')
125
+ KEYCLOAK_CLIENT_ID = os.getenv('KEYCLOAK_CLIENT_ID','alsclient')
125
126
  ''' keycloak client id '''
126
127
 
127
- SECURITY_ENABLED = False # disables security (regardless of SECURITY_PROVIDER)
128
+ SECURITY_ENABLED = os.getenv("SECURITY_ENABLED",False)
128
129
  SECURITY_PROVIDER = None
129
130
  if os.getenv('SECURITY_ENABLED'): # e.g. export SECURITY_ENABLED=true
130
131
  security_export = os.getenv('SECURITY_ENABLED') # type: ignore # type: str
@@ -141,6 +142,8 @@ class Config:
141
142
  app_logger.debug(f'config.py - security enabled')
142
143
  else:
143
144
  app_logger.info(f'config.py - security disabled')
145
+
146
+ app_logger.info(f'SECURITY_PROVIDER={SECURITY_PROVIDER}')
144
147
 
145
148
  # Begin Multi-Database URLs (from ApiLogicServer add-db...)
146
149
  auth_db_path = str(project_path.joinpath('database/authentication_db.sqlite'))
@@ -164,11 +167,22 @@ class Config:
164
167
  SQLALCHEMY_TRACK_MODIFICATIONS = False
165
168
  PROPAGATE_EXCEPTIONS = False
166
169
 
167
- KAFKA_PRODUCER = '{"bootstrap.servers": "localhost:9092"}' # , "client.id": "aaa.b.c.d"}'
168
- KAFKA_PRODUCER = None # comment out to enable Kafka producer
169
- KAFKA_CONSUMER = '{"bootstrap.servers": "localhost:9092", "group.id": "als-default-group1"}'
170
- KAFKA_CONSUMER = None # comment out to enable Kafka consumer
171
-
170
+ KAFKA_PRODUCER = None
171
+ KAFKA_CONSUMER = None
172
+ KAFKA_CONSUMER_GROUP = None
173
+ KAFKA_SERVER = None
174
+ #KAFKA_SERVER = os.getenv('KAFKA_SERVER','localhost:9092') # if running locally default
175
+ if KAFKA_SERVER:
176
+ app_logger.info(f'config.py - KAFKA_SERVER: {KAFKA_SERVER}')
177
+ KAFKA_PRODUCER = os.getenv('KAFKA_PRODUCER',{"bootstrap.servers": f"{KAFKA_SERVER}"}) # , "client.id": "aaa.b.c.d"}'
178
+ KAFKA_CONSUMER_GROUP = os.getenv('KAFKA_CONSUMER_GROUP','als-default-group1')
179
+ KAFKA_CONSUMER = os.getenv('KAFKA_CONSUMER', {"bootstrap.servers": f"{KAFKA_SERVER}", "group.id": f"{KAFKA_CONSUMER_GROUP}", "enable.auto.commit": "false", "auto.offset.reset": "earliest"})
180
+ else:
181
+ app_logger.info(f'config.py - KAFKA_SERVER: {KAFKA_SERVER} - not set, no kafka producer/consumer')
182
+ print(f'config.py - KAFKA_PRODUCER: {KAFKA_PRODUCER}')
183
+ print(f'config.py - KAFKA_CONSUMER: {KAFKA_CONSUMER}')
184
+ print(f'config.py - KAFKA_CONSUMER_GROUP: {KAFKA_CONSUMER_GROUP}')
185
+ print(f'config.py - KAFKA_SERVER: {KAFKA_SERVER}')
172
186
  # N8N Webhook Args (for testing)
173
187
  # 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
174
188
  wh_scheme = "http"
@@ -245,6 +259,7 @@ class Args():
245
259
  self.http_scheme = Config.CREATED_HTTP_SCHEME
246
260
  self.kafka_producer = Config.KAFKA_PRODUCER
247
261
  self.kafka_consumer = Config.KAFKA_CONSUMER
262
+ self.kafka_consumer_group = Config.KAFKA_CONSUMER_GROUP
248
263
  self.n8n_producer = Config.N8N_PRODUCER
249
264
  self.keycloak_base = Config.KEYCLOAK_BASE
250
265
  self.keycloak_realm = Config.KEYCLOAK_REALM
@@ -463,14 +478,11 @@ class Args():
463
478
  @property
464
479
  def kafka_producer(self) -> dict:
465
480
  """ kafka connect string """
466
- if "KAFKA_PRODUCER" in self.flask_app.config:
467
- if self.flask_app.config["KAFKA_PRODUCER"] is not None:
468
- value = self.flask_app.config["KAFKA_PRODUCER"]
469
- if isinstance(value, dict):
470
- pass # eg, from VSCode Run Config: "APILOGICPROJECT_KAFKA_PRODUCER": "{\"bootstrap.servers\": \"localhost:9092\"}",
471
- else:
472
- value = json.loads(self.flask_app.config["KAFKA_PRODUCER"])
473
- return value
481
+ if "KAFKA_PRODUCER" in self.flask_app.config and self.flask_app.config["KAFKA_PRODUCER"] is not None:
482
+ value = self.flask_app.config["KAFKA_PRODUCER"]
483
+ if not isinstance(value, dict):
484
+ value = json.loads(self.flask_app.config["KAFKA_PRODUCER"])
485
+ return value
474
486
  return None
475
487
 
476
488
  @kafka_producer.setter
@@ -480,23 +492,30 @@ class Args():
480
492
  @property
481
493
  def kafka_consumer(self) -> dict:
482
494
  """ kafka enable consumer """
483
- if "KAFKA_CONSUMER" in self.flask_app.config:
484
- if self.flask_app.config["KAFKA_CONSUMER"] is not None:
485
- return json.loads(self.flask_app.config["KAFKA_CONSUMER"])
495
+ if "KAFKA_CONSUMER" in self.flask_app.config and self.flask_app.config["KAFKA_CONSUMER"] is not None:
496
+ value = self.flask_app.config["KAFKA_CONSUMER"]
497
+ if not isinstance(value, dict):
498
+ value = json.loads(self.flask_app.config["KAFKA_CONSUMER"])
499
+ return value
486
500
  return None
487
501
 
488
502
  @kafka_consumer.setter
489
503
  def kafka_consumer(self, a: str):
490
504
  self.flask_app.config["KAFKA_CONSUMER"] = a
491
505
 
492
- def __str__(self) -> str:
493
- rtn = f'.. flask_host: {self.flask_host}, port: {self.port}, \n'\
494
- f'.. swagger_host: {self.swagger_host}, swagger_port: {self.swagger_port}, \n'\
495
- f'.. client_uri: {self.client_uri}, \n'\
496
- f'.. http_scheme: {self.http_scheme}, api_prefix: {self.api_prefix}, \n'\
497
- f'.. | verbose: {self.verbose}, create_and_run: {self.create_and_run}'
498
- return rtn
499
-
506
+
507
+ @property
508
+ def kafka_consumer_group(self) -> dict:
509
+ """ kafka enable consumer group """
510
+ if "KAFKA_CONSUMER_GROUP" in self.flask_app.config:
511
+ if self.flask_app.config["KAFKA_CONSUMER_GROUP"] is not None:
512
+ return self.flask_app.config["KAFKA_CONSUMER_GROUP"]
513
+ return None
514
+
515
+ @kafka_consumer_group.setter
516
+ def kafka_consumer_group(self, a: str):
517
+ self.flask_app.config["KAFKA_CONSUMER_GROUP"] = a
518
+
500
519
  @property
501
520
  def n8n_producer(self) -> dict:
502
521
  """ n8n connect string """
@@ -515,6 +534,15 @@ class Args():
515
534
  self.flask_app.config["N8N_PRODUCER"] = a
516
535
 
517
536
 
537
+ def __str__(self) -> str:
538
+ rtn = f'.. flask_host: {self.flask_host}, port: {self.port}, \n'\
539
+ f'.. swagger_host: {self.swagger_host}, swagger_port: {self.swagger_port}, \n'\
540
+ f'.. client_uri: {self.client_uri}, \n'\
541
+ f'.. http_scheme: {self.http_scheme}, api_prefix: {self.api_prefix}, \n'\
542
+ f'.. | verbose: {self.verbose}, create_and_run: {self.create_and_run}'
543
+ return rtn
544
+
545
+
518
546
  def get_cli_args(self, args: 'Args', dunder_name: str):
519
547
  """
520
548
  returns tuple of start args:
@@ -0,0 +1,12 @@
1
+ ### Add Natural Language Logic to Your Project
2
+
3
+ You can add Natural Language logic files to this directory, e.g.:
4
+
5
+ * `.sales_by_category.prompt`: Graph Sales by Category
6
+
7
+ Then, use GenAI to create executable logic in your `api/api_discovery` directory, e.g.,
8
+
9
+ ```bash
10
+ als genai-graphics
11
+ ```
12
+ 1. Consider renaming your graphics files afterward (`valid_names.z-prompt`)so they are skipped on future runs
@@ -78,7 +78,7 @@ class Authentication_Provider(Abstract_Authentication_Provider):
78
78
  from flask import jsonify, request
79
79
  from config.config import Args # circular import error if at top
80
80
 
81
- jwks_uri = Args.instance.keycloak_base + '/protocol/openid-connect/certs'
81
+ jwks_uri = Args.instance.keycloak_base_url + '/protocol/openid-connect/certs'
82
82
  for i in range(100):
83
83
  # we retry a couple of times in case there are connection problems
84
84
  try:
@@ -34,6 +34,8 @@ class Roles():
34
34
  admin = "CS_ADMIN"
35
35
  public="public" # p1/p (no roles, but gets public)
36
36
  sa="sa"
37
+ default_roles_kcals = "default-roles-kcals"
38
+ uma_authorization = "uma_authorization"
37
39
 
38
40
  DefaultRolePermission(to_role=Roles.sa, can_read=True, can_update=True, can_insert=True, can_delete=True)
39
41
  DefaultRolePermission(to_role=Roles.tenant, can_read=True, can_delete=True)
@@ -43,3 +45,5 @@ DefaultRolePermission(to_role=Roles.teller, can_read=True, can_insert=True,can_u
43
45
  DefaultRolePermission(to_role=Roles.customer, can_read=True, can_insert=True,can_update=True, can_delete=False)
44
46
  DefaultRolePermission(to_role=Roles.read_only, can_read=True, can_insert=False,can_update=False, can_delete=False)
45
47
  DefaultRolePermission(to_role=Roles.public, can_read=True, can_insert=False,can_update=False, can_delete=False)
48
+ DefaultRolePermission(to_role=Roles.default_roles_kcals, can_read=True, can_insert=True,can_update=True, can_delete=False)
49
+ DefaultRolePermission(to_role=Roles.uma_authorization, can_read=True, can_insert=True,can_update=True, can_delete=False)
@@ -140,7 +140,7 @@ def admin_events(flask_app: Flask, args: Args, validation_error: ValidationError
140
140
  if "keycloak" in provider_name:
141
141
  s = (f'\n'
142
142
  f' keycloak:\n'
143
- f' url: {args.keycloak_base_url}\n'
143
+ f' url: {args.keycloak_base}\n'
144
144
  f' realm: {args.keycloak_realm}\n'
145
145
  f' clientId: {args.keycloak_client_id}\n'
146
146
  )
@@ -148,6 +148,8 @@ def admin_events(flask_app: Flask, args: Args, validation_error: ValidationError
148
148
  elif "sql" in provider_name:
149
149
  sql_auth_config = f'\n endpoint: {args.http_scheme}://{args.swagger_host}:{args.swagger_port}/{args.api_prefix[1:]}/auth/login\n'
150
150
  content = content.replace("'{system-default}'", sql_auth_config)
151
+ elif getattr(Config.SECURITY_PROVIDER, 'auth_config', None):
152
+ content = content.replace("'{system-default}'", Config.SECURITY_PROVIDER.auth_config)
151
153
  else:
152
154
  sys.exit(f"ERROR[admin_loader]: unknown security type: {Config.SECURITY_PROVIDER}")
153
155
 
@@ -98,7 +98,7 @@ Then, try your own databases [(db-url examples here)](https://apilogicserver.git
98
98
 
99
99
  <br>You can do this with or without signup:
100
100
 
101
- 1. If you have signed up, this will create and open a project called `genai_demo` from `genai_demo.prompt` (available in left Explorer pane):
101
+ 1. If you have signed up (see *To obtain a ChatGPT API Key*, below), this will create and open a project called `genai_demo` from `genai_demo.prompt` (available in left Explorer pane):
102
102
 
103
103
  ```bash
104
104
  als genai --using=system/genai/examples/genai_demo/genai_demo.prompt --project-name=genai_demo
@@ -122,6 +122,20 @@ Verify it's operating properly:
122
122
 
123
123
  </br>
124
124
 
125
+
126
+ <details markdown>
127
+
128
+ <summary> To obtain a ChatGPT API Key</summary>
129
+
130
+ <br>GenAI-Logic uses OpenAI, which requires an Open API Key:
131
+
132
+ 1. Obtain one from [here](https://platform.openai.com/account/api-keys) or [here](https://platform.openai.com/api-keys)
133
+ 2. Authorize payments [here](https://platform.openai.com/settings/organization/billing/overview)
134
+
135
+ </details>
136
+
137
+ </br>
138
+
125
139
  <details markdown>
126
140
 
127
141
  <summary> What Just Happened? &nbsp;&nbsp;&nbsp;Next Steps...</summary>
@@ -544,6 +558,21 @@ ApiLogicServer create --project-name=samples/nw_sample_nocust --db-url=nw
544
558
 
545
559
  &nbsp;
546
560
 
561
+ ## Explore WebGenAI
562
+
563
+ In addition to the CLI examples above, you can use [WebGenAI](https://apilogicserver.github.io/Docs/WebGenAI/) - a web interface for creating projects from prompts. You can install WebGenAI on a server, so that created projects are easy to review with colleagues.
564
+
565
+ To try WebGenAI:
566
+
567
+ ```bash
568
+ cd webgenai
569
+ docker compose up
570
+ ```
571
+
572
+ You will be directed to the registration process. You will also require a ChatGPT API Key as described above.
573
+
574
+ &nbsp;
575
+
547
576
  &nbsp;
548
577
 
549
578
  ## Appendix: Quick Basic Demo
@@ -14,6 +14,8 @@ Use case: Check Credit
14
14
  Use case: App Integration
15
15
  1. Send the Order to Kafka topic 'order_shipping' if the date_shipped is not None.
16
16
 
17
+ Graph sales by month.
18
+
17
19
  Ensure each customer and product has a unique name.
18
20
 
19
21
  Ensure each Item quantity is not null.