python-infrakit-dev 0.1.2__py3-none-any.whl → 0.1.4__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/scaffolder/ai.py +37 -16
- infrakit/scaffolder/backend.py +106 -2
- infrakit/scaffolder/cli_tool.py +1 -1
- infrakit/scaffolder/generator.py +93 -28
- infrakit/scaffolder/pipeline.py +1 -1
- python_infrakit_dev-0.1.4.dist-info/METADATA +318 -0
- {python_infrakit_dev-0.1.2.dist-info → python_infrakit_dev-0.1.4.dist-info}/RECORD +9 -9
- python_infrakit_dev-0.1.2.dist-info/METADATA +0 -124
- {python_infrakit_dev-0.1.2.dist-info → python_infrakit_dev-0.1.4.dist-info}/WHEEL +0 -0
- {python_infrakit_dev-0.1.2.dist-info → python_infrakit_dev-0.1.4.dist-info}/entry_points.txt +0 -0
infrakit/scaffolder/ai.py
CHANGED
|
@@ -58,9 +58,12 @@ utils.llm
|
|
|
58
58
|
~~~~~~~~~
|
|
59
59
|
Thin wrapper that boots the infrakit LLM client once and exports it.
|
|
60
60
|
|
|
61
|
+
Reads all configuration from the project config file (.env / config.yaml /
|
|
62
|
+
config.json) via infrakit.config — no raw os.getenv calls.
|
|
63
|
+
|
|
61
64
|
The client reads key state from ``~/.infrakit/llm/`` by default, and
|
|
62
65
|
loads quota limits from ``~/.infrakit/llm/quotas.json`` if that file
|
|
63
|
-
exists. Both paths can be overridden
|
|
66
|
+
exists. Both paths can be overridden via LLM_STATE_DIR / LLM_QUOTA_FILE.
|
|
64
67
|
|
|
65
68
|
Usage
|
|
66
69
|
-----
|
|
@@ -89,24 +92,38 @@ Usage
|
|
|
89
92
|
"""
|
|
90
93
|
|
|
91
94
|
import json
|
|
92
|
-
import os
|
|
93
95
|
from pathlib import Path
|
|
94
96
|
|
|
97
|
+
from infrakit.core.config.loader import load, load_env
|
|
95
98
|
from infrakit.llm import LLMClient, Prompt # re-export Prompt for convenience
|
|
96
99
|
|
|
100
|
+
# ── config loading ────────────────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
def _load_cfg() -> dict:
|
|
103
|
+
if Path(".env").exists():
|
|
104
|
+
return load_env(".env", cast_values=True)
|
|
105
|
+
if Path("config.yaml").exists():
|
|
106
|
+
return load("config.yaml")
|
|
107
|
+
if Path("config.json").exists():
|
|
108
|
+
return load("config.json")
|
|
109
|
+
return {{}}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
_cfg = _load_cfg()
|
|
113
|
+
|
|
97
114
|
# ── key loading ───────────────────────────────────────────────────────────────
|
|
98
|
-
# Keys are read from the
|
|
115
|
+
# Keys are read from the project config or from a local keys.json file.
|
|
99
116
|
# Never commit real API keys — use .env or your secret manager.
|
|
100
117
|
|
|
101
118
|
def _load_keys() -> dict:
|
|
102
|
-
keys_file = Path(
|
|
119
|
+
keys_file = Path(_cfg.get("LLM_KEYS_FILE", "keys.json"))
|
|
103
120
|
if keys_file.exists():
|
|
104
121
|
with open(keys_file) as f:
|
|
105
122
|
return json.load(f)
|
|
106
123
|
|
|
107
|
-
# fall back to
|
|
108
|
-
openai_key =
|
|
109
|
-
gemini_key =
|
|
124
|
+
# fall back to keys declared in the config file
|
|
125
|
+
openai_key = _cfg.get("OPENAI_API_KEY", "")
|
|
126
|
+
gemini_key = _cfg.get("GEMINI_API_KEY", "")
|
|
110
127
|
return {{
|
|
111
128
|
"openai_keys": [openai_key] if openai_key else [],
|
|
112
129
|
"gemini_keys": [gemini_key] if gemini_key else [],
|
|
@@ -118,13 +135,13 @@ def _load_keys() -> dict:
|
|
|
118
135
|
llm: LLMClient = LLMClient(
|
|
119
136
|
keys=_load_keys(),
|
|
120
137
|
# storage_dir and quota_file default to ~/.infrakit/llm/
|
|
121
|
-
# override
|
|
122
|
-
storage_dir=
|
|
123
|
-
quota_file=
|
|
124
|
-
mode=
|
|
125
|
-
max_concurrent=int(
|
|
126
|
-
openai_model=
|
|
127
|
-
gemini_model=
|
|
138
|
+
# override via LLM_STATE_DIR / LLM_QUOTA_FILE in your config file:
|
|
139
|
+
storage_dir=_cfg.get("LLM_STATE_DIR") or None,
|
|
140
|
+
quota_file=_cfg.get("LLM_QUOTA_FILE") or None,
|
|
141
|
+
mode=_cfg.get("LLM_MODE", "async"), # "async" | "threaded"
|
|
142
|
+
max_concurrent=int(_cfg.get("LLM_CONCURRENCY", 3)),
|
|
143
|
+
openai_model=_cfg.get("OPENAI_MODEL") or None,
|
|
144
|
+
gemini_model=_cfg.get("GEMINI_MODEL") or None,
|
|
128
145
|
)
|
|
129
146
|
|
|
130
147
|
__all__ = ["llm", "Prompt"]
|
|
@@ -474,10 +491,14 @@ def scaffold_ai(
|
|
|
474
491
|
_write(result, project_dir / "pipelines" / "preprocess.py", _pipeline_preprocess())
|
|
475
492
|
_write(result, project_dir / "pipelines" / "predict.py", _pipeline_predict())
|
|
476
493
|
|
|
494
|
+
# ── prompts ───────────────────────────────────────────────────────────────
|
|
495
|
+
_write(result, project_dir / "prompts" / "default.txt", _default_prompt())
|
|
496
|
+
|
|
477
497
|
# ── utils ─────────────────────────────────────────────────────────────────
|
|
478
498
|
_write(result, project_dir / "utils" / "__init__.py", '"""Shared utilities."""\n')
|
|
479
499
|
_write(result, project_dir / "utils" / "logger.py", _logger_util())
|
|
480
|
-
|
|
500
|
+
if include_llm:
|
|
501
|
+
_write(result, project_dir / "utils" / "llm.py", _llm_util(project_name))
|
|
481
502
|
|
|
482
503
|
# ── notebooks ─────────────────────────────────────────────────────────────
|
|
483
504
|
if include_notebooks:
|
|
@@ -488,7 +509,7 @@ def scaffold_ai(
|
|
|
488
509
|
_write(result, project_dir / "tests" / "__init__.py", _tests_init())
|
|
489
510
|
|
|
490
511
|
# ── config ────────────────────────────────────────────────────────────────
|
|
491
|
-
cfg_name, cfg_content = _config_content(config_fmt)
|
|
512
|
+
cfg_name, cfg_content = _config_content(config_fmt, include_llm=include_llm)
|
|
492
513
|
_write(result, project_dir / cfg_name, cfg_content)
|
|
493
514
|
|
|
494
515
|
# ── keys template (safe placeholder — never contains real keys) ───────────
|
infrakit/scaffolder/backend.py
CHANGED
|
@@ -47,7 +47,6 @@ from infrakit.scaffolder.generator import (
|
|
|
47
47
|
ScaffoldResult,
|
|
48
48
|
_mkdir,
|
|
49
49
|
_write,
|
|
50
|
-
_config_content,
|
|
51
50
|
_gitignore,
|
|
52
51
|
_logger_util,
|
|
53
52
|
_src_init,
|
|
@@ -58,6 +57,107 @@ from infrakit.scaffolder.generator import (
|
|
|
58
57
|
# ── template content ──────────────────────────────────────────────────────────
|
|
59
58
|
|
|
60
59
|
|
|
60
|
+
def _backend_env_config(project_name: str, include_llm: bool) -> str:
|
|
61
|
+
llm_block = """\
|
|
62
|
+
|
|
63
|
+
# LLM
|
|
64
|
+
LLM_KEYS_FILE=keys.json
|
|
65
|
+
OPENAI_API_KEY=
|
|
66
|
+
GEMINI_API_KEY=
|
|
67
|
+
LLM_MODE=async
|
|
68
|
+
LLM_CONCURRENCY=3
|
|
69
|
+
# OPENAI_MODEL=gpt-4o
|
|
70
|
+
# GEMINI_MODEL=gemini-2.0-flash
|
|
71
|
+
""" if include_llm else ""
|
|
72
|
+
return f"""\
|
|
73
|
+
# Application
|
|
74
|
+
APP_NAME={project_name}
|
|
75
|
+
APP_ENV=development
|
|
76
|
+
APP_DEBUG=false
|
|
77
|
+
APP_HOST=0.0.0.0
|
|
78
|
+
APP_PORT=8000
|
|
79
|
+
|
|
80
|
+
# Database
|
|
81
|
+
DATABASE_URL=sqlite:///./app.db
|
|
82
|
+
|
|
83
|
+
# Logger
|
|
84
|
+
LOG_DIR=logs
|
|
85
|
+
LOG_STRATEGY=date
|
|
86
|
+
LOG_STREAM=stdout
|
|
87
|
+
LOG_FORMAT=human
|
|
88
|
+
LOG_LEVEL=DEBUG
|
|
89
|
+
{llm_block}"""
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _backend_yaml_config(project_name: str, include_llm: bool) -> str:
|
|
93
|
+
llm_block = """
|
|
94
|
+
|
|
95
|
+
# LLM
|
|
96
|
+
LLM_KEYS_FILE: keys.json
|
|
97
|
+
OPENAI_API_KEY: ""
|
|
98
|
+
GEMINI_API_KEY: ""
|
|
99
|
+
LLM_MODE: async
|
|
100
|
+
LLM_CONCURRENCY: 3
|
|
101
|
+
# OPENAI_MODEL: gpt-4o
|
|
102
|
+
# GEMINI_MODEL: gemini-2.0-flash
|
|
103
|
+
""" if include_llm else ""
|
|
104
|
+
return f"""\
|
|
105
|
+
# Application configuration
|
|
106
|
+
app:
|
|
107
|
+
name: {project_name}
|
|
108
|
+
env: development
|
|
109
|
+
debug: false
|
|
110
|
+
host: 0.0.0.0
|
|
111
|
+
port: 8000
|
|
112
|
+
|
|
113
|
+
# Database
|
|
114
|
+
DATABASE_URL: sqlite:///./app.db
|
|
115
|
+
|
|
116
|
+
# Logger (flat keys — read by utils/logger.py via infrakit.config)
|
|
117
|
+
LOG_DIR: logs
|
|
118
|
+
LOG_STRATEGY: date
|
|
119
|
+
LOG_STREAM: stdout
|
|
120
|
+
LOG_FORMAT: human
|
|
121
|
+
LOG_LEVEL: DEBUG
|
|
122
|
+
{llm_block}"""
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _backend_json_config(project_name: str, include_llm: bool) -> str:
|
|
126
|
+
llm_keys = """,
|
|
127
|
+
"LLM_KEYS_FILE": "keys.json",
|
|
128
|
+
"OPENAI_API_KEY": "",
|
|
129
|
+
"GEMINI_API_KEY": "",
|
|
130
|
+
"LLM_MODE": "async",
|
|
131
|
+
"LLM_CONCURRENCY": 3""" if include_llm else ""
|
|
132
|
+
return f"""\
|
|
133
|
+
{{
|
|
134
|
+
"app": {{
|
|
135
|
+
"name": "{project_name}",
|
|
136
|
+
"env": "development",
|
|
137
|
+
"debug": false,
|
|
138
|
+
"host": "0.0.0.0",
|
|
139
|
+
"port": 8000
|
|
140
|
+
}},
|
|
141
|
+
"DATABASE_URL": "sqlite:///./app.db",
|
|
142
|
+
"LOG_DIR": "logs",
|
|
143
|
+
"LOG_STRATEGY": "date",
|
|
144
|
+
"LOG_STREAM": "stdout",
|
|
145
|
+
"LOG_FORMAT": "human",
|
|
146
|
+
"LOG_LEVEL": "DEBUG"{llm_keys}
|
|
147
|
+
}}
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _backend_config_content(
|
|
152
|
+
fmt: str, project_name: str, include_llm: bool
|
|
153
|
+
) -> tuple[str, str]:
|
|
154
|
+
if fmt == "yaml":
|
|
155
|
+
return "config.yaml", _backend_yaml_config(project_name, include_llm)
|
|
156
|
+
if fmt == "json":
|
|
157
|
+
return "config.json", _backend_json_config(project_name, include_llm)
|
|
158
|
+
return ".env", _backend_env_config(project_name, include_llm)
|
|
159
|
+
|
|
160
|
+
|
|
61
161
|
def _app_init(version: str) -> str:
|
|
62
162
|
return f'__version__ = "{version}"\n'
|
|
63
163
|
|
|
@@ -526,13 +626,17 @@ def scaffold_backend(
|
|
|
526
626
|
# ── utils ─────────────────────────────────────────────────────────────────
|
|
527
627
|
_write(result, project_dir / "utils" / "__init__.py", '"""Shared utilities."""\n')
|
|
528
628
|
_write(result, project_dir / "utils" / "logger.py", _logger_util())
|
|
629
|
+
if include_llm:
|
|
630
|
+
from infrakit.scaffolder.ai import _llm_util, _keys_json_template
|
|
631
|
+
_write(result, project_dir / "utils" / "llm.py", _llm_util(project_name))
|
|
632
|
+
_write(result, project_dir / "keys.json", _keys_json_template())
|
|
529
633
|
|
|
530
634
|
# ── tests ─────────────────────────────────────────────────────────────────
|
|
531
635
|
_write(result, project_dir / "tests" / "__init__.py", _tests_init())
|
|
532
636
|
_write(result, project_dir / "tests" / "test_health.py", _test_health())
|
|
533
637
|
|
|
534
638
|
# ── config ────────────────────────────────────────────────────────────────
|
|
535
|
-
cfg_name, cfg_content =
|
|
639
|
+
cfg_name, cfg_content = _backend_config_content(config_fmt, project_name, include_llm)
|
|
536
640
|
_write(result, project_dir / cfg_name, cfg_content)
|
|
537
641
|
|
|
538
642
|
# ── docker ────────────────────────────────────────────────────────────────
|
infrakit/scaffolder/cli_tool.py
CHANGED
|
@@ -366,7 +366,7 @@ def scaffold_cli_tool(
|
|
|
366
366
|
_write(result, project_dir / "tests" / "test_cli.py", _test_cli(project_name))
|
|
367
367
|
|
|
368
368
|
# ── config ────────────────────────────────────────────────────────────────
|
|
369
|
-
cfg_name, cfg_content = _config_content(config_fmt)
|
|
369
|
+
cfg_name, cfg_content = _config_content(config_fmt, include_llm=include_llm)
|
|
370
370
|
_write(result, project_dir / cfg_name, cfg_content)
|
|
371
371
|
|
|
372
372
|
# ── dependency file ───────────────────────────────────────────────────────
|
infrakit/scaffolder/generator.py
CHANGED
|
@@ -78,7 +78,7 @@ name = "{project_name}"
|
|
|
78
78
|
version = "{version}"
|
|
79
79
|
description = "{description}"
|
|
80
80
|
readme = "README.md"
|
|
81
|
-
requires-python = ">=3.
|
|
81
|
+
requires-python = ">=3.13"
|
|
82
82
|
authors = [
|
|
83
83
|
{author_line}
|
|
84
84
|
]
|
|
@@ -103,45 +103,95 @@ infrakit
|
|
|
103
103
|
"""
|
|
104
104
|
|
|
105
105
|
|
|
106
|
-
def _env_config() -> str:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
#
|
|
106
|
+
def _env_config(include_llm: bool = False) -> str:
|
|
107
|
+
llm_block = """\
|
|
108
|
+
|
|
109
|
+
# LLM
|
|
110
|
+
LLM_KEYS_FILE=keys.json
|
|
111
|
+
OPENAI_API_KEY=
|
|
112
|
+
GEMINI_API_KEY=
|
|
113
|
+
LLM_MODE=async
|
|
114
|
+
LLM_CONCURRENCY=3
|
|
115
|
+
# OPENAI_MODEL=gpt-4o
|
|
116
|
+
# GEMINI_MODEL=gemini-2.0-flash
|
|
117
|
+
# LLM_STATE_DIR=
|
|
118
|
+
# LLM_QUOTA_FILE=
|
|
119
|
+
""" if include_llm else ""
|
|
120
|
+
return f"""\
|
|
121
|
+
# Application
|
|
110
122
|
APP_ENV=development
|
|
111
123
|
APP_DEBUG=false
|
|
112
124
|
APP_SECRET=YOUR_VALUE_HERE
|
|
113
|
-
"""
|
|
114
125
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
126
|
+
# Logger
|
|
127
|
+
LOG_DIR=logs
|
|
128
|
+
LOG_STRATEGY=date
|
|
129
|
+
LOG_STREAM=stdout
|
|
130
|
+
LOG_FORMAT=human
|
|
131
|
+
LOG_LEVEL=DEBUG
|
|
132
|
+
{llm_block}"""
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _yaml_config(include_llm: bool = False) -> str:
|
|
136
|
+
llm_block = """
|
|
137
|
+
|
|
138
|
+
# LLM
|
|
139
|
+
LLM_KEYS_FILE: keys.json
|
|
140
|
+
OPENAI_API_KEY: ""
|
|
141
|
+
GEMINI_API_KEY: ""
|
|
142
|
+
LLM_MODE: async
|
|
143
|
+
LLM_CONCURRENCY: 3
|
|
144
|
+
# OPENAI_MODEL: gpt-4o
|
|
145
|
+
# GEMINI_MODEL: gemini-2.0-flash
|
|
146
|
+
# LLM_STATE_DIR: ""
|
|
147
|
+
# LLM_QUOTA_FILE: ""
|
|
148
|
+
""" if include_llm else ""
|
|
149
|
+
return f"""\
|
|
118
150
|
# Application configuration
|
|
119
151
|
app:
|
|
120
152
|
env: development
|
|
121
153
|
debug: false
|
|
122
154
|
secret: YOUR_VALUE_HERE
|
|
123
|
-
"""
|
|
124
155
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
156
|
+
# Logger (flat keys — read by utils/logger.py via infrakit.config)
|
|
157
|
+
LOG_DIR: logs
|
|
158
|
+
LOG_STRATEGY: date
|
|
159
|
+
LOG_STREAM: stdout
|
|
160
|
+
LOG_FORMAT: human
|
|
161
|
+
LOG_LEVEL: DEBUG
|
|
162
|
+
{llm_block}"""
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _json_config(include_llm: bool = False) -> str:
|
|
166
|
+
llm_keys = """,
|
|
167
|
+
"LLM_KEYS_FILE": "keys.json",
|
|
168
|
+
"OPENAI_API_KEY": "",
|
|
169
|
+
"GEMINI_API_KEY": "",
|
|
170
|
+
"LLM_MODE": "async",
|
|
171
|
+
"LLM_CONCURRENCY": 3""" if include_llm else ""
|
|
172
|
+
return f"""\
|
|
173
|
+
{{
|
|
174
|
+
"app": {{
|
|
130
175
|
"env": "development",
|
|
131
176
|
"debug": false,
|
|
132
177
|
"secret": "YOUR_VALUE_HERE"
|
|
133
|
-
}
|
|
134
|
-
|
|
178
|
+
}},
|
|
179
|
+
"LOG_DIR": "logs",
|
|
180
|
+
"LOG_STRATEGY": "date",
|
|
181
|
+
"LOG_STREAM": "stdout",
|
|
182
|
+
"LOG_FORMAT": "human",
|
|
183
|
+
"LOG_LEVEL": "DEBUG"{llm_keys}
|
|
184
|
+
}}
|
|
135
185
|
"""
|
|
136
186
|
|
|
137
187
|
|
|
138
|
-
def _config_content(fmt: str) -> tuple[str, str]:
|
|
188
|
+
def _config_content(fmt: str, *, include_llm: bool = False) -> tuple[str, str]:
|
|
139
189
|
"""Return (filename, content) for the chosen config format."""
|
|
140
190
|
if fmt == "yaml":
|
|
141
|
-
return "config.yaml", _yaml_config()
|
|
191
|
+
return "config.yaml", _yaml_config(include_llm)
|
|
142
192
|
if fmt == "json":
|
|
143
|
-
return "config.json", _json_config()
|
|
144
|
-
return ".env", _env_config() # default
|
|
193
|
+
return "config.json", _json_config(include_llm)
|
|
194
|
+
return ".env", _env_config(include_llm) # default
|
|
145
195
|
|
|
146
196
|
|
|
147
197
|
def _logger_util() -> str:
|
|
@@ -151,6 +201,9 @@ utils.logger
|
|
|
151
201
|
~~~~~~~~~~~~
|
|
152
202
|
Thin wrapper that boots the infrakit logger once and exports ``get_logger``.
|
|
153
203
|
|
|
204
|
+
Reads LOG_DIR, LOG_STRATEGY, LOG_STREAM, LOG_FORMAT, and LOG_LEVEL from the
|
|
205
|
+
project config file (.env / config.yaml / config.json) via infrakit.config.
|
|
206
|
+
|
|
154
207
|
Usage
|
|
155
208
|
-----
|
|
156
209
|
from utils.logger import get_logger
|
|
@@ -159,22 +212,34 @@ Usage
|
|
|
159
212
|
log.info("hello")
|
|
160
213
|
\"\"\"
|
|
161
214
|
|
|
162
|
-
import
|
|
215
|
+
from pathlib import Path
|
|
216
|
+
from infrakit.core.config.loader import load, load_env
|
|
163
217
|
from infrakit.core.logger import setup, get_logger # re-export get_logger
|
|
164
218
|
|
|
165
219
|
_booted = False
|
|
166
220
|
|
|
167
221
|
|
|
222
|
+
def _load_cfg() -> dict:
|
|
223
|
+
if Path(".env").exists():
|
|
224
|
+
return load_env(".env", cast_values=True)
|
|
225
|
+
if Path("config.yaml").exists():
|
|
226
|
+
return load("config.yaml")
|
|
227
|
+
if Path("config.json").exists():
|
|
228
|
+
return load("config.json")
|
|
229
|
+
return {}
|
|
230
|
+
|
|
231
|
+
|
|
168
232
|
def _boot() -> None:
|
|
169
233
|
global _booted
|
|
170
234
|
if _booted:
|
|
171
235
|
return
|
|
236
|
+
cfg = _load_cfg()
|
|
172
237
|
setup(
|
|
173
|
-
log_dir=
|
|
174
|
-
strategy=
|
|
175
|
-
stream=
|
|
176
|
-
fmt=
|
|
177
|
-
level=
|
|
238
|
+
log_dir=cfg.get("LOG_DIR", "logs"),
|
|
239
|
+
strategy=cfg.get("LOG_STRATEGY", "date"),
|
|
240
|
+
stream=cfg.get("LOG_STREAM", "stdout"),
|
|
241
|
+
fmt=cfg.get("LOG_FORMAT", "human"),
|
|
242
|
+
level=cfg.get("LOG_LEVEL", "DEBUG"),
|
|
178
243
|
)
|
|
179
244
|
_booted = True
|
|
180
245
|
|
|
@@ -318,7 +383,7 @@ def scaffold_basic(
|
|
|
318
383
|
_write(result, project_dir / "tests" / "__init__.py", _tests_init())
|
|
319
384
|
|
|
320
385
|
# ── config file ───────────────────────────────────────────────────────────
|
|
321
|
-
cfg_name, cfg_content = _config_content(config_fmt)
|
|
386
|
+
cfg_name, cfg_content = _config_content(config_fmt, include_llm=include_llm)
|
|
322
387
|
_write(result, project_dir / cfg_name, cfg_content)
|
|
323
388
|
|
|
324
389
|
# ── dependency file ───────────────────────────────────────────────────────
|
infrakit/scaffolder/pipeline.py
CHANGED
|
@@ -543,7 +543,7 @@ def scaffold_pipeline(
|
|
|
543
543
|
_write(result, project_dir / "tests" / "test_pipeline.py", _test_pipeline())
|
|
544
544
|
|
|
545
545
|
# ── config ────────────────────────────────────────────────────────────────
|
|
546
|
-
cfg_name, cfg_content = _config_content(config_fmt)
|
|
546
|
+
cfg_name, cfg_content = _config_content(config_fmt, include_llm=include_llm)
|
|
547
547
|
_write(result, project_dir / cfg_name, cfg_content)
|
|
548
548
|
|
|
549
549
|
# ── dependency file ───────────────────────────────────────────────────────
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-infrakit-dev
|
|
3
|
+
Version: 0.1.4
|
|
4
|
+
Summary: A comprehensive Python developer infrastructure toolkit
|
|
5
|
+
Project-URL: Homepage, https://github.com/chiragg21/infrakit
|
|
6
|
+
Project-URL: Repository, https://github.com/chiragg21/infrakit
|
|
7
|
+
Requires-Python: >=3.13
|
|
8
|
+
Requires-Dist: google-genai>=1.69.0
|
|
9
|
+
Requires-Dist: isort>=8.0.1
|
|
10
|
+
Requires-Dist: openai>=2.30.0
|
|
11
|
+
Requires-Dist: pydantic>=2.12.5
|
|
12
|
+
Requires-Dist: python-dotenv>=1.2.2
|
|
13
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
14
|
+
Requires-Dist: tqdm>=4.67.3
|
|
15
|
+
Requires-Dist: typer>=0.24.1
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# Infrakit
|
|
19
|
+
|
|
20
|
+
A modular developer toolkit for Python — project scaffolding, logging, config loading, a multi-provider LLM client, and dependency utilities.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install infrakit
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The CLI is available as `ik`.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Scaffolding
|
|
31
|
+
|
|
32
|
+
Bootstrap a new project in one command:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
ik init my-project # basic layout
|
|
36
|
+
ik init my-api -t backend # FastAPI service
|
|
37
|
+
ik init my-model -t ai # AI/ML project with pipelines and notebooks
|
|
38
|
+
ik init my-etl -t pipeline # ETL/data pipeline
|
|
39
|
+
ik init my-cli -t cli_tool # distributable CLI app
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**All flags:**
|
|
43
|
+
|
|
44
|
+
| Flag | Values | Default | Description |
|
|
45
|
+
|---|---|---|---|
|
|
46
|
+
| `-t` / `--template` | `basic`, `backend`, `ai`, `pipeline`, `cli_tool` | `basic` | Project template |
|
|
47
|
+
| `-v` / `--version` | e.g. `0.1.0` | `0.1.0` | Starting version string |
|
|
48
|
+
| `--description` | string | `""` | Short description added to pyproject.toml and README |
|
|
49
|
+
| `--author` | string | `""` | Author line in pyproject.toml |
|
|
50
|
+
| `--config` | `env`, `yaml`, `json` | `env` | Config file format to generate |
|
|
51
|
+
| `--deps` | `toml`, `requirements` | `toml` | Dependency file style |
|
|
52
|
+
| `--include-llm` | flag | off | Add `utils/llm.py` wired to `infrakit.llm` |
|
|
53
|
+
|
|
54
|
+
Every template generates a config file pre-populated with the variables its utilities need (logger settings, LLM keys, app settings). Re-running over an existing directory skips files already present.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Core — Config & Logger
|
|
59
|
+
|
|
60
|
+
### Config loader
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from infrakit.core.config.loader import load, load_env
|
|
64
|
+
|
|
65
|
+
cfg = load_env(".env", cast_values=True) # "true" → bool, "42" → int
|
|
66
|
+
cfg = load("config.yaml")
|
|
67
|
+
cfg = load("config.json")
|
|
68
|
+
|
|
69
|
+
port = cfg.get("APP_PORT", 8000)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**`load(path, *, ...)`** — load a config file (JSON, YAML, INI, or `.env`), format inferred from extension.
|
|
73
|
+
|
|
74
|
+
| Parameter | Default | Description |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `path` | — | Path to the config file |
|
|
77
|
+
| `env_override` | `False` | Let existing environment variables override file values |
|
|
78
|
+
| `env_file` | `".env"` | `.env` file to merge in alongside the config |
|
|
79
|
+
| `inject_new` | `False` | Add any new keys from the env file into the result |
|
|
80
|
+
| `interpolate` | `True` | Expand `${VAR}` references within values |
|
|
81
|
+
| `cast_values` | `True` | Convert strings to int, float, bool where possible |
|
|
82
|
+
|
|
83
|
+
**`load_env(path, *, cast_values)`** — convenience wrapper to load a `.env` file directly. `cast_values` defaults to `False`.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
### Logger
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from infrakit.core.logger import setup, get_logger
|
|
91
|
+
|
|
92
|
+
setup(log_dir="logs", strategy="date", stream="stdout", fmt="human", level="INFO")
|
|
93
|
+
|
|
94
|
+
log = get_logger(__name__)
|
|
95
|
+
log.info("started on port %d", 8080)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**`setup(*, ...)`** — configure logging once at startup. All parameters are keyword-only.
|
|
99
|
+
|
|
100
|
+
| Parameter | Default | Description |
|
|
101
|
+
|---|---|---|
|
|
102
|
+
| `level` | `"DEBUG"` | Minimum log level (`DEBUG`, `INFO`, `WARNING`, `ERROR`) |
|
|
103
|
+
| `fmt` | `"human"` | Console format — `"human"` for readable text, `"json"` for structured |
|
|
104
|
+
| `file_fmt` | `"human"` | Format used in log files (same options as `fmt`) |
|
|
105
|
+
| `strategy` | `"date"` | File rotation strategy — `"date"`, `"date_level"`, or `"single"` |
|
|
106
|
+
| `stream` | `"stdout"` | Where to echo logs — `"stdout"`, `"stderr"`, or `None` to disable |
|
|
107
|
+
| `log_dir` | `"logs"` | Directory where log files are written |
|
|
108
|
+
| `session` | `None` | Label this run — adds a session prefix to log filenames |
|
|
109
|
+
| `retention` | `30` | Days to keep old log files before deletion |
|
|
110
|
+
| `max_bytes` | `10MB` | Max size of a single log file before rotation |
|
|
111
|
+
| `force` | `False` | Re-apply setup even if already configured |
|
|
112
|
+
|
|
113
|
+
**`get_logger(name)`** — returns a stdlib `Logger`. Always call as `get_logger(__name__)`. Safe to call before `setup()`.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## LLM Client
|
|
118
|
+
|
|
119
|
+
A unified client for **OpenAI** and **Gemini** with key rotation, rate limiting, quota tracking, and async/batch generation.
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from infrakit.llm import LLMClient, Prompt
|
|
123
|
+
|
|
124
|
+
client = LLMClient(
|
|
125
|
+
keys={"openai_keys": ["sk-..."], "gemini_keys": ["AIza..."]},
|
|
126
|
+
storage_dir="./llm_state", # persists key usage across restarts
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
response = client.generate(Prompt(user="What is 2+2?"), provider="openai")
|
|
130
|
+
print(response.content)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**`LLMClient(keys, ...)`**
|
|
134
|
+
|
|
135
|
+
| Parameter | Default | Description |
|
|
136
|
+
|---|---|---|
|
|
137
|
+
| `keys` | — | Dict with `openai_keys` and/or `gemini_keys` lists of API key strings |
|
|
138
|
+
| `storage_dir` | `~/.infrakit/llm` | Directory where key state is persisted across restarts |
|
|
139
|
+
| `quota_file` | `None` | Path to a `quotas.json` file with per-provider/model limits |
|
|
140
|
+
| `mode` | `"async"` | Batch execution mode — `"async"` or `"threaded"` |
|
|
141
|
+
| `max_concurrent` | `3` | Max parallel requests in batch calls |
|
|
142
|
+
| `key_retries` | `3` | How many keys to try before giving up on a request |
|
|
143
|
+
| `schema_retries` | `2` | How many times to retry structured output parsing on schema mismatch |
|
|
144
|
+
| `meta_window` | `200` | Number of recent requests to keep in memory per key |
|
|
145
|
+
| `openai_model` | `"gpt-4o-mini"` | Default OpenAI model |
|
|
146
|
+
| `gemini_model` | `"gemini-2.0-flash"` | Default Gemini model |
|
|
147
|
+
| `show_progress` | `True` | Show a progress bar during batch generation |
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
**`generate(prompt, provider, response_model, **kwargs)`** — blocking single call, safe in any context.
|
|
152
|
+
|
|
153
|
+
**`async_generate(prompt, provider, response_model, **kwargs)`** — async version, use inside `async` functions.
|
|
154
|
+
|
|
155
|
+
| Parameter | Default | Description |
|
|
156
|
+
|---|---|---|
|
|
157
|
+
| `prompt` | — | `Prompt(system=..., user=...)` — `system` is optional |
|
|
158
|
+
| `provider` | — | `"openai"` or `"gemini"` |
|
|
159
|
+
| `response_model` | `None` | Pydantic `BaseModel` subclass for structured output parsing |
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
**`batch_generate(prompts, provider, ...)`** — run many prompts; results match input order.
|
|
164
|
+
|
|
165
|
+
**`async_batch_generate(prompts, provider, ...)`** — async version for use inside `async` functions.
|
|
166
|
+
|
|
167
|
+
| Parameter | Default | Description |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| `prompts` | — | List of `Prompt` objects |
|
|
170
|
+
| `provider` | — | `"openai"` or `"gemini"` |
|
|
171
|
+
| `response_model` | `None` | Pydantic model for structured output on every item |
|
|
172
|
+
| `max_concurrent` | client default | Override the client-level concurrency limit for this batch |
|
|
173
|
+
| `show_progress` | client default | Override the client-level progress bar setting |
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
batch = client.batch_generate(prompts, provider="openai")
|
|
177
|
+
print(batch.success_count, batch.failure_count, batch.total_tokens)
|
|
178
|
+
for r in batch.results:
|
|
179
|
+
print(r.content if not r.error else r.error)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
**Structured output:**
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from pydantic import BaseModel
|
|
188
|
+
|
|
189
|
+
class Summary(BaseModel):
|
|
190
|
+
title: str
|
|
191
|
+
bullets: list[str]
|
|
192
|
+
|
|
193
|
+
response = client.generate(Prompt(user="Summarise: ..."), provider="openai", response_model=Summary)
|
|
194
|
+
if response.schema_matched:
|
|
195
|
+
print(response.parsed.bullets) # typed Summary instance
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
**`set_quota(provider, key_id, quota)`** — set limits for a specific key.
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
from infrakit.llm import QuotaConfig
|
|
204
|
+
|
|
205
|
+
# key-level RPM (applies to all models on this key)
|
|
206
|
+
client.set_quota(provider="openai", key_id="sk-key1", quota=QuotaConfig(rpm_limit=60))
|
|
207
|
+
|
|
208
|
+
# per-model daily token limit
|
|
209
|
+
client.set_quota(provider="gemini", key_id="AIza-key1", quota=QuotaConfig(model="gemini-2.5-pro", daily_token_limit=250_000))
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
`QuotaConfig` fields: `model` (None = all models on the key), `rpm_limit`, `daily_token_limit`, `tpm_limit`.
|
|
213
|
+
|
|
214
|
+
Quota defaults can also be set in a `quotas.json` file — pass the path via `quota_file=` in the constructor.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
**`status(provider, key_id)`** — return key status as a list of dicts. **`print_status(provider, key_id)`** — same but pretty-printed to stdout.
|
|
219
|
+
|
|
220
|
+
| Parameter | Default | Description |
|
|
221
|
+
|---|---|---|
|
|
222
|
+
| `provider` | `None` | Filter to `"openai"` or `"gemini"`; `None` returns all |
|
|
223
|
+
| `key_id` | `None` | Filter to a specific key (first 8 chars); `None` returns all |
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
rows = client.status(provider="openai")
|
|
227
|
+
# each row: provider, key_id, status, rpm_limit, current_rpm, models
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**CLI:**
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
ik llm status --storage-dir ./llm_state
|
|
234
|
+
ik llm quota set --provider openai --key sk-abc --rpm 60 --storage-dir ./llm_state
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Other Features
|
|
240
|
+
|
|
241
|
+
### Dependency management (`infrakit.deps`)
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
ik deps scan . # list packages your code actually imports
|
|
245
|
+
ik deps export . --format toml # sync pyproject.toml / requirements.txt
|
|
246
|
+
ik deps check --packages numpy # outdated, security, and license checks
|
|
247
|
+
ik deps clean . --dry-run # find unused installed packages
|
|
248
|
+
ik deps optimise . # sort and clean imports (isort)
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**`scan(root, include_notebooks, use_gitignore)`**
|
|
252
|
+
|
|
253
|
+
| Parameter | Default | Description |
|
|
254
|
+
|---|---|---|
|
|
255
|
+
| `root` | — | Project root to scan |
|
|
256
|
+
| `include_notebooks` | `False` | Also scan `.ipynb` notebook files |
|
|
257
|
+
| `use_gitignore` | `True` | Skip paths matched by `.gitignore` |
|
|
258
|
+
|
|
259
|
+
**`export(root, output, inplace, keep_versions, include_notebooks, use_gitignore)`**
|
|
260
|
+
|
|
261
|
+
| Parameter | Default | Description |
|
|
262
|
+
|---|---|---|
|
|
263
|
+
| `root` | — | Project root to scan |
|
|
264
|
+
| `output` | `None` | Write to this path instead of the detected dependency file |
|
|
265
|
+
| `inplace` | `False` | Update the existing file in place |
|
|
266
|
+
| `keep_versions` | `True` | Preserve pinned version specifiers already in the file |
|
|
267
|
+
| `include_notebooks` | `False` | Also scan `.ipynb` files |
|
|
268
|
+
| `use_gitignore` | `True` | Skip gitignored paths |
|
|
269
|
+
|
|
270
|
+
**`check(root, packages, outdated, security, licenses)`**
|
|
271
|
+
|
|
272
|
+
| Parameter | Default | Description |
|
|
273
|
+
|---|---|---|
|
|
274
|
+
| `root` | `None` | Auto-scan this root for packages (used if `packages` is `None`) |
|
|
275
|
+
| `packages` | `None` | Explicit list of package names to check |
|
|
276
|
+
| `outdated` | `True` | Check for newer versions on PyPI |
|
|
277
|
+
| `security` | `True` | Run vulnerability checks |
|
|
278
|
+
| `licenses` | `True` | Check license compatibility |
|
|
279
|
+
|
|
280
|
+
Returns a `HealthReport` with `.outdated`, `.vulnerable`, `.licenses`, and `.errors` lists.
|
|
281
|
+
|
|
282
|
+
**`clean(root, protected, dry_run)`**
|
|
283
|
+
|
|
284
|
+
| Parameter | Default | Description |
|
|
285
|
+
|---|---|---|
|
|
286
|
+
| `root` | — | Project root (used to determine what is actually imported) |
|
|
287
|
+
| `protected` | `None` | Set of package names to never remove |
|
|
288
|
+
| `dry_run` | `True` | Preview only — pass `False` to actually uninstall |
|
|
289
|
+
|
|
290
|
+
Returns a `CleanResult` with `.to_remove`, `.removed`, `.skipped`, and `.errors` lists.
|
|
291
|
+
|
|
292
|
+
**`optimise(root, files, convert_to, use_isort, dry_run)`**
|
|
293
|
+
|
|
294
|
+
| Parameter | Default | Description |
|
|
295
|
+
|---|---|---|
|
|
296
|
+
| `root` | — | Project root |
|
|
297
|
+
| `files` | `None` | Specific files to process; `None` processes all `.py` files |
|
|
298
|
+
| `convert_to` | `None` | Rewrite import style (e.g. `"absolute"`) |
|
|
299
|
+
| `use_isort` | `True` | Run `isort` on each file |
|
|
300
|
+
| `dry_run` | `False` | Preview changes without writing files |
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
### Profiling (`infrakit.time`)
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
ik time run script.py
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
```python
|
|
311
|
+
from infrakit.time import pipeline_profiler, track
|
|
312
|
+
|
|
313
|
+
@pipeline_profiler("My Pipeline")
|
|
314
|
+
def main(): ...
|
|
315
|
+
|
|
316
|
+
@track(name="Load Step")
|
|
317
|
+
def load_data(): ...
|
|
318
|
+
```
|
|
@@ -38,15 +38,15 @@ infrakit/llm/providers/base.py,sha256=SglgqSIGm_jHsxj7gXvha4CsKb0L9Q8TXBXcUaVXK5
|
|
|
38
38
|
infrakit/llm/providers/gemini.py,sha256=rVI5seLpmb_R1AqcP9B6Hp9WvyTmWeDdCVttyCH05mI,5552
|
|
39
39
|
infrakit/llm/providers/openai.py,sha256=qAZ1iXRbEilvqOvYXkUh3rS-PhQVp_fQFXiUNk-vers,5469
|
|
40
40
|
infrakit/scaffolder/__init__.py,sha256=VBsYgmzomi4abMF0G7xtto3vecKqKBVgKtguugtmzkQ,1132
|
|
41
|
-
infrakit/scaffolder/ai.py,sha256=
|
|
42
|
-
infrakit/scaffolder/backend.py,sha256=
|
|
43
|
-
infrakit/scaffolder/cli_tool.py,sha256=
|
|
44
|
-
infrakit/scaffolder/generator.py,sha256=
|
|
45
|
-
infrakit/scaffolder/pipeline.py,sha256=
|
|
41
|
+
infrakit/scaffolder/ai.py,sha256=otj0YjPApBfLwXTc1EwA-t-ePTefe6bhIKskk4TcZTk,16679
|
|
42
|
+
infrakit/scaffolder/backend.py,sha256=Ty_uEPzT-qMi2c5Kb8HM32dQC9kTRhgJHYycTSvrJ08,18725
|
|
43
|
+
infrakit/scaffolder/cli_tool.py,sha256=QOgqtTqi-OASowIh8ezQWFPMZLMcamz4gt7tK7i9qeg,12056
|
|
44
|
+
infrakit/scaffolder/generator.py,sha256=DrkFFRkePuie5VyGx-dEYs7dUX6S-JIrrbxLCViFots,11558
|
|
45
|
+
infrakit/scaffolder/pipeline.py,sha256=HeBkreiso0fYIHrObkaPKRB3XewU3UgQevrrcWHY_wI,17106
|
|
46
46
|
infrakit/scaffolder/registry.py,sha256=TqZetmDChmXD_oW6kl10A0SbN6ADm4xi7NdK2m9GNGI,3802
|
|
47
47
|
infrakit/time/__init__.py,sha256=R-o_F3XkqaC-QWtfMJ8TCUKnCqLycFl2WQHs20e8ElI,1206
|
|
48
48
|
infrakit/time/profiler.py,sha256=bfM-UCFULr1cA4Y6fgBWdQ7d4tNgdY_YJowohhVH-So,16075
|
|
49
|
-
python_infrakit_dev-0.1.
|
|
50
|
-
python_infrakit_dev-0.1.
|
|
51
|
-
python_infrakit_dev-0.1.
|
|
52
|
-
python_infrakit_dev-0.1.
|
|
49
|
+
python_infrakit_dev-0.1.4.dist-info/METADATA,sha256=RdoayVeTCtHJmwJImyLOXnq3ITFLQb85_DMznw2Srnw,11484
|
|
50
|
+
python_infrakit_dev-0.1.4.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
51
|
+
python_infrakit_dev-0.1.4.dist-info/entry_points.txt,sha256=9uBSvx5accKf026gAQijwUkkN_eL37RBWlCO3e_9Iz4,80
|
|
52
|
+
python_infrakit_dev-0.1.4.dist-info/RECORD,,
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: python-infrakit-dev
|
|
3
|
-
Version: 0.1.2
|
|
4
|
-
Summary: A comprehensive Python developer infrastructure toolkit
|
|
5
|
-
Project-URL: Homepage, https://github.com/chiragg21/infrakit
|
|
6
|
-
Project-URL: Repository, https://github.com/chiragg21/infrakit
|
|
7
|
-
Requires-Python: >=3.13
|
|
8
|
-
Requires-Dist: google-genai>=1.69.0
|
|
9
|
-
Requires-Dist: isort>=8.0.1
|
|
10
|
-
Requires-Dist: openai>=2.30.0
|
|
11
|
-
Requires-Dist: pydantic>=2.12.5
|
|
12
|
-
Requires-Dist: python-dotenv>=1.2.2
|
|
13
|
-
Requires-Dist: pyyaml>=6.0.3
|
|
14
|
-
Requires-Dist: tqdm>=4.67.3
|
|
15
|
-
Requires-Dist: typer>=0.24.1
|
|
16
|
-
Description-Content-Type: text/markdown
|
|
17
|
-
|
|
18
|
-
# Infrakit
|
|
19
|
-
|
|
20
|
-
Infrakit is a comprehensive, modular developer infrastructure toolkit for Python projects. It provides CLI utilities, rich project scaffolding, a robust multi-provider LLM client, dependency management, profiling, and core utilities for logging and configuration.
|
|
21
|
-
|
|
22
|
-
## Installation
|
|
23
|
-
|
|
24
|
-
Install via pip:
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
pip install infrakit
|
|
28
|
-
# or if using uv
|
|
29
|
-
uv pip install infrakit
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
The CLI is exposed as `infrakit` and conveniently aliased as `ik`.
|
|
33
|
-
|
|
34
|
-
## Key Features
|
|
35
|
-
|
|
36
|
-
### 1. Project Scaffolding (`ik init`)
|
|
37
|
-
|
|
38
|
-
Quickly bootstrap new Python projects with standardized layouts, ready-to-use boilerplate, and optional LLM integration. Existing files are safely skipped if you re-run over a directory.
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
ik init my-project -t basic
|
|
42
|
-
ik init my-fastapi-app -t backend -v 0.1.0
|
|
43
|
-
ik init my-ai-tool -t ai --include-llm
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
**Available Templates**:
|
|
47
|
-
- **`basic`**: Minimal template (src, utils, tests).
|
|
48
|
-
- **`backend`**: FastAPI service (app, routes, middleware, Dockerfile, docker-compose).
|
|
49
|
-
- **`cli_tool`**: Distributable CLI application using Typer.
|
|
50
|
-
- **`pipeline`**: Data pipeline / ETL template (extract, transform, load, enrich).
|
|
51
|
-
- **`ai`**: AI/ML project optimized for notebooks and pipelines.
|
|
52
|
-
|
|
53
|
-
### 2. Multi-provider LLM Client (`infrakit.llm`)
|
|
54
|
-
|
|
55
|
-
A unified LLM client interface supporting **OpenAI** and **Gemini**. Built natively for robust production use cases, particularly when navigating free-tier API quotas.
|
|
56
|
-
|
|
57
|
-
**Features:**
|
|
58
|
-
- Seamless key rotation and persistent storage tracking.
|
|
59
|
-
- Local rate limiting (RPM/TPM gates).
|
|
60
|
-
- Async and multi-threaded batch generation.
|
|
61
|
-
- Structured output parsing and schema validation using `pydantic.BaseModel`.
|
|
62
|
-
|
|
63
|
-
**Quick Start:**
|
|
64
|
-
```python
|
|
65
|
-
from infrakit.llm import LLMClient, Prompt
|
|
66
|
-
|
|
67
|
-
client = LLMClient(keys={"openai_keys": ["sk-..."], "gemini_keys": ["AIza..."]}, storage_dir="./logs")
|
|
68
|
-
response = client.generate(Prompt(user="What is the capital of France?"), provider="openai")
|
|
69
|
-
print(response.content)
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
**LLM CLI tools:**
|
|
73
|
-
Monitor and control limits right from the CLI.
|
|
74
|
-
- `ik llm status --storage-dir ./logs`
|
|
75
|
-
- `ik llm quota set --provider openai --key sk-abc --rpm 60 --storage-dir ./logs`
|
|
76
|
-
|
|
77
|
-
### 3. Dependency Management (`infrakit.deps`)
|
|
78
|
-
|
|
79
|
-
In-depth Python dependency tools available under `ik deps` (or programmatically via `infrakit.deps`) to clean, health-check, scan, and optimize dependencies.
|
|
80
|
-
|
|
81
|
-
**Features:**
|
|
82
|
-
- `scan(root)`: Scan your project for actual Python dependencies used in code.
|
|
83
|
-
- `export()`: Export used dependencies to update `requirements.txt` or `pyproject.toml` automatically.
|
|
84
|
-
- `check(packages)`: Run health, security, and outdated checks on packages.
|
|
85
|
-
- `clean()`: Find and remove unused packages from the virtual environment.
|
|
86
|
-
- `optimise()`: Optimize and format imports across the project (integrates with `isort`).
|
|
87
|
-
|
|
88
|
-
### 4. Code Profiling (`infrakit.time`)
|
|
89
|
-
|
|
90
|
-
Lightweight timing and execution profiling for your Python projects.
|
|
91
|
-
|
|
92
|
-
**CLI Usage:**
|
|
93
|
-
Profile any script instantly to detect performance bottlenecks.
|
|
94
|
-
```bash
|
|
95
|
-
ik time run script.py --max-functions 30 --min-time 1.0
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
**Decorator Usage:**
|
|
99
|
-
Track specific pipeline steps inside your code:
|
|
100
|
-
```python
|
|
101
|
-
from infrakit.time import pipeline_profiler, track
|
|
102
|
-
|
|
103
|
-
@pipeline_profiler("Data Processing Pipeline")
|
|
104
|
-
def main():
|
|
105
|
-
load_data()
|
|
106
|
-
transform_data()
|
|
107
|
-
|
|
108
|
-
@track(name="Load Step")
|
|
109
|
-
def load_data(): pass
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### 5. Core Utilities (`infrakit.core`)
|
|
113
|
-
|
|
114
|
-
Convenient implementations for common application needs.
|
|
115
|
-
|
|
116
|
-
- **Config Loader** (`infrakit.core.config.loader`): Multi-format configuration loader supporting JSON, YAML, INI, and `.env`. Automatically resolves variables using `python-dotenv` and converts types properly natively.
|
|
117
|
-
- **Logger** (`infrakit.core.logger`): Unified logging setups for consistent output across applications.
|
|
118
|
-
```python
|
|
119
|
-
from infrakit.core.logger import setup, get_logger
|
|
120
|
-
|
|
121
|
-
setup(level="INFO", strategy="date_level", stream="stdout")
|
|
122
|
-
log = get_logger(__name__)
|
|
123
|
-
log.info("Ready")
|
|
124
|
-
```
|
|
File without changes
|
{python_infrakit_dev-0.1.2.dist-info → python_infrakit_dev-0.1.4.dist-info}/entry_points.txt
RENAMED
|
File without changes
|