fastapi-fullstack 0.1.10__tar.gz → 0.1.11__tar.gz
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.10 → fastapi_fullstack-0.1.11}/PKG-INFO +1 -1
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/fastapi_gen/config.py +8 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/fastapi_gen/prompts.py +3 -2
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/pyproject.toml +1 -1
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/VARIABLES.md +4 -2
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/cookiecutter.json +1 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/CLAUDE.md +1 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/agents/__init__.py +10 -0
- fastapi_fullstack-0.1.11/template/{{cookiecutter.project_slug}}/backend/app/agents/langgraph_assistant.py +371 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/agent.py +442 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/pyproject.toml +11 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/.gitignore +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/LICENSE +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/README.md +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/fastapi_gen/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/fastapi_gen/cli.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/fastapi_gen/generator.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/hooks/post_gen_project.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/.env.prod.example +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/.github/workflows/ci.yml +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/.gitignore +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/.gitlab-ci.yml +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/AGENTS.md +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/Makefile +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/README.md +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/.dockerignore +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/.env.example +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/.pre-commit-config.yaml +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/Dockerfile +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/alembic/env.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/alembic/script.py.mako +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/alembic/versions/.gitkeep +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/alembic.ini +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/admin.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/agents/assistant.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/agents/langchain_assistant.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/agents/prompts.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/datetime_tool.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/api/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/api/deps.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/api/exception_handlers.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/api/router.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/auth.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/conversations.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/health.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/items.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/oauth.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/sessions.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/users.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/webhooks.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/ws.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/api/versioning.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/clients/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/clients/redis.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/commands/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/commands/cleanup.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/commands/example.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/commands/seed.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/core/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/core/cache.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/core/config.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/core/csrf.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/core/exceptions.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/core/logfire_setup.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/core/middleware.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/core/oauth.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/core/rate_limit.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/core/sanitize.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/core/security.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/db/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/db/base.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/db/models/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/db/models/conversation.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/db/models/item.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/db/models/session.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/db/models/user.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/db/models/webhook.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/db/session.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/main.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/pipelines/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/pipelines/base.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/repositories/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/repositories/base.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/repositories/conversation.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/repositories/item.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/repositories/session.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/repositories/user.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/repositories/webhook.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/schemas/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/schemas/base.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/schemas/conversation.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/schemas/item.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/schemas/session.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/schemas/token.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/schemas/user.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/schemas/webhook.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/services/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/services/conversation.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/services/item.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/services/session.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/services/user.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/services/webhook.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/worker/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/worker/arq_app.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/worker/celery_app.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/worker/taskiq_app.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/examples.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/schedules.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/taskiq_examples.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/cli/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/cli/commands.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/scripts/.gitkeep +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/api/__init__.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/api/test_auth.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/api/test_exceptions.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/api/test_health.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/api/test_items.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/api/test_metrics.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/api/test_users.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/conftest.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/test_admin.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/test_agents.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/test_clients.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/test_commands.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/test_core.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/test_pipelines.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/test_repositories.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/test_security.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/test_services.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/backend/tests/test_worker.py +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/docker-compose.dev.yml +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/docker-compose.frontend.yml +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/docker-compose.prod.yml +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/docker-compose.yml +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/docs/adding_features.md +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/docs/architecture.md +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/docs/patterns.md +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/docs/testing.md +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/.dockerignore +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/.env.example +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/.gitignore +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/.prettierignore +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/.prettierrc +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/Dockerfile +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/README.md +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.setup.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.spec.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/e2e/chat.spec.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/e2e/home.spec.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/instrumentation.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/messages/en.json +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/messages/pl.json +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/next.config.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/package.json +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/playwright.config.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/postcss.config.mjs +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(auth)/layout.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(auth)/login/page.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(auth)/register/page.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/chat/page.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/dashboard/page.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/layout.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/profile/page.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/auth/callback/page.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/layout.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/page.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/login/route.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/logout/route.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/me/route.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/oauth-callback/route.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/refresh/route.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/register/route.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/messages/route.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/route.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/route.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/health/route.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/globals.css +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/layout.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/app/providers.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/index.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/login-form.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/register-form.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-container.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-input.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/conversation-sidebar.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/copy-button.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/index.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/local-conversation-sidebar.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/markdown-content.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-item.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-list.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/tool-call-card.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/google-icon.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/index.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/language-switcher.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/header.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/index.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/sidebar.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/index.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-provider.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-toggle.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/badge.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.test.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/card.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/index.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/input.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/label.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/sheet.tsx +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/hooks/index.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-auth.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-chat.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-conversations.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-local-chat.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-websocket.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/i18n.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/lib/api-client.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/lib/constants.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/lib/server-api.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.test.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/middleware.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.test.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/stores/chat-sidebar-store.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/stores/chat-store.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/stores/conversation-store.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/stores/index.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/stores/local-chat-store.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/stores/sidebar-store.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/stores/theme-store.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/types/api.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/types/auth.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/types/chat.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/types/conversation.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/src/types/index.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/tsconfig.json +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/vitest.config.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/frontend/vitest.setup.ts +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/kubernetes/configmap.yaml +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/kubernetes/deployment.yaml +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/kubernetes/ingress.yaml +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/kubernetes/kustomization.yaml +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/kubernetes/namespace.yaml +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/kubernetes/secret.yaml +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/kubernetes/service.yaml +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/nginx/nginx.conf +0 -0
- {fastapi_fullstack-0.1.10 → fastapi_fullstack-0.1.11}/template/{{cookiecutter.project_slug}}/nginx/ssl/.gitkeep +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-fullstack
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.11
|
|
4
4
|
Summary: Full-stack FastAPI + Next.js template generator with PydanticAI/LangChain agents, WebSocket streaming, 20+ enterprise integrations, and Logfire/LangSmith observability. Ship AI apps fast. CLI tool to generate production-ready FastAPI + Next.js projects with AI agents, auth, and observability.
|
|
5
5
|
Project-URL: Homepage, https://github.com/vstorm-co/full-stack-fastapi-nextjs-llm-template
|
|
6
6
|
Project-URL: Documentation, https://github.com/vstorm-co/full-stack-fastapi-nextjs-llm-template#readme
|
|
@@ -89,6 +89,7 @@ class AIFrameworkType(str, Enum):
|
|
|
89
89
|
|
|
90
90
|
PYDANTIC_AI = "pydantic_ai"
|
|
91
91
|
LANGCHAIN = "langchain"
|
|
92
|
+
LANGGRAPH = "langgraph"
|
|
92
93
|
|
|
93
94
|
|
|
94
95
|
class LLMProviderType(str, Enum):
|
|
@@ -263,6 +264,12 @@ class ProjectConfig(BaseModel):
|
|
|
263
264
|
and self.llm_provider == LLMProviderType.OPENROUTER
|
|
264
265
|
):
|
|
265
266
|
raise ValueError("OpenRouter is not supported with LangChain")
|
|
267
|
+
if (
|
|
268
|
+
self.enable_ai_agent
|
|
269
|
+
and self.ai_framework == AIFrameworkType.LANGGRAPH
|
|
270
|
+
and self.llm_provider == LLMProviderType.OPENROUTER
|
|
271
|
+
):
|
|
272
|
+
raise ValueError("OpenRouter is not supported with LangGraph")
|
|
266
273
|
if (
|
|
267
274
|
self.enable_rate_limiting
|
|
268
275
|
and self.rate_limit_storage == RateLimitStorageType.REDIS
|
|
@@ -345,6 +352,7 @@ class ProjectConfig(BaseModel):
|
|
|
345
352
|
"ai_framework": self.ai_framework.value,
|
|
346
353
|
"use_pydantic_ai": self.ai_framework == AIFrameworkType.PYDANTIC_AI,
|
|
347
354
|
"use_langchain": self.ai_framework == AIFrameworkType.LANGCHAIN,
|
|
355
|
+
"use_langgraph": self.ai_framework == AIFrameworkType.LANGGRAPH,
|
|
348
356
|
"llm_provider": self.llm_provider.value,
|
|
349
357
|
"use_openai": self.llm_provider == LLMProviderType.OPENAI,
|
|
350
358
|
"use_anthropic": self.llm_provider == LLMProviderType.ANTHROPIC,
|
|
@@ -530,6 +530,7 @@ def prompt_ai_framework() -> AIFrameworkType:
|
|
|
530
530
|
choices = [
|
|
531
531
|
questionary.Choice("PydanticAI (recommended)", value=AIFrameworkType.PYDANTIC_AI),
|
|
532
532
|
questionary.Choice("LangChain", value=AIFrameworkType.LANGCHAIN),
|
|
533
|
+
questionary.Choice("LangGraph (ReAct agent)", value=AIFrameworkType.LANGGRAPH),
|
|
533
534
|
]
|
|
534
535
|
|
|
535
536
|
return cast(
|
|
@@ -549,7 +550,7 @@ def prompt_llm_provider(ai_framework: AIFrameworkType) -> LLMProviderType:
|
|
|
549
550
|
|
|
550
551
|
Args:
|
|
551
552
|
ai_framework: The selected AI framework. OpenRouter is only
|
|
552
|
-
available for PydanticAI.
|
|
553
|
+
available for PydanticAI (not LangChain or LangGraph).
|
|
553
554
|
"""
|
|
554
555
|
console.print()
|
|
555
556
|
console.print("[bold cyan]LLM Provider[/]")
|
|
@@ -560,7 +561,7 @@ def prompt_llm_provider(ai_framework: AIFrameworkType) -> LLMProviderType:
|
|
|
560
561
|
questionary.Choice("Anthropic (claude-sonnet-4-5)", value=LLMProviderType.ANTHROPIC),
|
|
561
562
|
]
|
|
562
563
|
|
|
563
|
-
# OpenRouter only available for PydanticAI
|
|
564
|
+
# OpenRouter only available for PydanticAI (not LangChain or LangGraph)
|
|
564
565
|
if ai_framework == AIFrameworkType.PYDANTIC_AI:
|
|
565
566
|
choices.append(
|
|
566
567
|
questionary.Choice("OpenRouter (multi-provider)", value=LLMProviderType.OPENROUTER)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "fastapi-fullstack"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.11"
|
|
4
4
|
description = "Full-stack FastAPI + Next.js template generator with PydanticAI/LangChain agents, WebSocket streaming, 20+ enterprise integrations, and Logfire/LangSmith observability. Ship AI apps fast. CLI tool to generate production-ready FastAPI + Next.js projects with AI agents, auth, and observability."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -187,9 +187,10 @@ These variables are set automatically by the generator.
|
|
|
187
187
|
| Variable | Type | Default | Description | Dependencies |
|
|
188
188
|
|----------|------|---------|-------------|--------------|
|
|
189
189
|
| `enable_ai_agent` | bool | `false` | Enable AI agent functionality | - |
|
|
190
|
-
| `ai_framework` | enum | `"pydantic_ai"` | AI framework. Values: `pydantic_ai`, `langchain` | Requires `enable_ai_agent` |
|
|
190
|
+
| `ai_framework` | enum | `"pydantic_ai"` | AI framework. Values: `pydantic_ai`, `langchain`, `langgraph` | Requires `enable_ai_agent` |
|
|
191
191
|
| `use_pydantic_ai` | bool | `true` | PydanticAI is selected | Computed from `ai_framework` |
|
|
192
192
|
| `use_langchain` | bool | `false` | LangChain is selected | Computed from `ai_framework` |
|
|
193
|
+
| `use_langgraph` | bool | `false` | LangGraph (ReAct agent) is selected | Computed from `ai_framework` |
|
|
193
194
|
| `llm_provider` | enum | `"openai"` | LLM provider. Values: `openai`, `anthropic`, `openrouter` | Requires `enable_ai_agent` |
|
|
194
195
|
| `use_openai` | bool | `true` | OpenAI is selected | Computed from `llm_provider` |
|
|
195
196
|
| `use_anthropic` | bool | `false` | Anthropic is selected | Computed from `llm_provider` |
|
|
@@ -198,7 +199,8 @@ These variables are set automatically by the generator.
|
|
|
198
199
|
|
|
199
200
|
**Notes:**
|
|
200
201
|
- PydanticAI uses `iter()` for full event streaming over WebSocket
|
|
201
|
-
-
|
|
202
|
+
- LangGraph implements a ReAct (Reasoning + Acting) agent pattern with graph-based architecture
|
|
203
|
+
- OpenRouter with LangChain or LangGraph is not supported
|
|
202
204
|
|
|
203
205
|
---
|
|
204
206
|
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
{%- if cookiecutter.enable_redis %}, Redis{%- endif %}
|
|
13
13
|
{%- if cookiecutter.enable_ai_agent and cookiecutter.use_pydantic_ai %}, PydanticAI{%- endif %}
|
|
14
14
|
{%- if cookiecutter.enable_ai_agent and cookiecutter.use_langchain %}, LangChain{%- endif %}
|
|
15
|
+
{%- if cookiecutter.enable_ai_agent and cookiecutter.use_langgraph %}, LangGraph{%- endif %}
|
|
15
16
|
{%- if cookiecutter.use_celery %}, Celery{%- endif %}
|
|
16
17
|
{%- if cookiecutter.use_taskiq %}, Taskiq{%- endif %}
|
|
17
18
|
{%- if cookiecutter.use_frontend %}, Next.js 15{%- endif %}
|
|
@@ -18,6 +18,16 @@ Tools are defined in the tools/ subdirectory.
|
|
|
18
18
|
from app.agents.langchain_assistant import AgentContext, AgentState, LangChainAssistant
|
|
19
19
|
|
|
20
20
|
__all__ = ["LangChainAssistant", "AgentContext", "AgentState"]
|
|
21
|
+
{%- elif cookiecutter.enable_ai_agent and cookiecutter.use_langgraph %}
|
|
22
|
+
"""AI Agents module using LangGraph.
|
|
23
|
+
|
|
24
|
+
This module contains a ReAct agent built with LangGraph.
|
|
25
|
+
Tools are defined in the tools/ subdirectory.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from app.agents.langgraph_assistant import AgentContext, AgentState, LangGraphAssistant
|
|
29
|
+
|
|
30
|
+
__all__ = ["LangGraphAssistant", "AgentContext", "AgentState"]
|
|
21
31
|
{%- else %}
|
|
22
32
|
"""AI Agents - not configured."""
|
|
23
33
|
{%- endif %}
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
{%- if cookiecutter.enable_ai_agent and cookiecutter.use_langgraph %}
|
|
2
|
+
"""LangGraph ReAct Agent implementation.
|
|
3
|
+
|
|
4
|
+
A simple ReAct (Reasoning + Acting) agent built with LangGraph.
|
|
5
|
+
Uses a graph-based architecture with conditional edges for tool execution.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Annotated, Any, Literal, TypedDict
|
|
10
|
+
|
|
11
|
+
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, SystemMessage, ToolMessage
|
|
12
|
+
from langchain_core.tools import tool
|
|
13
|
+
from langgraph.checkpoint.memory import MemorySaver
|
|
14
|
+
from langgraph.graph import END, START, StateGraph
|
|
15
|
+
from langgraph.graph.message import add_messages
|
|
16
|
+
{%- if cookiecutter.use_openai %}
|
|
17
|
+
from langchain_openai import ChatOpenAI
|
|
18
|
+
{%- endif %}
|
|
19
|
+
{%- if cookiecutter.use_anthropic %}
|
|
20
|
+
from langchain_anthropic import ChatAnthropic
|
|
21
|
+
{%- endif %}
|
|
22
|
+
|
|
23
|
+
from app.agents.prompts import DEFAULT_SYSTEM_PROMPT
|
|
24
|
+
from app.agents.tools import get_current_datetime
|
|
25
|
+
from app.core.config import settings
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AgentContext(TypedDict, total=False):
|
|
31
|
+
"""Runtime context for the agent.
|
|
32
|
+
|
|
33
|
+
Passed via config parameter to the graph.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
user_id: str | None
|
|
37
|
+
user_name: str | None
|
|
38
|
+
metadata: dict[str, Any]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class AgentState(TypedDict):
|
|
42
|
+
"""State for the LangGraph agent.
|
|
43
|
+
|
|
44
|
+
This is what flows through the agent graph.
|
|
45
|
+
The messages field uses add_messages reducer to properly
|
|
46
|
+
append new messages to the conversation history.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
messages: Annotated[list[BaseMessage], add_messages]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@tool
|
|
53
|
+
def current_datetime() -> str:
|
|
54
|
+
"""Get the current date and time.
|
|
55
|
+
|
|
56
|
+
Use this tool when you need to know the current date or time.
|
|
57
|
+
"""
|
|
58
|
+
return get_current_datetime()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# List of all available tools
|
|
62
|
+
ALL_TOOLS = [current_datetime]
|
|
63
|
+
|
|
64
|
+
# Create a dictionary for quick tool lookup by name
|
|
65
|
+
TOOLS_BY_NAME = {t.name: t for t in ALL_TOOLS}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class LangGraphAssistant:
|
|
69
|
+
"""ReAct agent wrapper using LangGraph.
|
|
70
|
+
|
|
71
|
+
Implements a graph-based agent with:
|
|
72
|
+
- An agent node that processes messages and decides actions
|
|
73
|
+
- A tools node that executes tool calls
|
|
74
|
+
- Conditional edges that loop back for tool execution or end
|
|
75
|
+
|
|
76
|
+
The ReAct pattern:
|
|
77
|
+
1. Agent receives input and reasons about it
|
|
78
|
+
2. If tool calls are needed, execute them
|
|
79
|
+
3. Tool results are added to messages
|
|
80
|
+
4. Agent reasons again with new information
|
|
81
|
+
5. Repeat until agent provides final response
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def __init__(
|
|
85
|
+
self,
|
|
86
|
+
model_name: str | None = None,
|
|
87
|
+
temperature: float | None = None,
|
|
88
|
+
system_prompt: str | None = None,
|
|
89
|
+
):
|
|
90
|
+
self.model_name = model_name or settings.AI_MODEL
|
|
91
|
+
self.temperature = temperature or settings.AI_TEMPERATURE
|
|
92
|
+
self.system_prompt = system_prompt or DEFAULT_SYSTEM_PROMPT
|
|
93
|
+
self._graph = None
|
|
94
|
+
self._checkpointer = MemorySaver()
|
|
95
|
+
|
|
96
|
+
def _create_model(self):
|
|
97
|
+
"""Create the LLM model with tools bound."""
|
|
98
|
+
{%- if cookiecutter.use_openai %}
|
|
99
|
+
model = ChatOpenAI(
|
|
100
|
+
model=self.model_name,
|
|
101
|
+
temperature=self.temperature,
|
|
102
|
+
api_key=settings.OPENAI_API_KEY,
|
|
103
|
+
streaming=True,
|
|
104
|
+
)
|
|
105
|
+
{%- endif %}
|
|
106
|
+
{%- if cookiecutter.use_anthropic %}
|
|
107
|
+
model = ChatAnthropic(
|
|
108
|
+
model=self.model_name,
|
|
109
|
+
temperature=self.temperature,
|
|
110
|
+
api_key=settings.ANTHROPIC_API_KEY,
|
|
111
|
+
streaming=True,
|
|
112
|
+
)
|
|
113
|
+
{%- endif %}
|
|
114
|
+
|
|
115
|
+
return model.bind_tools(ALL_TOOLS)
|
|
116
|
+
|
|
117
|
+
def _agent_node(self, state: AgentState) -> dict[str, list[BaseMessage]]:
|
|
118
|
+
"""Agent node that processes messages and decides whether to call tools.
|
|
119
|
+
|
|
120
|
+
This is the main reasoning node in the ReAct pattern.
|
|
121
|
+
"""
|
|
122
|
+
model = self._create_model()
|
|
123
|
+
|
|
124
|
+
# Prepend system message to the conversation
|
|
125
|
+
messages = [SystemMessage(content=self.system_prompt), *state["messages"]]
|
|
126
|
+
|
|
127
|
+
response = model.invoke(messages)
|
|
128
|
+
|
|
129
|
+
logger.info(
|
|
130
|
+
f"Agent processed message - Tool calls: {len(response.tool_calls) if hasattr(response, 'tool_calls') else 0}"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
return {"messages": [response]}
|
|
134
|
+
|
|
135
|
+
def _tools_node(self, state: AgentState) -> dict[str, list[ToolMessage]]:
|
|
136
|
+
"""Tools node that executes tool calls from the agent.
|
|
137
|
+
|
|
138
|
+
Processes each tool call and returns results as ToolMessages.
|
|
139
|
+
"""
|
|
140
|
+
messages = state["messages"]
|
|
141
|
+
last_message = messages[-1]
|
|
142
|
+
|
|
143
|
+
tool_results = []
|
|
144
|
+
|
|
145
|
+
if hasattr(last_message, "tool_calls") and last_message.tool_calls:
|
|
146
|
+
for tool_call in last_message.tool_calls:
|
|
147
|
+
tool_name = tool_call["name"]
|
|
148
|
+
tool_args = tool_call["args"]
|
|
149
|
+
tool_id = tool_call["id"]
|
|
150
|
+
|
|
151
|
+
logger.info(f"Executing tool: {tool_name} with args: {tool_args}")
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
tool_fn = TOOLS_BY_NAME.get(tool_name)
|
|
155
|
+
if tool_fn:
|
|
156
|
+
result = tool_fn.invoke(tool_args)
|
|
157
|
+
tool_results.append(
|
|
158
|
+
ToolMessage(
|
|
159
|
+
content=str(result),
|
|
160
|
+
tool_call_id=tool_id,
|
|
161
|
+
name=tool_name,
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
logger.info(f"Tool {tool_name} completed successfully")
|
|
165
|
+
else:
|
|
166
|
+
error_msg = f"Unknown tool: {tool_name}"
|
|
167
|
+
logger.error(error_msg)
|
|
168
|
+
tool_results.append(
|
|
169
|
+
ToolMessage(
|
|
170
|
+
content=error_msg,
|
|
171
|
+
tool_call_id=tool_id,
|
|
172
|
+
name=tool_name,
|
|
173
|
+
)
|
|
174
|
+
)
|
|
175
|
+
except Exception as e:
|
|
176
|
+
error_msg = f"Error executing {tool_name}: {str(e)}"
|
|
177
|
+
logger.error(error_msg, exc_info=True)
|
|
178
|
+
tool_results.append(
|
|
179
|
+
ToolMessage(
|
|
180
|
+
content=error_msg,
|
|
181
|
+
tool_call_id=tool_id,
|
|
182
|
+
name=tool_name,
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
return {"messages": tool_results}
|
|
187
|
+
|
|
188
|
+
def _should_continue(self, state: AgentState) -> Literal["tools", "__end__"]:
|
|
189
|
+
"""Conditional edge that decides whether to continue to tools or end.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
- "tools" if the agent made tool calls (needs to execute tools)
|
|
193
|
+
- "__end__" if the agent provided a final response (no tool calls)
|
|
194
|
+
"""
|
|
195
|
+
messages = state["messages"]
|
|
196
|
+
last_message = messages[-1]
|
|
197
|
+
|
|
198
|
+
if hasattr(last_message, "tool_calls") and last_message.tool_calls:
|
|
199
|
+
logger.info(f"Continuing to tools - {len(last_message.tool_calls)} tool(s) to execute")
|
|
200
|
+
return "tools"
|
|
201
|
+
|
|
202
|
+
logger.info("No tool calls - ending conversation")
|
|
203
|
+
return "__end__"
|
|
204
|
+
|
|
205
|
+
def _build_graph(self) -> StateGraph:
|
|
206
|
+
"""Build and compile the LangGraph state graph."""
|
|
207
|
+
workflow = StateGraph(AgentState)
|
|
208
|
+
|
|
209
|
+
# Add nodes
|
|
210
|
+
workflow.add_node("agent", self._agent_node)
|
|
211
|
+
workflow.add_node("tools", self._tools_node)
|
|
212
|
+
|
|
213
|
+
# Add edges
|
|
214
|
+
workflow.add_edge(START, "agent")
|
|
215
|
+
workflow.add_conditional_edges(
|
|
216
|
+
"agent",
|
|
217
|
+
self._should_continue,
|
|
218
|
+
{"tools": "tools", "__end__": END},
|
|
219
|
+
)
|
|
220
|
+
workflow.add_edge("tools", "agent")
|
|
221
|
+
|
|
222
|
+
return workflow.compile(checkpointer=self._checkpointer)
|
|
223
|
+
|
|
224
|
+
@property
|
|
225
|
+
def graph(self):
|
|
226
|
+
"""Get or create the compiled graph instance."""
|
|
227
|
+
if self._graph is None:
|
|
228
|
+
self._graph = self._build_graph()
|
|
229
|
+
return self._graph
|
|
230
|
+
|
|
231
|
+
@staticmethod
|
|
232
|
+
def _convert_history(
|
|
233
|
+
history: list[dict[str, str]] | None,
|
|
234
|
+
) -> list[HumanMessage | AIMessage | SystemMessage]:
|
|
235
|
+
"""Convert conversation history to LangChain message format."""
|
|
236
|
+
messages: list[HumanMessage | AIMessage | SystemMessage] = []
|
|
237
|
+
|
|
238
|
+
for msg in history or []:
|
|
239
|
+
if msg["role"] == "user":
|
|
240
|
+
messages.append(HumanMessage(content=msg["content"]))
|
|
241
|
+
elif msg["role"] == "assistant":
|
|
242
|
+
messages.append(AIMessage(content=msg["content"]))
|
|
243
|
+
elif msg["role"] == "system":
|
|
244
|
+
messages.append(SystemMessage(content=msg["content"]))
|
|
245
|
+
|
|
246
|
+
return messages
|
|
247
|
+
|
|
248
|
+
async def run(
|
|
249
|
+
self,
|
|
250
|
+
user_input: str,
|
|
251
|
+
history: list[dict[str, str]] | None = None,
|
|
252
|
+
context: AgentContext | None = None,
|
|
253
|
+
thread_id: str = "default",
|
|
254
|
+
) -> tuple[str, list[Any], AgentContext]:
|
|
255
|
+
"""Run agent and return the output along with tool call events.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
user_input: User's message.
|
|
259
|
+
history: Conversation history as list of {"role": "...", "content": "..."}.
|
|
260
|
+
context: Optional runtime context with user info.
|
|
261
|
+
thread_id: Thread ID for conversation continuity.
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Tuple of (output_text, tool_events, context).
|
|
265
|
+
"""
|
|
266
|
+
messages = self._convert_history(history)
|
|
267
|
+
messages.append(HumanMessage(content=user_input))
|
|
268
|
+
|
|
269
|
+
agent_context: AgentContext = context if context is not None else {}
|
|
270
|
+
|
|
271
|
+
logger.info(f"Running agent with user input: {user_input[:100]}...")
|
|
272
|
+
|
|
273
|
+
config = {
|
|
274
|
+
"configurable": {
|
|
275
|
+
"thread_id": thread_id,
|
|
276
|
+
**agent_context,
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
result = await self.graph.ainvoke({"messages": messages}, config=config)
|
|
281
|
+
|
|
282
|
+
# Extract the final response and tool events
|
|
283
|
+
output = ""
|
|
284
|
+
tool_events: list[Any] = []
|
|
285
|
+
|
|
286
|
+
for message in result.get("messages", []):
|
|
287
|
+
if isinstance(message, AIMessage):
|
|
288
|
+
if message.content:
|
|
289
|
+
output = message.content if isinstance(message.content, str) else str(message.content)
|
|
290
|
+
if hasattr(message, "tool_calls") and message.tool_calls:
|
|
291
|
+
tool_events.extend(message.tool_calls)
|
|
292
|
+
|
|
293
|
+
logger.info(f"Agent run complete. Output length: {len(output)} chars")
|
|
294
|
+
|
|
295
|
+
return output, tool_events, agent_context
|
|
296
|
+
|
|
297
|
+
async def stream(
|
|
298
|
+
self,
|
|
299
|
+
user_input: str,
|
|
300
|
+
history: list[dict[str, str]] | None = None,
|
|
301
|
+
context: AgentContext | None = None,
|
|
302
|
+
thread_id: str = "default",
|
|
303
|
+
):
|
|
304
|
+
"""Stream agent execution with message and state update streaming.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
user_input: User's message.
|
|
308
|
+
history: Conversation history.
|
|
309
|
+
context: Optional runtime context.
|
|
310
|
+
thread_id: Thread ID for conversation continuity.
|
|
311
|
+
|
|
312
|
+
Yields:
|
|
313
|
+
Tuples of (stream_mode, data) for streaming responses.
|
|
314
|
+
- stream_mode="messages": (chunk, metadata) for LLM tokens
|
|
315
|
+
- stream_mode="updates": state updates after each node
|
|
316
|
+
"""
|
|
317
|
+
messages = self._convert_history(history)
|
|
318
|
+
messages.append(HumanMessage(content=user_input))
|
|
319
|
+
|
|
320
|
+
agent_context: AgentContext = context if context is not None else {}
|
|
321
|
+
|
|
322
|
+
config = {
|
|
323
|
+
"configurable": {
|
|
324
|
+
"thread_id": thread_id,
|
|
325
|
+
**agent_context,
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
logger.info(f"Starting stream for user input: {user_input[:100]}...")
|
|
330
|
+
|
|
331
|
+
async for stream_mode, data in self.graph.astream(
|
|
332
|
+
{"messages": messages},
|
|
333
|
+
config=config,
|
|
334
|
+
stream_mode=["messages", "updates"],
|
|
335
|
+
):
|
|
336
|
+
yield stream_mode, data
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def get_agent() -> LangGraphAssistant:
|
|
340
|
+
"""Factory function to create a LangGraphAssistant.
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
Configured LangGraphAssistant instance.
|
|
344
|
+
"""
|
|
345
|
+
return LangGraphAssistant()
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
async def run_agent(
|
|
349
|
+
user_input: str,
|
|
350
|
+
history: list[dict[str, str]],
|
|
351
|
+
context: AgentContext | None = None,
|
|
352
|
+
thread_id: str = "default",
|
|
353
|
+
) -> tuple[str, list[Any], AgentContext]:
|
|
354
|
+
"""Run agent and return the output along with tool call events.
|
|
355
|
+
|
|
356
|
+
This is a convenience function for backwards compatibility.
|
|
357
|
+
|
|
358
|
+
Args:
|
|
359
|
+
user_input: User's message.
|
|
360
|
+
history: Conversation history.
|
|
361
|
+
context: Optional runtime context.
|
|
362
|
+
thread_id: Thread ID for conversation continuity.
|
|
363
|
+
|
|
364
|
+
Returns:
|
|
365
|
+
Tuple of (output_text, tool_events, context).
|
|
366
|
+
"""
|
|
367
|
+
agent = get_agent()
|
|
368
|
+
return await agent.run(user_input, history, context, thread_id)
|
|
369
|
+
{%- else %}
|
|
370
|
+
"""LangGraph Assistant agent - not configured."""
|
|
371
|
+
{%- endif %}
|