model-forge-llm 0.2.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.
- model_forge_llm-0.2.0/CODE_REVIEW_GUIDELINES.md +680 -0
- model_forge_llm-0.2.0/CONTRIBUTING.md +157 -0
- model_forge_llm-0.2.0/LICENSE +21 -0
- model_forge_llm-0.2.0/MANIFEST.in +25 -0
- model_forge_llm-0.2.0/PKG-INFO +327 -0
- model_forge_llm-0.2.0/README.md +264 -0
- model_forge_llm-0.2.0/pyproject.toml +192 -0
- model_forge_llm-0.2.0/setup.cfg +4 -0
- model_forge_llm-0.2.0/src/model_forge_llm.egg-info/PKG-INFO +327 -0
- model_forge_llm-0.2.0/src/model_forge_llm.egg-info/SOURCES.txt +20 -0
- model_forge_llm-0.2.0/src/model_forge_llm.egg-info/dependency_links.txt +1 -0
- model_forge_llm-0.2.0/src/model_forge_llm.egg-info/entry_points.txt +2 -0
- model_forge_llm-0.2.0/src/model_forge_llm.egg-info/requires.txt +18 -0
- model_forge_llm-0.2.0/src/model_forge_llm.egg-info/top_level.txt +1 -0
- model_forge_llm-0.2.0/src/modelforge/__init__.py +7 -0
- model_forge_llm-0.2.0/src/modelforge/auth.py +503 -0
- model_forge_llm-0.2.0/src/modelforge/cli.py +720 -0
- model_forge_llm-0.2.0/src/modelforge/config.py +211 -0
- model_forge_llm-0.2.0/src/modelforge/exceptions.py +29 -0
- model_forge_llm-0.2.0/src/modelforge/logging_config.py +69 -0
- model_forge_llm-0.2.0/src/modelforge/modelsdev.py +364 -0
- model_forge_llm-0.2.0/src/modelforge/registry.py +272 -0
@@ -0,0 +1,680 @@
|
|
1
|
+
# ModelForge Code Quality & Review Guidelines
|
2
|
+
|
3
|
+
## LLM Instructions for Code Review
|
4
|
+
|
5
|
+
You are a senior Python developer conducting a code review for the ModelForge project - a library for managing LLM providers, authentication, and model selection. Apply these guidelines consistently to maintain code quality and project standards.
|
6
|
+
|
7
|
+
## 📋 **Core Review Criteria**
|
8
|
+
|
9
|
+
> **⚠️ PREREQUISITE**: All unit tests must pass before code review begins. Run `poetry run pytest tests/ -v` to verify.
|
10
|
+
|
11
|
+
### 1. **Code Style & Formatting**
|
12
|
+
|
13
|
+
**MUST CHECK:**
|
14
|
+
- [ ] Code follows Ruff formatting (88 char line length)
|
15
|
+
- [ ] Imports are organized (stdlib → third-party → local)
|
16
|
+
- [ ] No trailing whitespace or unnecessary blank lines
|
17
|
+
- [ ] Consistent quote usage (double quotes preferred)
|
18
|
+
- [ ] Variable names are descriptive and follow snake_case
|
19
|
+
- [ ] Class names follow PascalCase
|
20
|
+
- [ ] Constants are UPPER_CASE
|
21
|
+
|
22
|
+
**EXAMPLES:**
|
23
|
+
```python
|
24
|
+
# ✅ GOOD
|
25
|
+
from typing import Dict, Any, Optional
|
26
|
+
import json
|
27
|
+
from pathlib import Path
|
28
|
+
|
29
|
+
from langchain_core.language_models.chat_models import BaseChatModel
|
30
|
+
|
31
|
+
from . import config, auth
|
32
|
+
from .exceptions import AuthenticationError
|
33
|
+
|
34
|
+
# ❌ BAD
|
35
|
+
from .exceptions import AuthenticationError
|
36
|
+
from . import config, auth
|
37
|
+
import json
|
38
|
+
from pathlib import Path
|
39
|
+
from typing import Dict,Any,Optional
|
40
|
+
from langchain_core.language_models.chat_models import BaseChatModel
|
41
|
+
```
|
42
|
+
|
43
|
+
### 2. **Type Safety**
|
44
|
+
|
45
|
+
**MUST CHECK:**
|
46
|
+
- [ ] All function signatures have type hints
|
47
|
+
- [ ] Return types are explicitly annotated
|
48
|
+
- [ ] Complex types use proper imports from `typing`
|
49
|
+
- [ ] Optional parameters are properly annotated
|
50
|
+
- [ ] No use of `Any` without justification
|
51
|
+
|
52
|
+
**EXAMPLES:**
|
53
|
+
```python
|
54
|
+
# ✅ GOOD
|
55
|
+
def get_config() -> Tuple[Dict[str, Any], Path]:
|
56
|
+
"""Loads model configuration with local-over-global precedence."""
|
57
|
+
|
58
|
+
def set_current_model(provider: str, model: str, local: bool = False) -> bool:
|
59
|
+
"""Sets the currently active model in the configuration."""
|
60
|
+
|
61
|
+
# ❌ BAD
|
62
|
+
def get_config():
|
63
|
+
"""Loads model configuration with local-over-global precedence."""
|
64
|
+
|
65
|
+
def set_current_model(provider, model, local=False):
|
66
|
+
"""Sets the currently active model in the configuration."""
|
67
|
+
```
|
68
|
+
|
69
|
+
### 3. **Error Handling & Exceptions**
|
70
|
+
|
71
|
+
**MUST CHECK:**
|
72
|
+
- [ ] Use custom exceptions from `exceptions.py` instead of generic ones
|
73
|
+
- [ ] No bare `except:` clauses
|
74
|
+
- [ ] Exceptions include meaningful error messages
|
75
|
+
- [ ] Use logging instead of print statements for errors
|
76
|
+
- [ ] Proper exception chaining with `raise ... from ...`
|
77
|
+
|
78
|
+
**EXAMPLES:**
|
79
|
+
```python
|
80
|
+
# ✅ GOOD
|
81
|
+
from .exceptions import ConfigurationError, AuthenticationError
|
82
|
+
from .logging_config import get_logger
|
83
|
+
|
84
|
+
logger = get_logger(__name__)
|
85
|
+
|
86
|
+
try:
|
87
|
+
with open(config_path, "r") as f:
|
88
|
+
return json.load(f), config_path
|
89
|
+
except FileNotFoundError as e:
|
90
|
+
logger.error("Configuration file not found: %s", config_path)
|
91
|
+
raise ConfigurationError(f"Config file not found: {config_path}") from e
|
92
|
+
except json.JSONDecodeError as e:
|
93
|
+
logger.error("Invalid JSON in config file: %s", config_path)
|
94
|
+
raise ConfigurationError(f"Invalid JSON in config: {config_path}") from e
|
95
|
+
|
96
|
+
# ❌ BAD
|
97
|
+
try:
|
98
|
+
with open(config_path, "r") as f:
|
99
|
+
return json.load(f), config_path
|
100
|
+
except:
|
101
|
+
print(f"Error: Could not read config file at {config_path}")
|
102
|
+
return {}, config_path
|
103
|
+
```
|
104
|
+
|
105
|
+
### 4. **Logging & Output**
|
106
|
+
|
107
|
+
**MUST CHECK:**
|
108
|
+
- [ ] Use logging instead of print statements
|
109
|
+
- [ ] Log levels are appropriate (DEBUG, INFO, WARNING, ERROR)
|
110
|
+
- [ ] Sensitive information is not logged
|
111
|
+
- [ ] User-facing messages are clear and actionable
|
112
|
+
- [ ] Debug information uses proper logger
|
113
|
+
|
114
|
+
**EXAMPLES:**
|
115
|
+
```python
|
116
|
+
# ✅ GOOD
|
117
|
+
from .logging_config import get_logger
|
118
|
+
|
119
|
+
logger = get_logger(__name__)
|
120
|
+
|
121
|
+
def authenticate(self) -> Dict[str, Any]:
|
122
|
+
logger.info("Starting authentication for provider: %s", self.provider_name)
|
123
|
+
try:
|
124
|
+
# ... authentication logic
|
125
|
+
logger.debug("Authentication successful")
|
126
|
+
return {"api_key": api_key}
|
127
|
+
except Exception as e:
|
128
|
+
logger.error("Authentication failed: %s", str(e))
|
129
|
+
raise AuthenticationError("Authentication failed") from e
|
130
|
+
|
131
|
+
# ❌ BAD
|
132
|
+
def authenticate(self) -> Dict[str, Any]:
|
133
|
+
print(f"Please enter the API key for {self.provider_name}:")
|
134
|
+
try:
|
135
|
+
# ... authentication logic
|
136
|
+
print("API key stored successfully.")
|
137
|
+
return {"api_key": api_key}
|
138
|
+
except Exception as e:
|
139
|
+
print(f"Error: {e}")
|
140
|
+
return None
|
141
|
+
```
|
142
|
+
|
143
|
+
### 5. **Function Design & Architecture**
|
144
|
+
|
145
|
+
**MUST CHECK:**
|
146
|
+
- [ ] Functions have single responsibility
|
147
|
+
- [ ] Functions are not longer than 30 lines (prefer 15-20)
|
148
|
+
- [ ] No deep nesting (max 3 levels)
|
149
|
+
- [ ] Parameters have default values where appropriate
|
150
|
+
- [ ] Return values are consistent and documented
|
151
|
+
|
152
|
+
**EXAMPLES:**
|
153
|
+
```python
|
154
|
+
# ✅ GOOD - Single responsibility, clear purpose
|
155
|
+
def _validate_provider_config(provider_data: Dict[str, Any], provider_name: str) -> None:
|
156
|
+
"""Validate provider configuration data."""
|
157
|
+
if not provider_data:
|
158
|
+
raise ConfigurationError(f"Provider '{provider_name}' not found")
|
159
|
+
|
160
|
+
required_fields = ["llm_type", "auth_strategy"]
|
161
|
+
for field in required_fields:
|
162
|
+
if field not in provider_data:
|
163
|
+
raise ConfigurationError(f"Missing required field '{field}' for provider '{provider_name}'")
|
164
|
+
|
165
|
+
def get_llm(self, provider_name: Optional[str] = None, model_alias: Optional[str] = None) -> Optional[BaseChatModel]:
|
166
|
+
"""Retrieve and initialize a LangChain chat model."""
|
167
|
+
provider_name, model_alias = self._resolve_model_params(provider_name, model_alias)
|
168
|
+
provider_data = self._get_provider_data(provider_name)
|
169
|
+
self._validate_provider_config(provider_data, provider_name)
|
170
|
+
return self._create_llm_instance(provider_data, provider_name, model_alias)
|
171
|
+
|
172
|
+
# ❌ BAD - Too long, multiple responsibilities
|
173
|
+
def get_llm(self, provider_name: Optional[str] = None, model_alias: Optional[str] = None) -> Optional[BaseChatModel]:
|
174
|
+
if not provider_name or not model_alias:
|
175
|
+
current_model = config.get_current_model()
|
176
|
+
if not current_model:
|
177
|
+
print("Error: No model selected...")
|
178
|
+
return None
|
179
|
+
provider_name = current_model.get("provider")
|
180
|
+
model_alias = current_model.get("model")
|
181
|
+
|
182
|
+
provider_data = self._config.get("providers", {}).get(provider_name)
|
183
|
+
if not provider_data:
|
184
|
+
print(f"Error: Provider '{provider_name}' not found...")
|
185
|
+
return None
|
186
|
+
# ... 50+ more lines of mixed logic
|
187
|
+
```
|
188
|
+
|
189
|
+
### 6. **Documentation & Comments**
|
190
|
+
|
191
|
+
**MUST CHECK:**
|
192
|
+
- [ ] All public functions have comprehensive docstrings
|
193
|
+
- [ ] Docstrings follow Google/NumPy style
|
194
|
+
- [ ] Complex logic has explanatory comments
|
195
|
+
- [ ] No commented-out code (use git instead)
|
196
|
+
- [ ] Type information is included in docstrings when helpful
|
197
|
+
|
198
|
+
**EXAMPLES:**
|
199
|
+
```python
|
200
|
+
# ✅ GOOD
|
201
|
+
def get_credentials(provider_name: str, model_alias: str, verbose: bool = False) -> Optional[Dict[str, Any]]:
|
202
|
+
"""
|
203
|
+
Retrieve stored credentials for a given provider.
|
204
|
+
|
205
|
+
Reads the configuration, determines the appropriate auth strategy,
|
206
|
+
and returns the credentials if available.
|
207
|
+
|
208
|
+
Args:
|
209
|
+
provider_name: The name of the provider (e.g., 'openai', 'github_copilot').
|
210
|
+
model_alias: The alias of the model (used for context in error messages).
|
211
|
+
verbose: If True, print debug information during credential retrieval.
|
212
|
+
|
213
|
+
Returns:
|
214
|
+
A dictionary containing credentials (e.g., {'api_key': '...'}) or None
|
215
|
+
if credentials are not found or authentication fails.
|
216
|
+
|
217
|
+
Raises:
|
218
|
+
AuthenticationError: If the authentication strategy is invalid or fails.
|
219
|
+
ConfigurationError: If the provider configuration is missing or invalid.
|
220
|
+
"""
|
221
|
+
|
222
|
+
# ❌ BAD
|
223
|
+
def get_credentials(provider_name: str, model_alias: str, verbose: bool = False) -> Optional[Dict[str, Any]]:
|
224
|
+
# Gets credentials
|
225
|
+
pass
|
226
|
+
```
|
227
|
+
|
228
|
+
### 7. **Security Considerations**
|
229
|
+
|
230
|
+
**MUST CHECK:**
|
231
|
+
- [ ] No hardcoded secrets or API keys
|
232
|
+
- [ ] Sensitive data uses secure storage (configuration files)
|
233
|
+
- [ ] Input validation for user-provided data
|
234
|
+
- [ ] No logging of sensitive information
|
235
|
+
- [ ] Proper handling of authentication tokens
|
236
|
+
|
237
|
+
**EXAMPLES:**
|
238
|
+
```python
|
239
|
+
# ✅ GOOD
|
240
|
+
def store_api_key(self, api_key: str) -> None:
|
241
|
+
"""Store API key securely in configuration file."""
|
242
|
+
if not api_key or not api_key.strip():
|
243
|
+
raise ValueError("API key cannot be empty")
|
244
|
+
|
245
|
+
# Validate API key format (example for OpenAI)
|
246
|
+
if not api_key.startswith(('sk-', 'sk-proj-')):
|
247
|
+
raise ValueError("Invalid API key format")
|
248
|
+
|
249
|
+
self._save_auth_data({"api_key": api_key})
|
250
|
+
logger.info("API key stored successfully for provider: %s", self.provider_name)
|
251
|
+
|
252
|
+
# ❌ BAD
|
253
|
+
def store_api_key(self, api_key: str) -> None:
|
254
|
+
"""Store API key."""
|
255
|
+
logger.info("Storing API key: %s", api_key) # SECURITY ISSUE!
|
256
|
+
self._save_auth_data({"api_key": api_key})
|
257
|
+
```
|
258
|
+
|
259
|
+
### 8. **Testing Considerations**
|
260
|
+
|
261
|
+
**MUST CHECK:**
|
262
|
+
- [ ] **All unit tests pass** (no failing tests allowed)
|
263
|
+
- [ ] Code is testable (no hard dependencies on external services)
|
264
|
+
- [ ] Functions can be easily mocked
|
265
|
+
- [ ] Side effects are minimized
|
266
|
+
- [ ] New functionality includes corresponding tests
|
267
|
+
- [ ] Edge cases are considered
|
268
|
+
- [ ] Tests run successfully in CI/CD pipeline
|
269
|
+
|
270
|
+
**EXAMPLES:**
|
271
|
+
```python
|
272
|
+
# ✅ GOOD - Testable design
|
273
|
+
class ApiKeyAuth(AuthStrategy):
|
274
|
+
def __init__(self, provider_name: str):
|
275
|
+
self.provider_name = provider_name
|
276
|
+
|
277
|
+
def _get_auth_data(self) -> Dict[str, Any]:
|
278
|
+
"""Wrapper for config access to enable testing."""
|
279
|
+
return get_config()[0].get("providers", {}).get(self.provider_name, {}).get("auth_data", {})
|
280
|
+
|
281
|
+
# ❌ BAD - Hard to test
|
282
|
+
class ApiKeyAuth(AuthStrategy):
|
283
|
+
def get_credentials(self) -> Optional[Dict[str, Any]]:
|
284
|
+
# Direct config call - hard to mock in tests
|
285
|
+
config_data, _ = get_config()
|
286
|
+
auth_data = config_data.get("providers", {}).get(self.provider_name, {}).get("auth_data", {})
|
287
|
+
return auth_data if auth_data else None
|
288
|
+
```
|
289
|
+
|
290
|
+
### **Test Execution Requirements**
|
291
|
+
|
292
|
+
**MANDATORY BEFORE CODE REVIEW:**
|
293
|
+
```bash
|
294
|
+
# All tests must pass before submitting for review
|
295
|
+
poetry run pytest tests/ -v
|
296
|
+
|
297
|
+
# Expected output should show all tests passing:
|
298
|
+
# ============ 19 passed in 3.10s ============
|
299
|
+
|
300
|
+
# If any tests fail, they must be fixed before merge
|
301
|
+
```
|
302
|
+
|
303
|
+
**MUST VERIFY:**
|
304
|
+
- [ ] `poetry run pytest tests/ -v` shows all tests passing
|
305
|
+
- [ ] No test files are skipped or ignored
|
306
|
+
- [ ] New functionality includes corresponding test coverage
|
307
|
+
- [ ] Tests run in clean environment (no external dependencies)
|
308
|
+
|
309
|
+
## 🧹 **Linter Error Clearance Checklist**
|
310
|
+
|
311
|
+
### **📋 Pre-Review Requirements**
|
312
|
+
Before any code review, **ALL** linter errors must be resolved. This section provides a systematic approach to clearing linter issues.
|
313
|
+
|
314
|
+
### **🔍 Step 1: Automated Fixes**
|
315
|
+
```bash
|
316
|
+
# Apply all auto-fixable rules
|
317
|
+
poetry run ruff check --fix .
|
318
|
+
|
319
|
+
# Apply unsafe fixes (be careful, review changes)
|
320
|
+
poetry run ruff check --fix --unsafe-fixes .
|
321
|
+
|
322
|
+
# Format code with Black/Ruff
|
323
|
+
poetry run ruff format .
|
324
|
+
```
|
325
|
+
|
326
|
+
### **📊 Progress Tracking**
|
327
|
+
**Progress Made:** 600+ errors → 104 errors (83% improvement) ✅
|
328
|
+
|
329
|
+
**✅ COMPLETED:**
|
330
|
+
- Type annotations (ANN001, ANN201, ANN202, ANN204)
|
331
|
+
- Import organization (I001)
|
332
|
+
- Trailing whitespace (W291, W293)
|
333
|
+
- Basic formatting issues
|
334
|
+
|
335
|
+
**🚧 REMAINING (104 errors):**
|
336
|
+
- Line length violations (E501): ~25 errors
|
337
|
+
- Exception handling (TRY003, TRY301, TRY401): ~40 errors
|
338
|
+
- Path operations (PTH123): 2 errors
|
339
|
+
- Security issues (S110, S311, S113): 4 errors
|
340
|
+
- Code complexity (PLR0912, PLR0913, PLR0915): 4 errors
|
341
|
+
- Other misc issues: ~29 errors
|
342
|
+
|
343
|
+
### **🎯 Step 2: Systematic Error Resolution**
|
344
|
+
|
345
|
+
#### **📏 Line Length Violations (E501)**
|
346
|
+
**What to Fix:**
|
347
|
+
- Lines longer than 88 characters
|
348
|
+
- Common causes: Long strings, method calls, complex expressions
|
349
|
+
|
350
|
+
**How to Fix:**
|
351
|
+
```python
|
352
|
+
# ❌ Too long
|
353
|
+
really_long_message = f"This is a very long message that contains {variable_name} and {another_variable} and exceeds the line limit"
|
354
|
+
|
355
|
+
# ✅ Fixed with parentheses
|
356
|
+
really_long_message = (
|
357
|
+
f"This is a very long message that contains {variable_name} "
|
358
|
+
f"and {another_variable} and exceeds the line limit"
|
359
|
+
)
|
360
|
+
|
361
|
+
# ❌ Long method call
|
362
|
+
result = some_object.very_long_method_name(argument1, argument2, argument3, argument4)
|
363
|
+
|
364
|
+
# ✅ Fixed with line breaks
|
365
|
+
result = some_object.very_long_method_name(
|
366
|
+
argument1, argument2, argument3, argument4
|
367
|
+
)
|
368
|
+
```
|
369
|
+
|
370
|
+
#### **⚠️ Exception Handling (TRY003, TRY301, TRY401)**
|
371
|
+
**What to Fix:**
|
372
|
+
- TRY003: Long exception messages outside exception class
|
373
|
+
- TRY301: Abstract raise to inner function
|
374
|
+
- TRY401: Redundant str(e) in logging.exception
|
375
|
+
|
376
|
+
**How to Fix:**
|
377
|
+
```python
|
378
|
+
# ❌ TRY003: Long message in raise
|
379
|
+
raise ConfigurationError(f"Very long error message with {variable}")
|
380
|
+
|
381
|
+
# ✅ Fixed: Move to exception class or shorten
|
382
|
+
class ConfigurationError(Exception):
|
383
|
+
@classmethod
|
384
|
+
def provider_not_found(cls, provider: str) -> "ConfigurationError":
|
385
|
+
return cls(f"Provider '{provider}' not found")
|
386
|
+
|
387
|
+
raise ConfigurationError.provider_not_found(provider_name)
|
388
|
+
|
389
|
+
# ❌ TRY401: Redundant str(e)
|
390
|
+
logger.exception("Error occurred: %s", str(e))
|
391
|
+
|
392
|
+
# ✅ Fixed: Remove str(e)
|
393
|
+
logger.exception("Error occurred")
|
394
|
+
```
|
395
|
+
|
396
|
+
#### **📁 Path Operations (PTH123)**
|
397
|
+
**What to Fix:**
|
398
|
+
- Replace `open()` with `Path.open()`
|
399
|
+
|
400
|
+
**How to Fix:**
|
401
|
+
```python
|
402
|
+
# ❌ Old style
|
403
|
+
with open(config_path, "w") as f:
|
404
|
+
json.dump(data, f)
|
405
|
+
|
406
|
+
# ✅ Modern pathlib
|
407
|
+
with config_path.open("w") as f:
|
408
|
+
json.dump(data, f)
|
409
|
+
```
|
410
|
+
|
411
|
+
#### **🔒 Security Issues (S110, S311, S113)**
|
412
|
+
**What to Fix:**
|
413
|
+
- S110: try-except-pass without logging
|
414
|
+
- S311: Insecure random for crypto
|
415
|
+
- S113: Missing request timeouts
|
416
|
+
|
417
|
+
**How to Fix:**
|
418
|
+
```python
|
419
|
+
# ❌ S110: Silent exception
|
420
|
+
try:
|
421
|
+
risky_operation()
|
422
|
+
except Exception:
|
423
|
+
pass
|
424
|
+
|
425
|
+
# ✅ Fixed: Log the exception
|
426
|
+
try:
|
427
|
+
risky_operation()
|
428
|
+
except Exception:
|
429
|
+
logger.debug("Optional operation failed, continuing")
|
430
|
+
|
431
|
+
# ❌ S311: Insecure random
|
432
|
+
delay = random.uniform(0, 1)
|
433
|
+
|
434
|
+
# ✅ Fixed: Use secrets for crypto, random for non-crypto
|
435
|
+
delay = random.uniform(0, 1) # OK for backoff delays
|
436
|
+
|
437
|
+
# ❌ S113: No timeout
|
438
|
+
response = requests.post(url, data=data)
|
439
|
+
|
440
|
+
# ✅ Fixed: Add timeout
|
441
|
+
response = requests.post(url, data=data, timeout=30)
|
442
|
+
```
|
443
|
+
|
444
|
+
#### **🏗️ Code Complexity (PLR0912, PLR0913, PLR0915)**
|
445
|
+
**What to Fix:**
|
446
|
+
- PLR0912: Too many branches (>12)
|
447
|
+
- PLR0913: Too many arguments (>5)
|
448
|
+
- PLR0915: Too many statements (>50)
|
449
|
+
|
450
|
+
**How to Fix:**
|
451
|
+
```python
|
452
|
+
# ❌ PLR0913: Too many arguments
|
453
|
+
def complex_function(a, b, c, d, e, f, g):
|
454
|
+
pass
|
455
|
+
|
456
|
+
# ✅ Fixed: Use dataclass or config object
|
457
|
+
@dataclass
|
458
|
+
class Config:
|
459
|
+
a: str
|
460
|
+
b: str
|
461
|
+
c: str
|
462
|
+
# ... etc
|
463
|
+
|
464
|
+
def simplified_function(config: Config):
|
465
|
+
pass
|
466
|
+
```
|
467
|
+
|
468
|
+
### **🔄 Step 3: Final Verification**
|
469
|
+
```bash
|
470
|
+
# Check remaining errors
|
471
|
+
poetry run ruff check .
|
472
|
+
|
473
|
+
# Run tests to ensure no regressions
|
474
|
+
poetry run pytest
|
475
|
+
|
476
|
+
# Verify pre-commit passes
|
477
|
+
poetry run pre-commit run --all-files
|
478
|
+
```
|
479
|
+
|
480
|
+
### **📝 Step 4: Commit Clean Code**
|
481
|
+
```bash
|
482
|
+
git add .
|
483
|
+
git commit -m "fix: Clear all remaining linter errors
|
484
|
+
|
485
|
+
✅ Fixed line length violations (E501)
|
486
|
+
✅ Improved exception handling patterns
|
487
|
+
✅ Updated path operations to use pathlib
|
488
|
+
✅ Added security improvements
|
489
|
+
✅ Reduced code complexity
|
490
|
+
|
491
|
+
All 19 tests passing. Code ready for review."
|
492
|
+
```
|
493
|
+
|
494
|
+
### **📝 Code Review Notes**
|
495
|
+
When reviewing code with linter errors:
|
496
|
+
|
497
|
+
**❌ Reject immediately if:**
|
498
|
+
- Any linter errors remain unfixed
|
499
|
+
- `poetry run ruff check .` shows non-zero errors
|
500
|
+
- Type checking fails with `mypy .`
|
501
|
+
|
502
|
+
**✅ Accept only when:**
|
503
|
+
- All automated fixes have been applied
|
504
|
+
- Manual fixes follow this checklist
|
505
|
+
- All tests still pass after fixes
|
506
|
+
- Code maintainability is preserved or improved
|
507
|
+
|
508
|
+
**🔄 Iterative Process:**
|
509
|
+
1. Run automated fixes first
|
510
|
+
2. Address type annotations systematically
|
511
|
+
3. Fix line length and formatting
|
512
|
+
4. Resolve exception handling patterns
|
513
|
+
5. Address security and complexity issues
|
514
|
+
6. Verify all tests still pass
|
515
|
+
7. Commit with descriptive message
|
516
|
+
|
517
|
+
## 🏗️ **Architecture Patterns to Enforce**
|
518
|
+
|
519
|
+
### 1. **Strategy Pattern for Authentication**
|
520
|
+
- Each auth method should inherit from `AuthStrategy`
|
521
|
+
- New auth methods should be added to `AUTH_STRATEGY_MAP`
|
522
|
+
- Auth strategies should be stateless where possible
|
523
|
+
|
524
|
+
### 2. **Configuration Management**
|
525
|
+
- Use the two-tier system (global/local)
|
526
|
+
- Always use `get_config()` to read configuration
|
527
|
+
- Validate configuration before use
|
528
|
+
|
529
|
+
### 3. **Factory Pattern for LLM Creation**
|
530
|
+
- `ModelForgeRegistry` acts as the factory
|
531
|
+
- Provider-specific logic should be contained
|
532
|
+
- LLM instances should be ready-to-use upon creation
|
533
|
+
|
534
|
+
## 🚨 **Common Anti-Patterns to Flag**
|
535
|
+
|
536
|
+
### 1. **Print Statement Usage**
|
537
|
+
```python
|
538
|
+
# ❌ REJECT THIS
|
539
|
+
print("Error: Something went wrong")
|
540
|
+
|
541
|
+
# ✅ REQUIRE THIS
|
542
|
+
logger.error("Something went wrong: %s", error_details)
|
543
|
+
```
|
544
|
+
|
545
|
+
### 2. **Generic Exception Handling**
|
546
|
+
```python
|
547
|
+
# ❌ REJECT THIS
|
548
|
+
try:
|
549
|
+
risky_operation()
|
550
|
+
except Exception:
|
551
|
+
return None
|
552
|
+
|
553
|
+
# ✅ REQUIRE THIS
|
554
|
+
try:
|
555
|
+
risky_operation()
|
556
|
+
except SpecificError as e:
|
557
|
+
logger.error("Operation failed: %s", str(e))
|
558
|
+
raise ConfigurationError("Operation failed") from e
|
559
|
+
```
|
560
|
+
|
561
|
+
### 3. **Long Parameter Lists**
|
562
|
+
```python
|
563
|
+
# ❌ REJECT THIS (>5 parameters)
|
564
|
+
def create_provider(name, type, url, key, secret, timeout, retries, debug):
|
565
|
+
pass
|
566
|
+
|
567
|
+
# ✅ REQUIRE THIS
|
568
|
+
@dataclass
|
569
|
+
class ProviderConfig:
|
570
|
+
name: str
|
571
|
+
type: str
|
572
|
+
url: str
|
573
|
+
key: str
|
574
|
+
secret: str
|
575
|
+
timeout: int = 30
|
576
|
+
retries: int = 3
|
577
|
+
debug: bool = False
|
578
|
+
|
579
|
+
def create_provider(config: ProviderConfig):
|
580
|
+
pass
|
581
|
+
```
|
582
|
+
|
583
|
+
## 📊 **Review Checklist Template**
|
584
|
+
|
585
|
+
For each code review, verify:
|
586
|
+
|
587
|
+
```markdown
|
588
|
+
## Code Quality Review
|
589
|
+
|
590
|
+
### ✅ Style & Formatting
|
591
|
+
- [ ] Code follows Ruff formatting standards
|
592
|
+
- [ ] Imports are properly organized
|
593
|
+
- [ ] Variable names are descriptive
|
594
|
+
|
595
|
+
### ✅ Type Safety
|
596
|
+
- [ ] All functions have type hints
|
597
|
+
- [ ] Return types are annotated
|
598
|
+
- [ ] No unjustified use of `Any`
|
599
|
+
|
600
|
+
### ✅ Error Handling
|
601
|
+
- [ ] Custom exceptions used appropriately
|
602
|
+
- [ ] No bare except clauses
|
603
|
+
- [ ] Proper logging instead of print statements
|
604
|
+
|
605
|
+
### ✅ Security
|
606
|
+
- [ ] No hardcoded secrets
|
607
|
+
- [ ] Input validation present
|
608
|
+
- [ ] Sensitive data handled securely
|
609
|
+
|
610
|
+
### ✅ Architecture
|
611
|
+
- [ ] Follows established patterns
|
612
|
+
- [ ] Single responsibility principle
|
613
|
+
- [ ] Functions are appropriately sized
|
614
|
+
|
615
|
+
### ✅ Documentation
|
616
|
+
- [ ] Comprehensive docstrings
|
617
|
+
- [ ] Complex logic is commented
|
618
|
+
- [ ] Public API is documented
|
619
|
+
|
620
|
+
### ✅ Testing
|
621
|
+
- [ ] **All unit tests pass**
|
622
|
+
- [ ] Code is testable
|
623
|
+
- [ ] Dependencies can be mocked
|
624
|
+
- [ ] Edge cases considered
|
625
|
+
- [ ] New tests added for new functionality
|
626
|
+
```
|
627
|
+
|
628
|
+
## 🎯 **Priority Levels for Review Comments**
|
629
|
+
|
630
|
+
### 🔴 **CRITICAL** (Must fix before merge)
|
631
|
+
- **Failing unit tests** (all tests must pass)
|
632
|
+
- Security vulnerabilities
|
633
|
+
- Broken functionality
|
634
|
+
- Missing type hints on public APIs
|
635
|
+
- Use of print statements instead of logging
|
636
|
+
|
637
|
+
### 🟡 **IMPORTANT** (Should fix)
|
638
|
+
- Poor error handling
|
639
|
+
- Missing documentation
|
640
|
+
- Code style violations
|
641
|
+
- Performance concerns
|
642
|
+
|
643
|
+
### 🔵 **SUGGESTION** (Nice to have)
|
644
|
+
- Alternative implementations
|
645
|
+
- Code optimization opportunities
|
646
|
+
- Additional test cases
|
647
|
+
- Documentation improvements
|
648
|
+
|
649
|
+
## 🚀 **Review Response Format**
|
650
|
+
|
651
|
+
Structure feedback as:
|
652
|
+
|
653
|
+
```markdown
|
654
|
+
## Code Review Feedback
|
655
|
+
|
656
|
+
### 🔴 Critical Issues
|
657
|
+
- **Tests**: `tests/test_auth.py::test_api_key_auth_authenticate`
|
658
|
+
**Issue**: Unit test failing due to missing mock
|
659
|
+
**Fix**: Add proper mocking for keyring.set_password in test setup
|
660
|
+
|
661
|
+
- **File**: `src/modelforge/auth.py`, Line 45
|
662
|
+
**Issue**: Using print statement instead of logging
|
663
|
+
**Fix**: Replace with `logger.error("Authentication failed: %s", str(e))`
|
664
|
+
|
665
|
+
### 🟡 Important Issues
|
666
|
+
- **File**: `src/modelforge/registry.py`, Line 23
|
667
|
+
**Issue**: Missing type hints
|
668
|
+
**Fix**: Add return type annotation `-> Optional[BaseChatModel]`
|
669
|
+
|
670
|
+
### 🔵 Suggestions
|
671
|
+
- Consider extracting validation logic into separate function
|
672
|
+
- Could benefit from additional error handling for edge case X
|
673
|
+
|
674
|
+
### ✅ Positive Feedback
|
675
|
+
- Excellent use of Strategy pattern
|
676
|
+
- Clear and comprehensive docstrings
|
677
|
+
- Good separation of concerns
|
678
|
+
```
|
679
|
+
|
680
|
+
Use these guidelines consistently to maintain ModelForge's code quality and help developers learn Python best practices.
|