fastapi-fullstack 0.1.2__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.
- fastapi_fullstack-0.1.2.dist-info/METADATA +545 -0
- fastapi_fullstack-0.1.2.dist-info/RECORD +221 -0
- fastapi_fullstack-0.1.2.dist-info/WHEEL +4 -0
- fastapi_fullstack-0.1.2.dist-info/entry_points.txt +2 -0
- fastapi_fullstack-0.1.2.dist-info/licenses/LICENSE +21 -0
- fastapi_gen/__init__.py +3 -0
- fastapi_gen/cli.py +256 -0
- fastapi_gen/config.py +255 -0
- fastapi_gen/generator.py +181 -0
- fastapi_gen/prompts.py +648 -0
- fastapi_gen/template/cookiecutter.json +76 -0
- fastapi_gen/template/hooks/post_gen_project.py +111 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/.env.example +136 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/.github/workflows/ci.yml +150 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/.gitignore +108 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/CLAUDE.md +357 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/Makefile +298 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/README.md +723 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.dockerignore +60 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.pre-commit-config.yaml +32 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/Dockerfile +56 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/env.py +76 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/script.py.mako +30 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/versions/.gitkeep +0 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic.ini +48 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/__init__.py +3 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/admin.py +115 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/__init__.py +13 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/assistant.py +202 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/__init__.py +13 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/datetime_tool.py +17 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/__init__.py +1 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/deps.py +528 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/exception_handlers.py +85 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/router.py +10 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/__init__.py +9 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/__init__.py +87 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/agent.py +448 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/auth.py +395 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/conversations.py +490 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/health.py +227 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/items.py +275 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/oauth.py +205 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/sessions.py +168 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/users.py +333 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/webhooks.py +477 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/ws.py +46 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/versioning.py +221 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/clients/__init__.py +14 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/clients/redis.py +88 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/__init__.py +117 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/cleanup.py +75 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/example.py +28 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/seed.py +266 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/__init__.py +5 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/cache.py +23 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/config.py +247 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/csrf.py +153 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/exceptions.py +122 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/logfire_setup.py +101 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/middleware.py +99 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/oauth.py +23 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/rate_limit.py +58 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/sanitize.py +271 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/security.py +102 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/__init__.py +7 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/base.py +41 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/__init__.py +31 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/conversation.py +319 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/item.py +96 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/session.py +126 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/user.py +218 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/webhook.py +244 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/session.py +113 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/main.py +326 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/pipelines/__init__.py +9 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/pipelines/base.py +73 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/__init__.py +49 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/base.py +154 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/conversation.py +760 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/item.py +222 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/session.py +318 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/user.py +322 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/webhook.py +358 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/__init__.py +50 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/base.py +57 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/conversation.py +195 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/item.py +52 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/session.py +42 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/token.py +31 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/user.py +64 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/webhook.py +89 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/__init__.py +38 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/conversation.py +797 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/item.py +246 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/session.py +333 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/user.py +432 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/webhook.py +561 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/__init__.py +5 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/celery_app.py +64 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/taskiq_app.py +38 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/__init__.py +25 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/examples.py +106 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/schedules.py +29 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/taskiq_examples.py +92 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/cli/__init__.py +1 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/cli/commands.py +438 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/pyproject.toml +158 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/scripts/.gitkeep +0 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/__init__.py +1 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/__init__.py +1 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_auth.py +242 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_exceptions.py +151 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_health.py +113 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_items.py +310 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_users.py +253 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/conftest.py +151 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_agents.py +121 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_clients.py +183 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_commands.py +173 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_core.py +143 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_pipelines.py +118 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_repositories.py +181 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_security.py +124 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_services.py +363 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_worker.py +85 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.dev.yml +242 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.frontend.yml +31 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.prod.yml +382 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.yml +241 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.env.example +12 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.gitignore +45 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.prettierignore +19 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.prettierrc +11 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/Dockerfile +44 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/README.md +693 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.setup.ts +49 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.spec.ts +134 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/chat.spec.ts +207 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/home.spec.ts +73 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/instrumentation.ts +14 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/messages/en.json +84 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/messages/pl.json +84 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/next.config.ts +76 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/package.json +66 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/playwright.config.ts +101 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/postcss.config.mjs +7 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(auth)/layout.tsx +11 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(auth)/login/page.tsx +5 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(auth)/register/page.tsx +5 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/chat/page.tsx +20 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/dashboard/page.tsx +99 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/layout.tsx +17 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/profile/page.tsx +156 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/login/route.ts +58 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/logout/route.ts +24 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/me/route.ts +39 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/oauth-callback/route.ts +50 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/refresh/route.ts +54 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/register/route.ts +26 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/messages/route.ts +41 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/route.ts +108 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/route.ts +73 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/health/route.ts +21 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/auth/callback/page.tsx +96 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/globals.css +108 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/layout.tsx +25 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/page.tsx +73 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/providers.tsx +29 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/index.ts +2 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/login-form.tsx +120 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/register-form.tsx +153 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-container.tsx +135 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-input.tsx +73 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/conversation-sidebar.tsx +261 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/index.ts +8 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-item.tsx +63 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-list.tsx +18 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/tool-call-card.tsx +60 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/google-icon.tsx +32 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/index.ts +3 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/language-switcher.tsx +97 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/header.tsx +45 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/index.ts +2 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/sidebar.tsx +48 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/index.ts +7 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-provider.tsx +53 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-toggle.tsx +83 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/badge.tsx +35 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.test.tsx +75 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.tsx +54 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/card.tsx +82 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/index.ts +12 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/input.tsx +21 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/label.tsx +21 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/index.ts +6 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-auth.ts +97 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-chat.ts +203 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-conversations.ts +175 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-websocket.ts +105 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/i18n.ts +32 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/api-client.ts +90 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/constants.ts +39 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/server-api.ts +78 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.test.ts +44 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.ts +44 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/middleware.ts +33 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.test.ts +72 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.ts +48 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/chat-store.ts +65 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/conversation-store.ts +76 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/index.ts +6 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/theme-store.ts +44 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/api.ts +27 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/auth.ts +52 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/chat.ts +81 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/conversation.ts +49 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/index.ts +10 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/tsconfig.json +28 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/vitest.config.ts +36 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/vitest.setup.ts +56 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{%- if cookiecutter.use_jwt %}
|
|
2
|
+
"""Security utilities for JWT authentication."""
|
|
3
|
+
|
|
4
|
+
from datetime import UTC, datetime, timedelta
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import bcrypt
|
|
8
|
+
import jwt
|
|
9
|
+
|
|
10
|
+
from app.core.config import settings
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def create_access_token(
|
|
14
|
+
subject: str | Any,
|
|
15
|
+
expires_delta: timedelta | None = None,
|
|
16
|
+
) -> str:
|
|
17
|
+
"""Create a JWT access token."""
|
|
18
|
+
if expires_delta:
|
|
19
|
+
expire = datetime.now(UTC) + expires_delta
|
|
20
|
+
else:
|
|
21
|
+
expire = datetime.now(UTC) + timedelta(
|
|
22
|
+
minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
to_encode = {"exp": expire, "sub": str(subject), "type": "access"}
|
|
26
|
+
return jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def create_refresh_token(
|
|
30
|
+
subject: str | Any,
|
|
31
|
+
expires_delta: timedelta | None = None,
|
|
32
|
+
) -> str:
|
|
33
|
+
"""Create a JWT refresh token."""
|
|
34
|
+
if expires_delta:
|
|
35
|
+
expire = datetime.now(UTC) + expires_delta
|
|
36
|
+
else:
|
|
37
|
+
expire = datetime.now(UTC) + timedelta(
|
|
38
|
+
minutes=settings.REFRESH_TOKEN_EXPIRE_MINUTES
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
to_encode = {"exp": expire, "sub": str(subject), "type": "refresh"}
|
|
42
|
+
return jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def verify_token(token: str) -> dict[str, Any] | None:
|
|
46
|
+
"""Verify a JWT token and return payload."""
|
|
47
|
+
try:
|
|
48
|
+
payload = jwt.decode(
|
|
49
|
+
token,
|
|
50
|
+
settings.SECRET_KEY,
|
|
51
|
+
algorithms=[settings.ALGORITHM],
|
|
52
|
+
)
|
|
53
|
+
return payload
|
|
54
|
+
except jwt.PyJWTError:
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
|
59
|
+
"""Verify a password against a hash."""
|
|
60
|
+
return bcrypt.checkpw(
|
|
61
|
+
plain_password.encode("utf-8"),
|
|
62
|
+
hashed_password.encode("utf-8"),
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def get_password_hash(password: str) -> str:
|
|
67
|
+
"""Hash a password."""
|
|
68
|
+
return bcrypt.hashpw(
|
|
69
|
+
password.encode("utf-8"),
|
|
70
|
+
bcrypt.gensalt(),
|
|
71
|
+
).decode("utf-8")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
{%- elif cookiecutter.use_api_key %}
|
|
75
|
+
"""Security utilities for API Key authentication."""
|
|
76
|
+
|
|
77
|
+
from fastapi import HTTPException, Security, status
|
|
78
|
+
from fastapi.security import APIKeyHeader
|
|
79
|
+
|
|
80
|
+
from app.core.config import settings
|
|
81
|
+
|
|
82
|
+
api_key_header = APIKeyHeader(name=settings.API_KEY_HEADER, auto_error=False)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
async def verify_api_key(api_key: str = Security(api_key_header)) -> str:
|
|
86
|
+
"""Verify API key from header."""
|
|
87
|
+
if api_key is None:
|
|
88
|
+
raise HTTPException(
|
|
89
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
90
|
+
detail="API Key header missing",
|
|
91
|
+
)
|
|
92
|
+
if api_key != settings.API_KEY:
|
|
93
|
+
raise HTTPException(
|
|
94
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
|
95
|
+
detail="Invalid API Key",
|
|
96
|
+
)
|
|
97
|
+
return api_key
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
{%- else %}
|
|
101
|
+
"""Security - not configured."""
|
|
102
|
+
{%- endif %}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{%- if cookiecutter.use_postgresql or cookiecutter.use_sqlite %}
|
|
2
|
+
"""SQLAlchemy base model."""
|
|
3
|
+
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
|
|
6
|
+
from sqlalchemy import DateTime, MetaData, func
|
|
7
|
+
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
|
8
|
+
|
|
9
|
+
# Naming convention for database constraints and indexes
|
|
10
|
+
# This ensures consistent naming across all migrations
|
|
11
|
+
NAMING_CONVENTION = {
|
|
12
|
+
"ix": "%(column_0_label)s_idx",
|
|
13
|
+
"uq": "%(table_name)s_%(column_0_name)s_key",
|
|
14
|
+
"ck": "%(table_name)s_%(constraint_name)s_check",
|
|
15
|
+
"fk": "%(table_name)s_%(column_0_name)s_fkey",
|
|
16
|
+
"pk": "%(table_name)s_pkey",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Base(DeclarativeBase):
|
|
21
|
+
"""Base class for all SQLAlchemy models."""
|
|
22
|
+
|
|
23
|
+
metadata = MetaData(naming_convention=NAMING_CONVENTION)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TimestampMixin:
|
|
27
|
+
"""Mixin for created_at and updated_at timestamps."""
|
|
28
|
+
|
|
29
|
+
created_at: Mapped[datetime] = mapped_column(
|
|
30
|
+
DateTime(timezone=True),
|
|
31
|
+
server_default=func.now(),
|
|
32
|
+
nullable=False,
|
|
33
|
+
)
|
|
34
|
+
updated_at: Mapped[datetime | None] = mapped_column(
|
|
35
|
+
DateTime(timezone=True),
|
|
36
|
+
onupdate=func.now(),
|
|
37
|
+
nullable=True,
|
|
38
|
+
)
|
|
39
|
+
{%- else %}
|
|
40
|
+
"""Database base - not using SQLAlchemy."""
|
|
41
|
+
{%- endif %}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Database models."""
|
|
2
|
+
{%- if cookiecutter.use_postgresql or cookiecutter.use_sqlite %}
|
|
3
|
+
# ruff: noqa: I001, RUF022 - Imports structured for Jinja2 template conditionals
|
|
4
|
+
{%- endif %}
|
|
5
|
+
{%- if cookiecutter.use_postgresql or cookiecutter.use_sqlite %}
|
|
6
|
+
{%- set models = [] %}
|
|
7
|
+
{%- if cookiecutter.use_jwt %}
|
|
8
|
+
{%- set _ = models.append("User") %}
|
|
9
|
+
from app.db.models.user import User
|
|
10
|
+
{%- endif %}
|
|
11
|
+
{%- if cookiecutter.enable_session_management and cookiecutter.use_jwt %}
|
|
12
|
+
{%- set _ = models.append("Session") %}
|
|
13
|
+
from app.db.models.session import Session
|
|
14
|
+
{%- endif %}
|
|
15
|
+
{%- if cookiecutter.include_example_crud %}
|
|
16
|
+
{%- set _ = models.append("Item") %}
|
|
17
|
+
from app.db.models.item import Item
|
|
18
|
+
{%- endif %}
|
|
19
|
+
{%- if cookiecutter.enable_conversation_persistence %}
|
|
20
|
+
{%- set _ = models.extend(["Conversation", "Message", "ToolCall"]) %}
|
|
21
|
+
from app.db.models.conversation import Conversation, Message, ToolCall
|
|
22
|
+
{%- endif %}
|
|
23
|
+
{%- if cookiecutter.enable_webhooks %}
|
|
24
|
+
{%- set _ = models.extend(["Webhook", "WebhookDelivery"]) %}
|
|
25
|
+
from app.db.models.webhook import Webhook, WebhookDelivery
|
|
26
|
+
{%- endif %}
|
|
27
|
+
{%- if models %}
|
|
28
|
+
|
|
29
|
+
__all__ = {{ models }}
|
|
30
|
+
{%- endif %}
|
|
31
|
+
{%- endif %}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
{%- if cookiecutter.enable_conversation_persistence and cookiecutter.use_postgresql %}
|
|
2
|
+
"""Conversation and message models for AI chat persistence."""
|
|
3
|
+
|
|
4
|
+
import uuid
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Literal
|
|
7
|
+
|
|
8
|
+
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text
|
|
9
|
+
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
|
10
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
11
|
+
|
|
12
|
+
from app.db.base import Base, TimestampMixin
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Conversation(Base, TimestampMixin):
|
|
16
|
+
"""Conversation model - groups messages in a chat session.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
id: Unique conversation identifier
|
|
20
|
+
user_id: Optional user who owns this conversation (if auth enabled)
|
|
21
|
+
title: Auto-generated or user-defined title
|
|
22
|
+
is_archived: Whether the conversation is archived
|
|
23
|
+
messages: List of messages in this conversation
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
__tablename__ = "conversations"
|
|
27
|
+
|
|
28
|
+
id: Mapped[uuid.UUID] = mapped_column(
|
|
29
|
+
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
|
30
|
+
)
|
|
31
|
+
{%- if cookiecutter.use_jwt %}
|
|
32
|
+
user_id: Mapped[uuid.UUID | None] = mapped_column(
|
|
33
|
+
UUID(as_uuid=True),
|
|
34
|
+
ForeignKey("users.id", ondelete="CASCADE"),
|
|
35
|
+
nullable=True,
|
|
36
|
+
index=True,
|
|
37
|
+
)
|
|
38
|
+
{%- endif %}
|
|
39
|
+
title: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
|
40
|
+
is_archived: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
|
41
|
+
|
|
42
|
+
# Relationships
|
|
43
|
+
messages: Mapped[list["Message"]] = relationship(
|
|
44
|
+
"Message",
|
|
45
|
+
back_populates="conversation",
|
|
46
|
+
cascade="all, delete-orphan",
|
|
47
|
+
order_by="Message.created_at",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def __repr__(self) -> str:
|
|
51
|
+
return f"<Conversation(id={self.id}, title={self.title})>"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class Message(Base, TimestampMixin):
|
|
55
|
+
"""Message model - individual message in a conversation.
|
|
56
|
+
|
|
57
|
+
Attributes:
|
|
58
|
+
id: Unique message identifier
|
|
59
|
+
conversation_id: The conversation this message belongs to
|
|
60
|
+
role: Message role (user, assistant, system)
|
|
61
|
+
content: Message text content
|
|
62
|
+
model_name: AI model used (for assistant messages)
|
|
63
|
+
tokens_used: Token count for this message
|
|
64
|
+
tool_calls: List of tool calls made in this message
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
__tablename__ = "messages"
|
|
68
|
+
|
|
69
|
+
id: Mapped[uuid.UUID] = mapped_column(
|
|
70
|
+
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
|
71
|
+
)
|
|
72
|
+
conversation_id: Mapped[uuid.UUID] = mapped_column(
|
|
73
|
+
UUID(as_uuid=True),
|
|
74
|
+
ForeignKey("conversations.id", ondelete="CASCADE"),
|
|
75
|
+
nullable=False,
|
|
76
|
+
index=True,
|
|
77
|
+
)
|
|
78
|
+
role: Mapped[str] = mapped_column(
|
|
79
|
+
String(20), nullable=False
|
|
80
|
+
) # user, assistant, system
|
|
81
|
+
content: Mapped[str] = mapped_column(Text, nullable=False)
|
|
82
|
+
model_name: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
|
83
|
+
tokens_used: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
84
|
+
|
|
85
|
+
# Relationships
|
|
86
|
+
conversation: Mapped["Conversation"] = relationship(
|
|
87
|
+
"Conversation", back_populates="messages"
|
|
88
|
+
)
|
|
89
|
+
tool_calls: Mapped[list["ToolCall"]] = relationship(
|
|
90
|
+
"ToolCall",
|
|
91
|
+
back_populates="message",
|
|
92
|
+
cascade="all, delete-orphan",
|
|
93
|
+
order_by="ToolCall.started_at",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
def __repr__(self) -> str:
|
|
97
|
+
return f"<Message(id={self.id}, role={self.role})>"
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class ToolCall(Base):
|
|
101
|
+
"""ToolCall model - record of a tool invocation.
|
|
102
|
+
|
|
103
|
+
Attributes:
|
|
104
|
+
id: Unique tool call identifier
|
|
105
|
+
message_id: The assistant message that triggered this call
|
|
106
|
+
tool_call_id: External ID from PydanticAI
|
|
107
|
+
tool_name: Name of the tool that was called
|
|
108
|
+
args: JSON arguments passed to the tool
|
|
109
|
+
result: Result returned by the tool
|
|
110
|
+
status: Current status (pending, running, completed, failed)
|
|
111
|
+
started_at: When the tool call started
|
|
112
|
+
completed_at: When the tool call completed
|
|
113
|
+
duration_ms: Execution time in milliseconds
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
__tablename__ = "tool_calls"
|
|
117
|
+
|
|
118
|
+
id: Mapped[uuid.UUID] = mapped_column(
|
|
119
|
+
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
|
120
|
+
)
|
|
121
|
+
message_id: Mapped[uuid.UUID] = mapped_column(
|
|
122
|
+
UUID(as_uuid=True),
|
|
123
|
+
ForeignKey("messages.id", ondelete="CASCADE"),
|
|
124
|
+
nullable=False,
|
|
125
|
+
index=True,
|
|
126
|
+
)
|
|
127
|
+
tool_call_id: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
128
|
+
tool_name: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
129
|
+
args: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict)
|
|
130
|
+
result: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
131
|
+
status: Mapped[str] = mapped_column(
|
|
132
|
+
String(20), nullable=False, default="pending"
|
|
133
|
+
) # pending, running, completed, failed
|
|
134
|
+
started_at: Mapped[datetime] = mapped_column(
|
|
135
|
+
DateTime(timezone=True), nullable=False
|
|
136
|
+
)
|
|
137
|
+
completed_at: Mapped[datetime | None] = mapped_column(
|
|
138
|
+
DateTime(timezone=True), nullable=True
|
|
139
|
+
)
|
|
140
|
+
duration_ms: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
141
|
+
|
|
142
|
+
# Relationships
|
|
143
|
+
message: Mapped["Message"] = relationship("Message", back_populates="tool_calls")
|
|
144
|
+
|
|
145
|
+
def __repr__(self) -> str:
|
|
146
|
+
return f"<ToolCall(id={self.id}, tool_name={self.tool_name}, status={self.status})>"
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
{%- elif cookiecutter.enable_conversation_persistence and cookiecutter.use_sqlite %}
|
|
150
|
+
"""Conversation and message models for AI chat persistence."""
|
|
151
|
+
|
|
152
|
+
import uuid
|
|
153
|
+
from datetime import datetime
|
|
154
|
+
|
|
155
|
+
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text
|
|
156
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
157
|
+
|
|
158
|
+
from app.db.base import Base, TimestampMixin
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class Conversation(Base, TimestampMixin):
|
|
162
|
+
"""Conversation model - groups messages in a chat session."""
|
|
163
|
+
|
|
164
|
+
__tablename__ = "conversations"
|
|
165
|
+
|
|
166
|
+
id: Mapped[str] = mapped_column(
|
|
167
|
+
String(36), primary_key=True, default=lambda: str(uuid.uuid4())
|
|
168
|
+
)
|
|
169
|
+
{%- if cookiecutter.use_jwt %}
|
|
170
|
+
user_id: Mapped[str | None] = mapped_column(
|
|
171
|
+
String(36),
|
|
172
|
+
ForeignKey("users.id", ondelete="CASCADE"),
|
|
173
|
+
nullable=True,
|
|
174
|
+
index=True,
|
|
175
|
+
)
|
|
176
|
+
{%- endif %}
|
|
177
|
+
title: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
|
178
|
+
is_archived: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
|
179
|
+
|
|
180
|
+
# Relationships
|
|
181
|
+
messages: Mapped[list["Message"]] = relationship(
|
|
182
|
+
"Message",
|
|
183
|
+
back_populates="conversation",
|
|
184
|
+
cascade="all, delete-orphan",
|
|
185
|
+
order_by="Message.created_at",
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
def __repr__(self) -> str:
|
|
189
|
+
return f"<Conversation(id={self.id}, title={self.title})>"
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class Message(Base, TimestampMixin):
|
|
193
|
+
"""Message model - individual message in a conversation."""
|
|
194
|
+
|
|
195
|
+
__tablename__ = "messages"
|
|
196
|
+
|
|
197
|
+
id: Mapped[str] = mapped_column(
|
|
198
|
+
String(36), primary_key=True, default=lambda: str(uuid.uuid4())
|
|
199
|
+
)
|
|
200
|
+
conversation_id: Mapped[str] = mapped_column(
|
|
201
|
+
String(36),
|
|
202
|
+
ForeignKey("conversations.id", ondelete="CASCADE"),
|
|
203
|
+
nullable=False,
|
|
204
|
+
index=True,
|
|
205
|
+
)
|
|
206
|
+
role: Mapped[str] = mapped_column(String(20), nullable=False)
|
|
207
|
+
content: Mapped[str] = mapped_column(Text, nullable=False)
|
|
208
|
+
model_name: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
|
209
|
+
tokens_used: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
210
|
+
|
|
211
|
+
# Relationships
|
|
212
|
+
conversation: Mapped["Conversation"] = relationship(
|
|
213
|
+
"Conversation", back_populates="messages"
|
|
214
|
+
)
|
|
215
|
+
tool_calls: Mapped[list["ToolCall"]] = relationship(
|
|
216
|
+
"ToolCall",
|
|
217
|
+
back_populates="message",
|
|
218
|
+
cascade="all, delete-orphan",
|
|
219
|
+
order_by="ToolCall.started_at",
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
def __repr__(self) -> str:
|
|
223
|
+
return f"<Message(id={self.id}, role={self.role})>"
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class ToolCall(Base):
|
|
227
|
+
"""ToolCall model - record of a tool invocation."""
|
|
228
|
+
|
|
229
|
+
__tablename__ = "tool_calls"
|
|
230
|
+
|
|
231
|
+
id: Mapped[str] = mapped_column(
|
|
232
|
+
String(36), primary_key=True, default=lambda: str(uuid.uuid4())
|
|
233
|
+
)
|
|
234
|
+
message_id: Mapped[str] = mapped_column(
|
|
235
|
+
String(36),
|
|
236
|
+
ForeignKey("messages.id", ondelete="CASCADE"),
|
|
237
|
+
nullable=False,
|
|
238
|
+
index=True,
|
|
239
|
+
)
|
|
240
|
+
tool_call_id: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
241
|
+
tool_name: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
242
|
+
args: Mapped[str] = mapped_column(Text, nullable=False, default="{}") # JSON as string
|
|
243
|
+
result: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
244
|
+
status: Mapped[str] = mapped_column(String(20), nullable=False, default="pending")
|
|
245
|
+
started_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
|
|
246
|
+
completed_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
|
247
|
+
duration_ms: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
248
|
+
|
|
249
|
+
# Relationships
|
|
250
|
+
message: Mapped["Message"] = relationship("Message", back_populates="tool_calls")
|
|
251
|
+
|
|
252
|
+
def __repr__(self) -> str:
|
|
253
|
+
return f"<ToolCall(id={self.id}, tool_name={self.tool_name})>"
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
{%- elif cookiecutter.enable_conversation_persistence and cookiecutter.use_mongodb %}
|
|
257
|
+
"""Conversation and message models for AI chat persistence (MongoDB)."""
|
|
258
|
+
|
|
259
|
+
from datetime import UTC, datetime
|
|
260
|
+
from typing import Literal, Optional
|
|
261
|
+
|
|
262
|
+
from beanie import Document, Link
|
|
263
|
+
from pydantic import Field
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class ToolCall(Document):
|
|
267
|
+
"""ToolCall document model - record of a tool invocation."""
|
|
268
|
+
|
|
269
|
+
message_id: str
|
|
270
|
+
tool_call_id: str
|
|
271
|
+
tool_name: str
|
|
272
|
+
args: dict = Field(default_factory=dict)
|
|
273
|
+
result: Optional[str] = None
|
|
274
|
+
status: Literal["pending", "running", "completed", "failed"] = "pending"
|
|
275
|
+
started_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
276
|
+
completed_at: Optional[datetime] = None
|
|
277
|
+
duration_ms: Optional[int] = None
|
|
278
|
+
|
|
279
|
+
class Settings:
|
|
280
|
+
name = "tool_calls"
|
|
281
|
+
indexes = ["message_id"]
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
class Message(Document):
|
|
285
|
+
"""Message document model - individual message in a conversation."""
|
|
286
|
+
|
|
287
|
+
conversation_id: str
|
|
288
|
+
role: Literal["user", "assistant", "system"]
|
|
289
|
+
content: str
|
|
290
|
+
model_name: Optional[str] = None
|
|
291
|
+
tokens_used: Optional[int] = None
|
|
292
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
293
|
+
|
|
294
|
+
class Settings:
|
|
295
|
+
name = "messages"
|
|
296
|
+
indexes = ["conversation_id"]
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
class Conversation(Document):
|
|
300
|
+
"""Conversation document model - groups messages in a chat session."""
|
|
301
|
+
|
|
302
|
+
{%- if cookiecutter.use_jwt %}
|
|
303
|
+
user_id: Optional[str] = None
|
|
304
|
+
{%- endif %}
|
|
305
|
+
title: Optional[str] = None
|
|
306
|
+
is_archived: bool = False
|
|
307
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
308
|
+
updated_at: Optional[datetime] = None
|
|
309
|
+
|
|
310
|
+
class Settings:
|
|
311
|
+
name = "conversations"
|
|
312
|
+
{%- if cookiecutter.use_jwt %}
|
|
313
|
+
indexes = ["user_id"]
|
|
314
|
+
{%- endif %}
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
{%- else %}
|
|
318
|
+
"""Conversation models - not configured."""
|
|
319
|
+
{%- endif %}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{%- if cookiecutter.include_example_crud and cookiecutter.use_postgresql %}
|
|
2
|
+
"""Item database model - example CRUD entity."""
|
|
3
|
+
|
|
4
|
+
import uuid
|
|
5
|
+
|
|
6
|
+
from sqlalchemy import String, Text
|
|
7
|
+
from sqlalchemy.dialects.postgresql import UUID
|
|
8
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
9
|
+
|
|
10
|
+
from app.db.base import Base, TimestampMixin
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Item(Base, TimestampMixin):
|
|
14
|
+
"""Item model - example entity for demonstrating CRUD operations.
|
|
15
|
+
|
|
16
|
+
This is a simple example model. You can use it as a template
|
|
17
|
+
for creating your own models or remove it if not needed.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
__tablename__ = "items"
|
|
21
|
+
|
|
22
|
+
id: Mapped[uuid.UUID] = mapped_column(
|
|
23
|
+
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
|
24
|
+
)
|
|
25
|
+
title: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
|
|
26
|
+
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
27
|
+
is_active: Mapped[bool] = mapped_column(default=True, nullable=False)
|
|
28
|
+
|
|
29
|
+
def __repr__(self) -> str:
|
|
30
|
+
return f"<Item(id={self.id}, title={self.title})>"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
{%- elif cookiecutter.include_example_crud and cookiecutter.use_sqlite %}
|
|
34
|
+
"""Item database model - example CRUD entity."""
|
|
35
|
+
|
|
36
|
+
import uuid
|
|
37
|
+
|
|
38
|
+
from sqlalchemy import Boolean, String, Text
|
|
39
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
40
|
+
|
|
41
|
+
from app.db.base import Base, TimestampMixin
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class Item(Base, TimestampMixin):
|
|
45
|
+
"""Item model - example entity for demonstrating CRUD operations.
|
|
46
|
+
|
|
47
|
+
This is a simple example model. You can use it as a template
|
|
48
|
+
for creating your own models or remove it if not needed.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
__tablename__ = "items"
|
|
52
|
+
|
|
53
|
+
id: Mapped[str] = mapped_column(
|
|
54
|
+
String(36), primary_key=True, default=lambda: str(uuid.uuid4())
|
|
55
|
+
)
|
|
56
|
+
title: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
|
|
57
|
+
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
58
|
+
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
|
59
|
+
|
|
60
|
+
def __repr__(self) -> str:
|
|
61
|
+
return f"<Item(id={self.id}, title={self.title})>"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
{%- elif cookiecutter.include_example_crud and cookiecutter.use_mongodb %}
|
|
65
|
+
"""Item document model for MongoDB - example CRUD entity."""
|
|
66
|
+
|
|
67
|
+
from datetime import UTC, datetime
|
|
68
|
+
from typing import Optional
|
|
69
|
+
|
|
70
|
+
from beanie import Document
|
|
71
|
+
from pydantic import Field
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class Item(Document):
|
|
75
|
+
"""Item document model - example entity for demonstrating CRUD operations.
|
|
76
|
+
|
|
77
|
+
This is a simple example model. You can use it as a template
|
|
78
|
+
for creating your own models or remove it if not needed.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
title: str = Field(max_length=255)
|
|
82
|
+
description: Optional[str] = None
|
|
83
|
+
is_active: bool = True
|
|
84
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
85
|
+
updated_at: Optional[datetime] = None
|
|
86
|
+
|
|
87
|
+
class Settings:
|
|
88
|
+
name = "items"
|
|
89
|
+
indexes = [
|
|
90
|
+
"title",
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
{%- else %}
|
|
95
|
+
"""Item model - not configured."""
|
|
96
|
+
{%- endif %}
|