zrb 1.0.0a2__py3-none-any.whl → 1.0.0a4__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.
- zrb/__init__.py +49 -40
- zrb/__main__.py +5 -3
- zrb/attr/type.py +2 -1
- zrb/builtin/__init__.py +42 -2
- zrb/builtin/base64.py +34 -0
- zrb/builtin/git.py +156 -0
- zrb/builtin/git_subtree.py +88 -0
- zrb/builtin/group.py +34 -0
- zrb/builtin/llm/llm_chat.py +47 -0
- zrb/builtin/llm/tool/cli.py +9 -0
- zrb/builtin/llm/tool/rag.py +189 -0
- zrb/builtin/llm/tool/web.py +74 -0
- zrb/builtin/md5.py +36 -0
- zrb/builtin/project/add/fastapp.py +72 -0
- zrb/builtin/project/add/fastapp_template/.gitignore +4 -0
- zrb/builtin/project/add/fastapp_template/README.md +7 -0
- zrb/builtin/project/add/fastapp_template/_zrb/config.py +17 -0
- zrb/builtin/project/add/fastapp_template/_zrb/group.py +16 -0
- zrb/builtin/project/add/fastapp_template/_zrb/helper.py +97 -0
- zrb/builtin/project/add/fastapp_template/_zrb/main.py +132 -0
- zrb/builtin/project/add/fastapp_template/_zrb/venv_task.py +22 -0
- zrb/builtin/project/add/fastapp_template/common/app.py +18 -0
- zrb/builtin/project/add/fastapp_template/common/db_engine.py +5 -0
- zrb/builtin/project/add/fastapp_template/common/db_repository.py +134 -0
- zrb/builtin/project/add/fastapp_template/common/error.py +8 -0
- zrb/builtin/project/add/fastapp_template/common/schema.py +5 -0
- zrb/builtin/project/add/fastapp_template/common/usecase.py +232 -0
- zrb/builtin/project/add/fastapp_template/config.py +29 -0
- zrb/builtin/project/add/fastapp_template/main.py +7 -0
- zrb/builtin/project/add/fastapp_template/migrate.py +3 -0
- zrb/builtin/project/add/fastapp_template/module/__init__.py +0 -0
- zrb/builtin/project/add/fastapp_template/module/auth/alembic.ini +117 -0
- zrb/builtin/project/add/fastapp_template/module/auth/client/api_client.py +7 -0
- zrb/builtin/project/add/fastapp_template/module/auth/client/base_client.py +27 -0
- zrb/builtin/project/add/fastapp_template/module/auth/client/direct_client.py +6 -0
- zrb/builtin/project/add/fastapp_template/module/auth/client/factory.py +9 -0
- zrb/builtin/project/add/fastapp_template/module/auth/migration/README +1 -0
- zrb/builtin/project/add/fastapp_template/module/auth/migration/env.py +108 -0
- zrb/builtin/project/add/fastapp_template/module/auth/migration/script.py.mako +26 -0
- zrb/builtin/project/add/fastapp_template/module/auth/migration/versions/3093c7336477_add_user_table.py +37 -0
- zrb/builtin/project/add/fastapp_template/module/auth/migration_metadata.py +6 -0
- zrb/builtin/project/add/fastapp_template/module/auth/route.py +22 -0
- zrb/builtin/project/add/fastapp_template/module/auth/service/__init__.py +0 -0
- zrb/builtin/project/add/fastapp_template/module/auth/service/user/__init__.py +0 -0
- zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/__init__.py +0 -0
- zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/db_repository.py +39 -0
- zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/factory.py +13 -0
- zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/repository.py +34 -0
- zrb/builtin/project/add/fastapp_template/module/auth/service/user/usecase.py +45 -0
- zrb/builtin/project/add/fastapp_template/module/gateway/alembic.ini +117 -0
- zrb/builtin/project/add/fastapp_template/module/gateway/migration/README +1 -0
- zrb/builtin/project/add/fastapp_template/module/gateway/migration/env.py +108 -0
- zrb/builtin/project/add/fastapp_template/module/gateway/migration/script.py.mako +26 -0
- zrb/builtin/project/add/fastapp_template/module/gateway/migration/versions/.gitkeep +0 -0
- zrb/builtin/project/add/fastapp_template/module/gateway/migration_metadata.py +3 -0
- zrb/builtin/project/add/fastapp_template/module/gateway/route.py +27 -0
- zrb/builtin/project/add/fastapp_template/requirements.txt +6 -0
- zrb/builtin/project/add/fastapp_template/schema/__init__.py +0 -0
- zrb/builtin/project/add/fastapp_template/schema/role.py +31 -0
- zrb/builtin/project/add/fastapp_template/schema/user.py +31 -0
- zrb/builtin/project/add/fastapp_template/template.env +2 -0
- zrb/builtin/project/create/__init__.py +0 -0
- zrb/builtin/project/create/create.py +41 -0
- zrb/builtin/project/create/project-template/README.md +3 -0
- zrb/builtin/project/create/project-template/zrb_init.py +7 -0
- zrb/builtin/python.py +11 -0
- zrb/builtin/shell/__init__.py +0 -5
- zrb/builtin/shell/autocomplete/__init__.py +0 -9
- zrb/builtin/shell/autocomplete/bash.py +5 -6
- zrb/builtin/shell/autocomplete/subcmd.py +7 -8
- zrb/builtin/shell/autocomplete/zsh.py +5 -6
- zrb/builtin/todo.py +219 -0
- zrb/callback/any_callback.py +1 -1
- zrb/callback/callback.py +5 -5
- zrb/cmd/cmd_val.py +2 -2
- zrb/config.py +16 -3
- zrb/content_transformer/any_content_transformer.py +1 -1
- zrb/content_transformer/content_transformer.py +2 -2
- zrb/context/any_context.py +1 -1
- zrb/context/any_shared_context.py +3 -3
- zrb/context/context.py +10 -8
- zrb/context/shared_context.py +9 -8
- zrb/env/__init__.py +0 -3
- zrb/env/any_env.py +1 -1
- zrb/env/env.py +3 -4
- zrb/env/env_file.py +4 -4
- zrb/env/env_map.py +2 -2
- zrb/group/__init__.py +0 -3
- zrb/group/any_group.py +3 -3
- zrb/group/group.py +7 -6
- zrb/input/any_input.py +1 -1
- zrb/input/base_input.py +4 -4
- zrb/input/bool_input.py +5 -5
- zrb/input/float_input.py +3 -3
- zrb/input/int_input.py +3 -3
- zrb/input/option_input.py +51 -0
- zrb/input/password_input.py +2 -2
- zrb/input/str_input.py +1 -1
- zrb/input/text_input.py +12 -10
- zrb/runner/cli.py +80 -45
- zrb/runner/web_app.py +150 -0
- zrb/runner/web_controller/__init__.py +0 -0
- zrb/runner/web_controller/group_info_ui/__init__.py +0 -0
- zrb/runner/{web_app → web_controller}/group_info_ui/controller.py +7 -8
- zrb/runner/{web_app → web_controller}/group_info_ui/view.html +2 -2
- zrb/runner/web_controller/home_page/__init__.py +0 -0
- zrb/runner/{web_app → web_controller}/home_page/controller.py +7 -6
- zrb/runner/{web_app → web_controller}/home_page/view.html +2 -2
- zrb/runner/web_controller/task_ui/__init__.py +0 -0
- zrb/runner/{web_app → web_controller}/task_ui/controller.py +8 -12
- zrb/runner/{web_app → web_controller}/task_ui/view.html +2 -2
- zrb/runner/web_util.py +5 -35
- zrb/session/any_session.py +13 -7
- zrb/session/session.py +78 -40
- zrb/session_state_log/session_state_log.py +7 -5
- zrb/session_state_logger/any_session_state_logger.py +1 -1
- zrb/session_state_logger/default_session_state_logger.py +2 -2
- zrb/session_state_logger/file_session_state_logger.py +19 -27
- zrb/task/any_task.py +4 -4
- zrb/task/base_task.py +33 -23
- zrb/task/base_trigger.py +11 -12
- zrb/task/cmd_task.py +72 -65
- zrb/task/http_check.py +13 -13
- zrb/task/llm_task.py +215 -0
- zrb/task/make_task.py +9 -9
- zrb/task/rsync_task.py +25 -25
- zrb/task/scaffolder.py +18 -15
- zrb/task/scheduler.py +6 -7
- zrb/task/task.py +1 -1
- zrb/task/tcp_check.py +11 -13
- zrb/util/attr.py +19 -3
- zrb/util/cli/style.py +71 -2
- zrb/util/cli/subcommand.py +2 -2
- zrb/util/codemod/__init__.py +0 -0
- zrb/util/codemod/add_code_to_class.py +35 -0
- zrb/util/codemod/add_code_to_function.py +36 -0
- zrb/util/codemod/add_code_to_method.py +55 -0
- zrb/util/codemod/add_key_to_dict.py +51 -0
- zrb/util/codemod/add_param_to_function_call.py +39 -0
- zrb/util/codemod/add_property_to_class.py +55 -0
- zrb/util/git.py +156 -0
- zrb/util/git_subtree.py +94 -0
- zrb/util/group.py +2 -2
- zrb/util/llm/tool.py +63 -0
- zrb/util/string/conversion.py +7 -0
- zrb/util/todo.py +259 -0
- {zrb-1.0.0a2.dist-info → zrb-1.0.0a4.dist-info}/METADATA +13 -5
- zrb-1.0.0a4.dist-info/RECORD +197 -0
- zrb/builtin/shell/_group.py +0 -9
- zrb/builtin/shell/autocomplete/_group.py +0 -6
- zrb/runner/web_app/any_request_handler.py +0 -24
- zrb/runner/web_server.py +0 -224
- zrb-1.0.0a2.dist-info/RECORD +0 -120
- /zrb/{runner/web_app → builtin/project}/__init__.py +0 -0
- /zrb/{runner/web_app/group_info_ui → builtin/project/add}/__init__.py +0 -0
- /zrb/{runner/web_app/home_page → builtin/project/add/fastapp_template}/__init__.py +0 -0
- /zrb/{runner/web_app/task_ui → builtin/project/add/fastapp_template/common}/__init__.py +0 -0
- /zrb/runner/{web_app → web_controller}/group_info_ui/partial/group_info.html +0 -0
- /zrb/runner/{web_app → web_controller}/group_info_ui/partial/group_li.html +0 -0
- /zrb/runner/{web_app → web_controller}/group_info_ui/partial/task_info.html +0 -0
- /zrb/runner/{web_app → web_controller}/group_info_ui/partial/task_li.html +0 -0
- /zrb/runner/{web_app → web_controller}/home_page/partial/group_info.html +0 -0
- /zrb/runner/{web_app → web_controller}/home_page/partial/group_li.html +0 -0
- /zrb/runner/{web_app → web_controller}/home_page/partial/task_info.html +0 -0
- /zrb/runner/{web_app → web_controller}/home_page/partial/task_li.html +0 -0
- /zrb/runner/{web_app → web_controller}/static/favicon-32x32.png +0 -0
- /zrb/runner/{web_app → web_controller}/static/pico.min.css +0 -0
- /zrb/runner/{web_app → web_controller}/task_ui/partial/common-util.js +0 -0
- /zrb/runner/{web_app → web_controller}/task_ui/partial/input.html +0 -0
- /zrb/runner/{web_app → web_controller}/task_ui/partial/main.js +0 -0
- /zrb/runner/{web_app → web_controller}/task_ui/partial/show-existing-session.js +0 -0
- /zrb/runner/{web_app → web_controller}/task_ui/partial/visualize-history.js +0 -0
- {zrb-1.0.0a2.dist-info → zrb-1.0.0a4.dist-info}/WHEEL +0 -0
- {zrb-1.0.0a2.dist-info → zrb-1.0.0a4.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,189 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
import sys
|
4
|
+
from collections.abc import Callable, Iterable
|
5
|
+
|
6
|
+
import litellm
|
7
|
+
|
8
|
+
from zrb.config import (
|
9
|
+
RAG_CHUNK_SIZE,
|
10
|
+
RAG_EMBEDDING_MODEL,
|
11
|
+
RAG_MAX_RESULT_COUNT,
|
12
|
+
RAG_OVERLAP,
|
13
|
+
)
|
14
|
+
from zrb.util.cli.style import stylize_error, stylize_faint
|
15
|
+
from zrb.util.run import run_async
|
16
|
+
|
17
|
+
Document = str | Callable[[], str]
|
18
|
+
Documents = Callable[[], Iterable[Document]] | Iterable[Document]
|
19
|
+
|
20
|
+
|
21
|
+
def create_rag_from_directory(
|
22
|
+
tool_name: str,
|
23
|
+
tool_description: str,
|
24
|
+
document_dir_path: str = "./documents",
|
25
|
+
model: str = RAG_EMBEDDING_MODEL,
|
26
|
+
vector_db_path: str = "./chroma",
|
27
|
+
vector_db_collection: str = "documents",
|
28
|
+
chunk_size: int = RAG_CHUNK_SIZE,
|
29
|
+
overlap: int = RAG_OVERLAP,
|
30
|
+
max_result_count: int = RAG_MAX_RESULT_COUNT,
|
31
|
+
):
|
32
|
+
return create_rag(
|
33
|
+
tool_name=tool_name,
|
34
|
+
tool_description=tool_description,
|
35
|
+
documents=get_rag_documents(os.path.expanduser(document_dir_path)),
|
36
|
+
model=model,
|
37
|
+
vector_db_path=vector_db_path,
|
38
|
+
vector_db_collection=vector_db_collection,
|
39
|
+
reset_db=get_rag_reset_db(
|
40
|
+
document_dir_path=os.path.expanduser(document_dir_path),
|
41
|
+
vector_db_path=os.path.expanduser(vector_db_path),
|
42
|
+
),
|
43
|
+
chunk_size=chunk_size,
|
44
|
+
overlap=overlap,
|
45
|
+
max_result_count=max_result_count,
|
46
|
+
)
|
47
|
+
|
48
|
+
|
49
|
+
def create_rag(
|
50
|
+
tool_name: str,
|
51
|
+
tool_description: str,
|
52
|
+
documents: Documents = [],
|
53
|
+
model: str = RAG_EMBEDDING_MODEL,
|
54
|
+
vector_db_path: str = "./chroma",
|
55
|
+
vector_db_collection: str = "documents",
|
56
|
+
reset_db: Callable[[], bool] | bool = False,
|
57
|
+
chunk_size: int = RAG_CHUNK_SIZE,
|
58
|
+
overlap: int = RAG_OVERLAP,
|
59
|
+
max_result_count: int = RAG_MAX_RESULT_COUNT,
|
60
|
+
) -> Callable[[str], str]:
|
61
|
+
async def retrieve(query: str) -> str:
|
62
|
+
import chromadb
|
63
|
+
from chromadb.config import Settings
|
64
|
+
|
65
|
+
is_db_exist = os.path.isdir(vector_db_path)
|
66
|
+
client = chromadb.PersistentClient(
|
67
|
+
path=vector_db_path, settings=Settings(allow_reset=True)
|
68
|
+
)
|
69
|
+
should_reset_db = (
|
70
|
+
await run_async(reset_db()) if callable(reset_db) else reset_db
|
71
|
+
)
|
72
|
+
if (not is_db_exist) or should_reset_db:
|
73
|
+
client.reset()
|
74
|
+
collection = client.get_or_create_collection(vector_db_collection)
|
75
|
+
chunk_index = 0
|
76
|
+
print(stylize_faint("Scanning documents"), file=sys.stderr)
|
77
|
+
docs = await run_async(documents()) if callable(documents) else documents
|
78
|
+
for document in docs:
|
79
|
+
if callable(document):
|
80
|
+
try:
|
81
|
+
document = await run_async(document())
|
82
|
+
except Exception as error:
|
83
|
+
print(stylize_error(f"Error: {error}"), file=sys.stderr)
|
84
|
+
continue
|
85
|
+
for i in range(0, len(document), chunk_size - overlap):
|
86
|
+
chunk = document[i : i + chunk_size]
|
87
|
+
if len(chunk) > 0:
|
88
|
+
print(
|
89
|
+
stylize_faint(f"Vectorize chunk {chunk_index}"),
|
90
|
+
file=sys.stderr,
|
91
|
+
)
|
92
|
+
response = await litellm.aembedding(model=model, input=[chunk])
|
93
|
+
vector = response["data"][0]["embedding"]
|
94
|
+
print(
|
95
|
+
stylize_faint(f"Adding chunk {chunk_index} to db"),
|
96
|
+
file=sys.stderr,
|
97
|
+
)
|
98
|
+
collection.upsert(
|
99
|
+
ids=[f"id{chunk_index}"],
|
100
|
+
embeddings=[vector],
|
101
|
+
documents=[chunk],
|
102
|
+
)
|
103
|
+
chunk_index += 1
|
104
|
+
collection = client.get_or_create_collection(vector_db_collection)
|
105
|
+
# Generate embedding for the query
|
106
|
+
print(stylize_faint("Vectorize query"), file=sys.stderr)
|
107
|
+
query_response = await litellm.aembedding(model=model, input=[query])
|
108
|
+
print(stylize_faint("Search documents"), file=sys.stderr)
|
109
|
+
# Search for the top_k most similar documents
|
110
|
+
results = collection.query(
|
111
|
+
query_embeddings=query_response["data"][0]["embedding"],
|
112
|
+
n_results=max_result_count,
|
113
|
+
)
|
114
|
+
return json.dumps(results)
|
115
|
+
|
116
|
+
retrieve.__name__ = tool_name
|
117
|
+
retrieve.__doc__ = tool_description
|
118
|
+
return retrieve
|
119
|
+
|
120
|
+
|
121
|
+
def get_rag_documents(document_dir_path: str) -> Callable[[], list[Callable[[], str]]]:
|
122
|
+
def get_documents() -> list[Callable[[], str]]:
|
123
|
+
# Walk through the directory
|
124
|
+
readers = []
|
125
|
+
for root, _, files in os.walk(document_dir_path):
|
126
|
+
for file in files:
|
127
|
+
file_path = os.path.join(root, file)
|
128
|
+
if file_path.lower().endswith(".pdf"):
|
129
|
+
readers.append(_get_pdf_reader(file_path))
|
130
|
+
continue
|
131
|
+
readers.append(_get_text_reader(file_path))
|
132
|
+
return readers
|
133
|
+
|
134
|
+
return get_documents
|
135
|
+
|
136
|
+
|
137
|
+
def _get_text_reader(file_path: str):
|
138
|
+
def read():
|
139
|
+
print(stylize_faint(f"Start reading {file_path}"), file=sys.stderr)
|
140
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
141
|
+
content = f.read()
|
142
|
+
print(stylize_faint(f"Complete reading {file_path}"), file=sys.stderr)
|
143
|
+
return content
|
144
|
+
|
145
|
+
return read
|
146
|
+
|
147
|
+
|
148
|
+
def _get_pdf_reader(file_path):
|
149
|
+
def read():
|
150
|
+
import pdfplumber
|
151
|
+
|
152
|
+
print(stylize_faint(f"Start reading {file_path}"), file=sys.stderr)
|
153
|
+
contents = []
|
154
|
+
with pdfplumber.open(file_path) as pdf:
|
155
|
+
for page in pdf.pages:
|
156
|
+
contents.append(page.extract_text())
|
157
|
+
print(stylize_faint(f"Complete reading {file_path}"), file=sys.stderr)
|
158
|
+
return "\n".join(contents)
|
159
|
+
|
160
|
+
return read
|
161
|
+
|
162
|
+
|
163
|
+
def get_rag_reset_db(
|
164
|
+
document_dir_path: str, vector_db_path: str = "./chroma"
|
165
|
+
) -> Callable[[], bool]:
|
166
|
+
def should_reset_db() -> bool:
|
167
|
+
document_exist = os.path.isdir(document_dir_path)
|
168
|
+
if not document_exist:
|
169
|
+
raise ValueError(f"Document directory not exists: {document_dir_path}")
|
170
|
+
vector_db_exist = os.path.isdir(vector_db_path)
|
171
|
+
if not vector_db_exist:
|
172
|
+
return True
|
173
|
+
document_mtime = _get_most_recent_mtime(document_dir_path)
|
174
|
+
vector_db_mtime = _get_most_recent_mtime(vector_db_path)
|
175
|
+
return document_mtime > vector_db_mtime
|
176
|
+
|
177
|
+
return should_reset_db
|
178
|
+
|
179
|
+
|
180
|
+
def _get_most_recent_mtime(directory):
|
181
|
+
most_recent_mtime = 0
|
182
|
+
for root, dirs, files in os.walk(directory):
|
183
|
+
# Check mtime for directories
|
184
|
+
for name in dirs + files:
|
185
|
+
file_path = os.path.join(root, name)
|
186
|
+
mtime = os.path.getmtime(file_path)
|
187
|
+
if mtime > most_recent_mtime:
|
188
|
+
most_recent_mtime = mtime
|
189
|
+
return most_recent_mtime
|
@@ -0,0 +1,74 @@
|
|
1
|
+
import json
|
2
|
+
from typing import Annotated
|
3
|
+
|
4
|
+
|
5
|
+
def open_web_page(url: str) -> str:
|
6
|
+
"""Get content from a web page."""
|
7
|
+
import requests
|
8
|
+
|
9
|
+
response = requests.get(
|
10
|
+
url,
|
11
|
+
headers={
|
12
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" # noqa
|
13
|
+
},
|
14
|
+
)
|
15
|
+
if response.status_code != 200:
|
16
|
+
raise Exception(
|
17
|
+
f"Error: Unable to retrieve search results (status code: {response.status_code})" # noqa
|
18
|
+
)
|
19
|
+
return json.dumps(parse_html_text(response.text))
|
20
|
+
|
21
|
+
|
22
|
+
def query_internet(
|
23
|
+
query: Annotated[str, "Search query"],
|
24
|
+
num_results: Annotated[int, "Search result count, by default 10"] = 10,
|
25
|
+
) -> str:
|
26
|
+
"""Search factual information from the internet by using Google."""
|
27
|
+
import requests
|
28
|
+
|
29
|
+
response = requests.get(
|
30
|
+
"https://google.com/search",
|
31
|
+
headers={
|
32
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" # noqa
|
33
|
+
},
|
34
|
+
params={
|
35
|
+
"q": query,
|
36
|
+
"num": num_results,
|
37
|
+
"hl": "en",
|
38
|
+
"safe": "off",
|
39
|
+
},
|
40
|
+
)
|
41
|
+
if response.status_code != 200:
|
42
|
+
raise Exception(
|
43
|
+
f"Error: Unable to retrieve search results (status code: {response.status_code})" # noqa
|
44
|
+
)
|
45
|
+
return json.dumps(parse_html_text(response.text))
|
46
|
+
|
47
|
+
|
48
|
+
def parse_html_text(html_text: str) -> dict[str, str]:
|
49
|
+
from bs4 import BeautifulSoup
|
50
|
+
|
51
|
+
ignored_tags = [
|
52
|
+
"script",
|
53
|
+
"link",
|
54
|
+
"meta",
|
55
|
+
"style",
|
56
|
+
"code",
|
57
|
+
"footer",
|
58
|
+
"nav",
|
59
|
+
"header",
|
60
|
+
"aside",
|
61
|
+
]
|
62
|
+
soup = BeautifulSoup(html_text, "html.parser")
|
63
|
+
links = []
|
64
|
+
for anchor in soup.find_all("a"):
|
65
|
+
if not anchor or "href" not in anchor.attrs:
|
66
|
+
continue
|
67
|
+
link: str = anchor["href"]
|
68
|
+
if link.startswith("#") or link.startswith("/"):
|
69
|
+
continue
|
70
|
+
links.append(link)
|
71
|
+
for tag in soup(ignored_tags):
|
72
|
+
tag.decompose()
|
73
|
+
html_text = soup.get_text(separator=" ", strip=True)
|
74
|
+
return {"content": html_text, "links_on_page": links}
|
zrb/builtin/md5.py
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
from zrb.builtin.group import md5_group
|
2
|
+
from zrb.context.any_context import AnyContext
|
3
|
+
from zrb.input.str_input import StrInput
|
4
|
+
from zrb.task.make_task import make_task
|
5
|
+
|
6
|
+
|
7
|
+
@make_task(
|
8
|
+
name="hash-md5",
|
9
|
+
input=StrInput(name="text", description="Text", prompt="Text to encode"),
|
10
|
+
description="🧩 Hash text to md5",
|
11
|
+
group=md5_group,
|
12
|
+
alias="hash",
|
13
|
+
)
|
14
|
+
def hash_md5(ctx: AnyContext) -> str:
|
15
|
+
import hashlib
|
16
|
+
|
17
|
+
result = hashlib.md5(ctx.input.text.encode()).hexdigest()
|
18
|
+
ctx.print(result)
|
19
|
+
return result
|
20
|
+
|
21
|
+
|
22
|
+
@make_task(
|
23
|
+
name="sum-md5",
|
24
|
+
input=StrInput(name="file", description="File name", prompt="File name"),
|
25
|
+
description="➕ Get md5 checksum of a file",
|
26
|
+
group=md5_group,
|
27
|
+
alias="sum",
|
28
|
+
)
|
29
|
+
def sum_md5(ctx: AnyContext) -> str:
|
30
|
+
import hashlib
|
31
|
+
|
32
|
+
with open(ctx.input.file, mode="rb") as file:
|
33
|
+
content = file.read()
|
34
|
+
result = hashlib.md5(content).hexdigest()
|
35
|
+
ctx.print(result)
|
36
|
+
return result
|
@@ -0,0 +1,72 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
from zrb.builtin.group import add_to_project_group
|
4
|
+
from zrb.context.any_context import AnyContext
|
5
|
+
from zrb.input.str_input import StrInput
|
6
|
+
from zrb.task.make_task import make_task
|
7
|
+
from zrb.task.scaffolder import Scaffolder
|
8
|
+
from zrb.task.task import Task
|
9
|
+
from zrb.util.string.conversion import double_quote
|
10
|
+
from zrb.util.string.name import get_random_name
|
11
|
+
|
12
|
+
_DIR = os.path.dirname(__file__)
|
13
|
+
|
14
|
+
|
15
|
+
scaffold_fastapp = Scaffolder(
|
16
|
+
name="scaffold-fastapp",
|
17
|
+
input=[
|
18
|
+
StrInput(
|
19
|
+
name="project-dir",
|
20
|
+
description="Project directory",
|
21
|
+
prompt="Project directory",
|
22
|
+
default_str=lambda _: os.getcwd(),
|
23
|
+
),
|
24
|
+
StrInput(
|
25
|
+
name="app-name",
|
26
|
+
description="App name",
|
27
|
+
prompt="App name",
|
28
|
+
default_str=lambda _: get_random_name(separator="_"),
|
29
|
+
),
|
30
|
+
],
|
31
|
+
source_path=os.path.join(_DIR, "fastapp_template"),
|
32
|
+
render_source_path=False,
|
33
|
+
destination_path=lambda ctx: os.path.join(
|
34
|
+
ctx.input["project-dir"], ctx.input["app-name"]
|
35
|
+
),
|
36
|
+
transform_content={
|
37
|
+
"fastapp_template": "{to_snake_case(ctx.input['app-name'])}",
|
38
|
+
"App Name": "{ctx.input['app-name'].title()}",
|
39
|
+
"App name": "{ctx.input['app-name'].capitalize()}",
|
40
|
+
"app-name": "{to_kebab_case(ctx.input['app-name'])}",
|
41
|
+
"app_name": "{to_snake_case(ctx.input['app-name'])}",
|
42
|
+
"APP_NAME": "{to_snake_case(ctx.input['app-name']).upper()}",
|
43
|
+
"secure-password": lambda _: get_random_name(),
|
44
|
+
},
|
45
|
+
retries=0,
|
46
|
+
)
|
47
|
+
|
48
|
+
|
49
|
+
@make_task(
|
50
|
+
name="register-fastapp-automation",
|
51
|
+
)
|
52
|
+
def register_fastapp_automation(ctx: AnyContext):
|
53
|
+
project_dir_path = ctx.input["project-dir"]
|
54
|
+
zrb_init_path = os.path.join(project_dir_path, "zrb_init.py")
|
55
|
+
app_dir_path = ctx.input["app-name"]
|
56
|
+
app_automation_file_part = ", ".join(
|
57
|
+
[double_quote(part) for part in [app_dir_path, "_zrb", "main.py"]]
|
58
|
+
)
|
59
|
+
with open(zrb_init_path, "+a") as f:
|
60
|
+
f.write(f"load_file(os.path.join(_DIR, {app_automation_file_part}))\n")
|
61
|
+
|
62
|
+
|
63
|
+
scaffold_fastapp >> register_fastapp_automation
|
64
|
+
|
65
|
+
add_fastapp_to_project = add_to_project_group.add_task(
|
66
|
+
Task(
|
67
|
+
name="add-fastapp",
|
68
|
+
description="🚀 Add FastApp to project",
|
69
|
+
),
|
70
|
+
alias="fastapp",
|
71
|
+
)
|
72
|
+
add_fastapp_to_project << [register_fastapp_automation]
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import os
|
2
|
+
import platform
|
3
|
+
|
4
|
+
DIR = os.path.dirname(__file__)
|
5
|
+
APP_DIR = os.path.dirname(DIR)
|
6
|
+
APP_MODULE_NAME = os.path.basename(APP_DIR)
|
7
|
+
|
8
|
+
MICROSERVICES_ENV_VARS = {
|
9
|
+
"APP_NAME_MODE": "microservices",
|
10
|
+
"APP_NAME_AUTH_BASE_URL": "http://localhost:3002",
|
11
|
+
}
|
12
|
+
MONOLITH_ENV_VARS = {"APP_NAME_MODE": "monolith"}
|
13
|
+
|
14
|
+
if platform.system() == "Windows":
|
15
|
+
ACTIVATE_VENV_SCRIPT = "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser; . .venv\Scripts\Activate" # noqa
|
16
|
+
else:
|
17
|
+
ACTIVATE_VENV_SCRIPT = "source .venv/bin/activate"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
from zrb import Group
|
2
|
+
from zrb.builtin import project_group
|
3
|
+
|
4
|
+
app_group = project_group.add_group(
|
5
|
+
Group(name="app-name", description="🚀 Managing App Name")
|
6
|
+
)
|
7
|
+
|
8
|
+
app_run_group = app_group.add_group(Group(name="run", description="🟢 Run App Name"))
|
9
|
+
|
10
|
+
app_migrate_group = app_group.add_group(
|
11
|
+
Group(name="migrate", description="📦 Run App Name DB migration")
|
12
|
+
)
|
13
|
+
|
14
|
+
app_create_group = app_group.add_group(
|
15
|
+
Group(name="create", description="✨ Create resources for App Name")
|
16
|
+
)
|
@@ -0,0 +1,97 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
from fastapp_template._zrb.config import (
|
4
|
+
ACTIVATE_VENV_SCRIPT,
|
5
|
+
APP_DIR,
|
6
|
+
MICROSERVICES_ENV_VARS,
|
7
|
+
MONOLITH_ENV_VARS,
|
8
|
+
)
|
9
|
+
|
10
|
+
from zrb import Cmd, CmdTask, EnvFile, EnvMap, StrInput, Task
|
11
|
+
|
12
|
+
|
13
|
+
def create_migration(name: str, module: str) -> Task:
|
14
|
+
return CmdTask(
|
15
|
+
name=f"create-app-name-{name}-migration",
|
16
|
+
description=f"🧩 Create App Name {name.capitalize()} DB migration",
|
17
|
+
input=StrInput(
|
18
|
+
name="message",
|
19
|
+
description="Migration message",
|
20
|
+
prompt="Migration message",
|
21
|
+
allow_empty=False,
|
22
|
+
),
|
23
|
+
env=[
|
24
|
+
EnvFile(path=os.path.join(APP_DIR, "template.env")),
|
25
|
+
EnvMap(
|
26
|
+
vars={
|
27
|
+
"APP_DB_URL": f"sqlite:///{APP_DIR}/.migration.{module}.db",
|
28
|
+
"APP_NAME_MODULES": f"{module}",
|
29
|
+
}
|
30
|
+
),
|
31
|
+
],
|
32
|
+
cwd=APP_DIR,
|
33
|
+
cmd=[
|
34
|
+
ACTIVATE_VENV_SCRIPT,
|
35
|
+
f"cd {os.path.join(APP_DIR, 'module', module)}",
|
36
|
+
"alembic upgrade head",
|
37
|
+
Cmd(
|
38
|
+
"alembic revision --autogenerate -m {double_quote(ctx.input.message)}",
|
39
|
+
auto_render=True,
|
40
|
+
),
|
41
|
+
],
|
42
|
+
render_cmd=False,
|
43
|
+
retries=2,
|
44
|
+
)
|
45
|
+
|
46
|
+
|
47
|
+
def migrate_module(name: str, module: str, as_microservices: bool) -> Task:
|
48
|
+
env_vars = MICROSERVICES_ENV_VARS if as_microservices else MONOLITH_ENV_VARS
|
49
|
+
return CmdTask(
|
50
|
+
name=(
|
51
|
+
f"migrate-app-name-{name}"
|
52
|
+
if as_microservices
|
53
|
+
else f"migrate-{name}-on-monolith"
|
54
|
+
),
|
55
|
+
description=f"🧩 Run App Name {name.capitalize()} DB migration",
|
56
|
+
env=[
|
57
|
+
EnvFile(path=os.path.join(APP_DIR, "template.env")),
|
58
|
+
EnvMap(
|
59
|
+
vars={
|
60
|
+
**env_vars,
|
61
|
+
"APP_NAME_MODULES": f"{module}",
|
62
|
+
}
|
63
|
+
),
|
64
|
+
],
|
65
|
+
cwd=APP_DIR,
|
66
|
+
cmd=[
|
67
|
+
ACTIVATE_VENV_SCRIPT,
|
68
|
+
f"cd {os.path.join(APP_DIR, 'module', module)}",
|
69
|
+
"alembic upgrade head",
|
70
|
+
],
|
71
|
+
render_cmd=False,
|
72
|
+
retries=2,
|
73
|
+
)
|
74
|
+
|
75
|
+
|
76
|
+
def run_microservice(name: str, port: int, module: str) -> Task:
|
77
|
+
return CmdTask(
|
78
|
+
name=f"run-app-name-{name}",
|
79
|
+
description=f"🧩 Run App Name {name.capitalize()}",
|
80
|
+
env=[
|
81
|
+
EnvFile(path=os.path.join(APP_DIR, "template.env")),
|
82
|
+
EnvMap(
|
83
|
+
vars={
|
84
|
+
**MICROSERVICES_ENV_VARS,
|
85
|
+
"APP_NAME_PORT": f"{port}",
|
86
|
+
"APP_NAME_MODULES": f"{module}",
|
87
|
+
}
|
88
|
+
),
|
89
|
+
],
|
90
|
+
cwd=APP_DIR,
|
91
|
+
cmd=[
|
92
|
+
ACTIVATE_VENV_SCRIPT,
|
93
|
+
'fastapi dev main.py --port "${APP_NAME_PORT}"',
|
94
|
+
],
|
95
|
+
render_cmd=False,
|
96
|
+
retries=2,
|
97
|
+
)
|
@@ -0,0 +1,132 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
from fastapp_template._zrb.config import ACTIVATE_VENV_SCRIPT, APP_DIR
|
4
|
+
from fastapp_template._zrb.group import (
|
5
|
+
app_create_group,
|
6
|
+
app_migrate_group,
|
7
|
+
app_run_group,
|
8
|
+
)
|
9
|
+
from fastapp_template._zrb.helper import (
|
10
|
+
create_migration,
|
11
|
+
migrate_module,
|
12
|
+
run_microservice,
|
13
|
+
)
|
14
|
+
from fastapp_template._zrb.venv_task import prepare_venv
|
15
|
+
|
16
|
+
from zrb import CmdTask, Env, EnvFile, Task
|
17
|
+
|
18
|
+
# 🚀 Run/Migrate All ===========================================================
|
19
|
+
|
20
|
+
run_all = app_run_group.add_task(
|
21
|
+
Task(
|
22
|
+
name="run-app-name", description="🟢 Run App Name as monolith and microservices"
|
23
|
+
),
|
24
|
+
alias="all",
|
25
|
+
)
|
26
|
+
|
27
|
+
migrate_all = app_migrate_group.add_task(
|
28
|
+
Task(
|
29
|
+
name="migrate-app-name",
|
30
|
+
description="📦 Run App Name DB migration for monolith and microservices",
|
31
|
+
),
|
32
|
+
alias="all",
|
33
|
+
)
|
34
|
+
|
35
|
+
create_all_migration = app_create_group.add_task(
|
36
|
+
Task(
|
37
|
+
name="create-app-name-migration", description="📦 Create App Name DB migration"
|
38
|
+
),
|
39
|
+
alias="migration",
|
40
|
+
)
|
41
|
+
|
42
|
+
# 🗿 Run/Migrate Monolith =====================================================
|
43
|
+
|
44
|
+
run_monolith = app_run_group.add_task(
|
45
|
+
CmdTask(
|
46
|
+
name="run-monolith-app-name",
|
47
|
+
description="🗿 Run App Name as a monolith",
|
48
|
+
env=[
|
49
|
+
EnvFile(path=os.path.join(APP_DIR, "template.env")),
|
50
|
+
Env(name="APP_NAME_MODE", default="monolith"),
|
51
|
+
],
|
52
|
+
cwd=APP_DIR,
|
53
|
+
cmd=[
|
54
|
+
ACTIVATE_VENV_SCRIPT,
|
55
|
+
'fastapi dev main.py --port "${APP_NAME_PORT}"',
|
56
|
+
],
|
57
|
+
render_cmd=False,
|
58
|
+
retries=2,
|
59
|
+
),
|
60
|
+
alias="monolith",
|
61
|
+
)
|
62
|
+
prepare_venv >> run_monolith >> run_all
|
63
|
+
|
64
|
+
migrate_monolith = app_migrate_group.add_task(
|
65
|
+
Task(
|
66
|
+
name="migrate-monolith-app-name",
|
67
|
+
description="🗿 Run App Name DB migration for monolith",
|
68
|
+
),
|
69
|
+
alias="monolith",
|
70
|
+
)
|
71
|
+
migrate_monolith >> migrate_all
|
72
|
+
|
73
|
+
# 🌐 Run/Migrate Microsevices ==================================================
|
74
|
+
|
75
|
+
run_microservices = app_run_group.add_task(
|
76
|
+
Task(
|
77
|
+
name="run-microservices-app-name",
|
78
|
+
description="🌐 Run App Name as microservices",
|
79
|
+
),
|
80
|
+
alias="microservices",
|
81
|
+
)
|
82
|
+
run_microservices >> run_all
|
83
|
+
|
84
|
+
migrate_microservices = app_migrate_group.add_task(
|
85
|
+
Task(
|
86
|
+
name="migrate-microservices-app-name",
|
87
|
+
description="🌐 Run App Name DB migration for microservices",
|
88
|
+
),
|
89
|
+
alias="microservices",
|
90
|
+
)
|
91
|
+
migrate_microservices >> migrate_all
|
92
|
+
|
93
|
+
# 📡 Run/Migrate Gateway =======================================================
|
94
|
+
|
95
|
+
run_gateway = app_run_group.add_task(
|
96
|
+
run_microservice("gateway", 3001, "gateway"), alias="microservices-gateway"
|
97
|
+
)
|
98
|
+
prepare_venv >> run_gateway >> run_microservices
|
99
|
+
|
100
|
+
create_gateway_migration = app_create_group.add_task(
|
101
|
+
create_migration("gateway", "gateway"), alias="gateway-migration"
|
102
|
+
)
|
103
|
+
prepare_venv >> create_gateway_migration >> create_all_migration
|
104
|
+
|
105
|
+
migrate_monolith_gateway = migrate_module("gateway", "gateway", as_microservices=False)
|
106
|
+
prepare_venv >> migrate_monolith_gateway >> [migrate_monolith, run_monolith]
|
107
|
+
|
108
|
+
migrate_microservices_gateway = app_migrate_group.add_task(
|
109
|
+
migrate_module("gateway", "gateway", as_microservices=True),
|
110
|
+
alias="microservices-gateway",
|
111
|
+
)
|
112
|
+
prepare_venv >> migrate_microservices_gateway >> [migrate_microservices, run_gateway]
|
113
|
+
|
114
|
+
# 🔐 Run/Migrate Auth ==========================================================
|
115
|
+
|
116
|
+
run_auth = app_run_group.add_task(
|
117
|
+
run_microservice("auth", 3002, "auth"), alias="microservices-auth"
|
118
|
+
)
|
119
|
+
prepare_venv >> run_auth >> run_microservices
|
120
|
+
|
121
|
+
create_auth_migration = app_create_group.add_task(
|
122
|
+
create_migration("auth", "auth"), alias="auth-migration"
|
123
|
+
)
|
124
|
+
prepare_venv >> create_auth_migration >> create_all_migration
|
125
|
+
|
126
|
+
migrate_monolith_auth = migrate_module("auth", "auth", as_microservices=False)
|
127
|
+
prepare_venv >> migrate_monolith_auth >> [migrate_monolith, run_monolith]
|
128
|
+
|
129
|
+
migrate_microservices_auth = app_migrate_group.add_task(
|
130
|
+
migrate_module("auth", "auth", as_microservices=True), alias="microservices-auth"
|
131
|
+
)
|
132
|
+
prepare_venv >> migrate_microservices_auth >> [migrate_microservices, run_auth]
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
from fastapp_template._zrb.config import ACTIVATE_VENV_SCRIPT, APP_DIR
|
4
|
+
from fastapp_template._zrb.group import app_group
|
5
|
+
|
6
|
+
from zrb import CmdTask
|
7
|
+
|
8
|
+
create_venv = CmdTask(
|
9
|
+
name="create-app-name-venv",
|
10
|
+
cwd=APP_DIR,
|
11
|
+
cmd="python -m venv .venv",
|
12
|
+
execute_condition=lambda _: not os.path.isdir(os.path.join(APP_DIR, ".venv")),
|
13
|
+
)
|
14
|
+
|
15
|
+
prepare_venv = CmdTask(
|
16
|
+
name="prepare-app-name-venv",
|
17
|
+
cmd=[ACTIVATE_VENV_SCRIPT, "pip install -r requirements.txt"],
|
18
|
+
cwd=APP_DIR,
|
19
|
+
)
|
20
|
+
create_venv >> prepare_venv
|
21
|
+
|
22
|
+
app_group.add_task(create_venv, alias="prepare")
|
@@ -0,0 +1,18 @@
|
|
1
|
+
from contextlib import asynccontextmanager
|
2
|
+
|
3
|
+
from fastapi import FastAPI
|
4
|
+
from fastapp_template.common.db_engine import engine
|
5
|
+
from fastapp_template.config import APP_MODE, APP_MODULES
|
6
|
+
from sqlmodel import SQLModel
|
7
|
+
|
8
|
+
|
9
|
+
@asynccontextmanager
|
10
|
+
async def lifespan(app: FastAPI):
|
11
|
+
SQLModel.metadata.create_all(engine)
|
12
|
+
yield
|
13
|
+
|
14
|
+
|
15
|
+
app_title = (
|
16
|
+
"App Name" if APP_MODE == "monolith" else f"App Name - {', '.join(APP_MODULES)}"
|
17
|
+
)
|
18
|
+
app = FastAPI(title=app_title, lifespan=lifespan)
|