zrb 1.0.0b2__py3-none-any.whl → 1.0.0b4__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/__main__.py +3 -0
- zrb/builtin/llm/llm_chat.py +85 -5
- zrb/builtin/llm/previous-session.js +13 -0
- zrb/builtin/llm/tool/api.py +29 -0
- zrb/builtin/llm/tool/cli.py +1 -1
- zrb/builtin/llm/tool/rag.py +108 -145
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/client_method.py +6 -6
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/gateway_subroute.py +3 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_db_repository.py +88 -44
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/config.py +12 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_client.py +28 -22
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_auth_tables.py +6 -6
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_db_repository.py +43 -29
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_repository.py +8 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/role_service.py +46 -14
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_db_repository.py +158 -20
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_repository.py +29 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service.py +36 -14
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py +14 -14
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/permission.py +1 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/role.py +34 -6
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/session.py +2 -6
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/user.py +41 -2
- zrb/builtin/todo.py +1 -0
- zrb/config.py +23 -4
- zrb/input/any_input.py +5 -0
- zrb/input/base_input.py +6 -0
- zrb/input/bool_input.py +2 -0
- zrb/input/float_input.py +2 -0
- zrb/input/int_input.py +2 -0
- zrb/input/option_input.py +2 -0
- zrb/input/password_input.py +2 -0
- zrb/input/text_input.py +2 -0
- zrb/runner/common_util.py +1 -1
- zrb/runner/web_route/error_page/show_error_page.py +2 -1
- zrb/runner/web_route/static/resources/session/current-session.js +4 -2
- zrb/runner/web_route/static/resources/session/event.js +8 -2
- zrb/runner/web_route/task_session_api_route.py +48 -3
- zrb/task/base_task.py +14 -13
- zrb/task/llm_task.py +214 -84
- zrb/util/llm/tool.py +3 -7
- {zrb-1.0.0b2.dist-info → zrb-1.0.0b4.dist-info}/METADATA +2 -1
- {zrb-1.0.0b2.dist-info → zrb-1.0.0b4.dist-info}/RECORD +45 -43
- {zrb-1.0.0b2.dist-info → zrb-1.0.0b4.dist-info}/WHEEL +0 -0
- {zrb-1.0.0b2.dist-info → zrb-1.0.0b4.dist-info}/entry_points.txt +0 -0
@@ -1,11 +1,23 @@
|
|
1
|
-
|
1
|
+
import datetime
|
2
|
+
from typing import Any, Callable
|
2
3
|
|
4
|
+
import ulid
|
3
5
|
from my_app_name.common.base_db_repository import BaseDBRepository
|
6
|
+
from my_app_name.common.error import NotFoundError
|
7
|
+
from my_app_name.config import (
|
8
|
+
APP_AUTH_GUEST_USER,
|
9
|
+
APP_AUTH_GUEST_USER_PERMISSIONS,
|
10
|
+
APP_AUTH_SUPER_USER,
|
11
|
+
APP_AUTH_SUPER_USER_PASSWORD,
|
12
|
+
APP_MAX_PARALLEL_SESSION,
|
13
|
+
APP_SESSION_EXPIRE_MINUTES,
|
14
|
+
)
|
4
15
|
from my_app_name.module.auth.service.user.repository.user_repository import (
|
5
16
|
UserRepository,
|
6
17
|
)
|
7
18
|
from my_app_name.schema.permission import Permission
|
8
19
|
from my_app_name.schema.role import Role, RolePermission
|
20
|
+
from my_app_name.schema.session import Session, SessionResponse
|
9
21
|
from my_app_name.schema.user import (
|
10
22
|
User,
|
11
23
|
UserCreateWithAudit,
|
@@ -14,8 +26,10 @@ from my_app_name.schema.user import (
|
|
14
26
|
UserUpdateWithAudit,
|
15
27
|
)
|
16
28
|
from passlib.context import CryptContext
|
17
|
-
from sqlalchemy.
|
18
|
-
from
|
29
|
+
from sqlalchemy.engine import Engine
|
30
|
+
from sqlalchemy.ext.asyncio import AsyncEngine
|
31
|
+
from sqlalchemy.sql import ClauseElement, ColumnElement, Select
|
32
|
+
from sqlmodel import SQLModel, delete, insert, select
|
19
33
|
|
20
34
|
# Password hashing context
|
21
35
|
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
@@ -36,39 +50,163 @@ class UserDBRepository(
|
|
36
50
|
entity_name = "user"
|
37
51
|
column_preprocessors = {"password": hash_password}
|
38
52
|
|
53
|
+
def __init__(
|
54
|
+
self,
|
55
|
+
engine: Engine | AsyncEngine,
|
56
|
+
super_user_username: str = APP_AUTH_SUPER_USER,
|
57
|
+
super_user_password: str = APP_AUTH_SUPER_USER_PASSWORD,
|
58
|
+
guest_user_username: str = APP_AUTH_GUEST_USER,
|
59
|
+
guest_user_password: str = APP_AUTH_SUPER_USER_PASSWORD,
|
60
|
+
guest_user_permission_names: list[str] = APP_AUTH_GUEST_USER_PERMISSIONS,
|
61
|
+
max_parallel_session: int = APP_MAX_PARALLEL_SESSION,
|
62
|
+
session_expire_minutes: int = APP_SESSION_EXPIRE_MINUTES,
|
63
|
+
filter_param_parser: (
|
64
|
+
Callable[[SQLModel, str], list[ClauseElement]] | None
|
65
|
+
) = None,
|
66
|
+
sort_param_parser: Callable[[SQLModel, str], list[ColumnElement]] | None = None,
|
67
|
+
):
|
68
|
+
super().__init__(
|
69
|
+
engine=engine,
|
70
|
+
filter_param_parser=filter_param_parser,
|
71
|
+
sort_param_parser=sort_param_parser,
|
72
|
+
)
|
73
|
+
self._super_user_username = super_user_username
|
74
|
+
self._super_user_passwored = super_user_password
|
75
|
+
self._guest_user_username = guest_user_username
|
76
|
+
self._guest_user_password = guest_user_password
|
77
|
+
self._guest_user_permission_names = guest_user_permission_names
|
78
|
+
self._max_parallel_session = max_parallel_session
|
79
|
+
self._session_expire_minutes = session_expire_minutes
|
80
|
+
self._super_user: User | None = None
|
81
|
+
self._guest_user: User | None = None
|
82
|
+
|
39
83
|
def _select(self) -> Select:
|
40
84
|
return (
|
41
|
-
select(User, Role, Permission)
|
85
|
+
select(User, Role, Permission, Session)
|
42
86
|
.join(UserRole, UserRole.user_id == User.id, isouter=True)
|
43
87
|
.join(Role, Role.id == UserRole.role_id, isouter=True)
|
44
88
|
.join(RolePermission, RolePermission.role_id == Role.id, isouter=True)
|
45
|
-
.join(
|
89
|
+
.join(
|
90
|
+
Permission, Permission.id == RolePermission.permission_id, isouter=True
|
91
|
+
)
|
92
|
+
.join(Session, Session.user_id == User.id)
|
46
93
|
)
|
47
94
|
|
48
|
-
def _rows_to_responses(
|
49
|
-
self, rows: list[tuple[User, Role, Permission]]
|
50
|
-
) -> UserResponse:
|
95
|
+
def _rows_to_responses(self, rows: list[tuple[Any, ...]]) -> list[UserResponse]:
|
51
96
|
user_map: dict[str, dict[str, Any]] = {}
|
52
|
-
|
97
|
+
user_role_map: dict[str, list[str]] = {}
|
98
|
+
user_permission_map: dict[str, list[str]] = {}
|
99
|
+
for user, role, permission, _ in rows:
|
53
100
|
if user.id not in user_map:
|
54
|
-
user_map[user.id] = {"user": user, "roles":
|
55
|
-
|
56
|
-
|
57
|
-
if
|
58
|
-
|
101
|
+
user_map[user.id] = {"user": user, "roles": [], "permissions": []}
|
102
|
+
user_role_map[user.id] = []
|
103
|
+
user_permission_map[user.id] = []
|
104
|
+
if role is not None and role.id not in user_role_map[user.id]:
|
105
|
+
user_role_map[user.id].append(role.id)
|
106
|
+
user_map[user.id]["roles"].append(role.model_dump())
|
107
|
+
if (
|
108
|
+
permission is not None
|
109
|
+
and permission.id not in user_permission_map[user.id]
|
110
|
+
):
|
111
|
+
user_permission_map[user.id].append(permission.id)
|
112
|
+
user_map[user.id]["permissions"].append(permission.model_dump())
|
59
113
|
return [
|
60
114
|
UserResponse(
|
61
115
|
**data["user"].model_dump(),
|
62
116
|
roles=list(data["roles"]),
|
63
|
-
permissions=list(data["permissions"])
|
117
|
+
permissions=list(data["permissions"]),
|
64
118
|
)
|
65
119
|
for data in user_map.values()
|
66
120
|
]
|
67
121
|
|
122
|
+
async def add_roles(self, data: dict[str, list[str]], created_by: str):
|
123
|
+
now = datetime.datetime.now(datetime.timezone.utc)
|
124
|
+
data_dict_list: list[dict[str, Any]] = []
|
125
|
+
for user_id, role_ids in data.items():
|
126
|
+
for role_id in role_ids:
|
127
|
+
data_dict_list.append(
|
128
|
+
self._model_to_data_dict(
|
129
|
+
UserRole(
|
130
|
+
id=ulid.new().str,
|
131
|
+
user_id=user_id,
|
132
|
+
role_id=role_id,
|
133
|
+
created_at=now,
|
134
|
+
created_by=created_by,
|
135
|
+
)
|
136
|
+
)
|
137
|
+
)
|
138
|
+
async with self._session_scope() as session:
|
139
|
+
await self._execute_statement(
|
140
|
+
session, insert(UserRole).values(data_dict_list)
|
141
|
+
)
|
142
|
+
|
143
|
+
async def remove_all_roles(self, user_ids: list[str] = []):
|
144
|
+
async with self._session_scope() as session:
|
145
|
+
await self._execute_statement(
|
146
|
+
session,
|
147
|
+
delete(UserRole).where(UserRole.user_id._in(user_ids)),
|
148
|
+
)
|
149
|
+
|
68
150
|
async def get_by_credentials(self, username: str, password: str) -> UserResponse:
|
69
|
-
|
70
|
-
|
151
|
+
rows = await self._select_to_response(
|
152
|
+
lambda q: q.where(
|
153
|
+
User.username == username, User.password == hash_password(password)
|
154
|
+
)
|
71
155
|
)
|
72
|
-
|
73
|
-
|
74
|
-
|
156
|
+
return self._ensure_one(rows)
|
157
|
+
|
158
|
+
async def get_by_token(self, token: str) -> UserResponse:
|
159
|
+
rows = await self._select_tor_response(
|
160
|
+
lambda q: q.where(Session.token == token)
|
161
|
+
)
|
162
|
+
return self._ensure_one(rows)
|
163
|
+
|
164
|
+
async def add_token(self, user_id: str, token: str):
|
165
|
+
async with self._session_scope() as session:
|
166
|
+
await self._execute_statement(
|
167
|
+
session,
|
168
|
+
insert(Session).values(
|
169
|
+
{
|
170
|
+
"id": ulid.new().str,
|
171
|
+
"user_id": user_id,
|
172
|
+
"token": token,
|
173
|
+
"created_by": "system",
|
174
|
+
"created_at": datetime.datetime.now(datetime.timezone.utc),
|
175
|
+
}
|
176
|
+
),
|
177
|
+
)
|
178
|
+
|
179
|
+
async def remove_token(self, user_id: str, token: str):
|
180
|
+
async with self._session_scope() as session:
|
181
|
+
await self._execute_statement(
|
182
|
+
session,
|
183
|
+
delete(Session).where(
|
184
|
+
Session.token == token, Session.user_id == user_id
|
185
|
+
),
|
186
|
+
)
|
187
|
+
|
188
|
+
async def get_sessions(self, user_id: str) -> list[SessionResponse]:
|
189
|
+
async with self._session_scope() as session:
|
190
|
+
statement = select(Session).where(Session.user_id == user_id)
|
191
|
+
result = await self._execute_statement(session, statement)
|
192
|
+
return [
|
193
|
+
SessionResponse(**session.model_dump())
|
194
|
+
for session in result.scalars().all()
|
195
|
+
]
|
196
|
+
|
197
|
+
async def remove_session(self, user_id: str, session_id: str) -> SessionResponse:
|
198
|
+
async with self._session_scope() as session:
|
199
|
+
statement = select(Session).where(
|
200
|
+
Session.user_id == user_id, Session.id == session_id
|
201
|
+
)
|
202
|
+
result = await self._execute_statement(session, statement)
|
203
|
+
session = result.scalar_one_or_none()
|
204
|
+
if not session:
|
205
|
+
raise NotFoundError(f"{self.entity_name} not found")
|
206
|
+
await self._execute_statement(
|
207
|
+
session,
|
208
|
+
delete(Session).where(
|
209
|
+
Session.id == session_id, Session.user_id == user_id
|
210
|
+
),
|
211
|
+
)
|
212
|
+
return SessionResponse(**session.model_dump())
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from abc import ABC, abstractmethod
|
2
2
|
|
3
|
+
from my_app_name.schema.session import SessionResponse
|
3
4
|
from my_app_name.schema.user import (
|
4
5
|
User,
|
5
6
|
UserCreateWithAudit,
|
@@ -18,6 +19,14 @@ class UserRepository(ABC):
|
|
18
19
|
async def get_by_ids(self, id_list: list[str]) -> UserResponse:
|
19
20
|
"""Get users by ids"""
|
20
21
|
|
22
|
+
@abstractmethod
|
23
|
+
async def add_roles(self, data: dict[str, list[str]], created_by: str):
|
24
|
+
"""Add roles to user"""
|
25
|
+
|
26
|
+
@abstractmethod
|
27
|
+
async def remove_all_roles(self, user_ids: list[str] = []):
|
28
|
+
"""Remove roles from user"""
|
29
|
+
|
21
30
|
@abstractmethod
|
22
31
|
async def get(
|
23
32
|
self,
|
@@ -61,3 +70,23 @@ class UserRepository(ABC):
|
|
61
70
|
@abstractmethod
|
62
71
|
async def get_by_credentials(self, username: str, password: str) -> UserResponse:
|
63
72
|
"""Get user by credential"""
|
73
|
+
|
74
|
+
@abstractmethod
|
75
|
+
async def get_by_token(self, token: str) -> UserResponse:
|
76
|
+
"""Get user by token"""
|
77
|
+
|
78
|
+
@abstractmethod
|
79
|
+
async def add_token(self, user_id: str, token: str):
|
80
|
+
"""Add token to user"""
|
81
|
+
|
82
|
+
@abstractmethod
|
83
|
+
async def remove_token(self, user_id: str, token: str):
|
84
|
+
"""Remove token from user"""
|
85
|
+
|
86
|
+
@abstractmethod
|
87
|
+
async def get_sessions(self, user_id: str) -> list[SessionResponse]:
|
88
|
+
"""Get sessions"""
|
89
|
+
|
90
|
+
@abstractmethod
|
91
|
+
async def remove_session(self, user_id: str, session_id: str) -> SessionResponse:
|
92
|
+
"""Remove a session"""
|
@@ -6,9 +6,9 @@ from my_app_name.module.auth.service.user.repository.user_repository import (
|
|
6
6
|
)
|
7
7
|
from my_app_name.schema.user import (
|
8
8
|
MultipleUserResponse,
|
9
|
-
|
9
|
+
UserCreateWithRolesAndAudit,
|
10
10
|
UserResponse,
|
11
|
-
|
11
|
+
UserUpdateWithRolesAndAudit,
|
12
12
|
)
|
13
13
|
|
14
14
|
|
@@ -42,35 +42,57 @@ class UserService(BaseService):
|
|
42
42
|
count = await self.user_repository.count(filter)
|
43
43
|
return MultipleUserResponse(data=users, count=count)
|
44
44
|
|
45
|
-
@BaseService.route(
|
46
|
-
"/api/v1/users",
|
47
|
-
methods=["post"],
|
48
|
-
response_model=UserResponse,
|
49
|
-
)
|
50
|
-
async def create_user(self, data: UserCreateWithAudit) -> UserResponse:
|
51
|
-
user = await self.user_repository.create(data)
|
52
|
-
return await self.user_repository.get_by_id(user.id)
|
53
|
-
|
54
45
|
@BaseService.route(
|
55
46
|
"/api/v1/users/bulk",
|
56
47
|
methods=["post"],
|
57
48
|
response_model=list[UserResponse],
|
58
49
|
)
|
59
50
|
async def create_user_bulk(
|
60
|
-
self, data: list[
|
51
|
+
self, data: list[UserCreateWithRolesAndAudit]
|
61
52
|
) -> list[UserResponse]:
|
53
|
+
role_ids = [row.get_role_ids() for row in data]
|
54
|
+
data = [row.get_user_create_with_audit() for row in data]
|
62
55
|
users = await self.user_repository.create_bulk(data)
|
56
|
+
if len(users) > 0:
|
57
|
+
created_by = users[0].created_by
|
58
|
+
await self.user_repository.add_roles(
|
59
|
+
data={user.id: role_ids[i] for i, user in enumerate(data)},
|
60
|
+
created_by=created_by,
|
61
|
+
)
|
63
62
|
return await self.user_repository.get_by_ids([user.id for user in users])
|
64
63
|
|
64
|
+
@BaseService.route(
|
65
|
+
"/api/v1/users",
|
66
|
+
methods=["post"],
|
67
|
+
response_model=UserResponse,
|
68
|
+
)
|
69
|
+
async def create_user(self, data: UserCreateWithRolesAndAudit) -> UserResponse:
|
70
|
+
role_ids = data.get_role_ids()
|
71
|
+
data = data.get_user_create_with_audit()
|
72
|
+
user = await self.user_repository.create(data)
|
73
|
+
await self.user_repository.add_roles(
|
74
|
+
data={user.id: role_ids}, created_by=user.created_by
|
75
|
+
)
|
76
|
+
return await self.user_repository.get_by_id(user.id)
|
77
|
+
|
65
78
|
@BaseService.route(
|
66
79
|
"/api/v1/users/bulk",
|
67
80
|
methods=["put"],
|
68
81
|
response_model=UserResponse,
|
69
82
|
)
|
70
83
|
async def update_user_bulk(
|
71
|
-
self, user_ids: list[str], data:
|
84
|
+
self, user_ids: list[str], data: UserUpdateWithRolesAndAudit
|
72
85
|
) -> UserResponse:
|
86
|
+
role_ids = [row.get_role_ids() for row in data]
|
87
|
+
data = [row.get_user_create_with_audit() for row in data]
|
73
88
|
users = await self.user_repository.update_bulk(user_ids, data)
|
89
|
+
if len(users) > 0:
|
90
|
+
updated_by = users[0].updated_by
|
91
|
+
await self.user_repository.remove_all_roles([user.id for user in users])
|
92
|
+
await self.user_repository.add_roles(
|
93
|
+
data={user.id: role_ids[i] for i, user in enumerate(data)},
|
94
|
+
updated_by=updated_by,
|
95
|
+
)
|
74
96
|
return await self.user_repository.get_by_ids([user.id for user in users])
|
75
97
|
|
76
98
|
@BaseService.route(
|
@@ -79,7 +101,7 @@ class UserService(BaseService):
|
|
79
101
|
response_model=UserResponse,
|
80
102
|
)
|
81
103
|
async def update_user(
|
82
|
-
self, user_id: str, data:
|
104
|
+
self, user_id: str, data: UserUpdateWithRolesAndAudit
|
83
105
|
) -> UserResponse:
|
84
106
|
user = await self.user_repository.update(user_id, data)
|
85
107
|
return await self.user_repository.get_by_id(user.id)
|
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py
CHANGED
@@ -8,15 +8,15 @@ from my_app_name.schema.permission import (
|
|
8
8
|
)
|
9
9
|
from my_app_name.schema.role import (
|
10
10
|
MultipleRoleResponse,
|
11
|
-
|
11
|
+
RoleCreateWithPermissions,
|
12
12
|
RoleResponse,
|
13
|
-
|
13
|
+
RoleUpdateWithPermissions,
|
14
14
|
)
|
15
15
|
from my_app_name.schema.user import (
|
16
16
|
MultipleUserResponse,
|
17
|
-
|
17
|
+
UserCreateWithRoles,
|
18
18
|
UserResponse,
|
19
|
-
|
19
|
+
UserUpdateWithRoles,
|
20
20
|
)
|
21
21
|
|
22
22
|
|
@@ -44,7 +44,7 @@ def serve_auth_route(app: FastAPI):
|
|
44
44
|
response_model=list[PermissionResponse],
|
45
45
|
)
|
46
46
|
async def create_permission_bulk(data: list[PermissionCreate]):
|
47
|
-
return await auth_client.
|
47
|
+
return await auth_client.create_permission_bulk(
|
48
48
|
[row.with_audit(created_by="system") for row in data]
|
49
49
|
)
|
50
50
|
|
@@ -108,8 +108,8 @@ def serve_auth_route(app: FastAPI):
|
|
108
108
|
"/api/v1/roles/bulk",
|
109
109
|
response_model=list[RoleResponse],
|
110
110
|
)
|
111
|
-
async def create_role_bulk(data: list[
|
112
|
-
return await auth_client.
|
111
|
+
async def create_role_bulk(data: list[RoleCreateWithPermissions]):
|
112
|
+
return await auth_client.create_role_bulk(
|
113
113
|
[row.with_audit(created_by="system") for row in data]
|
114
114
|
)
|
115
115
|
|
@@ -117,14 +117,14 @@ def serve_auth_route(app: FastAPI):
|
|
117
117
|
"/api/v1/roles",
|
118
118
|
response_model=RoleResponse,
|
119
119
|
)
|
120
|
-
async def create_role(data:
|
120
|
+
async def create_role(data: RoleCreateWithPermissions):
|
121
121
|
return await auth_client.create_role(data.with_audit(created_by="system"))
|
122
122
|
|
123
123
|
@app.put(
|
124
124
|
"/api/v1/roles/bulk",
|
125
125
|
response_model=list[RoleResponse],
|
126
126
|
)
|
127
|
-
async def update_role_bulk(role_ids: list[str], data:
|
127
|
+
async def update_role_bulk(role_ids: list[str], data: RoleUpdateWithPermissions):
|
128
128
|
return await auth_client.update_role_bulk(
|
129
129
|
role_ids, data.with_audit(updated_by="system")
|
130
130
|
)
|
@@ -133,7 +133,7 @@ def serve_auth_route(app: FastAPI):
|
|
133
133
|
"/api/v1/roles/{role_id}",
|
134
134
|
response_model=RoleResponse,
|
135
135
|
)
|
136
|
-
async def update_role(role_id: str, data:
|
136
|
+
async def update_role(role_id: str, data: RoleUpdateWithPermissions):
|
137
137
|
return await auth_client.update_role(data.with_audit(updated_by="system"))
|
138
138
|
|
139
139
|
@app.delete(
|
@@ -171,7 +171,7 @@ def serve_auth_route(app: FastAPI):
|
|
171
171
|
"/api/v1/users/bulk",
|
172
172
|
response_model=list[UserResponse],
|
173
173
|
)
|
174
|
-
async def create_user_bulk(data: list[
|
174
|
+
async def create_user_bulk(data: list[UserCreateWithRoles]):
|
175
175
|
return await auth_client.create_user(
|
176
176
|
[row.with_audit(created_by="system") for row in data]
|
177
177
|
)
|
@@ -180,14 +180,14 @@ def serve_auth_route(app: FastAPI):
|
|
180
180
|
"/api/v1/users",
|
181
181
|
response_model=UserResponse,
|
182
182
|
)
|
183
|
-
async def create_user(data:
|
183
|
+
async def create_user(data: UserCreateWithRoles):
|
184
184
|
return await auth_client.create_user(data.with_audit(created_by="system"))
|
185
185
|
|
186
186
|
@app.put(
|
187
187
|
"/api/v1/users/bulk",
|
188
188
|
response_model=list[UserResponse],
|
189
189
|
)
|
190
|
-
async def update_user_bulk(user_ids: list[str], data:
|
190
|
+
async def update_user_bulk(user_ids: list[str], data: UserUpdateWithRoles):
|
191
191
|
return await auth_client.update_user_bulk(
|
192
192
|
user_ids, data.with_audit(updated_by="system")
|
193
193
|
)
|
@@ -196,7 +196,7 @@ def serve_auth_route(app: FastAPI):
|
|
196
196
|
"/api/v1/users/{user_id}",
|
197
197
|
response_model=UserResponse,
|
198
198
|
)
|
199
|
-
async def update_user(user_id: str, data:
|
199
|
+
async def update_user(user_id: str, data: UserUpdateWithRoles):
|
200
200
|
return await auth_client.update_user(data.with_audit(updated_by="system"))
|
201
201
|
|
202
202
|
@app.delete(
|
@@ -47,5 +47,5 @@ class Permission(SQLModel, table=True):
|
|
47
47
|
created_by: str | None = Field(index=True)
|
48
48
|
updated_at: datetime.datetime | None = Field(index=True)
|
49
49
|
updated_by: str | None = Field(index=True)
|
50
|
-
name: str = Field(index=True)
|
50
|
+
name: str = Field(index=True, unique=True)
|
51
51
|
description: str
|
@@ -22,7 +22,7 @@ class RoleCreateWithAudit(RoleCreate):
|
|
22
22
|
|
23
23
|
|
24
24
|
class RoleCreateWithPermissions(RoleCreate):
|
25
|
-
|
25
|
+
permission_ids: list[str] | None = None
|
26
26
|
|
27
27
|
def with_audit(self, created_by: str) -> "RoleCreateWithPermissionsAndAudit":
|
28
28
|
return RoleCreateWithPermissionsAndAudit(
|
@@ -35,14 +35,16 @@ class RoleCreateWithPermissionsAndAudit(RoleCreateWithPermissions):
|
|
35
35
|
|
36
36
|
def get_role_create_with_audit(self) -> RoleCreateWithAudit:
|
37
37
|
data = {
|
38
|
-
key: val
|
38
|
+
key: val
|
39
|
+
for key, val in self.model_dump().items()
|
40
|
+
if key != "permission_ids"
|
39
41
|
}
|
40
42
|
return RoleCreateWithAudit(**data)
|
41
43
|
|
42
|
-
def
|
43
|
-
if self.
|
44
|
+
def get_permission_ids(self) -> list[str]:
|
45
|
+
if self.permission_ids is None:
|
44
46
|
return []
|
45
|
-
return self.
|
47
|
+
return self.permission_ids
|
46
48
|
|
47
49
|
|
48
50
|
class RoleUpdate(SQLModel):
|
@@ -57,6 +59,32 @@ class RoleUpdateWithAudit(RoleUpdate):
|
|
57
59
|
updated_by: str
|
58
60
|
|
59
61
|
|
62
|
+
class RoleUpdateWithPermissions(RoleUpdate):
|
63
|
+
permission_ids: list[str] | None = None
|
64
|
+
|
65
|
+
def with_audit(self, updated_by: str) -> "RoleUpdateWithPermissionsAndAudit":
|
66
|
+
return RoleUpdateWithPermissionsAndAudit(
|
67
|
+
**self.model_dump(), updated_by=updated_by
|
68
|
+
)
|
69
|
+
|
70
|
+
|
71
|
+
class RoleUpdateWithPermissionsAndAudit(RoleUpdateWithPermissions):
|
72
|
+
updated_by: str
|
73
|
+
|
74
|
+
def get_role_update_with_audit(self) -> RoleUpdateWithAudit:
|
75
|
+
data = {
|
76
|
+
key: val
|
77
|
+
for key, val in self.model_dump().items()
|
78
|
+
if key != "permission_ids"
|
79
|
+
}
|
80
|
+
return RoleUpdateWithAudit(**data)
|
81
|
+
|
82
|
+
def get_permission_ids(self) -> list[str]:
|
83
|
+
if self.permission_ids is None:
|
84
|
+
return []
|
85
|
+
return self.permission_ids
|
86
|
+
|
87
|
+
|
60
88
|
class RoleResponse(RoleBase):
|
61
89
|
id: str
|
62
90
|
permissions: list[Permission]
|
@@ -73,7 +101,7 @@ class Role(SQLModel, table=True):
|
|
73
101
|
created_by: str | None = Field(index=True)
|
74
102
|
updated_at: datetime.datetime | None = Field(index=True)
|
75
103
|
updated_by: str | None = Field(index=True)
|
76
|
-
name: str = Field(index=True)
|
104
|
+
name: str = Field(index=True, unique=True)
|
77
105
|
description: str
|
78
106
|
|
79
107
|
|
@@ -7,9 +7,7 @@ from sqlmodel import Field, SQLModel
|
|
7
7
|
|
8
8
|
class SessionBase(SQLModel):
|
9
9
|
user_id: str
|
10
|
-
|
11
|
-
os: str
|
12
|
-
browser: str
|
10
|
+
token: str
|
13
11
|
expired_at: datetime.datetime | None
|
14
12
|
|
15
13
|
|
@@ -47,6 +45,4 @@ class MultipleSessionResponse(BaseModel):
|
|
47
45
|
class Session(SQLModel, table=True):
|
48
46
|
id: str = Field(default_factory=lambda: ulid.new().str, primary_key=True)
|
49
47
|
user_id: str = Field(index=True)
|
50
|
-
|
51
|
-
os: str
|
52
|
-
browser: str
|
48
|
+
token: str = Field(index=True, unique=True)
|
@@ -13,7 +13,6 @@ class UserBase(SQLModel):
|
|
13
13
|
|
14
14
|
class UserCreate(UserBase):
|
15
15
|
password: str
|
16
|
-
roles: list[str] | None = None
|
17
16
|
|
18
17
|
def with_audit(self, created_by: str) -> "UserCreateWithAudit":
|
19
18
|
return UserCreateWithAudit(**self.model_dump(), created_by=created_by)
|
@@ -23,6 +22,26 @@ class UserCreateWithAudit(UserCreate):
|
|
23
22
|
created_by: str
|
24
23
|
|
25
24
|
|
25
|
+
class UserCreateWithRoles(UserCreate):
|
26
|
+
role_ids: list[str] | None = None
|
27
|
+
|
28
|
+
def with_audit(self, created_by: str) -> "UserCreateWithRolesAndAudit":
|
29
|
+
return UserCreateWithRolesAndAudit(**self.model_dump(), created_by=created_by)
|
30
|
+
|
31
|
+
|
32
|
+
class UserCreateWithRolesAndAudit(UserCreateWithRoles):
|
33
|
+
created_by: str
|
34
|
+
|
35
|
+
def get_user_create_with_audit(self) -> UserCreateWithAudit:
|
36
|
+
data = {key: val for key, val in self.model_dump().items() if key != "role_ids"}
|
37
|
+
return UserCreateWithAudit(**data)
|
38
|
+
|
39
|
+
def get_role_ids(self) -> list[str]:
|
40
|
+
if self.role_ids is None:
|
41
|
+
return []
|
42
|
+
return self.role_ids
|
43
|
+
|
44
|
+
|
26
45
|
class UserUpdate(SQLModel):
|
27
46
|
username: str | None = None
|
28
47
|
password: str | None = None
|
@@ -35,6 +54,26 @@ class UserUpdateWithAudit(UserUpdate):
|
|
35
54
|
updated_by: str
|
36
55
|
|
37
56
|
|
57
|
+
class UserUpdateWithRoles(UserUpdate):
|
58
|
+
role_ids: list[str] | None = None
|
59
|
+
|
60
|
+
def with_audit(self, updated_by: str) -> "UserUpdateWithRolesAndAudit":
|
61
|
+
return UserUpdateWithRolesAndAudit(**self.model_dump(), updated_by=updated_by)
|
62
|
+
|
63
|
+
|
64
|
+
class UserUpdateWithRolesAndAudit(UserUpdateWithRoles):
|
65
|
+
updated_by: str
|
66
|
+
|
67
|
+
def get_user_update_with_audit(self) -> UserUpdateWithAudit:
|
68
|
+
data = {key: val for key, val in self.model_dump().items() if key != "role_ids"}
|
69
|
+
return UserUpdateWithAudit(**data)
|
70
|
+
|
71
|
+
def get_role_ids(self) -> list[str]:
|
72
|
+
if self.role_ids is None:
|
73
|
+
return []
|
74
|
+
return self.role_ids
|
75
|
+
|
76
|
+
|
38
77
|
class UserResponse(UserBase):
|
39
78
|
id: str
|
40
79
|
roles: list[Role]
|
@@ -52,7 +91,7 @@ class User(SQLModel, table=True):
|
|
52
91
|
created_by: str = Field(index=True)
|
53
92
|
updated_at: datetime.datetime | None = Field(index=True)
|
54
93
|
updated_by: str | None = Field(index=True)
|
55
|
-
username: str = Field(index=True)
|
94
|
+
username: str = Field(index=True, unique=True)
|
56
95
|
password: str
|
57
96
|
|
58
97
|
|
zrb/builtin/todo.py
CHANGED
@@ -294,6 +294,7 @@ def _get_default_stop_work_time_str() -> str:
|
|
294
294
|
description="Todo.txt content",
|
295
295
|
prompt="Todo.txt content (will override existing)",
|
296
296
|
default_str=lambda _: _get_todo_txt_content(),
|
297
|
+
allow_positional_parsing=False,
|
297
298
|
),
|
298
299
|
],
|
299
300
|
description="📝 Edit todo",
|
zrb/config.py
CHANGED
@@ -77,13 +77,32 @@ WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES = int(
|
|
77
77
|
os.getenv("ZRB_WEB_REFRESH_TOKEN_EXPIRE_MINUTES", "60")
|
78
78
|
)
|
79
79
|
LLM_MODEL = os.getenv("ZRB_LLM_MODEL", "ollama_chat/llama3.1")
|
80
|
-
|
80
|
+
|
81
|
+
_DEFAULT_PROMPT = """
|
82
|
+
You are a helpful assistant and you have access to several tools.
|
83
|
+
Your goal is to provide a final answer by executing a series of planning, actions, reasoning, and evaluations.
|
84
|
+
|
85
|
+
Breakdown user request into several actionable tasks. For example, when user ask about current weather on current location, you should get the current location first.
|
86
|
+
|
87
|
+
DO NOT TRY TO SIMULATE TOOL OUTPUT.
|
88
|
+
|
89
|
+
ERROR HANDLING
|
90
|
+
1. If you receive an error, read the error message carefully and identify the specific issue.
|
91
|
+
2. Adjust your response accordingly and perform the review process again before resubmitting.
|
92
|
+
|
93
|
+
REMINDER:
|
94
|
+
- ALWAYS double-check your response format and function arguments before submitting.
|
95
|
+
- DON'T make up answers.
|
96
|
+
""".strip()
|
97
|
+
LLM_SYSTEM_PROMPT = os.getenv("ZRB_LLM_SYSTEM_PROMPT", _DEFAULT_PROMPT)
|
98
|
+
LLM_HISTORY_DIR = os.getenv(
|
99
|
+
"ZRB_LLM_HISTORY_DIR", os.path.expanduser(os.path.join("~", ".zrb-llm-history"))
|
100
|
+
)
|
81
101
|
LLM_HISTORY_FILE = os.getenv(
|
82
|
-
"ZRB_LLM_HISTORY_FILE",
|
83
|
-
os.path.expanduser(os.path.join("~", ".zrb-llm-history.json")),
|
102
|
+
"ZRB_LLM_HISTORY_FILE", os.path.join(LLM_HISTORY_DIR, "history.json")
|
84
103
|
)
|
85
104
|
LLM_ALLOW_ACCESS_SHELL = to_boolean(os.getenv("ZRB_LLM_ACCESS_FILE", "1"))
|
86
|
-
|
105
|
+
LLM_ALLOW_ACCESS_INTERNET = to_boolean(os.getenv("ZRB_LLM_ACCESS_INTERNET", "1"))
|
87
106
|
RAG_EMBEDDING_MODEL = os.getenv("ZRB_RAG_EMBEDDING_MODEL", "ollama/nomic-embed-text")
|
88
107
|
RAG_CHUNK_SIZE = int(os.getenv("ZRB_RAG_CHUNK_SIZE", "1024"))
|
89
108
|
RAG_OVERLAP = int(os.getenv("ZRB_RAG_OVERLAP", "128"))
|
zrb/input/any_input.py
CHANGED