fastapi-startkit 0.50.0__tar.gz → 0.52.0__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_startkit-0.50.0 → fastapi_startkit-0.52.0}/PKG-INFO +62 -1
- fastapi_startkit-0.52.0/README.md +58 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/pyproject.toml +5 -3
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/ai/__init__.py +10 -10
- fastapi_startkit-0.52.0/src/fastapi_startkit/ai/agent.py +99 -0
- fastapi_startkit-0.52.0/src/fastapi_startkit/ai/ai.py +108 -0
- fastapi_startkit-0.52.0/src/fastapi_startkit/ai/judge.py +34 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/ai/pipeline.py +22 -2
- fastapi_startkit-0.52.0/src/fastapi_startkit/ai/recording.py +182 -0
- fastapi_startkit-0.52.0/src/fastapi_startkit/ai/runner.py +321 -0
- fastapi_startkit-0.52.0/src/fastapi_startkit/ai/state.py +88 -0
- fastapi_startkit-0.52.0/src/fastapi_startkit/ai/testing.py +596 -0
- fastapi_startkit-0.52.0/src/fastapi_startkit/ai/types.py +5 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/application.py +2 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/configuration/Configuration.py +1 -1
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/container/container.py +4 -7
- fastapi_startkit-0.52.0/src/fastapi_startkit/events/__init__.py +13 -0
- fastapi_startkit-0.52.0/src/fastapi_startkit/events/dispatcher.py +141 -0
- fastapi_startkit-0.52.0/src/fastapi_startkit/events/fake.py +79 -0
- fastapi_startkit-0.52.0/src/fastapi_startkit/events/helpers.py +19 -0
- fastapi_startkit-0.52.0/src/fastapi_startkit/events/listener.py +16 -0
- fastapi_startkit-0.52.0/src/fastapi_startkit/events/provider.py +31 -0
- fastapi_startkit-0.52.0/src/fastapi_startkit/facades/Event.py +5 -0
- fastapi_startkit-0.52.0/src/fastapi_startkit/facades/Event.pyi +41 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/__init__.py +1 -0
- fastapi_startkit-0.52.0/src/fastapi_startkit/fastapi/__init__.py +15 -0
- fastapi_startkit-0.52.0/src/fastapi_startkit/fastapi/events.py +29 -0
- fastapi_startkit-0.52.0/src/fastapi_startkit/fastapi/middleware.py +54 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/fastapi/providers/fastapi_provider.py +13 -1
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/loader/Loader.py +1 -1
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/collection/Collection.py +15 -2
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/connections/factory.py +35 -14
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/models/attribute.py +5 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/models/builder.py +72 -10
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/models/model.py +47 -17
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/relationships/BelongsToMany.py +28 -45
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/relationships/MorphMany.py +23 -21
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/relationships/MorphOne.py +21 -17
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/relationships/MorphToMany.py +12 -8
- fastapi_startkit-0.52.0/src/fastapi_startkit/py.typed +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/support/providers/provider.py +3 -2
- fastapi_startkit-0.50.0/src/fastapi_startkit/ai/agent.py +0 -299
- fastapi_startkit-0.50.0/src/fastapi_startkit/ai/fakes.py +0 -53
- fastapi_startkit-0.50.0/src/fastapi_startkit/ai/model_builder.py +0 -47
- fastapi_startkit-0.50.0/src/fastapi_startkit/ai/response.py +0 -93
- fastapi_startkit-0.50.0/src/fastapi_startkit/ai/runner.py +0 -59
- fastapi_startkit-0.50.0/src/fastapi_startkit/ai/testing.py +0 -190
- fastapi_startkit-0.50.0/src/fastapi_startkit/fastapi/__init__.py +0 -6
- fastapi_startkit-0.50.0/src/fastapi_startkit/masoniteorm.backup/.pytest_cache/.gitignore +0 -2
- fastapi_startkit-0.50.0/src/fastapi_startkit/masoniteorm.backup/.pytest_cache/CACHEDIR.TAG +0 -4
- fastapi_startkit-0.50.0/src/fastapi_startkit/masoniteorm.backup/.pytest_cache/README.md +0 -8
- fastapi_startkit-0.50.0/src/fastapi_startkit/masoniteorm.backup/.pytest_cache/v/cache/lastfailed +0 -3
- fastapi_startkit-0.50.0/src/fastapi_startkit/masoniteorm.backup/.pytest_cache/v/cache/nodeids +0 -1
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/.DS_Store +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/ai/audio.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/ai/audio_factory.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/ai/config/ai.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/ai/config/config.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/ai/decorators.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/ai/document.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/ai/image.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/ai/image_factory.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/ai/lab.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/ai/providers/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/ai/providers/ai_provider.py +0 -0
- /fastapi_startkit-0.50.0/src/fastapi_startkit/masoniteorm/config/__init__.py → /fastapi_startkit-0.52.0/src/fastapi_startkit/ai/tinker.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/broadcasting/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/broadcasting/auth.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/broadcasting/channels.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/broadcasting/config.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/broadcasting/drivers/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/broadcasting/drivers/log_driver.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/broadcasting/drivers/reverb_driver.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/broadcasting/event.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/broadcasting/helpers.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/broadcasting/manager.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/broadcasting/provider.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/broadcasting/reverb/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/broadcasting/reverb/channel_manager.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/broadcasting/reverb/connection.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/broadcasting/reverb/server.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/broadcasting/stubs/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/broadcasting/stubs/channels.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/carbon/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/carbon/carbon.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/config/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/config/app.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/configuration/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/configuration/config.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/configuration/helpers.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/configuration/providers/ConfigurationProvider.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/configuration/providers/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/console/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/console/application.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/console/can_override_config.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/console/can_override_default_options.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/console/command.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/console/publish_command.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/container/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/environment/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/environment/environment.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/exceptions/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/exceptions/exceptions.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/exceptions/handler.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/AI.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/AI.pyi +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Cache.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Config.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Config.pyi +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Dump.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Dump.pyi +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Facade.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Gate.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Gate.pyi +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Hash.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Hash.pyi +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Loader.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Loader.pyi +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Mail.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Mail.pyi +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Notification.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Notification.pyi +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Queue.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Queue.pyi +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/RateLimiter.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/RateLimiter.pyi +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Request.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Request.pyi +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Response.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Response.pyi +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Session.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Session.pyi +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Url.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Url.pyi +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/View.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/View.pyi +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Vite.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/facades/Vite.pyi +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/fastapi/commands/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/fastapi/commands/serve_command.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/fastapi/config/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/fastapi/config/fastapi.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/fastapi/exceptions.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/fastapi/requests/model.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/fastapi/routers/router.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/fastapi/testing/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/fastapi/testing/assertable_json.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/fastapi/testing/test_case.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/fastapi/testing/test_response.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/foundation/app_provider.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/inertia/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/inertia/constant.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/inertia/context.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/inertia/inertia.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/inertia/middleware.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/inertia/props/props.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/inertia/provider.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/jsonapi/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/jsonapi/response.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/loader/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/ChannelFactory.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/channels/BaseChannel.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/channels/DailyChannel.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/channels/MultiBaseChannel.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/channels/SingleChannel.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/channels/SlackChannel.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/channels/StackChannel.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/channels/SyslogChannel.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/channels/TerminalChannel.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/channels/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/config/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/config/channels.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/config/logging.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/drivers/BaseDriver.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/drivers/LogSingleDriver.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/drivers/LogSlackDriver.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/drivers/LogSyslogDriver.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/drivers/LogTerminalDriver.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/drivers/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/factory.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/file.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/handler.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/listeners.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/logger.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/managers/LoggingManager.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/managers/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/providers/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/logging/providers/log_provider.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/collection/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/DBMigrateCommand.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/DBSeedCommand.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/MakeMigrationCommand.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/MakeModelCommand.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/MakeModelDocstringCommand.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/MakeObserverCommand.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/MakeSeedCommand.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/MigrateFreshCommand.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/MigrateRefreshCommand.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/MigrateResetCommand.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/MigrateRollbackCommand.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/MigrateStatusCommand.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/ShellCommand.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/stubs/create_migration.stub +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/stubs/create_seed.stub +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/stubs/model.stub +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/stubs/observer.stub +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/commands/stubs/table_migration.stub +0 -0
- {fastapi_startkit-0.50.0/src/fastapi_startkit/masoniteorm/helpers → fastapi_startkit-0.52.0/src/fastapi_startkit/masoniteorm/config}/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/config/config.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/config/database.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/connections/connection.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/connections/manager.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/connections/mysql_connection.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/connections/postgres_connection.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/connections/sqlite_connection.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/exceptions.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/expressions/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/expressions/expressions.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/facades/DB.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/facades/Schema.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/facades/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/factory/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/factory/factory.py +0 -0
- {fastapi_startkit-0.50.0/src/fastapi_startkit/masoniteorm/query → fastapi_startkit-0.52.0/src/fastapi_startkit/masoniteorm/helpers}/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/helpers/misc.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/migrations/Migration.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/migrations/Migrator.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/migrations/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/models/MigrationModel.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/models/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/models/caster.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/models/fields.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/models/observer.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/models/pivot.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/models/registry.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/models/relationship.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/observers/ObservesEvents.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/observers/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/pagination/BasePaginator.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/pagination/LengthAwarePaginator.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/pagination/SimplePaginator.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/pagination/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/providers/DatabaseProvider.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/providers/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/query/EagerLoadMixin.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/query/EagerRelation.py +0 -0
- {fastapi_startkit-0.50.0/src/fastapi_startkit/mcp/providers → fastapi_startkit-0.52.0/src/fastapi_startkit/masoniteorm/query}/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/query/grammars/BaseGrammar.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/query/grammars/MSSQLGrammar.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/query/grammars/MySQLGrammar.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/query/grammars/PostgresGrammar.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/query/grammars/SQLiteGrammar.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/query/grammars/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/query/processors/MSSQLPostProcessor.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/query/processors/MySQLPostProcessor.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/query/processors/PostgresPostProcessor.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/query/processors/SQLitePostProcessor.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/query/processors/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/query/support.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/relationships/BaseRelationship.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/relationships/BelongsTo.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/relationships/HasMany.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/relationships/HasManyThrough.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/relationships/HasOne.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/relationships/HasOneThrough.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/relationships/MorphTo.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/relationships/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/schema/Blueprint.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/schema/Column.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/schema/ColumnDiff.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/schema/Constraint.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/schema/ForeignKeyConstraint.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/schema/Index.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/schema/Table.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/schema/TableDiff.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/schema/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/schema/platforms/MSSQLPlatform.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/schema/platforms/MySQLPlatform.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/schema/platforms/Platform.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/schema/platforms/PostgresPlatform.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/schema/platforms/SQLitePlatform.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/schema/platforms/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/schema/schema.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/seeders/Seeder.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/seeders/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/stubs/create-migration.html +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/stubs/table-migration.html +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/testing/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/masoniteorm/testing/transaction.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/mcp/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/mcp/argument.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/mcp/prompt.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/mcp/protocol.py +0 -0
- /fastapi_startkit-0.50.0/src/fastapi_startkit/py.typed → /fastapi_startkit-0.52.0/src/fastapi_startkit/mcp/providers/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/mcp/providers/mcp_provider.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/mcp/request.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/mcp/resource.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/mcp/response.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/mcp/server.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/mcp/tool.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/process/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/process/exception.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/process/fake.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/process/process.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/process/result.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/skills/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/skills/adapters/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/skills/adapters/base.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/skills/adapters/claude.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/skills/adapters/gemini.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/skills/commands/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/skills/commands/skills.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/skills/parser.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/skills/provider.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/skills/registry.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/skills/stubs/.ai/fastapi-startkit/database/SKILL.md +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/skills/stubs/.ai/fastapi-startkit/fastapi/SKILL.md +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/skills/stubs/.ai/fastapi-startkit/inertia/SKILL.md +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/storage/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/storage/config/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/storage/config/storage.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/storage/data/mime.types +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/storage/drivers/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/storage/drivers/fake.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/storage/drivers/local.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/storage/drivers/s3.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/storage/file.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/storage/filestream.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/storage/helper.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/storage/providers/provider.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/storage/storage.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/support/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/support/collection.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/support/dataclass.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/support/providers/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/support/string.py +0 -0
- {fastapi_startkit-0.50.0/src/fastapi_startkit/utils → fastapi_startkit-0.52.0/src/fastapi_startkit/support}/structures.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/support/uri.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/testing/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/testing/test_case.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/vite/__init__.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/vite/config/vite.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/vite/exceptions.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/vite/providers/provider.py +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/vite/stubs/package.json +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/vite/stubs/resources/css/app.css +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/vite/stubs/resources/js/app.ts +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/vite/stubs/resources/templates/index.html +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/vite/stubs/tsconfig.json +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/vite/stubs/vite.config.ts +0 -0
- {fastapi_startkit-0.50.0 → fastapi_startkit-0.52.0}/src/fastapi_startkit/vite/vite.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: fastapi-startkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.52.0
|
|
4
4
|
Summary: A modular, provider-driven framework for building async Python web applications with FastAPI — bundling a service container, configuration system, an async-first ORM, facades, and a Cleo-powered console.
|
|
5
5
|
Keywords: fastapi,framework,async,web,asgi,orm,sqlalchemy,pydantic,ioc-container,dependency-injection,service-provider,cli,console,configuration,microservices,starter-kit,boilerplate
|
|
6
6
|
Author: Bedram Tamang
|
|
@@ -22,6 +22,7 @@ Requires-Dist: dotenv>=0.9.9
|
|
|
22
22
|
Requires-Dist: pydantic>=2.12.5
|
|
23
23
|
Requires-Dist: langchain>=1.0.0 ; extra == 'ai'
|
|
24
24
|
Requires-Dist: langchain-core>=1.0.0 ; extra == 'ai'
|
|
25
|
+
Requires-Dist: langgraph>=1.0.0 ; extra == 'ai'
|
|
25
26
|
Requires-Dist: faker>=40.13.0 ; extra == 'database'
|
|
26
27
|
Requires-Dist: sqlalchemy[asyncio]>=2.0.38 ; extra == 'database'
|
|
27
28
|
Requires-Dist: fastapi[standard]>=0.124.4 ; extra == 'fastapi'
|
|
@@ -46,3 +47,63 @@ Provides-Extra: mysql
|
|
|
46
47
|
Provides-Extra: postgres
|
|
47
48
|
Provides-Extra: sqlite
|
|
48
49
|
Provides-Extra: vite
|
|
50
|
+
Description-Content-Type: text/markdown
|
|
51
|
+
|
|
52
|
+
# FastAPI Startkit
|
|
53
|
+
|
|
54
|
+
[](https://codecov.io/gh/fastapi-startkit/fastapi-startkit-framework)
|
|
55
|
+
[](https://pypi.org/project/fastapi-startkit/)
|
|
56
|
+
[](https://pypi.org/project/fastapi-startkit/)
|
|
57
|
+
|
|
58
|
+
A modular, provider-driven framework for building Python applications with FastAPI. It bundles a
|
|
59
|
+
service container, configuration system, an async-first ORM, facades, and a Cleo-powered console into
|
|
60
|
+
a single cohesive foundation.
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install fastapi-startkit
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Optional extras enable additional capabilities:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
fastapi-startkit[fastapi] # FastAPI + Starlette
|
|
72
|
+
fastapi-startkit[database] # SQLAlchemy async ORM
|
|
73
|
+
fastapi-startkit[vite] # Jinja2 for Vite integration
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Features
|
|
77
|
+
|
|
78
|
+
- **Service container** — IoC container with `bind`/`make`/`resolve` auto-wiring and lifecycle hooks.
|
|
79
|
+
- **Configuration** — dataclass-based config sourced from the environment with dotted-key access.
|
|
80
|
+
- **Providers** — two-phase (`register` / `boot`) service registration.
|
|
81
|
+
- **Routing** — a `Router` wrapper around FastAPI's `APIRouter` with a `resource()` CRUD shortcut.
|
|
82
|
+
- **ORM** — async-first Masonite ORM fork on SQLAlchemy async, with relationships and migrations.
|
|
83
|
+
- **Facades** — static-like access to container-resolved services (`Config`, `Auth`, ...).
|
|
84
|
+
- **Console** — Cleo-based CLI (`artisan`) for migrations, seeders, and code generation.
|
|
85
|
+
|
|
86
|
+
## Documentation
|
|
87
|
+
|
|
88
|
+
Full documentation is available at
|
|
89
|
+
[fastapi-startkit.github.io](https://fastapi-startkit.github.io).
|
|
90
|
+
|
|
91
|
+
## Development
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# Install dependencies
|
|
95
|
+
uv sync --group dev --extra database --extra sqlite --extra fastapi --extra vite --extra postgres
|
|
96
|
+
|
|
97
|
+
# Run the test suite
|
|
98
|
+
uv run pytest tests/ -v
|
|
99
|
+
|
|
100
|
+
# Run tests with coverage
|
|
101
|
+
uv run pytest --cov --cov-report=term-missing
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Coverage is collected in CI and reported to
|
|
105
|
+
[Codecov](https://codecov.io/gh/fastapi-startkit/fastapi-startkit-framework).
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
See the repository root for license details.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# FastAPI Startkit
|
|
2
|
+
|
|
3
|
+
[](https://codecov.io/gh/fastapi-startkit/fastapi-startkit-framework)
|
|
4
|
+
[](https://pypi.org/project/fastapi-startkit/)
|
|
5
|
+
[](https://pypi.org/project/fastapi-startkit/)
|
|
6
|
+
|
|
7
|
+
A modular, provider-driven framework for building Python applications with FastAPI. It bundles a
|
|
8
|
+
service container, configuration system, an async-first ORM, facades, and a Cleo-powered console into
|
|
9
|
+
a single cohesive foundation.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install fastapi-startkit
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Optional extras enable additional capabilities:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
fastapi-startkit[fastapi] # FastAPI + Starlette
|
|
21
|
+
fastapi-startkit[database] # SQLAlchemy async ORM
|
|
22
|
+
fastapi-startkit[vite] # Jinja2 for Vite integration
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- **Service container** — IoC container with `bind`/`make`/`resolve` auto-wiring and lifecycle hooks.
|
|
28
|
+
- **Configuration** — dataclass-based config sourced from the environment with dotted-key access.
|
|
29
|
+
- **Providers** — two-phase (`register` / `boot`) service registration.
|
|
30
|
+
- **Routing** — a `Router` wrapper around FastAPI's `APIRouter` with a `resource()` CRUD shortcut.
|
|
31
|
+
- **ORM** — async-first Masonite ORM fork on SQLAlchemy async, with relationships and migrations.
|
|
32
|
+
- **Facades** — static-like access to container-resolved services (`Config`, `Auth`, ...).
|
|
33
|
+
- **Console** — Cleo-based CLI (`artisan`) for migrations, seeders, and code generation.
|
|
34
|
+
|
|
35
|
+
## Documentation
|
|
36
|
+
|
|
37
|
+
Full documentation is available at
|
|
38
|
+
[fastapi-startkit.github.io](https://fastapi-startkit.github.io).
|
|
39
|
+
|
|
40
|
+
## Development
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Install dependencies
|
|
44
|
+
uv sync --group dev --extra database --extra sqlite --extra fastapi --extra vite --extra postgres
|
|
45
|
+
|
|
46
|
+
# Run the test suite
|
|
47
|
+
uv run pytest tests/ -v
|
|
48
|
+
|
|
49
|
+
# Run tests with coverage
|
|
50
|
+
uv run pytest --cov --cov-report=term-missing
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Coverage is collected in CI and reported to
|
|
54
|
+
[Codecov](https://codecov.io/gh/fastapi-startkit/fastapi-startkit-framework).
|
|
55
|
+
|
|
56
|
+
## License
|
|
57
|
+
|
|
58
|
+
See the repository root for license details.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "fastapi-startkit"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.52.0"
|
|
4
4
|
description = "A modular, provider-driven framework for building async Python web applications with FastAPI — bundling a service container, configuration system, an async-first ORM, facades, and a Cleo-powered console."
|
|
5
|
+
readme = "README.md"
|
|
5
6
|
keywords = [
|
|
6
7
|
"fastapi",
|
|
7
8
|
"framework",
|
|
@@ -86,6 +87,7 @@ inertia = [
|
|
|
86
87
|
ai = [
|
|
87
88
|
"langchain>=1.0.0",
|
|
88
89
|
"langchain-core>=1.0.0",
|
|
90
|
+
"langgraph>=1.0.0",
|
|
89
91
|
]
|
|
90
92
|
|
|
91
93
|
[dependency-groups]
|
|
@@ -129,7 +131,7 @@ exclude = [
|
|
|
129
131
|
"**/__pycache__",
|
|
130
132
|
"**/.venv",
|
|
131
133
|
]
|
|
132
|
-
typeCheckingMode = "
|
|
134
|
+
typeCheckingMode = "standard"
|
|
133
135
|
pythonVersion = "3.12"
|
|
134
136
|
|
|
135
137
|
[tool.pytest.ini_options]
|
|
@@ -148,7 +150,7 @@ omit = [
|
|
|
148
150
|
[tool.coverage.report]
|
|
149
151
|
show_missing = true
|
|
150
152
|
skip_covered = false
|
|
151
|
-
fail_under =
|
|
153
|
+
fail_under = 80
|
|
152
154
|
exclude_lines = [
|
|
153
155
|
"pragma: no cover",
|
|
154
156
|
"if TYPE_CHECKING:",
|
|
@@ -6,31 +6,31 @@ from .config.config import AnthropicConfig, ElevenLabsConfig, GoogleConfig, Open
|
|
|
6
6
|
from .config.ai import AIConfig
|
|
7
7
|
from .decorators import max_steps, max_tokens, model, provider, timeout, top_p
|
|
8
8
|
from .document import Document
|
|
9
|
-
from .fakes import fake_chat_model
|
|
10
9
|
from .image import Image, ImageResponse
|
|
11
10
|
from .image_factory import ImageFactory
|
|
11
|
+
from .ai import Ai
|
|
12
|
+
from .judge import JudgeAgent
|
|
12
13
|
from .providers.ai_provider import AIProvider
|
|
13
|
-
from .
|
|
14
|
-
from .testing import
|
|
14
|
+
from . import state
|
|
15
|
+
from .testing import AgentFake, AgentRecordFake, AssertToolCall
|
|
15
16
|
|
|
16
17
|
__all__ = [
|
|
17
18
|
"Agent",
|
|
19
|
+
"Ai",
|
|
18
20
|
"Middleware",
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"AgentSnapshot",
|
|
21
|
+
"AgentFake",
|
|
22
|
+
"state",
|
|
22
23
|
"AIConfig",
|
|
23
24
|
"AIProvider",
|
|
24
25
|
"AnthropicConfig",
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
26
|
+
"JudgeAgent",
|
|
27
|
+
"AgentRecordFake",
|
|
28
|
+
"AssertToolCall",
|
|
28
29
|
"Audio",
|
|
29
30
|
"AudioResponse",
|
|
30
31
|
"AudioFactory",
|
|
31
32
|
"Document",
|
|
32
33
|
"ElevenLabsConfig",
|
|
33
|
-
"fake_chat_model",
|
|
34
34
|
"GoogleConfig",
|
|
35
35
|
"Image",
|
|
36
36
|
"ImageFactory",
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
from typing import TYPE_CHECKING, AsyncIterator, Callable, Generic, Optional, Type, TypeVar
|
|
5
|
+
|
|
6
|
+
from .document import Document
|
|
7
|
+
from .runner import BaseRunner, Runner
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from langchain_core.tools import BaseTool
|
|
11
|
+
|
|
12
|
+
from .testing import AgentFake, AgentRecordFake
|
|
13
|
+
from .types import Middleware
|
|
14
|
+
|
|
15
|
+
_R = TypeVar("_R")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class _hybridmethod(Generic[_R]):
|
|
19
|
+
"""A method callable on the class (bound to a freshly built instance) or on an
|
|
20
|
+
existing instance (bound to that instance). Lets ``Agent.record()`` and
|
|
21
|
+
``Agent(state, config).record()`` both work, the latter keeping its state."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, func: Callable[..., _R]) -> None:
|
|
24
|
+
self._func = func
|
|
25
|
+
|
|
26
|
+
def __get__(self, instance: object, owner: type) -> Callable[..., _R]:
|
|
27
|
+
target = instance if instance is not None else owner()
|
|
28
|
+
return functools.partial(self._func, target)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Agent:
|
|
32
|
+
provider: str | None = None
|
|
33
|
+
model: str | None = None
|
|
34
|
+
max_steps: int = 10
|
|
35
|
+
max_tokens: int = 4096
|
|
36
|
+
timeout: float = 30.0
|
|
37
|
+
top_p: float = 1.0
|
|
38
|
+
temperature: float | None = None
|
|
39
|
+
tool_choice: str | None = None
|
|
40
|
+
|
|
41
|
+
def messages(self) -> list[dict]:
|
|
42
|
+
return []
|
|
43
|
+
|
|
44
|
+
def instructions(self) -> str | None:
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
def schema(self) -> Optional[Type]:
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
def tools(self) -> list[BaseTool]:
|
|
51
|
+
return []
|
|
52
|
+
|
|
53
|
+
def middleware(self) -> list[Middleware]:
|
|
54
|
+
return []
|
|
55
|
+
|
|
56
|
+
def provider_options(self) -> dict:
|
|
57
|
+
return {}
|
|
58
|
+
|
|
59
|
+
async def prompt(
|
|
60
|
+
self,
|
|
61
|
+
message: str,
|
|
62
|
+
*,
|
|
63
|
+
model: str | None = None,
|
|
64
|
+
attachments: list[Document] | None = None,
|
|
65
|
+
provider_options: dict | None = None,
|
|
66
|
+
) -> dict:
|
|
67
|
+
"""Returns the turn as ``{"messages": [HumanMessage, AIMessage, ToolMessage,
|
|
68
|
+
...]}`` — the same state langchain's ``create_agent().invoke()`` yields —
|
|
69
|
+
plus ``"structured_response"`` when the agent defines a schema()."""
|
|
70
|
+
return await self.runner().run(message, model=model, attachments=attachments, provider_options=provider_options)
|
|
71
|
+
|
|
72
|
+
async def stream(
|
|
73
|
+
self,
|
|
74
|
+
message: str,
|
|
75
|
+
*,
|
|
76
|
+
model: str | None = None,
|
|
77
|
+
provider_options: dict | None = None,
|
|
78
|
+
) -> AsyncIterator[dict]:
|
|
79
|
+
"""Yields StandardStreamEvent dicts — the same shape LangChain's
|
|
80
|
+
astream_events produces: on_chat_model_(start|stream|end) for the model
|
|
81
|
+
turn (data.chunk carries each AIMessageChunk) and on_tool_(start|end)
|
|
82
|
+
around each tool execution."""
|
|
83
|
+
async for chunk in self.runner().stream(message, model=model, provider_options=provider_options):
|
|
84
|
+
yield chunk
|
|
85
|
+
|
|
86
|
+
def runner(self) -> BaseRunner:
|
|
87
|
+
return Runner(self)
|
|
88
|
+
|
|
89
|
+
@classmethod
|
|
90
|
+
def fake(cls, responses: list) -> "AgentFake":
|
|
91
|
+
from .testing import AgentFake
|
|
92
|
+
|
|
93
|
+
return AgentFake(cls, responses)
|
|
94
|
+
|
|
95
|
+
@_hybridmethod
|
|
96
|
+
def record(self, cassette: str | None = None, messages: list | None = None) -> "AgentRecordFake":
|
|
97
|
+
from .testing import AgentRecordFake
|
|
98
|
+
|
|
99
|
+
return AgentRecordFake(self, cassette, messages)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
4
|
+
|
|
5
|
+
from .lab import Lab
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from langchain_core.language_models.fake_chat_models import GenericFakeChatModel
|
|
9
|
+
|
|
10
|
+
from .agent import Agent
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Ai:
|
|
14
|
+
_fakes: dict[str, GenericFakeChatModel] = {}
|
|
15
|
+
|
|
16
|
+
def __init__(self) -> None:
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def _key(agent: "Agent | str") -> str:
|
|
21
|
+
return agent if isinstance(agent, str) else type(agent).__name__
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def fake(cls, agent: "Agent | str", messages: list) -> Any:
|
|
25
|
+
from langchain_core.language_models.fake_chat_models import GenericFakeChatModel
|
|
26
|
+
from langchain_core.messages import AIMessage
|
|
27
|
+
|
|
28
|
+
turns = [message if hasattr(message, "content") else AIMessage(content=str(message)) for message in messages]
|
|
29
|
+
model = GenericFakeChatModel(messages=iter(turns))
|
|
30
|
+
cls._fakes[cls._key(agent)] = model
|
|
31
|
+
return model
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def has_fake_model_for(cls, agent: "Agent | str") -> bool:
|
|
35
|
+
return cls._key(agent) in cls._fakes
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def get_fake_model_for(cls, agent: "Agent | str") -> Any:
|
|
39
|
+
return cls._fakes[cls._key(agent)]
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def forget(cls, agent: "Agent | str") -> None:
|
|
43
|
+
cls._fakes.pop(cls._key(agent), None)
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def reset_fakes(cls) -> None:
|
|
47
|
+
cls._fakes.clear()
|
|
48
|
+
|
|
49
|
+
def get_model_for(
|
|
50
|
+
self,
|
|
51
|
+
agent: "Agent",
|
|
52
|
+
model: str | None = None,
|
|
53
|
+
provider_options: dict | None = None,
|
|
54
|
+
) -> Any:
|
|
55
|
+
if self.has_fake_model_for(agent):
|
|
56
|
+
return self.get_fake_model_for(agent)
|
|
57
|
+
return self.build(agent, model, provider_options)
|
|
58
|
+
|
|
59
|
+
def build(
|
|
60
|
+
self,
|
|
61
|
+
agent: "Agent",
|
|
62
|
+
model: str | None = None,
|
|
63
|
+
provider_options: dict | None = None,
|
|
64
|
+
) -> Any:
|
|
65
|
+
from langchain.chat_models import init_chat_model # noqa: PLC0415
|
|
66
|
+
|
|
67
|
+
lab = Lab.get_provider(agent.provider)
|
|
68
|
+
kwargs: dict[str, Any] = {"model_provider": lab.get_provider_key()}
|
|
69
|
+
|
|
70
|
+
api_key = lab.get_api_key()
|
|
71
|
+
if api_key:
|
|
72
|
+
kwargs["api_key"] = api_key
|
|
73
|
+
if agent.max_tokens:
|
|
74
|
+
kwargs["max_tokens"] = agent.max_tokens
|
|
75
|
+
if agent.top_p != 1.0:
|
|
76
|
+
kwargs["top_p"] = agent.top_p
|
|
77
|
+
if agent.temperature is not None:
|
|
78
|
+
kwargs["temperature"] = agent.temperature
|
|
79
|
+
if agent.timeout:
|
|
80
|
+
kwargs["timeout"] = agent.timeout
|
|
81
|
+
|
|
82
|
+
kwargs.update(self._resolve_provider_options(agent, provider_options))
|
|
83
|
+
|
|
84
|
+
chat_model = init_chat_model(self._resolve_model(agent, model), **kwargs)
|
|
85
|
+
|
|
86
|
+
tools = list(agent.tools())
|
|
87
|
+
if tools:
|
|
88
|
+
if agent.tool_choice:
|
|
89
|
+
chat_model = chat_model.bind_tools(tools, tool_choice=agent.tool_choice)
|
|
90
|
+
else:
|
|
91
|
+
chat_model = chat_model.bind_tools(tools)
|
|
92
|
+
|
|
93
|
+
schema = agent.schema()
|
|
94
|
+
if schema is not None:
|
|
95
|
+
chat_model = chat_model.with_structured_output(schema, include_raw=True)
|
|
96
|
+
|
|
97
|
+
return chat_model
|
|
98
|
+
|
|
99
|
+
def _resolve_model(self, agent: "Agent", override: str | None = None) -> str:
|
|
100
|
+
return Lab.get_provider(agent.provider).get_model(override or agent.model or None)
|
|
101
|
+
|
|
102
|
+
def _resolve_provider_options(self, agent: "Agent", override: dict | None = None) -> dict:
|
|
103
|
+
options = dict(agent.provider_options().get(agent.provider, {}))
|
|
104
|
+
if override:
|
|
105
|
+
provider_specific = override.get(agent.provider, override)
|
|
106
|
+
if isinstance(provider_specific, dict):
|
|
107
|
+
options.update(provider_specific)
|
|
108
|
+
return options
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from .agent import Agent
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Verdict(BaseModel):
|
|
9
|
+
passed: bool
|
|
10
|
+
reasoning: str = ""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class JudgeAgent(Agent):
|
|
14
|
+
"""Grades a response against a natural-language expectation.
|
|
15
|
+
|
|
16
|
+
A plain ``Agent`` whose ``schema()`` is a ``Verdict`` model, so the JSON
|
|
17
|
+
reply is parsed into a typed result through the standard structured-output
|
|
18
|
+
path — no hand-rolled verdict parsing. Set ``.model``/``.provider`` like
|
|
19
|
+
any other agent; it's fakeable via ``fake()`` and replayable via
|
|
20
|
+
``record()`` for free.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def instructions(self) -> str:
|
|
24
|
+
return (
|
|
25
|
+
"You are grading whether an AI agent's response satisfies an expectation. "
|
|
26
|
+
'Reply with strict JSON only, no prose: {"passed": true|false, "reasoning": "<one sentence>"}'
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def schema(self):
|
|
30
|
+
return Verdict
|
|
31
|
+
|
|
32
|
+
async def judge(self, expectation: str, content: str) -> dict:
|
|
33
|
+
state = await self.prompt(f"Expectation: {expectation}\n\nResponse to grade:\n{content}")
|
|
34
|
+
return state["structured_response"].model_dump()
|
|
@@ -40,13 +40,33 @@ class Response:
|
|
|
40
40
|
await result
|
|
41
41
|
return final
|
|
42
42
|
|
|
43
|
+
@staticmethod
|
|
44
|
+
def _merge(accumulated: Any, chunk: Any) -> Any:
|
|
45
|
+
# Stream events (astream_events shape) merge into joined text for the
|
|
46
|
+
# after-hooks: model chunks and tool outputs contribute their text, the
|
|
47
|
+
# start/end envelopes add nothing. Any other dict (e.g. a
|
|
48
|
+
# structured-output result carrying "raw"/"parsed") passes through whole.
|
|
49
|
+
if isinstance(chunk, dict) and "event" in chunk:
|
|
50
|
+
data = chunk.get("data") or {}
|
|
51
|
+
if chunk["event"] == "on_chat_model_stream":
|
|
52
|
+
text = getattr(data.get("chunk"), "content", "") or ""
|
|
53
|
+
elif chunk["event"] == "on_tool_end":
|
|
54
|
+
text = getattr(data.get("output"), "content", "") or ""
|
|
55
|
+
else:
|
|
56
|
+
text = ""
|
|
57
|
+
text = text if isinstance(text, str) else str(text)
|
|
58
|
+
if accumulated is None:
|
|
59
|
+
return text or None
|
|
60
|
+
return accumulated + text
|
|
61
|
+
return chunk if accumulated is None else accumulated + chunk
|
|
62
|
+
|
|
43
63
|
def __aiter__(self) -> AsyncIterator:
|
|
44
64
|
async def _it() -> AsyncIterator:
|
|
45
65
|
accumulated = None
|
|
46
66
|
finished = False
|
|
47
67
|
try:
|
|
48
68
|
async for chunk in self._source():
|
|
49
|
-
accumulated =
|
|
69
|
+
accumulated = self._merge(accumulated, chunk)
|
|
50
70
|
yield chunk
|
|
51
71
|
finished = True
|
|
52
72
|
await self._finish(accumulated)
|
|
@@ -63,7 +83,7 @@ class Response:
|
|
|
63
83
|
async def _run() -> Any:
|
|
64
84
|
accumulated = None
|
|
65
85
|
async for chunk in self._source():
|
|
66
|
-
accumulated =
|
|
86
|
+
accumulated = self._merge(accumulated, chunk)
|
|
67
87
|
return await self._finish(accumulated)
|
|
68
88
|
|
|
69
89
|
return _run().__await__()
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""Ordered per-turn recording format for ``Agent.record()`` cassettes.
|
|
2
|
+
|
|
3
|
+
A recorded turn is an ordered list of message dicts that captures the full
|
|
4
|
+
interaction for one user input — the human prompt, every model turn (with its
|
|
5
|
+
token ``uses`` and ``response_time``), and every tool call (with the tool's
|
|
6
|
+
response and its own ``response_time``). Persisting the whole ordered exchange —
|
|
7
|
+
rather than only the final answer — lets recordings be replayed and inspected
|
|
8
|
+
faithfully.
|
|
9
|
+
|
|
10
|
+
[
|
|
11
|
+
{"type": "human", "content": "suggest me jobs"},
|
|
12
|
+
{"type": "ai", "tool_calls": [...], "uses": {...}, "response_time": 12.0},
|
|
13
|
+
{"type": "tool_response", "content_type": "json", "content": "[...]", "response_time": 5.0},
|
|
14
|
+
{"type": "ai", "content": "Here is a job...", "uses": {...}, "response_time": 8.0},
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
``response_time`` is milliseconds; ``uses`` mirrors LangChain ``usage_metadata``
|
|
18
|
+
as ``input_token`` / ``output_token`` / ``cache_token`` / ``total_token``.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import json
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, ToolMessage
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _infer_content_type(content: str) -> str:
|
|
30
|
+
try:
|
|
31
|
+
json.loads(content)
|
|
32
|
+
except (ValueError, TypeError):
|
|
33
|
+
return "text"
|
|
34
|
+
return "json"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def uses_from_usage_metadata(meta: dict | None) -> dict:
|
|
38
|
+
"""Map a LangChain ``usage_metadata`` dict onto the cassette ``uses`` shape."""
|
|
39
|
+
meta = meta or {}
|
|
40
|
+
details = meta.get("input_token_details") or {}
|
|
41
|
+
return {
|
|
42
|
+
"input_token": meta.get("input_tokens", 0),
|
|
43
|
+
"output_token": meta.get("output_tokens", 0),
|
|
44
|
+
"cache_token": details.get("cache_read", 0),
|
|
45
|
+
"total_token": meta.get("total_tokens", 0),
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def is_transcript(value: Any) -> bool:
|
|
50
|
+
"""True when ``value`` is a new-format ordered transcript (list of typed entries)."""
|
|
51
|
+
return isinstance(value, list) and bool(value) and isinstance(value[0], dict) and "type" in value[0]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def human(content: str) -> dict:
|
|
55
|
+
"""Build a ``human`` transcript entry."""
|
|
56
|
+
return {"type": "human", "content": content}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def ai(
|
|
60
|
+
content: str = "",
|
|
61
|
+
tool_calls: list[dict] | None = None,
|
|
62
|
+
uses: dict | None = None,
|
|
63
|
+
response_time: float = 0.0,
|
|
64
|
+
chunks: list[str] | None = None,
|
|
65
|
+
) -> dict:
|
|
66
|
+
"""Build an ``ai`` transcript entry (a model turn: answer and/or tool calls)."""
|
|
67
|
+
entry: dict[str, Any] = {"type": "ai"}
|
|
68
|
+
if content:
|
|
69
|
+
entry["content"] = content
|
|
70
|
+
if tool_calls:
|
|
71
|
+
entry["tool_calls"] = tool_calls
|
|
72
|
+
entry["uses"] = uses or {}
|
|
73
|
+
entry["response_time"] = response_time
|
|
74
|
+
if chunks is not None:
|
|
75
|
+
entry["chunks"] = chunks
|
|
76
|
+
return entry
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def tool_response(content: str, response_time: float = 0.0, content_type: str | None = None) -> dict:
|
|
80
|
+
"""Build a ``tool_response`` transcript entry (a tool's output)."""
|
|
81
|
+
return {
|
|
82
|
+
"type": "tool_response",
|
|
83
|
+
"content_type": content_type or _infer_content_type(content),
|
|
84
|
+
"content": content,
|
|
85
|
+
"response_time": response_time,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def usage_metadata_from_uses(uses: dict) -> dict:
|
|
90
|
+
"""Map a cassette ``uses`` dict back onto LangChain ``usage_metadata``."""
|
|
91
|
+
return {
|
|
92
|
+
"input_tokens": uses.get("input_token", 0),
|
|
93
|
+
"output_tokens": uses.get("output_token", 0),
|
|
94
|
+
"total_tokens": uses.get("total_token", 0),
|
|
95
|
+
"input_token_details": {"cache_read": uses.get("cache_token", 0)},
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _as_text(content: Any) -> str:
|
|
100
|
+
return content if isinstance(content, str) else str(content)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def entries_from_messages(messages: list[BaseMessage]) -> list[dict]:
|
|
104
|
+
"""Convert a turn's AI/tool LangChain messages into cassette entries.
|
|
105
|
+
|
|
106
|
+
Human (and system) messages are skipped — the cassette turn opens with its
|
|
107
|
+
own ``human`` entry. ``response_time`` is read from ``additional_kwargs``,
|
|
108
|
+
where the runner stamps it on every AI and tool message."""
|
|
109
|
+
entries: list[dict] = []
|
|
110
|
+
for message in messages or []:
|
|
111
|
+
kind = getattr(message, "type", "")
|
|
112
|
+
response_time = (getattr(message, "additional_kwargs", None) or {}).get("response_time", 0.0)
|
|
113
|
+
if kind == "ai":
|
|
114
|
+
entries.append(
|
|
115
|
+
ai(
|
|
116
|
+
content=_as_text(message.content),
|
|
117
|
+
tool_calls=list(getattr(message, "tool_calls", None) or []),
|
|
118
|
+
uses=uses_from_usage_metadata(getattr(message, "usage_metadata", None)),
|
|
119
|
+
response_time=response_time,
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
elif kind == "tool":
|
|
123
|
+
entries.append(tool_response(content=_as_text(message.content), response_time=response_time))
|
|
124
|
+
return entries
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def messages_from_transcript(transcript: list[dict]) -> list[BaseMessage]:
|
|
128
|
+
"""Rebuild the turn's LangChain messages from cassette entries, so a replayed
|
|
129
|
+
response carries the same ``messages`` a live run produces. Cassette entries
|
|
130
|
+
don't store the tool's name/call id, so those stay empty on replay."""
|
|
131
|
+
messages: list[BaseMessage] = []
|
|
132
|
+
for entry in transcript:
|
|
133
|
+
kind = entry.get("type")
|
|
134
|
+
if kind == "human":
|
|
135
|
+
messages.append(HumanMessage(content=entry.get("content", "")))
|
|
136
|
+
elif kind == "ai":
|
|
137
|
+
uses = entry.get("uses") or {}
|
|
138
|
+
messages.append(
|
|
139
|
+
AIMessage(
|
|
140
|
+
content=entry.get("content", ""),
|
|
141
|
+
tool_calls=entry.get("tool_calls") or [],
|
|
142
|
+
additional_kwargs={"response_time": entry.get("response_time", 0.0)},
|
|
143
|
+
usage_metadata=usage_metadata_from_uses(uses) if uses else None,
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
elif kind == "tool_response":
|
|
147
|
+
messages.append(
|
|
148
|
+
ToolMessage(
|
|
149
|
+
content=entry.get("content", ""),
|
|
150
|
+
tool_call_id="",
|
|
151
|
+
additional_kwargs={"response_time": entry.get("response_time", 0.0)},
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
return messages
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def to_state(transcript: list[dict]) -> dict:
|
|
158
|
+
"""Reconstruct a turn's ``{"messages": [...]}`` state from cassette entries,
|
|
159
|
+
so a replayed turn has the same shape a live run returns."""
|
|
160
|
+
return {"messages": messages_from_transcript(transcript)}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def accumulate_uses(messages: list, totals: dict) -> None:
|
|
164
|
+
"""Add every AI message's token usage in a turn's ``messages`` into ``totals``
|
|
165
|
+
(keys: input, output, cache, total)."""
|
|
166
|
+
for message in messages or []:
|
|
167
|
+
if getattr(message, "type", "") != "ai":
|
|
168
|
+
continue
|
|
169
|
+
uses = uses_from_usage_metadata(getattr(message, "usage_metadata", None))
|
|
170
|
+
totals["input"] += uses["input_token"]
|
|
171
|
+
totals["output"] += uses["output_token"]
|
|
172
|
+
totals["cache"] += uses["cache_token"]
|
|
173
|
+
totals["total"] += uses["total_token"]
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def chunks_from_transcript(transcript: list[dict]) -> list[str]:
|
|
177
|
+
"""Collect streamed chunks recorded on the turn's ``ai`` messages."""
|
|
178
|
+
chunks: list[str] = []
|
|
179
|
+
for entry in transcript:
|
|
180
|
+
if entry.get("type") == "ai" and entry.get("chunks"):
|
|
181
|
+
chunks.extend(entry["chunks"])
|
|
182
|
+
return chunks
|