python-infrakit-dev 0.1.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.
- infrakit/__init__.py +0 -0
- infrakit/cli/__init__.py +1 -0
- infrakit/cli/commands/__init__.py +1 -0
- infrakit/cli/commands/deps.py +530 -0
- infrakit/cli/commands/init.py +129 -0
- infrakit/cli/commands/llm.py +295 -0
- infrakit/cli/commands/logger.py +160 -0
- infrakit/cli/commands/module.py +342 -0
- infrakit/cli/commands/time.py +81 -0
- infrakit/cli/main.py +65 -0
- infrakit/core/__init__.py +0 -0
- infrakit/core/config/__init__.py +0 -0
- infrakit/core/config/converter.py +480 -0
- infrakit/core/config/exporter.py +304 -0
- infrakit/core/config/loader.py +713 -0
- infrakit/core/config/validator.py +389 -0
- infrakit/core/logger/__init__.py +21 -0
- infrakit/core/logger/formatters.py +143 -0
- infrakit/core/logger/handlers.py +322 -0
- infrakit/core/logger/retention.py +176 -0
- infrakit/core/logger/setup.py +314 -0
- infrakit/deps/__init__.py +239 -0
- infrakit/deps/clean.py +141 -0
- infrakit/deps/depfile.py +405 -0
- infrakit/deps/health.py +357 -0
- infrakit/deps/optimizer.py +642 -0
- infrakit/deps/scanner.py +550 -0
- infrakit/llm/__init__.py +35 -0
- infrakit/llm/batch.py +165 -0
- infrakit/llm/client.py +575 -0
- infrakit/llm/key_manager.py +728 -0
- infrakit/llm/llm_readme.md +306 -0
- infrakit/llm/models.py +148 -0
- infrakit/llm/providers/__init__.py +5 -0
- infrakit/llm/providers/base.py +112 -0
- infrakit/llm/providers/gemini.py +164 -0
- infrakit/llm/providers/openai.py +168 -0
- infrakit/llm/rate_limiter.py +54 -0
- infrakit/scaffolder/__init__.py +31 -0
- infrakit/scaffolder/ai.py +508 -0
- infrakit/scaffolder/backend.py +555 -0
- infrakit/scaffolder/cli_tool.py +386 -0
- infrakit/scaffolder/generator.py +338 -0
- infrakit/scaffolder/pipeline.py +562 -0
- infrakit/scaffolder/registry.py +121 -0
- infrakit/time/__init__.py +60 -0
- infrakit/time/profiler.py +511 -0
- python_infrakit_dev-0.1.0.dist-info/METADATA +124 -0
- python_infrakit_dev-0.1.0.dist-info/RECORD +51 -0
- python_infrakit_dev-0.1.0.dist-info/WHEEL +4 -0
- python_infrakit_dev-0.1.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
"""
|
|
2
|
+
infrakit.scaffolder.templates.backend
|
|
3
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
4
|
+
Scaffold a FastAPI backend service.
|
|
5
|
+
|
|
6
|
+
Layout
|
|
7
|
+
------
|
|
8
|
+
<project>/
|
|
9
|
+
├── app/
|
|
10
|
+
│ ├── __init__.py
|
|
11
|
+
│ ├── main.py # FastAPI app factory + lifespan
|
|
12
|
+
│ ├── config.py # Pydantic Settings — reads from env
|
|
13
|
+
│ ├── dependencies.py # Shared FastAPI dependencies (db, auth, llm)
|
|
14
|
+
│ ├── routes/
|
|
15
|
+
│ │ ├── __init__.py
|
|
16
|
+
│ │ └── health.py # GET /health — always included
|
|
17
|
+
│ ├── models/
|
|
18
|
+
│ │ ├── __init__.py
|
|
19
|
+
│ │ └── base.py # SQLAlchemy / Pydantic base stubs
|
|
20
|
+
│ ├── services/
|
|
21
|
+
│ │ ├── __init__.py
|
|
22
|
+
│ │ └── llm_service.py # thin service wrapper around utils.llm
|
|
23
|
+
│ └── middleware/
|
|
24
|
+
│ ├── __init__.py
|
|
25
|
+
│ └── logging.py # request-level access log middleware
|
|
26
|
+
├── utils/
|
|
27
|
+
│ ├── __init__.py
|
|
28
|
+
│ ├── logger.py
|
|
29
|
+
│ └── llm.py # infrakit.llm singleton (optional)
|
|
30
|
+
├── tests/
|
|
31
|
+
│ ├── __init__.py
|
|
32
|
+
│ └── test_health.py
|
|
33
|
+
├── logs/
|
|
34
|
+
├── Dockerfile
|
|
35
|
+
├── docker-compose.yml
|
|
36
|
+
├── pyproject.toml / requirements.txt
|
|
37
|
+
├── config.{env|yaml|json}
|
|
38
|
+
├── README.md
|
|
39
|
+
└── .gitignore
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
from pathlib import Path
|
|
45
|
+
|
|
46
|
+
from infrakit.scaffolder.generator import (
|
|
47
|
+
ScaffoldResult,
|
|
48
|
+
_mkdir,
|
|
49
|
+
_write,
|
|
50
|
+
_config_content,
|
|
51
|
+
_gitignore,
|
|
52
|
+
_logger_util,
|
|
53
|
+
_src_init,
|
|
54
|
+
_tests_init,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# ── template content ──────────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _app_init(version: str) -> str:
|
|
62
|
+
return f'__version__ = "{version}"\n'
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _app_config(project_name: str) -> str:
|
|
66
|
+
return f'''\
|
|
67
|
+
"""
|
|
68
|
+
app.config
|
|
69
|
+
~~~~~~~~~~
|
|
70
|
+
All configuration is read from environment variables (12-factor style).
|
|
71
|
+
|
|
72
|
+
Usage
|
|
73
|
+
-----
|
|
74
|
+
from app.config import settings
|
|
75
|
+
|
|
76
|
+
print(settings.app_env)
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class Settings(BaseSettings):
|
|
83
|
+
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
|
|
84
|
+
|
|
85
|
+
# application
|
|
86
|
+
app_name: str = "{project_name}"
|
|
87
|
+
app_env: str = "development"
|
|
88
|
+
app_debug: bool = False
|
|
89
|
+
app_host: str = "0.0.0.0"
|
|
90
|
+
app_port: int = 8000
|
|
91
|
+
|
|
92
|
+
# optional: database
|
|
93
|
+
database_url: str = "sqlite:///./app.db"
|
|
94
|
+
|
|
95
|
+
# optional: LLM keys (override ~/.infrakit/llm/ defaults)
|
|
96
|
+
openai_api_key: str = ""
|
|
97
|
+
gemini_api_key: str = ""
|
|
98
|
+
llm_mode: str = "async"
|
|
99
|
+
llm_concurrency: int = 3
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
settings = Settings()
|
|
103
|
+
'''
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _app_main(project_name: str) -> str:
|
|
107
|
+
title = project_name.replace("-", " ").replace("_", " ").title()
|
|
108
|
+
return f'''\
|
|
109
|
+
"""
|
|
110
|
+
app.main
|
|
111
|
+
~~~~~~~~
|
|
112
|
+
FastAPI application factory.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
from contextlib import asynccontextmanager
|
|
116
|
+
|
|
117
|
+
from fastapi import FastAPI
|
|
118
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
119
|
+
|
|
120
|
+
from app.middleware.logging import AccessLogMiddleware
|
|
121
|
+
from app.routes import health
|
|
122
|
+
from utils.logger import get_logger
|
|
123
|
+
|
|
124
|
+
log = get_logger(__name__)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@asynccontextmanager
|
|
128
|
+
async def lifespan(app: FastAPI):
|
|
129
|
+
log.info("startup: {project_name}")
|
|
130
|
+
yield
|
|
131
|
+
log.info("shutdown: {project_name}")
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def create_app() -> FastAPI:
|
|
135
|
+
app = FastAPI(
|
|
136
|
+
title="{title}",
|
|
137
|
+
lifespan=lifespan,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
app.add_middleware(
|
|
141
|
+
CORSMiddleware,
|
|
142
|
+
allow_origins=["*"],
|
|
143
|
+
allow_methods=["*"],
|
|
144
|
+
allow_headers=["*"],
|
|
145
|
+
)
|
|
146
|
+
app.add_middleware(AccessLogMiddleware)
|
|
147
|
+
|
|
148
|
+
app.include_router(health.router)
|
|
149
|
+
|
|
150
|
+
return app
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
app = create_app()
|
|
154
|
+
'''
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _app_dependencies() -> str:
|
|
158
|
+
return '''\
|
|
159
|
+
"""
|
|
160
|
+
app.dependencies
|
|
161
|
+
~~~~~~~~~~~~~~~~
|
|
162
|
+
Shared FastAPI dependency-injection helpers.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
from fastapi import Header, HTTPException
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
async def get_api_key(x_api_key: str = Header(...)) -> str:
|
|
169
|
+
"""Placeholder API-key auth dependency."""
|
|
170
|
+
if not x_api_key:
|
|
171
|
+
raise HTTPException(status_code=401, detail="Missing API key")
|
|
172
|
+
return x_api_key
|
|
173
|
+
'''
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _route_health() -> str:
|
|
177
|
+
return '''\
|
|
178
|
+
"""
|
|
179
|
+
app.routes.health
|
|
180
|
+
~~~~~~~~~~~~~~~~~
|
|
181
|
+
GET /health — liveness probe.
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
from fastapi import APIRouter
|
|
185
|
+
from pydantic import BaseModel
|
|
186
|
+
|
|
187
|
+
router = APIRouter(tags=["health"])
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class HealthResponse(BaseModel):
|
|
191
|
+
status: str
|
|
192
|
+
version: str
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@router.get("/health", response_model=HealthResponse)
|
|
196
|
+
async def health() -> HealthResponse:
|
|
197
|
+
from app import __version__
|
|
198
|
+
return HealthResponse(status="ok", version=__version__)
|
|
199
|
+
'''
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _models_base() -> str:
|
|
203
|
+
return '''\
|
|
204
|
+
"""
|
|
205
|
+
app.models.base
|
|
206
|
+
~~~~~~~~~~~~~~~
|
|
207
|
+
Shared base classes for ORM models and Pydantic schemas.
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
from pydantic import BaseModel, ConfigDict
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class APIModel(BaseModel):
|
|
214
|
+
"""Base class for all API request/response schemas."""
|
|
215
|
+
model_config = ConfigDict(from_attributes=True)
|
|
216
|
+
'''
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _services_llm() -> str:
|
|
220
|
+
return '''\
|
|
221
|
+
"""
|
|
222
|
+
app.services.llm_service
|
|
223
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
224
|
+
Thin async service wrapper around the infrakit LLM client.
|
|
225
|
+
|
|
226
|
+
Keeps route handlers clean — they call this service, never the LLM client directly.
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
from pydantic import BaseModel
|
|
230
|
+
from typing import Optional, Type
|
|
231
|
+
|
|
232
|
+
from utils.llm import llm, Prompt
|
|
233
|
+
from utils.logger import get_logger
|
|
234
|
+
|
|
235
|
+
log = get_logger(__name__)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
async def generate(
|
|
239
|
+
user: str,
|
|
240
|
+
*,
|
|
241
|
+
system: Optional[str] = None,
|
|
242
|
+
provider: str = "openai",
|
|
243
|
+
response_model: Optional[Type[BaseModel]] = None,
|
|
244
|
+
):
|
|
245
|
+
"""
|
|
246
|
+
Single-prompt async generate.
|
|
247
|
+
|
|
248
|
+
Returns the LLMResponse — callers should check .error before using .content.
|
|
249
|
+
"""
|
|
250
|
+
prompt = Prompt(user=user, system=system)
|
|
251
|
+
response = await llm.async_generate(prompt, provider=provider,
|
|
252
|
+
response_model=response_model)
|
|
253
|
+
if response.error:
|
|
254
|
+
log.warning("llm_service: error — %s", response.error)
|
|
255
|
+
return response
|
|
256
|
+
'''
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _middleware_logging() -> str:
|
|
260
|
+
return '''\
|
|
261
|
+
"""
|
|
262
|
+
app.middleware.logging
|
|
263
|
+
~~~~~~~~~~~~~~~~~~~~~~
|
|
264
|
+
Minimal access-log middleware — logs method, path, status, and latency.
|
|
265
|
+
"""
|
|
266
|
+
|
|
267
|
+
import time
|
|
268
|
+
|
|
269
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
270
|
+
from starlette.requests import Request
|
|
271
|
+
|
|
272
|
+
from utils.logger import get_logger
|
|
273
|
+
|
|
274
|
+
log = get_logger("access")
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class AccessLogMiddleware(BaseHTTPMiddleware):
|
|
278
|
+
async def dispatch(self, request: Request, call_next):
|
|
279
|
+
t0 = time.perf_counter()
|
|
280
|
+
response = await call_next(request)
|
|
281
|
+
ms = (time.perf_counter() - t0) * 1000
|
|
282
|
+
log.info(
|
|
283
|
+
"%s %s %d %.0fms",
|
|
284
|
+
request.method,
|
|
285
|
+
request.url.path,
|
|
286
|
+
response.status_code,
|
|
287
|
+
ms,
|
|
288
|
+
)
|
|
289
|
+
return response
|
|
290
|
+
'''
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _test_health() -> str:
|
|
294
|
+
return '''\
|
|
295
|
+
"""tests.test_health — basic liveness check."""
|
|
296
|
+
|
|
297
|
+
from fastapi.testclient import TestClient
|
|
298
|
+
|
|
299
|
+
from app.main import app
|
|
300
|
+
|
|
301
|
+
client = TestClient(app)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def test_health_ok():
|
|
305
|
+
response = client.get("/health")
|
|
306
|
+
assert response.status_code == 200
|
|
307
|
+
data = response.json()
|
|
308
|
+
assert data["status"] == "ok"
|
|
309
|
+
assert "version" in data
|
|
310
|
+
'''
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _dockerfile(project_name: str) -> str:
|
|
314
|
+
return f"""\
|
|
315
|
+
FROM python:3.11-slim
|
|
316
|
+
|
|
317
|
+
WORKDIR /app
|
|
318
|
+
|
|
319
|
+
COPY pyproject.toml .
|
|
320
|
+
RUN pip install --no-cache-dir -e .
|
|
321
|
+
|
|
322
|
+
COPY . .
|
|
323
|
+
|
|
324
|
+
EXPOSE 8000
|
|
325
|
+
|
|
326
|
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
327
|
+
"""
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def _docker_compose(project_name: str) -> str:
|
|
331
|
+
return f"""\
|
|
332
|
+
version: "3.9"
|
|
333
|
+
|
|
334
|
+
services:
|
|
335
|
+
api:
|
|
336
|
+
build: .
|
|
337
|
+
container_name: {project_name}
|
|
338
|
+
ports:
|
|
339
|
+
- "8000:8000"
|
|
340
|
+
env_file:
|
|
341
|
+
- .env
|
|
342
|
+
volumes:
|
|
343
|
+
- ./logs:/app/logs
|
|
344
|
+
restart: unless-stopped
|
|
345
|
+
"""
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def _backend_pyproject(
|
|
349
|
+
project_name: str, version: str, description: str, author: str
|
|
350
|
+
) -> str:
|
|
351
|
+
author_line = f' "{author}",' if author else ' # "Your Name <you@example.com>",'
|
|
352
|
+
return f"""\
|
|
353
|
+
[project]
|
|
354
|
+
name = "{project_name}"
|
|
355
|
+
version = "{version}"
|
|
356
|
+
description = "{description}"
|
|
357
|
+
readme = "README.md"
|
|
358
|
+
requires-python = ">=3.10"
|
|
359
|
+
authors = [
|
|
360
|
+
{author_line}
|
|
361
|
+
]
|
|
362
|
+
|
|
363
|
+
dependencies = [
|
|
364
|
+
"infrakit",
|
|
365
|
+
"fastapi>=0.110",
|
|
366
|
+
"uvicorn[standard]",
|
|
367
|
+
"pydantic>=2.0",
|
|
368
|
+
"pydantic-settings",
|
|
369
|
+
"openai",
|
|
370
|
+
"google-generativeai",
|
|
371
|
+
"tqdm",
|
|
372
|
+
]
|
|
373
|
+
|
|
374
|
+
[project.optional-dependencies]
|
|
375
|
+
dev = [
|
|
376
|
+
"pytest",
|
|
377
|
+
"pytest-cov",
|
|
378
|
+
"httpx", # required by TestClient
|
|
379
|
+
]
|
|
380
|
+
|
|
381
|
+
[project.scripts]
|
|
382
|
+
serve = "{project_name}.app.main:app"
|
|
383
|
+
"""
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def _backend_readme(project_name: str, description: str) -> str:
|
|
387
|
+
title = project_name.replace("-", " ").replace("_", " ").title()
|
|
388
|
+
desc_line = f"\n{description}\n" if description else ""
|
|
389
|
+
return f"""\
|
|
390
|
+
# {title}
|
|
391
|
+
{desc_line}
|
|
392
|
+
## Setup
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
pip install -e .
|
|
396
|
+
cp .env .env # fill in secrets
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
## Run
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
uvicorn app.main:app --reload
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
Or with Docker:
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
docker-compose up --build
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## Endpoints
|
|
412
|
+
|
|
413
|
+
| Method | Path | Description |
|
|
414
|
+
|---|---|---|
|
|
415
|
+
| GET | `/health` | Liveness probe |
|
|
416
|
+
|
|
417
|
+
## Structure
|
|
418
|
+
|
|
419
|
+
| Path | Purpose |
|
|
420
|
+
|---|---|
|
|
421
|
+
| `app/main.py` | FastAPI app factory |
|
|
422
|
+
| `app/config.py` | Pydantic Settings (env-driven) |
|
|
423
|
+
| `app/routes/` | Route handlers |
|
|
424
|
+
| `app/models/` | ORM / schema models |
|
|
425
|
+
| `app/services/` | Business logic |
|
|
426
|
+
| `app/middleware/` | Request middleware |
|
|
427
|
+
| `utils/llm.py` | LLM client singleton |
|
|
428
|
+
| `utils/logger.py` | Logger singleton |
|
|
429
|
+
|
|
430
|
+
## Development
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
pip install -e ".[dev]"
|
|
434
|
+
pytest
|
|
435
|
+
```
|
|
436
|
+
"""
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
def _backend_gitignore() -> str:
|
|
440
|
+
return _gitignore() + """\
|
|
441
|
+
# Docker
|
|
442
|
+
.dockerignore
|
|
443
|
+
|
|
444
|
+
# Database
|
|
445
|
+
*.db
|
|
446
|
+
*.sqlite3
|
|
447
|
+
|
|
448
|
+
# Keys
|
|
449
|
+
.env
|
|
450
|
+
keys.json
|
|
451
|
+
"""
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
# ── public API ────────────────────────────────────────────────────────────────
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def scaffold_backend(
|
|
458
|
+
project_dir: Path,
|
|
459
|
+
*,
|
|
460
|
+
version: str = "0.1.0",
|
|
461
|
+
description: str = "",
|
|
462
|
+
author: str = "",
|
|
463
|
+
config_fmt: str = "env",
|
|
464
|
+
deps: str = "toml",
|
|
465
|
+
include_llm: bool = True,
|
|
466
|
+
) -> ScaffoldResult:
|
|
467
|
+
"""
|
|
468
|
+
Scaffold a FastAPI backend service under ``project_dir``.
|
|
469
|
+
|
|
470
|
+
Parameters
|
|
471
|
+
----------
|
|
472
|
+
project_dir:
|
|
473
|
+
Root directory for the project.
|
|
474
|
+
version:
|
|
475
|
+
Starting version string.
|
|
476
|
+
description:
|
|
477
|
+
Short project description.
|
|
478
|
+
author:
|
|
479
|
+
Author string.
|
|
480
|
+
config_fmt:
|
|
481
|
+
Config file format — ``"env"``, ``"yaml"``, or ``"json"``.
|
|
482
|
+
deps:
|
|
483
|
+
``"toml"`` or ``"requirements"``.
|
|
484
|
+
include_llm:
|
|
485
|
+
Whether to include ``utils/llm.py`` and ``app/services/llm_service.py``.
|
|
486
|
+
"""
|
|
487
|
+
result = ScaffoldResult(project_dir=project_dir)
|
|
488
|
+
project_name = project_dir.name
|
|
489
|
+
|
|
490
|
+
# ── directories ───────────────────────────────────────────────────────────
|
|
491
|
+
_mkdir(result, project_dir)
|
|
492
|
+
_mkdir(result, project_dir / "app")
|
|
493
|
+
_mkdir(result, project_dir / "app" / "routes")
|
|
494
|
+
_mkdir(result, project_dir / "app" / "models")
|
|
495
|
+
_mkdir(result, project_dir / "app" / "services")
|
|
496
|
+
_mkdir(result, project_dir / "app" / "middleware")
|
|
497
|
+
_mkdir(result, project_dir / "utils")
|
|
498
|
+
_mkdir(result, project_dir / "tests")
|
|
499
|
+
_mkdir(result, project_dir / "logs")
|
|
500
|
+
|
|
501
|
+
# ── app package ───────────────────────────────────────────────────────────
|
|
502
|
+
_write(result, project_dir / "app" / "__init__.py", _app_init(version))
|
|
503
|
+
_write(result, project_dir / "app" / "main.py", _app_main(project_name))
|
|
504
|
+
_write(result, project_dir / "app" / "config.py", _app_config(project_name))
|
|
505
|
+
_write(result, project_dir / "app" / "dependencies.py", _app_dependencies())
|
|
506
|
+
|
|
507
|
+
# ── routes ────────────────────────────────────────────────────────────────
|
|
508
|
+
_write(result, project_dir / "app" / "routes" / "__init__.py", "")
|
|
509
|
+
_write(result, project_dir / "app" / "routes" / "health.py", _route_health())
|
|
510
|
+
|
|
511
|
+
# ── models ────────────────────────────────────────────────────────────────
|
|
512
|
+
_write(result, project_dir / "app" / "models" / "__init__.py", "")
|
|
513
|
+
_write(result, project_dir / "app" / "models" / "base.py", _models_base())
|
|
514
|
+
|
|
515
|
+
# ── services ──────────────────────────────────────────────────────────────
|
|
516
|
+
_write(result, project_dir / "app" / "services" / "__init__.py", "")
|
|
517
|
+
if include_llm:
|
|
518
|
+
_write(result, project_dir / "app" / "services" / "llm_service.py",
|
|
519
|
+
_services_llm())
|
|
520
|
+
|
|
521
|
+
# ── middleware ────────────────────────────────────────────────────────────
|
|
522
|
+
_write(result, project_dir / "app" / "middleware" / "__init__.py", "")
|
|
523
|
+
_write(result, project_dir / "app" / "middleware" / "logging.py",
|
|
524
|
+
_middleware_logging())
|
|
525
|
+
|
|
526
|
+
# ── utils ─────────────────────────────────────────────────────────────────
|
|
527
|
+
_write(result, project_dir / "utils" / "__init__.py", '"""Shared utilities."""\n')
|
|
528
|
+
_write(result, project_dir / "utils" / "logger.py", _logger_util())
|
|
529
|
+
|
|
530
|
+
# ── tests ─────────────────────────────────────────────────────────────────
|
|
531
|
+
_write(result, project_dir / "tests" / "__init__.py", _tests_init())
|
|
532
|
+
_write(result, project_dir / "tests" / "test_health.py", _test_health())
|
|
533
|
+
|
|
534
|
+
# ── config ────────────────────────────────────────────────────────────────
|
|
535
|
+
cfg_name, cfg_content = _config_content(config_fmt)
|
|
536
|
+
_write(result, project_dir / cfg_name, cfg_content)
|
|
537
|
+
|
|
538
|
+
# ── docker ────────────────────────────────────────────────────────────────
|
|
539
|
+
_write(result, project_dir / "Dockerfile", _dockerfile(project_name))
|
|
540
|
+
_write(result, project_dir / "docker-compose.yml", _docker_compose(project_name))
|
|
541
|
+
|
|
542
|
+
# ── dependency file ───────────────────────────────────────────────────────
|
|
543
|
+
if deps == "requirements":
|
|
544
|
+
from infrakit.scaffolder.generator import _requirements_txt
|
|
545
|
+
_write(result, project_dir / "requirements.txt",
|
|
546
|
+
_requirements_txt(project_name))
|
|
547
|
+
else:
|
|
548
|
+
_write(result, project_dir / "pyproject.toml",
|
|
549
|
+
_backend_pyproject(project_name, version, description, author))
|
|
550
|
+
|
|
551
|
+
# ── repo files ────────────────────────────────────────────────────────────
|
|
552
|
+
_write(result, project_dir / "README.md", _backend_readme(project_name, description))
|
|
553
|
+
_write(result, project_dir / ".gitignore", _backend_gitignore())
|
|
554
|
+
|
|
555
|
+
return result
|