FastAPI-fastkit 1.2.0__py3-none-any.whl → 1.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. fastapi_fastkit/__init__.py +1 -1
  2. fastapi_fastkit/backend/inspector.py +153 -32
  3. fastapi_fastkit/backend/interactive/__init__.py +2 -0
  4. fastapi_fastkit/backend/interactive/config_builder.py +21 -2
  5. fastapi_fastkit/backend/interactive/prompts.py +52 -1
  6. fastapi_fastkit/backend/interactive/selectors.py +10 -0
  7. fastapi_fastkit/backend/main.py +114 -6
  8. fastapi_fastkit/backend/package_managers/factory.py +1 -1
  9. fastapi_fastkit/backend/package_managers/pdm_manager.py +3 -3
  10. fastapi_fastkit/backend/package_managers/poetry_manager.py +71 -10
  11. fastapi_fastkit/backend/package_managers/uv_manager.py +3 -3
  12. fastapi_fastkit/backend/project_builder/__init__.py +3 -0
  13. fastapi_fastkit/backend/project_builder/config_generator.py +17 -10
  14. fastapi_fastkit/backend/project_builder/preset_layout.py +203 -0
  15. fastapi_fastkit/backend/transducer.py +0 -1
  16. fastapi_fastkit/cli.py +109 -44
  17. fastapi_fastkit/core/settings.py +17 -1
  18. fastapi_fastkit/fastapi_project_template/README.md +72 -23
  19. fastapi_fastkit/fastapi_project_template/fastapi-async-crud/pyproject.toml-tpl +4 -1
  20. fastapi_fastkit/fastapi_project_template/fastapi-custom-response/pyproject.toml-tpl +4 -1
  21. fastapi_fastkit/fastapi_project_template/fastapi-default/pyproject.toml-tpl +4 -1
  22. fastapi_fastkit/fastapi_project_template/fastapi-dockerized/pyproject.toml-tpl +4 -1
  23. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/.env-tpl +2 -0
  24. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/.gitignore-tpl +31 -0
  25. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/README.md-tpl +128 -0
  26. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/pyproject.toml-tpl +70 -0
  27. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/requirements.txt-tpl +11 -0
  28. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/scripts/format.sh-tpl +5 -0
  29. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/scripts/lint.sh-tpl +6 -0
  30. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/scripts/run-server.sh-tpl +8 -0
  31. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/scripts/test.sh-tpl +6 -0
  32. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/__init__.py-tpl +0 -0
  33. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/__init__.py-tpl +0 -0
  34. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/api/__init__.py-tpl +0 -0
  35. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/api/health.py-tpl +11 -0
  36. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/api/router.py-tpl +12 -0
  37. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/core/__init__.py-tpl +0 -0
  38. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/core/config.py-tpl +49 -0
  39. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/db/__init__.py-tpl +0 -0
  40. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/db/memory.py-tpl +48 -0
  41. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/__init__.py-tpl +0 -0
  42. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/__init__.py-tpl +9 -0
  43. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/models.py-tpl +17 -0
  44. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/repository.py-tpl +48 -0
  45. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/router.py-tpl +59 -0
  46. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/schemas.py-tpl +23 -0
  47. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/domains/items/service.py-tpl +52 -0
  48. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/src/app/main.py-tpl +25 -0
  49. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/tests/__init__.py-tpl +0 -0
  50. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/tests/conftest.py-tpl +24 -0
  51. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/tests/test_health.py-tpl +11 -0
  52. fastapi_fastkit/fastapi_project_template/fastapi-domain-starter/tests/test_items.py-tpl +77 -0
  53. fastapi_fastkit/fastapi_project_template/fastapi-empty/README.md-tpl +2 -2
  54. fastapi_fastkit/fastapi_project_template/fastapi-empty/pyproject.toml-tpl +4 -1
  55. fastapi_fastkit/fastapi_project_template/fastapi-mcp/pyproject.toml-tpl +4 -1
  56. fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/pyproject.toml-tpl +4 -1
  57. fastapi_fastkit/fastapi_project_template/fastapi-single-module/pyproject.toml-tpl +4 -1
  58. fastapi_fastkit/utils/main.py +73 -6
  59. {fastapi_fastkit-1.2.0.dist-info → fastapi_fastkit-1.3.0.dist-info}/METADATA +33 -3
  60. {fastapi_fastkit-1.2.0.dist-info → fastapi_fastkit-1.3.0.dist-info}/RECORD +63 -32
  61. {fastapi_fastkit-1.2.0.dist-info → fastapi_fastkit-1.3.0.dist-info}/WHEEL +1 -1
  62. {fastapi_fastkit-1.2.0.dist-info → fastapi_fastkit-1.3.0.dist-info}/entry_points.txt +0 -0
  63. {fastapi_fastkit-1.2.0.dist-info → fastapi_fastkit-1.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -4,7 +4,7 @@
4
4
  # @author bnbong bbbong9@gmail.com
5
5
  # --------------------------------------------------------------------------
6
6
  import subprocess
7
- from typing import List
7
+ from typing import List, Tuple
8
8
 
9
9
  from fastapi_fastkit.core.exceptions import BackendExceptions
10
10
  from fastapi_fastkit.utils.logging import debug_log, get_logger
@@ -19,6 +19,47 @@ from .base import BasePackageManager
19
19
 
20
20
  logger = get_logger(__name__)
21
21
 
22
+ # PEP 440 version specifier operators, longest-match first so "===" beats "==".
23
+ _PEP440_OPERATORS = ("===", "==", "!=", "<=", ">=", "~=", "<", ">")
24
+
25
+
26
+ def _parse_pip_requirement(
27
+ requirement: str,
28
+ ) -> Tuple[str, List[str], str, str]:
29
+ """Parse a pip/PEP 508 requirement string.
30
+
31
+ Returns ``(name, extras, version_spec, marker)`` where ``version_spec``
32
+ includes the operator (e.g. ``">=1.2.3"``) or is empty when unspecified,
33
+ and ``marker`` is the environment marker without the leading semicolon.
34
+ """
35
+ req = requirement.strip()
36
+
37
+ marker = ""
38
+ if ";" in req:
39
+ req, marker = req.split(";", 1)
40
+ req = req.strip()
41
+ marker = marker.strip()
42
+
43
+ version_spec = ""
44
+ split_at = len(req)
45
+ for op in _PEP440_OPERATORS:
46
+ idx = req.find(op)
47
+ if idx != -1 and idx < split_at:
48
+ split_at = idx
49
+ if split_at < len(req):
50
+ version_spec = req[split_at:].strip()
51
+ req = req[:split_at].strip()
52
+
53
+ extras: List[str] = []
54
+ if "[" in req and req.endswith("]"):
55
+ bare_name, _, extras_part = req.partition("[")
56
+ extras = [
57
+ extra.strip() for extra in extras_part[:-1].split(",") if extra.strip()
58
+ ]
59
+ req = bare_name.strip()
60
+
61
+ return req, extras, version_spec, marker
62
+
22
63
 
23
64
  class PoetryManager(BasePackageManager):
24
65
  """Poetry package manager implementation."""
@@ -182,22 +223,42 @@ build-backend = "poetry.core.masonry.api"
182
223
  pyproject_path = self.get_dependency_file_path()
183
224
 
184
225
  try:
185
- # Create dependencies section for Poetry
226
+ # Create dependencies section for Poetry. Parse each requirement as
227
+ # PEP 508 so non-``==`` specifiers (``>=``, ``~=``, ...), extras, and
228
+ # environment markers all round-trip to valid TOML.
186
229
  deps_section = ""
187
230
  for dep in dependencies:
188
- # Convert pip-style to poetry-style
189
- if "==" in dep:
190
- name, version = dep.split("==", 1)
191
- deps_section += f'{name} = "{version}"\n'
231
+ name_part, extras, version_spec, marker = _parse_pip_requirement(dep)
232
+ if not name_part:
233
+ continue
234
+
235
+ if not version_spec:
236
+ version_str = "*"
237
+ elif version_spec.startswith("=="):
238
+ # Poetry treats a bare version as a pin; keep the old
239
+ # formatting to avoid churn in generated files.
240
+ version_str = version_spec[2:].strip()
241
+ else:
242
+ version_str = version_spec
243
+
244
+ if extras or marker:
245
+ parts = [f'version = "{version_str}"']
246
+ if extras:
247
+ extras_str = ", ".join(f'"{extra}"' for extra in extras)
248
+ parts.append(f"extras = [{extras_str}]")
249
+ if marker:
250
+ escaped_marker = marker.replace('"', '\\"')
251
+ parts.append(f'markers = "{escaped_marker}"')
252
+ deps_section += f"{name_part} = {{{', '.join(parts)}}}\n"
192
253
  else:
193
- deps_section += f'{dep} = "*"\n'
254
+ deps_section += f'{name_part} = "{version_str}"\n'
194
255
 
195
256
  # Create basic pyproject.toml content for Poetry
196
257
  pyproject_content = f"""[tool.poetry]
197
- name = "{project_name or 'fastapi-project'}"
258
+ name = "{project_name or "fastapi-project"}"
198
259
  version = "0.1.0"
199
- description = "{description or 'A FastAPI project'}"
200
- authors = ["{author or 'Author'} <{author_email or 'author@example.com'}>"]
260
+ description = "{description or "A FastAPI project"}"
261
+ authors = ["{author or "Author"} <{author_email or "author@example.com"}>"]
201
262
  readme = "README.md"
202
263
  license = "MIT"
203
264
  packages = [{{include = "src"}}]
@@ -160,11 +160,11 @@ class UvManager(BasePackageManager):
160
160
 
161
161
  # Create basic pyproject.toml content for UV
162
162
  pyproject_content = f"""[project]
163
- name = "{project_name or 'fastapi-project'}"
163
+ name = "{project_name or "fastapi-project"}"
164
164
  version = "0.1.0"
165
- description = "{description or 'A FastAPI project'}"
165
+ description = "{description or "A FastAPI project"}"
166
166
  authors = [
167
- {{name = "{author or 'Author'}", email = "{author_email or 'author@example.com'}"}},
167
+ {{name = "{author or "Author"}", email = "{author_email or "author@example.com"}"}},
168
168
  ]
169
169
  dependencies = {deps_toml}
170
170
  requires-python = ">=3.8"
@@ -10,8 +10,11 @@
10
10
  # --------------------------------------------------------------------------
11
11
  from .config_generator import DynamicConfigGenerator
12
12
  from .dependency_collector import DependencyCollector
13
+ from .preset_layout import PresetLayoutStrategist, PresetProfile
13
14
 
14
15
  __all__ = [
15
16
  "DependencyCollector",
16
17
  "DynamicConfigGenerator",
18
+ "PresetLayoutStrategist",
19
+ "PresetProfile",
17
20
  ]
@@ -10,7 +10,6 @@
10
10
  #
11
11
  # @author bnbong bbbong9@gmail.com
12
12
  # --------------------------------------------------------------------------
13
- import os
14
13
  from pathlib import Path
15
14
  from typing import Any, Dict, List, Optional
16
15
 
@@ -179,11 +178,11 @@ class DynamicConfigGenerator:
179
178
  # App initialization
180
179
  project_name = self.config.get("project_name", "FastAPI App")
181
180
  description = self.config.get("description", "")
182
- content_parts.append(f"app = FastAPI(")
181
+ content_parts.append("app = FastAPI(")
183
182
  content_parts.append(f' title="{project_name}",')
184
183
  content_parts.append(f' description="{description}",')
185
- content_parts.append(f' version="0.1.0",')
186
- content_parts.append(f")")
184
+ content_parts.append(' version="0.1.0",')
185
+ content_parts.append(")")
187
186
  content_parts.append("")
188
187
 
189
188
  # App configuration
@@ -448,12 +447,20 @@ class DynamicConfigGenerator:
448
447
 
449
448
  return "\n".join(content)
450
449
 
451
- def generate_docker_files(self) -> None:
452
- """Generate Dockerfile and docker-compose.yml."""
450
+ def generate_docker_files(self, app_module: str = "src.main:app") -> None:
451
+ """Generate Dockerfile and docker-compose.yml.
452
+
453
+ The ``app_module`` is the ``module:attr`` string baked into the
454
+ Dockerfile's ``CMD`` (and into docker-compose's `command` if the
455
+ compose generator ever needs it). Architecture presets that put
456
+ the FastAPI app at a non-default location (e.g. ``domain-starter``
457
+ ships ``src/app/main.py``) must pass the matching dotted path so
458
+ the generated container actually starts.
459
+ """
453
460
  deployment = self.config.get("deployment", [])
454
461
 
455
462
  if "Docker" in deployment:
456
- dockerfile_content = self._generate_dockerfile()
463
+ dockerfile_content = self._generate_dockerfile(app_module=app_module)
457
464
  dockerfile_path = self.project_dir / "Dockerfile"
458
465
  with open(dockerfile_path, "w") as f:
459
466
  f.write(dockerfile_content)
@@ -464,8 +471,8 @@ class DynamicConfigGenerator:
464
471
  with open(compose_path, "w") as f:
465
472
  f.write(compose_content)
466
473
 
467
- def _generate_dockerfile(self) -> str:
468
- """Generate Dockerfile content."""
474
+ def _generate_dockerfile(self, app_module: str = "src.main:app") -> str:
475
+ """Generate Dockerfile content with a layout-aware uvicorn target."""
469
476
  content = []
470
477
  content.append(
471
478
  "# --------------------------------------------------------------------------"
@@ -488,7 +495,7 @@ class DynamicConfigGenerator:
488
495
  content.append("")
489
496
  content.append("# Run application")
490
497
  content.append(
491
- "CMD ['uvicorn', 'src.main:app', '--host', '0.0.0.0', '--port', '8000']"
498
+ f'CMD ["uvicorn", "{app_module}", "--host", "0.0.0.0", "--port", "8000"]'
492
499
  )
493
500
  content.append("")
494
501
 
@@ -0,0 +1,203 @@
1
+ # --------------------------------------------------------------------------
2
+ # Architecture-preset layout strategy for interactive project generation.
3
+ #
4
+ # Maps each architecture preset (issue #44) to the actual generation
5
+ # decisions interactive ``init`` makes:
6
+ #
7
+ # - which template ships as the base scaffold,
8
+ # - whether the dynamic ``main.py`` overlay should overwrite the shipped one,
9
+ # - where database / auth config files should land so they sit next to the
10
+ # template's existing structure rather than in a parallel ``src/config``,
11
+ # - and which feature combinations need a "you must wire this up manually"
12
+ # warning because the dynamic ``main.py`` overlay isn't applied.
13
+ #
14
+ # Keeping every preset's layout knowledge in one place lets the CLI flow stay
15
+ # linear ("ask the strategist where to write the file") instead of growing a
16
+ # branching maze of preset-specific if/else blocks.
17
+ #
18
+ # @author bnbong bbbong9@gmail.com
19
+ # --------------------------------------------------------------------------
20
+ from __future__ import annotations
21
+
22
+ from dataclasses import dataclass, field
23
+ from pathlib import Path
24
+ from typing import Any, Dict, List, Tuple
25
+
26
+ # Canonical preset id used when a caller doesn't supply one. Picked to
27
+ # preserve pre-#45 behaviour: interactive ``init`` historically deployed
28
+ # ``fastapi-empty`` and regenerated ``src/main.py`` from feature flags.
29
+ _FALLBACK_PRESET_ID: str = "minimal"
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ class PresetProfile:
34
+ """Per-preset generation decisions. Treat as a value object."""
35
+
36
+ preset_id: str
37
+ base_template: str
38
+ regenerate_main: bool
39
+ main_py_relpath: str
40
+ db_config_relpath: str
41
+ auth_config_relpath: str
42
+ # Hint shown when ``regenerate_main`` is False and the user picked a
43
+ # feature whose dynamic main.py overlay won't run. Empty string means
44
+ # "no special note for this preset".
45
+ manual_wiring_note: str = ""
46
+ extra_warning_targets: Tuple[str, ...] = field(default_factory=tuple)
47
+
48
+
49
+ _PRESET_PROFILES: Dict[str, PresetProfile] = {
50
+ "minimal": PresetProfile(
51
+ preset_id="minimal",
52
+ base_template="fastapi-empty",
53
+ regenerate_main=True,
54
+ main_py_relpath="src/main.py",
55
+ db_config_relpath="src/config/database.py",
56
+ auth_config_relpath="src/config/auth.py",
57
+ ),
58
+ "single-module": PresetProfile(
59
+ preset_id="single-module",
60
+ base_template="fastapi-single-module",
61
+ regenerate_main=True,
62
+ main_py_relpath="src/main.py",
63
+ db_config_relpath="src/config/database.py",
64
+ auth_config_relpath="src/config/auth.py",
65
+ ),
66
+ "classic-layered": PresetProfile(
67
+ preset_id="classic-layered",
68
+ base_template="fastapi-default",
69
+ regenerate_main=False,
70
+ main_py_relpath="src/main.py",
71
+ db_config_relpath="src/core/database.py",
72
+ auth_config_relpath="src/core/auth.py",
73
+ # CORS is intentionally NOT in this list: fastapi-default's shipped
74
+ # main.py already imports CORSMiddleware and adds it conditionally
75
+ # on settings.all_cors_origins, so the user only has to populate
76
+ # BACKEND_CORS_ORIGINS in .env — no code edits needed.
77
+ manual_wiring_note=(
78
+ "fastapi-default's shipped src/main.py is preserved. The "
79
+ "selections below need manual wiring there (CORS is already "
80
+ "wired — set BACKEND_CORS_ORIGINS in .env to activate it)."
81
+ ),
82
+ extra_warning_targets=("Rate-Limiting", "Prometheus"),
83
+ ),
84
+ "domain-starter": PresetProfile(
85
+ preset_id="domain-starter",
86
+ base_template="fastapi-domain-starter",
87
+ regenerate_main=False,
88
+ main_py_relpath="src/app/main.py",
89
+ db_config_relpath="src/app/core/database.py",
90
+ auth_config_relpath="src/app/core/auth.py",
91
+ manual_wiring_note=(
92
+ "fastapi-domain-starter's shipped src/app/main.py is preserved. "
93
+ "The selections below need manual wiring there (CORS is already "
94
+ "wired — set BACKEND_CORS_ORIGINS in .env to activate it)."
95
+ ),
96
+ extra_warning_targets=("Rate-Limiting", "Prometheus"),
97
+ ),
98
+ }
99
+
100
+
101
+ class PresetLayoutStrategist:
102
+ """Single source of truth for preset → generation-layout decisions."""
103
+
104
+ def __init__(self, preset_id: str | None) -> None:
105
+ # Empty / None / unknown ids fall back to ``minimal`` so older callers
106
+ # that pre-date the architecture-preset prompt keep working.
107
+ canonical = (preset_id or _FALLBACK_PRESET_ID).strip()
108
+ self.profile: PresetProfile = _PRESET_PROFILES.get(
109
+ canonical, _PRESET_PROFILES[_FALLBACK_PRESET_ID]
110
+ )
111
+
112
+ @classmethod
113
+ def supported_presets(cls) -> List[str]:
114
+ """Return the ordered list of preset ids the strategist understands."""
115
+ return list(_PRESET_PROFILES.keys())
116
+
117
+ @property
118
+ def preset_id(self) -> str:
119
+ return self.profile.preset_id
120
+
121
+ @property
122
+ def base_template(self) -> str:
123
+ return self.profile.base_template
124
+
125
+ @property
126
+ def should_regenerate_main(self) -> bool:
127
+ return self.profile.regenerate_main
128
+
129
+ def main_py_target(self, project_dir: str) -> Path:
130
+ """Absolute path where the dynamic main.py overlay should land."""
131
+ return Path(project_dir) / self.profile.main_py_relpath
132
+
133
+ @property
134
+ def app_module(self) -> str:
135
+ """Return the ``module:attr`` string uvicorn / Docker should target.
136
+
137
+ Derived from ``main_py_relpath`` so docker generation, runserver,
138
+ and any future container-orchestration code all agree on the
139
+ entrypoint a given preset produces.
140
+ """
141
+ # Strip the trailing ``.py`` and convert path separators to dots.
142
+ relpath = self.profile.main_py_relpath
143
+ if relpath.endswith(".py"):
144
+ relpath = relpath[: -len(".py")]
145
+ module_part = relpath.replace("/", ".").replace("\\", ".")
146
+ return f"{module_part}:app"
147
+
148
+ def db_config_target(self, project_dir: str) -> Path:
149
+ """Absolute path for the generated database config module."""
150
+ return Path(project_dir) / self.profile.db_config_relpath
151
+
152
+ def auth_config_target(self, project_dir: str) -> Path:
153
+ """Absolute path for the generated authentication config module."""
154
+ return Path(project_dir) / self.profile.auth_config_relpath
155
+
156
+ def compatibility_warnings(self, config: Dict[str, Any]) -> List[str]:
157
+ """Return user-facing warnings for unsupported preset/feature mixes.
158
+
159
+ The dynamic ``main.py`` overlay (CORS middleware wiring, Prometheus
160
+ instrumentation, rate-limit hookup) only runs for presets that
161
+ regenerate ``main.py``. For the other presets we keep the
162
+ template-shipped ``main.py`` intact and surface a single warning
163
+ listing the affected features so users know to wire them up
164
+ themselves rather than assuming the package install was enough.
165
+ """
166
+ if self.profile.regenerate_main:
167
+ return []
168
+
169
+ affected = self._affected_overlay_targets(config)
170
+ if not affected:
171
+ return []
172
+
173
+ warnings: List[str] = []
174
+ if self.profile.manual_wiring_note:
175
+ warnings.append(self.profile.manual_wiring_note)
176
+ warnings.append(
177
+ "Affected selections (packages installed, but no dynamic main.py "
178
+ "edits applied for the '"
179
+ + self.profile.preset_id
180
+ + "' preset): "
181
+ + ", ".join(affected)
182
+ )
183
+ return warnings
184
+
185
+ def _affected_overlay_targets(self, config: Dict[str, Any]) -> List[str]:
186
+ """Detect which of the user's selections rely on main.py overlay."""
187
+ triggered: List[str] = []
188
+ utilities = set(config.get("utilities") or [])
189
+
190
+ for target in self.profile.extra_warning_targets:
191
+ if target in {"CORS", "Rate-Limiting"}:
192
+ if target in utilities:
193
+ triggered.append(target)
194
+ elif target == "Prometheus":
195
+ if config.get("monitoring") == "Prometheus":
196
+ triggered.append(target)
197
+ return triggered
198
+
199
+
200
+ __all__ = [
201
+ "PresetLayoutStrategist",
202
+ "PresetProfile",
203
+ ]
@@ -10,7 +10,6 @@ import os
10
10
  import shutil
11
11
  from typing import Dict, Optional
12
12
 
13
- from fastapi_fastkit.core.settings import settings
14
13
  from fastapi_fastkit.utils.logging import debug_log, get_logger
15
14
 
16
15
  logger = get_logger(__name__)